diff --git a/doc/md/arch/dubbo-go-design-implement-and-featrues.md b/doc/md/arch/dubbo-go-design-implement-and-featrues.md new file mode 100644 index 0000000000..c0600ef21f --- /dev/null +++ b/doc/md/arch/dubbo-go-design-implement-and-featrues.md @@ -0,0 +1,129 @@ +# [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 diff --git a/doc/md/arch/dubbo-go-review-and-future.md b/doc/md/arch/dubbo-go-review-and-future.md new file mode 100644 index 0000000000..9fc1df668b --- /dev/null +++ b/doc/md/arch/dubbo-go-review-and-future.md @@ -0,0 +1,246 @@ +# [dubbogo 回顾与展望](https://blog.csdn.net/RA681t58CJxsgCkJ31/article/details/103856203/) + +2020-01-06 08:30:00 + +Dubbo 是阿里于 2011 年开源的一款高性能 RPC 框架,在 Java 生态中具有不小的影响力。2019年5月21日,Dubbo 从 Apache 软件基金会毕业,成为 Apache 顶级项目。目前,毕业后的 Dubbo 项目的生态中已经正式官宣引入了 Go 语言,发布了 Dubbogo 项目。本文即是对 Dubbogo 这一项目的完整回顾与真实展望。由蚂蚁金服中间件技术专家于雨和携程基础中台研发部工程师方银城合作完成。 + +**01** + +**Dubbogo 整体框架** + +先介绍一下 dubbogo 的缘起,先看下面这幅图: + +       ![](../../pic/arch/dubbo-go-review-and-future-a.jpg)       + +最右边的 service0 和 service1 是 Dubbo 的服务端,左边的 gateway 是网关,HTTP  请求从网关进来,必须转化成 Dubbo 的协议才能到后面的服务,所以中间加了一层proxy 完成相关功能。基本上每个 service 都需要一个 proxy 去转化协议和请求,所以这个时候 dubbogo 的项目需求就出来了。最初的实现就是以 Dubbo 的 Go 版本作为目标,实现与 Java 版本 Dubbo 的互调。 + +**Dubbogo 目标** + +     ![](../../pic/arch/dubbo-go-review-and-future-b.jpg)         + +然后这个图是 dubbogo 的现在达到的目标:用一份 Go 客户端的代码能够在没有任何代理和其他中间件的情况下直接调用其他语言端,主要是Java 服务端的服务和 Go 服务端的服务,而 Go 作为服务端的时候,Java 客户端也可以直接调用 Go 服务端的服务。 + +**Dubbogo 发展历程** + +       ![](../../pic/arch/dubbo-go-review-and-future-c.jpg)       + +下面介绍 dubbogo 的整个发展历程,在2016年8月份的时候是于雨构建了 dubbogo 项目,那个时候的 dubbogo 只支持通过 jsonrpc 2.0 协议 进行 HTTP 通信,到 2018 年2 月份支持 hessian2 协议进行 TCP 通信,到 2018 年 5 月项目被 dubbo 官方关注后开始从零重构,于雨 2018 年 8 月份初步重构出一个 0.1 版本。由于我们携程这边的一些需求,2019 年初我和我的同事何鑫铭也开始参与了 dubbogo 项目的重构,同时和于雨一起开始组建社区,在 2019 年 6 月份的时候 dubbogo1.0 版本上线,这个版本的重构是参照了 Dubbo 的整体设计,主体功能都在这个版本里面有呈现,同期该项目进入了 Apache 组织。今年 8 月份由社区同学望哥负责的 Dubbo-go-hessian2 的项目也进了 Apache 组织。到目前为止我们社区有些工作已经和 dubbo 齐头并进,例如对 grpc 和 k8s 的支持,相关代码正在 review 中,年底发布的 v1.3 版本会包含 grpc 支持。预计到2020年,也是明年的目标,希望项目能以全新姿态融入云原生时代。 + +**Dubbogo 整体设计** + +       ![](../../pic/arch/dubbo-go-review-and-future-d.jpg)       + +这个图大家是不是看着很熟悉,是 Dubbo 的整个分层设计图,但是少了 Dubbo 里面的很多东西,因为我们是借鉴了 Dubbo 的分层设计和易拓展性的思想,但是由于 Go 语言和 Java 语言的本质差别决定了我们项目设计不可能完全照搬它,有一些东西就给它简化了,特别是协议层这一块。比如说 Dubbo 里面 SPI 的拓展,在 Go 里面我们是借用了 Go 的非侵入式接口的方式去实现的,由于 Go 禁止 package 循环引用,所以 dubbogo 在代码的分包分层上面也是有严格的规定,这正好跟它的易拓展性的特性结合了起来。 + +关于代理部分,因为 Java 有动态代理,Go 的反射就没有 Java 的反射那么强大,所以我们这边代理的实现方式也跟它是不一样的。 + +**Dubbogo 能力大图** + +       ![](../../pic/arch/dubbo-go-review-and-future-e.jpg)       + +上面的图是我们当前 dubbogo 项目实现的能力大图,最上层是当前实现的一些注册中心有 zk、etcd、nacos、consul,现在与 k8s 关联的功能正在开发中。配置中心目前是支持 Apollo 和 zookeeper。左边是消费端,消费端这边实现的是有 cluster 的,策略上基本上实现了 dubbo 支持的所有策略。然后还有负载均衡策略,fillter 主要是有一个 tps 的限流还有泛化调用,这两个后面会讲到。编码层现在就是 jsonrpc 2.0 和 hessian2,protobuf v3 正在加紧 review 中。目前社区正在开发中的支持,包括 trace、grpc、k8s注册中心,以及对 restful 的支持。 + +**关键项目** + +       ![](../../pic/arch/dubbo-go-review-and-future-f.jpg)       + +目前 dubbogo 项目整体由 4 个组成部分。第一个是 getty,一个异步网络 IO 库,是实现 tcp 通信协议最坚实的基础;第二个是 dubbo-go-hessian2,这个是与当前 java hessian2 高度兼容的项目;第三个是 gost,是 dubbogo 的 基础库;最后是 dubbogo 的示例库,目前已经迁移到 https://github.com/apache/dubbo-samples,和 Java 示例合并了。这些都是当前 dubbogo 主要的组成项目。 + +**02** + +**协议实现** + +       ![](../../pic/arch/dubbo-go-review-and-future-g.jpg)       + +接下来讲一些具体的实现和部分的功能,上图是 dubbo-go-hessian2 实现,列出来是一些主要的功能列表,第一个是 Java 的 JDK Exceptions 的实现,里面实现了 40 多种的 Java JDK 主要的异常,可以与 Java 的 hessian2 版本互相解编码的支持,支持自动扩展自己实现 exceptions,或者是不常见的 Exceptions;第二个是支持字段名的联名,Go 可序列化的字段是大写字母开头,但是 Java 默认是小写开头的,所以有编码的字段名不一致的问题,这就有别名识别和支持自定义命名。 + +go-hessian2 还支持 Java 的 bigdecimal、Date、Time、基本类型的包装类型、Generic Invocation、Dubbo Attachements,甚至支持 emoji 表情。 + +go-hessian2 里面如果要解码和编码用户自定义类型,用户需要自己把它注册进去,前提是支持 go-hessian2 的 POJO interface,才能跟 JAVA 对应类互相解编码。 + +       ![](../../pic/arch/dubbo-go-review-and-future-h.jpg)       + +上面是 go-hessian2 的类型对应表,需要特别点出的是 int,go 这边的 int 类型在不同字长系统下是有不同的大小,可能是 32 位也可能 64位的,而 Java 的 int 是 32 位的,所以我们以 go 语言的 int32 类型对应 Java int 类型。 + +刚才提到了 Java 的 Class 和 go struct 的对应。上图有一个 go-hessian2 的 POJO 接口定义,每个 Java class 对应到 go struct,则 struct 需要给出 Java ClassName。 + +       ![](../../pic/arch/dubbo-go-review-and-future-i.jpg)       + +你也可以加 hessian 标签,解析的时候会把这个字段名用别名写进去,实现自定义 fieldName。默认情况下,go-hessian2 中会自动把 struct field 首字母变成小写作为其 fieldName。 + +       ![](../../pic/arch/dubbo-go-review-and-future-j.jpg)       + +泛化引用,是 dubbogo 里面比较重要的功能。社区一位同学需要基于 dubbogo 实现网关,收集外部的请求,然后通过泛化引用的形式调用其他 Dubbo 服务,最后自己动手把它实现了。使用时,首先需要在项目里内置一个 GenericService 服务,调用Load,然后就像正常的调用服务一样直接调用,跟 Java 是类似的,Go 客户端可以不知道 Java 的接口定义和类定义,把方法名、参数类型、参数数组以一个 map 的形式传输到 Java 服务端,Java 服务端收到请求后进行识别,把它转换成 POJO 类。 + +以上是 go-hessian2 一些细节。上文讲到的泛化引用,是把网关作为整个内网 Dubbo 服务的公共消费端,使用的时候只需要知道请求的方法、参数类别,然后就能够调用 Dubbo 的服务。后面主要分享三部分内容:首先是网络引擎、底层网络库这块;其次是服务治理方面的内容,其中包含以 k8s 作为注册中心的一个初步的解决方案;第三部分是互联互通,主要是和 grpc 打通。最后给出一个展望,包含 dubbogo 社区明年的工作内容。 + +**03** + +**网络引擎** + +dubbogo 的网络引擎里面分为三层, 如下图所示: + +![](../../pic/arch/dubbo-go-review-and-future-k.jpg) + +最底层 streaming 处理二进制流,第二层 codec层,进行协议的序列化和反序列化,第三层是 Eventlistener,提供应用使用接口。streaming 层能支持 websocket、TCP、UDP 三种网络通讯协议,这层具有一定的灵活性,今年年初上海有一个同学今年把 KCP 也加进去了,当时说要开源贡献出来,我还在期待中。codec 层可以适用不同协议,用户自定义即可。 + +       ![](../../pic/arch/dubbo-go-review-and-future-l.jpg) + +EventListener 对上层暴露了 4 个回调接口。第一个是 OnOpen,网络连接初建成功时被调用,应用层如果判定其为正常连接,则可以把连接 session 存储下来,如果用户判断当前连接过多则返回一个非空的 error,则这个连接会被 dubbogo 关闭。其次是 OnError 事件,当网络连接出错,就会回调到这个接口,在 dubbogo 关闭这个连接之前允许用户做相应处理,如把网络连接 session 从应用层的 session 池中删除。第三个是 OnCron,处理定时任务,如心跳,dubbogo 针对 websocket 协议在底层直接把心跳热任务处理了,针对 tcp 和 udp 协议需要用户在这个回调函数中自己实现。第四个接口是 OnMessage,用作处理一个完整的网络包。可以看到整个回调接口风格跟 websocket 的接口很像。 + +       ![](../../pic/arch/dubbo-go-review-and-future-m.jpg)       + +**协程池** + +dubbogo 的 goroutine pool 里有 worker channel 【数量为 M】和逻辑处理 goroutine 【数量为 N】和网络任务【网络包】三种角色,网络解包后把把包按照某种规则放入某个 worker pool,然后逻辑处理 goroutine 从 channel 中读取数据包并执行逻辑处理,其目的是是为了把网络 I/O 与逻辑处理分开。不同的 goroutine pool 设计中,有的 N 大小会变化,有的不变,分别可称之为可伸缩 goroutine pool 和不可伸缩 goroutine pool,可伸缩 goroutine pool 可以对机器资源的使用不可预计。dubbogo 采用了不可伸缩 goroutine pool,其考量是限定其网络资源使用的上限。 + +另外,dubbogo 的 goroutine pool 不考虑收包后的处理顺序。譬如,dubbogo 服务端收到了 A 和 B 两个网络包,dubbogo 有可能先处理网络包 B,后处理网络包 A。如果客户端的每次请求都是独立的,没有前后顺序关系,则带有不考虑网络包处理顺序是没有问题的。如果有强顺序要求,譬如上层用户关注 A 和 B 请求处理的前后顺序,则可以把 A 和 B 两个请求合并为一个请求,或者把 dubbogo 的 goroutine pool 特性关闭。 + +一般情况下,不建议大家自己写 goroutine pool,因为 Go 语言对 goroutine 资源的管理已经非常先进,比如释放一个协程,Go 不会马上销毁掉相关的资源,一旦有创建 goroutine 的需要,马上就可复用这个成本是很低的。什么情况下使用 Goroutine Pool 呢?个人觉得像网络库逻辑处理这类场景下执行同样类型任务场景下确定 goroutine 会被迅速重复使用时可以尝试使用,但是怎么用好还是需要仔细考量,即需要仔细考量 M 与 N 的比例关系。 + +假设处理某种网络任务请求,有的请求1秒就处理完了,有的可能10毫秒处理完了,设置 M 与 N 比例为 1:1,这样 1 对 1 造成的后果可能是饥饿,就是有一些队列处理的很快,有的处理很慢,整体负载不均衡,这种情况下就不推荐你用协成池了。 + +还有一个比例模型是是1:N的,一写多读,比如说所有的请求都交给一个队列,所有逻辑处理 goroutine pool 都消费这个队列,造成的结果是什么呢?因为你只有一个生产者,那你就只有一个队列,多个消费者消费这一个队列,造成的结果是什么呢?因为 go channel 的低效率【整体使用一个 mutex lock】造成消费者 goroutine hang 在锁竞争上,当然其网络包处理顺序更无从保证。 + +比较均衡的效果就是 M 和 N 都大于 1,dubbogo 的的 goroutine pool 模型中 M 和 N 的取值可以自行配置,其效果是每个 channel 被 N/M 个 goroutine 消费,这种模型类似于 kafka 的 consumer group,其优点是兼顾处理效率和锁压力平衡,可以做到总体层面的任务处理均衡。 + +## + +**优化改进** + +优化改进主要从三个方面入手, 如下图所示: + +![](../../pic/arch/dubbo-go-review-and-future-n.jpg) + +      + +1\. 内存池。goroutine pool 是管理对 CPU 资源的分配,内存池就是管理内存资源的分配。我个人反对纯粹为了炫技没有目的地写内存池,其实 Go 的内存管理这块目前优化的很好了。Go 语言初始版本的内存管理使用了谷歌自家的 tcmalloc 库,这个库把应用释放的内存自己先缓存住,待失效期后才释放,那这样造成的结果是什么呢?就是早期的 Go 程序的内存成本很高。假设程序一个 sidecar 程序的资源限制是内存2G,CPU 核数是 2 核,用这样一个内存管理库,内存用完不释放给操作系统,那么没人敢用这个项目,当然最新的 Go 内存管理器是经过完全重构的,虽然也区分不同大小 span 的内存在 P 级别和全局级别进行缓存,但是基本上不用考虑这种内存膨胀不可控的问题了。那么什么情况下使用内存池呢?你确定你的业务有一些对象是频繁的复用则可以尝试使用。 目前大部分内存池技术底层依赖的底座都是 sync.Pool,自己写一个也不难。而且 Go 1.13 之后的 sync.Pool 已经可以做到跨 GC span 不释放缓存对象,非常之好。   + +2\. 定时器。Go 语言早期定时器因为整体使用一把大锁的缘故效率极差,当然最新的就相当好了,通过每个 CPU 核下一个定时器的方法【类似于分片锁】分散了竞争压力,但是很多情况下还是有竞争压力,如果对时间精度要求不高个人建议在自己的应用中自己写一个简单的时间轮实现一个定时器,释放 CPU 压力。 + +3\. 网络写 buffer 合并。写 buffer 合并一般采用 writev,但是 Go 语言的 writev 有内存泄露问题,我这边一个负责 MOSN 开发的同事元总发现的。他先给 Go 语言官方提交了 PR,然后在 MOSN 中把 writev 扔掉自己写了一个简单好用的写 buffer 合并发送实现:通过 for 循环 10 次从发送 channel 中把网络包读取出来然后合并发送,当然循环中间网络发送 channel 没有足够的网络包就通过 \`select-default\` 分支立即退出循环。 + +**channel 使用** + +Go 语言是一个适合处理 IO 密集型任务的语言,不擅长处理 CPU 密集型任务,其内存通信的基础就是 channel。channel 整体的内存基础是一个 ring buffer 数组和一个 lock,外加其他一些读写通知队列等,也是因为一把大锁的缘故,则 buffer 型 channel 如果使用不当则效率不会很高,如每个 channel element 的内存使用过大。channel 还有一个 closed 字段,用于判定 channel 的写是否被关闭掉,Go 语言对其操作是以原子锁方式进行的,很多人以这个字段为基础进行信号通知,如果使用不当很可能造成 for 循环 CPU 100% 的问题,所以在 for-select 循环中特别要谨慎使用,dubbogo 在这方面踩过坑。 + +**04** + +**服务治理** + +下面为大家讲一下服务治理,说到服务治理,其实最重要的还是服务发现和服务注册,这块逻辑跟 Dubbo 类似,这次不作展开。下面主要包含两方面的内容,分别是限流算法和优雅退出。 + +**限流算法** + +限流算法首先需要考虑限流的对象,dubbogo 需要考虑 interface 和 method。其次是限流方法,首先需要考虑的是单机限流还是集群限流,单机限流算法很多,譬如常用的固定窗口算法和滑动窗口算法,以及更进一步的自适应限流。限流时一个重要问题就是限流参数是很难配的,譬如线上服务到底需要使用多少机器资源合理,限流窗口的时间窗口时长应该多长合适,其 qps 值设置多少合适呢?这都是 dubbogo 需要解决的问题。先进如谷歌的 BBR 算法,可以在当前的网络环境恶化前不断尝试改进相关参数,直到尝试出一段时间内的最佳参数。还有一些业务形态下的限流,如针对会员和非会员分别设计不同的限流链路。 + +Dubbo 的限流接口源码如下: + +       ![](../../pic/arch/dubbo-go-review-and-future-o.png)       + +这个接口抽象是非常漂亮的,第一个是限流 url,第二个服务调用。下面是 Dubbo 的固定窗口限流源码: + +       ![](../../pic/arch/dubbo-go-review-and-future-p.png)       + +上面的代码很明显,"private final" 决定了 Dubbo 使用者只能使用期给定的固定窗口限流限算法,无法扩展。 + +以下是 dubbogo 的限流接口: + +       ![](../../pic/arch/dubbo-go-review-and-future-q.jpg)       + +TpsLimiter 是限流对象,TpsLimitStrategy 是限流算法,RejectedExecutionHandle 是限流动作。 + +接下来是一个固定窗口算法实现: + +       ![](../../pic/arch/dubbo-go-review-and-future-r.jpg)        + +上图是 dubbogo 的固定窗口算法实现,其非线程安全,大家看一下代码就可以了,不推荐大家用。下图是 dubbogo 的滑动窗口算法实现: + +       ![](../../pic/arch/dubbo-go-review-and-future-s.jpg)         + +其基本原理是用一个队列存储一段时间内的请求,然后根据队列长度判定即可。 + +不管是固定窗口还是滑动窗口,其判定算法简单,麻烦的是其参数设置,如下图: + +       ![](../../pic/arch/dubbo-go-review-and-future-t.png)        + +固定窗口时长精度很难控制。比如说限流一秒 QPS 值 1000,前 100 毫秒来了一千个请求,然后判定算法把请求放过了,而其后 900 毫秒 任何请求都无法处理。一般的处理方法是把时间粒度更精细一些,dubbogo 的时间窗口最小单位是一毫秒,则用户可以把时间窗口设定为 100 毫秒,总体来说一段时间内是很平稳的。下面这个图是我们社区的 commiter 邓明写完博客发出来,行业大佬微信评论如下: + +       ![](../../pic/arch/dubbo-go-review-and-future-u.png)       + +图中第一个问题是 qps 和 tps 每个请求成本不同,这个问题怎么处理呢?个人觉得这是一个分级限流问题,在同一个服务下针对不同的请求做不同的分级处理。第二个问题 ”配置了 qps 1000,但是请求过来是10万你还是死“,这个就需要更上层的运维能力进行应对,譬如判定为恶意流量攻击就应该在网关层拦截掉,如果是服务能力不行就扩容。 + +针对分级限流,dubbogo 目前尚无法在同一个进程内完成,这需要 dubbogo 的配置中心更完善以后进行处理,用户可以通过搭建不同的服务链路处理之。譬如会员/非会员分级,同一个服务针对不同的会员等级搭建相应的链路,在网关层就判定一个 userID 是否是会员,然后发送不同的链路。 + +dubbogo 的单机熔断是基于 hystrix-go 实现的,其判定参数有最大并发请求数、超时时间、错误率;其次是保护窗口,是熔断时长,熔断多久后进行服务恢复;第三个是保护性动作,就是在保护时间窗口之内执行什么样的动作,具体实现用户自定义。 + +       ![](../../pic/arch/dubbo-go-review-and-future-v.jpg)        + +**优雅退出** + +优雅退出也是邓明同学的大作,可以在网络上搜到相关博客。实现优雅退出的步骤有: + +1. 告知注册中心,服务即将关闭,此时等待并处理请求; + +2. 注册中心通知别的客户端,别的客户端停止发送新请求,等待已发请求的响应; + +3. 节点处理完所有接收到的请求并且返回响应后,释放作为服务端相关的组件和资源; + +4. 节点释放作为客户端的组件和资源。 + + +        ![](../../pic/arch/dubbo-go-review-and-future-w.jpg)        + +所以每一步基本上都要给程序一定的时间进行等待,所以等的时间窗口是多少呢?dubbogo 默认每个步骤大概花2秒,总体一个时间窗口是10秒。 + +       ![](../../pic/arch/dubbo-go-review-and-future-x.png)       + +基本上在别的 RPC 框架里面,可能不太常见到这种处理。 + +**05** + +**Dubbogo 上云 +** + +   + +dubbogo 作为微服务框架如何适配 k8s,如何部署?dubbogo 本身是一个 RPC 框架,但是其又有了服务治理能力,这部分能力与 k8s 的部分能力有些重合,不可能为了适配 k8s 就彻底抛弃。目前 Dubbo 官方也没有很好的解决方案供我们参考,所以这里我们 dubbogo 先给出一个简单的常识性的实践方案。下面先分析下 dubbogo 的 interface/service 和 k8s service 两者直接的差别。 + +![](../../pic/arch/dubbo-go-review-and-future-y.jpg) + +      + +k8s service 是许多具有相同服务能力 pod 资源的聚合,它自己的负载均衡算法以及健康检查等功能。而 Dubbo 里面的 interface/service 仅仅是服务 provider 集合,服务治理能力依赖 dubbo 的 directory、router 和 loadbalace 等额外的功能模块。并且Dubbo 服务区分 group/version,还有 provider、consumer 角色等等。Dubbo interface/service 无法与 k8s service 对标,Dubbo interface/service 和其整体服务治理能力才能对标成 k8s service。二者差异这么大,如何将 dubbo 集成到 k8s 中呢? + +k8s 提供了 pod/endpoint/service 三层维度的资源。简单的做法,可以通过监听pod/endpoint/service 三层维度资源的事件,作出合理的处理以达到服务治理的目的。目前我们社区成员王翔提交了一个基于监听 pod 事件来实现服务治理的 pr,优点就是不需要引入额外组件,通过监听 k8s 中最细粒度资源 pod 的事件,通过 k8s apiserver 获取 pod 列表,只是通过 apiserver 使用 etcd 的服务注册和服务通知能力,其他继续使用 Dubbo 的服务治理能力。其优点是模型简单,不需要实现额外的模块,几乎不需要对 Dubbo 作出改动,缺点就是其实无法使用 k8s 自己的健康检查能力,需要自己监听很细粒度的 pod 事件来综合处理服务健康、服务上下线等情况,而且还存在没有使用 k8s service 的事件监听能力,每个 consumer 冗余监听一些不必要监听的事件,加大 apiserver 的网络压力。所以其实现目前来看可能还不是最优解,与 k8s 建议的operator 方式也有一定的背离。社区目前还在讨论新方案,寻求 k8s 最优解,大部分人倾向于采用 k8s 社区推荐的 operator 方案,但是其开发和线上维护成本就上升了。后面两种方式会共存,使用者见仁见智。 + +**06** + +**互融互通** + +   + +关于互融互通,Dubbo 明年有个三个重要目标,其中一个目标是与外面的微服务生态进行互联互通,比如说跟 grpc 互通。目前 dubbo 的 grpc 的解决方案已经开放出来,dubbogo 与 grpc 互通的开发工作也几近完成。 + +下面左边 dubbogo 的代码生成器工具根据 grpc 的 pb 服务定义文件自动生成的适配 dubbogo 的代码,右边是对应的使用示例。不同于 k8s service 的复杂性,grpc 整体仅仅具有 rpc 能力,没有服务治理能力,所以原始的 grpc 就可以很好的嵌入到 dubbogo 里面,grpc server 的 methodhandler 对我们 dubbogo 来说就是 dubbo invoker,grpc 的一些相关的接口直接跟我们的接口嵌套起来,两个生态就对接起来了。 + +      ![](../../pic/arch/dubbo-go-review-and-future-z.jpg) + +**07** + +**展望未来 +** + + + +最后就是展望未来,也就是明年的规划。 + +![](../../pic/arch/dubbo-go-review-and-future-0.jpg) + +      + +明年我们将会很快实现 dubbo router。社区在 8月份已经实现了 router 功能需要的底层的算法模块,但是当时配置中心下发相关的参数的能力还不是很强,所以没有最终完成。最近服务治理配置刚刚支持了 zookeeper 和 apollo,预计很快就可以将 router 的参数通过配置中心下发的形式支持掉。另外,还有 tracing,我们将会引入社区主流的 tracing 方案,以 opentracing 为标准,去集成 opentracing 开源生态的相关能力。第三个是 kubernetes operator,这个就是刚才说的 K8s 的服务调用,我们会基于 operator 的方案做一版新的基于 k8s 的注册中心实现。最后就是云原生生态的融入,即与 istio 的集成,dubbogo 将会成为 dubbo 在 service mesh 生态中的重要角色。 + +目前 dubbogo 项目,今年是能 run 起来,质量方面还有很多工作要做,功能基本上到明年可与 dubbo 2.7 补齐,目前已经基本够用。目前落地实践的是 3 个比较典型的公司,一个是携程,还有一个是涂鸦智能。 + +dubbogo 本身是一个 go 语言项目,也期待与其他 go 社区的指正或者需求,一起成长。 \ No newline at end of file diff --git a/doc/md/config-center/how-to-implement-remote-configuration-management-in-dubbo-go.md b/doc/md/config-center/how-to-implement-remote-configuration-management-in-dubbo-go.md new file mode 100644 index 0000000000..55356f286d --- /dev/null +++ b/doc/md/config-center/how-to-implement-remote-configuration-management-in-dubbo-go.md @@ -0,0 +1,274 @@ +# [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 diff --git a/doc/md/course/the-5th-years-of-dubbo-go.md b/doc/md/course/the-5th-years-of-dubbo-go.md new file mode 100644 index 0000000000..288c8ab0fb --- /dev/null +++ b/doc/md/course/the-5th-years-of-dubbo-go.md @@ -0,0 +1,165 @@ +# [写在 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 也经常使用,受益良多; + +* 代码规范,社区内部有一份简单的代码规范,并随着项目发展不断完善。 + + +# 展望 + +
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 diff --git a/doc/md/getty/getty-development-log.md b/doc/md/getty/getty-development-log.md new file mode 100644 index 0000000000..c98be503ad --- /dev/null +++ b/doc/md/getty/getty-development-log.md @@ -0,0 +1,224 @@ +## [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 diff --git a/doc/md/hessian2/dubbo-go-hessian2-performance-optimization.md b/doc/md/hessian2/dubbo-go-hessian2-performance-optimization.md new file mode 100644 index 0000000000..875ea15896 --- /dev/null +++ b/doc/md/hessian2/dubbo-go-hessian2-performance-optimization.md @@ -0,0 +1,233 @@ +# 记一次对 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 diff --git a/doc/md/hessian2/what's-new-in-dubbo-go-hessian2-v1.6.0.md b/doc/md/hessian2/what's-new-in-dubbo-go-hessian2-v1.6.0.md new file mode 100644 index 0000000000..61589e6b64 --- /dev/null +++ b/doc/md/hessian2/what's-new-in-dubbo-go-hessian2-v1.6.0.md @@ -0,0 +1,64 @@ +# [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 diff --git a/doc/md/hessian2/what's-new-in-dubbo-go-hessian2-v1.7.0.md b/doc/md/hessian2/what's-new-in-dubbo-go-hessian2-v1.7.0.md new file mode 100644 index 0000000000..5e8ce0bb47 --- /dev/null +++ b/doc/md/hessian2/what's-new-in-dubbo-go-hessian2-v1.7.0.md @@ -0,0 +1,268 @@ +# [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 diff --git a/doc/md/interview/dubbo-go-published.md b/doc/md/interview/dubbo-go-published.md new file mode 100644 index 0000000000..cb2848dee7 --- /dev/null +++ b/doc/md/interview/dubbo-go-published.md @@ -0,0 +1,157 @@ +# [冲上云原生,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 diff --git a/doc/md/interview/what's-new-in -dubbo-go-v1.4.0.md b/doc/md/interview/what's-new-in -dubbo-go-v1.4.0.md new file mode 100644 index 0000000000..67dd72d92b --- /dev/null +++ b/doc/md/interview/what's-new-in -dubbo-go-v1.4.0.md @@ -0,0 +1,167 @@ +# [dubbo-go 1.4.0 版本发布,支持 K8s 注册中心、rest 协议](https://blog.csdn.net/weixin_45583158/article/details/105132322) + + +2020-03-26 09:30:00 + +得益于社区活跃的支持,2020 年 3 月 25 日 我们发布了一个让人兴奋的版本——dubbo-go v1.4.0。除了继续支持已有的 Dubbo 的一些特性外, dubbo-go 开始了一些自己的创新尝试。 + +这个版本,最大的意义在于,做了一些支持云原生的准备工作。比如说,社区在探讨了很久的 k8s 落地之后,终于拿出来了使用 k8s 作为注册中心的解决方案。 + +其次一个比较大的改进是--我们在可观测性上也迈出了重要的一步。在这之前,dubbo-go只提供了日志这么一个单一手段,内部的信息比较不透明,这个版本将有很大的改善。 + +最后一个令人心动的改进是,我们支持了 REST 协议。 + +## 1\. K8s 注册中心 + +dubbo-go 注册中心的本质为K/V型的数据存储。当前版本实现了以 Endpoint 为维度在 k8s API Server 进行服务注册和发现的方案【下文简称 Endpoint 方案】,架构图如下。 + +![](../../pic/interview/what's-new-in-dubbo-go-v1.4.0-a.png "dubbo-go-k8s.png") + +Endpoint 方案,首先将每个 dubbo-go 进程自身服务信息序列化后,通过 Kubernetes 提供的 Patch 的接口写入在自身 Pod 对象的 Annotation 中。其次,通过 Kubernetes 的 Watch 接口观察集群中本 Namespace 内带有某些固定lable \[见上图\] Pod 的Annotation 信息的更新,处理服务健康检查、服务上下线等情况并实时更新本地缓存。整体流程仅使用 Kubernetes 原生 API 完成将 Kubernetes 作为注册中心的功能特性。 + +这个方案非常简洁,不需要实现额外的第三方模块,也不需要对 Dubbo 业务作出改动,仅仅把 k8s 当做部署平台,依赖其容器管理能力,没有使用其 label selector 和 service 等服务治理特性。如果站在 k8s Operator 的角度来看,Operator 方案的优点即 Endpoint 方案的缺点,Endpoint 方案无法使用 k8s 的健康检查能力,亦没有使用 k8s service 的事件监听能力,每个 consumer 冗余监听一些不必要监听的事件,当 Endpoint 过多时会加大 API Server 的网络压力。 + +目前 dubbo-go 社区其实已经有了 operator 版本注册中心的技术方案, 后续版本【计划版本是 v1.6】的 dubbo-go 会给出其实现。相比当前实现,operator 方案开发和线上维护成本当然上升很多。二者如同硬币的两面,社区会让两种方式会共存,以满足不同 level 的使用者。 + +注意: 因 Pod 被调度而 IP 发生变化时,当前版本的 configuration 以及 router config 模块暂时无法动态更新。这有待于我们进一步解决。 + +参考范例\[1\]. + +## 2\. tracing 和 metric + +可观测性是微服务重要的一环,也是我们1.4版本着力支持的部分。在1.4版本中,我们主要在 tracing 和 metric 两个方向提供了支持。 + +为了支持 tracing 和 metric,关键的一点是支持context在整个调用过程中传递。为此我们解决了context跨端传递的问题。目前用户可以在接口中声明 context 并且设置值,dubbo-go 在底层完成 context 内容从 client 传递到 server 的任务。 + +![](../../pic/interview/what's-new-in-dubbo-go-v1.4.0-b.png "image.png") + +在 metric 方面,dubbo-go 开始支持 Prometheus 采集数据了。目前支持 Prometheus中 的 Histogram 和 Summary。用户也可以通过扩展 Reporter 接口来自定义数据采集。 + +在 tracing 方面,目前 dubbo-go 的设计是采用 opentracing 作为统一的 API,在该 API 的基础上,通过在 client 和 server 之中传递 context,从而将整个链路串起来。用户可以采用任何支持 opentracing API 的监控框架来作为实现,例如 zipkin,jaeger 等。 + +## 3\. rest协议支持 + +Dubbo 生态的应用与其他生态的应用互联互通,一直是 dubbo-go 社区追求的目标。dubbo-go v1.3 版本已经实现了 dubbo-go 与 grpc 生态应用的互联互通,若想与其他生态如 Spring 生态互联互通,借助 rest 协议无疑是一个很好的技术手段。 + +Rest 协议是一个很强大并且社区呼声很高的特性,它能够有效解决 open API,前端通信,异构系统通信等问题。比如,如果你的公司里面有一些陈年代码是通过 http 接口来提供服务的,那么使用我们的 rest 协议就可以无缝集成了。 + +通过在 dubbo-go 中发布 RESTful 的接口的应用可以调用任意的 RESTful 的接口,也可以被任何客户端以 http 的形式调用,框架图如下: + + +![](../../pic/interview/what's-new-in-dubbo-go-v1.4.0-c.png "dubbo-go-rest.png") + +在设计过程中,考虑到不同的公司内部使用的 web 框架并不相同,所以我们允许用户扩展自己 rest server ( web 框架在 dubbo-go的封装)的实现,当然,与 rest server 相关的,诸如 filter 等,都可以在自己的 rest server 实现内部扩展。 + +## 4\. 路由功能增强 + +路由规则在发起一次 RPC 调用前起到过滤目标服务器地址的作用,过滤后的地址列表,将作为消费端最终发起 RPC 调用的备选地址。v1.4 版本的 dubbo-go 实现了 Condition Router 和 Health Instance First Router,将在后面版本中陆续给出诸如 Tag Router 等剩余 Router 的实现。 + +### 4.1 条件路由 + +条件路由,是 dubbo-go 中第一个支持的路由规则,允许用户通过配置文件及远端配置中心管理路由规则。 + +与之相似的一个概念是 dubbo-go 里面的 group 概念,但是条件路由提供了更加细粒度的控制手段和更加丰富的表达语义。比较典型的使用场景是黑白名单设置,灰度以及测试等。 + +参考范例\[2\]。 + +### 4.2 健康实例优先路由 + +在 RPC 调用中,我们希望尽可能地将请求命中到那些处理能力快、处于健康状态的实例,该路由的功能就是通过某种策略断定某个实例不健康,并将其排除在候选调用列表,优先调用那些健康的实例。这里的"健康"可以是我们自己定义的状态,默认实现即当错误比例到达某一个阈值时或者请求活跃数大于上限则认为其不健康,允许用户扩展健康检测策略。 + +在我们服务治理里面,核心的问题其实就在于如何判断一个实例是否可用。无论是负载均衡、 + +熔断还是限流,都是对这个问题的解答。所以,这个 feature 是一个很好的尝试。因为我们接下来计划提供的特性,基于规则的限流以及动态限流,都是要解决“如何断定一个实例是否可用”这么一个问题。 + +所以欢迎大家使用这个特性,并向社区反馈各自设定的健康指标。这对我们接下来的工作会有很大的帮助。 + +## 5\. hessian 协议增强 + +相较于 dubbo 的 Java 语言以及其他多语言版本,dubbo-go 社区比较自豪的地方之一就是:无论底层网络引擎还是原生使用的 hessian2 协议,以及整体服务治理框架,都由 dubbo-go 社区从零开发并维护。v1.4 版本的 dubbo-go 对 hessian2 协议又带来了诸多新 feature。 + +### 5.1 支持 dubbo 协议的 attachments + +在 dubbo-go中,attachments 机制用于传递业务参数之外的附加信息,是在客户端和服务端之间传递非业务参数信息的重要方式。 + +hessian 编码协议将之编码在 body 内容的后面进行传输,dubbo-go-hessian2 之前并不支持读/写 attachments,在多个使用方【如蚂蚁金服】的要求下,dubbo-go-hessian2 以兼容已有的使用方式为前提,支持了 attachments 的读/写。 + +Request 和 Response 的 struct 中定义了 attachments 的 map,当需要使用 attachments,需要由使用方构造这两种类型的参数或者返回对象。否则,将无法在hessian的传输流中获取和写入attachments。 + +另外,利用 dubbo-go 调用链中传输 context 的功能,用户已经可以在服务方法中通过 context 添加 attachments了。 + +### 5.2 支持忽略非注册 pojo 的解析方式 + +由于 hessian 编码协议与 Java 的类型高度耦合,在 golang 的实现中会相对比较麻烦,需要有指明的对应类型。dubbo-go-hessian2 的实现方式是:定义 POJO 接口,要求实现 JavaClassName 方法来供程序获取 Java 对应的类名。这导致了接收到包含未注册类的请求时,将会无法解析而报错,这个问题以前是无法解决的。 + +但是,有一些使用场景如网关或者 service mesh 的 sidecar,需要在不关心 Java 类的具体定义的情况下,像 http读取 header 信息一样仅仅读取 dubbo 请求的附加信息,将 dubbo/dubbo-go 请求转发。通过该 feature,网关/sidecar 并不关注请求的具体内容,可以在解析请求的数据流时跳过无法解析的具体类型,直接读取 attachments 的内容。 + +该实现通过在 Decoder 中添加的 skip 字段,对每一个 object 做出特殊处理。 + +### 5.3 支持 java.math.BigInteger 和 java.math.BigDecimal + +在 Java 服务中,java.math.BigInteger 和 java.math.BigDecimal 是被频繁使用的数字类型,hessian 库将它们映射为 github.com/dubbogo/gost/math/big 下的对应类型。 + +### 5.4 支持 ‘继承’ 和忽略冗余字段 + +由于 go 没有继承的概念,所以在之前的版本,Java 父类的字段不被 dubbo-go-hessian2 所支持。新版本中,dubbo-go-hessian2 将Java来自父类的字段用匿名结构体对应,如: + +```cpp +type Dog struct { + Animal + Gender string + DogName string `hessian:"-"` +} +``` + +同时,就像 json 编码中通过 `immediately` 可以在序列化中忽略该字段,同理,通过 `hessian:"-"` 用户也可以让冗余字段不参与 hessian 序列化。 + +目前,上述四个特性已被某 Go 版本的 sidecar 集成到其商业版本中提供商业服务。 + +## 6\. Nacos 配置中心 + +配置中心是现代微服务架构里面的核心组件,现在 dubbo-go 提供了对配置中心的支持。 + +![](../../pic/interview/what's-new-in-dubbo-go-v1.4.0-d.png "image.png") + +Nacos 作为一个易于构建云原生应用的动态服务发现、配置管理和服务管理平台,在该版本终于作为配置中心而得到了支持。 + +参考范例\[3\]. + +## 7\. 接口级签名认证 + +Dubbo 鉴权认证是为了避免敏感接口被匿名用户调用而在 SDK 层面提供的额外保障。用户可以在接口级别进行定义是否允许匿名调用,并对调用方进行验签操作,对于验签不通过的消费端,禁止调用。 + +![](../../pic/interview/what's-new-in-dubbo-go-v1.4.0-e.png "image.png") + +如上图,总体实现基于 AK/SK 机制,应用通过 HTTPS 通信,启动时向鉴权服务拉取,定期更新。且允许用户自定义获取 AK/SK 的源,在 RPC 层面保障安全性。 + +## 8\. 回顾与展望 + +目前 dubbo-go 已经到了一个比较稳定成熟的状态。在接下来的版本里面,我们将集中精力在云原生上。下一个版本,我们将首先实现应用维度的服务注册,这是一个和现有注册模型完全不同的新的注册模型。也是我们朝着云原生努力的一个关键版本。 + +在可观测性上,我们计划在整个 dubbo-go 的框架内,引入更多的埋点,收集更加多的内部状态。这需要实际生产环境用户的使用反馈,从而知道该如何埋点,收集何种数据。 + +在限流和熔断上,可以进一步扩展。当下的限流算法,是一种静态的算法--限流参数并没有实时根据当前服务器的状态来推断是否应该限流,它可能仅仅是用户的经验值。其缺点在于,用户难以把握应该如何配置,例如 TPS 究竟应该设置在多大。所以计划引入一种基于规则的限流和熔断。这种基于规则的限流和熔断,将允许用户设置一些系统状态的状态,如 CPU 使用率,磁盘 IO,网络 IO 等。当系统状态符合用户规则时,将触发熔断。 + +目前这些规划的 任务清单\[4\],都已经放入在 dubbo-go 项目的 issue 里面,欢迎感兴趣的朋友认领参与开发。dubbo-go 社区在 **钉钉群 23331795** 欢迎你的加入。 + +**文中链接:** + +\[1\] https://github.com/apache/dubbo-samples/tree/master/golang/registry/kubernetes + +\[2\] https://github.com/dubbogo/dubbo-samples/tree/master/golang/router/condition + +\[3\] https://github.com/dubbogo/dubbo-samples/tree/master/golang/configcenter/nacos + +\[4\] https://github.com/apache/dubbo-go/milestone/1 + +**参考阅读:** + +* [从lstio的角度谈微服务的一些误区](https://blog.csdn.net/weixin_45583158/article/details/105085686) + +* [Go语言如何实现stop the world?](https://blog.csdn.net/weixin_45583158/article/details/104912555) + +* [关于Golang GC的一些误解--真的比Java算法更领先吗?](https://blog.csdn.net/weixin_45583158/article/details/100143593) + +* [Swift程序员对Rust印象:内存管理](https://blog.csdn.net/weixin_45583158/article/details/104853360) + +* [JDK 14发布,空指针错误改进正式落地](https://blog.csdn.net/weixin_45583158/article/details/104981073) \ No newline at end of file diff --git a/doc/md/practice/dubbo-go-experience.md b/doc/md/practice/dubbo-go-experience.md new file mode 100644 index 0000000000..5ee1d6db53 --- /dev/null +++ b/doc/md/practice/dubbo-go-experience.md @@ -0,0 +1,199 @@ +# [dubbo-go 踩坑记](https://dubbogo.github.io/dubbo-go-website/zh-cn/blog/dubbo-go-experience.html) + +## 扯淡 + +### 前尘 + +由于我的一个项目需要做公司用户鉴权,而组内其他小伙伴刚好有一个 _dubbo_ 的鉴权 _rpc_ ,一开始我是打算直接的读 _redis_ 数据然后自己做解密。工作进行到一半,由于考虑到如果以后这个服务有任何变动,我这边要有联动行为,所以改用 _go_ 来调用 _dubbo_ 的 _rpc_ ,于是我在 _github_ 上找到了 [雨神](https://github.com/AlexStocks) 的 [dubbogo](https://github.com/AlexStocks/dubbogo) (PS: 这个是 _dubbo-go_ 前身)。不得不说,雨神是热心的人儿啊,当时还帮着我调试代码。最后也是接入了一个阉割版的吧,主要是当时 _hessian2_ 对泛型支持的不怎么好。 + +### 现在 + +目前 [dubbo-go](https://github.com/apache/dubbo-go)隶属于 _apache_ 社区,相比以前做了部分重构,并且维护也很活跃了。 + +## 接入 + +### 问题 + +目前整个项目在快速的迭代中,很多功能还没有完善,维护人员还没有时间来完善文档,所以在接入的时候要自己看源码或调试。 + +### 说明 + +目前我司在使用 _dubbo_ 的过程使用的 _zookeeper_ 作为注册中心,序列化是 _hessian2_ ,所以我们要做如下初始化: + +```go + import ( + _ "github.com/apache/dubbo-go/common/proxy/proxy_factory" + _ "github.com/apache/dubbo-go/registry/protocol" + + _ "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" + ) +``` + +### 配置 + +由于我是接入客户端,所以我这边只配置了 _ConsumerConfig_ 。 + +```yaml +dubbo: + # client + request_timeout: "3s" + # connect timeout + connect_timeout: "3s" + check: true + application: + organization: "dfire.com" + name: "soa.sso.ITokenService" + module: "dubbogo token service client" + version: "1.0.0" + owner: "congbai" + registries: + "hangzhouzk": + protocol: "zookeeper" + timeout: "3s" + address: "zk1.2dfire-daily.com:2181" + username: "" + password: "" + references: + "ITokenService": + registry: "hangzhouzk" + protocol: "dubbo" + interface: "com.dfire.soa.sso.ITokenService" + version: "1.0.0" + methods: + - name: "validate" + retries: "3" +``` + +我这里是把 _dubbo-go_ 作为第三方库来用,所以我没使用官方 [dubbo-samples](https://github.com/dubbogo/dubbo-samples/golang) 那样在 _init_ 函数中读入配置。 + +配置代码如下: + +```go + import ( + "github.com/apache/dubbo-go/config" + "github.com/apache/dubbo-go/protocol/dubbo" + ) + + type DubboCli struct { + } + + func NewCli(cconf config.ConsumerConfig) *DubboCli { + config.SetConsumerConfig(cconf) + + dubbo.SetClientConf(dubbo.GetDefaultClientConfig()) + + config.Load() + + return &DubboCli{} + } +``` + +### 接入 + +好了,配置加载完就说明我们的准备工作已经做好了,接下来就要接入 _rpc_ 接口了。 + +#### 返回值 + +一般 _rpc_ 调用的返回值都是自定义的,所以我们也要告诉 _dubbo-go_ 长什么样子。这个结构体要跟 _java_ 的类对应起来,这里我们是要实现 _hessian2_ 的 _interface_ : + +```go +// POJO interface +// !!! Pls attention that Every field name should be upper case. +// Otherwise the app may panic. +type POJO interface { + JavaClassName() string // got a go struct's Java Class package name which should be a POJO class. +} +``` + +我的实现如下: + +```go +type Result struct { + Model interface{} `json:"model,omitempty"` + Models []interface{} `json:"models,omitempty"` + ResultCode string `json:"resultCode"` + Success bool `json:"success"` + Message string `json:"message"` + TotalRecord int `json:"totalRecord"` +} + +func (r Result) JavaClassName() string { + return "com.twodfire.share.result.ResultSupport" +} +``` + +这里的 _JavaClassName_ 接口的意义就如函数签名一样,返回的就是 _java_ 的类名。 + +#### 接口 + +要想调用 _dubbo_ 的接口就必须实现下面这个 _interface_ + +```go +// rpc service interface +type RPCService interface { + Reference() string // rpc service id or reference id +} +``` + +所以我需要构造一个 _struct_ 来做这个事情,比如: + +```go +type ITokenService struct { + Validate func(ctx context.Context, req []interface{}, resp *Result) error `dubbo:"validate"` +} + +func (i *ITokenService) Reference() string { + return "ITokenService" +} +``` + +这个结构体一般是不会有什么数据成员。 + +这里我们注意到 _Validate_ 函数声明后面跟的 _dubbo tag_ ,这个是为如果 _rpc_ 名称的首字母是小写(比如我要调用的 _dubbo_ 接口就是 _validate_ )准备的 _MethodMapper_ ,类似于 _json_ 的映射 _tag_ 功效。一开始我就是遇到这个坑,我按官方的例子实现,日志一直说找不到接口,后来我也在官方群里询问大佬才知道有这个功能。 + +#### 注册 + +好了,上面的准备全部完成后,我们要做最后一步,那就是告诉 _dubbo-go_ 我们想要的是什么。代码如下: + +```go + import ( + hessian "github.com/apache/dubbo-go-hessian2" + "github.com/apache/dubbo-go/config" + ) + + var tokenProvider = new(ITokenService) + + func init() { + config.SetConsumerService(tokenProvider) + hessian.RegisterPOJO(&Result{}) + } +``` + +#### 调用 + +接下来我们就可以完成我们的 _DubboCli_ 接口了,代码如下: + +```go +func (d *DubboCli) CheckUser(token, app string) (bool, error) { + args := []interface{}{token, app} + resp := &Result{} + + if err := tokenProvider.Validate(context.Background(), args, resp); err != nil { + return false, err + } + if resp.Success { + return resp.Success, nil + } + return resp.Success, errors.New(resp.Message) +} +``` + +好了,至此我们就完成了 _dubbo-go_ 的全部接入工作。 Happy Coding... + +## 写在最后 + +其实代码格式这个问题,我在接入的时候跟官方群里的维护者大佬提过,使用 _go_ 官方的代码格式工具 [goimports](https://github.com/golang/tools/tree/master/cmd/goimports) 来统一代码格式,这 样对于维护者以外的人提 _PR_ 也是有利。我在接入的过程中遇到一个 _bug_ ,我反馈给雨神,他就让我提了个 _PR_ ,在整个过程就是这个 代码格式的问题,导致我反复的修改代码。 \ No newline at end of file diff --git a/doc/md/practice/dubbo-go-quick-start.md b/doc/md/practice/dubbo-go-quick-start.md new file mode 100644 index 0000000000..69482f7d05 --- /dev/null +++ b/doc/md/practice/dubbo-go-quick-start.md @@ -0,0 +1,197 @@ +# [快速开始](https://dubbogo.github.io/dubbo-go-website/zh-cn/docs/user/quick-start.html) + +通过一个 `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](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](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. 参考 [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" +``` + +## 接着是客户端 + +### 第一步:编写客户端 `Provider` + +> [https://github.com/dubbogo/dubbo-samples/blob/master/golang/helloworld/dubbo/go-client/app/user.go](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](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. 参考 [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" +``` \ No newline at end of file diff --git a/doc/md/registry-center/dubbo-go-registry-center--nacos.md b/doc/md/registry-center/dubbo-go-registry-center--nacos.md new file mode 100644 index 0000000000..5ee3f77ccc --- /dev/null +++ b/doc/md/registry-center/dubbo-go-registry-center--nacos.md @@ -0,0 +1,116 @@ +# [解构 Dubbo-go 的核心注册引擎 Nacos](https://my.oschina.net/dubbogo/blog/4608576) + +近几年,随着Go语言社区逐渐发展和壮大,越来越多的公司开始尝试采用Go搭建微服务体系,也涌现了一批Go的微服务框架,如go-micro、go-kit、Dubbo-go等,跟微服务治理相关的组件也逐渐开始在Go生态发力,如Sentinel、Hystrix等都推出了Go语言版本,而作为微服务框架的核心引擎--注册中心,也是必不可缺少的组件,市面已经有多款注册中心支持Go语言,应该如何选择呢?我们可以对目前主流的支持Go语言的注册中心做个对比。 + +![](../../pic/registry-center/dubbo-go-registry-center--nacos-a.png) + +根据上表的对比我们可以从以下几个维度得出结论: + +* 生态:各注册中心对Go语言都有支持,但是Nacos、 Consul、Etcd 社区活跃,zookeeper和Eureka社区活跃度较低; +* 易用性:Nacos、Eureka、Consul都有现成的管控平台,Etcd、zookeeper本身作为kv存储,没有相应的管控平台,Nacos支持中文界面,比较符合国人使用习惯; +* 场景支持:CP模型主要针对强一致场景,如金融类,AP模型适用于高可用场景,Nacos可以同时满足两种场景,Eureka主要满足高可用场景,Consul、Zookeepr、Etcd主要满足强一致场景,此外Nacos支持从其它注册中心同步数据,方便用户注册中心迁移; +* 功能完整性:所有注册中心都支持健康检查,Nacos、Consul支持的检查方式较多,满足不同应用场景,Zookeeper通过keep alive方式,能实时感知实例变化;Nacos、Consul和Eureka都支持负载均衡策略,Nacos通过Metadata selector支持更灵活的策略;此外,Nacos、Eureka都支持雪崩保护,避免因为过多的实例不健康对健康的实例造成雪崩效应。 + +综合上面各维度的对比,可以了解到Nacos作为注册中心有一定的优势,那么它对Go微服务生态的集成做得如何?接下来我们首先探索下Nacos是如何与Dubbo-go集成。 + +# 引言 + +Dubbo-go目前是Dubbo多语言生态中最火热的一个项目,从2016年发布至今,已经走过5个年头。最近,Dubbo-go发布了v1.5版本,全面兼容Dubbo 2.7.x版本,支持了应用维度的服务注册与发现,和主流的注册模型保持一致,标志着Dubbo-go向云原生迈出了关键的一步。作为驱动服务运转的核心引擎--注册中心,在切换到应用维度的注册模型后,也需要做相应的适配,本文将解析如何以Nacos为核心引擎实现应用维度的服务注册与发现,并且给出相应的实践案例。此外,本文代码基于Dubbo-go v1.5.1,Nacos-SDK-go v1.0.0和Nacos v1.3.2。 + +# 服务注册与发现架构 + +从架构中,我们可以看到,与接口级别的服务注册发现不同的是,Dubbo-go的provider启动后会调用Nacos-go-sdk的RegisterInstance接口向Nacos注册服务实例,注册的服务名即为应用名称,而不是接口名称。Conusmer启动后则会调用Subscribe接口订阅该应用的服务实例变化,并对的实例发起服务调用。 + +![](../../pic/registry-center/dubbo-go-registry-center--nacos-b.png) + +# 服务模型 + +图3是我们Dubbo-go的应用维度服务发现模型,主要有服务和实例两个层级关系,服务实例的属性主要包含实例Id、主机地址、服务端口、激活状态和元数据。图4为Nacos的服务分级存储模型,包含服务、集群和实例三个层次。两者对比,多了一个集群维度的层级,而且实例属性信息能够完全匹配。所以在Dubbo-go将应用服务实例注册到Nacos时,我们只需要将集群设置为默认集群,再填充服务和实例的相关属性,即可完成服务模型上的匹配。此外Nacos可以将服务注册到不同的Namespace下,实现多租户的隔离。 ![](../../pic/registry-center/dubbo-go-registry-center--nacos-j.png) + +![](../../pic/registry-center/dubbo-go-registry-center--nacos-c.png) + +# 服务实例心跳维持 + +Dubbo-go的Provider在向Nacos注册应用服务实例信息后,需要主动上报心跳,让Nacos服务端感知实例的存活与否,以判断是否将该节点从实例列表中移除。维护心跳的工作是在Nacos-SDK-go完成的,从图5代码中可以看到,当Dubbo-go调用RegisterInstance注册一个服务实例时,SDK除了调用Nacos的Register API之外,还会调用AddBeatInfo,将服务实例信息添加到本地缓存,通过后台协程定期向Nacos发送服务实例信息,保持心跳。当服务下线时,可以通过调用DeRegisterInstance执行反注册,并移除本地的心跳保持任务,Nacos实例列表中也会将该实例移除。 + +![](../../pic/registry-center/dubbo-go-registry-center--nacos-d.png) + +# 订阅服务实例变化 + +Dubbo-go的Consumer在启动的时候会调用Nacos-SDK-go的Subscribe接口,该接口入参如图6,订阅的时候只需要传递ServiceName即应用名和回调函数SubscribeCallback,Nacos在服务实例发生变化的时候即可通过回调函数通知Dubbo-go。Nacos-SDK-go是如何感知Nacos的服务实例变化的呢?主要有两种方式: + +* Nacos服务端主动推送,Nacos-SDK-go在启动的时候会监听一个UDP端口,该端口在调用Nacos Register API的时候作为参数传递,Nacos会记录Ip和端口,当服务实例发生变化时,Nacos会对所有监听该服务的Ip和端口发送UDP请求,推送变化后的服务实例信息。 + +* Nacos-SDK-go定期查询,SDK会对订阅的服务实例定时调用查询接口,如果查询有变化则通过回调接口通知Dubbo-go。作为兜底策略保证Nacos服务端推送失败后,仍能感知到变化。 + + ![](../../pic/registry-center/dubbo-go-registry-center--nacos-e.png) + + +此外Nacos-SDK-go还支持推空保护,当Nacos推送的实例列表为空时,不更新本地缓存,也不通知Dubbo-go变更,避免Consumer无可用实例调用,造成故障。同时,SDK还支持服务实例信息本地持久化存储,可以保证在Nacos服务故障过程中,Consumer重启也能获取到可用实例,具备容灾效果。 + +# 范例实践 + +## 环境准备 + +dubbo-go samples代码下载:[https://github.com/apache/dubbo-samples/tree/master/golang,基于Nacos注册中心的应用级服务发现的hello](https://github.com/apache/dubbo-samples/tree/master/golang%EF%BC%8C%E5%9F%BA%E4%BA%8ENacos%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83%E7%9A%84%E5%BA%94%E7%94%A8%E7%BA%A7%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0%E7%9A%84hello) world代码目录在 registry/servicediscovery/nacos。 + +![](../../pic/registry-center/dubbo-go-registry-center--nacos-f.png) + +Nacos服务端搭建,参考官方文档:[https://nacos.io/zh-cn/docs/quick-start.html,或者使用官方提供的公共Nacos服务:http://console.nacos.io/nacos(账号密码:nacos,仅供测试),或者购买阿里云服务:https://help.aliyun.com/document\_detail/139460.html?spm=a2c4g.11186623.6.559.d7e264b7bLpZIs](https://nacos.io/zh-cn/docs/quick-start.html%EF%BC%8C%E6%88%96%E8%80%85%E4%BD%BF%E7%94%A8%E5%AE%98%E6%96%B9%E6%8F%90%E4%BE%9B%E7%9A%84%E5%85%AC%E5%85%B1Nacos%E6%9C%8D%E5%8A%A1%EF%BC%9Ahttp://console.nacos.io/nacos(%E8%B4%A6%E5%8F%B7%E5%AF%86%E7%A0%81:nacos%EF%BC%8C%E4%BB%85%E4%BE%9B%E6%B5%8B%E8%AF%95)%EF%BC%8C%E6%88%96%E8%80%85%E8%B4%AD%E4%B9%B0%E9%98%BF%E9%87%8C%E4%BA%91%E6%9C%8D%E5%8A%A1%EF%BC%9Ahttps://help.aliyun.com/document_detail/139460.html?spm=a2c4g.11186623.6.559.d7e264b7bLpZIs) + +## Server端搭建 + +进入registry/servicediscovery/nacos/go-server/profiles文件,可以看到有dev、release和test三个文件夹,分别对应开发、测试和生产配置。我们使用dev配置来搭建开发环境,dev文件下有log.yml和server.yml文件,下面对server.yml配置进行修改。 + +remote配置,这里使用公共的Nacos服务,address支持配置多个地址,用逗号分割。params参数配置nacos-sdk的日志目录。 + +```Yaml +remote: + nacos: + address: "console.nacos.io:80" + timeout: "5s" + params: + logDir: "/data/nacos-sdk/log" +configCenter配置 +config_center: + protocol: "nacos" + address: "console.nacos.io:80" +``` + +配置server端环境变量 + +```Bash +export CONF_PROVIDER_FILE_PATH=server端的server.yml文件路径 +export APP_LOG_CONF_FILE=server端的log.yml文件路径 +``` + +进入registry/servicediscovery/nacos/go-server/app,运行server.go的main方法,可以从Nacos的控制台([http://console.nacos.io/nacos/#/serviceManagement?dataId=&group=&appName=&namespace=)](http://console.nacos.io/nacos/#/serviceManagement?dataId=&group=&appName=&namespace=%EF%BC%89) + +看到,应用user-info-server已经注册成功。 + +![](../../pic/registry-center/dubbo-go-registry-center--nacos-g.png) + +![](../../pic/registry-center/dubbo-go-registry-center--nacos-h.png) + +## Client端搭建 + +client的配置文件在registry/servicediscovery/nacos/go-server/profiles目录下,需要修改的地方跟server端一样,这里不赘述。 + +配置client端环境变量 + +```Bash +export CONF_CONSUMER_FILE_PATH=client端的server.yml文件路径 +export APP_LOG_CONF_FILE=client端的log.yml文件路径 +``` + +进入registry/servicediscovery/nacos/go-client/app,运行client.go的main方法,看到如下日志输出,表示调用server端成功。 + +![](../../pic/registry-center/dubbo-go-registry-center--nacos-i.png) + +作者:李志鹏 + +Github账号:Lzp0412,Nacos-SDK-go作者,Apache/Dubbo-go Contributor。现就职于阿里云云原生应用平台,主要参与服务发现、CoreDNS、ServiceMesh相关工作,负责推动Nacos Go微服务生态建设。 + +相关链接 Nacos-SDK-go项目地址:[https://github.com/nacos-group/nacos-sdk-go](https://github.com/nacos-group/nacos-sdk-go) Nacos golang生态交流群:23191211 Nacos项目地址:[https://nacos.io/](https://nacos.io/) Nacos社区交流群:30438813 + +Dubbo-go 项目地址:[https://github.com/apache/dubbo-go](https://github.com/apache/dubbo-go) Dubbo-go社区交流群:23331795 \ No newline at end of file diff --git a/doc/md/routing-rule/how-to-implement-routing-rule-in-dubbo-go.md b/doc/md/routing-rule/how-to-implement-routing-rule-in-dubbo-go.md new file mode 100644 index 0000000000..14da444755 --- /dev/null +++ b/doc/md/routing-rule/how-to-implement-routing-rule-in-dubbo-go.md @@ -0,0 +1,215 @@ +# [dubbo-go 中如何实现路由规则功能](https://zouyx.github.io/posts/2020/03/30/dubbo-go%20%E4%B8%AD%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E8%B7%AF%E7%94%B1%E8%A7%84%E5%88%99%E5%8A%9F%E8%83%BD.html) + +dubbo-go 中如何实现路由规则功能 + +# Let‘s Go! + +* * * + +最近在 Apache/dubbo-go(以下简称 dubbo-go )社区中,路由规则突然成了呼声最高的功能之一。那到底为什么需要路由规则? + +先路由规则需要实现的功能: + +路由规则( routing rule )是为了改变网络流量所经过的途径而修改路由信息的技术,主要通过改变路由属性(包括可达性)来实现。在发起一次 RPC 调用前起到过滤目标服务器地址的作用,过滤后的地址列表,将作为消费端最终发起 RPC 调用的备选地址。 + +试想该下场景:使用 dubbo-go 在生产环境上,排除预发布机。使用路由规则实现不是很合适吗? + +虽然知道了路由规则需要实现什么功能,但还不足以实现一个完整的路由规则功能。除此之外,还需要知道如何方便的管理路由规则。 + +# 目标 + +综上所述,可以总结出以下 **目标** + +* 支持方便扩展路由规则的配置; +* 可以方便的管理路由规则配置,如支持本地与远程配置中心管理; +* 与 Dubbo 现有的配置中心内的路由规则配置文件兼容,降低在新增语言栈的学习及使用成本; + +# 路由规则设计 + +在设计之初,首先要考虑的是路由规则应该放在整个服务治理周期的哪个阶段呢? + +有些读者可能会有点困惑,我连架构图都不知道,如何考虑在哪个阶段?不怕,下图马上给你解惑。 + +![dubbo-go-arch.png](../../pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-a.png) + +可以看到图中的 Router 就是路由规则插入的位置,目前路由规则主要用于控制 Consumer 到 Provider 之间的网络流量的路由路径。 + +除此之外,还有几个问题是需要优先考虑: + +1.需要什么功能? + +* 通过配置信息生成路由规则,包括:读取并解析本地配置文件,读取并解析配置中心的配置。以责任链模式串联起来。 +* 通过路由规则,匹配本地信息与远端服务信息,过滤出可以调用的远端节点,再进行负载均衡。 + +2.如何设计接口? + +通过第一点,我们能设计出以下接口来实现所需的功能。 + +* 路由规则接口:用于路由规则过滤出可以调用的远端节点。 + +* 路由规则责任链接口:允许执行多个路由规则。 + +* 配置信息生成路由规则接口:解析内部配置信息(common.URL)生成对应的路由规则。 + +* 配置文件生成路由规则接口:解析配置文件生成对应的路由规则。 + + +3.如何实现本地与远程路由规则配置加载? + +* 本地路由规则配置:在原配置加载阶段,新增读取路由配置文件。使用 `FIleRouterFactory` 解析后,生成对应路由规则,放置到内存中备用。 +* 远程路由规则配置:在 zookeeper 注册并监听静态资源目录后。读取静态资源,筛选符合路由规则配置信息,通过 `RouterFactory` 生成对应路由规则,放置到内存中备用。 + +## Router + +匹配及过滤远程实例的路由规则。 ![router.png](../../pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-b.png) 目前已有实现类包括: + +* listenableRouter: +* AppRouter: +* ConditionRouter: +* HealthCheckRouter: +* FileConditionRouter: + +## RouterChain + +执行多个路由规则的责任链。 ![router-chain.png](../../pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-c.png) + +## FIleRouterFactory + +生成解析配置文件生成路由规则的工厂类。 ![file-router-factory.png](../../pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-d.png) + +## RouterFactory + +通过配置信息生成路由规则的工厂类。 ![router-factory.png](../../pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-e.png) + +# 实现 + +![router-design.png](../../pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-f.png) + +实现路由规则以兼容 dubbo 为首要目标,降低使用者的学习成本为辅助目标。与配置中心模块相结合,实现路由规则远程统一管理与下发。 + +## 规则类型 + +下面先来介绍一下有哪些具体的路由规则实现。 + +### **条件路由** + +dubbo-go 中第一个支持的路由规则,允许用户通过配置文件及配置中心管理路由规则。 + +与之相似的一个概念是 dubbo-go 里面的 group 概念,但是条件路由提供了更加细粒度的控制手段和更加丰富的表达语义。比较典型的使用场景是黑白名单设置,灰度以及测试等。 + +### **健康检查路由** + +在 RPC 调用中,我们希望尽可能地将请求命中到那些处理能力快、处于健康状态的实例,该路由的功能就是通过某种策略断定某个实例不健康,并将其排除在候选调用列表,优先调用那些健康的实例。这里的”健康”可以是我们自己定义的状态,默认实现即当错误比例到达某一个阈值时或者请求活跃数大于上限则认为其不健康,允许用户扩展健康检测策略。 + +在我们服务治理里面,核心的问题其实就在于如何判断一个实例是否可用。无论是负载均衡、 + +熔断还是限流,都是对这个问题的解答。所以,这个 feature 是一个很好的尝试。因为我们接下来计划提供的特性,基于规则的限流以及动态限流,都是要解决 “如何断定一个实例是否可用” 这么一个问题。 + +所以欢迎大家使用这个特性,并向社区反馈各自设定的健康指标。这对我们接下来的工作会有很大的帮助。 + +### **标签路由** + +以 Provider 为维度,通过将某一个或多个服务的提供者划分到同一个分组,约束流量只在指定分组中流转,从而实现流量隔离的目的,可以作为蓝绿发布、灰度发布等场景的能力基础。 + +* 静态打标:根据配置文件所配置的标签,固定给 Provider 设置标签。 +* 动态打标:基于健康检查路由,根据服务不同时刻,不同状态,动态在 Provider 设置适合的标签。 + +## 分析 + +接着,以条件路由在 zookeeper 实现为例,对服务提供者与服务消费者进行整体流程分析。 + +### 如何配置条件路由规则 + +配置条件路由规则既可以通过本地配置文件也能通过远程配置中心进行配置,配置生效流程都是:配置文件 => dubbo 内部协议 => 缓存至应用级内存 => 过滤出可调用节点。 + +**dubbo-admin** 【服务治理/条件路由】增加路由规则配置,zookeeper 中会自动生成其对应配置节点,内容均为 **dubbo-admin** 中设置的配置。 + +**_全局配置_** + +对应应用级全局路由规则配置。 + +```plain +/dubbo/config/dubbo/user-info-server(应用名).condition-router +``` + +应用名:只对 user-info-server 应用生效 .condition-router: 路由类型。除此之外,还有 .tag-router 表示标签路由。 + +**_服务配置_** + +对应服务级所有路由规则配置。 + +```plain +/dubbo/ com.ikurento.user.UserProvider(服务名) /routers +``` + +服务名:只对 com.ikurento.user.UserProvider 服务生效。 + +## 实现 Router + +![router-route.png](../../pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-g.png) + +以下为必须实现的方法,以下方法用于获取过滤服务端节点配置。 + +* Route: 根据配置,调用节点与被调用节点,过滤出可调用节点。 +* Priority: 路由规则优先级,需要是个正整数。 +* URL: 通过路由规则转换出来的 dubbo 内部协议。 + +更多实现参考: + +路由规则:[https://github.com/apache/dubbo-go/tree/master/cluster/router/condition](https://github.com/apache/dubbo-go/tree/master/cluster/router/condition) + +其中包含监听配置中心实现:[https://github.com/apache/dubbo-go/blob/master/cluster/router/condition/listenable\_router.go](https://github.com/apache/dubbo-go/blob/master/cluster/router/condition/listenable_router.go) + +# 使用方法 + +经过上面设计与实现的分析,大概也能猜测到如何使用: + +![router-import.png](../../pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-h.png) + +如图所示,使用路由规则并不复杂,只需要把对应的依赖引入进来。在包初始化的时候,会创建出来对应的路由规则的实现。比如说加载条件路由、健康检测路由或者标签作为路由规则: + +## 本地路由规则配置 + +```plain +_ "github.com/apache/dubbo-go/cluster/router/condition" +``` + +仅仅引用依赖包还不直接使用,还需要配置指定的配置文件: **_router\_config.yml_** ,内容如下: + +```plain +# dubbo router yaml configure file +priority: 1 +force: true +conditions : ["host = 1.1.1.1 => host = 192.168.199.214"] +``` + +更多配置方式:[条件路由配置](http://dubbo.apache.org/zh-cn/docs/user/demos/routing-rule.html) + +## 配置中心配置 + +```plain +_ "github.com/apache/dubbo-go/config_center/zookeeper" +``` + +目前仅支持 zookeeper 配置中心,与 dubbo-admin 结合即可使用。配置方式如下: + +![dubbo-admin-1.png](../../pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-i.png) + +![dubbo-admin-2.png](../../pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-j.png) + +# 总结 + +更加具体的实现,我就不详细论述,大家可以去看源码,欢迎大家持续关注,或者贡献代码。 + +整个路由规则功能,已经能跟上 dubbo 2.7.x 版本,已经支持本地及远端路由规则配置管理。从扩展性来说,是比较便利。目前已经支持条件路由、标签路由与健康检测路由,虽然能满足基本使用场景,距离完善还有还长远的路。 + +未来计划: + +1. 更多的配置中心支持,理论上已经支持,但还没测试。 +2. service-router(未支持) +3. 标签路由-配置中心(未支持) +4. 目前路由与配置中心结合的代码,对新增路由规则并不友好,有一定接入成本。 + +欢迎大家关注或者贡献代码,[https://github.com/apache/dubbo-go](https://github.com/apache/dubbo-go) + +Written on March 31, 2020 \ No newline at end of file diff --git a/doc/md/rpc/dubb-go-adaptive-grpc.md b/doc/md/rpc/dubb-go-adaptive-grpc.md new file mode 100644 index 0000000000..2105d23ef6 --- /dev/null +++ b/doc/md/rpc/dubb-go-adaptive-grpc.md @@ -0,0 +1,129 @@ +# [无缝衔接 gRPC 与 dubbo-go](https://developer.aliyun.com/article/742946) + +[中间件小哥](https://developer.aliyun.com/profile/g6g63f3lanvck) 2020-01-19 1530浏览量 + +**简介:** + +最近我们 dubbo-go 社区里面,呼声很大的一个 feature 就是对 gRPC 的支持。在某位大佬的不懈努力之下,终于弄出来了。 + +今天我就给大家分析一下大佬是怎么连接 dubbo-go 和 gRPC 。 + +## gRPC + +先来简单介绍一下 gRPC 。它是 Google 推出来的一个 RPC 框架。gRPC是通过 IDL ( Interface Definition Language )——接口定义语言——编译成不同语言的客户端来实现的。可以说是RPC理论的一个非常非常标准的实现。 + +因而 gRPC 天然就支持多语言。这几年,它几乎成为了跨语言 RPC 框架的标准实现方式了,很多优秀的rpc框架,如 Spring Cloud 和 dubbo ,都支持 gRPC 。 + +server 端 + +在 Go 里面,server 端的用法是: +![1](../../pic/rpc/dubb-go-adaptive-grpc-a.png "1") + +它的关键部分是:`s := grpc.NewServer()`和`pb.RegisterGreeterServer(s, &server{})`两个步骤。第一个步骤很容易,唯独第二个步骤`RegisterGreeterServer`有点麻烦。为什么呢? + +因为`pb.RegisterGreeterServer(s, &server{})`这个方法是通过用户定义的`protobuf`编译出来的。 + +好在,这个编译出来的方法,本质上是: + +![2](../../pic/rpc/dubb-go-adaptive-grpc-b.png "2") + +也就是说,如果我们在 dubbo-go 里面拿到这个 \_Greeter\_serviceDesc ,就可以实现这个 server 的注册。因此,可以看到,在 dubbo-go 里面,要解决的一个关键问题就是如何拿到这个 serviceDesc 。 + +## Client 端 + +Client 端的用法是: +![3](../../pic/rpc/dubb-go-adaptive-grpc-c.png "3") + +这个东西要复杂一点: +1、创建连接:conn, err := grpc.Dial(address) +2、创建client:c := pb.NewGreeterClient(conn) +3、调用方法:r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name}) + +第一个问题其实挺好解决的,毕竟我们可以从用户的配置里面读出 address ; + +第二个问题就是最难的地方了。如同 RegisterGreeterServer 是被编译出来的那样,这个 NewGreeterClient 也是被编译出来的。 + +而第三个问题,乍一看是用反射就能解决,但是我们打开 SayHello 就能看到: +![4](../../pic/rpc/dubb-go-adaptive-grpc-d.png "4") + +结合 greetClient 的定义,很容易看到,我们的关键就在于 err := c.cc.Invoke ( ctx, "/helloworld.Greeter/SayHello", in, out, opts... )。换言之,我们只需要创建出来连接,并且拿到方法、参数就能通过类似的调用来模拟出 c.SayHello 。 + +通过对 gRPC 的简单分析,我们大概知道要怎么弄了。还剩下一个问题,就是我们的解决方案怎么和 dubbo-go 结合起来呢? + +## 设计 + +我们先来看一下 dubbo-go 的整体设计,思考一下,如果我们要做 gRPC 的适配,应该是在哪个层次上做适配。 +![5](../../pic/rpc/dubb-go-adaptive-grpc-e.png "5") + +我们根据前面介绍的 gRPC 的相关特性可以看出来,gRPC 已经解决了 codec 和 transport 两层的问题。 + +而从 cluster 往上,显然 gRPC 没有涉及。于是,从这个图里面我们就可以看出来,要做这种适配,那么 protocol 这一层是最合适的。即,我们可以如同 dubbo protocol 那般,扩展出来一个 grpc protocol 。 + +这个 gRPC protocol 大体上相当于一个适配器,将底层的 gRPC 的实现和我们自身的 dubbo-go 连接在一起。 + +![6](../../pic/rpc/dubb-go-adaptive-grpc-f.png "6") + +## 实现 + +在 dubbo-go 里面,和 gRPC 相关的主要是: + +![7](../../pic/rpc/dubb-go-adaptive-grpc-g.png "7") + +我们直接进去看看在 gRPC 小节里面提到的要点是如何实现的。 + +### server端 + +![8](../../pic/rpc/dubb-go-adaptive-grpc-h.png "8") + +这样看起来,还是很清晰的。如同 dubbo- go 其它的 protocol 一样,先拿到 service ,而后通过 service 来拿到 serviceDesc ,完成服务的注册。 + +注意一下上图我红线标准的 ds, ok := service.(DubboGrpcService) 这一句。 + +为什么我说这个地方有点奇怪呢?是因为理论上来说,我们这里注册的这个 service 实际上就是 protobuf 编译之后生成的 gRPC 服务端的那个 service ——很显然,单纯的编译一个 protobuf 接口,它肯定不会实现 DubboGrpcService 接口: + +![9](../../pic/rpc/dubb-go-adaptive-grpc-n.png "9") + +那么 ds, ok := service.(DubboGrpcService) 这一句,究竟怎么才能让它能够执行成功呢? + +我会在后面给大家揭晓这个谜底。 + +## Client端 + +dubbo-go 设计了自身的 Client ,作为对 gRPC 里面 Client 的一种模拟与封装: +![10](../../pic/rpc/dubb-go-adaptive-grpc-i.png "10") + +注意看,这个 Client 的定义与前面 greetClient 的定义及其相似。再看下面的 NewClient 方法,里面也无非就是创建了连接 conn ,而后利用 conn 里创建了一个 Client 实例。 + +注意的是,这里面维护的 invoker 实际上是一个 stub 。 + +当真正发起调用的时候: + +![11](../../pic/rpc/dubb-go-adaptive-grpc-j.png "11") + +红色框框框住的就是关键步骤。利用反射从 invoker ——也就是 stub ——里面拿到调用的方法,而后通过反射调用。 + +### 代码生成 + +前面提到过 ds, ok := service.(DubboGrpcService) 这一句,面临的问题是如何让 protobuf 编译生成的代码能够实现 DubboGrpcService 接口呢? + +有些小伙伴可能也注意到,在我贴出来的一些代码里面,反射操作会根据名字来获取method实例,比如NewClient方法里面的method := reflect.ValueOf(impl).MethodByName("GetDubboStub")这一句。这一句的impl,即指服务的实现,也是 protobuf 里面编译出来的,怎么让 protobuf 编译出来的代码里面含有这个 GetDubboStub 方法呢? + +到这里,答案已经呼之欲出了:修改 protobuf 编译生成代码的逻辑! + +庆幸的是,在 protobuf 里面允许我们通过插件的形式扩展我们自己的代码生成的逻辑。 + +所以我们只需要注册一个我们自己的插件: +![12](../../pic/rpc/dubb-go-adaptive-grpc-k.png "12") + +然后这个插件会把我们所需要的代码给嵌入进去。比如说嵌入`GetDubboStub`方法: +![13](../../pic/rpc/dubb-go-adaptive-grpc-l.png "13") + +还有`DubboGrpcService`接口: + +![14](../../pic/rpc/dubb-go-adaptive-grpc-m.png "14") + +这个东西,属于难者不会会者不难。就是如果你不知道可以通过`plugin`的形式来修改生成的代码,那就是真难;但是如果知道了,这个东西就很简单了——无非就是水磨工夫罢了。 + +**作者信息:**邓明,毕业于南京大学,就职于 eBay Payment 部门,负责退款业务开发。 + +编解码 自然语言处理 Dubbo Java 应用服务中间件 Go Spring \ No newline at end of file diff --git a/doc/md/service-governance/dubbo-go-metrics-design.md b/doc/md/service-governance/dubbo-go-metrics-design.md new file mode 100644 index 0000000000..d2234440c9 --- /dev/null +++ b/doc/md/service-governance/dubbo-go-metrics-design.md @@ -0,0 +1,111 @@ +# [eBay 邓明:dubbo-go 中 metrics 的设计](https://mp.weixin.qq.com/s/_ibXd2z1RqjOJwk7jMAwig) + +发布于:2020 年 4 月 22 日 17:15 + +最近因为要在 Apache/dubbo-go(以下简称 dubbo-go )里面实现类似的这个 metrics 功能,于是花了很多时间去了解现在 Dubbo 里面的 metrics 是怎么实现的。该部分,实际上是被放在一个独立的项目里面,即 metrics 。 + +总体上来说,Dubbo 的 metrics 是一个从设计到实现都非常优秀的模块,理论上来说,大部分的 Java 项目是可以直接使用 metrics 的。但也因为兼顾性能、扩展性等各种非功能特性,所以初看代码会有种无从下手的感觉。 + +今天这篇文章将会从比较大的概念和抽象上讨论一下 dubbo-go 中的 metrics 模块的设计——实际上也就是 Dubbo 中的 metrics 的设计。因为我仅仅是将 Dubbo 里面的相关内容在 dubbo-go 中复制一份。 + +目前 dubbo-go 的 metrics 刚刚开始起步,第一个 PR 是: + +## 总体设计 + +**Metric** + +要想理解 metrics 的设计,首先要理解,我们需要收集一些什么数据。我们可以轻易列举出来在 RPC 领域里面我们所关心的各种指标,诸如每个服务的调用次数,响应时间;如果更加细致一点,还有各种响应时间的分布,平均响应时间,999 线…… + +但是上面列举的是从数据的内容上划分的。 metrics 在抽象上,则是摒弃了这种划分方式,而是结合了数据的特性和表现形式综合划分的。 + +从源码里面很容易找到这种划分的抽象。 + +metrics 设计了 Metric 接口作为所有数据的顶级抽象: + +在 Dubbo 里面,其比较关键的子接口是: + +为了大家理解,这里我抄一下这些接口的用途: + +* Gauge: 一种实时数据的度量,反映的是瞬态的数据,不具有累加性,例如当前 JVM 的线程数; +* Counter: 计数器型指标,适用于记录调用总量等类型的数据; +* Histogram : 直方分布指标,例如,可以用于统计某个接口的响应时间,可以展示 50%, 70%, 90% 的请求响应时间落在哪个区间内; +* Meter: 一种用于度量一段时间内吞吐率的计量器。例如,一分钟内,五分钟内,十五分钟内的 qps 指标; +* Timer: Timer 相当于 Meter+Histogram 的组合,同时统计一段代码,一个方法的 qps,以及执行时间的分布情况; + +目前 dubbo-go 只实现了 FastCompass ,它也是 Metric 的子类: + +这个接口功能很简单,就是用于收集一段时间之内的 subCategory 执行的次数和响应时间。 subCategory 是一个比较宽泛的概念,无论是在 Dubbo 还是在 dubbo-go 里面,一个典型的 subCategory 就会是某个服务。 + +这里的设计要点在于,它是从什么角度上去做这些数据的抽象的。 + +很多人在开发这种采集数据的相关系统或者功能的时候,最容易陷入的就是从数据内容上做抽象,例如抽象一个接口,里面的方法就是获得服务的调用次数或者平均响应时间等。 + +这种抽象并非不可以,尤其是在简单系统里面,还非常好用。唯独在通用性和扩展性上要差很多。 + +**MetricManager** + +在我们定义了 Metric 之后,很容易就想到,我要有一个东西来管理这些 Metric 。这就是 MetricManager ——对应到 Dubbo 里面的 IMetricManager 接口。 + +MetricManager 接口目前在 dubbo-go 里面还很简单: + +本质上来说,我在前面提到的那些 Metric 的子类,都可以从这个 MetricManager 里面拿到。它是对外的唯一入口。 + +因此无论是上报采集的数据,还是某些功能要用这些采集的数据,最重要的就是获得一个 MetricManager 的实例。例如我们最近正在开发的接入 Prometheus 就是拿到这个 MetriManger 实例,而后从里面拿到 FastCompass 的实例,而后采集这些数据: + +**MetricRegistry** + +MetricRegistry 是一个对 Metric 集合的抽象。 MetricManager 的默认实现里面,就是使用 MetricRegistry 来管理 Metric 的: + +所以,本质上它就是提供了一些注册 Metric 然后再从里面捞出来的方法。 + +于是,这就有一个问题了:为什么我在有了 MetricManager 之后,还有有一个 MetricRegistry?似乎这两个功能有些重叠? + +答案大概是两个方面: +1、除了管理所有的 Metric 之外,还承担着额外的功能,这些功能典型的就是 IsEnabled 。而实际上,在未来我们会赋予它管理生命周期的责任,比如说在 Dubbo 里面,该接口就还有一个 clear 方法; +2、 metrics 里面还有一个 group 的概念,而这只能由 MetricManager 来进行管理,至少交给 MetricRegistry 是不合适的。 + +metrics 的 group 说起来也很简单。比如在 Dubbo 框架里面采集的数据,都会归属于 Dubbo 这个 group 。也就是说,如果我想将非框架层面采集的数据——比如纯粹的业务数据——分隔出来,就可以借用一个 business group 。又或者我采集到的机器自身的数据,可以将其归类到 system 这个 group 下。 + +所以 MetricManger 和 MetricRegistry 的关系是: + +Clock 抽象是一个初看没什么用,再看会觉得其抽象的很好。Clock 里面就两个方法: + +一个是获得时间戳,另外一个则是获得时间周期 (Tick)。比如通常采集数据可能是每一分钟采集一次,所以你得知道现在处在哪个时间周期里面。Clock 就提供了这种抽象。 + +很多人在实现自己的这种 metrics 的框架的时候,大多数都是直接使用系统的时钟,也就是系统的时间戳。于是所有的 Metic 在采集数据或者上报数据的时候,不得不自己去处理这种时钟方面的问题。 + +这样不同的 Metric 之间就很难做到时钟的同步。比如说可能在某个 Metric1 里面,采集周期是当前这一分钟,而 Metric2 是当前这一分钟的第三十秒到下一分钟的第三十秒。虽然它们都是一分钟采集一次,但是这个周期就对不上了。 + +另外一个有意思的地方在于,Clock 提供的这种抽象,允许我们不必真的按照现实时间的时间戳来处理。比如说,可以考虑按照 CPU 的运行时间来设计 Clock 的实现。 + +## 例子 + +就用这一次 PR 的内容来展示一下这个设计。 + +在 dubbo-go 里面这次实现了 metricsFilter ,它主要就是收集调用次数和响应时间,其核心是: + +report 其实就是把 metrics reports 给 MetricManager : + +所以,这里面可以看出来,如果我们要收集什么数据,也是要先获得 MetricManager 的实例。 + +FastCompass 的实现里面会将这一次调用的服务及其响应时间保存下来。而后在需要的时候再取出来。 + +所谓的需要的时候,通常就是上报给监控系统的时候。比如前面的提到的上报给 Prometheus。 + +所以这个流程可以抽象表达为: + +这是一个更加宽泛的抽象。也就是意味着,我们除了可以从这个 metricFilter 里面收集数据,也可以从自身的业务里面去收集数据。比如说统计某段代码的执行时间,一样可以使用 FastCompass 。 + +而除了 Prometheus ,如果用户自己的公司里面有监控框架,那么他们可以自己实现自己的上报逻辑。而上报的数据则只需要拿到 MetricManager 实例就能拿到。 + +## 总结 + +本质上来说,整个 metrics 可以看做是一个巨大无比的 provider-conumer 模型。 + +不同的数据会在不同的地方和不同时间点上被采集。有些人在读这些源码的时候会有点困惑,就是这些数据什么时间点会被采集呢? + +它们只会在两类时间点采集: +1、实时采集。如我上面举例的 metricsFilter ,一次调用过来,它的数据就被采集了; +2、另外一个则是如同 Prometheus 。每次 Prometheus 触发了 collect 方法,那么它就会把每种(如 Meter, Gauge )里面的数据收集过来,然后上报,可以称为是定时采集; + +这些具体的实现,我就不一一讨论了,大家有兴趣可以去看看源码。这些数据,也是我们 dubbo-go 后面要陆续实现的东西,欢迎大家持续关注,或者来贡献代码。 \ No newline at end of file diff --git a/doc/md/service-governance/dubbo-go-sentinel.md b/doc/md/service-governance/dubbo-go-sentinel.md new file mode 100644 index 0000000000..ef9358c460 --- /dev/null +++ b/doc/md/service-governance/dubbo-go-sentinel.md @@ -0,0 +1,155 @@ +# [在dubbo-go中使用sentinel](https://dubbogo.github.io/dubbo-go-website/zh-cn/blog/dubbo-go-sentinel.html) + +时至今日,Apache/dubbo-go(以下简称 dubbo-go )项目在功能上已经逐步对齐java版本,稳定性也在不同的生产环境得到了验证。社区便开始再服务治理、监控等方向发力。随着 1.2和1.3 版本发布, dubbo-go 新增了大量此类新feature。 + +今天我们聊一聊限流相关话题,此前dubbo-go已经支持了[tps limit](https://github.com/apache/dubbo-go/pull/237)、[execute limit](https://github.com/apache/dubbo-go/pull/246) 、[hystrix](https://github.com/apache/dubbo-go/pull/133) 的内置filter,用户只要简单配置就能马上用上。但我们知道,在 java 的 dubbo 生态中,有一项限流工具被广泛使用,那就是sentinel。sentinel因为强大的动态规划配置、优秀的dashboard以及对dubbo的良好适配,成为众多使用dubbo的企业选用限流工具的不二之选。 + +就在前些日子,社区非常高兴得知 Sentinel Golang 首个版本 0.1.0 正式发布,这使得 dubbo-go也可以使用 sentinel 作为工具进行一些服务治理、监控的工作了。随着sentinel golang的健壮,我们相信用户马上可以像sentinel管理java dubbo服务那样管理dubbo-go的服务了。 + +完成sentinel golang的dubbo-adapter其实非常简单,这得益于dubbo-go早就完成了filter链的构造,用户可以自定义filter,并且灵活的安排其执行顺序。在1.3发布后,增加了filter中的context传递,构建sentinel/adapter/dubbo更为方便。 + +我们以其中的provider filter适配为例: + +![SentinelProviderFilter](../../pic/service-governance/dubbo-go-sentinel-a.png) + +此 filter 实现了 dubbo-go的filter接口,只要用户在服务启动时将此filter加载到dubbo-go中,即可使用此filter。 ![Sentinel-design](../../pic/service-governance/dubbo-go-sentinel-b.png) +sentinel实现原理与其他限流、熔断库大同小异,底层是用的滑动窗口算法。与hystrix等框架相比不同点是设计理念,Sentinel 的设计理念是让您自由选择控制的角度,并进行灵活组合,从而达到想要的效果。 + +下面我整理了完整的使用流程:(注意:dubbo-go版本请使用1.3.0-rc3及其以上版本) + +在dubbo-go中使用sentinel主要分为以下几步: + +1.初始化sentinel + +2.将sentinel注入dubbo-go的filter + +3.初始化dubbo-go + +4.配置规划 + +## 初始化sentinel + +示例代码: + +```go +import ( + sentinel "github.com/alibaba/sentinel-golang/api" +) + +func initSentinel() { + err := sentinel.InitWithLogDir(confPath, logDir) + if err != nil { + // 初始化 Sentinel 失败 + } +} +``` + +## 将sentinel注入dubbo-go的filter + +你可以通过import包的形式执行,执行其中的init()来注入filter + +```go +import ( + _ "github.com/alibaba/sentinel-golang/adapter/dubbo" +) +``` + +也可以手动执行,给你的filter取上自己想要的名字 + +```go +import ( + "github.com/apache/dubbo-go/common/extension" + sd "github.com/alibaba/sentinel-golang/adapter/dubbo" +) + +func main(){ + extension.SetFilter("myConsumerFilter",sd.GetConsumerFilter()) + extension.SetFilter("myProviderFilter",sd.GetConsumerFilter()) +} +``` + +完成以上步骤,你就可以在需要的dubbo接口配置里写入sentinel的filterName,构建起接口的filter链条。比如以下以consumer.yml配置文件为例 + +```yml +references: + "UserProvider": + registry: "hangzhouzk" + protocol : "dubbo" + interface : "com.ikurento.user.UserProvider" + cluster: "failover" + filter: "myConsumerFilter" + methods : + - name: "GetUser" + retries: 3 +``` + +## 初始化dubbo-go + +到这一步,你只需要正常启动dubbo-go程序就完成了服务启动。用以下代码做一个较为完整举例 + +```go +import ( + hessian "github.com/apache/dubbo-go-hessian2" + sd "github.com/alibaba/sentinel-golang/adapter/dubbo" +) + +import ( + "github.com/apache/dubbo-go/common/logger" + _ "github.com/apache/dubbo-go/common/proxy/proxy_factory" + "github.com/apache/dubbo-go/config" + _ "github.com/apache/dubbo-go/filter/impl" + _ "github.com/apache/dubbo-go/protocol/dubbo" + _ "github.com/apache/dubbo-go/registry/protocol" + + _ "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/common/extension" +) + +func main() { + + hessian.RegisterPOJO(&User{}) + extension.SetFilter("myConsumerFilter",sd.GetConsumerFilter()) + extension.SetFilter("myProviderFilter",sd.GetConsumerFilter()) + config.Load() + + // init finish, do your work + test() +} +``` + +## 规划配置 + +sentinel以强大的规划配置吸引了很多使用者,其提供动态数据源接口进行扩展,用户可以通过动态文件或 etcd 等配置中心来动态地配置规则。但目前sentinel-golang作为破蛋版本,动态配置还在开发中 + +### 动态数据源 + +(开发中)Sentinel 提供动态数据源接口进行扩展,用户可以通过动态文件或 etcd 等配置中心来动态地配置规则。 + +### 硬编码方式 + +Sentinel 也支持原始的硬编码方式加载规则,可以通过各个模块的 `LoadRules(rules)` 方法加载规则。以下是硬编码方式对某个method在consumer端的QPS流控: + +```go +_, err := flow.LoadRules([]*flow.FlowRule{ + { + ID: 666, + Resource: "dubbo:consumer:com.ikurento.user.UserProvider:myGroup:1.0.0:hello()", + MetricType: flow.QPS, + Count: 10, + ControlBehavior: flow.Reject, + }, +}) +if err != nil { + // 加载规则失败,进行相关处理 +} +``` + +# 总结 + +更加具体的实现,我就不详细论述,大家可以去看源码进一步了解。 + +最后,欢迎大家持续关注,或者贡献代码,期待dubbo-go在2020年在云原生领域继续突破。 + +dubbo-go仓库地址:[https://github.com/apache/dubbo-go](https://github.com/apache/dubbo-go) \ No newline at end of file diff --git a/doc/md/service-governance/dubbo-go-tps-limit-design-and-implement.md b/doc/md/service-governance/dubbo-go-tps-limit-design-and-implement.md new file mode 100644 index 0000000000..962ec98fdf --- /dev/null +++ b/doc/md/service-governance/dubbo-go-tps-limit-design-and-implement.md @@ -0,0 +1,163 @@ +# [dubbogo中的TPS Limit设计与实现](https://developer.aliyun.com/article/726804) + +# 前言 + +[Apache Dubbo](http://dubbo.apache.org)是由阿里开源的一个RPC框架,除了基本的RPC功能以外,还提供了一整套的服务治理相关功能。目前它已经是Apache基金会下的顶级项目。 + +而[dubbogo](https://github.com/apache/dubbo-go)则是dubbo的go语言实现。 + +最近在`dubbogo`的`todo list`上发现,它还没有实现`TPS Limit`的模块,于是就抽空实现了这个部分。 + +`TPS limit`实际上就是限流,比如说限制一分钟内某个接口只能访问200次,超过这个次数,则会被拒绝服务。在`Dubbo`的Java版本上,只有一个实现,就是`DefaultTPSLimiter`。 + +`DefaultTPSLimiter`是在服务级别上进行限流。虽然`dubbo`的官方文档里面声称可以在`method`级别上进行限流,但是我看了一下它的源码,实际上这个是做不到的。当然,如果自己通过实现`Filter`接口来实现`method`级别的限流,那么自然是可以的——这样暴露了`dubbo`Java版本实现的另外一个问题,就是`dubbo`的`TpsLimitFilter`实现,是不允许接入自己`TpsLimiter`的实现的。这从它的源码也可以看出来: + +![](../../pic/service-governance/dubbo-go-tps-limit-design-and-implement-a.png) + +它直接写死了`TpsLimiter`的实现。 + +这个实现的目前只是合并到了`develop`上,等下次发布正式版本的时候才会发布出来。 + +Github: [https://github.com/apache/dubbo-go/pull/237](https://github.com/apache/dubbo-go/pull/237) + +# 设计思路 + +于是我大概参考了一下`dubbo`已有的实现,做了一点改进。 + +`dubbo`里面的核心抽象是`TpsLimiter`接口。`TpsLimitFilter`只是简单调用了一下这个接口的方法而已: + +![](../../pic/service-governance/dubbo-go-tps-limit-design-and-implement-b.png) + +这个抽象是很棒的。但是还欠缺了一些抽象。 + +实际上,一个TPS Limit就要解决三个问题: + +1. 对什么东西进行`limit`。比如说,对服务进行限流,或者对某个方法进行限流,或者对IP进行限流,或者对用户进行限流; +2. 如何判断已经`over limitation`。这是从算法层面上考虑,即用什么算法来判断某个调用进来的时候,已经超过配置的上限了; +3. 被拒绝之后该如何处理。如果一个请求被断定为已经`over limititation`了,那么该怎么处理; + +所以在`TpsLimiter`接口的基础上,我再加了两个抽象: + +![](../../pic/service-governance/dubbo-go-tps-limit-design-and-implement-c.png) + +TpsLimiter + +![](../../pic/service-governance/dubbo-go-tps-limit-design-and-implement-d.png) + +TpsLimitStrategy + +![](../../pic/service-governance/dubbo-go-tps-limit-design-and-implement-e.png) + +RejectedExecutionHandler + +`TpsLimiter`对应到Java的`TpsLimiter`,两者是差不多。在我的设想里面,它既是顶级入口,还需要承担解决第一个问题的职责。 + +而`TpsLimitStrategy`则是第二个问题的抽象的接口定义。它代表的是纯粹的算法。该接口完全没有参数,实际上,所有的实现需要维护自身的状态——对于大部分实现而言,它大概只需要获取一下系统时间戳,所以不需要参数。 + +最后一个接口`RejectedExecutionHandler`代表的是拒绝策略。在`TpsLimitFilter`里面,如果它调用`TpsLimiter`的实现,发现该请求被拒绝,那么就会使用该接口的实现来获取一个返回值,返回给客户端。 + +# 实现 + +其实实现没太多好谈的。不过有一些微妙的地方,我虽然在代码里面注释了,但是我觉得在这里再多说一点也是可以的。 + +首先提及的就是拒绝策略`RejectedExecutionHandler`,我就是提供了一种实现,就是随便log了一下,什么都没做。因为这个东西是强业务相关的,我也不能提供更加多的通用的实现。 + +## 方法与服务双重支持的TpsLimiter + +`TpsLimiter`我只有一个实现,那就是`MethodServiceTpsLimiterImpl`。它就是根据配置,如果方法级别配置了参数,那么会在方法级别上进行限流。否则,如果在服务级别(ServiceKey)上有配置,那么会在服务级别进行限流。 + +举个最复杂的例子:服务A限制100,有四个方法,方法M1配置限制40,方法M2和方法M3无配置,方法M4配置限制-1:那么方法M1会单独限流40;M2和M3合并统计,被限制在100;方法M4则会被忽略。 + +用户可以配置具体的算法。比如说使用我接下来说的,我已经实现的三种实现。 + +## FixedWindow和ThreadSafeFixedWindow + +`FixedWindow`直接对应到Java的`DefaultTpsLimiter`。它采用的是`fixed-window`算法:比如说配置了一分钟内只能调用100次。假如从00:00开始计时,那么00:00-01:00内,只能调用100次。只有到达01:00,才会开启新的窗口01:00-02:00。如图: + +![](../../pic/service-governance/dubbo-go-tps-limit-design-and-implement-f.png) + +Fixed-Window图示 + +![](../../pic/service-governance/dubbo-go-tps-limit-design-and-implement-g.png) + +Fixed-Window实现 + +这里有一个很有意思的地方。就是这个实现,是一个几乎线程安全但是其实并不是线程安全的实现。 + +在所有的实现里面,它是最为简单,而且性能最高的。我在衡量了一番之后,还是没把它做成线程安全的。事实上,Java版本的也不是线程安全的。 + +它只会在多个线程通过第67行的检测之后,才会出现并发问题,这个时候就不是线程安全了。但是在最后的`return`语句中,那一整个是线程安全的。它因为不断计数往上加,所以多个线程同时跑到这里,其实不会有什么问题。 + +现在我要揭露一个最为奇诡的特性了:**并发越高,那么这个`raise condition`就越严重,也就是说越不安全。** + +但是从实际使用角度而言,有极端TPS的还是比较少的。对于那些TPS只有几百每秒的,是没什么问题的。 + +**为了保持和dubbo一致的特性,我把它作为默认的实现。** + +此外,我还为它搞了一个线程安全版本,也就是`ThreadSafeFixedWindowTpsLimitStrategyImpl`,只是简单的用`sync`封装了一下,可以看做是一个`Decorator`模式的应用。 + +如果强求线程安全,可以考虑使用这个。 + +## SlidingWindow + +这是我比较喜欢的实现。它跟网络协议里面的滑动窗口算法在理念上是比较接近的。 + +![](../../pic/service-governance/dubbo-go-tps-limit-design-and-implement-h.png) + +Sliding-Window + +具体来说,假如我设置的同样是一分钟1000次,它统计的永远是从当前时间点往前回溯一分钟内,已经被调用了多少次。如果这一分钟内,调用次数没超过1000,请求会被处理,如果已经超过,那么就会拒绝。 + +我再来描述一下,`SldingWindow`和`FixedWindow`两种算法的区别。这两者很多人会搞混。假如当前的时间戳是00:00,两个算法同时收到了第一个请求,开启第一个时间窗口。 + +那么`FixedWindow`就是00:00-01:00是第一个窗口,接下来依次是01:00-02:00, 02:00-03:00, ...。当然假如说01:00之后的三十秒内都没有请求,在01:31又来了一个请求,那么时间窗口就是01:31-02:31。 + +而`SildingWindow`则没有这种概念。假如在01:30收到一个请求,那么`SlidingWindow`统计的则是00:30-01:30内有没有达到1000次。**它永远计算的都是接收到请求的那一刻往前回溯一分钟的请求数量。** + +如果还是觉得有困难,那么简单来说就是`FixedWindow`往后看一分钟,`SlidingWindow`回溯一分钟。 + +> 这个说法并不严谨,只是为了方便理解。 + +在真正写这个实现的时候,我稍微改了一点点: + +![](../../pic/service-governance/dubbo-go-tps-limit-design-and-implement-i.png) + +我用了一个队列来保存每次访问的时间戳。一般的写法,都是请求进来,先把已经不在窗口时间内的时间戳删掉,然后统计剩下的数量,也就是后面的`slow path`的那一堆逻辑。 + +但是我改了的一点是,我进来直接统计队列里面的数量——也就是请求数量,如果都小于上限,那么我可以直接返回`true`。即`quick path`。 + +这种改进的核心就是:我只有在检测到当前队列里面有超过上限数量的请求数量时候,才会尝试删除已经不在窗口内的时间戳。 + +这其实就是,是每个请求过来,我都清理一下队列呢?还是只有队列元素超出数量了,我才清理呢?我选择的是后者。 + +我认为这是一种改进……当然从本质上来说,整体开销是没有减少的——因为`golang`语言里面`List`的实现,一次多删除几个,和每次删除一个,多删几次,并没有多大的区别。 + +### 算法总结 + +无论是`FixedWindow`算法还是`SlidingWindow`算法都有一个固有的缺陷,就是这个时间窗口难控制。 + +我们设想一下,假如说我们把时间窗口设置为一分钟,允许1000次调用。然而,在前十秒的时候就调用了1000次。在后面的五十秒,服务器虽然将所有的请求都处理完了,然是因为窗口还没到新窗口,所以这个时间段过来的请求,全部会被拒绝。 + +![](../../pic/service-governance/dubbo-go-tps-limit-design-and-implement-j.png) + +解决的方案就是调小时间窗口,比如调整到一秒。但是时间窗口的缩小,会导致`FixedWindow`算法的`raise condition`情况加剧。`SlidingWindow`也会受影响,但是影响要小很多。 + +## 那些没有实现的 + +### 基于特定业务对象的限流 + +举例来说,某些特殊业务用的针对用户ID进行限流和针对IP进行限流,我就没有在`dubbogo`里面实现。有需要的可以通过实现`TpsLimiter`接口来完成。 + +### 全局TPS limit + +这篇文章之前讨论的都是单机限流。如果全局限流,比如说针对某个客户,它购买的服务是每分钟调用100次,那么就需要全局限流——虽然这种case都不会用`Filter`方案,而是另外做一个`API`接入控制。 + +比如说,很常用的使用Redis进行限流的。针对某个客户,一分钟只能访问100次,那我就用客户ID做key,value设置成List,每次调用过来,随便塞一个值进去,设置过期时间一分钟。那么每次统计只需要统计当前key的存活的值的数量就可以了。 + +这种我也没实现,因为好像没什么需求。国内讨论TPS limit都是讨论单机TPS limit比较多。 + +这个同样可以通过实现`TpsLimiter`接口来实现。 + +### Leaky Bucket算法 + +这个本来可以是`TpsLimitStrategy`的一种实现的。后来我觉得,它其实并没有特别大的优势——虽然号称可以做到均匀,但是其实并做不到真正的均匀。通过调整`SlidingWindow`的窗口大小,是可以接近它宣称的均匀消费的效果的。比如说调整到一秒,那其实就已经很均匀了。而这并不会带来多少额外的开销。 \ No newline at end of file diff --git a/doc/pic/arch/dubbo-go-design-implement-and-featrues-a.png b/doc/pic/arch/dubbo-go-design-implement-and-featrues-a.png new file mode 100644 index 0000000000..0f77e7c7bd Binary files /dev/null and b/doc/pic/arch/dubbo-go-design-implement-and-featrues-a.png differ diff --git a/doc/pic/arch/dubbo-go-design-implement-and-featrues-b.png b/doc/pic/arch/dubbo-go-design-implement-and-featrues-b.png new file mode 100644 index 0000000000..3189a48027 Binary files /dev/null and b/doc/pic/arch/dubbo-go-design-implement-and-featrues-b.png differ diff --git a/doc/pic/arch/dubbo-go-design-implement-and-featrues-c.png b/doc/pic/arch/dubbo-go-design-implement-and-featrues-c.png new file mode 100644 index 0000000000..2fc84afe62 Binary files /dev/null and b/doc/pic/arch/dubbo-go-design-implement-and-featrues-c.png differ diff --git a/doc/pic/arch/dubbo-go-design-implement-and-featrues-d.png b/doc/pic/arch/dubbo-go-design-implement-and-featrues-d.png new file mode 100644 index 0000000000..fe5f233b6e Binary files /dev/null and b/doc/pic/arch/dubbo-go-design-implement-and-featrues-d.png differ diff --git a/doc/pic/arch/dubbo-go-design-implement-and-featrues-e.png b/doc/pic/arch/dubbo-go-design-implement-and-featrues-e.png new file mode 100644 index 0000000000..52298189dd Binary files /dev/null and b/doc/pic/arch/dubbo-go-design-implement-and-featrues-e.png differ diff --git a/doc/pic/arch/dubbo-go-design-implement-and-featrues-f.png b/doc/pic/arch/dubbo-go-design-implement-and-featrues-f.png new file mode 100644 index 0000000000..1368e1c0b7 Binary files /dev/null and b/doc/pic/arch/dubbo-go-design-implement-and-featrues-f.png differ diff --git a/doc/pic/arch/dubbo-go-design-implement-and-featrues-g.png b/doc/pic/arch/dubbo-go-design-implement-and-featrues-g.png new file mode 100644 index 0000000000..ffde3b56b0 Binary files /dev/null and b/doc/pic/arch/dubbo-go-design-implement-and-featrues-g.png differ diff --git a/doc/pic/arch/dubbo-go-design-implement-and-featrues-h.png b/doc/pic/arch/dubbo-go-design-implement-and-featrues-h.png new file mode 100644 index 0000000000..6783926994 Binary files /dev/null and b/doc/pic/arch/dubbo-go-design-implement-and-featrues-h.png differ diff --git a/doc/pic/arch/dubbo-go-design-implement-and-featrues-i.png b/doc/pic/arch/dubbo-go-design-implement-and-featrues-i.png new file mode 100644 index 0000000000..0e2c25b198 Binary files /dev/null and b/doc/pic/arch/dubbo-go-design-implement-and-featrues-i.png differ diff --git a/doc/pic/arch/dubbo-go-design-implement-and-featrues-j.png b/doc/pic/arch/dubbo-go-design-implement-and-featrues-j.png new file mode 100644 index 0000000000..1ea510561d Binary files /dev/null and b/doc/pic/arch/dubbo-go-design-implement-and-featrues-j.png differ diff --git a/doc/pic/arch/dubbo-go-design-implement-and-featrues-k.png b/doc/pic/arch/dubbo-go-design-implement-and-featrues-k.png new file mode 100644 index 0000000000..f5ae884bfb Binary files /dev/null and b/doc/pic/arch/dubbo-go-design-implement-and-featrues-k.png differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-0.jpg b/doc/pic/arch/dubbo-go-review-and-future-0.jpg new file mode 100644 index 0000000000..5ec22f1753 Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-0.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-a.jpg b/doc/pic/arch/dubbo-go-review-and-future-a.jpg new file mode 100644 index 0000000000..dc8f1dc5f4 Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-a.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-b.jpg b/doc/pic/arch/dubbo-go-review-and-future-b.jpg new file mode 100644 index 0000000000..a8234572e3 Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-b.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-c.jpg b/doc/pic/arch/dubbo-go-review-and-future-c.jpg new file mode 100644 index 0000000000..652bd00dfd Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-c.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-d.jpg b/doc/pic/arch/dubbo-go-review-and-future-d.jpg new file mode 100644 index 0000000000..5371cbbb76 Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-d.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-e.jpg b/doc/pic/arch/dubbo-go-review-and-future-e.jpg new file mode 100644 index 0000000000..ad7a3f4c29 Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-e.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-f.jpg b/doc/pic/arch/dubbo-go-review-and-future-f.jpg new file mode 100644 index 0000000000..13812d7eac Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-f.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-g.jpg b/doc/pic/arch/dubbo-go-review-and-future-g.jpg new file mode 100644 index 0000000000..1a174ae11d Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-g.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-h.jpg b/doc/pic/arch/dubbo-go-review-and-future-h.jpg new file mode 100644 index 0000000000..fc398af847 Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-h.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-i.jpg b/doc/pic/arch/dubbo-go-review-and-future-i.jpg new file mode 100644 index 0000000000..42558f45f0 Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-i.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-j.jpg b/doc/pic/arch/dubbo-go-review-and-future-j.jpg new file mode 100644 index 0000000000..8eb3e7fd5b Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-j.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-k.jpg b/doc/pic/arch/dubbo-go-review-and-future-k.jpg new file mode 100644 index 0000000000..3af59c4f44 Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-k.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-l.jpg b/doc/pic/arch/dubbo-go-review-and-future-l.jpg new file mode 100644 index 0000000000..7e10f682d0 Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-l.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-m.jpg b/doc/pic/arch/dubbo-go-review-and-future-m.jpg new file mode 100644 index 0000000000..17d78da72c Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-m.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-n.jpg b/doc/pic/arch/dubbo-go-review-and-future-n.jpg new file mode 100644 index 0000000000..b64a975bae Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-n.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-o.png b/doc/pic/arch/dubbo-go-review-and-future-o.png new file mode 100644 index 0000000000..f97468ca76 Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-o.png differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-p.png b/doc/pic/arch/dubbo-go-review-and-future-p.png new file mode 100644 index 0000000000..3f9e924e77 Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-p.png differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-q.jpg b/doc/pic/arch/dubbo-go-review-and-future-q.jpg new file mode 100644 index 0000000000..48bb4a943c Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-q.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-r.jpg b/doc/pic/arch/dubbo-go-review-and-future-r.jpg new file mode 100644 index 0000000000..72290335be Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-r.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-s.jpg b/doc/pic/arch/dubbo-go-review-and-future-s.jpg new file mode 100644 index 0000000000..33b4d7472b Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-s.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-t.png b/doc/pic/arch/dubbo-go-review-and-future-t.png new file mode 100644 index 0000000000..7aee88fb00 Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-t.png differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-u.png b/doc/pic/arch/dubbo-go-review-and-future-u.png new file mode 100644 index 0000000000..99649afbec Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-u.png differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-v.jpg b/doc/pic/arch/dubbo-go-review-and-future-v.jpg new file mode 100644 index 0000000000..909738ebae Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-v.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-w.jpg b/doc/pic/arch/dubbo-go-review-and-future-w.jpg new file mode 100644 index 0000000000..ec8606bc02 Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-w.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-x.png b/doc/pic/arch/dubbo-go-review-and-future-x.png new file mode 100644 index 0000000000..328e046643 Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-x.png differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-y.jpg b/doc/pic/arch/dubbo-go-review-and-future-y.jpg new file mode 100644 index 0000000000..c8f1801e07 Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-y.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-z.jpg b/doc/pic/arch/dubbo-go-review-and-future-z.jpg new file mode 100644 index 0000000000..219784c9bc Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-z.jpg differ diff --git a/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-a.jpg b/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-a.jpg new file mode 100644 index 0000000000..3fc7600e76 Binary files /dev/null and b/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-a.jpg differ diff --git a/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-b.png b/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-b.png new file mode 100644 index 0000000000..08b9f62d73 Binary files /dev/null and b/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-b.png differ diff --git a/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-c.png b/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-c.png new file mode 100644 index 0000000000..046f9be158 Binary files /dev/null and b/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-c.png differ diff --git a/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-d.png b/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-d.png new file mode 100644 index 0000000000..0c0ad73c08 Binary files /dev/null and b/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-d.png differ diff --git a/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-e.png b/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-e.png new file mode 100644 index 0000000000..4c035fd8e5 Binary files /dev/null and b/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-e.png differ diff --git a/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-f.png b/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-f.png new file mode 100644 index 0000000000..cebcd37672 Binary files /dev/null and b/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-f.png differ diff --git a/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-g.png b/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-g.png new file mode 100644 index 0000000000..fdee33ba12 Binary files /dev/null and b/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-g.png differ diff --git a/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-h.png b/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-h.png new file mode 100644 index 0000000000..47b9ea64db Binary files /dev/null and b/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-h.png differ diff --git a/doc/pic/course/the-5th-years-of-dubbo-go-a.png b/doc/pic/course/the-5th-years-of-dubbo-go-a.png new file mode 100644 index 0000000000..6e8fa474f8 Binary files /dev/null and b/doc/pic/course/the-5th-years-of-dubbo-go-a.png differ diff --git a/doc/pic/course/the-5th-years-of-dubbo-go-b.png b/doc/pic/course/the-5th-years-of-dubbo-go-b.png new file mode 100644 index 0000000000..09bb691717 Binary files /dev/null and b/doc/pic/course/the-5th-years-of-dubbo-go-b.png differ diff --git a/doc/pic/course/the-5th-years-of-dubbo-go-c.png b/doc/pic/course/the-5th-years-of-dubbo-go-c.png new file mode 100644 index 0000000000..31b09ae1a7 Binary files /dev/null and b/doc/pic/course/the-5th-years-of-dubbo-go-c.png differ diff --git a/doc/pic/getty/getty_connected_udp_socket.gif b/doc/pic/getty/getty_connected_udp_socket.gif new file mode 100644 index 0000000000..011cd2329e Binary files /dev/null and b/doc/pic/getty/getty_connected_udp_socket.gif differ diff --git a/doc/pic/getty/getty_dns_udp.gif b/doc/pic/getty/getty_dns_udp.gif new file mode 100644 index 0000000000..253dddf239 Binary files /dev/null and b/doc/pic/getty/getty_dns_udp.gif differ diff --git a/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-a.png b/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-a.png new file mode 100644 index 0000000000..484e07d3ac Binary files /dev/null and b/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-a.png differ diff --git a/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-b.png b/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-b.png new file mode 100644 index 0000000000..2a208c6952 Binary files /dev/null and b/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-b.png differ diff --git a/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-c.png b/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-c.png new file mode 100644 index 0000000000..f89c20fe3b Binary files /dev/null and b/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-c.png differ diff --git a/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-d.png b/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-d.png new file mode 100644 index 0000000000..5d639b918a Binary files /dev/null and b/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-d.png differ diff --git a/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-e.png b/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-e.png new file mode 100644 index 0000000000..940950ec29 Binary files /dev/null and b/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-e.png differ diff --git a/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-f.png b/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-f.png new file mode 100644 index 0000000000..26a543b227 Binary files /dev/null and b/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-f.png differ diff --git a/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-g.png b/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-g.png new file mode 100644 index 0000000000..a98c7222d7 Binary files /dev/null and b/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-g.png differ diff --git a/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-h.png b/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-h.png new file mode 100644 index 0000000000..3da98bfe96 Binary files /dev/null and b/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-h.png differ diff --git a/doc/pic/interview/dubbo-go-published-a.jpg b/doc/pic/interview/dubbo-go-published-a.jpg new file mode 100644 index 0000000000..ce27147a1a Binary files /dev/null and b/doc/pic/interview/dubbo-go-published-a.jpg differ diff --git a/doc/pic/interview/dubbo-go-published-b.jpg b/doc/pic/interview/dubbo-go-published-b.jpg new file mode 100644 index 0000000000..9cc7e6486c Binary files /dev/null and b/doc/pic/interview/dubbo-go-published-b.jpg differ diff --git a/doc/pic/interview/dubbo-go-published-c.jpg b/doc/pic/interview/dubbo-go-published-c.jpg new file mode 100644 index 0000000000..69c70c7d51 Binary files /dev/null and b/doc/pic/interview/dubbo-go-published-c.jpg differ diff --git a/doc/pic/interview/what's-new-in-dubbo-go-v1.4.0-a.png b/doc/pic/interview/what's-new-in-dubbo-go-v1.4.0-a.png new file mode 100644 index 0000000000..97f3caa29e Binary files /dev/null and b/doc/pic/interview/what's-new-in-dubbo-go-v1.4.0-a.png differ diff --git a/doc/pic/interview/what's-new-in-dubbo-go-v1.4.0-b.png b/doc/pic/interview/what's-new-in-dubbo-go-v1.4.0-b.png new file mode 100644 index 0000000000..a491cbbf46 Binary files /dev/null and b/doc/pic/interview/what's-new-in-dubbo-go-v1.4.0-b.png differ diff --git a/doc/pic/interview/what's-new-in-dubbo-go-v1.4.0-c.png b/doc/pic/interview/what's-new-in-dubbo-go-v1.4.0-c.png new file mode 100644 index 0000000000..fa867fc6cf Binary files /dev/null and b/doc/pic/interview/what's-new-in-dubbo-go-v1.4.0-c.png differ diff --git a/doc/pic/interview/what's-new-in-dubbo-go-v1.4.0-d.png b/doc/pic/interview/what's-new-in-dubbo-go-v1.4.0-d.png new file mode 100644 index 0000000000..5fe471a16d Binary files /dev/null and b/doc/pic/interview/what's-new-in-dubbo-go-v1.4.0-d.png differ diff --git a/doc/pic/interview/what's-new-in-dubbo-go-v1.4.0-e.png b/doc/pic/interview/what's-new-in-dubbo-go-v1.4.0-e.png new file mode 100644 index 0000000000..679e90abfc Binary files /dev/null and b/doc/pic/interview/what's-new-in-dubbo-go-v1.4.0-e.png differ diff --git a/doc/pic/registry-center/dubbo-go-registry-center--nacos-a.png b/doc/pic/registry-center/dubbo-go-registry-center--nacos-a.png new file mode 100644 index 0000000000..dafc150553 Binary files /dev/null and b/doc/pic/registry-center/dubbo-go-registry-center--nacos-a.png differ diff --git a/doc/pic/registry-center/dubbo-go-registry-center--nacos-b.png b/doc/pic/registry-center/dubbo-go-registry-center--nacos-b.png new file mode 100644 index 0000000000..85b3997c8f Binary files /dev/null and b/doc/pic/registry-center/dubbo-go-registry-center--nacos-b.png differ diff --git a/doc/pic/registry-center/dubbo-go-registry-center--nacos-c.png b/doc/pic/registry-center/dubbo-go-registry-center--nacos-c.png new file mode 100644 index 0000000000..11ada496e3 Binary files /dev/null and b/doc/pic/registry-center/dubbo-go-registry-center--nacos-c.png differ diff --git a/doc/pic/registry-center/dubbo-go-registry-center--nacos-d.png b/doc/pic/registry-center/dubbo-go-registry-center--nacos-d.png new file mode 100644 index 0000000000..e66f3bd389 Binary files /dev/null and b/doc/pic/registry-center/dubbo-go-registry-center--nacos-d.png differ diff --git a/doc/pic/registry-center/dubbo-go-registry-center--nacos-e.png b/doc/pic/registry-center/dubbo-go-registry-center--nacos-e.png new file mode 100644 index 0000000000..a02d9c7322 Binary files /dev/null and b/doc/pic/registry-center/dubbo-go-registry-center--nacos-e.png differ diff --git a/doc/pic/registry-center/dubbo-go-registry-center--nacos-f.png b/doc/pic/registry-center/dubbo-go-registry-center--nacos-f.png new file mode 100644 index 0000000000..4d97fcf4c2 Binary files /dev/null and b/doc/pic/registry-center/dubbo-go-registry-center--nacos-f.png differ diff --git a/doc/pic/registry-center/dubbo-go-registry-center--nacos-g.png b/doc/pic/registry-center/dubbo-go-registry-center--nacos-g.png new file mode 100644 index 0000000000..da690449bd Binary files /dev/null and b/doc/pic/registry-center/dubbo-go-registry-center--nacos-g.png differ diff --git a/doc/pic/registry-center/dubbo-go-registry-center--nacos-h.png b/doc/pic/registry-center/dubbo-go-registry-center--nacos-h.png new file mode 100644 index 0000000000..1a3e6834fb Binary files /dev/null and b/doc/pic/registry-center/dubbo-go-registry-center--nacos-h.png differ diff --git a/doc/pic/registry-center/dubbo-go-registry-center--nacos-i.png b/doc/pic/registry-center/dubbo-go-registry-center--nacos-i.png new file mode 100644 index 0000000000..a5e7503ed3 Binary files /dev/null and b/doc/pic/registry-center/dubbo-go-registry-center--nacos-i.png differ diff --git a/doc/pic/registry-center/dubbo-go-registry-center--nacos-j.png b/doc/pic/registry-center/dubbo-go-registry-center--nacos-j.png new file mode 100644 index 0000000000..a05602b815 Binary files /dev/null and b/doc/pic/registry-center/dubbo-go-registry-center--nacos-j.png differ diff --git a/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-a.png b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-a.png new file mode 100644 index 0000000000..87726d8848 Binary files /dev/null and b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-a.png differ diff --git a/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-b.png b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-b.png new file mode 100644 index 0000000000..c4f2c8dcaf Binary files /dev/null and b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-b.png differ diff --git a/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-c.png b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-c.png new file mode 100644 index 0000000000..c55fa2e7af Binary files /dev/null and b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-c.png differ diff --git a/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-d.png b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-d.png new file mode 100644 index 0000000000..3d57d26861 Binary files /dev/null and b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-d.png differ diff --git a/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-e.png b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-e.png new file mode 100644 index 0000000000..0474e7c927 Binary files /dev/null and b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-e.png differ diff --git a/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-f.png b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-f.png new file mode 100644 index 0000000000..f763f7c51d Binary files /dev/null and b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-f.png differ diff --git a/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-g.png b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-g.png new file mode 100644 index 0000000000..5d71cd67b6 Binary files /dev/null and b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-g.png differ diff --git a/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-h.png b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-h.png new file mode 100644 index 0000000000..710ff688cc Binary files /dev/null and b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-h.png differ diff --git a/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-i.png b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-i.png new file mode 100644 index 0000000000..6112cfe105 Binary files /dev/null and b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-i.png differ diff --git a/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-j.png b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-j.png new file mode 100644 index 0000000000..10e69939ab Binary files /dev/null and b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-j.png differ diff --git a/doc/pic/rpc/dubb-go-adaptive-grpc-a.png b/doc/pic/rpc/dubb-go-adaptive-grpc-a.png new file mode 100644 index 0000000000..c71a361498 Binary files /dev/null and b/doc/pic/rpc/dubb-go-adaptive-grpc-a.png differ diff --git a/doc/pic/rpc/dubb-go-adaptive-grpc-b.png b/doc/pic/rpc/dubb-go-adaptive-grpc-b.png new file mode 100644 index 0000000000..b401df59e5 Binary files /dev/null and b/doc/pic/rpc/dubb-go-adaptive-grpc-b.png differ diff --git a/doc/pic/rpc/dubb-go-adaptive-grpc-c.png b/doc/pic/rpc/dubb-go-adaptive-grpc-c.png new file mode 100644 index 0000000000..66fc888c54 Binary files /dev/null and b/doc/pic/rpc/dubb-go-adaptive-grpc-c.png differ diff --git a/doc/pic/rpc/dubb-go-adaptive-grpc-d.png b/doc/pic/rpc/dubb-go-adaptive-grpc-d.png new file mode 100644 index 0000000000..59160482c2 Binary files /dev/null and b/doc/pic/rpc/dubb-go-adaptive-grpc-d.png differ diff --git a/doc/pic/rpc/dubb-go-adaptive-grpc-e.png b/doc/pic/rpc/dubb-go-adaptive-grpc-e.png new file mode 100644 index 0000000000..9f81f6226d Binary files /dev/null and b/doc/pic/rpc/dubb-go-adaptive-grpc-e.png differ diff --git a/doc/pic/rpc/dubb-go-adaptive-grpc-f.png b/doc/pic/rpc/dubb-go-adaptive-grpc-f.png new file mode 100644 index 0000000000..d35e11c3f1 Binary files /dev/null and b/doc/pic/rpc/dubb-go-adaptive-grpc-f.png differ diff --git a/doc/pic/rpc/dubb-go-adaptive-grpc-g.png b/doc/pic/rpc/dubb-go-adaptive-grpc-g.png new file mode 100644 index 0000000000..9c52e9e88a Binary files /dev/null and b/doc/pic/rpc/dubb-go-adaptive-grpc-g.png differ diff --git a/doc/pic/rpc/dubb-go-adaptive-grpc-h.png b/doc/pic/rpc/dubb-go-adaptive-grpc-h.png new file mode 100644 index 0000000000..a1833f0fa1 Binary files /dev/null and b/doc/pic/rpc/dubb-go-adaptive-grpc-h.png differ diff --git a/doc/pic/rpc/dubb-go-adaptive-grpc-i.png b/doc/pic/rpc/dubb-go-adaptive-grpc-i.png new file mode 100644 index 0000000000..a5d0deb864 Binary files /dev/null and b/doc/pic/rpc/dubb-go-adaptive-grpc-i.png differ diff --git a/doc/pic/rpc/dubb-go-adaptive-grpc-j.png b/doc/pic/rpc/dubb-go-adaptive-grpc-j.png new file mode 100644 index 0000000000..de21c4fa2d Binary files /dev/null and b/doc/pic/rpc/dubb-go-adaptive-grpc-j.png differ diff --git a/doc/pic/rpc/dubb-go-adaptive-grpc-k.png b/doc/pic/rpc/dubb-go-adaptive-grpc-k.png new file mode 100644 index 0000000000..813cee6ce0 Binary files /dev/null and b/doc/pic/rpc/dubb-go-adaptive-grpc-k.png differ diff --git a/doc/pic/rpc/dubb-go-adaptive-grpc-l.png b/doc/pic/rpc/dubb-go-adaptive-grpc-l.png new file mode 100644 index 0000000000..747a5a2059 Binary files /dev/null and b/doc/pic/rpc/dubb-go-adaptive-grpc-l.png differ diff --git a/doc/pic/rpc/dubb-go-adaptive-grpc-m.png b/doc/pic/rpc/dubb-go-adaptive-grpc-m.png new file mode 100644 index 0000000000..55a1ae13de Binary files /dev/null and b/doc/pic/rpc/dubb-go-adaptive-grpc-m.png differ diff --git a/doc/pic/rpc/dubb-go-adaptive-grpc-n.png b/doc/pic/rpc/dubb-go-adaptive-grpc-n.png new file mode 100644 index 0000000000..7bf287145a Binary files /dev/null and b/doc/pic/rpc/dubb-go-adaptive-grpc-n.png differ diff --git a/doc/pic/service-governance/dubbo-go-sentinel-a.png b/doc/pic/service-governance/dubbo-go-sentinel-a.png new file mode 100644 index 0000000000..3806989c8b Binary files /dev/null and b/doc/pic/service-governance/dubbo-go-sentinel-a.png differ diff --git a/doc/pic/service-governance/dubbo-go-sentinel-b.png b/doc/pic/service-governance/dubbo-go-sentinel-b.png new file mode 100644 index 0000000000..03df855c49 Binary files /dev/null and b/doc/pic/service-governance/dubbo-go-sentinel-b.png differ diff --git a/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-a.png b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-a.png new file mode 100644 index 0000000000..8a34165318 Binary files /dev/null and b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-a.png differ diff --git a/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-b.png b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-b.png new file mode 100644 index 0000000000..3a6f1f1512 Binary files /dev/null and b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-b.png differ diff --git a/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-c.png b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-c.png new file mode 100644 index 0000000000..77f10d99a5 Binary files /dev/null and b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-c.png differ diff --git a/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-d.png b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-d.png new file mode 100644 index 0000000000..65b7185a8a Binary files /dev/null and b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-d.png differ diff --git a/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-e.png b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-e.png new file mode 100644 index 0000000000..07740479b7 Binary files /dev/null and b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-e.png differ diff --git a/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-f.png b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-f.png new file mode 100644 index 0000000000..49b30ef7fe Binary files /dev/null and b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-f.png differ diff --git a/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-g.png b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-g.png new file mode 100644 index 0000000000..22d4e6f01c Binary files /dev/null and b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-g.png differ diff --git a/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-h.png b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-h.png new file mode 100644 index 0000000000..132057b12a Binary files /dev/null and b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-h.png differ diff --git a/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-i.png b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-i.png new file mode 100644 index 0000000000..b39a83787b Binary files /dev/null and b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-i.png differ diff --git a/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-j.png b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-j.png new file mode 100644 index 0000000000..e671ff58ce Binary files /dev/null and b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-j.png differ diff --git a/doc/ppt/arch/dubbogo20191228-hangzhou.pptx b/doc/ppt/arch/dubbogo20191228-hangzhou.pptx new file mode 100644 index 0000000000..c6c09b2890 Binary files /dev/null and b/doc/ppt/arch/dubbogo20191228-hangzhou.pptx differ diff --git a/doc/ppt/arch/panty_dubbo-go_share.pptx b/doc/ppt/arch/panty_dubbo-go_share.pptx new file mode 100644 index 0000000000..65c5100037 Binary files /dev/null and b/doc/ppt/arch/panty_dubbo-go_share.pptx differ