我们先上一张客户端服务引用的时序图。

我们首先来看看ComsumerConfig的refer方法吧

    public T refer() {
if (consumerBootstrap == null) {
//如果服务消费者启动类为空,怎创建一个
consumerBootstrap = Bootstraps.from(this);
}
return consumerBootstrap.refer();
}

然后我们再看Bootstraps是怎么创建的

    public static <T> ConsumerBootstrap<T> from(ConsumerConfig<T> consumerConfig) {
String bootstrap = consumerConfig.getBootstrap();
ConsumerBootstrap consumerBootstrap;
//如果有传入启动器,那么就使用启动器的参数
if (StringUtils.isNotEmpty(bootstrap)) {
consumerBootstrap = ExtensionLoaderFactory.getExtensionLoader(ConsumerBootstrap.class)
.getExtension(bootstrap,
new Class[] { ConsumerConfig.class },
new Object[] { consumerConfig });
} else {
//没有传入启动器,那么就使用协议的参数
// default is same with protocol
bootstrap = consumerConfig.getProtocol();
ExtensionLoader extensionLoader = ExtensionLoaderFactory.getExtensionLoader(ConsumerBootstrap.class);
ExtensionClass<ConsumerBootstrap> extensionClass = extensionLoader.getExtensionClass(bootstrap);
//预防性代码,实际不可能为空
if (extensionClass == null) {
// if not exist, use default consumer bootstrap
// 为空的话,则是使用默认的启动器DefaultConsumerBootstrap
bootstrap = RpcConfigs.getStringValue(RpcOptions.DEFAULT_CONSUMER_BOOTSTRAP);
consumerConfig.setBootstrap(bootstrap);
consumerBootstrap = ExtensionLoaderFactory.getExtensionLoader(ConsumerBootstrap.class)
.getExtension(bootstrap, new Class[] { ConsumerConfig.class }, new Object[] { consumerConfig });
} else {
consumerConfig.setBootstrap(bootstrap);
consumerBootstrap = extensionClass.getExtInstance(
new Class[] { ConsumerConfig.class }, new Object[] { consumerConfig });
}
}
return (ConsumerBootstrap<T>) consumerBootstrap;
}

这里返回的consumerBootstrap和用的启动器和协议有关,如果用的是bolt那么返回的就是BoltConsumerBootstrap实例。

从这里可以看出很多功能都是继承自父类DefaultConsumerBootstrap的。

继续调用consumerBootstrap#refer方法,会直接跳到父类的refer方法中。

    public T refer() {
if (proxyIns != null) {
return proxyIns;
}
synchronized (this) {
if (proxyIns != null) {
return proxyIns;
}
String key = consumerConfig.buildKey();
String appName = consumerConfig.getAppName();
// 检查参数
checkParameters();
// 提前检查接口类
if (LOGGER.isInfoEnabled(appName)) {
LOGGER.infoWithApp(appName, "Refer consumer config : {} with bean id {}", key, consumerConfig.getId());
} // 注意同一interface,同一tags,同一protocol情况
AtomicInteger cnt = REFERRED_KEYS.get(key); // 计数器
if (cnt == null) { // 没有发布过
cnt = CommonUtils.putToConcurrentMap(REFERRED_KEYS, key, new AtomicInteger(0));
}
int c = cnt.incrementAndGet();
//同一个服务 的最大引用次数,防止由于代码bug导致重复引用,每次引用都会生成一个代理类对象,-1表示不检查
int maxProxyCount = consumerConfig.getRepeatedReferLimit();
if (maxProxyCount > 0) {
if (c > maxProxyCount) {
cnt.decrementAndGet();
// 超过最大数量,直接抛出异常
throw new SofaRpcRuntimeException("Duplicate consumer config with key " + key
+ " has been referred more than " + maxProxyCount + " times!"
+ " Maybe it's wrong config, please check it."
+ " Ignore this if you did that on purpose!");
} else if (c > 1) {
if (LOGGER.isInfoEnabled(appName)) {
LOGGER.infoWithApp(appName, "Duplicate consumer config with key {} has been referred!"
+ " Maybe it's wrong config, please check it."
+ " Ignore this if you did that on purpose!", key);
}
}
} try {
// build cluster
//默认是FailOverCluster
cluster = ClusterFactory.getCluster(this);
// build listeners
consumerConfig.setConfigListener(buildConfigListener(this));
consumerConfig.setProviderInfoListener(buildProviderInfoListener(this));
// init cluster
cluster.init();
// 构造Invoker对象(执行链)
proxyInvoker = buildClientProxyInvoker(this);
// 创建代理类
proxyIns = (T) ProxyFactory.buildProxy(consumerConfig.getProxy(), consumerConfig.getProxyClass(),
proxyInvoker); //动态配置
final String dynamicAlias = consumerConfig.getParameter(DynamicConfigKeys.DYNAMIC_ALIAS);
if (StringUtils.isNotBlank(dynamicAlias)) {
final DynamicConfigManager dynamicManager = DynamicConfigManagerFactory.getDynamicManager(
consumerConfig.getAppName(), dynamicAlias);
dynamicManager.initServiceConfiguration(consumerConfig.getInterfaceId());
}
} catch (Exception e) {
if (cluster != null) {
cluster.destroy();
cluster = null;
}
consumerConfig.setConfigListener(null);
consumerConfig.setProviderInfoListener(null);
cnt.decrementAndGet(); // 发布失败不计数
if (e instanceof SofaRpcRuntimeException) {
throw (SofaRpcRuntimeException) e;
} else {
throw new SofaRpcRuntimeException("Build consumer proxy error!", e);
}
}
if (consumerConfig.getOnAvailable() != null && cluster != null) {
cluster.checkStateChange(false); // 状态变化通知监听器
}
RpcRuntimeContext.cacheConsumerConfig(this);
return proxyIns;
}
}

