SOFARPC源码解析系列:

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

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

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

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

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

6.源码分析---和dubbo相比SOFARPC是如何实现负载均衡的?

7.源码分析---SOFARPC是如何实现连接管理与心跳?

8.源码分析---从设计模式中看SOFARPC中的EventBus?

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

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


先把栗子放上,让大家方便测试用:

Service端

public static void main(String[] args) {
ServerConfig serverConfig = new ServerConfig()
.setProtocol("bolt") // 设置一个协议,默认bolt
.setPort(12200) // 设置一个端口,默认12200
.setDaemon(false); // 非守护线程 ProviderConfig<HelloService> providerConfig = new ProviderConfig<HelloService>()
.setInterfaceId(HelloService.class.getName()) // 指定接口
.setRef(new HelloServiceImpl()) // 指定实现
.setServer(serverConfig); // 指定服务端 providerConfig.export(); // 发布服务
} public class HelloServiceImpl implements HelloService { private final static Logger LOGGER = LoggerFactory.getLogger(HelloServiceImpl.class); @Override
public String sayHello(String string) {
LOGGER.info("Server receive: " + string); // 获取请求透传数据并打印
System.out.println("service receive reqBag -> " + RpcInvokeContext.getContext().getRequestBaggage("req_bag"));
// 设置响应透传数据到当前线程的上下文中
RpcInvokeContext.getContext().putResponseBaggage("req_bag", "s2c"); return "hello " + string + " !";
}
}

client端

public static void main(String[] args) {
ConsumerConfig<HelloService> consumerConfig = new ConsumerConfig<HelloService>()
.setInterfaceId(HelloService.class.getName()) // 指定接口
.setProtocol("bolt") // 指定协议
.setDirectUrl("bolt://127.0.0.1:12200") // 指定直连地址
.setConnectTimeout(10 * 1000); RpcInvokeContext.getContext().putRequestBaggage("req_bag", "a2bbb"); HelloService helloService = consumerConfig.refer(); while (true) {
System.out.println("service receive reqBag -> " + RpcInvokeContext.getContext().getResponseBaggage("req_bag"));
try {
LOGGER.info(helloService.sayHello("world"));
} catch (Exception e) {
e.printStackTrace();
} try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} }
}

通过上面的栗子我们可以看出整个流程应该是:

  1. 客户端把需要透传的数据放入到requestBaggage中,然后调用服务端
  2. 服务端在HelloServiceImpl中获取请求透传数据并打印,并把响应数据放入到responseBaggage中
  3. 客户端收到透传数据

所以下面我们从客户端开始源码讲解。

客户端数据透传给服务端

首先客户端在引用之前要设置putRequestBaggage,然后在客户端引用的时候会调用ClientProxyInvoker#invoke方法。

如下:

ClientProxyInvoker#invoke

public SofaResponse invoke(SofaRequest request) throws SofaRpcException {
....
// 包装请求
decorateRequest(request);
....
}

通过调用decorateRequest会调用到子类DefaultClientProxyInvoker的decorateRequest方法。

DefaultClientProxyInvoker#decorateRequest

protected void decorateRequest(SofaRequest request) {
....
RpcInvokeContext invokeCtx = RpcInvokeContext.peekContext();
RpcInternalContext internalContext = RpcInternalContext.getContext();
if (invokeCtx != null) {
....
// 如果用户指定了透传数据
if (RpcInvokeContext.isBaggageEnable()) {
// 需要透传
BaggageResolver.carryWithRequest(invokeCtx, request);
internalContext.setAttachment(HIDDEN_KEY_INVOKE_CONTEXT, invokeCtx);
}
}
....
}

在decorateRequest方法里首先会校验有没有开启透传数据,如果开启了,那么就调用BaggageResolver#carryWithRequest,把要透传的数据放入到request里面

BaggageResolver#carryWithRequest

public static void carryWithRequest(RpcInvokeContext context, SofaRequest request) {
if (context != null) {
//获取所有的透传数据
Map<String, String> requestBaggage = context.getAllRequestBaggage();
if (CommonUtils.isNotEmpty(requestBaggage)) { // 需要透传
request.addRequestProp(RemotingConstants.RPC_REQUEST_BAGGAGE, requestBaggage);
}
}
}

