我们首先看看BoltClientProxyInvoker的关系图

所以当我们用BoltClientProxyInvoker#invoke的时候实际上是调用了父类的invoke方法

ClientProxyInvoker#invoke

  1. @Override
  2. public SofaResponse invoke(SofaRequest request) throws SofaRpcException {
  3. SofaResponse response = null;
  4. Throwable throwable = null;
  5. try {
  6. RpcInternalContext.pushContext();
  7. RpcInternalContext context = RpcInternalContext.getContext();
  8. context.setProviderSide(false);
  9. // 包装request请求
  10. decorateRequest(request);
  11. try {
  12. // 产生开始调用事件
  13. if (EventBus.isEnable(ClientStartInvokeEvent.class)) {
  14. EventBus.post(new ClientStartInvokeEvent(request));
  15. }
  16. // 得到结果
  17. response = cluster.invoke(request);
  18. } catch (SofaRpcException e) {
  19. throwable = e;
  20. throw e;
  21. } finally {
  22. // 产生调用结束事件
  23. if (!request.isAsync()) {
  24. if (EventBus.isEnable(ClientEndInvokeEvent.class)) {
  25. EventBus.post(new ClientEndInvokeEvent(request, response, throwable));
  26. }
  27. }
  28. }
  29. // 包装响应
  30. decorateResponse(response);
  31. return response;
  32. } finally {
  33. RpcInternalContext.removeContext();
  34. RpcInternalContext.popContext();
  35. }
  36. }

这个方法主要做了几件事:

  1. 包装request请求,设置必要的参数
  2. 调用FailOverCluster的invoke方法,将reques请求发送出去,并得到response相应
  3. 包装response响应

我们在调用FailOverCluster的时候实际上是调用的父类AbstractCluster的invoker方法,FailOverCluster关系图如下:

所以我们进入到AbstractCluster的invoker方法中:

  1. @Override
  2. public SofaResponse invoke(SofaRequest request) throws SofaRpcException {
  3. SofaResponse response = null;
  4. try {
  5. // 做一些初始化检查,例如未连接可以连接
  6. checkClusterState();
  7. // 开始调用
  8. countOfInvoke.incrementAndGet(); // 计数+1
  9. response = doInvoke(request);
  10. return response;
  11. } catch (SofaRpcException e) {
  12. // 客户端收到异常(客户端自己的异常)
  13. throw e;
  14. } finally {
  15. countOfInvoke.decrementAndGet(); // 计数-1
  16. }
  17. }

checkClusterState方法主要是用来校验是否已销毁了,或是调用了init方法进行初始化了。

然后会在调用之前记一下数。

然后我们进入到doInvoke方法中:

  1. public SofaResponse doInvoke(SofaRequest request) throws SofaRpcException {
  2. String methodName = request.getMethodName();
  3. int retries = consumerConfig.getMethodRetries(methodName);
  4. int time = 0;
  5. SofaRpcException throwable = null;// 异常日志
  6. List<ProviderInfo> invokedProviderInfos = new ArrayList<ProviderInfo>(retries + 1);
  7. do {
  8. //负载均衡
  9. ProviderInfo providerInfo = select(request, invokedProviderInfos);
  10. try {
  11. //调用过滤器链
  12. SofaResponse response = filterChain(providerInfo, request);
  13. if (response != null) {
  14. if (throwable != null) {
  15. if (LOGGER.isWarnEnabled(consumerConfig.getAppName())) {
  16. LOGGER.warnWithApp(consumerConfig.getAppName(),
  17. LogCodes.getLog(LogCodes.WARN_SUCCESS_BY_RETRY,
  18. throwable.getClass() + ":" + throwable.getMessage(),
  19. invokedProviderInfos));
  20. }
  21. }
  22. return response;
  23. } else {
  24. throwable = new SofaRpcException(RpcErrorType.CLIENT_UNDECLARED_ERROR,
  25. "Failed to call " + request.getInterfaceName() + "." + methodName
  26. + " on remote server " + providerInfo + ", return null");
  27. time++;
  28. }
  29. } catch (SofaRpcException e) { // 服务端异常+ 超时异常 才发起rpc异常重试
  30. if (e.getErrorType() == RpcErrorType.SERVER_BUSY
  31. || e.getErrorType() == RpcErrorType.CLIENT_TIMEOUT) {
  32. throwable = e;
  33. time++;
  34. } else {
  35. throw e;
  36. }
  37. } catch (Exception e) { // 其它异常不重试
  38. throw new SofaRpcException(RpcErrorType.CLIENT_UNDECLARED_ERROR,
  39. "Failed to call " + request.getInterfaceName() + "." + request.getMethodName()
  40. + " on remote server: " + providerInfo + ", cause by unknown exception: "
  41. + e.getClass().getName() + ", message is: " + e.getMessage(), e);
  42. } finally {
  43. if (RpcInternalContext.isAttachmentEnable()) {
  44. RpcInternalContext.getContext().setAttachment(RpcConstants.INTERNAL_KEY_INVOKE_TIMES,
  45. time + 1); // 重试次数
  46. }
  47. }
  48. invokedProviderInfos.add(providerInfo);
  49. } while (time <= retries);
  50. throw throwable;
  51. }

这个方法里面主要做了这这件事:

  1. 如果失败的话就循环调用
  2. 负载均衡,选取provider
  3. 通过过滤器链调用服务端,并返回结果
  4. 异常处理

接着我们进入到filterChain方法中,根据过滤器链最后会跳到ConsumerInvoker中的invoke方法

  1. @Override
  2. public SofaResponse invoke(SofaRequest sofaRequest) throws SofaRpcException {
  3. // 设置下服务器应用
  4. ProviderInfo providerInfo = RpcInternalContext.getContext().getProviderInfo();
  5. String appName = providerInfo.getStaticAttr(ProviderInfoAttrs.ATTR_APP_NAME);
  6. if (StringUtils.isNotEmpty(appName)) {
  7. sofaRequest.setTargetAppName(appName);
  8. }
  9. // 目前只是通过client发送给服务端
  10. return consumerBootstrap.getCluster().sendMsg(providerInfo, sofaRequest);
  11. }

consumerBootstrap.getCluster()会返回FailOverCluster实例,然后调用父类AbstractCluster的sendMsg方法

  1. public SofaResponse sendMsg(ProviderInfo providerInfo, SofaRequest request) throws SofaRpcException {
  2. ClientTransport clientTransport = connectionHolder.getAvailableClientTransport(providerInfo);
  3. if (clientTransport != null && clientTransport.isAvailable()) {
  4. return doSendMsg(providerInfo, clientTransport, request);
  5. } else {
  6. throw unavailableProviderException(request.getTargetServiceUniqueName(), providerInfo.getOriginUrl());
  7. }
  8. }
  9. protected SofaResponse doSendMsg(ProviderInfo providerInfo, ClientTransport transport,
  10. SofaRequest request) throws SofaRpcException {
  11. RpcInternalContext context = RpcInternalContext.getContext();
  12. // 添加调用的服务端远程地址
  13. RpcInternalContext.getContext().setRemoteAddress(providerInfo.getHost(), providerInfo.getPort());
  14. try {
  15. checkProviderVersion(providerInfo, request); // 根据服务端版本特殊处理
  16. String invokeType = request.getInvokeType();
  17. int timeout = resolveTimeout(request, consumerConfig, providerInfo);
  18. SofaResponse response = null;
  19. // 同步调用
  20. if (RpcConstants.INVOKER_TYPE_SYNC.equals(invokeType)) {
  21. long start = RpcRuntimeContext.now();
  22. try {
  23. response = transport.syncSend(request, timeout);
  24. } finally {
  25. if (RpcInternalContext.isAttachmentEnable()) {
  26. long elapsed = RpcRuntimeContext.now() - start;
  27. context.setAttachment(RpcConstants.INTERNAL_KEY_CLIENT_ELAPSE, elapsed);
  28. }
  29. }
  30. }
  31. // 单向调用
  32. else if (RpcConstants.INVOKER_TYPE_ONEWAY.equals(invokeType)) {
  33. long start = RpcRuntimeContext.now();
  34. try {
  35. transport.oneWaySend(request, timeout);
  36. response = buildEmptyResponse(request);
  37. } finally {
  38. if (RpcInternalContext.isAttachmentEnable()) {
  39. long elapsed = RpcRuntimeContext.now() - start;
  40. context.setAttachment(RpcConstants.INTERNAL_KEY_CLIENT_ELAPSE, elapsed);
  41. }
  42. }
  43. }
  44. // Callback调用
  45. else if (RpcConstants.INVOKER_TYPE_CALLBACK.equals(invokeType)) {
  46. // 调用级别回调监听器
  47. SofaResponseCallback sofaResponseCallback = request.getSofaResponseCallback();
  48. if (sofaResponseCallback == null) {
  49. SofaResponseCallback methodResponseCallback = consumerConfig
  50. .getMethodOnreturn(request.getMethodName());
  51. if (methodResponseCallback != null) { // 方法的Callback
  52. request.setSofaResponseCallback(methodResponseCallback);
  53. }
  54. }
  55. // 记录发送开始时间
  56. context.setAttachment(RpcConstants.INTERNAL_KEY_CLIENT_SEND_TIME, RpcRuntimeContext.now());
  57. // 开始调用
  58. transport.asyncSend(request, timeout);
  59. response = buildEmptyResponse(request);
  60. }
  61. // Future调用
  62. else if (RpcConstants.INVOKER_TYPE_FUTURE.equals(invokeType)) {
  63. // 记录发送开始时间
  64. context.setAttachment(RpcConstants.INTERNAL_KEY_CLIENT_SEND_TIME, RpcRuntimeContext.now());
  65. // 开始调用
  66. ResponseFuture future = transport.asyncSend(request, timeout);
  67. // 放入线程上下文
  68. RpcInternalContext.getContext().setFuture(future);
  69. response = buildEmptyResponse(request);
  70. } else {
  71. throw new SofaRpcException(RpcErrorType.CLIENT_UNDECLARED_ERROR, "Unknown invoke type:" + invokeType);
  72. }
  73. return response;
  74. } catch (SofaRpcException e) {
  75. throw e;
  76. } catch (Throwable e) { // 客户端其它异常
  77. throw new SofaRpcException(RpcErrorType.CLIENT_UNDECLARED_ERROR, e);
  78. }
  79. }

