Origin源码阅读笔记——RPC

github地址:go 分布式微服务框架 Origin

初识Origin:https://blog.csdn.net/u013645668/article/details/115876646

1、RPC接口注册

        全局搜索“RPC_”会找到rpchandler.go文件里的RpcHandler.suitableMethods函数,会发现它的工作就是利用反射保存接口的参数(in/out/resp)类型信息,有了这个类型信息之后,RPC Server收到请求时才知道如何解析参数信息。

        这里没啥特别之处,需要对golang的reflect有一定的了解才能看懂,特别是这段:

	//1.判断第一个参数
	var parIdx = 1
	if typ.In(parIdx).String() == "rpc.RequestHandler" {
		parIdx += 1
		rpcMethodInfo.hasResponder = true
	}

        为啥是从第1个开始,而不是第0个开始?

        因为我们的RPC_开头的接口都是有一个Service接收者的,这个接收者就类似c++的this,它才是Method.In的第1[0]个参数。

2、RPC(Client)发起调用

        以Service里最常用的RPC调用接口AsyncCall为例,跟踪到rpchandler.go的asyncCallRpc接口。

        asyncCallRpc这里主要是做callback合法检测,然后利用反射创建callback返回值的实例的接口对象,用于存储返回结果。然后调用了rpc client的AsyncCall

        AsyncCall:

        1).创建一个唯一序号seq

        2).连同seq和RPC的调用参数一起打包序列号,并通过网络发送给服务端

        3).本地存储调用记录,包含序列号seq和前面callback的replay接口对象,和callback地址

显而易见,这个seq序号将是rpc server返回结果时查找对应的请求者需要的。

3、RPC(Server)处理调用、返回

看rpc/server.go里的  RpcAgent.Run,rpc 的tcp server 收到client的rpc请求后,

1).解析数据: 根据data[0]找到processor(pb/json/其他自定义),有了processor就可以对收到的数据初步解析了,以jsonprocessor为例,包含如下数据:

type JsonRpcRequestData struct {
	//packhead
	Seq           uint64             // sequence number chosen by client
	rpcMethodId   uint32
	ServiceMethod string   // format: "Service.Method"
	NoReply       bool           //是否需要返回
	//packbody
	InParam      []byte
}

        比较明了,除了这第二个rpcMethodId,它是用来处理raw类型rpc的,这里不深究它。

2). 创建req对象,保存调用信息seq,inParam,requestHandle(如果此次调用需要返回,就需要一个返回处理函数)

		if req.RpcRequestData.IsNoReply()==false {
			req.requestHandle = func(Returns interface{},Err RpcError){
				agent.WriteResponse(processor,req.RpcRequestData.GetServiceMethod(),req.RpcRequestData.GetSeq(),Returns,Err)
				ReleaseRpcRequest(req)
			}
		}

        不细看也能想到:将结果通过processor处理后发送给调用方,附带seq(这很重要,不然client不知道是哪个的结果)

3). 其中inParam需要进一步解析——从[]byte到对应的具体调用时的参数。用到rpcHandler.UnmarshalInParam:

	var err error
	param := reflect.New(v.inParamValue.Type().Elem()).Interface()
	err = rpcProcessor.Unmarshal(inParam,param)
	return param,err

第一步注册RPC接口时保存的类型信息的用处就在这里。

4).将此次rpc调用创建的req对象push进Server的Events队列,等待队列消费处理。查阅Service的代码,会看到Run()里面对event.ServiceRpcRequestEvent类型的事件的处理,最终会跳转到rpchandler.go的HandlerRpcRequest:

        里面也是用到反射:

        1).   用req.inParam生成paramlist

        2).   产生调用 v.method.Func.Call(paramList),这里实际调用的就是我们注册的“RPC_XXXX”

        3).   处理调用的返回,用到前面的 req.requestHandle

        

4、RPC (Client)接受返回结果

        看rpc/client.go的Run(),解读过server的Run, Client解析消息的细节就不说了。

        但是,在client.Run最下面,会看到Response——rpc的结果怎么通知到到调用者的:

			if v.callback!=nil && v.callback.IsValid() {
				 v.rpcHandler.PushRpcResponse(v)
			}else{
				v.done <- v
			}

        这里分两种情况,异步和同步调用

 1)、异步调用的会有callback,这里将整个调用信息对象req存进event队列,等待队列有序pop,然后调用rpchandler.go的HandlerRpcResponseCB,调用req.callback。 那么问题来了:这里为什么不直接调用req.callback, 而要把req push到Service.events队列后再pop出来处理呢?

        答案:让callback执行时回归原协程

 2)、同步调用,向done 这个channal对象写入包含调用结果的req对象,以解除Call的调用等待

5、深入思考

a. 大量临时对象的创建是否会增加GC压力

这点作者做了预防:

var rpcRequestPool = sync.NewPoolEx(make(chan sync.IPoolData,10240),func()sync.IPoolData{
	return &RpcRequest{}
})

var rpcCallPool =  sync.NewPoolEx(make(chan sync.IPoolData,10240),func()sync.IPoolData{
	return &Call{done:make(chan *Call,1)}
})

采用了缓存池,其中这里的rpcRequestPool, Client和Server都有用到

b.使用reflect(反射)对效率的影响

对于反射对性能的影响和优化不太清楚的可以看看这篇文章:Golang 反射性能优化

然后针对Origin,运行期利用到反射的,主要是 reflect.New(), 在以下三处

请求时:

reply := reflect.New(fVal.Type().In(0).Elem()).Interface()

被调用时:

param := reflect.New(v.inParamValue.Type().Elem()).Interface()
oParam = reflect.New(v.outParamValue.Type().Elem())

这个还是会有一定的性能影响的,但是照目前的每秒大几万的rpc处理速度,一般项目都够用了,如果究极考虑性能,可以参考上面说的那篇文章用pool减少reflect.New对性能的影响。或者考虑service协程数量,创建对应个数的reply/inParam/outParam实例,在每个协程复用。

总结:

RPC,老生常谈的问题,熟悉一点的人就会知道,RPC离不开会话标识、请求参数和返回结果打包方式(pb/json/其他)、网络通信方式(tcp/http2)这些细节。知道事物运行的本质了,看源码也会轻松许多。

PS:本篇博客未对Origin RPC功能中本节点调用和rpc原生转发做解读,有兴趣的可以自行了解。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回答: "strict-origin-when-cross-origin"是一个浏览器的引用策略。根据Chrome的计划,在85版开始,它将成为默认的引用策略。对于同源的请求,它会发送完整的URL作为引用地址。在同等安全级别的情况下,它会发送文件的源作为引用地址(HTTPS->HTTPS)。在降级的情况下,它不会发送此首部(HTTPS->HTTP)。\[3\]这个引用策略的目的是提高安全性和隐私保护。如果你在使用Vue前端部署到Nginx服务器后,浏览器访问资源时出现了"strict-origin-when-cross-origin"的报错,可能是因为跨域问题。你可以通过配置网关来解决这个问题。\[2\]具体的解决方案可能因具体情况而异,你可以参考相关文档或寻求专业的技术支持来解决这个问题。 #### 引用[.reference_title] - *1* [记录一次strict-origin-when-cross-origin的错误](https://blog.csdn.net/weixin_43405946/article/details/118221871)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [谷歌浏览器strict-origin-when-cross-origin 解决](https://blog.csdn.net/weixin_48687496/article/details/123749167)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [解决 strict-origin-when-cross-origin 问题](https://blog.csdn.net/qq_43371556/article/details/127848820)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值