这个方法里面要做的就是获取所有的透传数据,然后放置到RequestProp里面,这样在发送请求的时候就会传送到服务端。

服务端接受透传数据

服务端的调用流程如下:

BoltServerProcessor->FilterChain->ProviderExceptionFilter->FilterInvoker->RpcServiceContextFilter->FilterInvoker->ProviderBaggageFilter->FilterInvoker->ProviderTracerFilter->ProviderInvoker

所以从上面的调用链可以知道,在服务端引用的时候会经过ProviderBaggageFilter过滤器,我们下面看看这个过滤器做了什么事情:

ProviderBaggageFilter#invoke

public SofaResponse invoke(FilterInvoker invoker, SofaRequest request) throws SofaRpcException {
SofaResponse response = null;
try {
//从request中获取透传数据存入到requestBaggage中
BaggageResolver.pickupFromRequest(RpcInvokeContext.peekContext(), request, true);
response = invoker.invoke(request);
} finally {
if (response != null) {
BaggageResolver.carryWithResponse(RpcInvokeContext.peekContext(), response);
}
}
return response;
}

ProviderBaggageFilter会调用BaggageResolver#pickupFromRequest从request中获取数据

BaggageResolver#pickupFromRequest

public static void pickupFromRequest(RpcInvokeContext context, SofaRequest request, boolean init) {
if (context == null && !init) {
return;
}
// 解析请求
Map<String, String> requestBaggage = (Map<String, String>) request
.getRequestProp(RemotingConstants.RPC_REQUEST_BAGGAGE);
if (CommonUtils.isNotEmpty(requestBaggage)) {
if (context == null) {
context = RpcInvokeContext.getContext();
}
context.putAllRequestBaggage(requestBaggage);
}
}

最后会在ProviderBaggageFilter invoke方法的finally里面调用BaggageResolver#carryWithResponse把响应透传数据回写到response里面。

public static void carryWithResponse(RpcInvokeContext context, SofaResponse response) {
if (context != null) {
Map<String, String> responseBaggage = context.getAllResponseBaggage();
if (CommonUtils.isNotEmpty(responseBaggage)) {
String prefix = RemotingConstants.RPC_RESPONSE_BAGGAGE + ".";
for (Map.Entry<String, String> entry : responseBaggage.entrySet()) {
response.addResponseProp(prefix + entry.getKey(), entry.getValue());
}
}
}
}

客户端收到响应透传数据

最后客户端会在ClientProxyInvoker#invoke方法里调用decorateResponse获取response回写的数据。

public SofaResponse invoke(SofaRequest request) throws SofaRpcException {
....
// 包装响应
decorateResponse(response);
....
}

decorateResponse是在子类DefaultClientProxyInvoker实现的:

DefaultClientProxyInvoker#decorateResponse

protected void decorateResponse(SofaResponse response) {
....
//如果开启了透传
if (RpcInvokeContext.isBaggageEnable()) {
BaggageResolver.pickupFromResponse(invokeCtx, response, true);
}
....
}

这个方法里面会调用BaggageResolver#pickupFromResponse

public static void pickupFromResponse(RpcInvokeContext context, SofaResponse response, boolean init) {
if (context == null && !init) {
return;
}
Map<String, String> responseBaggage = response.getResponseProps();
if (CommonUtils.isNotEmpty(responseBaggage)) {
String prefix = RemotingConstants.RPC_RESPONSE_BAGGAGE + ".";
for (Map.Entry<String, String> entry : responseBaggage.entrySet()) {
if (entry.getKey().startsWith(prefix)) {
if (context == null) {
context = RpcInvokeContext.getContext();
}
//因为entry的key里面会包含rpc_resp_baggage,所以需要截取掉
context.putResponseBaggage(entry.getKey().substring(prefix.length()),
entry.getValue());
}
}
}
}

这个方法里面response获取所有的透传数据,然后放入到ResponseBaggage中。

到这里SOFARPC数据透传就分析完毕了

