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

我们首先来看看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. Niginx简单的配置

    #user nobody; #这里的数值不能超过 CPU 的总核数,因为在单个核上部署超过 1 个 Nginx 服务进程并不起到提高性能的作用.worker_processes 2; #nginx进程 ...

  2. [apue] 使用 poll 检测管道断开

    一般使用 poll 检测 socket 或标准输入时,只要指定 POLLIN 标志位,就可以检测是否有数据到达,或者连接断开: ]; fds[].fd = STDIN_FILENO; fds[].ev ...

  3. 跟我学SpringCloud | 第九篇:服务网关Zuul初

    SpringCloud系列教程 | 第九篇:服务网关Zuul初探 前面的文章我们介绍了,Eureka用于服务的注册于发现,Feign支持服务的调用以及均衡负载,Hystrix处理服务的熔断防止故障扩散 ...

  4. Thinkphp5.0之异常处理

    1.默认异常处理在调试模式下,系统默认展示的错误页面:请输入图片描述 异常处理接管 1.修改config.php 'app_debug' => false,2.在配置文件里添加如下代码 // 异 ...

  5. 在CentOS7下搭建Hadoop2.9.0集群

    系统环境:CentOS 7 JDK版本:jdk-8u191-linux-x64 MYSQL版本:5.7.26 Hadoop版本:2.9.0 Hive版本:2.3.4 Host Name Ip User ...

  6. django基础知识之csrf:

    csrf 全称Cross Site Request Forgery,跨站请求伪造 某些恶意网站上包含链接.表单按钮或者JavaScript,它们会利用登录过的用户在浏览器中的认证信息试图在你的网站上完 ...

  7. C++模版的用法

    模板是实现代码重用机制的一种工具,实质就是实现类型参数化,即把类型定义为参数. C++提供两种模板:函数模板,类模板 函数模板 template <typename T> T myMax( ...

  8. 使用OpenAPI构建更智能的API

    像OpenAPI这样的API描述规范是一个关键工具,您应该尽可能地将其好好掌握,记录和执行API的工作由计算机和开发人员完成:OpenAPI 3.0现在允许额外的表现力,可以让机器为我们做更多有用的工 ...

  9. 渐进式web应用开发--拥抱离线优先(三)

    _ 阅读目录 一:什么是离线优先? 二:常用的缓存模式 三:混合与匹配,创造新模式 四:规划缓存策略 五:实现缓存策略 回到顶部 一:什么是离线优先? 传统的web应用完全依赖于服务器端,比如像很早以 ...

  10. bzoj1854 游戏题解(二分图/并查集)

    1854: [Scoi2010]游戏 Time Limit: 5 Sec  Memory Limit: 162 MBSubmit: 5547  Solved: 2229[Submit][Status] ...