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

我们首先来看看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. List中的set方法和add方法

    public class TestList {public static void main(String[] args){   List l1 = new LinkedList();   for(i ...

  2. 003.SQLServer数据库镜像高可用部署

    一 数据库镜像部署准备 1.1 数据库镜像支持 有关对 SQL Server 2012 中的数据库镜像的支持的信息,请参考:https://docs.microsoft.com/zh-cn/previ ...

  3. 每天学点node系列-zlib

    永不放弃,永不放弃又有两个原则,第一个原则是永不放弃,第二个原则就是:当你想放弃时回头看第一个原则. 概览 做过web性能优化的同学,对性能优化大杀器gzip应该不陌生.浏览器向服务器发起资源请求,比 ...

  4. python 微信红包生成器

    #红包生成思路#200 块钱 10个红包#0-200 的一个轴,随机取9个点,分成10段, 每一段的值表示一个红包的大小 #把输入的 money值 * 100 拿到的数值就是分, 不用再考虑单位是元的 ...

  5. 分组在re模块中的使用

    import re #search s = "<a>wahaha</a>" #标签语言 html 和 web相关 ret= re.search(" ...

  6. java Springboot 生成 二维码 +logo

    上码,如有问题或者优化,劳请广友下方留言 1.工具类 import com.google.zxing.BarcodeFormat; import com.google.zxing.EncodeHint ...

  7. 一篇文章学会Docker命令

    目录 简介 镜像仓库 login pull push search 本地镜像管理 images rmi tag build history save load import 容器操作 ps inspe ...

  8. ~~Py2&Py3~~

    进击のpython python2 整型 int -- long(长整型) /获取的是整数 python3 整型 int /获取的是浮点数(小数) python2 print(range(1,10)) ...

  9. MyBatis从入门到精通:各个实体类

    SysUser类: package tk.mybatis.simple.model; import java.util.Date; public class SysUser { public Long ...

  10. 最全caffe安装踩坑记录(Anaconda,nvidia-docker,Linux编译)

    Anaconda,nvidia-docker,Linux三种方式安装caffe 1.Anaconda安装caffe 1.首先安装anaconda 2.创建虚拟环境(python2.7) conda c ...