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. mac环境下java项目无创建文件的权限

    1.问题: 先抛问题,由于刚刚换用mac环境,之前windows上开发的代码调试完毕,还未上线.之后上线部署之前,tl直连测试本地环境(mac)环境,功能无法使用,显示java.io.IOExcept ...

  2. 从后端到前端之Vue(二)写个tab试试水

    上一篇写了一下table,然后要写什么呢?当然是tab了.动态创建一个tab,里面放一个table,这样一个后台管理的基本功能(之一)就出来了. 好吧,这里其实只是试试水,感受一下vue的数据驱动可以 ...

  3. c# HttpWebResponse 各种情况下 获取StatusCode状态码

    捕捉网页出现404.500等会直接抛出WebException异常 异常代码: (HttpWebResponse)req.GetResponse(); 当执行这段代码出现异常 解决问题 那如果我们想获 ...

  4. Linux 安装MySql——ubuntu版

    这里简单地阐述一下rpm.deb.tar.gz的区别. rpm格式的软件包适用于基于Red Hat发行版的系统,如Red Hat Linux.SUSE.Fedora. deb格式的软件包则是适用于基于 ...

  5. maven打包添加依赖

    <plugins> <plugin> <artifactId>maven-assembly-plugin</artifactId> <versio ...

  6. mysql整数类型int后面的长度有什么意义

    int 的 SQL-92 同义字为 integer.SQL-92 是数据库的一个标准. int类型的存储大小为4个字节 unsigned(无符号) int 存储范围是 2^4*8 int(M) 中的M ...

  7. HDU 多校 第三场 Fansblog

    代码千万条,规范第一条 训练赛的时候打表找规律,发现答案是1/(st-pre-1)!,奈何用错了模板,一直TLE到比赛结束,一直以为是卡什么输入输出或者是两个素数相差太大导致复杂度过高,读入优化啥的都 ...

  8. Angular JS 中的内置方法之$watch

    在$apply方法中存在脏检查,首先apply方法会触发evel方法,当evel方法解析成功后,会去触发digest方法,digest方法会触发watch方法. $watch(watchFn,watc ...

  9. 【译】Hello Kubernetes快速交互实验手册

    原文:https://kubernetes.io/docs/tutorials 翻译:Edison Zhou 一.基本介绍 此交互实验可以让你不用搭建K8S环境就可以轻松地尝试管理一个简单的容器化应用 ...

  10. VSTO之PowerPoint(PPT)插件开发常用API汇总

    VSTO简介 VSTO(Visual Studio Tools for Office )是VBA的替代,使得开发Office应用程序更加简单,并且用VSTO来开发office应用程序可以使用Visua ...