最近我们 dubbo-go 社区里面,呼声很大的一个 feature 就是对 gRPC 的支持。在某位大佬的不懈努力之下,终于弄出来了。

今天我就给大家分析一下大佬是怎么连接 dubbo-go 和 gRPC 。

gRPC

先来简单介绍一下 gRPC 。它是 Google 推出来的一个 RPC 框架。gRPC是通过 IDL ( Interface Definition Language )——接口定义语言——编译成不同语言的客户端来实现的。可以说是RPC理论的一个非常非常标准的实现。

因而 gRPC 天然就支持多语言。这几年,它几乎成为了跨语言 RPC 框架的标准实现方式了,很多优秀的rpc框架,如 Spring Cloud 和 dubbo ,都支持 gRPC 。

server 端

在 Go 里面,server 端的用法是:

它的关键部分是:s := grpc.NewServer()pb.RegisterGreeterServer(s, &server{})两个步骤。第一个步骤很容易,唯独第二个步骤RegisterGreeterServer有点麻烦。为什么呢?

因为pb.RegisterGreeterServer(s, &server{})这个方法是通过用户定义的protobuf编译出来的。

好在,这个编译出来的方法,本质上是:

也就是说,如果我们在 dubbo-go 里面拿到这个 _Greeter_serviceDesc ,就可以实现这个 server 的注册。因此,可以看到,在 dubbo-go 里面,要解决的一个关键问题就是如何拿到这个 serviceDesc 。

Client 端

Client 端的用法是:

这个东西要复杂一点:
1、创建连接:conn, err := grpc.Dial(address)
2、创建client:c := pb.NewGreeterClient(conn)
3、调用方法:r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})

第一个问题其实挺好解决的,毕竟我们可以从用户的配置里面读出 address ;

第二个问题就是最难的地方了。如同 RegisterGreeterServer 是被编译出来的那样,这个 NewGreeterClient 也是被编译出来的。

而第三个问题,乍一看是用反射就能解决,但是我们打开 SayHello 就能看到:

结合 greetClient 的定义,很容易看到,我们的关键就在于 err := c.cc.Invoke ( ctx, "/helloworld.Greeter/SayHello", in, out, opts... )。换言之,我们只需要创建出来连接,并且拿到方法、参数就能通过类似的调用来模拟出 c.SayHello 。

通过对 gRPC 的简单分析,我们大概知道要怎么弄了。还剩下一个问题,就是我们的解决方案怎么和 dubbo-go 结合起来呢?

设计

我们先来看一下 dubbo-go 的整体设计,思考一下,如果我们要做 gRPC 的适配,应该是在哪个层次上做适配。

我们根据前面介绍的 gRPC 的相关特性可以看出来,gRPC 已经解决了 codec 和 transport 两层的问题。

而从 cluster 往上,显然 gRPC 没有涉及。于是,从这个图里面我们就可以看出来,要做这种适配,那么 protocol 这一层是最合适的。即,我们可以如同 dubbo protocol 那般,扩展出来一个 grpc protocol 。

这个 gRPC protocol 大体上相当于一个适配器,将底层的 gRPC 的实现和我们自身的 dubbo-go 连接在一起。

实现

在 dubbo-go 里面,和 gRPC 相关的主要是:

我们直接进去看看在 gRPC 小节里面提到的要点是如何实现的。

server端

这样看起来,还是很清晰的。如同 dubbo- go 其它的 protocol 一样,先拿到 service ,而后通过 service 来拿到 serviceDesc ,完成服务的注册。

注意一下上图我红线标准的 ds, ok := service.(DubboGrpcService) 这一句。

为什么我说这个地方有点奇怪呢?是因为理论上来说,我们这里注册的这个 service 实际上就是 protobuf 编译之后生成的 gRPC 服务端的那个 service ——很显然,单纯的编译一个 protobuf 接口,它肯定不会实现 DubboGrpcService 接口:

