Skip to content
Snippets Groups Projects
Commit f4c03f70 authored by 邹毅贤's avatar 邹毅贤
Browse files

delete doc

parent eb098d00
No related branches found
No related tags found
No related merge requests found
Showing
with 0 additions and 4187 deletions
# Apache 软件发版流程
> author: wongoo@apache.org
> last updated: 2020-03-06
Apache开源软件是有社区驱动的,为了提高发布软件质量而指定了软件发布流程,本文主要介绍此流程,以给第一次发布打包的apacher参考。
如果你要准备打包一个apache软件了,想必你已经是一个项目的committer了,而且知道社区、PMC这些概念,而你现在还担任本次发布的 release manager 一职。
发版流程其实也很简单,无非如下:
1. 整理变更内容,打包并对打包文件签名;
2. 将签名文件上传apache svn仓库;
3. 发邮件请社区PMC大佬投票;
4. 投票通过后发一个投票结果通告邮件;
5. 发版
6. 发版邮件通告社区新版本发布;
下面详细整理发版的一些流程步骤,使用 dubbo 的子项目 dubbog-go-hessian2 发版为例!
## 1. 发版准备
发版文件需要签名,需要安装pgp工具.
```bash
$ brew install gpg
$ gpg --version
$ gpg --full-gen-key
(1) RSA and RSA (default) <-- RSA 类型
What keysize do you want? (2048) 4096 <-- key大小为4096
0 = key does not expire <-- 永不过期
Real name: Liu Yang
Email address: wongoo@apache.org
Comment: CODE SIGNING KEY
gpg: /Users/gelnyang/.gnupg/trustdb.gpg: trustdb created
gpg: key 7DB68550D366E4C0 marked as ultimately trusted
gpg: revocation certificate stored as '/Users/gelnyang/.gnupg/openpgp-revocs.d/1376A2FF67E4C477573909BD7DB68550D366E4C0.rev'
public and secret key created and signed.
pub rsa4096 2019-10-17 [SC]
1376A2FF67E4C477573909BD7DB68550D366E4C0
uid Liu Yang (CODE SIGNING KEY) <wongoo@apache.org>
sub rsa4096 2019-10-17 [E]
$ gpg --list-keys
gpg: checking the trustdb
gpg: marginals needed: 3 completes needed: 1 trust model: pgp
gpg: depth: 0 valid: 1 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 1u
/Users/gelnyang/.gnupg/pubring.kbx
----------------------------------
pub rsa4096 2019-10-17 [SC]
1376A2FF67E4C477573909BD7DB68550D366E4C0
uid [ultimate] Liu Yang (CODE SIGNING KEY) <wongoo@apache.org>
sub rsa4096 2019-10-17 [E]
# 公钥服务器是网络上专门储存用户公钥的服务器
# 通过key id发送public key到keyserver
$ gpg --keyserver pgpkeys.mit.edu --send-key 1376A2FF67E4C477573909BD7DB68550D366E4C0
gpg: sending key 7DB68550D366E4C0 to hkp://pgpkeys.mit.edu
# 其中,pgpkeys.mit.edu为随意挑选的keyserver,keyserver列表为:https://sks-keyservers.net/status/,为相互之间是自动同步的,选任意一个都可以。
# 如果有多个public key,设置默认key。修改 ~/.gnupg/gpg.conf
$ vi ~/.gnupg/gpg.conf
default-key 7DB68550D366E4C0
# 如果有多个public key, 也可以删除无用的key:
### 先删除私钥,再删除公钥
$ gpg --yes --delete-secret-keys shenglicao2@gmail.com ###老的私钥,指明邮箱即可
$ gpg --delete-keys 1808C6444C781C0AEA0AAD4C4D6A8007D20DB8A4
## 由于公钥服务器没有检查机制,任何人都可以用你的名义上传公钥,所以没有办法保证服务器上的公钥的可靠性。
## 通常,你可以在网站上公布一个公钥指纹,让其他人核对下载到的公钥是否为真。
# fingerprint参数生成公钥指纹:
$ gpg --fingerprint wongoo
pub rsa4096 2019-10-17 [SC]
1376 A2FF 67E4 C477 5739 09BD 7DB6 8550 D366 E4C0
uid [ultimate] Liu Yang (CODE SIGNING KEY) <wongoo@apache.org>
sub rsa4096 2019-10-17 [E]
# 将上面的 fingerprint (即 1376 A2FF 67E4 C477 5739 09BD 7DB6 8550 D366 E4C0)粘贴到自己的用户信息中:
# https://id.apache.org OpenPGP Public Key Primary Fingerprint:
```
> 详细参考:
> - 发布签名: http://www.apache.org/dev/release-signing.html
> - 发布策略: http://www.apache.org/dev/release-distribution
> - 将密钥上传到公共密钥服务器: https://www.apache.org/dev/openpgp.html#generate-key
## 2. 打包签名
准备打包前(尤其提第一次打包)需要注意以下内容:
- 每个文件的LICENSE头部是否正确, 包括 `*.java`, `*.go`, `*.xml`, `Makefile`
- LICENSE 文件是否存在
- NOTICE 文件是否存在
- CHANGE.md 是否存在 (变更内容格式符合规范)
以上可以参考其他已发布项目的配置。
```
# NOTICE: 这里切分支,分支名称不要和版本号(tag用)类似,不然会有冲突
$ git checkout -b 1.4
$ git tag -a v1.4.0-rc1 -m "v1.4.0 release candidate 1"
$ git push --tags
# 打包
$ git archive --format=tar 1.4 --prefix=dubbo-go-hessian2-v1.4.0/ | gzip > dubbo-go-hessian2-v1.4.0-src.tar.gz
# 签名
$ gpg -u wongoo@apache.org --armor --output dubbo-go-hessian2-v1.4.0-src.tar.gz.asc --detach-sign dubbo-go-hessian2-v1.4.0-src.tar.gz
# 验证签名
$ gpg --verify dubbo-go-hessian2-v1.4.0-src.tar.gz.asc dubbo-go-hessian2-v1.4.0-src.tar.gz
# hash
$ shasum -a 512 dubbo-go-hessian2-v1.4.0-src.tar.gz > dubbo-go-hessian2-v1.4.0-src.tar.gz.sha512
# 验证 hash
$ shasum --check dubbo-go-hessian2-v1.4.0-src.tar.gz.sha512
```
> 发布版本: http://www.apache.org/dev/release-publishing.html
## 3. 上传打包文件到svn仓库
```
$ svn checkout https://dist.apache.org/repos/dist/dev/dubbo
$ cd dubbo
# 更新
$ svn update
# 添加 签名 和 public key 到KEYS文件并提交到SVN仓库
# 这里是将公钥KEYS放到根目录, 有的项目放到本次打包文件目录
$ (gpg --list-sigs wongoo && gpg --armor --export wongoo) >> KEYS
$ mkdir -p dubbo-go-hessian2/v1.4.0-rc1
# 拷贝相关文件到新建目录下
$ tree dubbo-go-hessian2
dubbo-go-hessian2
└── v1.4.0-rc1
├── dubbo-go-hessian2-v1.4.0-src.tar.gz
├── dubbo-go-hessian2-v1.4.0-src.tar.gz.asc
└── dubbo-go-hessian2-v1.4.0-src.tar.gz.sha512
$ svn add dubbo-go-hessian2
$ svn add dubbo-go-hessian2/*
$ svn status
$ svn commit --username wongoo -m "Release dubbo-go-hessian2 v1.4.0-rc1"
```
> 详细参考: svn版本管理 https://www.apache.org/dev/version-control.html
## 4. 发投票 [VOTE] 邮件
发任何邮件都是有一定格式的,你加入社区邮件列表后,就会收到很多这样的邮件,多看看就知道了,具体邮件范本参考文章后面的邮件范本。
发完【VOTE】邮件,私下沟通群里面请大佬PMC投票。
PMC投票会对你上传打包文件进行相关检查,
详细可以了解孵化中的项目发布完整的检查项参考: https://cwiki.apache.org/confluence/display/INCUBATOR2/IncubatorReleaseChecklist
收到3个binding邮件且超过72小时后,就可以发 投票结果 [RESULT] [VOTE] 邮件了。
> 原则上只有PMC的投票才算binding邮件, 当然也可以由社区决定。
这一步骤最常见有以下问题:
- 文件签名有问题
- 引用项目LICENSE问题
- 单元测试不通过
> 另外需要注意: 一个apache项目可能包含很多子项目,项目的PMC可能只对主项目比较了解, 他们并不清楚如何将子项目跑起来,也不知道如何跑单元测试,最好在邮件中附带一个如何进行单元测试的连接。例如 PMC 最了解 java,但子项目是golang,python,js等,你需要告诉他们如何测试你的项目。
可以参考投票规则: https://www.apache.org/foundation/voting.html
## 5. 发布版本
当正式发布投票成功后,先发[Result]邮件,然后就准备 release package。
将之前在dev下发布的对应rc文件夹下的源码包、签名文件和hash文件拷贝到另一个目录 v1.4.0,
注意文件名字中不要rcxx (可以rename,但不要重新计算签名,hash可以重新计算,结果不会变)。
将release包移动到正式版目录。如果你的软件是需要客户从apache下载的,则这一步是必须的。如果不是,比如golang引用github打包地址的则可以忽略。
```
svn up
cd dubbo-go-hessian2
svn move v1.4.0-rc1 v1.4.0
svn status
svn commit --username wongoo -m "Release dubbo-go-hessian2 v1.4.0"
```
移到发版目录后,还需要进行相应的正式版本发布, 这里将具体发布方式整理到单独的章节 `7. 不同语言版本发布`,因为发布流程马上就要结束了 ^v^
## 6. 新版本通告 ANNOUNCE 邮件
恭喜你你已经到发版最后一步了,邮件格式参考以下邮件范本!
## 7. 不同语言版本发布
### 7.1 golang
在 github 基于投票分支发布了 release 版本。
### 7.2 java
java项目发版需发布到java maven仓库。
TODO
### 7.3 js
js项目发版需发布到npm仓库。
TODO
### 7.4 python
TODO
## 8. 邮件范本
### 8.1. 提出发版投票
- TO: dev@dubbo.apache.org
- Title: [VOTE] Release Apache dubbo-go-hessian2 v1.4.0 RC1
```
Hello Dubbo/Dubbogo Community,
This is a call for vote to release Apache dubbo-go-hessian2 version v1.4.0 RC1.
The release candidates: https://dist.apache.org/repos/dist/dev/dubbo/dubbo-go-hessian2/v1.4.0-rc1/
Git tag for the release: https://github.com/apache/dubbo-go-hessian2/tree/1.4
Hash for the release tag: 4c31e88c35afe84c0321d9f12f036e6d3c8962d0
Release Notes: https://github.com/apache/dubbo-go-hessian2/blob/1.4/CHANGE.md
The artifacts have been signed with Key :7DB68550D366E4C0, which can be found in the keys file:
https://dist.apache.org/repos/dist/dev/dubbo/KEYS
The vote will be open for at least 72 hours or until necessary number of votes are reached.
Please vote accordingly:
[ ] +1 approve
[ ] +0 no opinion
[ ] -1 disapprove with the reason
Thanks,
The Apache Dubbo-go Team
```
### 8.2. PMC 投票邮件回复
范例1:
```
+1 approve <-- 首先表明同不同意
I have checked: <-- 其次要说明自己检查了哪些项
1.source code can build <-- 能否构建
2.tests can pass in my local <-- 单元测试能否通过
3. NOTICE LICENSE file exist <-- 协议文件是否存在
4.git tag is correct <-- git tag 是否正确
there is one minor thing that in change logs file, there is no space
between text And link. I suggest add one to make it looks better. <--- 一些其他改进建议
```
范例2:
```
+1
I checked the following items:
[v] Are release files in correct location? <-- 发布文件目录是否正确
[v] Do release files have the word incubating in their name?
[v] Are the digital signature and hashes correct? <-- 签名、hash是否正确
[v] Do LICENSE and NOTICE files exists?
[v] Is the LICENSE and NOTICE text correct? <-- 协议文本是否正确
[v] Is the NOTICE year correct? <-- 注意年份是否正确
[v] Un-included software dependencies are not mentioned in LICENSE or NOTICE? <-- 没有包含协议或注意没有提到的软件依赖
[v] License information is not mentioned in NOTICE? <-- 协议信息没有在注意中提及
[x] Is there any 3rd party code contained inside the release? If so: <-- 是否包含第三方代码
[ ] Does the software have a compatible license?
[ ] Are all software licenses mentioned in LICENSE?
[ ] Is the full text of the licenses (or pointers to it) in LICENSE?
Is any of this code Apache licensed? Do they have NOTICE files? If so:
[ ] Have relevant parts of those NOTICE files been added to this NOTICE file?
[v] Do all source files have ASF headers? <-- 是否所有源码都有ASF头部
[v] Do the contents of the release match with what's tagged in version control? <-- 发布的文件是否和github中tag标记的版本一致
[x] Are there any unexpected binary files in the release? <-- 是否包含不应该存在的二进制文件
[v] Can you compile from source? Are the instruction clear? <-- 能否编译?指令是否明确?
On my mac laptop, I could compile successfully but there's one failed unit
test against net.go. I believe this issue [1] can be fixed with [2] in the
next release. <-- 编译问题及建议
Is the issue minor? <-- 编译存在的问题是否都是较小的?
[v] Yes [ ] No [ ] Unsure
Could it possibly be fixed in the next release? <-- 能否在下一版本修复?
[v] Yes [ ] No [ ] Unsure
I vote with: <-- 我的投票
[v] +1 release the software
[ ] +0 not sure if it should be released
[ ] -1 don’t release the software because...
Regards,
-Ian.
1. https://github.com/apache/dubbo-go/issues/207
2. https://github.com/apache/dubbo-go/pull/209
```
范例3:
```
+1
I checked the following items:
[√] Do LICENSE and NOTICE files exists?
[√] Is the LICENSE and NOTICE text correct?
[√] Is the NOTICE year correct?
[√] Do all source files have ASF headers?
[√] Do the contents of the release match with what's tagged in version control?
[√] Can you compile from source?
I could compile successfully but there's failed units test. I run the unit
test refer to :https://github.com/apache/dubbo-go#running-unit-tests .
But I think it is not matter, the test can be fixed in next release.
I vote with:
[√] +1 release the software
```
范例4:
```
Great improvement over the previous release but there are still issues from the last vote that have not been resolved. e.g. [6][7][8]
Can someone tell me if these files [1][2][3][4][5] are just missing ASF headers or have a different license?
If they are just missing headers and [6][7][8] explained then it +1 form me, otherwise it’s probably a -1.
Can people please carefully check the contents, and write down what you checked, rather than just saying +1.
I checked:
- signatures and hashes good
- LICENSE is missing the appendix (not a major issue)
- LICENSE may be is missing some information[1][2][3][4][5]
- NOTICE is fine
- No binaries in source release
- Some files are missing ASF headers or other license headers [1][2][3][4][5] - please fix
Thanks,
Justin
1. dubbo-go-1.1.0/cluster/loadbalance/round_robin_test.go
2. dubbo-go-1.1.0/common/extension/router_factory.go
3. dubbo-go-1.1.0/config_center/configuration_parser.go
4. dubbo-go-1.1.0/config_center/configuration_parser_test.go
5. dubbo-go-1.1.0/registry/zookeeper/listener_test.go
6. dubbo-go-1.1.0/cluster/loadbalance/least_active.go
7. dubbo-go-1.1.0/protocol/RpcStatus.go
8. dubbo-go-1.1.0/filter/impl/active_filter.go
```
### 8.3. 发 [RESULT] [VOTE] 投票结果通知邮件
- TO: dev@dubbo.apache.org
- Title: [RESULT] [VOTE]: Release Apache dubbo-go-hessian2 v1.4.0 RC1
```
Hello Dubbo/Dubbogo Community,
The release dubbo-go-hessian2 v1.4.0 RC1 vote finished, We’ve received 3 +1 (binding) votes.
+1 binding, Stocks Alex
+1 binding, Ian Luo
+1 binding, Jun Liu
The vote and result thread:
https://lists.apache.org/thread.html/r8070f3b00984888069dd4ddad1bbc424cde51ea68b6ff0520e609e18%40%3Cdev.dubbo.apache.org%3E
The vote passed. Thanks all.
I will proceed with the formal release later.
Best regards,
The Apache Dubbogo Team
```
### 8.4. 发 Announce 发版邮件
- TO: dev@dubbo.apache.org
- [ANNOUNCE] Apache Dubbo version 2.7.4 Released
```
Hello Community,
The Apache Dubbo team is pleased to announce that the 2.7.4 has been
released.
Apache Dubbo™ is a high-performance, java based, open source
RPC framework. Dubbo offers three key functionalities, which include
interface based remote call, fault tolerance & load balancing, and
automatic service registration & discovery.
Both the source release[1] and the maven binary release[2] are available
now, you can also find the detailed release notes in here[3].
If you have any usage questions, or have problems when upgrading or find
any problems about enhancements included in this release, please don’t
hesitate to let us know by sending feedback to this mailing list or filing
an issue on GitHub[4].
[1] http://dubbo.apache.org/en-us/blog/download.html
[2] http://central.maven.org/maven2/org/apache/dubbo
[3] https://github.com/apache/dubbo/releases
[4] https://github.com/apache/dubbo/issues
```
## 9. 参考
- dubbo发布流程: http://dubbo.apache.org/zh-cn/docs/developers/committer-guide/release-guide_dev.html
- doris发布流程: https://github.com/apache/incubator-doris/blob/master/docs/documentation/cn/community/release-process.md
- spark发布流程: http://spark0apache0org.icopy.site/release-process.html
### How to release a new version?
---
* 1 Check the time range of NOTICE;
* 2 Add the features to the feature list of README.md/README_CN.md/CHANGE.md;
* 3 Check whether every code file has the Apache License 2.0 or not;
* 4 There should not be author info(name & email etc) exist in code file;
* 5 Run all unit tests;
* 6 Run all samples in apache/dubbo-samples/golang;
* 7 Write "What's New" by releaser who should be an apache/dubbo-go committer;
* 8 And then, u can release a new version refer to [Apache 软件发版流程](./apache-release-procedure-20200306.md);
\ No newline at end of file
# [dubbo-go 的开发、设计与功能介绍](https://www.infoq.cn/article/7JIDIi7pfwDDk47EpaXZ)
## dubbo-go 的前世今生
![dubbo-go 的开发、设计与功能介绍](../../pic/arch/dubbo-go-design-implement-and-featrues-a.png)
dubbo-go 是目前 Dubbo 多语言生态最火热的项目。dubbo-go 最早的版本应该要追溯到 2016 年,由社区于雨同学编写 dubbo-go 的初版。当时很多东西没有现成的轮子,如 Go 语言没有像 netty 一样的基于事件的网络处理引擎、 hessian2 协议没有 Go 语言版本实现,加上当时 Dubbo 也没有开始重新维护。所以从协议库到网络引擎,再到上层 dubbo-go ,其实都是从零开始写的。
在 2018 年,携程开始做 Go 语言的一些中间件以搭建内部的 Go 语言生态,需要有一个 Go 的服务框架可以与携程的现有 dubbo soa 生态互通。所以由我负责重构了 dubbo-go 并开源出这个版本。当时调研了很多开源的 Go 语言服务框架,当时能够支持 hessian2 协议的并跟 Dubbo 可以打通的仅找到了当时于雨写的 dubbo-go 早期版本。由于携程对社区版本的 Dubbo 做了挺多的扩展,源于对扩展性的需求我们 Go 语言版本需要一个更易于扩展的版本,加上当时这个版本本身的功能也比较简单,所以我们找到了作者合作重构了一个更好的版本。经过了大半年时间,在上图第三阶段 19 年 6 月的时候,基本上已经把 dubbo-go 重构了一遍,总体的思路是参考的 Dubbo 整体的代码架构,用 Go 语言完全重写了一个完整的具备服务端跟消费端的 Golang rpc/ 微服务框架。
后来我们将重构后的版本 dubbo-go 1.0 贡献给 Apache 基金会,到现在已经过去了两个多月的时间,近期社区发布了 1.1 版本。目前为止,已经有包括携程在内的公司已经在生产环境开始了试用和推广。
## Start dubbo-go
现在的 dubbo-go 已经能够跟 Java 版本做比较好的融合互通,同时 dubbo-go 自身也是一个完成的 Go 语言 rpc/ 微服务框架,它也可以脱离 java dubbo 来独立使用。
这边简单介绍一下用法,写一个 hello world 的例子。
![dubbo-go 的开发、设计与功能介绍](../../pic/arch/dubbo-go-design-implement-and-featrues-b.png)
上图是一个简单的 java service ,注册为一个 Dubbo 服务,是一个简单的获取用户信息的例子。
![dubbo-go 的开发、设计与功能介绍](../../pic/arch/dubbo-go-design-implement-and-featrues-c.png)
上图是 dubbo-go 的客户端,来订阅和调用这个 Java 的 Dubbo 服务。Go 语言客户端需要显式调用 SetConsumerService 来注册需要订阅的服务,然后通过调用 dubbo-go-hessian2 库的 registerPOJO 方法来注册 user 对象,做 Java 和 Go 语言之间的自定义 pojo 类型转换。具体的服务调用方法就是声明一个的 GetUser 闭包,便可直接调用。
![dubbo-go 的开发、设计与功能介绍](../../pic/arch/dubbo-go-design-implement-and-featrues-d.png)
上图,同样的可以基于 dubbo-go 发布一个 GetUser 的服务端,使用方式类似,发布完后可以被 dubbo java 的客户端调用。
![dubbo-go 的开发、设计与功能介绍](../../pic/arch/dubbo-go-design-implement-and-featrues-e.png)
如上图所示,现在已经做到了这样一个程度,同样一份 dubbo-go 客户端代码,可以去调用 dubbo-go 的服务端,也可以去调用 Dubbo Java 的服务端;同样一份 dubbo-go 的服务端代码,可以被 dubbo-go 客户端和 Java 客户端调用,所以基本上使用 Dubbo 作为 PPC 框架的 Go 语言应用跟 Java 应用是没有什么阻碍的,是完全的跨语言 RPC 调用。更重要的是 dubbo-go 继承了 Dubbo 的许多优点,如易于扩展、服务治理功能强大,大家在用 Go 语言开发应用的过程中,如果也遇到类似需要与 Dubbo Java 打通的需求,或者需要找一个服务治理功能完备的 Go 微服务框架,可以看下我们 dubbo-go 项目。
## dubbo-go 的组成项目
下面介绍一下 dubbo-go 的组成项目,为了方便可以被其他项目直接复用, dubbo-go 拆分成了多个项目,并全部以 Apache 协议开源。
**apache/dubbo-go**
dubbo-go 主项目, Dubbo 服务端、客户端完整 Go 语言实现。
**apache/dubbo-go-hession2**
目前应用最广泛,与 Java 版本兼容程度最高的 hessian2 协议 Go 语言实现,已经被多个 GolangRPC & Service Mesh 项目使用。
**dubbo-go/getty**
dubbo-go 异步网络 I/O 库,将网络处理层解耦。
**dubbo-go/gost**
基本类库,定义了 timeWheel、hashSet、taskPool 等。
**dubbo-go/dubbo-go-benchmark**
用于对 dubbo-go 进行简单的压力测试,性能测试。
**apache/dubbo-go-hessian2**
![dubbo-go 的开发、设计与功能介绍](../../pic/arch/dubbo-go-design-implement-and-featrues-f.png)
先简单介绍一下 dubbo-go-hessian2 项目。该项目就是 hessian2 协议的 Go 语言实现,最基本的可以将 Java 的基本数据类型和复杂数据类型(如一些包装类和 list 接口实现类)与 golang 这边对应。
详情可以参考: [https://github.com/hessian-group/hessian-type-mapping](https://github.com/hessian-group/hessian-type-mapping)
另外 Dubbo Java 服务端可以不捕获异常,将异常类通过 hession2 协议序列化通过网络传输给消费端,消费端进行反序列化对该异常对象并进行捕获。我们经过一段时间的整理,目前已经支持在 Go 消费端定义对应 Java 的超过 40 种 exception 类,来实现对 Java 异常的捕获,即使用 dubbo-go 也可以做到直接捕获 Java 服务端抛出的异常。
另外对于 Java 端 BigDecimal 高精度计算类的支持。涉及到一些金融相关的计算会有类似的需求,所以也对这个类进行了支持。
其他的,还有映射 java 端的方法别名,主要的原因是 Go 这边语言的规约,需要被序列化的方法名必须是首字母大写。而 Java 这边没有这种规范,所以我们加了一个 hessian 标签的支持,可以允许用户手动映射 Java 端的方法名称。
基本上现在的 dubbo-go 已经满足绝大多数与 Java 的类型互通需求,我们近期也在实现对 Java 泛型的支持。
**dubbo-go/getty**
![dubbo-go 的开发、设计与功能介绍](../../pic/arch/dubbo-go-design-implement-and-featrues-g.png)
Go 语言天生就是一个异步网络 I/O 模型,在 linux 上 Go 语言写的网络服务器也是采用的 epoll 作为最底层的数据收发驱动, 这跟 java 在 linux 的 nio 实现是一样的。所以 Go 语言的网络处理天生就是异步的。我们需要封装的其实是基于 Go 的异步网络读写以及之后的处理中间层。getty 将网络数据处理分为三层,入向方向分别经过对网络 i/o 封装的 streaming 层、根据不同协议对数据进行序列化反序列化的 codec 层,以及最后数据上升到需要上层消费的 handler 层。出向方向基本与入向经过的相反。每个链接的 IO 协程是成对出现的,比如读协程负责读取、 codec 逻辑然后数据到 listener 层,然后最后的事件由业务协程池来处理。
该项目目前是与 dubbo-go 解耦出来的,所以大家如果有类似需求可以直接拿来用,目前已经有对于 tcp/udp/websocket 的支持。
**Apache / dubbo-go**
![dubbo-go 的开发、设计与功能介绍](../../pic/arch/dubbo-go-design-implement-and-featrues-h.png)
dubbo-go 主项目,我们重构的这一版主要是基于 Dubbo 的分层代码设计,上图是 dubbo-go 的代码分层。基本上与 Java 版本 Dubbo 现有的分层一致,所以 dubbo-go 也继承了 Dubbo 的一些优良特性,比如整洁的代码架构、易于扩展、完善的服务治理功能。
我们携程这边,使用的是自己的注册中心,可以在 dubbo-go 扩展机制的基础上灵活扩展而无需去改动 dubbo-go 的源代码。
## dubbo-go 的功能介绍
**dubbo-go 已实现功能**
目前 dubbo-go 已经实现了 Dubbo 的常用功能(如负责均衡、集群策略、服务多版本多实现、服务多注册中心多协议发布、泛化调用、服务降级熔断等),其中服务注册发现已经支持 zookeeper/etcd/consul/nacos 主流注册中心。这里不展开详细介绍,目前 dubbo-go 支持的功能可以查看项目 readme 中的 feature list ,详情参考: [https://github.com/apache/dubbo-go#feature-list](https://github.com/apache/dubbo-go#feature-list)
目前社区正在开发中的功能,主要是早期用户使用过程中提出的一些需求,也是生产落地一些必需的需求,如监控、调用链跟踪以及服务路由、动态配置中心等更高级的服务治理需求。
**dubbo-go 功能介绍之泛化调用**
![dubbo-go 的开发、设计与功能介绍](../../pic/arch/dubbo-go-design-implement-and-featrues-i.png)
这里详细做几个重点功能的介绍。首先是泛化调用,如上图,这个也是社区同学提的需求。该同学公司内部有很多 Dubbo 服务,他们用 Go 做了一个 api gateway 网关,想要把 Dubbo 服务暴露成外网 http 接口。因为内部的 Dubbo 服务比较多,不可能每一个 Dubbo 服务都去做一个消费端接口去做适配,这样的话一旦服务端改动,客户端也要改。所以他这边的思路是做基于 dubbo-go 做泛化调用, api-gateway 解析出外网请求的地址,解析出想要调用的 Dubbo 服务的目标。基于 dubbo-go consumer 泛化调用指定 service、method ,以及调用参数。
具体的原理是, dubbo-go 这边作为消费端,实际会通过本地 genericService.invoke 方法做代理,参数里面包含了 service name,method name ,还包含被调用目标 service 需要的参数类型、值等数据,这些数据后面会通过 dubbo-go-hession2 做转换,会将内容转化成 map 类型,经过网络发送到对应的 Java 服务端,然后 Java 那边是接收的 map 类型的参数,会自动反序列化成自己的 pojo 类型。这样就实现了 dubbo-go 作为客户端,泛化调用 Dubbo 服务端的目的。
**dubbo-go 功能介绍之降级熔断**
![dubbo-go 的开发、设计与功能介绍](../../pic/arch/dubbo-go-design-implement-and-featrues-j.png)
降级熔断这边是基于的是大家比较熟悉的 hystrix 的 Go 语言版本,基于 hystrix ,用户可以定义熔断规则和降级触发的代码段。降级熔断支持是作为一个独立的 dubbo-go filter ,可以灵活选择是否启用,如果不启用就可以在打包的时候不将依赖引入。Filter 层是 dubbo-go 中对于请求链路的一个责任链模式抽象,目前有许多功能都是基于动态扩展 filter 链来实现的,包括 trace、leastactive load balacne、log 等。降级熔断设计成一个服务调用端独立的 filter 可以灵活满足调用端视角对于微服务架构中“防雪崩“的服务治理需求。
**dubbo-go 功能介绍之动态配置**
关于动态配置中心, Dubbo 的 2.6 到 2.7 版本做了一个比较大的变化,从之前的 url 配置形式过渡到了支持配置中心 yaml 格式配置的形式,治理粒度也从单服务级别的配置支持到了应用级别的配置,不过在 2.7 版本中还是兼容 2.6 版本 url 形式进行服务配置的。dubbo-go 这边考虑到跟 Dubbo2.6 和 2.7 的互通性,同样支持 url 和配置文件方式的服务配置,同时兼容应用级别和服务级别的配置,跟 dubbo 保持一致,目前已经实现了 zookeeper 和 apollo 作为配置中心的支持。
## dubbo-go roadmap 2019-2020
![dubbo-go 的开发、设计与功能介绍](../../pic/arch/dubbo-go-design-implement-and-featrues-k.png)
最后是大家比较关注的,社区关于 dubbo-go 2019 年下半年的计划,目前来看主要还是现有功能的补齐和一些问题的修复,我们的目标就是首先做到 Java 和 Go 在运行时的兼容互通和功能的一致,其次是查漏补缺 dubbo-go 作为一个完整 Go 语言微服务框架在功能上的可以改进之处。
另外值得关注的一点是,预计今年年底, dubbo-go 会发布一个支持 kubernetes 作为注册中心的扩展,积极拥抱云原生生态。关于云原生的支持,社区前期做了积极的工作,包括讨论关于 dubbo-go 与 Service Mesh 的关系以及在其中的定位,可以肯定的是, dubbo-go 将会配合 Dubbo 社区在 Service Mesh 方向的规划并扮演重要角色,我们初步预计会在明年给出与 Service Mesh 开源社区项目集成的方案,请大家期待。
dubbo-go 社区目前属于快速健康成长状态,从捐赠给 Apache 后的不到 3 个月的时间里,吸引了大批量的活跃开发者和感兴趣的用户,欢迎各位同道在使用或者学习中遇到问题能够来社区讨论或者给予指正,也欢迎对 dubbo-go 有潜在需求或者对 dubbo-go 感兴趣的同道能加入到社区中。
**作者介绍**
何鑫铭,目前就职于携程,基础中台研发部技术专家,dubbo-go 共同发起人、主要作者,Apache Dubbo committer,关注互联网中台以及中间件领域。
\ No newline at end of file
# [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 这个版本就是主要由我推进的——这大概还是因为我时间比较多。
This diff is collapsed.
# [dubbo-go 中如何实现远程配置管理?](https://blog.csdn.net/weixin_39860915/article/details/104548947)
2020-02-27 20:00:00
之前在 Apache/dubbo-go(以下简称 dubbo-go )社区中,有同学希望配置文件不仅可以放于本地,还可以放于配置管理中心里。那么,放在本地和配置管理中心究竟有哪些不一样呢?
放在本地,每次更新需要重启,配置文件管理困难,无法做到实时更新即刻生效。此外,本地文件还依赖人工版本控制,在微服务的场景下,大大的增加了运维的成本与难度。
而配置管理中心提供了统一的配置文件管理,支持文件更新、实时同步、统一版本控制、权限管理等功能。
# **目标**
* * *
基于以上几个背景,可以总结出以下**目标**
* 与 Dubbo 现有的配置中心内的配置文件兼容,降低新增语言栈的学习成本;
* 支持多种配置文件格式;
* 支持主流配置中心,适应不一样的使用场景,实现高扩展的配置下发;
# **配置中心**
* * *
配置中心在 dubbo-go 中主要承担以下场景的职责:
1、作为外部化配置中心,即存储 dubbo.properties 配置文件,此时,key 值通常为文件名如 dubbo.properties , value 则为配置文件内容。
2、存储单个配置项,如各种开关项、常量值等。
3、存储服务治理规则,此时 key 通常按照 “服务名 + 规则类型” 的格式来组织,而 value 则为具体的治理规则。
就目前而言,dubbo-go 首要支持的是 Dubbo 中支持的开源配置中心,包括:
1、Apollo :携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。
2、ZooKeeper :一个分布式的,开放源码的分布式应用程序协调服务,是 Google 的 Chubby 一个开源的实现,是 Hadoop 和 Hbase 的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。
3、Nacos : Alibaba 开源的配置管理组件,提供了一组简单易用的特性集,帮助您实现动态服务发现、服务配置管理、服务及流量管理。
而考虑到某些公司内部有自身的研发的配置中心,又或者当前流行而 Dubbo 尚未支持的配置中心,如 etcd,我们的核心在于设计一套机制,允许我们,也包括用户,可以通过扩展接口新的实现,来快速接入不同的配置中心。
那在 dubbo-go 中究竟怎么实现呢?我们的答案是:**基于动态的插件机制在启动时按需加载配置中心的不同实现。**
实现该部分功能放置于一个独立的子项目中,见:
_https://github.com/apache/dubbo-go/tree/master/config\_center_
## **dubbo-go 设计**
原逻辑为:启动时读取本地配置文件,将其加载进内存,通过配置文件中的配置读取注册中心的信息获取服务提供者,注册服务消费者。
有些读者会有点困惑,不是说好了使用配置中心的,为什么现在又要读取本地配置呢?答案就是,读取的这部分信息分成两部分:
* 使用什么作为配置中心;
* 该配置中心的元数据,比如说使用 zookeeper 作为配置中心,那么 zookeeper 的链接信息就是元数据,毕竟我们只有在知道了链接信息之后才能连上 zookeeper;
在改造的时候,需要考虑以下的问题:
**1、如何实现支持多个配置中心?如何实现按需加载?**
通过抽象 DynamicConfiguration 让开发者可以快速支持多个配置中心。使用者导入指定的组件包后,在启动阶段将需要的组件加载进内存中,以便给程序按需调用,如下图绿色部分。
**2、配置中心的配置加载阶段在什么时候?**
应在读取配置文件阶段后,读取并解析本地配置文件中配置中心信息。初始化配置中心链接,读取 /dubbo/config/dubbo/dubbo.properties 与 /dubbo/config/dubbo/应用名/dubbo.properties ,并将其加载到内存之中覆盖原有配置,监听其变更,实时更新至内存,如下图蓝色部分:
![](../../pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-a.jpg "configcenter-class.jpg")
### **ConfigCenterFactory**
使用者加载对应配置中心模块后,在初始化阶段加入各配置中心模块往其中注册其初始化类。
![](../../pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-b.png "configCenterFactory.png")
### **DynamicConfigurationFactory**
整个动态配置中心的关键点就在 DynamicConfigurationFactory 上,其中通过解析内部自定义的 URL ,获取其协议类型,反射其参数,用于创建配置中心的链接。
![](../../pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-c.png "configurationFactory.png")
如:
配置文件中配置:
```css
config_center:
protocol: zookeeper
address: 127.0.0.1:2181
namespace: test
```
dubbo-go 内部会解析为:
```javascript
zookeeper://127.0.0.1:2181?namespace=test
```
在内部传递,用于初始化配置中心链接。
**PS:**在 dubbo-go 中到处可见这种内部协议,透彻理解这个内部协议对阅读 dubbo-go 代码很有帮助。
### **DynamicConfiguration**
该接口规定了各个配置中心需要实现的功能:
* 配置数据反序列化方式:目前只有 Properties 转换器,参见:DefaultConfigurationParser 。
* 增加监听器:用于增加监听数据变化后增加特定逻辑(受限于配置中心 client 端实现)。
* 删除监听器:删除已有监听器(受限于配置中心 client 端实现,目前所知 nacos client 没有提供该方法)。
* 获取路由配置:获取路由表配置。
* 获取应用级配置:获取应用层级配置,如:协议类型配置等。
![](../../pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-d.png "dynamicConfiguration.png")
## **实现**
* * *
![](../../pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-e.png "design.jpg")
优先考虑与现有 Dubbo 设计兼容,从而降低使用者的学习成本,dubbo-admin 作为服务提供者实现应用级配置管理, dubbo-go 作为消费端实现配置下发管理功能。下面以 ZooKeeper 为例,对服务提供者与服务消费者进行整体流程分析。
### **如何存储配置管理**
dubbo-admin 配置管理中增加 global 配置,ZooKeeper 中会自动生成其对应配置节点,内容均为 dubbo-admin 中设置的配置。
1、/dubbo/config/dubbo/dubbo.properties 对应全局配置文件。
2、/dubbo/config/dubbo/ 应用名 /dubbo.properties 对应指定应用配置文件。
#### **节点路径**
#### ![](../../pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-f.png "key-struct.jpg")
上图展示了 dubbo.properties 文件在 ZooKeeper 和 Apollo 中的存储结构:
**ZooKeeper**
* 命名空间 namespace 都为:Dubbo
* 分组 group :全局级别为 dubbo , 所有应用共享;应用级别为应用名 demo-provider ,只对该应用生效
* key : dubbo.properties
**Apollo**
* app\_id : 自由指定,默认:dubbo ,最好与 zookeeper namespace 一致
* cluster : 自由指定,最好与 zookeeper group 一致
* 命名空间 namespace : dubbo.properties
ZooKeeper 与 Apollo 最大的不一样就在于 dubbo.properties 所在的节点。
### **实现配置管理中心支持**
以 Apollo 为例,简单的介绍,如何实现支持一个新的配置管理中心。
#### **选择配置管理中心 Client / SDK**
本例中使用的 Apollo Go Client 为:https://github.com/zouyx/agollo 。
**PS:** 如没找到,自己实现也是可以的哦。
#### **节点路径**
因为每个配置管理中心的存储结构各有特点,导致 Dubbo 在使用外部配置管理中心时,存储配置节点的结构不一样。在 dubbo-configcenter 找到希望支持的配置管理中心,而本例中 Apollo 则在 ApolloDynamicConfiguration.java 。
注释中表明,Apollo namespace = governance (governance .properties) 用于治理规则,namespace = dubbo (dubbo.properties) 用于配置文件。
#### **实现 DynamicConfiguration**
新建创建客户端方法,最好客户端保持为单例。
![](../../pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-g.png "image.png")
以下为必须实现的方法,以下方法用于获取配置中心配置。
* GetInternalProperty:在配置文件(Apollo 为 namespace)中,根据 key 获取对应 value;
* GetRule:获取治理配置文件(Apollo 为 namespace);
* GetProperties:获取整个配置文件(Apollo 为 namespace);
可选择实现的方法,如不实现,则不能动态更新 dubbo-go 中配置信息。
* RemoveListener
* AddListener
而 Parser & SetParser 使用默认实现即可,默认为 Properties 转换器。
更多信息,参考:dubbo-go-apollo ,详情参考:
_https://github.com/apache/dubbo-go/tree/master/config\_center/apollo_
## **使用方法**
从上面的设计里面,也能大概猜到怎么使用了:
![](../../pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-h.png "zookeeper-usercase.png")
很显然,使用配置中心并不复杂,只需要把对应的依赖引入进来。在包初始化的时候,会创建出来对应的配置中心的实现。比如说加载 ZooKeeper 或者 Apollo 作为配置中心:
**ZooKeeper**
```nginx
_ "github.com/apache/dubbo-go/config_center/zookeeper"
```
**Apollo**
```nginx
_ "github.com/apache/dubbo-go/config_center/apollo"
```
当然仅仅加载还不够,比如说虽然我加载了 zookeeper,但是我还需要知道怎么连上这个配置中心,即前面提到的配置中心的元数据,这部分信息是需要在本地配置出来的。比如说:
**ZooKeeper**
```css
config_center:
protocol: "zookeeper"
address: "127.0.0.1:2181"
```
**Apollo**
如果需要使用 Apollo 作为配置中心,请提前创建 namespace: dubbo.properties,用于配置管理。
```properties
config_center:
protocol: "apollo"
address: "127.0.0.1:8070"
app_id: test_app
cluster: dev
```
# **总结**
* * *
更加具体的实现,我就不详细论述,大家可以去看源码,欢迎大家持续关注,或者贡献代码。
整个配置中心的功能,麻雀虽小,但五脏俱全。目前并不算是十分完善,但是整个框架层面上来说,是走在了正确的路上。从扩展性来说,是比较便利。目前支持的配置中心还不够丰富,只有 ZooKeeper 与 Apollo ,支持的配置文件格式也只有 properties ,虽然能满足基本使用场景,距离完善还有还长远的路。
**未来计划:**
* Nacos(等待发布 )
* etcd(正在开发)
* consul(未支持)
* 丰富的文件配置格式,如:yml , xml 等
**本文作者:**邹毅贤,Github ID @zouyx,开源爱好者,就职于 SheIn 供应链部门,负责供应链开放平台。
本文缩略图:icon by 用户7388492991
\ No newline at end of file
# [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 整体的使用上还是非常好上手的,自己想了一下,如果当前项目想要接入的话,主要是服务的暴露、序列化方式、鉴权调整等存在开发工作。
上面砖头也抛的差不多了,对于你快速上手应该没有问题了,剩下的就要靠你自己了。
---
title: 快速开始
keywords: 快速开始, hellowworld, Provider
description: 快速上手dubbo-go,编写一个简单的hellowworld应用
---
# [快速开始](<(https://github.com/dubbogo/dubbo-go-website/blob/master/docs/zh-cn/user/quick-start.md)>)
通过一个 `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"
}
```
2. 编写业务逻辑,`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"
}
```
3. 注册服务和对象
```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"
)
```
2. 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` 返回值
2. 把上面的两个配置文件分别配置为环境变量
```shell
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"
)
```
2. 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` 返回值
2. 把上面的两个配置文件费别配置为环境变量,为防止 log 的环境变量和服务端的 log 环境变量冲突,建议所有的环境变量不要做全局配置,在当前起效即可。
```shell
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://my.oschina.net/u/3874284/blog/4577120)
09/16 14:33
![头图.png](../../pic/course/the-5th-years-of-dubbo-go-a.png)
作者 | 于雨
**阿里巴巴云原生公众号后台回复“915”即可查看 dubbogo v1.5.1 项目管理图清晰大图!**
# 引语
dubbogo 项目已进入第五个年头。
项目发展的前两年,我们把 hessian2 协议库、网络库和整体基础框架搭建一番。从 2018 年项目被 Dubbo 官方接纳开始,依托阿里平台,社区开始形成并快速发展。与社区同学们齐心合力之下,如今全面兼容 Dubbo v2.7.x 的 Dubbo-go v1.5.1 已经发布。
# 立项
一个项目整体必须提炼出核心目标,指明其存在的意义和价值。有了初心,项目发展过程中产生困惑时,才能明确答复 “我是谁?从哪里来?到哪里去”。
## 1\. dubbogo
dubbogo 项目有其自身的 milestone 要求,大致规划了每个阶段的关键里程碑,在项目发展初期仅仅是实现 Dubbo 的某个功能,但在发展过程中会不断结合当下的技术发展潮流,不断修正其未来发展方向。
其发版计划是通过“开发当前版本、规划新版本、根据反馈修正新版本”的模式定义当前版本的开发内容和下一个版本的发展方向。每次发版后会根据社区使用反馈对下一代的发展目标进行修正。
站在吃瓜人的角度,或许可以说出 “dubbogo 不就是 dubbo 的 Go 语言版本嘛,照着抄就是了” 之类的论调。而参与过 dubbogo 项目跟着社区一路走来的人,就知道 dubbogo 并不简单定位于 Dubbo 项目的 Go 语言版本。
dubbogo 初心不变,不同时间对自身定位均有升级。我认为当前 dubbogo 的定位是:
* 全面兼容 Dubbo;
* 一个 Go 语言应用通信框架,充分利用作为云原生时代第一语言---Go 语言的优势,扩展 dubbo 的能力。
## 2\. dubbo-go-proxy
dubbogo 项目初期目的是依靠 Dubbo 实现 "bridge the gap between Java and Go" ,目前 dubbogo 正与 Dubbo 齐头并进,已经达到项目立项的目标。有长期生命的通信框架,大概有 5 年的成长期和 5 年的稳定成熟期。目前的 dubbogo 处在成长期和稳定成熟期的过渡期,这意味着社区如果想保持发展态势,就必须开始走多元化道路,发展自己的生态了。
眼下 dubbogo 社区正在集中精力孵化一个新的项目---实现一个基于 dubbogo 的 [HTTP 网关](https://github.com/dubbogo/dubbo-go-proxy),项目的意义是:dubbogo 自身是一个流量控制中间件,在其上扩展项目,其方向很自然就是做一个 proxy/sidecar or gateway,且社区一直有网关这方面的需求。
项目目的如下:
* 做一个具有生产使用意义的网关;
* dubbo-go-proxy 验证 dubbogo 的能力,对 dubbogo 未来的进化指出新方向,共同进化;
* 优化 dubbogo 的稳定性和性能。
# 团队
项目立项完毕后,就进入招兵买马阶段了。
## 1\. 来源
dubbogo 社区发展初期,其关键成员都是通过提交 issue 或者 pr 的同学撩来的。通过这种方式撩来的同学因为志同道合,有极高的概率同社区一起走下来。dubbogo 社区的 core member 就是这样来的。
其次是与其他公司的合作。dubbogo 本身是一个有着极高生产环境需求的项目,在发展过程中依次与携程、涂鸦、斗鱼、虎牙、蚂蚁金服和阿里集团有过极深的合作,其间与携程的合作对 dubbogo 成型而言极为关键。
另一个途径是与其他社区合作。dubbogo 项目发展过程中,与以下社区合作过:
* 与 MOSN 社区合作实现 Dubbo Mesh;
* 与 sentinel 社区合作,在 Dubbo/Dubbo-go 完整接入 sentinel 的降级和限流方案;
* 与 Apollo 社区合作,在 Dubbo-go 中实现远程配置下发;
* 与 Nacos 社区合作,实现基于 Nacos 的服务发现。
与其他社区合作的好处是使得双方的项目都受益:扩展双方的能力和使用场景,其次是社区间人员的流动。在合作过程中,dubbogo 自身受益极大,目前有 4 个社区 committer 来自于其它社区。合作完成后并不意味着结束,而是一个新的双赢的开始:社区项目也是发展的,当一个项目有新特性时可以同时快速被另一个项目采用验证,对扩展开发者们的技术能力和人脉也是极为有利的,dubbogo 社区目前的好几个同学同时活跃在多个社区。
dubbogo 项目已经过了草莽阶段,形成了一个的 800 多人的社区群,所以 dubbogo-proxy 项目立项后,很快就在社区群内找到很多项目爱好者。
## 2\. 成员的 qualification
项目发展初期有很多同学会 Java 不懂 Dubbo 不会 Go,最后都通过参与项目提升了自我的能力。当然有些人会担心项目代码的质量,但只要秉持 "Community Over Code" 这个 "Apache Way",在发展过程中这些问题都不大。
2019 年时,参与 dubbogo 项目的成员中一部分同学平时的工作是进行业务开发,秉承着对中间件通信技术 “我是谁?我从哪里来?要到那里去” 的初心参与 dubbogo 的开发,无论是对 dubbogo 抑或是对其自身技术水平提升都产生了积极的影响。
dubbogo 社区对 dubbogo 发版时间有一定期限要求,所以对参与人员的时间投入也有一定的要求。每个版本的核心功能的 owner,需要保证在 deadline 期限内完成开发任务。
dubbogo 每个版本都有一个发版人,负责相应版本的任务拆分、发展跟踪、代码 Review 和最后的测试验收,这就要求发版人自身的技术水平和时间投入极高。目前 dubbogo 每个大版本的发版人都不是同一个人,每次 dubbogo 发版,都意味着每个发版人的体力和精力的极大付出。于某在此致敬历届发版人!
# 管理
项目立项后,就需要明确发展大方向、发展 milestone、版本规划、以及一段时间内的具体的开发规划。项目发展初期,Roadmap 可以不清晰,先摸着石头过河,在发展过程中逐步明确其内容。
## 1\. 需求收集
dubbogo 项目发展初期,其目标仅仅是实现 dubbo 某个版本的功能, 所以其需求收集并不用花费很久时间。随着 2019 年 8 月份发布 v1.0 后,dubbogo 越来越多地被多家生产厂商投入生产使用环境中,目前其需求方来源如下:
* 实现 dubbo 某个版本的功能;
* 实际使用方的生产需求;
* 为紧跟当下最近技术发展方向而进行的技术预演。
dubbogo 当前的 K8s 注册中心技术方案就是紧跟最新技术发展方向而进行预演的极好例证,其发展时间线如下:
* 2019 年 7 月,随着阿里集团和蚂蚁金服在云原生方向的推波助澜,阿里 dubbo 开发团队并未给出可靠的技术方向,dubbogo 社区 core members 也都没有云原生方向的技术积累和相应人才,决定先开发一个基于 kube-apiserver 的注册中心,以进行技术储备;
* 2019 年 8 月, 调研 dubbo 的 K8s 注册中心方案后,dubbogo 社区决定抛开它独立实现一个,争取以最低代价把 dubbo 应用迁移到 K8s 环境中运行起来,同时决定未来的发展方向是 dubbogo operator;
* 2019 年 11 月,社区的王翔同学给出了初步实现,在 2020 年 1 月随 dubbogo v1.2 版本发布;
* 2020 年 4 月,有用户要求在 K8s 环境中 consumer 可跨 namespace 访问 provider,相应实现在 2020 年 7 月随着 dubbogo v1.5 版本发布;
* 2020 年 5 月,dubbogo 社区和 mosn 社区合作实现了 dubbo mesh;
* 2020 年 6 月,社区意识到 kube-apiserver 是系统的运维态 IaaS 层的核心组件,不应该跨过 PaaS 层直接暴露给应用层,否则应用层使用不当或者框架自身的流量方面的 bug 把 kube-apiserver 打垮后将造成整个系统的 P0 级故障,dubbogo v1.6 应当给出 dubbogo operator 的具体实现;
* 2020 年 7 月,dubbogo v1.5 发布后,社区已经知道完全可以把目前的 kube-apiserver 注册中心的实现独立成为一个 dubbogo operator,未来的方向是结合 Istio 拓展其灰度发布、限流、故障注入和配置动态下发能力。
至于 dubbo-go-proxy ,dubbogo 社区并不打算借鉴其他项目,完全依靠社区同学贡献各自想法后,进行项目需求收集。目前 dubbogo 社区已经收集完毕 dubbo-go-proxy 的[项目需求方的意见](https://github.com/apache/dubbo-go/issues/540),社区已有 5 位同学参与项目一期开发,预计 10 月份发布初版。
## 2\. 项目管理
需求收集完毕,定义近期一段时间内的开发目标后,就进入了项目任务拆解和项目开发管理阶段。像 dubbogo 和 dubbo-go-proxy 这种后来者项目,处于追赶阶段,个人理解它们并没有所谓的后发优势,更没有特定的技术优势,能够做的就是快速迭代,缩短与竞品的差距。
dubbogo 要求接受开发任务的各个 feature owner 必须在某个 deadline 前完成相应的开发任务。当然,feature 的等级不同,非核心 feature 【如技术预演性的 feature】允许 delay,顺延发布也无不可。
我们在项目每个版本的需求收集阶段,会把爱好者统一拉入一个小群进行沟通交流。进入开发阶段时,由于项目时间 deadline 限定以及技术匹配度两项要求,每个版本就很自然能选出能够留下来参与项目开发的人。最终参与每个版本开发的人尽量不要超过 7 个人,否则沟通成本就会剧增。
下图是 dubbogo v1.5.1 的项目管理图\*\*(阿里巴巴云原生公众号后台回复“915”即可查看清晰大图)\*\*
![1.png](../../pic/course/the-5th-years-of-dubbo-go-b.png)
其有任务分解、技术风险以及风险预案。
![2.png](../../pic/course/the-5th-years-of-dubbo-go-c.png)
工具是生产力,目前以 dubbogo 项目开发进度跟踪工具使用 Github Projects。如上图,每个子任务进度,这个工具都会及时显示,相应的任务 owner 可以及时根据任务进度和 deadline ,调整开发计划。更进一步的好处是,没有人会对工具产生意见,摆脱“交通基本靠走,通讯基本靠吼”的沟通模式,减少版本发版人和 feature owner 之间的戾气。
## 3\. 代码质量
开源项目的开发任务不仅仅是开发代码,也不意味着因为各个 owner 仅仅是业余时间参与开源项目就可以降低对代码质量要求。
工具就是生产力,合适的工具能够减少人工工作量和提升代码质量。dubbogo 在项目开发过程中的各个阶段都用到了如下工具:
* auto-comment:contributor 提出 issue 或者 pr 后,可很方便地发出预先定制的评语;
* hound:一个 Go 语言项目静态代码检测工具,自动 Review 代码;
* travis:一个 Github 项目自动化测试工具,可自动执行代码单测和用户自定义的集成测试,并发出钉钉通知;
* 人工 Review:dubbogo 社区要求每个 pr 至少有三个 committer 级别的人 Review 通过;
* goreportcard:一个很好的 Github 项目静态代码检测工具;
* gitee:作为国内一个比较强大的代码托管网站,免费为项目提供了一些代码安全和静态代码质量检测工具,dubbogo 也经常使用,受益良多;
* 代码规范,社区内部有一份简单的代码规范,并随着项目发展不断完善。
# 展望
<br />dubbogo 项目每次发完版本,发版人都会首先发出一份 "What's New",除了总结最新版本的特性外,还会总结其近期进展并对未来发展进行规划,以帮助项目爱好者和使用者了解其实现思路和使用方法。
dubbogo 自身还有很多缺点,如:
* 网站建设和文档质量都有待改进;
* API 用户友好度不够;
* 配置文件过多且没有合理的文档说明导致入门门槛高;
* 整体性能改进,很多地方可添加调用链的缓存以减小锁竞争;
* 添加 prometheus 指标,继续提高 可观测性;
* 在协议层面支持其他微服务框架,实现原生通信,以继续提升其互联互通性;
* dubbo-samples 用例不够丰富,继续添加测试用例,以减小入门门槛;
希望借助社区之力,在 dubbogo 发展过程中消化并解决掉这些问题,dubbogo 社区【钉钉群号 23331795】与 dubbogo 同在。
### 作者简介
于雨,一个有十多年服务端基础架构研发一线工作经验的程序员,目前在蚂蚁金服可信原生部从事容器编排和 service mesh 工作。热爱开源,从 2015 年给 Redis 贡献代码开始,陆续改进过 Muduo/Pika/Dubbo/Dubbo-go 等知名项目。
\ No newline at end of file
# [邹毅贤: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
## [getty 开发日志](http://alexstocks.github.io/html/getty.html)
* * *
_written by Alex Stocks on 2018/03/19,版权所有,无授权不得转载_
### 0 说明
* * *
[getty](https://github.com/alexstocks/getty)是一个go语言实现的网络层引擎,可以处理TCP/UDP/websocket三种网络协议。
2016年6月我在上海做一个即时通讯项目时,接口层的底层网络驱动是当时的同事[sanbit](https://github.com/sanbit)写的,原始网络层实现了TCP Server,其命名规范学习了著名的netty。当时这个引擎比较简洁,随着我对这个项目的改进这个网络层引擎也就随之进化了(添加了TCP Client、抽象出了 TCP connection 和 TCP session),至2016年8月份(又添加了websocket)其与原始实现已经大异其趣了,征得原作者和相关领导同意后就放到了github上。
将近两年的时间我不间断地对其进行改进,年齿渐增但记忆速衰,觉得有必要记录下一些开发过程中遇到的问题以及解决方法,以备将来回忆之参考。
### 1 UDP connection
* * *
2018年3月5日 起给 getty 添加了UDP支持。
#### 1.1 UDP connect
* * *
UDP自身分为unconnected UDP和connected UDP两种,connected UDP的底层原理见下图。
![](../../pic/getty/getty_connected_udp_socket.gif)
当一端的UDP endpoint调用connect之后,os就会在内部的routing table上把udp socket和另一个endpoint的地址关联起来,在发起connect的udp endpoint端建立起一个单向的连接四元组:发出的datagram packet只能发往这个endpoint(不管sendto的时候是否指定了地址)且只能接收这个endpoint发来的udp datagram packet(如图???发来的包会被OS丢弃)。
UDP endpoint发起connect后,OS并不会进行TCP式的三次握手,操作系统共仅仅记录下UDP socket的peer udp endpoint 地址后就理解返回,仅仅会核查对端地址是否存在网络中。
至于另一个udp endpoint是否为connected udp则无关紧要,所以称udp connection是单向的连接。如果connect的对端不存在或者对端端口没有进程监听,则发包后对端会返回ICMP “port unreachable” 错误。
如果一个POSIX系统的进程发起UDP write时没有指定peer UDP address,则会收到ENOTCONN错误,而非EDESTADDRREQ。
![](../../pic/getty/getty_dns_udp.gif)
一般发起connect的为 UDP client,典型的场景是DNS系统,DNS client根据/etc/resolv.conf里面指定的DNS server进行connect动作。
至于 UDP server 发起connect的情形有 TFTP,UDP client 和 UDP server 需要进行长时间的通信, client 和 server 都需要调用 connect 成为 connected UDP。
如果一个 connected UDP 需要更换 peer endpoint address,只需要重新 connect 即可。
#### 1.2 connected UDP 的性能
* * *
connected UDP 的优势详见参考文档1。假设有两个 datagram 需要发送,unconnected UDP 的进行 write 时发送过程如下:
```none
* Connect the socket
* Output the first datagram
* Unconnect the socket
* Connect the socket
* Output the second datagram
* Unconnect the socket
```
每发送一个包都需要进行 connect,操作系统到 routine table cache 中判断本次目的地地址是否与上次一致,如果不一致还需要修改 routine table。
connected UDP 的两次发送过程如下:
```none
* Connect the socket
* Output first datagram
* Output second datagram
```
这个 case 下,内核只在第一次设定下虚拟链接的 peer address,后面进行连续发送即可。所以 connected UDP 的发送过程减少了 1/3 的等待时间。
2017年5月7日 我曾用 [python 程序](https://github.com/alexStocks/python-practice/blob/master/tcp_udp_http_ws/udp/client.py) 对二者之间的性能做过测试,如果 client 和 server 都部署在本机,测试结果显示发送 100 000 量的 UDP datagram packet 时,connected UDP 比 unconnected UDP 少用了 2 / 13 的时间。
这个测试的另一个结论是:不管是 connected UDP 还是 unconnected UDP,如果启用了 SetTimeout,则会增大发送延迟。
#### 1.3 Go UDP
* * *
Go 语言 UDP 编程也对 connected UDP 和 unconnected UDP 进行了明确区分,参考文档2 详细地列明了如何使用相关 API,根据这篇文档个人也写一个 [程序](https://github.com/alexstocks/go-practice/blob/master/udp-tcp-http/udp/connected-udp.go) 测试这些 API,测试结论如下:
```none
* 1 connected UDP 读写方法是 Read 和 Write;
* 2 unconnected UDP 读写方法是 ReadFromUDP 和 WriteToUDP(以及 ReadFrom 和 WriteTo);
* 3 unconnected UDP 可以调用 Read,只是无法获取 peer addr;
* 4 connected UDP 可以调用 ReadFromUDP(填写的地址会被忽略)
* 5 connected UDP 不能调用 WriteToUDP,”即使是相同的目标地址也不可以”,否则会得到错误 “use of WriteTo with pre-connected connection”;
* 6 unconnected UDP 不能调用 Write, “因为不知道目标地址”, error:”write: destination address requiredsmallnestMBP:udp smallnest”;
* 7 connected UDP 可以调用 WriteMsgUDP,但是地址必须为 nil;
* 8 unconnected UDP 可以调用 WriteMsgUDP,但是必须填写 peer endpoint address。
```
综上结论,读统一使用 ReadFromUDP,写则统一使用 WriteMsgUDP。
#### 1.4 Getty UDP
* * *
版本 v0.8.1 Getty 中添加 connected UDP 支持时,其连接函数 [dialUDP](https://github.com/alexstocks/getty/blob/master/client.go#L141) 这是简单调用了 net.DialUDP 函数,导致昨日(20180318 22:19 pm)测试的时候遇到一个怪现象:把 peer UDP endpoint 关闭,local udp endpoint 进行 connect 时 net.DialUDP 函数返回成功,然后 lsof 命令查验结果时看到确实存在这个单链接:
```none
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
echo_clie 31729 alex 9u IPv4 0xa5d288135c97569d 0t0 UDP localhost:63410->localhost:10000
```
然后当 net.UDPConn 进行 read 动作的时候,会得到错误 “read: connection refused”。
于是模仿C语言中对 TCP client connect 成功与否判断方法,对 [dialUDP](https://github.com/alexstocks/getty/blob/master/client.go#L141) 改进如下:
```none
* 1 net.DialUDP 成功之后,判断其是否是自连接,是则退出;
* 2 connected UDP 向对端发送一个无用的 datagram packet【”ping”字符串,对端会因其非正确 datagram 而丢弃】,失败则退出;
* 3 connected UDP 发起读操作,如果对端返回 “read: connection refused” 则退出,否则就判断为 connect 成功。
```
### 2 Compression
* * *
去年给 getty 添加了 TCP/Websocket compression 支持,Websocket 库使用的是 [gorilla/websocket](https://github.com/gorilla/websocket/)[Go 官网](https://godoc.org/golang.org/x/net/websocket)也推荐这个库,因为自 `This package("golang.org/x/net/websocket") currently lacks some features`
#### 2.1 TCP compression
* * *
最近在对 Websocket compression 进行测试的时候,发现 CPU 很容易就跑到 100%,且程序启动后很快就 panic 退出了。
根据 panic 信息提示查到 [gorilla/websocket/conn.go:ReadMsg](https://github.com/gorilla/websocket/blob/master/conn.go#L1018) 函数调用 [gorilla/websocket/conn.go:NextReader](https://github.com/gorilla/websocket/blob/master/conn.go#L928) 后就立即 panic 退出了。panic 的 `表层原因` 到是很容易查明:
* 1 [gorrilla/websocket:Conn::advanceFrame](https://github.com/gorilla/websocket/blob/master/conn.go#L768) 遇到读超时错误(io timeout);
* 2 [gorrilla/websocket:ConnConn.readErr](https://github.com/gorilla/websocket/blob/master/conn.go#L941)记录这个error;
* 3 [gorilla/websocket/conn.go:Conn::NextReader](https://github.com/gorilla/websocket/blob/master/conn.go#L959)开始读取之前则[检查这个错误](https://github.com/gorilla/websocket/blob/master/conn.go#L938),如以前发生过错误则不再读取 websocket frame,并对[gorrilla/websocket:ConnConn.readErr累积计数](https://github.com/gorilla/websocket/blob/master/conn.go#L957)
* 4 [当gorrilla/websocket:ConnConn.readErr数值大于 1000](https://github.com/gorilla/websocket/blob/master/conn.go#L958) 的时候,程序就会panic 退出。
但是为何发生读超时错误则毫无头绪。
2018/03/07 日测试 TCP compression 的时候发现启动 compression 后,程序 CPU 也会很快跑到 100%,进一步追查后发现函数 [getty/conn.go:gettyTCPConn::read](https://github.com/alexstocks/getty/blob/master/conn.go#L228) 里面的 log 有很多 “io timeout” error。当时查到这个错误很疑惑,因为我已经在 TCP read 之前进行了超时设置【SetReadDeadline】,难道启动 compression 会导致超时设置失效使得socket成了非阻塞的socket?
于是在 [getty/conn.go:gettyTCPConn::read](https://github.com/alexstocks/getty/blob/master/conn.go#L228) 中添加了一个逻辑:启用 TCP compression 的时不再设置超时时间【默认情况下tcp connection是永久阻塞的】,CPU 100% 的问题很快就得到了解决。
至于为何 `启用 TCP compression 会导致 SetDeadline 失效使得socket成了非阻塞的socket`,囿于个人能力和精力,待将来追查出结果后再在此补充之。
#### 2.2 Websocket compression
* * *
TCP compression 的问题解决后,个人猜想 Websocket compression 程序遇到的问题或许也跟 `启用 TCP compression 会导致 SetDeadline 失效使得socket成了非阻塞的socket` 有关。
于是借鉴 TCP 的解决方法,在 [getty/conn.go:gettyWSConn::read](https://github.com/alexstocks/getty/blob/master/conn.go#L527) 直接把超时设置关闭,然后 CPU 100% 被解决,且程序运转正常。
### 3 unix socket
本节与 getty 无关,仅仅是在使用 unix socket 过程中遇到一些 keypoint 的记录。
#### 3.1 reliable
unix socket datagram 形式的包也是可靠的,每次写必然要求对应一次读,否则写方会被阻塞。如果是 stream 形式,则 buffer 没有满之前,写者是不会被阻塞的。datagram 的优势在于 api 简单。
```none
Unix sockets are reliable. If the reader doesn't read, the writer blocks. If the socket is a datagram socket, each write is paired with a read. If the socket is a stream socket, the kernel may buffer some bytes between the writer and the reader, but when the buffer is full, the writer will block. Data is never discarded, except for buffered data if the reader closes the connection before reading the buffer. ---[Do UNIX Domain Sockets Overflow?](https://unix.stackexchange.com/questions/283323/do-unix-domain-sockets-overflow)
```
```none
On most UNIX implementations, UNIX domain datagram sockets are always reliable and don't reorder
datagrams. ---[man 7 socketpair](http://www.man7.org/linux/man-pages/man7/unix.7.html)
```
​ ---[Do UNIX Domain Sockets Overflow?](https://unix.stackexchange.com/questions/283323/do-unix-domain-sockets-overflow)
#### 3.2 buffer size
datagram 形式的 unix socket 的单个 datagram 包最大长度是 130688 B。
```none
AF_UNIX SOCK_DATAGRAM/SOCK_SEQPACKET datagrams need contiguous memory. Contiguous physical memory is hard to find, and the allocation fails. The max size actually is 130688 B. --- [the max size of AF_UNIX datagram message that can be sent in linux](https://stackoverflow.com/questions/4729315/what-is-the-max-size-of-af-unix-datagram-message-that-can-be-sent-in-linux)
```
```none
It looks like AF_UNIX sockets don't support scatter/gather on current Linux. it is a fixed size 130688 B. --- [Difference between UNIX domain STREAM and DATAGRAM sockets?](https://stackoverflow.com/questions/13953912/difference-between-unix-domain-stream-and-datagram-sockets)
```
### 4 Goroutine Pool
随着 [dubbogo/getty](https://github.com/dubbogo/getty)[apache/dubbo-go](https://github.com/apache/dubbo-go/) 用作底层 tcp 的 transport 引擎,处于提高系统吞吐的需要,[dubbogo/getty](https://github.com/dubbogo/getty) 面临着下一步的进化要求:[**针对 dubbo-go 和 Getty 的网络 I/O 与线程派发这一部分进行进一步优化**](https://www.oschina.net/question/3820517_2306822)。其中的关键就是添加 Goroutine Pool【下文简称 gr pool】,以分离网络 I/O 和 逻辑处理。
Gr Pool 成员有任务队列【其数目为 M】和 Gr 数组【其数目为 N】以及任务【或者称之为消息】,根据 N 的数目变化其类型分为可伸缩与固定大小,可伸缩 Gr Pool 好处是可以随着任务数目变化增减 N 以节约 CPU 和内存资源,但一般不甚常用,比人以前撸过一个后就躺在我的 [github repo](https://github.com/alexstocks/goext/blob/master/sync/pool/worker_pool.go) 里面了。
[dubbogo/getty](https://github.com/dubbogo/getty) 只关注 N 值固定大小的 gr pool,且不考虑收到包后的处理顺序。譬如,[dubbogo/getty](https://github.com/dubbogo/getty) 服务端收到了客户端发来的 A 和 B 两个网络包,不考虑处理顺序的 gr pool 模型可能造成客户端先收到 B 包的 response,后才收到 A 包的 response。
如果客户端的每次请求都是独立的,没有前后顺序关系,则带有 gr pool 特性的 [dubbogo/getty](https://github.com/dubbogo/getty) 不考虑顺序关系是没有问题的。如果上层用户关注 A 和 B 请求处理的前后顺序,则可以把 A 和 B 两个请求合并为一个请求,或者把 gr pool 特性关闭。
### 4.1 固定大小 Gr Pool
按照 M 与 N 的比例,固定大小 Gr Pool 又区分为 1:1、1:N、M:N 三类。
1:N 类型的 Gr Pool 最易实现,个人 2017 年在项目 [kafka-connect-elasticsearch](https://github.com/AlexStocks/kafka-connect-elasticsearch) 中实现过此类型的 [Gr Pool](https://github.com/AlexStocks/kafka-connect-elasticsearch/blob/master/app/worker.go):作为消费者从 kafka 读取数据然后放入消息队列,然后各个 worker gr 从此队列中取出任务进行消费处理。
[dubbogo/getty](https://github.com/dubbogo/getty) 中添加 gr pool 时也曾实现过这个版本的 [gr pool](https://github.com/dubbogo/getty/pull/6/commits/4b32c61e65858b3eea9d88d8f1c154ab730c32f1)。这种模型的 gr pool 整个 pool 只创建一个 chan, 所有 gr 去读取这一个 chan,其缺点是:队列读写模型是 一写多读,因为 go channel 的低效率【整体使用一个 mutex lock】造成竞争激烈,当然其网络包处理顺序更无从保证。
[dubbogo/getty](https://github.com/dubbogo/getty) 初始版本的 [gr pool](https://github.com/dubbogo/getty/pull/6/files/c4d06e2a329758a6c65c46abe464a90a3002e428#diff-9922b38d89e2ff9f820f2ce62f254162) 模型为 1:1,每个 gr 多有自己的 chan,其读写模型是一写一读,其优点是可保证网络包处理顺序性,
如读取 kafka 消息时候,按照 kafka message 的 key 的 hash 值以取余方式【hash(message key) % N】将其投递到某个 task queue,则同一 key 的消息都可以保证处理有序。但 [望哥](http://alexstocks.github.io/html/10) 指出了这种模型的缺陷:每个task处理要有时间,此方案会造成某个 gr 的 chan 里面有 task 堵塞,就算其他 gr 闲着,也没办法处理之【任务处理“饥饿”】。
[wenwei86](https://github.com/wenweihu86) 给出了更进一步的 1:1 模型的改进方案:每个 gr 一个 chan,如果 gr 发现自己的 chan 没有请求,就去找别的 chan,发送方也尽量发往消费快的协程。这个方案类似于 go runtime 内部的 MPG 调度算法,但是对我个人来说算法和实现均太复杂,故而没有采用。
[dubbogo/getty](https://github.com/dubbogo/getty) 目前采用了 M:N 模型版本的 [gr pool](https://github.com/dubbogo/getty/pull/6/commits/1991056b300ba9804de0554dbb49b5eb04560c4b),每个 task queue 被 N/M 个 gr 消费,这种模型的优点是兼顾处理效率和锁压力平衡,可以做到总体层面的任务处理均衡。此版本下 Task 派发采用 RoundRobin 方式。
## 总结
* * *
本文总结了 [getty](https://github.com/alexstocks/getty) 近期开发过程中遇到的一些问题,囿于个人水平只能给出目前自认为最好的解决方法【如何你有更好的实现,请留言】。
随着 [getty](https://github.com/alexstocks/getty) 若有新的 improvement 或者新 feature,我会及时补加此文。
此记。
## 参考文档
* * *
* 1 [connect Function with UDP](http://www.masterraghu.com/subjects/np/introduction/unix_network_programming_v1.3/ch08lev1sec11.html)
* 2 [深入Go UDP编程](http://colobu.com/2016/10/19/Go-UDP-Programming/)
\ No newline at end of file
# 记一次对 dubbo-go-hessian2 的性能优化
2020年05月02日
目录
[dubbo-go-hessian2](https://github.com/apache/dubbo-go-hessian2) 是一个用 Go 实现的 hessian 协议 v2.0 版本的序列化库。从项目名称里可以看到主要用在 [dubbo-go](http://github.com/apache/dubbo-go) 这个项目里。hessian 协议作为 dubbo 的默认协议,因此对性能有比较高的要求。
## 立项
譬如有网文 [基于 Go 的马蜂窝旅游网分布式 IM 系统技术实践](https://my.oschina.net/u/4231722/blog/3168223) 把 dubbo-go 与其他 RPC 框架对比如下:
![](../../pic/hessian2/dubbo-go-hessian2-performance-optimization-a.png)
有鉴于此,社区便开始组织部分人力,启动了对 dubbo-go 性能优化【同时也欢迎上文作者到钉钉群 23331795 与我们社区交流】。考察 dubbo-go 的各个组件,大家不约而同地决定首先优化比较独立的 [dubbo-go-hessian2](https://github.com/apache/dubbo-go-hessian2)
## 起步
在最开始的时候,并没有太想清楚需要做什么,改哪个地方,要优化到何种程度,所以最简单的办法就是看看现状。
首先,写了一个简单的例子,把常见的类型到一个结构体里,然后测一下耗时。
```go
type Mix struct {
A int
B string
CA time.Time
CB int64
CC string
CD []float64
D map[string]interface{}
}
m := Mix{A: int('a'), B: `hello`}
m.CD = []float64{1, 2, 3}
// 再加一层,使得数据显得复杂一些
m.D = map[string]interface{}{`floats`: m.CD, `A`: m.A, `m`: m}
```
> 看起来这个结构体跟真实环境里可能不太一样,但是用来分析瓶颈应该是足够了。
然后直接靠 Go Test 写个测试用例:
```go
func BenchmarkEncode(b *testing.B) {
for i := 0; i < b.N; i++ {
_, _ = encodeTarget(&m)
}
}
func BenchmarkDecode(b *testing.B) {
for i := 0; i < b.N; i++ {
_, _ = NewDecoder(bytes).Decode()
}
}
```
> go test -benchmem -run=^$ github.com/apache/dubbo-go-hessian2 -bench "^B" -vet=off -v
得到下面结果:
```plain
BenchmarkEncode-8 89461 11485 ns/op 3168 B/op 122 allocs/op
BenchmarkDecode-8 64914 19595 ns/op 7448 B/op 224 allocs/op
```
**_注:基于 MacBook Pro 2018【主频 Intel Core i7 2.6 GHz】测试。_**
不与同类库作横向比较,仅仅从这个测试结果里的数字上无法得出任何结论。对我们来说更重要的是:它到底慢在哪里。首先想到的手段便是:借助 pprof 生成火焰图,定位 CPU 消耗。
pprof 工具的用法可以参考官网文档。本文测试时直接使用了 Goland 内置 `CPU Profiler` 的测试工具:测试函数左边的 `Run xx with 'CPU Profiler'`
![](../../pic/hessian2/dubbo-go-hessian2-performance-optimization-b.png)
测试跑完后, Goland 直接显示火焰图如下:
![](../../pic/hessian2/dubbo-go-hessian2-performance-optimization-c.png)
从这个图里可以看到,测试代码大概占用了左边的 70%,右边 30% 是运行时的一些消耗,运行时部分一般包括 gc、schedule 两大块,一般不能直接优化。图上左边可以清晰地看到 `encObject``RegisterPOJO``Encode` 各占了小一半。
完成序列化功能的 `Encode` 消耗 CPU 如此之多尚可理解,而直觉上,把类对象进行解析和注册 `RegisterPOJO` 是不应该成为消耗大户的。所以猜测这个地方要么注册有问题,要么有重复注册。
下一步分析,用了一个简单的办法:在这个函数里加日志。然后继续再跑一下 benchmark,可以看到性能瓶颈处:容器读写的地方。
既然知道这里做了许多重复的无用功,就很容易明确优化方法:加缓存。把已经解析过的结果缓存下来,下次需要的时候直接取出使用。改进后的代码简单如下:
```go
if goName, ok := pojoRegistry.j2g[o.JavaClassName()]; ok {
return pojoRegistry.registry[goName].index
}
```
这里刚开始有个疑问,为什么要分两步先取 `JavaClassName` 再取 `GoName` 而不直接取后者?看起来好像是多此一举了,但其实 `JavaClassName` 是类直接定义的,而 `GoName` 却依赖一次反射。相较之下两次转换的消耗可以忽略了。改完之后再跑一下 benchmark:
```plain
BenchmarkEncode-8 197593 5601 ns/op 1771 B/op 51 allocs/op
```
非常惊讶地看到,吞吐量大概是原来的 200%。与上面的火焰图对比,可以粗略的计算,`RegiserPOJO` 大概占了整体的 30%,改进后应该也只有原来的 `1 / 0.7 * 100% = 140%` 才对。答案也可以在火焰图里找到:
![](../../pic/hessian2/dubbo-go-hessian2-performance-optimization-d.png)
除了 `RegisterPOJO` 被干掉以外,与上图对比,还有哪些区别呢?可以看到,原来占用将近 20% 的 `GC` 也几乎看不到了。所以真实的 CPU 利用率也要加上这部分的增长,大约 `1 / 0.5 * 100% = 200%`
> 需要提醒的是,benchmark 跑出来的结果并不算稳定,所以你自己压出来的结果跟我的可能不太一致,甚至多跑几次的结果也不完全一样。对于上面的数字你只要理解原因就好,上下浮动 10% 也都是正常范围。 反过来看,这也算是 GC 优化的一个角度。碰到 GC 占用 CPU 过高,除了去一个个换对象池,也可以重点看看那些被频繁调用的模块。当然更科学的方法是看 `pprof heap` / `memory profiler` 。
针对这个结果,可以看到 `encObject` 以上都被切割成了不同的小格子,不再有像 `RegisterPOJO` 那样的大块占用,一般情况下,优化到这里就可以了。
看完了 `Encode` ,再来看看 `Decode` ,方法类似,直接看 Goland 生成的火焰图:
![](../../pic/hessian2/dubbo-go-hessian2-performance-optimization-e.png)
这个图有点迷惑性,好像也被分成差不多的小格子了。可以点开 `decObject` 这一层:
![](../../pic/hessian2/dubbo-go-hessian2-performance-optimization-f.png)
这个时候原来小的 `...` 会显示具体内容,需要注意的是里面有两个 `findField` ,在复杂的调用里经常会遇到这种情况:一个耗资源的函数被分到了许多函数里,导致在看火焰图时并不能直观地看到它就是瓶颈。比较常见的有序列化、日志、网络请求等每个模块都会干一点却又没有一个全局的函数只干他一件事。这个时候除了肉眼去找以外也可以借助于另外一个工具:
![](../../pic/hessian2/dubbo-go-hessian2-performance-optimization-g.png)
在这个 `Method List` 里可以明显看到 `findField` 已经被合并到一起了,总占用接近 CPU 的一半,看到这里你大概就知道它应该是个优化点了。
## 进一步
函数 `func findField(name string, typ reflect.Type) ([]int, error)` 的作用是在一个类型里寻找指定属性的位置(Index,反射包里用它来表示是第几个字段)。很容易想到,对于一个结构体来说,每个字段的位置从一开始就确定了,所以用缓存一样可以解决这个问题。一个简单的优化如下:
```go
func findField(name string, typ reflect.Type) (indexes []int, err error) {
typCache, _ := _findFieldCache.LoadOrStore(typ, &sync.Map{})
indexes, _ := typCache.(*sync.Map).Load(name)
if len(indexes.([]int)) == 0 {
err = perrors.Errorf("failed to find field %s", name)
}
return indexes.([]int), err
// ...
}
```
```plain
- BenchmarkDecode-8 57723 17987 ns/op 7448 B/op 224 allocs/op
+ BenchmarkDecode-8 82995 12272 ns/op 7224 B/op 126 allocs/op
```
可以看到,结果并不如预期的那样提升一倍效果。这个代码乍看起来,好像除了有一些啰嗦的断言,好像也没别的东西了,为什么只有 60% 的提升呢,我们还是借助下工具
![](../../pic/hessian2/dubbo-go-hessian2-performance-optimization-h.png)
可以看到:读缓存耗费了 7% 的资源。其中,`sync.(*Map)` 不便优化,但 `newobejct` 是哪里来的呢?代码里可以看到,唯一定义新对象的地方就是函数第一行的 `&sync.Map` ,我抱着试一试的心态把 `LoadOrStore` 拆成了两步
```go
typCache, ok := _findFieldCache.Load(typ)
if !ok {
typCache = &sync.Map{}
_findFieldCache.Store(typ, typCache)
}
```
```plain
- BenchmarkDecode-8 82995 12272 ns/op 7224 B/op 126 allocs/op
+BenchmarkDecode-8 103876 12385 ns/op 6568 B/op 112 allocs/op
```
看结果,着实出乎意料。想起来以前看 Java 代码时经常碰到这样的代码:
```go
if ( logLevel == `info` ) {
log.Info(...)
}
```
以前一直觉得这个 `if` 真是浪费感情,现在想来,别是一番认知了。如果能提供一个 `LoadOrStore(key, func() interface{})` 的方法, 会不会更好一些? 到这里的话,我们做了两个比较大的优化,整体性能大约提升了一倍。如果仔细看火焰图,还会发现有很多小的优化点,但是由于没有什么特别质的飞跃,这里不再赘述。有兴趣的小伙伴可以到 [PR Imp: cache in reflection](https://github.com/apache/dubbo-go-hessian2/pull/179) 里阅读相关的讨论。
## 更进一步
优化到此,依然藏着一个更深层次的问题:找一个可靠的参考基准,以衡量目前的工作结果【毕竟没有对比就没有伤害】。一个很容易想到的比较对象是 Go 语言官方的 `json` 标准库。
[dubbo-go-hessian2](https://github.com/apache/dubbo-go-hessian2)`json` 标准库做比较如下:
```shell
$ go test -benchmem -run=^$ github.com/apache/dubbo-go-hessian2 -bench "^B" -vet=off -v -count=5
goos: darwin
goarch: amd64
pkg: github.com/apache/dubbo-go-hessian2
BenchmarkJsonEncode
BenchmarkJsonEncode-8 249114 4719 ns/op 832 B/op 15 allocs/op
BenchmarkJsonEncode-8 252224 4862 ns/op 832 B/op 15 allocs/op
BenchmarkJsonEncode-8 240582 4739 ns/op 832 B/op 15 allocs/op
BenchmarkJsonEncode-8 213283 4784 ns/op 832 B/op 15 allocs/op
BenchmarkJsonEncode-8 227101 4665 ns/op 832 B/op 15 allocs/op
BenchmarkEncode
BenchmarkEncode-8 182184 5615 ns/op 1771 B/op 51 allocs/op
BenchmarkEncode-8 183007 5565 ns/op 1771 B/op 51 allocs/op
BenchmarkEncode-8 218664 5593 ns/op 1771 B/op 51 allocs/op
BenchmarkEncode-8 214704 5886 ns/op 1770 B/op 51 allocs/op
BenchmarkEncode-8 181861 5605 ns/op 1770 B/op 51 allocs/op
BenchmarkJsonDecode
BenchmarkJsonDecode-8 123667 8412 ns/op 1776 B/op 51 allocs/op
BenchmarkJsonDecode-8 122796 8497 ns/op 1776 B/op 51 allocs/op
BenchmarkJsonDecode-8 132103 8471 ns/op 1776 B/op 51 allocs/op
BenchmarkJsonDecode-8 130687 8492 ns/op 1776 B/op 51 allocs/op
BenchmarkJsonDecode-8 127668 8476 ns/op 1776 B/op 51 allocs/op
BenchmarkDecode
BenchmarkDecode-8 107775 10092 ns/op 6424 B/op 98 allocs/op
BenchmarkDecode-8 110996 9950 ns/op 6424 B/op 98 allocs/op
BenchmarkDecode-8 111036 10760 ns/op 6424 B/op 98 allocs/op
BenchmarkDecode-8 113151 10063 ns/op 6424 B/op 98 allocs/op
BenchmarkDecode-8 109197 10002 ns/op 6424 B/op 98 allocs/op
PASS
ok github.com/apache/dubbo-go-hessian2 28.680s
```
虽然每次的结果不稳定,但就整体而言,目前的序列化和反序列化性能大概都是 JSON 标准库的 85% 左右。这个成绩并不能说好,但短期内能花 20 分的精力得到一个 80 分的结果,应该也是可以接受的。至于剩下的 20%,就不是靠改几行代码就能搞定了。内存分配是否合理、执行流程是否有冗余,都是需要一点一滴地去改进。
## 总结
最后,我们来总结一下本文主要的优化步骤:
* 利用火焰图 快速定位消耗 CPU 较高的模块;
* 利用缓存机制,快速消除重复的计算;
* 利用 CallTree、MethodList 等多种工具分析小段代码的精确消耗;
* 遵循二八定律,以最小的成本做出一个效果显著的收益。
### 欢迎加入 dubbo-go 社区
目前 dubbo-go 已经到了一个比较稳定成熟的状态。在接下来的版本里面,我们将集中精力在云原生上。下一个版本,我们将首先实现应用维度的服务注册,这是一个和现有注册模型完全不同的新的注册模型。也是我们朝着云原生努力的一个关键版本。
dubbo-go 钉钉群 **23331795** 欢迎你的加入。
## 作者信息
张慧仁,github id micln,任职 得到 APP 后端开发。
\ No newline at end of file
This diff is collapsed.
# [What's new in Dubbo-go-hessian2 v1.6.0](https://my.oschina.net/dubbogo/blog/4318016)
发版人:[望哥](https://github.com/wongoo)
## 1\. 增加缓存优化
dubbo-go-hessian2 在解析数据的数据大量使用到了 struct 的结构信息,这部分信息可以缓存起来反复利用,使得性能提升了一倍。优化过程记录可以详细阅读[《记一次对 dubbo-go-hessian2 的性能优化》](https://mp.weixin.qq.com/s/ouVxldQAt0_4BET7srjJ6Q).
对应 pr [#179](https://github.com/apache/dubbo-go-hessian2/pull/179),作者 [micln](https://github.com/micln)
## 2\. string 解析性能优化
由于 hessian ( dubbo 序列化协议,下称:hessian )对 string 的定义是16 bit 的 unicode 的 UTF-8 表示形式,字符长度表示是16 bit 的字符数。这是仅针对 java 制定的规范,java 中一个字符是16 bit,对应到 UTF-16. hessian 库也是对每一个字符进行转码序列化。但 golang 里面字符是和 UTF-8 对应的,dubbo-go-hessian2 里面的 rune 是 32bit,和 unicode一一映射。对于 U+10000 ~ U+10FFFF 的字符,需按照 UTF16 的规范,将字符转换为 2 个字节的代理字符,再做转换,才能和 java 的序列化方式对应起来。
原来不管是编码还是解析都是一个字符一个字符处理,特别是解析的时候,从流里面一个字节一个字节读取并组装成 rune,然后再转换为 string,这样效率特别低。我们的优化方案是,批次读取字节流到 buffer 中,对 buffer 进行解析转为 UTF-8 数组,并统计字符数量。其中需要对代理对字符将其转换为标准 UTF-8 子节数组。如果统计的字符数量不足,再进一步读取流种的数据进行解析。通过此方式提升一倍的解析效率。
对应 pr [#188](https://github.com/apache/dubbo-go-hessian2/pull/188),作者 [zonghaishang](https://github.com/zonghaishang)
## 3\. 解析忽略不存在的字段
hessian 库在解析数据的时候,对于一个 class 字段,如果不存在,则直接忽略掉。但 v1.6.0 版本之前 dubbo-go-hessian2 解析数据,如果遇到不存在的字段,会返回 error。从 v1.6.0 开始,与 hessian 一样,忽略不存在的字段。**因为这是一个特性的变更,所以升级的同学一定要注意了。**
对应 pr [#201](https://github.com/apache/dubbo-go-hessian2/pull/201),作者 [micln](https://github.com/micln) & [fangyincheng](https://github.com/fangyincheng)
## 4\. 解决浮点数精度丢失问题
在对 float32 类型进行序列化时,我们一律强制转换为 float64 再进行序列化操作。由于浮点数的精度问题,在这个转换过程中可能出现小数点后出现多余的尾数,例如 (float32)99.8-->(float64)99.80000305175781。
1.6.0 版本对 float32 的序列化进行了优化:
* 如果小数尾数小于 3 位,根据 hessian2 协议序列化为 double 32-bit 格式
* 否则先转换为 string 类型,再转换为 float64 类型,这样做可以避免由于浮点数精度问题产生多余的尾数,最后对 float64 进行序列化。
虽然对 float32 类型进行了优化,但是依然建议使用浮点数的时候优先使用 float64 类型。
对应 pr [#196](https://github.com/apache/dubbo-go-hessian2/pull/196),作者 [willson-chen](https://github.com/willson-chen)
## 5\. 解决 attachment 空值丢失问题
dubbo 请求中包含 attachment 信息,之前如果 attachment 里面含有如 `"key1":""`,这种 value 为空的情况,解析出来的结果会直接丢失这个属性 key1 ,v1.6.0 修复了此问题,现在解析出来的 attachment 会正确解析出空 value 的属性。
对应 pr [#191](https://github.com/apache/dubbo-go-hessian2/pull/191),作者 [champly](https://github.com/champly)
## 6\. 支持 ‘继承’ 和忽略冗余字段
由于 go 没有继承的概念,所以在之前的版本,Java 父类的字段不被 dubbo-go-hessian2 所支持。新版本中,dubbo-go-hessian2 将Java来自父类的字段用匿名结构体对应,如:
```rust
type Dog struct {
Animal
Gender string
DogName string `hessian:"-"`
}
```
同时,就像 json 编码中通过 immediately 可以在序列化中忽略该字段,同理,通过 hessian:"-" 用户也可以让冗余字段不参与 hessian 序列化。
对应pr [#154](https://github.com/apache/dubbo-go-hessian2/pull/154),作者 [micln](https://github.com/micln)
## 欢迎加入 dubbo-go 社区
钉钉群: **23331795**
[github](https://www.oschina.net/p/github)[apache](https://www.oschina.net/p/apache+http+server)[java](https://www.oschina.net/p/java)
\ No newline at end of file
# [Dubbo-go-hessian2 v1.7.0 发布](https://www.oschina.net/news/118648/dubbogo-hessian2-1-7-0-released)
Dubbo-go-hessian2 v1.7.0已发布,详见 [https://github.com/apache/dubbo-go-hessian2/releases/tag/v1.7.0,](https://github.com/apache/dubbo-go-hessian2/releases/tag/v1.7.0%EF%BC%8C) 以下对这次更新内容进行详细整理。
另外v1.6.3 将 attachment 类型由 map\[string\]stiring 改为map\[string\]interface{} 导致版本不兼容问题,这部分已还原,后续的计划是将dubbo协议的request/response对象整体迁移到dubbogo项目中进行迭代修改, hessian2中将不再改动到request/response对象。
## 1\. New Features
### 1.1 add GetStackTrace method into Throwabler and its implements. [#207](https://github.com/apache/dubbo-go-hessian2/pull/207)
> contributed by [https://github.com/cvictory](https://github.com/cvictory)
go语言client请求java语言服务时,如果java语言抛出了异常,异常对应的堆栈信息是被保存在StackTraceElement中。
这个异常信息在日志中最好能被打印出来,以方便客户端排查问题,所以在Throwabler和对应子类中增加了StackTraceElement的获取。
注:其实还有一种更好的方法,所有的具体的异常类型都包含java\_exception/exception.go的Throwable struct。这样只需要在Throwable中增加GetStackTrace方法就可以了。但是这种方式需要更多的测试验证,改动的逻辑相对会复杂一些。但是代码会更整洁。 这里先不用这种方法。
### 1.2 catch user defined exceptions. [#208](https://github.com/apache/dubbo-go-hessian2/pull/208)
> contributed by [https://github.com/cvictory](https://github.com/cvictory)
golang中增加一个java中Exception对象的序列化输出方法:
```css
func JavaException() []byte {
e := hessian.NewEncoder()
exception := java_exception.NewException("java_exception")
e.Encode(exception)
return e.Buffer()
}
```
在output/output.go 提供调用入口:添加如下函数初始化声明
```plain
func init() {
funcMap["JavaException"] = testfuncs.JavaException
}
```
java代码中增加调用go方法序列化结果: **说明**: Assert.assertEquals 不能直接比较Exception对象是否相等
```php
/**
* test java java.lang.Exception object and go java_exception Exception struct
*/
@Test
public void testException() {
Exception exception = new Exception("java_exception");
Object javaException = GoTestUtil.readGoObject("JavaException");
if (javaException instanceof Exception) {
Assert.assertEquals(exception.getMessage(), ((Exception) javaException).getMessage());
}
}
```
### 1.3 support java8 time object. [#212](https://github.com/apache/dubbo-go-hessian2/pull/212), [#221](https://github.com/apache/dubbo-go-hessian2/pull/221)
> contributed by [https://github.com/willson-chen](https://github.com/willson-chen), [https://github.com/cyb-code](https://github.com/cyb-code)
golang中增加一个java8对象的序列化输出方法:
```go
// test java8 java.time.Year
func Java8TimeYear() []byte {
e := hessian.NewEncoder()
year := java8_time.Year{Year: 2020}
e.Encode(year)
return e.Buffer()
}
// test java8 java.time.LocalDate
func Java8LocalDate() []byte {
e := hessian.NewEncoder()
date := java8_time.LocalDate{Year: 2020, Month: 9, Day: 12}
e.Encode(date)
return e.Buffer()
}
```
在output/output.go 提供调用入口:添加函数初始化声明
```plain
func init() {
funcMap["Java8TimeYear"] = testfuncs.Java8TimeYear
funcMap["Java8LocalDate"] = testfuncs.Java8LocalDate
}
```
java代码中增加调用go方法序列化结果:
```java
/**
* test java8 java.time.* object and go java8_time/* struct
*/
@Test
public void testJava8Year() {
Year year = Year.of(2020);
Assert.assertEquals(year
, GoTestUtil.readGoObject("Java8TimeYear"));
LocalDate localDate = LocalDate.of(2020, 9, 12);
Assert.assertEquals(localDate, GoTestUtil.readGoObject("Java8LocalDate"));
}
```
### 1.4 support test golang encoding data in java. [#213](https://github.com/apache/dubbo-go-hessian2/pull/213)
> contributed by [https://github.com/wongoo](https://github.com/wongoo)
为了更好的测试验证hessian库,原来已经支持在golang中测试java的序列化数据,现在增加在java中测试golang的序列化数据,实现双向测试验证。
golang中增加序列化输出方法:
```css
func HelloWorldString() []byte {
e := hessian.NewEncoder()
e.Encode("hello world")
return e.Buffer()
}
```
将该方法注册到output/output.go中
```plain
// add all output func here
func init() {
funcMap["HelloWorldString"] = testfuncs.HelloWorldString
}
```
output/output.go 提供调用入口:
```go
func main() {
flag.Parse()
if *funcName == "" {
_, _ = fmt.Fprintln(os.Stderr, "func name required")
os.Exit(1)
}
f, exist := funcMap[*funcName]
if !exist {
_, _ = fmt.Fprintln(os.Stderr, "func name not exist: ", *funcName)
os.Exit(1)
}
defer func() {
if err := recover(); err != nil {
_, _ = fmt.Fprintln(os.Stderr, "error: ", err)
os.Exit(1)
}
}()
if _, err := os.Stdout.Write(f()); err != nil {
_, _ = fmt.Fprintln(os.Stderr, "call error: ", err)
os.Exit(1)
}
os.Exit(0)
}
```
java代码中增加调用go方法序列化结果:
```plain
public class GoTestUtil {
public static Object readGoObject(String func) {
System.out.println("read go data: " + func);
try {
Process process = Runtime.getRuntime()
.exec("go run output/output.go -func_name=" + func,
null,
new File(".."));
int exitValue = process.waitFor();
if (exitValue != 0) {
Assert.fail(readString(process.getErrorStream()));
return null;
}
InputStream is = process.getInputStream();
Hessian2Input input = new Hessian2Input(is);
return input.readObject();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private static String readString(InputStream in) throws IOException {
StringBuilder out = new StringBuilder();
InputStreamReader reader = new InputStreamReader(in, StandardCharsets.UTF_8);
char[] buffer = new char[4096];
int bytesRead;
while ((bytesRead = reader.read(buffer)) != -1) {
out.append(buffer, 0, bytesRead);
}
return out.toString();
}
}
```
增加java测试代码:
```java
@Test
public void testHelloWordString() {
Assert.assertEquals("hello world"
, GoTestUtil.readGoObject("HelloWorldString"));
}
```
### 1.5 support java.sql.Time & java.sql.Date. [#219](https://github.com/apache/dubbo-go-hessian2/pull/219)
> contributed by [https://github.com/zhangshen023](https://github.com/zhangshen023)
增加了 java 类 java.sql.Time, java.sql.Date 支持,分别对应到hessian.Time 和 hessian.Date, 详见 [https://github.com/apache/dubbo-go-hessian2/pull/219/files。](https://github.com/apache/dubbo-go-hessian2/pull/219/files%E3%80%82)
## 2\. Enhancement
### 2.1 Export function EncNull. [#225](https://github.com/apache/dubbo-go-hessian2/pull/225)
> contributed by [https://github.com/cvictory](https://github.com/cvictory)
开放 hessian.EncNull 方法,以便用户特定情况下使用。
## 3\. Bugfixes
### 3.1 fix enum encode error in request. [#203](https://github.com/apache/dubbo-go-hessian2/pull/203)
> contributed by [https://github.com/pantianying](https://github.com/pantianying)
原来在 dubbo request 对象中没有判断 enum 类型的情况,此pr增加了判断是不是POJOEnum类型。详见 [https://github.com/apache/dubbo-go-hessian2/pull/203/files](https://github.com/apache/dubbo-go-hessian2/pull/203/files)
### 3.2 fix \[\]byte field decoding issue. [#216](https://github.com/apache/dubbo-go-hessian2/pull/216)
> contributed by [https://github.com/wongoo](https://github.com/wongoo)
v1.7.0 之前如果 struct中包含\[\]byte字段时无法反序列化, 报错“error list tag: 0x29”,主要原因是被当做list进行处理,对于这种情况应该按照binary数据进行处理即可。
```go
type Circular struct {
Num int
Previous *Circular
Next *Circular
ResponseDataBytes []byte // <----
}
func (Circular) JavaClassName() string {
return "com.company.Circular"
}
```
### 3.3 fix decoding error for map in map. [#229](https://github.com/apache/dubbo-go-hessian2/pull/229)
> contributed by [https://github.com/wongoo](https://github.com/wongoo)
v1.7.0 之前嵌套map无法正确解析,主要原因是对应的map对象被当做一个数据类型却未被自动加到类引用列表中,而嵌套map类信息是同一类型的引用,去类引用列表找,找不到就报错了。 解决这个问题的方法就是遇到map类对象,也将其加入到类引用列表中即可。 问题详细参考 [#119](https://github.com/apache/dubbo-go-hessian2/issues/119).
### 3.4 fix fields name mismatch in Duration class. [#234](https://github.com/apache/dubbo-go-hessian2/pull/234)
> contributed by [https://github.com/skyao](https://github.com/skyao)
这个 PR 解决了Duration对象中字段错误定义,原来是"second/nano", 应该是"seconds/nanos"。
同时改善了测试验证数据。之前使用0作为int字段的测试数据,这是不准确的,因为int类型默认值就是0.
\ No newline at end of file
# [冲上云原生,Dubbo 发布 Go 版本](https://www.oschina.net/question/3820517_2306822)
[h4cd](https://my.oschina.net/u/3820517) 发布于 2019/06/02 22:36
5 月 21 日,经过一年多的孵化,Apache Dubbo 从 Apache 软件基金会毕业,成为 Apache 顶级项目。
![](../../pic/interview/dubbo-go-published-a.jpg)
Dubbo 是阿里于 2011 年开源的一款高性能 RPC 框架,在 Java 生态中具有不小的影响力。当初经历过一段被外界诟病的“停止维护”灰暗时光,后来在 2017 年 Dubbo 浪子回头,官方宣布重新重点维护。
重新启航的 Dubbo 将首要目标定位于重新激活社区,赢回开发者的信任,并且逐渐将 Dubbo 打造成一个国际化与现代化的项目,目前距离宣布重启已经过了一年半的时间。
在这个过程中,Dubbo 发布了多个版本,并逐渐从一个 RPC 框架向微服务生态系统转变,18 年年初 Dubbo 入驻 Apache 软件基金会孵化器,开始以 Apache 之道发展社区。
一年之后,Dubbo 在 Apache 孵化器中发布了重启维护以来的首个里程碑版本 2.7.0,添加了社区呼声很高的异步化支持,以及注册中心与配置中心分离等特性。
这期间 Dubbo 3.0 的开发工作也被提上了日程,今年 4 月中旬,官方正式公布了 Dubbo 3.0 的进度,此版本新特性包括支持 Filter 链的异步化、响应式编程、云原生/Service Mesh 方向的探索,以及与阿里内外融合。
然后,Dubbo 毕业了。**毕业后的 Dubbo 近期有什么消息呢?**生态还在发展,Dubbo 社区在前几日公开了 [Dubbo Roadmap 2019](https://github.com/dubbo/awesome-dubbo/blob/master/slides/meetup/201905@beijing/DUBBO%20ROADMAP%202019.pdf),计划在 2020 年 2 月份发布 Dubbo 3.0 正式版,感兴趣的同学可以详细查阅。
![](../../pic/interview/dubbo-go-published-b.jpg)
而最近官方又**宣布 Go 语言加入 Dubbo 生态**,发布了 [dubbo-go 项目](https://github.com/dubbo/go-for-apache-dubbo)
![](../../pic/interview/dubbo-go-published-c.jpg)
在此之前 Dubbo 的**跨语言可扩展性**已经有一些实现,支持的语言包括 PHP、Node.js 与 Python,同时也基于标准 Java REST API - JAX-RS 2.0 实现了 REST 的调用支持,具体情况如下:
* **PHP**:php-for-apache-dubbo,by 乐信,提供客户端和服务端
* **Node.js**:dubbo2.js,by 千米网,提供客户端
* **Node.js**:egg-dubbo-rpc,by 蚂蚁金服 egg 团队,提供客户端和服务端
* **Python** :py-client-for-apache-dubbo,by 千米网,提供客户端
现在加入了 dubbo-go,Go 开发者也终于可以尝到 Dubbo 的滋味了。据悉,dubbo-go 项目将于**本周完成往 Apache 软件基金会的迁移**,作为 Apache Dubbo 顶级项目的子项目,届时 dubbo-go 项目的新地址也将变为:[https://github.com/apache/dubbo-go](https://github.com/apache/dubbo-go)
关于项目的研发背景与具体技术细节等相关内容,我们第一时间采访了项目共同发起人,目前在携程基础中台研发部的**何鑫铭**
**OSCHINA:**dubbo-go 是什么,定位是什么,为什么做这个项目?
**dubbo-go 何鑫铭:**
**dubbo****\-****go 是** **D****ubbo 的完整 Go 语言实现。**
我们知道 Dubbo 本身基于 Java,很多公司也都以 Java 开发为主,并且使用 Dubbo 作 RPC 或微服务开发框架。
而最近 Go 语言生态发展比较迅速,因其语言优势,我们已经有部门开始尝试使用 Go 开发一些新的项目,就会存在亟需解决的问题:
* 如何实现 Go 项目和 Java & Dubbo 项目的互通?
* 另外,Go 项目本身也有对 RPC 与微服务开发框架的诉求,如何解决?
基于这两个问题,我们携程团队基于 dubbo-go 的早期项目,重构开发了更易于扩展且功能更加完善的 dubbo-go v1.0.0 版本,并贡献回了社区,它**首要目的就是解决 Go 项目与 Java & Dubbo 项目的互通问题,同时****也****为 Go 项目提供****了****一种 RPC** **与微服务开发****框架的选择**
dubbo-go 提供客户端与服务器端,目前 dubbo-go 社区作为 Dubbo 生态最活跃的社区之一,后面的定位需要配合 Dubbo 官方的要求与社区用户的需求。
**OSCHINA:**我们知道 Dubbo 在 Java 生态上是有非常高的成就的,而目前 Go 生态本身也有一些知名的微服务框架,那 dubbo-go 之于 Go 生态,是否有与其它框架比拼的能力?
**dubbo-go 何鑫铭:**
我们最大的能力就是作为 Dubbo 的 Go 语言版本,打通了两种语言之间的 gap,**让 Dubbo 更加贴近云原生**,为开发者也提供了最大的灵活性,显著降低企业现有服务上云的成本,让企业在云原生时代多了一种选择。
**OSCHINA:**Go 的特性有没有在 dubbo-go 中得到相应的体现?(比如 Go 的高并发是怎么从基于 Java 的 Dubbo 中改造到 dubbo-go 中的?)
**dubbo****\-****go 何鑫铭:**
我对于 Go 语言的认知是,首先学习成本比较小,相比于 Java 的学习成本,Go 语言更容易学习和上手。
其次 Go 在语言层面上,比如其 CSP 编程模型在高并发处理上的简单高效、轻量级协程的优势,相比较基于 JVM 的 Java 程序来说,基于 runtime 的 Go 程序瞬时启动能力等特性都吸引着很多开发者,这里就不详细阐述了。
最后就是作为云原生语言的优势,随着 Docker、k8s 与 Istio 等优秀项目的出现,云原生底层基本被 Go 语言统一了,相信企业在云原生模式下开发的日子已经不远了。我觉得 Go 语言的生态应该会越来越好,也会有越来越多的人使用它。
将基于 Java 的 Dubbo 引入到 Go 中,像前边讲的,dubbo-go 带来的优势就是可以快速融入云原生的领域。要说 Go 语言特性体现的话,可以参考一下 **dubbo****\-****go** **中****异步网络 I/O 模型的设计,这部分将 Go 语言轻量级协程的优势体现了出来**
这里也说一下 Go 语言不足的地方:
* Go 相对 Java 来说还是很年轻的语言,没有模板库可用,所以社区在编写并维护Hessian 2 协议库上付出了很高的开发成本;
* 比起 Java 的 try/catch 错误处理方式,Go 的 error 处理能力偏弱;
* 总体生态还是不如 Java,如没有像 Netty 一样的强有力网络 I/O 库。
为什么提到这一点呢,因为 Dubbo 自身使用了 Netty 和 Hessian 2 协议官方 Java 库,而 dubbo-go 在开始做的时候这些都是没有的,这使得 **dubbo****\-****go** **一路走来非常艰辛,但是****社区****最终都****克服了,并且额外贡献了开源的 Getty 和** **H****essian2 项目。**
这里特别感谢 dubbo-go 社区早期的组织者于雨,项目的早期版本是 **2016 年**在其领导胡长城和同事刘畏三支持下开发的,他贡献的 Hessian2 和 Getty 项目,也为最新版本的 dubbo-go 打好了坚实的基础。
**OSCHINA:**前不久 Dubbo 才宣布之后会在 3.0 中强调 Service Mesh ,这就是语言无关的了,那 dubbo-go 还有必要在这时候加入生态吗?
**dubbo****\-****go 何鑫铭:**
Service Mesh 确实是微服务未来发展的的一个大方向,但是现阶段在国内大公司还没有看到非常成功的案例,很多中小公司自身微服务还未拆分完毕甚至于还未开始,目前 dubbo-go 社区优先解决这种类型企业微服务技术落地环节中遇到的问题,专注于补齐相关功能、优化整体性能和解决 bug。至于未来,我相信随着 Dubbo Mesh 在 Service Mesh 领域的探索,dubbo-go 肯定会跟进并扮演重要角色。
**OSCHINA:**dubbo-go 与 Dubbo 的更新关系是怎么样的?是同步更新特性还是有自己的一些创新?
**dubbo****\-****go 何鑫铭:**
我们现在发布的最新版本是 v1.0.0,我们在每一次 release 新的版本后,都会明确说明可以兼容的 Dubbo 版本。所以,dubbo-go 需要兼容对应 Dubbo 版本号的功能,会同步更新一些 Dubbo 特性。
**OSCHINA:**新发布版本带来什么值得关注的特性?
**dubbo****\-****go 何鑫铭:**
当前发布的 v1.0.0 版本支持的功能如下:
* 角色:Consumer(√)、Provider(√)
* 传输协议:HTTP(√)、TCP(√)
* 序列化协议:JsonRPC v2(√)、Hessian v2(√)
* 注册中心:ZooKeeper(√)
* 集群策略:Failover(√)
* 负载均衡:Random(√)
* 过滤器:Echo Health Check(√)
* extension 扩展机制
dubbo-go v1.0.0 版本,主要由我和同在携程的同事[方银城](https://github.com/fangyincheng)维护,社区成员[周子庆](https://github.com/u0x01)[高辛格](https://github.com/gaoxinge)参与贡献,该版本**沿用了** **D****ubbo 的代码分层解耦设计**。Dubbo 2.6.x 的主要功能都会逐渐在 dubbo-go 中实现,包括 Dubbo 基于 SPI 的代码拓展机制,dubbo-go 也有对应的 extension 扩展机制与之对应。
我们在未来将逐渐推出目前可扩展模块的更多实现,如补齐更多的 Loadbalance 负载均衡、Cluster Strategy 集群策略实现(目前这些任务由社区伙伴主动认领,希望更多的 Go 语言爱好者朋友可以加入社区贡献);又如云原生领域非常流行的 k8s,我们也将同步 Dubbo 的 roadmap,跟进 k8s 作为注册中心的支持,目前由社区成员[张海彬](https://github.com/NameHaibinZhang)负责跟进。
当然广大开发者们也可以对这些模块接口进行新的实现,通过 extension 拓展,以完成自己的特殊需求而无需修改源代码。同时,我们非常欢迎开发者为社区贡献有用的拓展实现。
此版本解决了一大重点问题:**与 Dubbo Java 版本互通的解决方案。**我们将这部分提取出了 [Hessi](https://github.com/dubbogo/hessian2)[a](https://github.com/dubbogo/hessian2)[n2](https://github.com/dubbogo/hessian2) 项目,该项目源自社区[于雨](https://github.com/AlexStocks)的早期贡献,现在由社区成员[望哥](https://github.com/wongoo)负责维护,[周子庆](https://github.com/u0x01)[高辛格](https://github.com/gaoxinge)参与贡献。目前该项目已经完成了对 Java 大部分类型的兼容支持。大家也可以单独将该项目集成到自己的项目中,它的开源协议是 Apache-2.0。
另外一个比较重要的就是 **dubbo****\-****go 现在使用的 TCP 异步网络 I/O 库**,该库也是基于[于雨](https://github.com/AlexStocks)早期写的 [Getty](https://github.com/dubbogo/getty) 项目,目前由社区的[望哥](https://github.com/wongoo)[方银城](https://github.com/fangyincheng)负责维护,它同样也是 Apache-2.0 的开源协议。下一版本我们**会针对 dubbo****\-****go 和 Getty 的网络 I/O 与线程派发这一部分进行进一步优化****。**
除此之外,我们计划下一步支持 Dubbo 的另外几大重要功能,如:
* routing rule 路由规则(dubbo v2.6.x)
* dynamic configuration 动态配置中心(dubbo v2.7.x)
* metrics 指标与监控(dubbo v2.7.x)
* trace 链路监控(dubbo ecos)
**OSCHINA:**目前项目的应用情况如何?
**dubbo****\-****go 何鑫铭:**
dubbo-go 现在已经开始被一些企业尝试应用于 Go 语言应用融入企业已有 Java & Dubbo 技术栈,以及搭建全新 Go 语言分布式应用等场景。比如中通快递内部 Go 调用 Java Dubbo 服务;作为携程 Go 语言应用的服务框架以及 Go、Java 应用互通。
具体的应用情况可以查看:
* [https://github.com/dubbo/go-for-apache-dubbo/issues/2](https://github.com/dubbo/go-for-apache-dubbo/issues/2)
**OSCHINA:**接下来的演进方向是怎么样的?
**dubbo****\-****go 何鑫铭:**
在 dubbo-go 迁往 Apache 软件基金会作为 Apache Dubbo 的子项目后,首先最重要的是**性能的进一步优化**,目前性能上虽然能够达到应用的生产级别要求,但我们觉得还没有发挥出 Go 语言的优势,还有比较大的优化空间。比如前边提到的 Getty,下一版本会针对 dubbo-go 应用 Getty 的网络 I/O 模型与线程派发做一些优化。
另外包含上面提到的我们近期需要补全一些重要功能,最大限度地在**功能完整性**上能够跟 Dubbo 兼容。关于未来 dubbo-go 的发展,也会向 Dubbo 2.7.x 版本这条线上的路线图演进。
**OSCHINA:**说到性能,当前性能情况具体如何?
**dubbo****\-****go 何鑫铭:**
我们有做一个 [**dubbo-go-****benchmark**](https://github.com/dubbogo/go-for-apache-dubbo-benchmark) 项目,在 CPU 型号为 Intel(R) Xeon(R) CPU E5-2609 0 @2.40GHz,CPU 核心数为 4\*8 的硬件水平下,发送 1k 并返回 1k 的数据,100 并发数,100w 总请求数,qps 可以达到 1.2 万左右。
CPU 性能换成比较高的配置如 Intel Core i9 2.9GHz,qps 可以到达 2 万左右。
我们后面会对 Hessian2 库和 Getty 库进行持续性能优化,以给广大使用者节约资源。
## **采访嘉宾介绍**
**何鑫铭**,携程基础中台研发部技术专家,dubbo-go 主要作者。目前专注于 Golang & Java、中台架构、中间件与区块链等技术。
\ No newline at end of file
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