sendMsg方法最后会调用到doSendMsg。

soSendMsg里面主要做了如下几件事:

  1. 如果是同步调用,则直接返回封装好的参数
  2. 如果是单向调用,则调用buildEmptyResponse方法,返回一个空的response
  3. 如果是callback调用asyncSend,RPC在获取到服务端的结果后会自动执行该回调实现。
  4. 服务端返回响应结果被 RPC 缓存,当客户端需要响应结果的时候需要主动获取结果,获取结果的过程阻塞线程。

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

  1. 2. 源码分析---SOFARPC客户端服务引用

    我们先上一张客户端服务引用的时序图. 我们首先来看看ComsumerConfig的refer方法吧 public T refer() { if (consumerBootstrap == null) ...

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

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

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

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

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

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

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

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

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

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

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

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

  8. Spring源码分析之`BeanFactoryPostProcessor`调用过程

    前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 本文内容: AbstractApplicationContext#refresh前部分的一点小内容 ...

  9. mybatis源码分析(方法调用过程)

    十一月月底,宿舍楼失火啦,搞得20多天没有网,目测直到放假也不会来了... 正题 嗯~,其实阅读源码不是为了应付面试,更重要的让你知道,大师是怎样去写代码的,同样是用Java,为啥Clinton Be ...

随机推荐

  1. CQRS之旅——旅程8(后记:经验教训)

    旅程8:后记:经验教训 我们的地图有多好?我们走了多远?我们学到了什么?我们迷路了吗? "这片土地可能对那些愿意冒险的人有益."亨利.哈德逊 这一章总结了我们旅程中的发现.它强调了 ...

  2. Linux下编译PHP常见错误及解决方法

    1.configure: error: xml2-config not found. Please check your libxml2 installation.yum install libxml ...

  3. linux查看文件内容命令tail、cat、tac、head、echo

    1.tail tail -f test.log 你会看到屏幕不断有内容被打印出来. 这时候中断第一个进程Ctrl-C, linux 如何显示一个文件的某几行(中间几行) 从第3000行开始,显示100 ...

  4. 微服务SpringCloud之熔断器

    学习SpringCloud微服务是参考纯洁的微笑博客,看到他提到股市的熔断我也忍不住吐槽一下,记得当时实施熔断第一天就熔断了,现在想想也还是搞笑,从之前的全民炒股到现在的全民炒房,都是一个炒字,问题是 ...

  5. C语言学习书籍推荐《C语言入门经典(第5版)》下载

    霍尔顿 (Ivor Horton) (作者), 杨浩 (译者) 下载地址:点我 C语言是每一位程序员都应该掌握的基础语言.C语言是微软.NET编程中使用的C#语言的基础:C语言是iPhone.iPad ...

  6. 关于String重写的hashcode的代码分析

    public int hashCode() { int h = hash; if (h == 0 && value.length > 0) { char val[] = valu ...

  7. Nginx+vsftpd

    一.安装Nginx 关闭selinux和firewalld setenforce sed -i 's/^SELINUX=enforcing$/SELINUX=disabled/' /etc/selin ...

  8. Bzoj 2839 集合计数 题解

    2839: 集合计数 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 495  Solved: 271[Submit][Status][Discuss] ...

  9. php if语句

    一.前言 if语句 是几乎所有编程语言都有的函数. 当然我们最好的php这么最好的语言也有啦~ 二.搞起! 直接上代码不多哔哔.talk is cheap show me the code 2.1 i ...

  10. C#3.0新增功能10 表达式树 06 生成表达式

    连载目录    [已更新最新开发文章,点击查看详细] 到目前为止,你所看到的所有表达式树都是由 C# 编译器创建的. 你所要做的是创建一个 lambda 表达式,将其分配给一个类型为 Expressi ...