那么 ds, ok := service.(DubboGrpcService) 这一句,究竟怎么才能让它能够执行成功呢?

我会在后面给大家揭晓这个谜底。

Client端

dubbo-go 设计了自身的 Client ,作为对 gRPC 里面 Client 的一种模拟与封装:

注意看,这个 Client 的定义与前面 greetClient 的定义及其相似。再看下面的 NewClient 方法,里面也无非就是创建了连接 conn ,而后利用 conn 里创建了一个 Client 实例。

注意的是,这里面维护的 invoker 实际上是一个 stub 。

当真正发起调用的时候:

红色框框框住的就是关键步骤。利用反射从 invoker ——也就是 stub ——里面拿到调用的方法,而后通过反射调用。

代码生成

前面提到过 ds, ok := service.(DubboGrpcService) 这一句,面临的问题是如何让 protobuf 编译生成的代码能够实现 DubboGrpcService 接口呢?

有些小伙伴可能也注意到,在我贴出来的一些代码里面,反射操作会根据名字来获取method实例,比如NewClient方法里面的method := reflect.ValueOf(impl).MethodByName("GetDubboStub")这一句。这一句的impl,即指服务的实现,也是 protobuf 里面编译出来的,怎么让 protobuf 编译出来的代码里面含有这个 GetDubboStub 方法呢?

到这里,答案已经呼之欲出了:修改 protobuf 编译生成代码的逻辑!

庆幸的是,在 protobuf 里面允许我们通过插件的形式扩展我们自己的代码生成的逻辑。

所以我们只需要注册一个我们自己的插件:

然后这个插件会把我们所需要的代码给嵌入进去。比如说嵌入GetDubboStub方法:

还有DubboGrpcService接口:

这个东西,属于难者不会会者不难。就是如果你不知道可以通过plugin的形式来修改生成的代码,那就是真难;但是如果知道了,这个东西就很简单了——无非就是水磨工夫罢了。

无缝衔接 gRPC 与 dubbo-go的更多相关文章

  1. 无缝衔接demo

    如题. <!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" con ...

  2. DJI Terra+EasyEarth让数据获取与应用无缝衔接

    大数据时代对地理信息数据的获取与应用提出了更高的要求: ⏩低成本.全要素.高效获取 ⏩数据采集.处理到应用无缝衔接 DJI Terra大疆智图 DJI Terra大疆智图是一款提供自主航线规划.飞行航 ...

  3. ASP.NET Core GRPC 和 Dubbo 互通

    一.前言 Dubbo 是比较流行的服务治理框架,国内不少大厂都在使用.以前的 Dubbo 使用的是私有协议,采集用的 hessian 序列化,对于多语言生态来说是极度的不友好.现在 Dubbo 发布了 ...

  4. Dubbo 在跨语言和协议穿透性方向的探索:支持 HTTP/2 gRPC

    Dubbo 在跨语言和协议穿透性方向上的探索:支持 HTTP/2 gRPC 和 Protobuf 本文整理自刘军在 Dubbo 成都 meetup 上分享的<Dubbo 在多语言和协议穿透性方向 ...

  5. Dubbo Spring Cloud 之 HTTP 实战

    上一篇文章<Spring Cloud Alibaba | Dubbo 与 Spring Cloud 完美结合>我们介绍了Dubbo Spring Cloud的基本使用,使用的服务中心为Sp ...

  6. dotnet core 之 gRPC

    dotnet core gRPC 原文在本人公众号中,欢迎关注我,时不时的会分享一些心得 HTTP和RPC是现代微服务架构中很常用的数据传输方式,两者有很多相似之处,但是又有很大的不同.HTTP是一种 ...

  7. dubbo使用和配置讲解

    1. 分布式系统中相关概念 1.1 互联网项目特点及目标 1.1.1 特点: 用户多 流量大.并发高 海量数据 易受攻击 功能繁琐 变更快 1.1.2 指标及相关目标 互联网项目三高目标:高并发.高可 ...

  8. Swift - 多层无缝循环滚动背景(SpriteKit游戏开发)

    在游戏开发中,比如跑酷游戏.我们需要实现背景的无限循环滚动,来营造运动的效果.除了单层的背景滚动,还有视差滚动. 视差滚动是指让多层背景以不同的速度移动,形成立体的效果,从而带来非常出色的视觉体验. ...

  9. 用CSS3实现无限循环的无缝滚动

    有时候在页面的某个模块中,需要无限循环的滚动一些消息.那么如果我们用js实现无缝衔接滚动的思路是什么呢(比如我们这个模块是向上滚动的)? 克隆A一份完全一样的数据B放在原数据A的后面: 使用setIn ...

  10. 基于 IJKPlayer-concat 协议的视频无缝拼接技术实现

    一.前言 Hi,大家好,我是承香墨影! 开门见山,开篇名义.今天来聊聊如何将多段视频,拼接成一个完整而连续的视频,然后无缝进行播放. 这样的需求应该不算偏门吧? 最简单的就是一些视频 App,会将大段 ...

