也就是说,thrift通过key-value保存了我们实际将要运行的函数,最终通过handler来执行。
这里就有点像我们使用golang系统中的http包中的ListenAndServer()函数时,提前通过Handfunc来设置好函数路由是一个意思。
再看看Serve()函数是如何实现的:
func (p *TSimpleServer) Serve() error {
err := p.Listen()
if err != nil {
return err
}
p.AcceptLoop()
return nil
}
func (p *TSimpleServer) AcceptLoop() error {
for {
client,err := p.serverTransport.Accept()
if err != nil{
//.......被我删掉
}
if client != nil {
go func() {
if err := p.processRequests(client); err != nil {
log.Println(error processing request:
Serve()函数负责监听连接到服务器上的client,并且通过processRequests()函数来处理client。实际处理过程中,服务器会获取client的processor,然后进一步处理client的请求。这部分先暂停一下,我们来分析一下client端的工作原理,之后再回过头来看看会比较清晰一些
首先我们架设client端如下,并且通过client端来发送一个SayHi的操作:
transport,err := thrift.NewTSocket(net.JoinHostPort("127.0.0.1","8808"))
if err != nil {
//...
}
protocolFactory := thrift.NewTBinaryProtocolFactoryDefault()
client := NewRpcServiceClientFactory(transport,protocolFactory)
if err := transport.Open(); err != nil {
...
}
defer transport.Close()
res,_ := client.SayHi("wordl")
现在问题来了,这个SayHi是如何通知给服务器的呢?不急,看源码
在我们调用thrift --gen go XXX命令的时候,thrift已经给我们生成了SayHi过程的代码,如下:
func (p *RpcServiceClient) SayHi(name string) (r if err = p.sendSayHi(name); err != nil {
return
}
return p.recvSayHi()
}
其中RpcServiceClient类型就是我们的client,可以看到先调用了一个sendSayHi,如果没有错误的话,又调用了一个recvSayHi。
其实sendSayHi就是我们通知服务器执行SayHi()函数的关键,而recvSayHi是接受服务器的执行结果的。
一起看下sendSayHi是如何实现的(代码被我精简,这保留了关键部分,完整代码可以自己通过thrift命令生成查看)
func (p *RpcServiceClient) sendSayHi(name string) (err error) {
oprot := p.OutputProtocol 获取传输协议
if err = oprot.WriteMessageBegin(",thrift.CALL,p.SeqId); err != nil { 发送SayHi字符创,告诉服务器将来执行的函数
return
}
args := RpcServiceSayHiArgs{ 构建参数
Name: name,}
if err = args.Write(oprot); err != nil { 将参数发送给服务器
if err = oprot.WriteMessageEnd(); err != nil { 通知服务器发送完毕
return oprot.Flush()
}
通过这样的一系列数据传输,服务器通过路由解析,便可以正确的知道该执行哪个函数了。thrift的精髓也正在此,实现了rpc架构,客户端只需要简单的调用client.SayHi(),不必知道这是本地调用还是远程调用。
好了,既然请求发出了,我们现在当然看看服务器是如何响应的,在源码中,有一个函数是专门响应客户端请求的:
func (p *RpcServiceProcessor) Process(iprot,oprot thrift.TProtocol) (success bool,err thrift.TException)
前面讲解服务器端是如何创建的时候讲到过一个processRequests()函数,它在client连接上server的时候会被server调用。我们看看源码:
func (p *TSimpleServer) processRequests(client TTransport) error {
processor := p.processorFactory.GetProcessor(client) ....
for {
ok,err := processor.Process(inputProtocol,outputProtocol)
if err{
....
}
}
return nil
}
在去除无关代码之后我们看到,服务器首先获取客户端的processor,然后调用processor的Process函数,从而执行响应客户端的请求。
看看Process函数具体是如何实现的:
func (p *RpcServiceProcessor) Process(iprot,255); line-height:1.5!important">bool
,err thrift.TException) {
name,_,seqId,err := iprot.ReadMessageBegin() 获取客户端请求执行的函数名称
return false,err
}
if processor,ok :=
p.GetProcessorFunction(name); ok {
return processor.Process(seqId,iprot,oprot)
执行
}
...
}
要注意的是,函数中用红色标注的Process是另外一个函数,这里可不是递归。两个Process函数的声明是不一样的:
RpcServiceProcessor是server的processor
func (p *rpcServiceProcessorSayHi) Process(seqId int32,err thrift.TException) rpcServiceProcessorSayHi是具体的handler,
现在到了最关键的时候了,我们看看handler是如何执行process的:
func (p *rpcServiceProcessorSayHi) Process(seqId int32,err thrift.TException) {
args :
= RpcServiceSayHiArgs{}
构建参数
if err = args.Read(iprot); err != nil {
读取客户端发来的参数
处理err
}
iprot.ReadMessageEnd() 读取客户端的结束消息
result :=
RpcServiceSayHiResult{}
var retval
string
var err2 error
if retval,err2 =
p.handler.SayHi(args.Name); err2 != nil {
执行函数
..处理err
}
else {
result.Success = &
retval
}
...将result发送给客户端,流程和client发送请求类似,client通过recvSayHi()函数接受result
true,err
}
现在,服务器和客户端究竟是如何工作的。你明白了吗 (编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!