这个方法里面除了做校验以外,主要做了如下几件事:

  1. 设置cluster属性,默认是FailOverCluster
  2. 设置监听器
  3. 初始化cluster
    1. 构造路由链表,主要有DirectUrlRouter、RegistryRouter、CustomRouter
    2. 设置loadBalancer属性,默认是RandomLoadBalancer
    3. 设置地址管理器addressHolder
    4. 设置连接管理器connectionHolder
    5. 构造Filter链
    6. 启动重连线程
  4. 设置proxyInvoker属性,如果用的是bolt协议,那么返回的是BoltClientProxyInvoker
  5. 创建代理类

如果暴露的服务的接口如下:

public interface HelloService {
String sayHello(String string);
}

默认用javagent代理生成代理类:

public class HelloService_proxy_0 extends Proxy implements HelloService {
public Invoker proxyInvoker = null;
private Method method_1; public HelloService_proxy_0() {
super(new UselessInvocationHandler());
this.method_1 = ReflectUtils.getMethod(HelloService.class, "sayHello", new Class[]{String.class, Integer.TYPE});
} public String sayHello(String var1, int var2) {
Class var3 = HelloService.class;
Method var4 = this.method_1;
Class[] var5 = new Class[2];
Object[] var6 = new Object[]{var1, null};
var5[0] = String.class;
var6[1] = new Integer(var2);
var5[1] = Integer.TYPE;
SofaRequest var7 = MessageBuilder.buildSofaRequest(var3, var4, var5, var6);
//这里的invoker应该是BoltClientProxyInvoker
SofaResponse var8 = this.proxyInvoker.invoke(var7);
if (var8.isError()) {
throw new SofaRpcException(199, var8.getErrorMsg());
} else {
Object var9 = var8.getAppResponse();
if (var9 instanceof Throwable) {
throw (Throwable)var9;
} else {
return (String)var9;
}
}
} public String toString() {
return this.proxyInvoker.toString();
} public int hashCode() {
return this.proxyInvoker.hashCode();
} public boolean equals(Object var1) {
return this == var1 || this.getClass().isInstance(var1) && this.proxyInvoker.equals(JavassistProxy.parseInvoker(var1));
}
}