随机推荐

  1. 基于python的定时PC定时录音机实现

    一 概念基础 这次用python实现一个定时录音机的功能,可以让你的i电脑秒变定时录音机. 这里用到了wave库,time库等.熟悉该源码,即可了解这些库的用法.   二 源码解析 1.录音函数,该函 ...

  2. VR虚拟现实原型制作-应用及解决方案的特点

    VR虚拟现实原型制作 利用VR虚拟现实软件进行原型制作可以用于增强原型测试期间的沉浸感,减少产品设计迭代次数,并将与产品原型制作相关的成本降低40-65%. VR虚拟现实原型制作市场规模 用于原型制作 ...

  3. 计算机网络-DNS以及FastGitHub

    前言 你是否观察到过这种现象,在访问Github时,有的时候能正常访问,有的时候再次刷新就访问不了,那么能不能有什么办法能一直访问.答案是有,就是在DNS层面能保证一直稳定获取可用并且快速的IP,这就 ...

  4. python基础九(模块和包)

    一 模块介绍 1.什么是模块? 模块就是一系列功能的集合体,分为三大类 I:内置的模块(python解释器内置) II:第三方的模块 III:自定义的模块 一个python文件本身就是一个模块,文件名 ...

  5. 反射改变TabLayout属性

    目录介绍 01.遇到的实际需求分析 02.原生TabLayout局限 03.TabLayout源码解析 3.1 Tab选项卡如何实现 3.2 滑动切换Tab选项卡 3.3 Tab选项卡指示线宽度 04 ...

  6. 记录--vue3中的ref,toRef,toRefs

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 1. ref的使用 ref 接受一个原始值,返回一个具有响应式的对象,对象有一个value属性,其值就是所传递的原始值. ref是做的一个 ...

  7. 开发进阶系列:Java并发之从基础到框架

    一  线程基础 1.synchronized取得的锁都是对象锁,哪个线程执行synchronized修饰的方法,哪个线程就获得这个方法所属对象的锁.不同对象不同锁,互不影响. 另一种情况是static ...

  8. 一些 IL 语言解释

    跳转指令集合 Public field Static     Beq     如果两个值相等,则将控制转移到目标指令. Public field Static     Beq_S     如果两个值相 ...

  9. hadoop集群实现分发文件命令xsync脚本文件

    1 #!/bin/bash 2 3 #1. 判断参数个数 4 if [ $# -lt 1 ] 5 then 6 echo Not Enough Arguement! 7 exit; 8 fi 9 10 ...

  10. #Raney引理,圆排列#洛谷 6672 [清华集训2016] 你的生命已如风中残烛

    题目 分析 转化一下条件,就是 \(\sum{w_i}\geq i\),将所有牌权值减一,那就是 \(\sum{w'_i}\geq 0\) 根据Raney引理,总和为 1 的数列,在循环移位时,只有一 ...