11.源码分析---SOFARPC数据透传是实现的?的更多相关文章

  1. SOFA 源码分析 — 链路数据透传

    前言 SOFA-RPC 支持数据链路透传功能,官方解释: 链路数据透传功能支持应用向调用上下文中存放数据,达到整个链路上的应用都可以操作该数据. 使用方式如下,可分别向链路的 request 和 re ...

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

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

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

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

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

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

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

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

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

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

  7. HDFS源码分析之数据块及副本状态BlockUCState、ReplicaState

    关于数据块.副本的介绍,请参考文章<HDFS源码分析之数据块Block.副本Replica>. 一.数据块状态BlockUCState 数据块状态用枚举类BlockUCState来表示,代 ...

  8. jQuery 源码分析(十) 数据缓存模块 data详解

    jQuery的数据缓存模块以一种安全的方式为DOM元素附加任意类型的数据,避免了在JavaScript对象和DOM元素之间出现循环引用,以及由此而导致的内存泄漏. 数据缓存模块为DOM元素和JavaS ...

  9. Hadoop源码分析之数据节点的握手,注册,上报数据块和心跳

    转自:http://www.it165.net/admin/html/201402/2382.html 在上一篇文章Hadoop源码分析之DataNode的启动与停止中分析了DataNode节点的启动 ...

随机推荐

  1. [USACO09OCT]Invasion of the Milkweed】乳草的侵占-C++

    Farmer John一直努力让他的草地充满鲜美多汁的而又健康的牧草.可惜天不从人愿,他在植物大战人类中败下阵来.邪恶的乳草已经在他的农场的西北部份占领了一片立足之地. 草地像往常一样,被分割成一个高 ...

  2. flash put_movie loadmovie 区别

    put_Movie 应该是c++的函数用来往程序加载一个swf用的,as3里没有loadmovie是flash用的,用来加载另一个swf或jpeg文件 不过这个loadmovie这个函数是在as2中用 ...

  3. 【CYH-02】noip2018数论模拟赛:赛后题解

    1.小奔的矩阵 2.大奔的方案 3.小奔与不等四边形 4.小奔的方案 当然本次比赛肯定难度不会仅限于此啦!后续还会--

  4. [leetcode] 51. N-Queens (递归)

    递归,经典的八皇后问题. 利用一位数组存储棋盘状态,索引表示行,值为-1表示空,否则表示列数. 对行进行搜索,对每一行的不同列数进行判断,如果可以摆放符合规则,则摆放,同时遍历下一行. 遍历过程中,若 ...

  5. web页面保存图片到本地

    web页生成分享海报功能踩坑经验 https://blog.csdn.net/candy_home/article/details/78424642 https://www.jianshu.com/p ...

  6. 《C#从入门到精通(第3版)》目录

    C#从入门到精通(第3版)pdf+源码 一.基础知识 1.初识C#及其开发环境 2.开始C#之旅 3.变量与常量 4.表达式与运算符 5.字符与字符串 6.流程控制语句 7.数组与集合 8.属性和方法 ...

  7. PHP对接口执行效率慢的优化

    PHP对接口执行效率慢的优化 PHP对接口执行效率慢的优化 造成执行效率低的原因可以由很多方面找原因 从代码层面,代码质量低,执行效率也会有很大影响的. 从硬件方面,服务器配置低,服务器配置是基础,这 ...

  8. Android App安装包瘦身计划

    Android App安装包瘦身计划 Android App安装包体积优化: 理由, 指标和可以采用的方法. 本文内容归纳如下图: 为什么要安装包瘦身 安装包需要瘦身吗? 不需要吗? 安装包要瘦身的主 ...

  9. 转 java - 如何判断单链表有环

    转自 https://blog.csdn.net/u010983881/article/details/78896293 1.穷举遍历 首先从头节点开始,依次遍历单链表的每一个节点.每遍历到一个新节点 ...

  10. Win常用软件

    本节只适合windows系统 VScode 下载 安装 双击安装 打开目录方式 右键文件夹->使用VSCode打开 命令行打开 code folder [dzlua@win10:~]$ ls a ...