2. 源码分析---SOFARPC客户端服务引用的更多相关文章

  1. 3. 源码分析---SOFARPC客户端服务调用

    我们首先看看BoltClientProxyInvoker的关系图 所以当我们用BoltClientProxyInvoker#invoke的时候实际上是调用了父类的invoke方法 ClientProx ...

  2. 4. 源码分析---SOFARPC服务端暴露

    服务端的示例 我们首先贴上我们的服务端的示例: public static void main(String[] args) { ServerConfig serverConfig = new Ser ...

  3. 7.源码分析---SOFARPC是如何实现故障剔除的?

    我在服务端引用那篇文章里面分析到,服务端在引用的时候会去获取服务端可用的服务,并进行心跳,维护一个可用的集合. 所以我们从客户端初始化这部分说起. 服务连接的维护 客户端初始化的时候会调用cluste ...

  4. 9.源码分析---SOFARPC是如何实现故障剔除的?

    SOFARPC源码解析系列: 1. 源码分析---SOFARPC可扩展的机制SPI 2. 源码分析---SOFARPC客户端服务引用 3. 源码分析---SOFARPC客户端服务调用 4. 源码分析- ...

  5. 11.源码分析---SOFARPC数据透传是实现的?

    先把栗子放上,让大家方便测试用: Service端 public static void main(String[] args) { ServerConfig serverConfig = new S ...

  6. 10.源码分析---SOFARPC内置链路追踪SOFATRACER是怎么做的?

    SOFARPC源码解析系列: 1. 源码分析---SOFARPC可扩展的机制SPI 2. 源码分析---SOFARPC客户端服务引用 3. 源码分析---SOFARPC客户端服务调用 4. 源码分析- ...

  7. 5.源码分析---SOFARPC调用服务

    我们这一次来接着上一篇文章<4. 源码分析---SOFARPC服务端暴露>讲一下服务暴露之后被客户端调用之后服务端是怎么返回数据的. 示例我们还是和上篇文章一样使用一样的bolt协议来讲: ...

  8. Netty源码分析之ByteBuf引用计数

    引用计数是一种常用的内存管理机制,是指将资源的被引用次数保存起来,当被引用次数变为零时就将其释放的过程.Netty在4.x版本开始使用引用计数机制进行部分对象的管理,其实现思路并不是特别复杂,它主要涉 ...

  9. 1. 源码分析---SOFARPC可扩展的机制SPI

    这几天离职在家,正好没事可以疯狂的输出一下,本来想写DUBBO的源码解析的,但是发现写DUBBO源码的太多了,所以找一个写的不那么多的框架,所以就选中SOFARPC这个框架了. SOFARPC是蚂蚁金 ...

随机推荐

  1. yii后台模板标签

    yii模板中的label标签 <?php echo $form->labelEx($model,'name'); ?> 编译后: <label for="Projec ...

  2. 搭建本地pip源

    搭建本地的pip源 开发环境部署机器的时候, 每次从网上下载pip包会很慢, 将需要的包和相关依赖下载到本地, 搭建一个本地源服务器. 基本都是安装多个包, 推荐使用文件的方式, 文件内容格式, 可以 ...

  3. Android 上传开源项目到 jcenter 实战踩坑之路

    本文微信公众号「AndroidTraveler」首发. 背景 其实 Android 上传开源项目到 jcenter 并不是一件新鲜事,网上也有很多文章. 包括我本人在将开源项目上传到 jcenter ...

  4. 跟我学SpringCloud | 第十篇:服务网关Zuul高级篇

    SpringCloud系列教程 | 第十篇:服务网关Zuul高级篇 Springboot: 2.1.6.RELEASE SpringCloud: Greenwich.SR1 如无特殊说明,本系列教程全 ...

  5. ElasticSearch核心概念和文档的CRUD

    目录 1. 基本概念 1.1 Node 与 Cluster 1.2 Index 1.3 Document 1.4 Type(将在ES6.0移除) 2. 数据的增删改查 2.1 添加数据 2.2 查询数 ...

  6. Spring Cloud Alibaba | Nacos服务注册与发现

    目录 Spring Cloud Alibaba | Nacos服务注册与发现 1. 服务提供者 1.1 pom.xml项目依赖 1.2 配置文件application.yml 1.3 启动类Produ ...

  7. android_MultiAutoCompleteTextView

    package cn.com.sxp;import android.app.Activity;import android.os.Bundle;import android.view.View;imp ...

  8. MPP install

    greenplum install 注意事项:1.hostname 不能大写##2.如果segment和mirror副本数比较大,建议调整/etc/ssh/sshd_config的maxconnect ...

  9. 【题解】【A % B Problem(P1865)】-C++

    题目背景 题目名称是吸引你点进来的 实际上该题还是很水的 题目描述 区间质数个数 输入输出格式 输入格式: 一行两个整数 询问次数n,范围m 接下来n行,每行两个整数 l,r 表示区间 输出格式: 对 ...

  10. 洛谷P2299 Mzc和体委的争夺战 题解

    题目 题目描述 mzc家很有钱(开玩笑),他家有n个男家丁(做过前三弹的都知道).但如此之多的男家丁吸引来了我们的体委(矮胖小伙),他要来与mzc争夺男家丁. mzc很生气,决定与其决斗,但cat的体 ...