11.源码分析---SOFARPC数据透传是实现的?
SOFARPC源码解析系列:
6.源码分析---和dubbo相比SOFARPC是如何实现负载均衡的?
8.源码分析---从设计模式中看SOFARPC中的EventBus?
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();
}
}
}
通过上面的栗子我们可以看出整个流程应该是:
- 客户端把需要透传的数据放入到requestBaggage中,然后调用服务端
- 服务端在HelloServiceImpl中获取请求透传数据并打印,并把响应数据放入到responseBaggage中
- 客户端收到透传数据
所以下面我们从客户端开始源码讲解。
客户端数据透传给服务端
首先客户端在引用之前要设置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数据透传是实现的?的更多相关文章
- SOFA 源码分析 — 链路数据透传
前言 SOFA-RPC 支持数据链路透传功能,官方解释: 链路数据透传功能支持应用向调用上下文中存放数据,达到整个链路上的应用都可以操作该数据. 使用方式如下,可分别向链路的 request 和 re ...
- 10.源码分析---SOFARPC内置链路追踪SOFATRACER是怎么做的?
SOFARPC源码解析系列: 1. 源码分析---SOFARPC可扩展的机制SPI 2. 源码分析---SOFARPC客户端服务引用 3. 源码分析---SOFARPC客户端服务调用 4. 源码分析- ...
- 5.源码分析---SOFARPC调用服务
我们这一次来接着上一篇文章<4. 源码分析---SOFARPC服务端暴露>讲一下服务暴露之后被客户端调用之后服务端是怎么返回数据的. 示例我们还是和上篇文章一样使用一样的bolt协议来讲: ...
- 7.源码分析---SOFARPC是如何实现故障剔除的?
我在服务端引用那篇文章里面分析到,服务端在引用的时候会去获取服务端可用的服务,并进行心跳,维护一个可用的集合. 所以我们从客户端初始化这部分说起. 服务连接的维护 客户端初始化的时候会调用cluste ...
- 4. 源码分析---SOFARPC服务端暴露
服务端的示例 我们首先贴上我们的服务端的示例: public static void main(String[] args) { ServerConfig serverConfig = new Ser ...
- 9.源码分析---SOFARPC是如何实现故障剔除的?
SOFARPC源码解析系列: 1. 源码分析---SOFARPC可扩展的机制SPI 2. 源码分析---SOFARPC客户端服务引用 3. 源码分析---SOFARPC客户端服务调用 4. 源码分析- ...
- HDFS源码分析之数据块及副本状态BlockUCState、ReplicaState
关于数据块.副本的介绍,请参考文章<HDFS源码分析之数据块Block.副本Replica>. 一.数据块状态BlockUCState 数据块状态用枚举类BlockUCState来表示,代 ...
- jQuery 源码分析(十) 数据缓存模块 data详解
jQuery的数据缓存模块以一种安全的方式为DOM元素附加任意类型的数据,避免了在JavaScript对象和DOM元素之间出现循环引用,以及由此而导致的内存泄漏. 数据缓存模块为DOM元素和JavaS ...
- Hadoop源码分析之数据节点的握手,注册,上报数据块和心跳
转自:http://www.it165.net/admin/html/201402/2382.html 在上一篇文章Hadoop源码分析之DataNode的启动与停止中分析了DataNode节点的启动 ...
随机推荐
- 【拓扑排序】威虎山上的分配-C++
威虎山上的分配 描述 每年过年的时候,座山雕都会给兄弟们分银子,分银子之前,座山雕允许大伙儿发表意见,因为要是没法满足所有人的意见,指不定谁要搞出什么大新闻.不过每个人在提意见的时候只能说:" ...
- <a>标签 IOS 安卓 亲测有效
一.普通链接 <a href="http://www.baidu.com">百度</a> 二.邮件链接 1.标签最简式 <a href="m ...
- python基础知识五 各类型数据方法补充,转换,分类,编码+坑中菜
3.9各类型数据方法补充,转换,分类,编码,坑中菜 3.9.1数据类型方法补充 1.str:不可变 补充方法 s1.capitalize():首字母大写 s1 = "alex" s ...
- Excel催化剂开源第1波-自定义函数的源代码全公开
Excel催化剂插件从2018年1月1日开始运营,到今天刚好一周年,在过去一年时间里,感谢社区里的许多友人们的关心和鼓励,得以坚持下来,并收获一定的用户量和粉丝数和少量的经济收入回报和个人知名度的提升 ...
- Android - ScrollView 使用小计 里面嵌套的View 如何设置全屏
设置ScrollView的属性android:fillViewport="true" 即可 <?xml version="1.0" encoding=&q ...
- c语言进阶2-变量的作用域与无参函数
一. 什么是函数 函数是具有特定功能的模块.可以说一个完整的程序其实是由多个函数共同完成的.C语言的全部工作都是由程式各样的函数完成的,所以也把C语言称为函数式语言.使用模块化设计可能 使 ...
- 快速掌握mongoDB(五)——读写分离的副本集实现和Sharing介绍
1 mongoDB副本集 1 副本集简介 前边我们介绍都是单机MongoDB的使用,在实际开发中很少会用单机MongoDB,因为使用单机会有数据丢失的风险,同时单台服务器无法做到高可用性(即当服务器宕 ...
- Python -----函数(基础部分)
函数: 1.定义: 函数是对功能的封装 2.语法: def 函数名 函数体 函数名 函数名的命名规则和变量一样 3.函数的返回值: return,函数执行完毕,不会执行后面的 1.如果函数中不写ret ...
- [学习笔记] pymysql入门
一.快速开始 对于会用MySQL的朋友来说,开始使用pymysql可以说真的so esay,只要用下面的代码,把想要对数据库的操作放在 sql = " " 里就可以了. 没有接触过 ...
- solidity的delete操作汇总
简介 Solidity中的特殊操作符delete用于释放空间,为鼓励主动对空间的回收,释放空间将会返还一些gas. delete操作符可以用于任何变量,将其设置成默认值0. 删除枚举类型时,会将其值重 ...