Skip to content
Snippets Groups Projects
Commit 225017c2 authored by Xin.Zh's avatar Xin.Zh Committed by GitHub
Browse files

Merge pull request #776 from zhangymPerson/docs

Add: dubbo-go docs
parents d57d8853 bbcab834
No related branches found
No related tags found
No related merge requests found
Showing
with 2885 additions and 0 deletions
# [Go 版本入 Dubbo 生态一周年:已和 Spring Cloud、gRPC 互通](https://www.oschina.net/question/4489239_2316774)
去年 5 月,阿里开源的高性能 RPC 框架 Dubbo 从 ASF 毕业并晋升顶级项目,同时,还宣布 Go 语言版本的 Dubbo-go 正式加入 Dubbo 官方生态。
经过一年的发展, Dubbo-go 在技术和社区运营方面都已经有了不错的成绩。Dubbo-go 是 Dubbo 的完整 Go 语言实现,在功能实现和技术路径上与 Dubbo 有不同程度的对标,项目团队预计很快便可以追平 Java 版的功能。当然,也是因为基于 Go 语言开发,Dubbo-go 更易上手,未来或将反哺 Dubbo 的云原生化。
Dubbo-go 近期还实现了 REST 协议以及 gRPC 的支持,打通了 Spring Cloud 和 gRPC 生态,再加上与 Java Dubbo 的互通,应用场景广泛。因此,它被其开发者叫做“all-in-one”的 RPC 框架。
目前 Dubbo 官方已经投入人力参与 Dubbo-go 的开发,阿里集团今年完成 HSF 和 Dubbo 的融合后,会在集团内逐步推广使用 Dubbo-go。
开源中国采访了当前正在开发中的 v1.5 版本的主要推进者邓明,回顾 Dubbo-go 的过往,尤其是最近一年的发展情况,并展望项目未来的发展。
## Dubbo-go 过去发展回顾
**OSCHINA:**
作为项目主要推动者之一,可以简单回顾下 Dubbo-go 的发展历程吗?
**Dubbo-go 邓明:**
首先,个人代表社区,借助贵方平台,感谢 Dubbo-go 的使用者、曾经合作过的各个媒体平台以及 Dubbo 官方过去一年来对我们项目的关注,Dubbo-go 目前的发展离不开各方力量的帮助。
实际上,在 Dubbo-go 加入 Dubbo 官方生态之前,已经发展了两年。它最早由其创始人于雨在 2016 年 5 月构建,同年 9 月发布并开源的。如下时间轴图清晰记录了 Dubbo-go 的前世今生。
![dubbo-go 的时间轴](../../pic/arch/dubbo-go-one-year-1.png)
**OSCHINA:**
在去年项目刚加入 Dubbo 官方生态的时候,有开发团队成员说,Dubbo-go 当时还没能发挥出 Go 语言的优势,功能完整性还要完善。作为一个为解决 Go 项目与 Java & Dubbo 项目互通的项目,经过一年的发展,项目现在能发挥出 Go 语言的优势了吗,为什么?
**Dubbo-go 邓明:**
和去年比起来,在发挥 Go 语言自身优势上,有了很大的提高。
Go 语言协程的个数上限比 Java 线程数目多。Go 语言的协程只运行在用户态,初始堆栈小且可伸缩,而 Java 线程启动因用户态系统态之间切换带来的额外成本被线程池抹平,所以只有在较大并发需求的场景下(核数限制的情况下,Java 线程池中最大线程数被限制),才会发挥优势。Dubbo 中类似的场景:异步处理网络和协议化的处理。我们在网络库 Getty 中加入了协程池,实现了网络收发和逻辑处理的解耦。
另外,Go 语言上手速度确实比 Java 快好几个数量级,只要搭好具有良好扩展性的架子,社区 contributor 培养的成本比 Java 低很多。得益于此,Dubbo-go 的功能和性能将很快追平 Java。
\***\*OSCHINA:**
\*\*关于 Dubbo-go 在 Java 和 Go 运行时的兼容互通和功能一致目标,目前进展如何?
**Dubbo-go 邓明:**
目前,Dubbo-go 已经完全对齐 Dubbo v2.6.x,正在全力开发 v1.5.0 版本可以全面对齐 v2.7.x。
Dubbo v2.7.5 之后开始支持应用维度的服务注册,这也是 v1.5.0 计划支持的核心特性。
可以剧透一下,目前 v1.5.0 版本的 Dubbo-go 开发工作已经进入了尾声,正处于测试阶段。等 v1.5.0 发布之后,我们会陆续发布几个小版本,用于对齐 Dubbo v2.7.5 之后的版本。可以说,v1.5.x 主要是为了配合 dubbo 的云原生化。
\***\*OSCHINA:**
\*\*Dubbo-go 近期实现了 REST 协议支持,可以和 Spring Cloud 生态互联;年初实现了和 gRPC 的互联,这对 Dubbo-go 有什么意义?
**Dubbo-go 邓明:**
Dubbo-go 在支持了 REST 协议之后,已经可以做到跟绝大部分基于 HTTP 协议的微服务框架进行通信。
![REST 总体设计](../../pic/arch/dubbo-go-one-year-2.png)
[REST 总体设计]
另外一个突出优点是,支持了 gRPC 和 REST 之后,Dubbo-go 就可以考虑和一些公司内部自研的框架进行通信了。通常一些比较大的公司会自研框架,或者深度定制某些开源框架。而只要它们支持 gRPC 或者 HTTP 协议,Dubbo-go 就可以保证与这些框架的无缝衔接。
还有一个优势,REST 协议对前端更友好,可以直接把 Dubbo-go 开发的服务给前端用,而不用加一层协议转换,也避免了前端直接发起 RPC 请求。因此,Dubbo-go 也就可以成为它们在 Go 微服务框架的一个比较优秀的选择。
**OSCHINA:**
1.4 版本中,Dubbo-go 在可观测性方面采用了 tracing 和 metric,metric 的实现参考了 Dubbo 的做法,也做了一些调整,具体是怎么样?
**Dubbo-go 邓明:**
可观测性是衡量一个微服务框架的重要方面。一般可观测性分成 tracing、metric 和 log 三个部分。
![可观测性](../../pic/arch/dubbo-go-one-year-3.png)
在 v1.4 Dubbo-go 之前,tracing 和 metric 是 Dubbo-go 的薄弱环节。为了支持这两个,我们考察了比较多的开源框架的做法。我们发现,因为要考虑对接非常多诸如 zipkin/cat 等监控框架,所以它们往往会设计一整套监控和度量的 API。
Dubbo 也是如此。Dubbo 的 metric 比较依赖阿里内部一个开源的 metric 的项目。这个项目也不是只能给 Dubbo 应用,而是 Java 项目都可以考虑。本质上来说,它定义了一套 API,而后提供了对不同开源框架的适配实现。把握住这一核心之后,我们就要考虑一个问题:要不要自己设计一套 API?我们的答案是 NO,并且选择了 opentracing API 作为我们监控使用的 API。
首先,我们回顾那些自己设计了 API 的开源项目,它们的 API 和 opentracing API 还比较相像。我觉得我设计不出来一个明显比 opentracing API 更加优雅的 API 了。
另外从实现效率上来说,如果我们使用 opentracing API 作为 Dubbo-go 接入 metric 和 tracing 的 API,那么,任何支持 opentracing API 的框架,都可以做到开箱即用。
目前我们正在向社区用户征集监控意见,看社区希望我们在框架内什么地方进一步埋点。我们也得到了很多反馈,下一步就要考虑进一步优化采集的数据。
**OSCHINA:**
Dubbo-go 的开发团队之前介绍,Dubbo-go 首要目的就是解决 Go 项目与 Java & Dubbo 项目的互通问题,同时也为 Go 项目提供了一种 RPC 与微服务开发框架的选择。但从之前的用户使用列表来看,直接把它作为 Go 的一个 RPC 框架来使用的好像并不是特别多,现在情况是这样吗?
**Dubbo-go 邓明:**
这个情况已经有了很大的改善了。最开始的时候,我们的用户大部分都是 Java Dubbo 那里过来的。但是到今年,据我们了解,已经有一些用户已经是直接把 Dubbo-go 作为 RPC 框架。在经过一年的发展以后,即便不考虑和 Dubbo 保持兼容这一特点,Dubbo-go 也可以说一个比较优秀的 Go 语言 RPC 框架。
尤其是在异构系统通信和服务治理方面,我们提供了非常多样化的支持。这是很多别的 RPC 框架所不具备,或者不如我们的。
**OSCHINA:**
总结一下这一年里,Dubbo-go 技术方面值得了解的进展吧?
**Dubbo-go 邓明:**
Dubbo-go 这一年的进步很大,实现了非常多非常重要的特性。
首先要提及的就是支持了很多协议,比如说基于 protobuf 的 gRPC 和 REST。这些协议保证了我们能够与市面上大多数的 RPC 框架进行通信,而且我们在 1.5.0 里面,还扩展支持支持基于 Json 的 gPRC 和 基于 protobuf 的 TCP 通信。
第二则是支持了配置中心。这个功能可以提供给用户极大的配置上的灵活性。
第三则是可观测性上改进,也就是前面提到的 metric 和 tracing。
第四则是现在正在进行的应用注册模型,它能让我们更好地拥抱 k8s 和 servise mesh。为了支持应用注册模型,我们还实现了一个元数据中心,这个元数据中心非常有利于做网关。此外还实现了很多功能,如新的限流算法,负载均衡算法和路由策略等。具体内容,欢迎大家去看我们的 release log。
![Dubbo-go 总体架构图](../../pic/arch/dubbo-go-one-year-4.png)
[Dubbo-go 总体架构图]
Dubbo-go 生态建设
**OSCHINA:**
上个月,Go 官方公布的最新调查报告显示,Go 语言的主要用途包括编写 RPC 服务,其次库和框架方面增量巨大。“竞争对手”变多会影响到 Dubbo-go 原本的计划实施吗,Dubbo-go 和其他同类项目比有什么不同?
**Dubbo-go 邓明:**
我们对 Go 社区的进步感同身受。实际上,Dubbo-go 这一年很多功能的实现,都离不开合作社区的支持。比如说我们提供的基于 Nacos 的配置中心支持,以及现在正在测试基于 Nacos 的应用维度服务注册与发现,都十分依赖 Nacos 的 Go 语言 SDK 支持。
而且我们也注意到,别的 Go 语言的微服务框架在这一年也取得了不错的进步,这是一种很好的鞭策。在 RPC 框架上,一直都是百家齐放百花争鸣局面,迫使我们朝着“人无我有,人有我精”的方向前进。到目前来说,我们感觉我们的竞争优势还是比较明显的:
第一点就是保持了和 Dubbo 的兼容,那么原本的 Dubbo 用户在考虑 Go 语言框架的时候,我们就会是首选;
第二个竞争优势则是支持多协议。这几年一个很明显的趋势就是,一个公司的技术栈难以保持单一,因为不同框架、不同语言会有不同优点。所以 Dubbo-go 也会是那些考虑连接异构系统用户的首选;
第三则是软实力,也就是我们社区自身的优点。我们社区非常有活力,用户有什么问题都能够得到很快的响应;而我们的迭代速度,一直比较快。如果用户觉得自己能够很快获得帮助,那么他们也会倾向选择我们。
**OSCHINA:**
我们了解到,Dubbo-go/getty 是 Dubbo-go 中比较能体现 Go 语言优势的部分,目前已经被解耦出来,可以直接用。Dubbo-go 的其他组成部分会考虑同样解耦吗?
**Dubbo-go 邓明:**
这可以说是一个非常长远和理想化的计划了。我们现在正在做的一件事,是把项目里面用的公共方法、和框架无关的代码抽取出来,做成一个工具类库,也就是 dubbogo-gost 这个项目。
我们注意到,不管是在 Dubbo-go,还是别的框架,这些代码都很类似,比如说对不同类型的数据排序。之前我们找过开源的 lib,但是都不尽如人意,所以我们打算把自己的拿出来,做成类似瑞士军刀一样小巧高效的工具。
另外还要提到 dubbo-go-hessian2 开源仓库。我们可以自豪地说,这个库是 Go 里面对 hessian v2 协议支持最好的开源库。不仅仅是我们在用,阿里和蚂蚁金服也在用。我们也希望吸引更加多用户来使用,帮我们改进。
**OSCHINA:**
Dubbo-go 今年 3 月 25 日的新版本 1.4.0 中“拿出了使用 k8s 作为注册中心的解决方案”,选择性放弃 Service 功能,将元数据直接写入到 Pod 对象的 Annotations 中。为什么做出这个决策,后续有什么落地计划?
**Dubbo-go 邓明:**
在使用 k8s 作为注册中心这个点上,讨论就花了很长的时间。
其实最初考虑的是直接使用 k8s 服务发现模型,后来发现 k8s service 和 Dubbo-go Interface 之间存在一些难以调和的矛盾。比如说 Kubernetes 已经为其承载的服务提供了⼀套服务发现,服务注册,以及服务集群管理机制。⽽ Dubbo-go 也拥有⾃成体系的服务集群管理。
这两个功能点形成了冲突,在无法调和两者的情况下,我们放弃了这个计划,并且提出了现在这个随 1.4.0 版本发布使用的模型。
![k8s registry 设计方案](../../pic/arch/dubbo-go-one-year-5.png)
后续,我们将主要考虑 k8s 本身提供的 CRD + Operator 的方案,毕竟越来越多的 k8s 周边的项目都在以 Operator 作为切入点。Dubbo-go 社区后续的方案将会以 CRD 的形式在 k8s 内注册和发现服务。这样做的原因有很多,首先是为了减少 Dubbo-go 对 kube-apiserver 的直接依赖。其次是为了标准化注册模型,当服务模型以 CRD 的形式存在在 k8s 集群中之后,其他围绕 k8s 的项目可以直接使用这些资源二次开发和拓展。而这种方式更加 CloudNative。
**OSCHINA:**
Dubbo-go 现在在云原生应用上的布局是怎样的?
**Dubbo-go 邓明:**
社区的主要人力正与蚂蚁金服的 mosn 社区展开合作。目前有 5 个人力与 mosn 社区一起在 mosn 中实现 Dubbo 的服务发现、服务注册和基本的 RPC 通信等数据平面的能力,在 istio 层面支持通过 XDS 实现配置下发,以实现 mosn + Dubbo-go 【嵌入 mosn】 + istio 这种 sidecar 形式的云原生方案。已完成的工作已经在多点科技展开测试,近期 mosn 社区同学会在 A2M 大会上公布具体进展。
除了 sidecar 这种 proxy 形式的云原生方案,社区还计划实现 Dubbo-go【应用 sdk】 + istio 这种 proxyless 方式的云原生方案。Java 应用或者 Go 应用通过 istio 的 xDS 协议完成服务注册和发现以及路由分发。或者说,我们力求微服务和云原生共存,可以称之为“双模微服务”。这种“双模微服务”允许标准的 Dubbo-go + sidecar 和 Dubbo-go【应用 sdk】 + istio 两种模式部署的应用共存。这将是 Dubbo-go v1.6 的核心工作。
**OSCHINA:**
Dubbo-go 几乎是刚一诞生就转移到 Apache,并且很快发布了 Apache Dubbo Go v1.1.0,这对社区运营的帮助是什么,可以分享下 Dubbo-go 的运营情况和经验吗?
**Dubbo-go 邓明:**
可以说,Apache 基金会对我们的帮助是很大的。
一方面,Apache 自身的光环十分有助于吸引开发关注和参与;另外一方面,Apache 的一些要求,也让社区运营更加规范。
社区运营需要进一步规范化,透明化,以吸引更加多的人参与。我注意到很多优秀的社区运营做得很好,他们对 issue 的管理很细致,打上了各种标签,做到了对 issue 的轻重缓急的管理。这种标签能够很容易吸引一些打算尝试开源的新人,给社区带来新的血液。
我们尝试使用 milestone 的方式来管理 Dubbo-go 的整体进度。现在也在尝试定期召开社区会议,讨论社区发展方向,重大特性的设计,以及解决争端会议会面向整个社区,想参与的人都可以参与。
Dubbo-go 应用及规划
**OSCHINA:**
Dubbo-go 适合什么样的企业和场景?
**Dubbo-go 邓明:**
我们认为,如果用户需要一款 Go 语言方面 gRPC 框架,可以考虑 Dubbo-go;如果公司有和异构系统通信的需求,Dubbo-go 也是一个比较好的选择。特别是,公司内部还有 Java Dubbo 或者 Spring Clound 之类的应用,那么 Dubbo-go 优势就更加大了。
Dubbo-go 可以说是 " all-in-one " 性质的 RPC 框架,自身包含服务治理等功能,非常省时省力,而且能够降低使用微服务的门槛。
**OSCHINA:**
GitHub 的用户列表中已经有来自 14 家企业的使用记录,Dubbo-go 一般会提供哪些后续支持?
**Dubbo-go 邓明:**
我们一直都快速响应用户的问题,而且积极鼓励用户参与到 Dubbo-go 的开发中来。目前涂鸦智能、携程等几家用户已经成为了社区贡献的主要力量。
有时候用户来做调研,进来社区咨询问题的时候,我们都会笑称他“如果选择了 Dubbo-go,就选择了一个强大的售后团队”。
社区一位很活跃的 Contributor 潘总【github id: pantianying】对我们的热情服务应该深有体会。比如他会提 issue,然后我们也会很快解决像 router、优雅退出功能就是在潘总提出之后,我们很快实现的, 还有早期一次重构之后,也是潘总尝鲜试用。尝鲜版通常有很多 BUG,所以我们都是上班打工,下班给潘总修 BUG,也算是服务周到热情用心了。
额外说下,目前 Dubbo 官方也已经投入人力参与 Dubbo-go 的开发,阿里集团今年完成 HSF 和 Dubbo 的融合后,会在集团内逐步推广使用 Dubbo-go。
**OSCHINA:**
Dubbo-go 下一版本预计什么时候发布,有没有一些长远的发展计划?
**Dubbo-go 邓明:**
当前正在测试的是 v1.5 版本,希望六月份能发出来。v1.6 版本正在设计和规划中,v1.6 是和 Dubbo 3 对齐的,所以也会是一个比较大的版本。
今年我们社区主要集中在两件事上。第一件是云和云原生的支持,现在进行中的 v1.5 和 v1.6 都是围绕这一点的。今年几乎所有大的 feature 都是这方面的。
第二件事,则是进一步提高 Dubbo-go 的文档、注释和代码质量。坦白来说,Dubbo-go 的文档并不是特别好,尤其是用户文档。我们也收到了很多用户的批评正在加强 CI 和自动化测试这块。总而言之,文档与质量这件事是今年的头等大事。
**OSCHINA:**
最后,请介绍一下自己吧。
**Dubbo-go 邓明:**
说起来挺有意思的,就是我本身是业务开发,并不是传统意义上的中间件开发或者基础架构开发。我目前在 eBay 做支付业务的开发。eBay 是一个 WLB 的公司,也就是在 eBay 我才有了足够的业余时间,开始投入到了开源社区中。
Dubbo-go 是我第一个深入参与的开源项目,也是我第一次尝试将个人对分布式系统和微服务治理的理解付诸实践的项目。它是我的第一个“孩子”。
因为工作的关系,我算是 Dubbo-go 社区投入时间最多的人之一,为 Dubbo-go 开发了一些很有意思的特性,也因此成了 Apache committer。另外我还扮演了编辑的角色,帮社区小伙伴写的博客文章把把关,润润色。我也算是 Dubbo-go 的主要管理人员了,比如说 v1.5 这个版本就是主要由我推进的——这大概还是因为我时间比较多。
# [Dubbo-go 应用维度注册模型](https://xie.infoq.cn/article/a6a7e05f8a0d26c5f9f9bbd85)
**本文作者:邓明(Github flycash)蒋超(Github @Patrick0308)**
Dubbo 3.0 将至。其最重要的一点就是服务自省,其基础即是应用维度的注册模型,作为目前与 Dubbo 在功能上完全对齐的 Dubbo-go,已于 本年【2020 年】7 月份发布了其 v1.5.0 版本,实现了该模型,为年底实现与 Dubbo 3.0 对齐的新版本奠定了基础。
Dubbo-go 作为 Dubbo 的 Go 语言版本,因跨语言之故,二者针对同一模型的实现必然有较大差异,故本文注重讨论 Dubbo-go 社区自身对该模型的理解和实现,以及其与 Dubbo 之间的差异。
## 1 引语
在 v1.5 以前,Dubbo-go 注册模型都是以服务为维度的,直观的理解可认为其是接口维度。譬如注册信息,按照服务维度模型其示例如下:
```json
"com.xxx.User":[
{"name":"instance1", "ip":"127.0.0.1", "metadata":{"timeout":1000}},
{"name":"instance2", "ip":"127.0.0.2", "metadata":{"timeout":2000}},
{"name":"instance3", "ip":"127.0.0.3", "metadata":{"timeout":3000}},
]
```
这种模式的好处是不言而喻的,简单直观,提供了细粒度的服务控制手段。
而近两年,随着云时代的到来,这种模式就暴露了不足:
主流的注册模型都是应用维度的;
以服务维度来注册,那么规模与服务数量成正比,大规模集群之下,注册中心压力非常大;
## 2 Dubbo-go v1.5.0 的新注册模型
这次 Dubbo-go 支持了新的注册模型,也就是应用维度的注册模型。简单而言,在应用维度注册下,其注册信息类似:
```jsoon
"application1": [
{"name":"instance1", "ip":"127.0.0.1", "metadata":{}},
{"name":"instance2", "ip":"127.0.0.2", "metadata":{}},
{"name":"instanceN", "ip":"127.0.0.3", "metadata":{}}
]
```
在此模式之下,可以看到注册信息将会大幅度减少,集群规模只与实例数量相关。
与此同时,在实现这一个功能的时候,Dubbo-go 还希望保持两个目标:
对用户完全兼容,用户迁移无感知;
保持住原本服务粒度上精细控制的能力——即保留现有的服务维度的元数据;
因此 Dubbo-go 要着力解决以下几点:
目前 Consumer 的配置是以接口为准的,如何根据接口找到该接口对应的应用?例如,用户配置了 com.xxx.User 服务,那么,Dubbo-go 怎么知道这个服务是由哪个应用来提供的呢?
在知道了是哪个应用之后,可以从注册中心拿到应用的注册信息,如实例信息等;那怎么知道 com.xxx.User 服务自身的元数据呢?
为了解决这两个问题,在已有的注册模型的基础上,Dubbo-go 引入两个额外的组件:ServiceNameMapping 和 MetadataService。
前者用于解决服务-应用之间的映射,后者用于获取服务的元数据。
由此,Dubbo-go 的应用维度注册模型就变为:
![1.png](../../pic/course/dubbo-go-application-dimension-registration-model-1.png)
### 2.1 ServiceNameMapping
ServiceNameMapping 并不复杂。考虑到一般人在 Consumer 侧想要调用一个服务,其十有八九是知道这个服务是哪个应用提供的,于是 Dubbo-go 引入了新的配置项 provideBy
![1.png](../../pic/course/dubbo-go-application-dimension-registration-model-2.png)
当然,所谓 “十有八九”就是说有些时候确实不知道是服务是谁提供的,所以 Dubbo-go 还支持了基于配置中心的 ServiceNameMapping 实现。Dubbo-go 会用服务名作为 Key 从配置中心里面读出对应的应用名。这意味着, Provider 启动的时候,也会在配置中心将自身的 服务-应用名映射 写入配置中心。
### 2.2 MetadataService
MetadataService 稍微要复杂一点,有 remote 和 local 两种模式。
类似于前面的 ServiceNameMapping,Dubbo-go 提供了基于配置中心的 MetadataService 的实现,即 remote 模式。Provider 启动的时候,就会将服务的元数据写进去。
另外一种模式是 local 模式。Dubbo-go 可以直接将 MetadataService 看做是一个普通的微服务,而后由 Provider 所提供。类似于:
![1.png](../../pic/course/dubbo-go-application-dimension-registration-model-3.png)
由此带来一个问题:
既然 Dubbo-go 将 MetadataService 看做是一个普通的服务,那么 MetadataService 的元数据,Consumer 该怎么获得呢?这是一个典型的鸡生蛋蛋生鸡的问题。
Dubbo-go 的方案非常简单粗暴,Provider 启动的时候,不仅仅往注册中心里面写入应用本身的信息,还要把它的 MetadataService 信息写入。
这是一个应用的注册信息:
![1.png](../../pic/course/dubbo-go-application-dimension-registration-model-4.png)
本质上来说,应用维度注册信息 + 服务元数据 = 服务维度注册信息。或者说,应用维度注册,只是一种重新组织这些信息的方式。
## 3 差异与改进
Dubbo-go v1.5.x 对标 Dubbo 2.7.5,可以认为是参照 Dubbo 2.7.5 直接实现其 Go 源码,但是考虑到 Java 和 Go 之间的语言差异,导致二者之间的实现不可能完全对等。
### 3.1 修订版本号 revision 比对
Dubbo v2.7.x 在 MetadataService 注册时,会对其 provider 应用的所有服务接口的 hash 值做为修订版本号写入元数据中心,此 revision 是对所有接口的方法以及其参数总体的计算结果。其目的是减少 consumer 端到注册中心的拉取次数。
在 Go 中用的计算 revision 的 hash 算法与 Java 是不一致的,而且 Go 与 Java 的方法签名信息是不相同的,所以计算出来的 hash 值一定是不一样的。
此不一致会导致如果 Go 应用和 Java 应用同时发布同一个服务的时候,Go 服务和 Java 服务的修订版本号必定是不相同的,Consumer 需要分别缓存这两个修订版本的元数据。
### 3.2 应用注册时机
Dubbo-go v1.5.0 实现时,其中一个考量是全面向后兼容 v1.4.x。Dubbo-go v1.5.x 应用 consumer 既可以调用 Dubbo-go v1.4.x 应用的服务,也可以调用 Dubbo v2.6.x 应用的服务,当然也可以调用其对标的 v2.7.x 应用的服务。
为了达到兼容性,Dubbo-go v1.5.x 实现时面临一个问题:Dubbo-go provider 应用启动时有一个服务启动成功,把应用信息注册到元数据中心之后,就会把实例注册到注册中心,而 Dubbo 2.7.x 的 provider 应用则是在其所有服务接口的信息注册到元数据中心后才会注册实例!
这个问题的后果就是:Dubbo-go v1.5.0 的 provider 每次发布接口到元数据中心的同时,都会触发 Dubbo-go v1.5.0 / Dubbo v2.7.x 的 consumer 应用拉取 Dubbo-go v1.5.0 应用信息,当 provider 发布的服务过多时 consumer 侧性能损耗非常明显!
Dubbo-go 在 v1.5.1 中已经修复了这个问题,provider 在启动时先将其全部服务接口发布到元数据中心,然后注册实例到注册中心,减少了 consumer 拉取元数据的次数。
Dubbo-go 项目地址:[https://github.com/apache/dubbo-go](https://github.com/apache/dubbo-go) Dubbo-go 社区交流群:23331795
### 欢迎加入 dubbo-go 社区
有任何 dubbo-go 相关的问题,可以加我们的钉钉群 23331795 询问探讨,我们一定第一时间给出反馈。
![1.png](../../pic/course/practice-and-exploration-of-dubbo-go-4.png)
# [曹大谈 dubbo mesh : 在 MOSN 中玩转 dubbo-go](https://my.oschina.net/dubbogo/blog/4309475)
## service mesh 简介
service mesh 本身的理念并不复杂,就是将现代微服务应用的功能性与非功能性需求进行分离,并将非功能性需求下沉到应用的外部模块,从而使应用模块可以尽量聚焦于业务,不用关心诸如:服务发现、限流、熔断、tracing 这类非业务需求。下沉之后,相关的 service mesh 模块可以交由基础架构团队进行维护,使基础设施和业务能够完成解耦。
service mesh 设计一般划分为两个模块,控制面和数据面。可以通过下图来理解相应的职责:
![1.png](../../pic/course/mosn-dubbo-go-1.png)
对于应用来说,所有流量都会经过 service mesh 中的数据面进行转发。而能顺利转发的前提:数据面需要知道转发的目标地址,目标地址本身是由一些业务逻辑来决定的(例如服务发现),所以自然而然地,我们可以推断控制面需要负责管理数据面能正常运行所需要的一些配置:
需要知道某次请求转发去哪里:服务发现配置
外部流量进入需要判断是否已经达到服务流量上限:限流配置
依赖服务返回错误时,需要能够执行相应的熔断逻辑:熔断配置
开源界目前比较有名的主要是 istio,envoy 和 linkerd 这几个项目,今天我们来介绍一下蚂蚁推出的 service mesh 数据面项目:MOSN。
## MOSN 简介
MOSN 是蚂蚁金服出品的用 Go 语言实现的 service mesh 数据面,在蚂蚁内部已大规模落地,在开源过程中我们了解到外部用户有较多的 dubbo 用户,这些 dubbo 用户也希望能够享受 service mesh 社区的发展红利。同时可以针对自己公司的特殊业务场景,对 service mesh 的数据面进行一定的扩展。
谈到扩展,MOSN 使用 Go 编写的优势就体现出来了。相比 C++,Go 语言通过自带的内存分配器与 GC 实现了一定程度的内存安全,解放了程序员的心智。相比 C++ 编写的 envoy,无论是编程和问题定位都要轻松不少。
MOSN 同时提供了强大的 XProtocol 协议扩展框架,用户可以根据自己的需求编写自定义协议解析。如果你使用的是 SOFA/Dubbo/HTTP/HTTP2,那么 MOSN 已经为你准备好了现成的实现。开箱即用。
为了满足社区的需求,从今年 4 月开始,MOSN 社区与 dubbo-go 社区进行了深入的交流与合作。可能还有些同学对 dubbo-go 不太了解,简单介绍一下。
## dubbo-go 简介
dubbo 是阿里巴巴出品一个非常优秀的 Java RPC 框架,相比其它框架,有较为全面的服务治理功能。
dubbo-go 是 dubbo 的 Go 语言版本,该项目已进入 apache 一年有余,开发社区很活跃,版本发布节奏较快。功能上也基本和 dubbo 的 Java 版都对齐了。
![1.png](../../pic/course/mosn-dubbo-go-2.png)
对于喜欢 dubbo 的 gopher 来说,dubbo-go 是个不错的选择。使用它来构建整个公司的服务框架,省时省心。
## MOSN + dubbo-go 双剑合璧
service mesh 能够给微服务的整体架构带来很多好处,然而业界提供的全家桶方案不一定能很好地在企业内落地,有下面一些原因:
要求数据面和控制面一起上线,早期 istio 因为设计问题,有众多模块。会大幅增加企业的运维负担。
企业上 mesh 一定是渐进部署,不可能一次性让所有服务全部上。这样就会有在 mesh 中的应用和非 mesh 中的应用需要能够互通的需求。
蚂蚁的 service mesh 落地过程相对较为成功,值得参考。在内部落地时,首先只落地数据面,这样运维负担较轻,同时出问题时也容易排查。
但只落地数据面的话,有些控制面的功能我们就没有了,比如服务发现、路由配置订阅等等。因此在 MOSN 中,又额外对这些功能进行了支持(相当于目前的数据面单模块同时承载了数据面和控制面的部分功能)。当数据面稳定落地之后,再进行控制面的落地相对来说负担就小很多了。
上面的是蚂蚁内部的情况,在社区内,MOSN 借助 dubbo-go 的能力,已经实现了服务发现功能,用户应用启动、退出时需要和 MOSN 进行简单的交互,以完成服务的发布和订阅功能,下面是发布流程:
![1.png](../../pic/course/mosn-dubbo-go-3.png)
在应用启动时,访问本地的 MOSN http 接口,告知需要将本地的服务发布出去,MOSN 收到请求后对外发布 MOSN 的 ip 和端口到注册中心。这样监听本服务的 consumer 便可以从注册中心收到服务变更的通知,并将本实例加入到相应的 provider 列表。当应用退出时,需要主动进行 unpub。
这种情况下的改造成本:
- 对于业务方来说,只需要升级 sdk。
- 对于 sdk 维护方,在执行 sub/pub/unsub/unpub 时,需要增加一个开关判断,- 开关打开时,说明本实例已上 service mesh。请求相应的本地 MOSN 接口。
- 对于 mesh 提供方,只要做好配置和部署就可以了。
接入 MOSN 后,mesh 化和非 mesh 化的应用可以互通:
![1.png](../../pic/course/mosn-dubbo-go-4.png)
当然,开发过程也并不是一帆风顺的。在我们刚开始使用 dubbo-go 时,便遇到了 dubbo-go 的依赖与 MOSN 的依赖有冲突的问题:
![1.png](../../pic/course/mosn-dubbo-go-5.png)
Go 语言的 go mod 语义会“自作聪明”地认为 0.y.z 的外部依赖都是彼此兼容的,然而我们从 semver 规范可以学习到:
> Major version zero (0.y.z) is for initial development. Anything MAY change at any time. The public API SHOULD NOT be considered stable.
0.y.z 只是用来做初始的开发,本身 API 的变化就是很频繁的,依赖管理工具不应该自动去升级这些依赖。但是 Go 社区的人有不少人坚持 go mod 的行为正确,并且祭出 MVS 算法,表示碰到问题的只是不懂 Go 的哲学。
当前 Go 的依赖管理使得两个大项目发生依赖冲突时,比较难处理,我们只能根据实际情况去 go.mod 中去写一些 replace 逻辑,来锁定外部依赖库版本。
除了依赖问题以外,dubbo-go 最初的设计大量使用了 init 函数。init 函数用来实现一些初始化和依赖注入确实比较方便,但如果一个项目中的模块会被其它外部项目依赖时,init 则可能给我们造成麻烦,举个简单的例子:
```go
package x
var GlobalMap = map[string]int{}
package a
func init() {
x.GlobalMap["x"] = 1
}
package b
func init() {
x.GlobalMap["x"] = 2
}
```
如果我们在某个包中同时依赖 a 和 b,那么下面的两种写法,得到的结果是不一样的:
```go
package show
import (
"a"
"b"
"x"
)
func main() {
println(x.GlobalMap["x"])
}
```
```go
package show
import (
"b"
"a"
"x"
)
func main() {
println(x.GlobalMap["x"])
}
```
为了避免这些隐式的 init 行为,我们实际上是 fork 了 dubbo-go 并进行了少量修改的。当然,改动并不多,未来如果 dubbo-go 有更好的模块化方法的话,我们也可以很轻松地迁移回直接依赖 dubbo-go。
在 MOSN 集成 dubbo-go 的过程中,dubbo-go 社区的老哥们给予了我们大量的支持,贤哥(https://github.com/zouyx)帮助我们在 dubbo-go 中实现了之前不支持的 unsub/unpub 功能,并帮助我们解决了很多技术方面的问题。dubbo-go 社区负责人于雨( https://github.com/alexstocks )也在开发过程中提供了大量的信息和技术支持。有这两位的支持,MOSN 和 dubbo 的集成才顺畅无比,否则的话要多走很多弯路。
## 其它选择
除了本文提到的 MOSN + dubbo-go 集成方式,多点的陈鹏同学为我们提供了另一种思路进行集成,本文就不展开了,感兴趣的同学可以参考文末的资料 [3]。
## 本文作者
曹春晖,开源 MOSN committer,@cch123,蚂蚁金服系统部技术专家,主攻 Service Mesh 方向。个人技术网站 xargin.com,和他人合著《Go 语言高级编程》。
## 参考资料
[1]. https://jimmysong.io/blog/what-is-a-service-mesh/
[2]. https://mosn.io/zh/docs/concept/multi-protocol
[3]. https://mp.weixin.qq.com/s/mhHnH6ZDPPs6Gr0a20WGOw
### 欢迎加入 dubbo-go 社区
有任何 dubbo-go 相关的问题,可以加我们的钉钉群 23331795 询问探讨,我们一定第一时间给出反馈。
![1.png](../../pic/course/practice-and-exploration-of-dubbo-go-4.png)
### 最新活动
Dubbo-go ASoC 相关题目 ,参加详情 请点击
# [涂鸦智能 dubbo-go 亿级流量的实践与探索](https://my.oschina.net/dubbogo/blog/4306343)
dubbo 是一个基于 Java 开发的高性能的轻量级 RPC 框架,dubbo 提供了丰富的服务治理功能和优秀的扩展能力。而 dubbo-go 在 java 与 golang 之间提供统一的服务化能力与标准,是涂鸦智能目前最需要解决的主要问题。本文分为实践和快速接入两部分,分享在涂鸦智能的 [dubbo-go](http://github.com/apache/dubbo-go) 实战经验,意在帮助用户快速接入 dubbo-go RPC 框架,希望能让大家少走些弯路。
另外,文中的测试代码基于 dubbo-go 版本 [v1.4.0](https://github.com/apache/dubbo-go/releases/tag/v1.4.0)
## dubbo-go 网关实践
![1.png](../../pic/course/practice-and-exploration-of-dubbo-go-1.png)
dubbo-go 在涂鸦智能的使用情况如上图,接下来会为大家详细介绍落地细节,希望这些在生产环境中总结的经验能够帮助到大家。
### 背景
在涂鸦智能,dubbo-go 已经作为了 golang 服务与原有 dubbo 集群打通的首选 RPC 框架。其中比较有代表性的 open-gateway 网关系统(下文统一称 gateway,开源版本见 [https://github.com/dubbogo/dubbo-go-proxy](https://github.com/dubbogo/dubbo-go-proxy))。该 gateway 动态加载内部 dubbo 接口信息,以 HTTP API 的形式对外暴露。该网关意在解决上一代网关的以下痛点。
- `通过页面配置 dubbo 接口开放规则,步骤繁琐,权限难以把控。`
- `接口非 RESTful 风格,对外部开发者不友好。`
- `依赖繁重,升级风险大。`
- `并发性能问题。`
### 架构设计
针对如上痛点,随即着手准备设计新的 gateway 架构。首先就是语言选型,golang 的协程调用模型使得 golang 非常适合构建 IO 密集型的应用,且应用部署上也较 java 简单。经过调研后我们敲定使用 golang 作为 proxy 的编码语言,并使用 dubbo-go 用于连接 dubbo provider 集群。provider 端的业务应用通过使用 java 的插件,以注解形式配置 API 配置信息,该插件会将配置信息和 dubbo 接口元数据更新到元数据注册中心(下图中的 redis )。这样一来,配置从管理后台页面转移到了程序代码中。开发人员在编码时,非常方便地看到 dubbo 接口对外的 API 描述,无需从另外一个管理后台配置 API 的使用方式。
![1.png](../../pic/course/practice-and-exploration-of-dubbo-go-2.png)
### 实践
从上图可以看到,网关能动态加载 dubbo 接口信息,调用 dubbo 接口是基于 dubbo 泛化调用。泛化调用使 client 不需要构建 provider 的 interface 代码,在 dubbo-go 中表现为无需调用 config.SetConsumerService 和 hessian.RegisterPOJO 方法,而是将请求模型纯参数完成,这使得 client 动态新增、修改接口成为可能。在 [apache/dubbo-sample/golang/generic/go-client](https://github.com/apache/dubbo-samples/tree/master/golang/generic/go-client) 中的有泛化调用的演示代码。
```go
func test() {
var appName = "UserProviderGer"
var referenceConfig = config.ReferenceConfig{
InterfaceName: "com.ikurento.user.UserProvider",
Cluster: "failover",
Registry: "hangzhouzk",
Protocol: dubbo.DUBBO,
Generic: true,
}
referenceConfig.GenericLoad(appName) // appName is the unique identification of RPCService
time.Sleep(3 * time.Second)
resp, err := referenceConfig.GetRPCService().(*config.GenericService).
Invoke([]interface{}{"GetUser", []string{"java.lang.String"}, []interface{}{"A003"}})
if err != nil {
panic(err)
}
}
```
泛化调用的实现其实相当简单。其功能作用在 dubbo 的 Filter 层中。[Generic Filter](https://github.com/apache/dubbo-go/blob/master/filter/filter_impl/generic_filter.go) 已经作为默认开启的 Filter 加入到 dubbo Filter 链中。其核心逻辑如下:
```go
func (ef *GenericFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
if invocation.MethodName() == constant.GENERIC && len(invocation.Arguments()) == 3 {
oldArguments := invocation.Arguments()
if oldParams, ok := oldArguments[2].([]interface{}); ok {
newParams := make([]hessian.Object, 0, len(oldParams))
for i := range oldParams {
newParams = append(newParams, hessian.Object(struct2MapAll(oldParams[i])))
}
newArguments := []interface{}{
oldArguments[0],
oldArguments[1],
newParams,
}
newInvocation := invocation2.NewRPCInvocation(invocation.MethodName(), newArguments, invocation.Attachments())
newInvocation.SetReply(invocation.Reply())
return invoker.Invoke(ctx, newInvocation)
}
}
return invoker.Invoke(ctx, invocation)
}
```
Generic Filter 将用户请求的结构体参数转化为统一格式的 map(代码中的 struct2MapAll ),将类( golang 中为 struct )的正反序列化操作变成 map 的正反序列化操作。这使得无需 POJO 描述通过硬编码注入 hessain 库。
从上面代码可以看到,泛化调用实际需要动态构建的内容有 4 个,ReferenceConfig 中需要的 InterfaceName 、参数中的 method 、ParameterTypes、实际入参 requestParams。
**那么这些参数是如何从 HTTP API 匹配获取到的呢?**
这里就会用到上文提到的 provider 用于收集元数据的插件。引入插件后,应用在启动时会扫描需要暴露的 dubbo 接口,将 dubbo 元数据和 HTTP API 关联。插件使用方法大致如下,这里调了几个简单的配置作为示例,实际生产时注解内容会更多。
![1.png](../../pic/course/practice-and-exploration-of-dubbo-go-3.png)
最终获得的 dubbo 元数据如下:
```json
{
"key": "POST:/hello/{uid}/add",
"interfaceName": "com.tuya.hello.service.template.IUserServer",
"methodName": "addUser",
"parameterTypes": [
"com.tuya.gateway.Context",
"java.lang.String",
"com.tuya.hello.User"
],
"parameterNames": [
"context",
"uid",
"userInfo"
],
"updateTimestamp": "1234567890",
"permissionDO": {},
"voMap": {
"userInfo": {
"name": "java.lang.String",
"sex": "java.lang.String",
"age": "java.lang.Integer"
}
},
"parameterNameHumpToLine": true,
"resultFiledHumpToLine": false,
"protocolName": "dubbo"
}
```
Gateway 从元数据配置中心订阅到以上信息,就能把一个 API 请求匹配到一个 dubbo 接口。再从 API 请求中抓取参数作为入参。这样功能就完成了流量闭环。
以上内容,大家应该对此 gateway 的项目拓扑结构有了清晰的认知。我接着分享项目在使用 dubbo-go 过程中遇到的问题和调优经验。19 年初,当时的 dubbo-go 项目还只是构建初期,没有什么用户落地的经验。我也是一边参与社区开发,一边编码公司内部网关项目。在解决了一堆 hessain 序列化和 zookeeper 注册中心的问题后,项目最终跑通了闭环。但是,作为一个核心应用,跑通闭环离上生产环境还有很长的路要走,特别是使用了当时稳定性待测试的新框架。整个测试加上功能补全,整整花费了一个季度的时间,直到项目趋于稳定,压测效果也良好。**单台网关机器( 2C 8G )全链路模拟真实环境压测达到 2000 QPS。由于引入了比较重的业务逻辑(单个请求平均调用 3 个 dubbo 接口),对于这个压测结果,是符合甚至超出预期的。**
总结了一些 dubbo-go 参数配置调优的经验,主要是一些网络相关配置。大家在跑 demo 时,应该会看到配置文件最后有一堆配置,但如果对 dubbo-go 底层网络模型不熟悉,就很难理解这些配置的含义。目前 dubbo-go 网络层以 [getty](https://github.com/AlexStocks/getty) 为底层框架,实现读写分离和协程池管理。getty 对外暴露 session 的概念,session 提供一系列网络层方法注入的实现,因为本文不是源码解析文档,在这里不过多论述。**读者可以简单的认为 dubbo-go 维护了一个 getty session 池,session 又维护了一个 TCP 连接池。对于每个连接,getty 会有读协程和写协程伴生,做到读写分离**。这里我尽量用通俗的注释帮大家梳理下对性能影响较大的几个配置含义:
```yaml
protocol_conf:
# 这里是协议独立的配置,在dubbo协议下,大多数配置即为getty session相关的配置。
dubbo:
# 一个session会始终保证connection_number个tcp连接个数,默认是16,
# 但这里建议大家配置相对小的值,一般系统不需要如此多的连接个数。
# 每隔reconnect_interval时间,检查连接个数,如果小于connection_number,
# 就建立连接。填0或不填都为默认值300ms
reconnect_interval: 0
connection_number: 2
# 客户端发送心跳的间隔
heartbeat_period: "30s"
# OnCron时session的超时时间,超过session_timeout无返回就关闭session
session_timeout: "30s"
# 每一个dubbo interface的客户端,会维护一个最大值为pool_size大小的session池。
# 每次请求从session池中select一个。所以真实的tcp数量是session数量*connection_number,
# 而pool_size是session数量的最大值。测试总结下来一般程序4个tcp连接足以。
pool_size: 4
# session保活超时时间,也就是超过session_timeout时间没有使用该session,就会关闭该session
pool_ttl: 600
# 处理返回值的协程池大小
gr_pool_size: 1200
# 读数据和协程池中的缓冲队列长度,目前已经废弃。不使用缓冲队列
queue_len: 64
queue_number: 60
getty_session_param:
compress_encoding: false
tcp_no_delay: true
tcp_keep_alive: true
keep_alive_period: "120s"
tcp_r_buf_size: 262144
tcp_w_buf_size: 65536
pkg_wq_size: 512
tcp_read_timeout: "1s" # 每次读包的超时时间
tcp_write_timeout: "5s" # 每次写包的超时时间
wait_timeout: "1s"
max_msg_len: 102400 # 最大数据传输长度
session_name: "client"
```
## dubbo-go 快速接入
前文已经展示过 dubbo-go 在涂鸦智能的实践成果,接下来介绍快速接入 dubbo-go 的方式。
### 第一步:hello world
dubbo-go 使用范例目前和 dubbo 一致,放置在 [apache/dubbo-samples](https://github.com/apache/dubbo-samples) 项目中。在 dubbo-sample/golang 目录下,用户可以选择自己感兴趣的 feature 目录,快速测试代码效果。
```shell
tree dubbo-samples/golang -L 1
dubbo-samples/golang
├── README.md
├── async
├── ci.sh
├── configcenter
├── direct
├── filter
├── general
├── generic
├── go.mod
├── go.sum
├── helloworld
├── multi_registry
└── registry
```
我们以 hello world 为例,按照 dubbo-samples/golang/README.md 中的步骤,分别启动 server 和 client 。可以尝试 golang 调用 java 、 java 调用 golang 、golang 调用 golang 、java 调用 java。dubbo-go 在协议上支持和 dubbo 互通。
我们以启动 go-server 为例,注册中心默认使用 zookeeper 。首先确认本地的 zookeeper 是否运行正常。然后执行以下命令,紧接着你就可以看到你的服务正常启动的日志了。
```shell
export ARCH=mac
export ENV=dev
cd dubbo-samples/golang/helloworld/dubbo/go-server
sh ./assembly/$ARCH/$ENV.sh
cd ./target/darwin/user_info_server-2.6.0-20200608-1056-dev/
sh ./bin/load.sh start
```
### 第二步:在项目中使用 dubbo-go
上面,我们通过社区维护的测试代码和启动脚本将用例跑了起来。接下来,我们需要在自己的代码中嵌入 dubbo-go 框架。很多朋友往往是在这一步遇到问题,这里我整理的一些常见问题,希望能帮到大家。
##### 1. 环境变量
目前 dubbo-go 有 3 个环境变量需要配置。
- `CONF_CONSUMER_FILE_PATH` : Consumer 端配置文件路径,使用 consumer 时必需。
- `CONF_PROVIDER_FILE_PATH`:Provider 端配置文件路径,使用 provider 时必需。
- `APP_LOG_CONF_FILE` :Log 日志文件路径,必需。
- `CONF_ROUTER_FILE_PATH`:File Router 规则配置文件路径,使用 File Router 时需要。
##### 2. 代码注意点
- 注入服务 : 检查是否执行以下代码
- 客户端
```go
func init() {
config.SetConsumerService(userProvider)
}
```
- 服务端
```go
func init() {
config.SetProviderService(new(UserProvider))
}
```
- 注入序列化描述 :检查是否执行以下代码
```go
hessian.RegisterJavaEnum(Gender(MAN))
hessian.RegisterJavaEnum(Gender(WOMAN))
hessian.RegisterPOJO(&User{})
```
##### 3. 正确理解配置文件
- `references/services` 下的 key ,如下面例子的 "UserProvider" 需要和服务 Reference() 返回值保持一致,此为标识改接口的 key。
```yaml
references:
"UserProvider":
registry: "hangzhouzk"
protocol: "dubbo"
interface: "com.ikurento.user.UserProvider"
cluster: "failover"
methods:
- name: "GetUser"
retries: 3
```
- 注册中心如果只有一个注册中心集群,只需配置一个。多个 IP 用逗号隔开,如下:
```yaml
registries:
"hangzhouzk":
protocol: "zookeeper"
timeout: "3s"
address: "172.16.120.181:2181,172.16.120.182:2181"
username: ""
password: ""
```
##### 4. java 和 go 的问题
- `go``java` 交互的大小写 :golang 为了适配 java 的驼峰格式,在调用 java 服务时,会自动将 method 和属性首字母变成小写。很多同学故意将 java 代码写成适配 golang 的参数定义,将首字母大写,最后反而无法序列化匹配。
### 第三步:拓展功能
dubbo-go 和 dubbo 都提供了非常丰富的拓展机制。可以实现自定义模块代替 dubbo-go 默认模块,或者新增某些功能。比如实现 Cluster、Filter 、Router 等来适配业务的需求。这些注入方法暴露在 dubbo-go/common/extension 中,允许用户调用及配置。
**_本文作者:_**
**潘天颖,Github ID @pantianying,开源爱好者,就职于涂鸦智能。**
## 欢迎加入 dubbo-go 社区
有任何 dubbo-go 相关的问题,可以加我们的钉钉群 23331795 询问探讨,我们一定第一时间给出反馈。
![1.png](../../pic/course/practice-and-exploration-of-dubbo-go-4.png)
### 最新活动
Dubbo-go ASoC [相关题目](https://github.com/apache/dubbo-go/issues?q=is%3Aissue+is%3Aopen+label%3AASOC2020) ,参加详情 [请点击](https://github.com/apache/dubbo/issues/6203)
# [快速上手 dubbo-go](https://studygolang.com/articles/29457)
## 前言
每次技术调研总会发现自己学不动了怎么办?用已有的知识来拓展要学习的新知识就好了~ by LinkinStar
最近需要调研使用 dubbo,之前完全是 0 基础,对于 dubbo 只存在于听说,今天上手实战一把,告诉你如何快速用 go 上手 dubbo
PS:以下的学习方式适用于很多新技术
## 基本概念
首先学习一个技术首先要看看它的整体架构和基本概念,每个技术都有着自己的名词解释和实现方式,如果文档齐全就简单很多。
http://dubbo.apache.org/zh-cn/docs/user/preface/background.html
大致浏览了背景、需求、架构之后基本上有一个大致概念
image.png
其实整体架构和很多微服务的架构都是类似的,就是有一个注册中心管理所有的服务列表,服务提供方先向注册中心注册,而消费方向注册中心请求服务列表,通过服务列表调用最终的服务。总的来说 dubbo 将整个过程封装在了里面,而作为使用者的我们来说更加关心业务实现,它帮我们做好了治理的工作。
然后我抓住了几个我想要知道的重点:
注册中心可替换,官方推荐的是 zk
如果有变更,注册中心将基于长连接推送变更数据给消费者,注册中心,服务提供者,服务消费者三者之间均为长连接
基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用
消费者在本地缓存了提供者列表
## 实际上手
官网文档中也给出如果使用 golang 开发,那么有 https://github.com/apache/dubbo-go 可以用,那么废话不多说,上手实践一把先。因为你看再多,都比不上实践一把来学的快。
### 环境搭建
大多数教程就会跟你说,一个 helloWorld 需要 zookeeper 环境,但是不告诉你如何搭建,因为这对于他们来说太简单了,而我不一样,我是 0 基础,那如何快速搭建一个需要调研项目的环境呢?最好的方式就是 docker。
```yml
version: "3"
services:
zookeeper:
image: zookeeper
ports:
- 2181:2181
admin:
image: apache/dubbo-admin
depends_on:
- zookeeper
ports:
- 8080:8080
environment:
- admin.registry.address=zookeeper://zookeeper:2181
- admin.config-center=zookeeper://zookeeper:2181
- admin.metadata-report.address=zookeeper://zookeeper:2181
```
```yml
version: "3"
services:
zookeeper:
image: zookeeper
ports:
- 2181:2181
admin:
image: chenchuxin/dubbo-admin
depends_on:
- zookeeper
ports:
- 8080:8080
environment:
- dubbo.registry.address=zookeeper://zookeeper:2181
- dubbo.admin.root.password=root
- dubbo.admin.guest.password=guest
```
上面两个 docker-compose 文件一个是官方提供的管理工具,一个包含的是个人修改之后的管理工具,记住这里有个用户名密码是 root-root,看你喜欢
废话不多说,直接创建 docker-compose.yaml 然后 `docker-compose up` 你就得到了一个环境,棒 👍
### 样例下载
有的技术会给出官方的实验样例,dubbo-go 也不例外
https://github.com/apache/dubbo-samples
里面包含了 go 和 java 的样例,有了它你就能快速上手了,把它下载到本地
### 样例运行
首先要做的第一步就是把 helloworld 给跑起来,进入 golang 目录,里面有个 README.md 看一下。然后开搞。
打开一个终端运行服务方
```sh
export ARCH=mac
export ENV=dev
cd helloworld/dubbo/go-server
sh ./assembly/$ARCH/$ENV.sh
cd ./target/linux/user_info_server-0.3.1-20190517-0930-release
sh ./bin/load.sh start
```
打开另一个终端运行客户端
```sh
export ARCH=mac
export ENV=dev
cd helloworld/dubbo/go-client
sh ./assembly/$ARCH/$ENV.sh
cd ./target/linux/user_info_client-0.3.1-20190517-0921-release
sh ./bin/load_user_info_client.sh start
```
启动过程中会出现一些警告,问题不大,如果成功,那么客户端会有一个调用服务端的请求,并在控制台中以白色底色进行打印
image.png
java 的服务也有相对应的启动方式,按照 README 中所说明的也可以进行注册和调用,并且 java 和 go 之间是可以互相调用的
### 查看服务
因为我们部署的时候有一个 dubbo-admin 用于管理 zk 上注册的服务,我们可以访问本地的 8080 端口看到对应的服务情况
image.png
至此你应该已经对于整体的链路调用有一个大致的认识,结合之前官网文档的中的架构图,应该也清晰了。
## 如何使用
那么现在你已经运行起来了,那么势必就要来看看具体是如何进行使用的了。
### 服务端
服务端,也就是服务提供者;
位置在:dubbo-samples/golang/helloworld/dubbo/go-server/app
```go
// 将服务进行注册
config.SetProviderService(new(UserProvider))
// 注册对象的hessian序列化
hessian.RegisterPOJO(&User{})
```
是不是看起来其实很简单,其实重点就是上面两句代码了
对于服务本身来说
```go
type UserProvider struct {
}
func (u *UserProvider) GetUser(ctx context.Context, req []interface{}) (*User, error) {
println("req:%#v", req)
rsp := User{"A001", "Alex Stocks", 18, time.Now()}
println("rsp:%#v", rsp)
return &rsp, nil
}
func (u *UserProvider) Reference() string {
return "UserProvider"
}
```
其实就是需要实现下面那个接口就可以了
```go
// rpc service interface
type RPCService interface {
Reference() string // rpc service id or reference id
}
```
其中有一步骤不要忘记了是 config.Load(),会加载配置文件的相关配置,不然你以为注册中心的地址等等是在哪里配置的呢?
客户端
看了服务端,其实客户端也就很简单了
```go
config.SetConsumerService(userProvider)
hessian.RegisterPOJO(&User{})
```
其实也是差不多的,也需要注册一个消费服务,并将序列化方式给注册上去
```go
user := &User{}
err := userProvider.GetUser(context.TODO(), []interface{}{"A001"}, user)
```
使用也就很简单了,同样的也需要实现 Reference 接口
```go
type UserProvider struct {
GetUser func(ctx context.Context, req []interface{}, rsp *User) error
}
func (u *UserProvider) Reference() string {
return "UserProvider"
}
```
回头想想
当我们看完了代码的上的使用,再回头想想 dubbo 的作用,你就会发现其实 dubbo 帮你做的事情真的屏蔽了很多。
- 你不需要关心服务是怎么注册的
- 你不需要关心服务是怎么获取的
- 你不需要关系服务是怎么调用的
- 甚至序列化的过程都是基本透明的
- 想对比来说,如果让你自己去是实现微服务,那是不是说,你需要实现服务的拉取,服务的负载均衡,服务的发现,序列化.....
这下你应该明白了 dubbo 是做什么的也就明白了它为什么会出现了
## 其他能力
当你学习了一个技术的基本使用之后,要学习其他的能力,以便在使用的过程中不会出现自己重复造轮子或者说有轮子没用到的情况,https://github.com/apache/dubbo-samples 不止有 helloworld 还要很多别的案例供你参考,你可以继续看看并进行试验。
### 支持扩展
https://github.com/apache/dubbo-go
在 Feature list 中列举了 dubbo-go 所支持的相关功能,比如序列化,比如注册中心,在比如过滤器。
也就是说,在使用 dubbo-go 的时候相关功能都是插件化的,想用什么就看你自己了,比如注册中心我想用 etcd,比如调用的协议我想换成 grpc 都可以。
## 相关问题
对于一个技术调研,那么肯定会有相关问题等着你去发现去考虑。
下面是我能想到的一些问题:
当前 dubbo-go 的版本最高在 1.4,所对应的 dubbo 版本应该是 2.6.x,如果调用更高版本的服务是否会有问题
java 和 go 之间互相调用,各种类型转换之间是否存在问题,是否容易出现无法正确反序列化的问题
## 后续学习
那么上面只是说能让你上手 dubbo-go,但是实际使用可能存在距离。为什么这么说呢?如果你在不动里面任何的原理情况下,那么如果遇到问题,你很可能就束手无策了。比如如果线上服务无法正常发现,你应该如何排查?调用过程中出现问题如何定位?
所以后续你需要做的是:
看看整体设计架构和思路,明白整条链路调用过程和规则
学习它的接口设计,为什么别人设计的接口能兼容那么多的注册中心?如果让你来设计你怎么设计呢?
性能也很重要,网上说性能很不错,为什么呢?什么地方做了优化,负载均衡的算法是怎么样的,你能自定义吗?
## 总结
总的来说,对于 dubbo-go 整体的使用上还是非常好上手的,自己想了一下,如果当前项目想要接入的话,主要是服务的暴露、序列化方式、鉴权调整等存在开发工作。
上面砖头也抛的差不多了,对于你快速上手应该没有问题了,剩下的就要靠你自己了。
# [dubbo-go 快速开始](https://github.com/dubbogo/dubbo-go-website/blob/master/docs/zh-cn/user/quick-start.md)
| title | keywords | description |
| -------- | ------------------------------- | -------------------------------------------------- |
| 快速开始 | 快速开始, hellowworld, Provider | 快速上手 dubbo-go,编写一个简单的 hellowworld 应用 |
# 快速开始
通过一个 `hellowworld` 例子带领大家快速上手 Dubbo-go 框架。
协议:Dubbo
编码:Hessian2
注册中心:Zookeeper
## 环境
- Go 编程环境
- 启动 zookeeper 服务,也可以使用远程实例
## 从服务端开始
### 第一步:编写 `Provider` 结构体和提供服务的方法
> https://github.com/dubbogo/dubbo-samples/blob/master/golang/helloworld/dubbo/go-server/app/user.go
1. 编写需要被编码的结构体,由于使用 `Hessian2` 作为编码协议,`User` 需要实现 `JavaClassName` 方法,它的返回值在 dubbo 中对应 User 类的类名。
```go
type User struct {
Id string
Name string
Age int32
Time time.Time
}
func (u User) JavaClassName() string {
return "com.ikurento.user.User"
}
```
1. 编写业务逻辑,`UserProvider` 相当于 dubbo 中的一个服务实现。需要实现 `Reference` 方法,返回值是这个服务的唯一标识,对应 dubbo 的 `beans``path` 字段。
```go
type UserProvider struct {
}
func (u *UserProvider) GetUser(ctx context.Context, req []interface{}) (*User, error) {
println("req:%#v", req)
rsp := User{"A001", "hellowworld", 18, time.Now()}
println("rsp:%#v", rsp)
return &rsp, nil
}
func (u *UserProvider) Reference() string {
return "UserProvider"
}
```
1. 注册服务和对象
```go
func init() {
config.SetProviderService(new(UserProvider))
// ------for hessian2------
hessian.RegisterPOJO(&User{})
}
```
### 第二步:编写主程序
> https://github.com/dubbogo/dubbo-samples/blob/master/golang/helloworld/dubbo/go-server/app/server.go
1. 引入必需的 dubbo-go 包
```go
import (
hessian "github.com/apache/dubbo-go-hessian2"
"github.com/apache/dubbo-go/config"
_ "github.com/apache/dubbo-go/registry/protocol"
_ "github.com/apache/dubbo-go/common/proxy/proxy_factory"
_ "github.com/apache/dubbo-go/filter/impl"
_ "github.com/apache/dubbo-go/cluster/cluster_impl"
_ "github.com/apache/dubbo-go/cluster/loadbalance"
_ "github.com/apache/dubbo-go/registry/zookeeper"
_ "github.com/apache/dubbo-go/protocol/dubbo"
)
```
1. main 函数
```go
func main() {
config.Load()
}
```
### 第三步:配置
#### 1.使用环境变量
1. 参考 [log](https://github.com/dubbogo/dubbo-samples/blob/master/golang/helloworld/dubbo/go-server/profiles/release/log.yml)[server](https://github.com/dubbogo/dubbo-samples/blob/master/golang/helloworld/dubbo/go-server/profiles/release/server.yml) 编辑配置文件。
主要编辑以下部分:
- `registries` 结点下需要配置 zk 的数量和地址
- `services` 结点下配置服务的具体信息,需要配置 `interface` 配置,修改为对应服务的接口名,服务的 key 对应第一步中 `Provider``Reference` 返回值
1. 把上面的两个配置文件分别配置为环境变量
```sh
export CONF_PROVIDER_FILE_PATH="xxx"
export APP_LOG_CONF_FILE="xxx"
```
#### 2.自定义配置文件
1. var consumerConfigStr = `xxxxx`// 配置文件内容,可以参考[log](https://github.com/dubbogo/dubbo-samples/blob/master/golang/helloworld/dubbo/go-client/profiles/release/log.yml)[client](https://github.com/dubbogo/dubbo-samples/blob/master/golang/helloworld/dubbo/go-client/profiles/release/client.yml)
- 在这里你可以定义配置文件的获取方式,比如配置中心,本地文件读取
2.`config.Load()`之前设置配置
```go
func main() {
hessian.RegisterPOJO(&User{})
providerConfig := config.ProviderConfig{}
yaml.Unmarshal([]byte(providerConfigStr), &providerConfig)
config.SetProviderConfig(providerConfig)
defaultServerConfig := dubbo.GetDefaultServerConfig()
dubbo.SetServerConfig(defaultServerConfig)
logger.SetLoggerLevel("warn") // info,warn
config.Load()
select {
}
}
```
## 接着是客户端
### 第一步:编写客户端 `Provider`
> https://github.com/dubbogo/dubbo-samples/blob/master/golang/helloworld/dubbo/go-client/app/user.go
1. 参考服务端第一步的第一点。
2. 与服务端不同的是,提供服务的方法作为结构体的参数,不需要编写具体业务逻辑。另外,`Provider` 不对应 dubbo 中的接口,而是对应一个实现。
```go
type UserProvider struct {
GetUser func(ctx context.Context, req []interface{}, rsp *User) error
}
func (u *UserProvider) Reference() string {
return "UserProvider"
}
```
3. 注册服务和对象
```go
func init() {
config.SetConsumerService(userProvider)
hessian.RegisterPOJO(&User{})
}
```
### 第二步:编写客户端主程序
> https://github.com/dubbogo/dubbo-samples/blob/master/golang/helloworld/dubbo/go-client/app/client.go
1. 引入必需的 dubbo-go 包
```go
import (
hessian "github.com/apache/dubbo-go-hessian2"
"github.com/apache/dubbo-go/config"
_ "github.com/apache/dubbo-go/registry/protocol"
_ "github.com/apache/dubbo-go/common/proxy/proxy_factory"
_ "github.com/apache/dubbo-go/filter/impl"
_ "github.com/apache/dubbo-go/cluster/cluster_impl"
_ "github.com/apache/dubbo-go/cluster/loadbalance"
_ "github.com/apache/dubbo-go/registry/zookeeper"
_ "github.com/apache/dubbo-go/protocol/dubbo"
)
```
1. main 函数
```go
func main() {
config.Load()
time.Sleep(3e9)
println("\n\n\nstart to test dubbo")
user := &User{}
err := userProvider.GetUser(context.TODO(), []interface{}{"A001"}, user)
if err != nil {
panic(err)
}
println("response result: %v\n", user)
}
func println(format string, args ...interface{}) {
fmt.Printf("\033[32;40m"+format+"\033[0m\n", args...)
}
```
### 第三步:配置
#### 1.使用环境变量
1. 参考 [log](https://github.com/dubbogo/dubbo-samples/blob/master/golang/helloworld/dubbo/go-client/profiles/release/log.yml)[client](https://github.com/dubbogo/dubbo-samples/blob/master/golang/helloworld/dubbo/go-client/profiles/release/client.yml) 编辑配置文件。
主要编辑以下部分:
- `registries` 结点下需要配置 zk 的数量和地址
- `references` 结点下配置服务的具体信息,需要配置 `interface` 配置,修改为对应服务的接口名,服务的 key 对应第一步中 `Provider``Reference` 返回值
1. 把上面的两个配置文件费别配置为环境变量,为防止 log 的环境变量和服务端的 log 环境变量冲突,建议所有的环境变量不要做全局配置,在当前起效即可。
```sh
export CONF_CONSUMER_FILE_PATH="xxx"
export APP_LOG_CONF_FILE="xxx"
```
#### 2.自定义配置文件
1. var consumerConfigStr = `xxxxx`// 配置文件内容,可以参考[log](https://github.com/dubbogo/dubbo-samples/blob/master/golang/helloworld/dubbo/go-client/profiles/release/log.yml)[client](https://github.com/dubbogo/dubbo-samples/blob/master/golang/helloworld/dubbo/go-client/profiles/release/client.yml)
- 在这里你可以定义配置文件的获取方式,比如配置中心,本地文件读取
2.`config.Load()`之前设置配置
```go
func main() {
p := config.ConsumerConfig{}
yaml.Unmarshal([]byte(consumerConfigStr), &p)
config.SetConsumerConfig(p)
defaultClientConfig := dubbo.GetDefaultClientConfig()
dubbo.SetClientConf(defaultClientConfig)
logger.SetLoggerLevel("warn") // info,warn
config.Load()
user := &User{}
err := userProvider.GetUser(context.TODO(), []interface{}{"A001"}, user)
if err != nil {
log.Print(err)
return
}
log.Print(user)
}
```
# [都已经十岁的 Apache Dubbo,还能再乘风破浪吗?](https://my.oschina.net/u/3874284/blog/4338792)
纵观中国开源历史,你真的没法找到第二个像 Dubbo 一样自带争议和讨论热度的开源项目。
一方面,2011 年,它的开源填补了当时生产环境使用的 RPC 框架的空白,一发布就被广泛采用;另一方面,它经历了停止维护、重启维护后捐献给 Apache 基金会、接着又以顶级项目的身份毕业。
面对多疑的开发者,在云原生时代,Apache Dubbo 将如何延续当前光芒?
今年是 Dubbo 从 Apache 基金会毕业的一周年,同时也是推进 Dubbo 3.0,即全面拥抱云原生的重要一年。开源中国与 Apaceh Dubbo 共同策划**【Dubbo 云原生之路】**系列文章,和大家一起回顾 Apache Dubbo 社区的发展。系列文章主要涵盖 Dubbo 技术解读、社区、应用案例解析三大部分,之后每周都会和大家见面。
在【阿里巴巴云原生公众号】留言说出与 Apache Dubbo 的故事,点赞排名前十的同学可领取 Dubbo 送出的专属奖品杯子一只;另外由 Apache Dubbo PMC @Chickenlj 随机抽取一名幸运读者,赠送价值 260 元护眼灯一台。下周三开奖。
### 作者简介
**刘军**,花名陆龟,GitHub 账号 Chickenlj,Apache Dubbo PMC,项目核心开发,见证了 Dubbo 重启开源,到从 Apache 基金会毕业的整个过程。现任职阿里云云原生应用平台团队,参与服务框架、微服务相关工作,目前主要在推动 Dubbo 3.0 - Dubbo 云原生。
## 系列开篇:3.0 全面铺开、ASF 毕业一周年
从 2019 年到现在,在 Dubbo 毕业的这一年时间里,Dubbo 社区和产品都取得长足进步,同时 Dubbo 云原生版本 - Dubbo 3.0 的开发工作也已经全面铺开。
社区方面。需要重点提及的有两点:一个是落地与贡献的企业用户进一步增加,主动与社区取得联系的中、大规模公司达 200 多家,如携程、工商银行、瓜子二手车、网联清算、中通等;另一个是以 Dubbo-go 为代表的子社区蓬勃发展。
产品技术演进方面。Dubbo Java 版发布 10 个版本,在多语言、协议、性能、服务治理模型等方面都有深度探索。Dubbo go 发布超过 8 个版本,在功能基本对齐 Java 版本的基础上,一些方向上也已经走在了 Java 版本前面。
值得一提的是,阿里巴巴内部也正在积极推动 Dubbo 社区版本在内部的落地,从今年开始逐步实现以 Dubbo 替换其内部的 HSF 框架。这一方面有利于阿里将其在 HSF 上的丰富服务治理经验回馈输出到社区,另一方面阿里官方的落地也将直接加速 Dubbo 云原生的发展。
在云原生大潮下,3.0 已被正式列为今年 Dubbo 产品建设的核心目标,涉及下一代 RPC 协议、服务治理模型、云原生基础设施适配等多方面的内容。其中,很多方面已经在当前的 2.7 版本中做了前置性探索,如近期发布的基于 HTTP/2 的协议支持、应用级服务发现等,后续工作将以此为基础展开。系列文章也会有对 Dubbo 3.0 Roadmap 及技术方案的详细解析。
## Dubbo 毕业一周年回顾
2017 年 7 月,Dubbo 开源项目被重新激活,2018 年捐献到 Apache 基金会,2019 年 5 月,Dubbo 正式从 Apache 基金会孵化毕业,成为 Apache 顶级项目。接下来,文章分别从社区、子社区、产品三方面介绍 Dubbo 过去一年的成绩。
### 社区一年发布 24 个版本,贡献者已超 300
如果说最开始重新激活是以阿里巴巴为主导的项目维护投入,那自 Dubbo 加入 Apache 起,它就已经开始成为一个社区主导、社区贡献为主的完全开放的基金会项目。
到今天,这一趋势正变得更明显。包括阿里巴巴、携程、工商银行、瓜子二手车、网联清算、中通等在内的互联网、传统企业公司,在 Dubbo 的使用与社区代码贡献上都有投入。Dubbo 社区正变得非常活跃和多样化。
过去一年,Dubbo 社区项目总共发布 24 个版本,发展 Committer/PMC 27 人,其中有 20% 的贡献者是来自于阿里巴巴,80% 以上来自不同组织的开发者或爱好者。
Dubbo 社区组织了超过 10 场线下 meetup 活动,基本覆盖了国内开发者聚集的城市。通过线下或线上直播活动,分享超过 100 个 topic 的演讲,深度讲解 Dubbo 社区最新动态、功能模块开发和近期规划等。主题演讲大多是社区采集方式,由 Dubbo 的深度企业分享实践经验,其中典型的代表包括携程、工商银行、考拉、信用算力等。
从 GitHub 统计数据来看,Dubbo Star 数取得新的里程碑,已超过 3 万,相比重启开源时增长了近 5 倍;贡献者由最初的几十个增长到现在的 300 多个,而这其中有 60 多人已经被提名为 committer,不论是贡献者数量还是 committer 比例都得到很大的提升;Dubbo Java 发布的有 65 个。
上述主要是对 Dubbo Java 项目社区发展的总结,下面将介绍 Dubbo Java 产品方面的进展。
### Dubbo Java 迭代,目前主要维护 3 个大版本
当前社区维护的 Dubbo Java 大版本主要有 3 个,分别是 2.5.x、2.6.x 和 2.7.x。
2.7.x 是社区的主要开发版本,在过去的一年共发布了 8 个版本(2.7.0 - 2.7.7),每个版本都有一些值得关注的特性或功能升级,涵盖从编程模型、服务治理、性能到协议的多个方面的增强;
2.6.x 版本则定位为 bugfix 版本,过去一年共发布了 3 个版本,主要以修复问题和安全漏洞为主,并没有增加太多新的 feature;
2.5.x 版本从 2019 年初开始已宣布 EOF,只做安全修复;而到了下半年已经完全停止了维护。
下面通过一个简要分层模块图,回顾过去一段时间 Dubbo 的技术架构演进,从编程模型、服务治理、传输协议、性能优化等角度切入:
![dubbo-go 的时间轴](../../pic/course/ten-year-dubbo-1.png)
以上很多功能都已被各大厂商落地,用于解决具体的业务问题。我们也期待,接下来这些厂商带来更多关于 Dubbo 实践经验的深度总结。
## Dubbo-go 发展的第五年,正与 Dubbo 齐头并进
除 Dubbo Java 之外,Dubbo 周边也发展出了很多优秀的子项目(子社区),其中包括 Dubbo-spring-boot-project、Dubbo-go 等,这里先着重介绍 Dubbo-go 子社区。
Dubbo-go 项目最早由于雨在 2016 年 5 月构建,同年 9 月发布并开源,如下时间轴图清晰记录了 Dubbo-go 的前世今生。
![dubbo-go 的时间轴](../../pic/arch/dubbo-go-one-year-1.png)
秉承 "bridge the gap between Java and Go" 天然使命的 Dubbo-go,已经进入第五个年头,也走出了自己独特的发展路径:
- 当前的 v1.4.0 版本已对齐 2.6.x 版本,即将发布的版本将与 v2.7.x【对标 v2.7.5】对齐,而后将会发布对标 Dubbo 3.x 的 v1.6.0 版本;
- 独立维护从底层的 hessian2 协议库 Dubbo-go-hessian2、网络库 getty 到上层对标 Dubbo 的 Dubbo-go 的全套实现;
- 独立的 TCP + Protobuf 和 gRPC + JSON 通信方案也已开发完成【将包含着在版本 v1.5.0 中】;
- 已与 Dubbo/gRPC/Spring Boot 实现互联互通;
- 通过接入 Opentracing 和 Promethus,Dubbo-go 在可观测性等微服务方向的进行了自己独特的探索;
- 已实现了基于 HTTPS 的可信 RPC 调用;
- 已经实现了自己独特的把 Kubernetes 作为注册中心的微服务方案;
Dubbo-go 从最开始 Dubbo 的 Go 语言实现,已发展成为目前 Dubbo 多语言版本中功能最强大者,它的发展离不开背后强大的 Dubbo-go 社区。除了上述 Dubbo-go 的自身特性外,通过跨社区合作,取得了如下成绩:
- 通过与 MOSN 社区合作,已经实现 Dubbo/Dubbo-go 应用可以零成本接入基于 MOSN 实现 Dubbo Mesh,实现微服务和云原生共存的 “双模微服务”;
- 与 sentinel 社区合作,在 Dubbo/Dubbo-go 完整接入 sentinel 的降级和限流方案;
- 与 Apollo 社区合作,在 Dubbo-go 中实现远程配置下发;
- 与 Nacos 社区合作,实现基于 Nacos 的服务发现;
### Dubbo-go 社区 2020 年 Q2 主要目标有:
发布完全对齐 Dubbo 2.7.x 的 v1.5.0 版本;
发布对标 Dubbo 3.0 的 v1.6.0 版本;
在云原生方面继续自己的探索;
继续与兄弟社区保持合作共进态势,扩大自身使用范围;
生产实践上推进在阿里集团,以及更多厂家的落地。
项目(包括子项目)目前已先后在携程、涂鸦智能和蚂蚁金服等公司生产落地。
今年阿里集团完成 HSF 和 Dubbo 的融合后,项目也将在阿里集团双十一战场经受考验。
## 云原生 Dubbo - Dubbo 3.0
3.0 是下一代 Dubbo 架构的代号。一年前,最开始探索 Reactive Stream 之时,社区也曾有过对 Dubbo 3.0 的广泛讨论。而这一次,在云原生大背景下,3.0 代表了更全面的 Dubbo 架构升级,涉及到下一代 RPC 协议、全新的服务治理模型和云原生基础设施适配等。
阿里巴巴是参与 Dubbo 3.0 开发建设的主要力量之一,这款始于阿里的开源项目正重新回归阿里内部落地。
去年开始,阿里巴巴就已经在逐步推动以 Dubbo 替换其内部的 HSF 框架的工作,通过将 Dubbo 与 HSF 两个框架融为一体,并在此基础上发展出适应云原生架构的 Dubbo 版本。Dubbo 重回阿里巴巴的落地是拥抱社区、拥抱云原生、拥抱标准化的一次很好的实践。
阿里巴巴内部 Dubbo 3.0 的落地,对社区也是一个重大利好,这一方面有利于阿里巴巴将其在 HSF 上的丰富服务治理经验回馈输出到社区,另一方面也将直接推动 Dubbo 云原生架构的快速演进。除了阿里巴巴之外,包括斗鱼、工商银行、爱奇艺、斗鱼等厂商也都在参与下一代 Dubbo 3.0 的建设。
下面列出了 Dubbo 3.0 中的三个重要方向,具体的 Roadmap 将在接下来文章中单独说明:
- 下一代 RPC 协议。新协议将提供更丰富的如 Stream、Flow Control 等内置语义,同时将具有更好的扩展性、网关的友好性等;
- 基于应用粒度的服务发现机制。在兼顾 Dubbo 面向接口的易用性与功能性的基础上,解决与 Kubernetes Native Service 适配问题,解决大规模集群下的地址推送性能瓶颈问题;
- 适配云原生基础设施的解决方案。这涉及到 Dubbo 服务与基础设施生命周期对接、Kubernetes Native Service 适配、适应基础设施调度的服务治理规则、适配 Service Mesh 架构的解决方案等;
接下来沿着这三个方面简要展开。
### 下一代 RPC 协议
专注在协议自身来说,下一代的协议主要聚焦在 HTTP/2、Reactive Stream、Flow Control 等方面:
- **Reactive Stream**:Reactive Stream 引入 RPC,带来更丰富的通信语义和 API 编程模型支持,如 Request-Stream、Bi-Stream 等;
- **HTTP/2**:微服务云原生场景下,基于 HTTP/2 构建的通信协议具有更好的通用性和穿透性;
- **Flow Control**:协议内置流控机制,支持类似 Reqctive Stream 的 Request (n) 流控机制。
从解决的业务场景问题上来说,基于新的协议 Dubbo 在框架层面要支持智能决策的负载均衡算法、对 Mesh 和网关更友好、更容易提供多语言实现与互通等。
- **Mesh**:协议对穿透 Mesh 更友好,区分协议头 Metadata 与 RPC Payload,方便完成与 Mesh 的协作,包括流量控制机制、应用层配置协商等;
- **协议通用性**:兼顾通用性与性能,支持协议能在各种设备上运行;
- **多语言支持**:如通过支持 Protobuf 提供了更完善的 跨语言服务定义 与 序列化传输的支持。
### 应用级服务治理
面向接口一直以来都是 Dubbo 框架的优势。一方面它的易用性,为开发者屏蔽了远程调用的存在;另一方面面向接口的地址发现、服务治理带来了更强大的能力,使得整个 Dubbo 治理体系非常强大与灵活。
既然面向接口有如此多的好处,那为什么我们还要探索面向应用的服务治理模式呢?
听起来似乎有些矛盾。其实到底是面向接口,还是面向应用,只是从不同的角度看 Dubbo。我们所聊的“面向接口 -> 面向应用”的改造,主要体现在服务注册、发现层面:
![dubbo-go 的时间轴](../../pic/course/ten-year-dubbo-2.png)
而我们说的面向应用的新模型,主要对第 2 点,即注册中心的数据组织转变为 “面向应用/实例” 粒度。这为我们解决两个问题:
- 在服务发现层面与 Kubernetes Service 等微服务模型对齐;
- 服务发现的数据量将有一个量级的下降,从 “接口数 _ 实例数 ”下降到 “应用数 _ 实例数”。
具体可以参见文章《Dubbo 迈出云原生重要一步 - 应用级服务发现解析》,本系列文章后续也会有对这部分机制和实现的更深度解析。
云原生基础设施
云原生带来了底层基础设施,应用开发、部署和运维等全方位的变化:
基础设施
基础设施调度机制变化,带来运维(生命周期)、服务治理等方面的变化;
服务发现能力下沉, Kubernetes 抽象了 Native Service Discovery。
Service Mesh - 云原生微服务解决方案
- Mesh 为跨语言、sdk 升级等提供了解决方案,Dubbo sdk 要与 Mesh 协作,做到功能、协议、服务治理等多方便的适配;
- Mesh 尚未大规模铺开,且其更适合对流量管控更关注的应用,传统 SDK 的性能优势仍旧存在,两者混部迁移场景可能会长期存在。
从应用场景上,Dubbo 可能的部署环境包括:
不使用 Kubernetes Native Service,Kubernetes 只作为容器编排调度设施,继续使用 Dubbo 自建的服务注册、发现机制;
复用 Kubernetes Native Service,Dubbo 不再关心服务注册,Dubbo Client 负责服务发现与流量分配;
Dubbo sdk 往 Mesh 迁移,一方面要做到适应 Mesh 架构,成为 Mesh 体系下的 RPC 编程和通信方案;另一方面要做到 Dubbo 与 Mesh 架构长期共存,互相打通服务发现和治理体系;
Kubernetes 上与云下混合部署的平滑迁移支持,包括服务发现的统一与网络通信方案的打通。
从 Dubbo 功能划分上,将着重从以下方面提供对云原生基础设施的支持:
- **生命周期**:Dubbo 与 Kubernetes 调度机制绑定,保持服务生命周期与 Pod 容器等生命周期的自动对齐;
- **治理规则**:服务治理规则在规则体、规则格式方面进行优化,如规则体以 YAML 描述、取消过滤规则对 IP 的直接依赖,定义规则特有的 CRD 资源等;
- **服务发现**:支持 K8S Native Service 的服务发现,包括 DNS、API-Server,支持 xDS 的服务发现;
- **Mesh 架构协作**:构建下一代的基于 HTTP/2 的通信协议,支持 xDS 的标准化的数据下发。
新一代的 RPC 协议和应用级服务发现模型将会是这一部分的前置基础。
## 总结与展望
作为系列文章开篇,我们在这里对 Dubbo 过去一年的成绩做了简要的总结与回顾,包括 Dubbo 社区、产品迭代的发展。接下来我们会看到更多来自深度 Dubbo 用户的落地经验分享,Dubbo-go 子社区的发展故事等。更重要的,我们也对下一代云原生 Dubbo - Dubbo 3.0 做了展望,后续关于 Dubbo 3.0 Roadmap、方案设计与进展解析等也将在此系列中发布。
在【阿里巴巴云原生公众号】留言说出与 Apache Dubbo 的故事,点赞排名前十的同学可领取 Dubbo 送出的专属奖品杯子一只;另外由 Apache Dubbo PMC @Chickenlj 随机抽取一名幸运读者,赠送价值 260 元护眼灯一台。下周三开奖。
# [邹毅贤:Dubbo-go 的成长与蜕变之路](https://m.bilibili.com/video/BV1BV411677w)
Dubbo-go 虽然有着 “bridge the gap between Java and Go” 的天然使命,但一路走来,已不仅仅是 Dubbo 的一个 Go 语言实现。通过提供可观测、可信性服务保障,快速结合 Dubbo 服务进行互联互通,且有其独特的云原生化实现与使用场景,Dubbo-go 已经走出了自己独特的发展路径。
[bilibili 的 dubbo-go视频](https://m.bilibili.com/video/BV1BV411677w)
\ No newline at end of file
This diff is collapsed.
# [Dubbo-go v1.5.1 发布,Apache Dubbo 的 Go 实现](https://www.oschina.net/news/118469/dubbo-go-1-5-1-released)
Dubbo-go 团队近期发布了 Dubbo-go v1.5.1,Dubbo-go 是 Apache Dubbo 项目的 Go 实现。
根据团队的介绍,虽然 v1.5.1 是 v1.5 的一个子版本,但相比于 v1.5.0, 社区还是投入了很大人力添加了如下重大改进。
## 1 应用维度注册模型
在新模型 release 后,团队发现 Provider 每个 URL 发布元数据都会注册 ServiceInstance,影响性能需要优化。
优化方案是: 去除 ServiceDiscoveryRegistry 中注册 ServiceInstance 的代码,在 config_loader 中的 loadProviderConfig 方法的最后注册 ServiceInstance 具体步骤:
1、获取所有注册的 Registry,过滤出 ServiceDiscoveryRegistry,拿取所有 ServiceDiscovery。
2、创建 ServiceInstance。
3、每个 ServiceDiscovery 注册 ServiceInstance。
保证 Provider 在注册成功之后,才暴露元数据信息。
## 2 支持基于 Seata 的事务
基于 Seata 扩展实现。通过增加过滤器,在服务端接收 xid 并结合 seata-golang 达到支持分布式事务的目的。 从而使 Dubbo-go 在分布式场景下,让用户有更多的选择,能适应更多的个性化场景。
开发团队在 dubbo-samples 中给出了 事务测试用例 。
## 3 多注册中心集群负载均衡
对于多注册中心订阅的场景,选址时的多了一层注册中心集群间的负载均衡:
在 Cluster Invoker 这一级,支持的选址策略有:
- 指定优先级
- 同 zone 优先
- 权重轮询
## 3 传输链路安全性
该版本在传输链路的安全性上做了尝试,对于内置的 Dubbo getty Server 提供了基于 TLS 的安全链路传输机制。
为尽可能保证应用启动的灵活性,TLS Cert 的指定通过配置文件方式,具体请参见 Dubbo-go 配置读取规则与 TLS 示例:
## 4 路由功能增强
本次路由功能重点支持了 动态标签路由 和 应用/服务级条件路由。
### 4.1 动态标签路由
标签路由通过将某一个或多个服务的提供者划分到同一个分组,约束流量只在指定分组中流转,从而实现流量隔离的目的,可以作为蓝绿发布、灰度发布等场景的能力基础。
标签主要是指对 Provider 端应用实例的分组,目前有两种方式可以完成实例分组,分别是动态规则打标和静态规则打标,其中动态规则相较于静态规则优先级更高,而当两种规则同时存在且出现冲突时,将以动态规则为准。
### 4.1.1 动态规则打标
可随时在服务治理控制台下发标签归组规则
```yml
# governance-tagrouter-provider应用增加了两个标签分组tag1和tag2
# tag1包含一个实例 127.0.0.1:20880
# tag2包含一个实例 127.0.0.1:20881
---
force: false
runtime: true
enabled: true
key: governance-tagrouter-provider
tags:
- name: tag1
addresses: ["127.0.0.1:20880"]
- name: tag2
addresses: ["127.0.0.1:20881"]
...
```
### 4.1.2 静态规则打标
可以在 server 配置文件的 tag 字段里设置
```yml
services:
"UserProvider":
registry: "hangzhouzk"
protocol: "dubbo"
interface: "com.ikurento.user.UserProvider"
loadbalance: "random"
warmup: "100"
tag: "beijing"
cluster: "failover"
methods:
- name: "GetUser"
retries: 1
loadbalance: "random"
```
consumer 添加 tag 至 attachment 即可
```go
ctx := context.Background()
attachment := make(map[string]string)
attachment["dubbo.tag"] = "beijing"
ctx = context.WithValue(ctx, constant.AttachmentKey, attachment)
err := userProvider.GetUser(ctx, []interface{}{"A001"}, user)
```
请求标签的作用域为每一次 invocation,使用 attachment 来传递请求标签,注意保存在 attachment 中的值将会在一次完整的远程调用中持续传递,得益于这样的特性,只需要在起始调用时,通过一行代码的设置,达到标签的持续传递。
### 4.1.3 规则详解
格式
Key 明确规则体作用到哪个应用。必填。
enabled=true 当前路由规则是否生效,可不填,缺省生效。
force=false 当路由结果为空时,是否强制执行,如果不强制执行,路由结果为空的路由规则将自动失效,可不填,缺省为 false。
runtime=false 是否在每次调用时执行路由规则,否则只在提供者地址列表变更时预先执行并缓存结果,调用时直接从缓存中获取路由结果。如果用了参数路由,必须设为 true,需要注意设置会影响调用的性能,可不填,缺省为 false。
priority=1 路由规则的优先级,用于排序,优先级越大越靠前执行,可不填,缺省为 0。
tags 定义具体的标签分组内容,可定义任意 n(n>=1)个标签并为每个标签指定实例列表。必填
name, 标签名称
addresses, 当前标签包含的实例列表
降级约定
request.tag=tag1 时优先选择 标记了 tag=tag1 的 provider。若集群中不存在与请求标记对应的服务,默认将降级请求 tag 为空的 provider;如果要改变这种默认行为,即找不到匹配 tag1 的 provider 返回异常,需设置 request.tag.force=true。
request.tag 未设置时,只会匹配 tag 为空的 provider。即使集群中存在可用的服务,若 tag 不匹配也就无法调用,这与约定 1 不同,携带标签的请求可以降级访问到无标签的服务,但不携带标签/携带其他种类标签的请求永远无法访问到其他标签的服务。
## 4.2 应用/服务级条件路由
可以在路由规则配置中配置多个条件路由及其粒度
Sample:
```yml
# dubbo router yaml configure file
routerRules:
- scope: application
key: BDTService
priority: 1
enable: false
force: true
conditions: ["host = 192.168.199.208 => host = 192.168.199.208 "]
- scope: service
key: com.ikurento.user.UserProvider
priority: 1
force: true
conditions: ["host = 192.168.199.208 => host = 192.168.199.208 "]
```
### 4.2.1 规则详解
#### 各字段含义
- scope 表示路由规则的作用粒度,scope 的取值会决定 key 的取值。必填。
- service 服务粒度
- application 应用粒度
- Key 明确规则体作用在哪个服务或应用。必填。
- scope=service 时,key 取值为[{group}/]{service}[:{version}]的组合
- scope=application 时,key 取值为 application 名称
- enabled=true 当前路由规则是否生效,可不填,缺省生效。
- force=false 当路由结果为空时,是否强制执行,如果不强制执行,路由结果为空的路由规则将自动失效,可不填,缺省为 false。
- runtime=false 是否在每次调用时执行路由规则,否则只在提供者地址列表变更时预先执行并缓存结果,调用时直接从缓存中获取路由结果。如果用了参数路由,必须设为 true,需要注意设置会影响调用的性能,可不填,缺省为 false。
- priority=1 路由规则的优先级,用于排序,优先级越大越靠前执行,可不填,缺省为 0。
- conditions 定义具体的路由规则内容。必填。
## 5 回顾与展望
Dubbo-go 处于一个比较稳定成熟的状态。目前新版本正处于往云原生方向的尝试,应用服务维度注册是首先推出的功能,这是一个和之前模型完全不一样的新注册模型。该版本是朝云原生迈进新一步的关键版本。除此之外,包含在该版本也有一些之前提到的优化。
下一个版本 v1.5.2,本次的关注重点以通信模型改进为主,除此之外,与 2.7.x 的兼容性、易用性及质量保证也是本次关注的信息。
在服务发现,会支持更加多的方式,如:文件、Consul。 从而使 Dubbo-go 在服务发现场景下,让用户有更多的选择,能适应更多的个性化场景。
另外 易用性及质量保证,主要关注的是 samples 与自动化构建部分。可降低用户上手 Dubbo-go 的难度,提高代码质量。
目前下一个版本正在紧锣密鼓的开发中,具体规划及任务清单,都已经在 Github 上体现。
更多信息:https://github.com/apache/dubbo-go/releases/tag/v1.5.1
# [Dubbo-go 发布 1.5 版,朝云原生迈出关键一步](https://mp.weixin.qq.com/s/zqRmQ3gsdKj47cO22Dwczw)
## 引语
计算机技术浪潮每 10 年都有一次技术颠覆,相关知识体系最迟每 5 年都会革新一次,大概每两年贬值一半,在应用服务通信框架领域亦然。凡是有长期生命的通信框架,大概有 5 年的成长期和 5 年的稳定成熟期。每个时代都有其匹配的应用通信框架,在 20 年前的 2G 时代,强跨语言跨平台而弱性能的 gRPC 是不会被采用的。
每个通信框架,不同的人从不同角度看出不同的结论:初学者看重易用易学,性能测评者注重性能,应用架构师考虑其维护成本,老板则考虑则综合成本。一个应用通信框架的性能固然重要,其稳定性和进化能力更重要,得到有效维护的框架可在长时间单位内降低其综合成本:学习成本、维护成本、升级成本和更换成本。
什么是 Dubbo-go?第一,它是 Dubbo 的 Go 语言版本,全面兼容 Dubbo 是其第一要义。第二,它是一个 Go 语言应用通信框架,会充分利用作为云原生时代第一语言---Go 语言的优势,扩展 dubbo 的能力。
2008 年诞生的 Dubbo 已有十多年历史,依靠阿里和其社区,历久弥新。2016 年发布的 Dubbo-go 也已进入第五个年头,如今全面兼容 Dubbo v2.7.x 的 Dubbo-go v1.5 终于发布了。
回首过往,Dubbo-go 已经具备如下能力:
- 互联互通:打通了 gRPC 和 Spring Cloud 生态;
- 可观测性:基于 OpenTracing 和 Prometheus,使得其在 Logging、Tracing 和 Metrics 方面有了长足进步;
- 云原生:Dubbo-go 实现了基于 Kubernetes API Server 为注册中心的通信能力,做到了升级成本最低。
毋庸讳言,相较于现有成绩,发展阶段的 Dubbo-go 对未来有更多的期待之处:
- 易用性:Dubbo-go 的入门成本并不低,把很多感兴趣者挡在了门外。但好消息是,随着 Dubbo-go 在阿里内部的逐步推开,阿里中间件团队对其进行了进一步的封装,经生产环境检验后会开放给社区使用。
- 云原生:目前的 Dubbo-go 的基于 kubernetes 的方案,从技术分层角度来看, Kubernetes API Server 终究是系统的运维态组件,不应该暴露给应用层,否则会造成 APIServer 自身通信压力过大,且系统整体风险很高:应用层使用不当,或者框架自身的流量方面的 bug,可能会把 APiServer 打垮,后果就是造成整体后端服务能力的瘫痪!所以应用层需要感知的是 kubernetes 提供给应用层的 Operator,不断进化的 Dubbo-go 计划在 v1.6 版本中发布 Dubbo-go Operator。
雄关漫道真如铁,而今迈步从头越。Dubbo-go 社区【钉钉群 23331795】与 Dubbo-go 同在。
## 应用维度注册模型
经过一段时间的努力之后,我们终于完成了应用维度的服务注册与发现。和原本已有的接口维度的注册模型比起来,新的注册模型有两个突出特点:
- 1.和主流的注册模型保持一致。目前的主流做法都是按照应用为基本单位来进行注册的,如 Spring Cloud。在支持应用维度注册之后,对于接下来的云原生支持,奠定了基础;
- 2.大幅度减轻对注册中心的压力。在该模型之下,从注册中心的视角看过去,集群规模只和实例数量成正比,而不是现有的和服务数量成正比;
当然,我们在设计的时候就考虑到了用户的迁移成本。要迁移到新的注册模型,只需要将现有使用的注册中心换成新的 `ServiceDiscoveryRegistry` 就可以。
ServiceDiscoveryRegistry 是支持多种实现的。目前来说,我们支持:
- nacos;
- etcd;
- zookeeper;
我们提倡新上线的业务尽量使用 nacos 和 etcd 这种更可靠稳定的注册中心。
## Metadata Report 元数据中心
v1.5 版本在支持应用维度注册模型时,有很重要的一个问题需要解决,即接口维度的元数据存储。服务维度的注册模型和应用维度的注册模型,本质的区别是往注册中心注册的数据维度的不一致。虽然我们在应用维度注册模型中,将接口维度的数据从注册中心中剔除了,但是在 rpc 的框架中,一个 consumer 要想真正找到想要调用的服务地址,就必须得到 provider 端开放的服务信息。这部分数据,在 v1.5 版本中,我们将它们存储到了元数据中心中。
元数据中心,是一个接口定义。泛指一块存储区域,可以对接口级别的元数据进行存储、读取,provider 端调用存储,consumer 端调用读取。元数据中心中的数据需要保持准确性、实时性。
目前元数据中心,有两个父类(Go 中没有继承,此处说的父子类,单纯指子类对父类的组合关系)实现,一个是 local 实现,一个是 remote 实现。local 实现是将 provider 的内存作为虚拟元数据中心,remote 实现是指依赖 ZooKeeper、etcd、nacos 等注册中心作为元数据中心。目前 remote 有 zookeeper、nacos、etcd 和 consul 的子类实现。即用户可以将元数据信息,通过以上的第三方注册中心进行数据存储和分发。
## Invocation 接口支持 attribute 属性
invocation 结构中新增 attribute 属性支持,用于流程内部的属性存储。和 attachment 不同点在于,attachment 会从 consumer 传递到 provider,但 attribute 属性不会。
## k8s 注册中心
在 v1.5 版本之前,k8s 注册中心的实现是通过直接使用 k8s client 中 Pod 对象的 List&&Watch 接口。在本次迭代中引入了 k8s informer。这样做的原因在于两点,首先一定的程度上来讲 dubbo-go 的 k8s 注册中心也是一个 k8s controller,使用 informer 的模式更加 k8s native。更重要的是社区计划后续向 CRD+Operator 的模式演进,informer 模式是对后续的演进的探索。除了这个铺垫之外,本次迭代还对跨 namespace 的服务发现做了支持。再有就是为了减少对 kube-apiserver List&&Watch 的压力,对 provider 和 consumer 的行为进行了区分,provider 不再进行 Watch 而仅对 kube-apiserver 进行写操作。
## 优化路由模型
在 1.5 版本之前,Router 模型中属性是包含:优先级与路由属性,Router Chain 只包含路由属性。从中能识别出其实 Router Chain 也是一种特殊 Router。1.5 版本之后,使 Router 更抽象,分离出其优先级属性,新增 Priority Router、Chain 继承 Router 使其变为特殊的 Router,使关系上看起来更加清晰。如下图:
![](../../pic/interview/what's-new-in-dubbo-go-v1.5-1.png)
## 回顾与展望
Dubbo-go 处于一个比较稳定成熟的状态。目前新版本正处于往云原生方向的尝试,应用服务维度注册是首先推出的功能,这是一个和之前模型完全不一样的新注册模型。该版本是我们朝云原生迈进新一步的关键版本。除此之外,包含在该版本也有一些之前提到的优化。
下一个版本 v1.5.1,虽然仍是以兼容 Dubbo 2.7.x 为主要任务,但在分布式能力的增强上,也是我们关注的重点。
在分布式事务方面,有一个重要的基于 Seata 扩展实现。通过增加过滤器,在服务端接收 xid 并结合 seata-golang[2] 达到支持分布式事务的目的。 从而使 Dubbo-go 在分布式场景下,让用户有更多的选择,能适应更多的个性化场景。
与此同时,在传输链路安全性上,TLS 安全传输链路是该版本重要功能之一。通过提供统一入口,未来能引入更多的与传输链路安全性相关的功能,适应用户不一样的使用场景。
注册中心模型上,支持多注册中心集群负载均衡。业务部署假设是双注册中心(图 1 ),从原来双注册中心中所有 Provider 一起选址。优化成选址时的多了一层注册中心集群间的负载均衡(图 2 )。
![](../../pic/interview/what's-new-in-dubbo-go-v1.5-2.png)
(图 1 )
![](../../pic/interview/what's-new-in-dubbo-go-v1.5-3.png)
(图 2 )
以前的 dubbo-go RPC 层直接复用了 getty 框架 的 RPC[3],未能实现协议和应用通信地址的隔离。阿里中间件展图同学重构了 dubbo-go RPC 层,实现了连接复用:可以实现 consumer 与 provider 端的同一个 TCP 连接上进行多协议通信。相关 PR 业已合并,会在 dubbo-go v1.5.1 中发布。
目前下一个版本正在紧锣密鼓的开发中,具体规划及任务清单[1] ,都已经在 Github 上体现。
[1] : https://github.com/apache/dubbo-go/projects/8
[2] : https://github.com/seata-golang/seata-golang
[3]: https://github.com/AlexStocks/getty/tree/master/rpc
# [dubbo-go K8s 注册中心的设计方案与实现](https://mp.weixin.qq.com/s/j6CIZpMrSk4VO27viUlDrg)
> 随着云原生的推广,越来越多的公司或组织将服务容器化,并将容器化后的服务部署在 K8s 集群中。
- 今天这篇文章将会介绍 dubbo-go 将 K8s 作为服务注册中心的方案设计,以及具体实现。到目前为止该方案的实现已经被合并到 dubbo-go 的 master 分支。具体实现为关于 Kubernetes 的 PullRequest 。
Kubernetes 的 PullRequest:
https://github.com/apache/dubbo-go/pull/400
## K8s 管理资源的哲学
K8s 作为容器集群化管理方案可以将管理资源的维度可主观的分为服务实例管理和服务接入管理。
1. 服务实例管理,主要体现方式为 Pod 设计模式加控制器模式。控制器保证具有特定标签(Label)的 Pod 保持在恒定的数量(多删,少补)。
2. 服务接入管理,主要为 Service ,该 Service 默认为具有特定标签(Label)的一批 Pod 提供一个 VIP(ClusterIP)作为服务的接入点,默认会按照 round-robin 的负载均衡策略将请求转发到真正提供服务的 Pod 。并且 CoreDNS 为该 Service 提供集群内唯一的域名。
## K8s 服务发现模型
为了明确 K8s 在服务接入管理提供的解决方案,我们以 kube-apiserver 提供的 API(HTTPS) 服务为例。K8s 集群为该服务分配了一个集群内有效的 ClusterIP ,并通过 CoreDNS 为其分配了唯一的域名 kubernetes 。如果集群内的 Pod 需要访问该服务时直接通过 https://kubernetes:443 即可完成。
![配图](../../pic/registry-center/design-and-implementation-of-dubbo-go-and-k8s-registry-1.png)
具体流程如上图所示(红色为客户端,绿色为 kube-apiserver ):
1. 首先客户端通过 CoreDNS 解析域名为 Kubernetes 的服务获得对应的 ClusterIP 为 10.96.0.1 。
2. 客户端向 10.96.0.1 发起 HTTPS 请求。
3. HTTPS 之下的 TCP 连接被 kube-proxy 创建的 iptables 的 PREROUTING 链拦截并 DNAT 为 10.0.2.16 或 10.0.2.15 。
4. Client 与最终提供服务的 Pod 建立连接并交互。
由此可见,K8s 提供的服务发现为域名解析级别。
## Dubbo 服务发现模型
同样为了明确 Dubbo 服务发现的模型,以一个简单的 Dubbo-Consumer 发现并访问 Provider 的具体流程为例。
![配图](../../pic/registry-center/design-and-implementation-of-dubbo-go-and-k8s-registry-2.png)
具体流程如上图所示:
1.Provider 将本进程的元数据注册到 Registry 中,包括 IP,Port,以及服务名称等。
2.Consumer 通过 Registry 获取 Provider 的接入信息,直接发起请求。
由此可见,Dubbo 当前的服务发现模型是针对 Endpoint 级别的,并且注册的信息不只 IP 和端口还包括其他的一些元数据。
## K8s service vs dubbo-go 服务
通过上述两个小节,答案基本已经比较清晰了。总结一下,无法直接使用 K8s 的服务发现模型的原因主要为以下几点:
1.K8s 的 Service 标准的资源对象具有的服务描述字段 中并未提供完整的 Dubbo 进程元数据字段因此,无法直接使用该标准对象进行服务注册与发现。
2.dubbo-go 的服务注册是基于每个进程的,每个 Dubbo 进程均需进行独立的注册。
3.K8s 的 Service 默认为服务创建 VIP ,提供 round-robin 的负载策略也与 Dubbo-go 自有的 Cluster 模块的负载策略形成了冲突。
## Dubbo-go 当前的方案
### 服务注册
K8s 基于 Service 对象实现服务注册/发现。可是 dubbo 现有方案为每个 dubbo-go 进程独立注册,因此 dubbo-go 选择将该进程具有的独有的元数据写入运行该 dubbo-go 进程的 Pod 在 K8s 中的 Pod 资源对象的描述信息中。
每个运行 dubbo 进程的 Pod 将本进程的元数据写入 Pod 的 Annotations 字段。为了避免与其他使用 Annotations 字段的 Operator 或者其他类型的控制器(istio)的字段冲突。
dubbo-go 使用 Key 为 dubbo.io/annotation value 为具体存储的 K/V 对的数组的 json 编码后的 base64 编码。
样例为:
```yaml
apiVersion: v1
kind: Pod
metadata:
annotations:
dubbo.io/annotation: W3siayI6Ii9kdWJibyIsInYiOiIifSx7ImsiOiIvZHViYm8vY29tLmlrdXJlbnRvLnVzZXIuVXNlclByb3ZpZGVyIiwidiI6IiJ9LHsiayI6Ii9kdWJiby9jb20uaWt1cmVudG8udXNlci5Vc2VyUHJvdmlkZXIvY29uc3VtZXJzIiwidiI6IiJ9LHsiayI6Ii9kdWJibyIsInYiOiIifSx7ImsiOiIvZHViYm8vY29tLmlrdXJlbnRvLnVzZXIuVXNlclByb3ZpZGVyIiwidiI6IiJ9LHsiayI6Ii9kdWJiby9jb20uaWt1cmVudG8udXNlci5Vc2VyUHJvdmlkZXIvcHJvdmlkZXJzIiwidiI6IiJ9LHsiayI6Ii9kdWJiby9jb20uaWt1cmVudG8udXNlci5Vc2VyUHJvdmlkZXIvY29uc3VtZXJzL2NvbnN1bWVyJTNBJTJGJTJGMTcyLjE3LjAuOCUyRlVzZXJQcm92aWRlciUzRmNhdGVnb3J5JTNEY29uc3VtZXJzJTI2ZHViYm8lM0RkdWJib2dvLWNvbnN1bWVyLTIuNi4wJTI2cHJvdG9jb2wlM0RkdWJibyIsInYiOiIifV0=
```
### 服务发现
依赖 kube-apiserver 提供了 WATCH 的功能。可以观察特定 namespace 内各 Pod 对象的变化。dubbo-go 为了避免 dubbo-go 进程 WATCH 到与 dubbo-go 进程无关的 Pod 的变化, dubbo-go 将 WATCH 的条件限制在当前 Pod 所在的 namespace ,以及仅 WATCH 具有 Key 为 dubbo.io/label Value 为 dubbo.io-value 的 Pod 。在 WATCH 到对应 Pod 的变化后实时更新本地 Cache ,并通过 Registry 提供的 Subscribe 接口通知建立在注册中心之上的服务集群管理其他模块。
### 总体设计图
![配图](../../pic/registry-center/design-and-implementation-of-dubbo-go-and-k8s-registry-3.png)
具体流程如上图所示:
1.启动 dubbo-go 的 Deployment 或其他类型控制器使用 K8s Downward-Api 将本 Pod 所在 namespace 通过环境变量的形式注入 dubbo-go 进程。
2.Consumer/Provider 进程所在的 Pod 启动后通过环境变量获得当前的 namespace 以及该 Pod 名称, 调用 kube-apiserver PATCH 功能为本 Pod 添加 Key 为 dubbo.io/label Value 为 dubbo.io-value 的 label。
3.Consumer/Provider 进程所在的 Pod 启动后调用 kube-apiserver 将本进程的元数据通过 PATCH 接口写入当前 Pod 的 Annotations 字段。
4.Consumer 进程通过 kube-apiserver LIST 当前 namespace 下其他具有同样标签的 Pod ,并解码对应的 Annotations 字段获取 Provider 的信息。
5.Consumer 进程通过 kube-apiserver WATCH 当前 namespace 下其他具有同样 label 的 Pod 的 Annotations 的字段变化,动态更新本地 Cache 。
## 总结
K8s 已经为其承载的服务提供了一套服务发现,服务注册,以及服务集群管理机制。而 dubbo-go 的同时也拥有自成体系的服务集群管理。这两个功能点形成了冲突,在无法调谐两者的情况, dubbo-go 团队决定保持 dubbo 自有的服务集群管理系,而选择性的放弃了 Service 功能,将元数据直接写入到 Pod 对象的 Annotations 中。
当然这只是 dubbo-go 在将 K8s 作为服务注册中心的方案之一,后续社区会以更加“云原生”的形式对接 K8s ,让我们拭目以待吧。
dubbo-go 社区钉钉群 :23331795 ,欢迎你的加入。
- 作者信息:
王翔,GithubID: sxllwx,就职于成都达闼科技有限公司,golang 开发工程师。
# [dubbo-go 可信 RPC 调用实现](https://mp.weixin.qq.com/s/30CjBKheCZClKaZCw1DRZA)
Apache Dubbo/Dubbo-Go 作为阿里巴巴开源的一款服务治理框架,因其适应 Java/Go 开发者面向接口的编程习惯、完全透明的调用方式、优越的性能以及强大的扩展性等优点,在国内使用非常广泛。除此之外,Dubbo 开源版本原生集成了很多开箱即用的服务治理功能,包括链路追踪,路由、负载均衡、服务注册发现、监控、认证等。
本文将讲解如何在 Dubbo/Dubbo-Go 中实现灵活、安全和高效的身份验证和授权方案。
## 定义可信
何为可信?可信的定义很广泛,依场景不同有不同的定义。在微服务架构中,每个服务都是无状态的,多个服务之间不可信,为了实现服务间更好地隔离,服务间应进行认证和鉴权。
如支付之类的业务场景,安全性敏感的服务会有限制匿名系统调用的需求,其他业务在接入该类敏感业务之前,需要通过审批方可正常调用,这就需要对这类敏感服务进行权限管控。尽管 Dubbo 开源版本中支持 Token 方式的鉴权实现,但是该实现方式总体来说安全性并不高,并且无法满足我们需要动态下发以及变更的灵活性需求。
针对于此,我们内部着重从巩固安全性和拓展灵活性层面重新设计了一套 Dubbo/Dubbo-Go 的服务间调用的鉴权认证功能。本文我们将主要从实现层面讲解其大致实现思路。
## 可信方案
总体而言,鉴权认证主要讨论以下两个问题:
1.身份认证:指验证应用的身份,每个应用在其生命周期内只有唯一身份,无法变更和伪造。
2.权限鉴定:根据身份信息鉴定权限是否满足调用。权限粒度可以进行控制。
我们通过 Access Key ID/Secret Access Key (后文简称为 AK/SK) 信息标识应用和应用之间的身份关系。例如上游 应用 A 依赖下游 服务 B 和 C,则 A 对 B 和 C 分别有一套 AK/SK,其相互独立没有任何关系,就算 A 服务 的 AK/SK 信息泄漏,也无法通过该 AK/SK 信息调用其他的服务。
在权限鉴定方面也借鉴了公有云开放 API 常用的 AK/SK 签名机制。在请求过程中使用 SK 签名生成 SigningKey,并通过 Dubbo 的 attachment 机制将额外的元数据信息以及 SigningKey 传输到服务端,交由服务端计算和验签,验签通过方能正常处理和响应。
签名过程主要通过如下三个方式进行加强 SigningKey 的可靠性和安全性。
1、验证请求者的身份,签名会通过对应应用的 SK 作为加密密钥对请求元数据(以及参数)进行加密,保证签名的唯一性和不可伪造性。
2、支持对参数进行计算签名,防止非法篡改,若请求参数在传输过程中遭到非法篡改,则收到请求后服务端验签匹配将失败,身份校验无法通过,从而防止请求参数被篡改。考虑到签名以及验签过程中加入请求参数的计算可能会影响性能,这个过程是可选的。
3、防止重放攻击,每一次请求生成的 SigningKey 都具有指定的有效时间,如请求被截获,该请求无法在有效时间外进行调用,一定程度避免了重放攻击。
同时为了去掉明文配置,防止 AK/SK 信息泄漏,我们通过鉴权系统分发和管理所有 AK/SK 信息。通过对接内部审批流程,达到流程化和规范化,需要鉴权的应用会通过启动获取的方式拉当前应用分发出去或者是已被授权的 AK/SK 信息。这种方式也带来了另一种好处,新增、吊销以及更新权限信息也无需重启应用。
## 可信流程
结合上面的这些需求和方案,整个接入和鉴权流程图如下所示:
![](../../pic/rpc/dubbo-go-trusted-RPC-call-implementation-1.png)
整体流程如下:
1、使用该功能的应用需要提前申请对应的证书,并向提供服务的应用提交申请访问工单,由双方负责人审批通过后,请求鉴权服务中心自动生成键值对。
2、开启鉴权认证的服务在应用启动之后,会运行一个后台线程,长轮询远鉴权服务中心,查询是否有新增权限变动信息,如果有则进行全量/增量的拉取。
3、上游应用在请求需要鉴权的服务时,会通过 SK 作为签名算法的 key,对本次请求的元数据信息甚至是参数信息进行计算得到签名,通过 Dubbo 协议 Attachment 字段传送到对端,除此之外还有请求时间戳、AK 信息等信息。
4、下游应用在处理鉴权服务时会对请求验签,验签通过则继续处理请求,否则直接返回异常。
其中需要说明的是第三步,使用鉴权服务的应用和鉴权服务中心的交互需通过 HTTPS 的双向认证,并在 TLS 信道上进行数据交互,保证 AK/SK 信息传输的安全性。
该方案目前已经有 Java/Go 实现,均已合并到 dubbo/dubbo-go。除了默认的 Hmac 签名算法实现之外,我们将签名和认证方法进行抽象,以 dubbo-go 中的实现为例:
```go
// Authenticator
type Authenticator interface {
// Sign
// give a sign to request
Sign(protocol.Invocation, *common.URL) error
// Authenticate
// verify the signature of the request is valid or not
Authenticate(protocol.Invocation, *common.URL) error
}
```
使用者可通过 SPI 机制定制签名和认证方式,以及适配公司内部基础设施的密钥服务下发 AK/SK。
## 示例
以 Helloworld 示例 中的代码接入当前社区版本中的默认鉴权认证功能实现为例:
### Helloworld 示例:
https://github.com/apache/dubbo-samples/tree/master/golang/helloworld/dubbo
在无需改变代码的情况下,只需要在配置上增加额外的相关鉴权配置即可,dubbo-go 服务端配置示例如下:
```yml
services:
"UserProvider":
# 可以指定多个registry,使用逗号隔开;不指定默认向所有注册中心注册
registry: "hangzhouzk"
protocol: "dubbo"
# 相当于dubbo.xml中的interface
interface: "com.ikurento.user.UserProvider"
loadbalance: "random"
# 本服务开启auth
auth: "true"
# 启用auth filter,对请求进行验签
filter: "auth"
# 默认实现通过配置文件配置AK、SK
params:
.accessKeyId: "SYD8-23DF"
.secretAccessKey: "BSDY-FDF1"
warmup: "100"
cluster: "failover"
methods:
- name: "GetUser"
retries: 1
loadbalance: "random"
```
dubbo-go 客户端配置示例如下:
```yml
references:
"UserProvider":
# 可以指定多个registry,使用逗号隔开;不指定默认向所有注册中心注册
registry: "hangzhouzk"
protocol: "dubbo"
interface: "com.ikurento.user.UserProvider"
cluster: "failover"
# 本服务开启sign filter,需要签名
filter: "sign"
# 默认实现通过配置文件配置AK、SK
params:
.accessKeyId: "SYD8-23DF"
.secretAccessKey: "BSDY-FDF1"
methods:
- name: "GetUser"
retries: 3
```
可以看到,dubbo-go 接入鉴权认证的功能也十分简单。需要补充说明的是,配置文件文件中 ak/sk 都加了特殊前缀 ".",是为了说明该字段是敏感信息,不能在发起网络请求时传输出去,相关代码可参阅 dubbo-go-pr-509。
dubbo-go-pr-509:
https://github.com/apache/dubbo-go/pull/509
## 总结
Apache Dubbo 作为一款老而弥新的服务治理框架,无论是其自身还是其生态都还在飞速进化中。本文描述的最新实现的可信服务调用,是为了避免敏感接口被匿名用户调用而在 SDK 层面提供的额外保障,在 RPC 层面保障安全性。
dubbo-go 作为 Dubbo 生态中发展最快的成员,目前基本上保持与 Dubbo 齐头并进的态势。dubbo-go 社区钉钉群号为 23331795, 欢迎你的加入。
- 作者信息:
郑泽超,Apache Dubbo/Dubbo-Go committer,GithubID: CodingSinger,目前就职于上海爱奇艺科技有限公司,Java/Golang 开发工程师。
# [dubbo-go 中 REST 协议实现](https://mp.weixin.qq.com/s/9ngGYnkdcu14GkaPBjOBLg)
在社区小伙伴们的努力下,Apache/dubbo-go 在 v1.4.0 中支持 REST 协议了。
## 什么是 REST 协议
REST 是 **RE**presentational **S**tate **T**ransfer(表述性状态转移)的简写,是一种软件架构风格。虽然 REST 架构风格不是一定要基于 HTTP 协议进行传输,但是因为 HTTP 协议的通用性和易用性,现在越来越多的 web 服务采用基于 HTTP 协议实现 RESTful 架构。
在 dubbo-go 中的 REST 协议指的是一种基于 HTTP 协议的远程调用方式。简单的来讲,REST 协议就是把 dubbo 服务发布成 RESTful 风格的 HTTP 接口并且能够能像调用 dubbo 接口一样的方式调用 HTTP 接口。
## 为什么要支持 REST 协议
在没有 REST 协议之前,小伙伴们是否一直在苦恼这样几个问题:
1. 传统的 web 服务不能直接调用我们发布的 dubbo 服务
1. 前端不能直接调用 dubbo 服务
1. dubbo 服务不能发布 Open API
上述问题,就是 REST 协议解决的核心问题。现在我们很多应用场景都是需要与异构的系统进行交互,而 REST 采用的 HTTP 通信协议非常适合用来打通异构系统,如图:
![640.webp](https://cdn.nlark.com/yuque/0/2020/webp/1001139/1585566216491-32cab604-7e00-412a-a79c-49d9c67d810f.webp#align=left&display=inline&height=430&margin=%5Bobject%20Object%5D&name=640.webp&originHeight=430&originWidth=532&size=10938&status=done&style=none&width=532)
## REST 协议没那么简单
REST 协议核心要解决一个问题:**Go 方法到 HTTP 接口的双向映射**。普通 HTTP 调用 dubbo-go 服务,考虑的是 HTTP 到 **Go** 方法的映射;而 dubbo-go 服务调用 HTTP 服务,则是 **Go** 方法到 HTTP 接口的映射。
下面是我们要与 **Go** 方法要做映射的 HTTP 请求协议内容:
```http
POST /path/{pathParam}?queryParam=1 HTTP/1.1
Accept: application/json
Content-Type: application/json
Host: http://localhost:8080
{"id":1111}
```
在服务提供方,当上面这种请求发送到服务器时,我们要把它路由到下面这个 **Go** 方法中,在服务消费方,我们也可以通过调用下面的 **Go** 方法把方法参数转化为上面的 HTTP 请求:
```go
type Provider struct {
// 该方法应该对应上面的http请求
GetResult func(pathParam string, queryParam string, body interface{}, host string) (*Result, error)
}
```
在消费方调用 `GetResult` 时,传入各个参数的值:
- 变量 pathParam 的内容应该是字符串 "pathParam";
- 变量 queryParam 的内容应该是字符串 "1" ;
- 变量 body 应该是有以字符串 "id" 为 key ,1111 为 value 的一个 map;
- 当然 host 变量的内容应该是字符串 "[http://localhost:8080](http://localhost:8080)" 。
在服务端执行 `GetResult` 方法时,得到的参数会与消费方调用时传入的值相同。
总结下来,我们要建立以下这些映射关系
1. 路径映射
1. Header 处理(固定 Header 和 Header 值也是参数两种情况)
1. POST or GET or ...(HTTP 方法映射)
1. 参数映射
要完成这种映射,我们首先要解决的是,如何知道这种映射关系?
答案只有一个,通过用户配置。而用户配置所面临的困难是,复杂且琐碎。(解决思路是提供大量默认配置减轻配置的负担,自定义配置方式允许用户使用自己熟悉的配置形式)
另外一个难点在于,使用何种 web 框架的问题。有些公司内部使用的是自研的 web 框架,他们有成熟的技术基础和运维能力。于是就会考虑说,能不能让 dubbo-go 在支持 REST 协议的时候,能够让他们将 REST 协议使用的 web 框架替换成他们所期望的呢?
## 如何建立 HTTP 接口与方法的映射关系
下面我举一个 HTTP 接口与方法映射的具体例子:
**Go** 结构体定义如下:
```go
type UserProvider struct {
GetUser func(id string, name string, age int) (*User, error)
}
```
要发布的 HTTP 接口形式是:
[http://127.0.0.1/UserProvider/GetUser/{id}?name=test&age=1](http://127.0.0.1/UserProvider/GetUser/%7Bid%7D?name=test&age=1)
服务端配置如下:
```yaml
services:
"UserProvider":
//注册中心
registry: "zookeeper"
//启用REST协议
protocol : "rest"
//DUBBO的接口名
interface : "com.ikurento.user.UserProvider"
// 服务接口路径
rest_path: "/UserProvider"
methods:
- name: "GetUser"
// 方法接口路径
rest_path: "/GetUser/{id}"
// HTTP方法
rest_method: "GET"
// HTTP查询参数
rest_query_params: "1:name,2:age"
// HTTP路径参数
rest_path_params: "0:id"
// 可以提供的内容类型
rest_produces: "application/json;application/xml"
// 可以接受的客户端参数类型
rest_consumes: "application/json;charset=utf-8,*/*"
// HTTP Body
rest_body: -1
```
在配置文件中我们定义了方法的路径,HTTP 方法等接口参数,这里需要注意的是路径参数和查询参数的配置方式,0:name 的意思是查询参数 name 对应 `GetUser` 方法的第一个参数,还有 rest_body 配置的数字也是对应这方法的参数,这里没有 body 参数所有就配置了 `-1`
## REST 协议的调用过程
![Dubbogo的Rest协议.001.jpeg](../../pic/service-governance/implementation-of-rest-protocol-in-dubbo-go-1.jpeg)
上图展示了用户在 Consumer 端调用 `GetUser` 方法到 Provdier 端 `GetUser` 方法被执行的整个过程,在 `RestClient``RestServer` 中分别**实现了 Go 方法参数到 HTTP 请求的转换和 HTTP 请求到 Go 方法的转换,这是最为核心和复杂的部分。**换言之,我们在这里实现了前面提到的 Go 方法和 HTTP 请求的双向映射。
这里我们可以注意到 `RestClient``RestServer` 是可以用户自行扩展的,下面我将具体介绍一下在 REST 协议中有哪些扩展点设计。
## REST 协议的扩展点设计
基于 dubbo-go 良好的 extension 扩展设计,我们定义了多个扩展点,用户可以自定义功能实现。
### 自定义 HTTP 服务器
RestServer 的扩展接口:
```go
type RestServer interface {
// sever启动函数
Start(url common.URL)
// 发布接口
Deploy(restMethodConfig *rest_config.RestMethodConfig, routeFunc func(request RestServerRequest, response RestServerResponse))
// 删除接口
UnDeploy(restMethodConfig *rest_config.RestMethodConfig)
// server关闭
Destroy()
}
```
在 dubbo-go 的 v1.4.0 中默认使用 go-restful 作为 HTTP 服务器,如果用户想用其他 HTTP 容器可以实现上面的接口,并在配置文件中配置使用自己自定义的服务器。
这个接口中,最核心的方法是 Deploy,在 restMethodConfig 方法参数中有用户配置的接口路径等一系列参数,routeFunc 是 HTTP 接口需要被路由执行的函数。不同的 http 服务器会有不同的 request 和 response ,所以我们定义了 `RestServerRequest` 接口和 `RestServerResponse` 接口让用户进行适配。
### 自定义 HTTP 客户端
RestClient 的扩展接口:
```go
// RestOptions
type RestOptions struct {
RequestTimeout time.Duration
ConnectTimeout time.Duration
}
// RestClientRequest
type RestClientRequest struct {
Header http.Header
Location string
Path string
Method string
PathParams map[string]string
QueryParams map[string]string
Body interface{}
}
// RestClient user can implement this client interface to send request
type RestClient interface {
Do(request *RestClientRequest, res interface{}) error
}
```
最后的请求到客户端时,都会被封装为 `RestRequest`,用户可以非常简单快速的扩展自己的 Client 端。`RestOptions` 中有一些客户端的超时配置,在创建自己的客户端时需要根据这些配置初始化客户端。
### 自定义 REST 配置形式
前面提到,REST 协议一个很麻烦的地方在于,配置很繁琐很琐碎。Go 不同于 Java,可以通过注解的形式来简化配置。
所以我们考虑到用户不同的使用习惯和公司的配置风格,提供了这个扩展点。
ConfigReader 的扩展接口:
```go
type ConfigReader interface {
// Consumer配置读取
ReadConsumerConfig(reader *bytes.Buffer) error
// Provider配置读取
ReadProviderConfig(reader *bytes.Buffer) error
}
```
`ReadConsumerConfig``ReadProviderConfig` 方法的参数是配置文件的文件流,在实现方法中可以再次解析,也可以使用二次编译或者硬编码方式等其他方式读取配置。这是一个通用的配置读取接口,以后可以用来扩展 REST 配置之外的其他配置,所以需要在方法中调用方法设置配置,如下:
```go
// 设置Rest的消费者配置
config.SetRestConsumerServiceConfigMap(restConsumerServiceConfigMap)
// 设置Rest的提供者配置
config.SetRestProviderServiceConfigMap(restProviderServiceConfigMap)
```
## 如何添加 HTTP 过滤器
因为不同 HTTP 服务器的过滤器,拦截器或者是 middleware 添加方式都不同,所以我们很难定义一个接口满足所有服务器。因此我们单独为 go-restful 定义了一个添加 filter 的方法,这里我们需要注意的一点是必须在 REST 接口发布前添加 filter。
```go
server_impl.AddGoRestfulServerFilter(func(request *restful.Request, response *restful.Response, chain *restful.FilterChain) {
// 鉴权等功能
chain.ProcessFilter(request, response)
})
// 启动dubbo服务,发布rest等接口
config.Load()
```
## 展望
以上是关于 REST 协议的一些介绍,具体的实现我就不详细讲了,大家可以去参阅源码。
如果想看具体的 Example,请参考:
[https://github.com/dubbogo/dubbo-samples/tree/master/golang/general/rest](https://github.com/dubbogo/dubbo-samples/tree/master/golang/general/rest)
REST 未来需要支持 HTTPS 协议和基于 open tracing 标准 api 的链路追踪。REST 的配置信息未来也不是 REST 协议独有的,这些配置信息未来可以作为每个 dubbo 接口的元数据,存储到元数据中心,为网关提供 HTTP 协议与 dubbo 协议之间的映射关系。
- 作者信息:
蒋超,github id Patrick0308,在 杭州贝安云科技有限公司 任职服务开发工程师。
# [Dubbo/Dubbo-go 应用零成本接入 MOSN](https://mosn.io/docs/dev/dubbo-integrate/)
## Dubbo 介绍
Dubbo 最初是 **Java 开发的一套 RPC 框架**,随着社区的发展。当前 dubbo 也渐渐成为一套跨语言的解决方案。**除了 Java 以外,还有相应的 Go 实现**。有规律的版本发布节奏,社区较为活跃。
## Dubbo 服务 mesh 化
接入 service mesh 的应用,其服务发现应该由相应的 mesh 模块接管。一般由控制面将相应的服务发现配置进行订阅和下发。但这里存在几个问题:
如果公司是第一次接入 service mesh,不希望一次引入太多模块,这样会增加整体的运维负担。如果可以渐进地迁移到 service mesh 架构,例如先接入数据面,再接入控制面。那么就可以随时以较低的成本进行回滚。也不会给运维造成太大的压力。
每个公司都有自己的发展规划,并不是每个公司都完整地拥抱了云原生。大部分公司可能存在部分上云,部分未上云的情况,在迁移到 service mesh 时,也存在部分应用接入了 service mesh,而另一部分未接入的情况。需要考虑跨架构互通。
我们这里提出的方案希望能够解决这些问题。
### 服务发现接入
#### 配置工作
在配置文件中,我们配置了两个 listener:
一个是 serverListener,负责拦截外部进入的流量,转发给本地模块,这个方向的请求不需要做特殊处理,只要使用 xprotocol 转发给本机即可。
一个是 clientListener,负责拦截本机向外发起的请求,因为外部集群根据服务注册中心下发的 endpoint 列表动态变化,所以该 listener 对应的也是一个 特殊的 router 名 “dubbo”。,这里务必注意。
```json
"listeners": [
{
"name": "serverListener",
"address": "127.0.0.1:2046",
"bind_port": true,
"log_path": "stdout",
"filter_chains": [
{
"tls_context": {},
"filters": [
{
"type": "proxy",
"config": {
"downstream_protocol": "X",
"upstream_protocol": "X",
"router_config_name": "server_router",
"extend_config": {
"sub_protocol": "dubbo"
}
}
}
]
}
]
},
{
"name": "clientListener",
"address": "0.0.0.0:2045",
"bind_port": true,
"log_path": "stdout",
"filter_chains": [
{
"tls_context": {},
"filters": [
{
"type": "proxy",
"config": {
"downstream_protocol": "X",
"upstream_protocol": "X",
"router_config_name": "dubbo",
"extend_config": {
"sub_protocol": "dubbo"
}
}
}
]
}
]
}
]
```
#### 开发工作
第一步,在 MOSN 配置中增加 dubbo_registry 扩展选项:
```json
"extend": [
{
"type": "dubbo_registry",
"config": {
"enable": true,
"server_port": 20080,
"api_port": 22222,
"log_path": "/tmp"
}
}
]
```
该配置与 tracing、admin 等为平级配置。
第二步,针对接入的服务,需要简单修改 sdk 中的 pub、sub 环节代码:
pub 时,如果当前环境为接入 MOSN 环境(可通过配置系统下发的开关来判断),则调用 MOSN 的 pub 接口,而非直接去注册中心 pub。
sub 时,如果当前环境为接入 MOSN 环境,则调用 MOSN 的 sub 接口,不去注册中心 sub。
第三步,应用退出时,需要将所有 pub、sub 的服务执行反向操作,即 unpub、unsub。
在本文中使用 httpie 来发送 http 请求。使用 dubbo-go 中的样例程序作为我们的服务的 client 和 server。
接下来我们使用 httpie 来模拟各种情况下的 pub、sub 流程。
直连 client 与正常的 dubbo service 互通
例子路径
Service 是正常的 dubbo service,所以会自动注册到 zk 中去,不需要我们帮它 pub,这里只要 sub 就可以了,所以执行流程为:
第一步,修改 MOSN 配置,增加 dubbo_registry 的 extend 扩展。
第二步,mosn start。
第三步,start server。
第四步,subscribe service。
```sh
http --json post localhost:22222/sub registry:='{"type":"zookeeper", "addr" : "127.0.0.1:2181"}' service:='{"interface" : "com.ikurento.user.UserProvider", "methods" :["GetUser"], "group" : "", "version" : ""}' --verbose
```
第五步,start client。
在 client 中正确看到返回结果的话,说明请求成功了。
直连 client 与直连 dubbo service 互通
例子路径
直连的服务不会主动对自身进行发布,直连的 client 不会主动进行订阅。因此此例子中,pub 和 sub 都是由我们来辅助进行的。
第一步,修改 MOSN 配置,增加 dubbo_registry 的 extend 扩展。
第二步,mosn start
第三步,start server
第四步,subscribe service
```sh
http --json post localhost:22222/sub registry:='{"type":"zookeeper", "addr" : "127.0.0.1:2181"}' service:='{"interface" : "com.ikurento.user.UserProvider", "methods" :["GetUser"], "group" : "", "version" : ""}' --verbose
```
第五步,publish service
http --json post localhost:22222/pub registry:='{"type":"zookeeper", "addr" : "127.0.0.1:2181"}' service:='{"interface" : "com.ikurento.user.UserProvider", "methods" :["GetUser"], "group" : "", "version" : ""}' --verbose
第六步,start client
此时应该能看到 client 侧的响应。
正常的 client 与直连 dubbo service 互通
例子路径
Client 是正常 client,因此 client 会自己去 subscribe。我们只要正常地把服务 pub 出去即可:
第一步,修改 MOSN 配置,增加 dubbo_registry 的 extend 扩展。
第二步,mosn start
第三步,start server
第四步,publish service
```sh
http --json post localhost:22222/sub registry:='{"type":"zookeeper", "addr" : "127.0.0.1:2181"}' service:='{"interface" : "com.ikurento.user.UserProvider", "methods" :["GetUser"], "group" : "", "version" : ""}' --verbose
```
第五步,start client
此时应该能看到 client 侧的响应。
doc/pic/arch/dubbo-go-one-year-1.png

58.8 KiB

doc/pic/arch/dubbo-go-one-year-2.png

56.6 KiB

doc/pic/arch/dubbo-go-one-year-3.png

49.1 KiB

doc/pic/arch/dubbo-go-one-year-4.png

219 KiB

doc/pic/arch/dubbo-go-one-year-5.png

149 KiB

0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment