This is an automated email from the ASF dual-hosted git repository.
iluo pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/dubbo-website.git
The following commit(s) were added to refs/heads/master by this push:
new 06cc39a dubbo-go blog batch#5
06cc39a is described below
commit 06cc39a079286b730571d5942df9f84680fc0dbd
Author: Ian Luo <[email protected]>
AuthorDate: Thu Jan 14 23:25:40 2021 +0800
dubbo-go blog batch#5
---
content/zh/blog/news/dubbo-go-codewalk-1.md | 670 ++++++++++++++++++++++
content/zh/blog/news/dubbo-go-nacos.md | 116 ++++
content/zh/blog/releases/dubbo-go-hessian2-1.7.md | 246 ++++++++
static/imgs/blog/dubbo-go/code1/p1.png | Bin 0 -> 17895 bytes
static/imgs/blog/dubbo-go/code1/p10.png | Bin 0 -> 51680 bytes
static/imgs/blog/dubbo-go/code1/p11.png | Bin 0 -> 5668 bytes
static/imgs/blog/dubbo-go/code1/p12.png | Bin 0 -> 29844 bytes
static/imgs/blog/dubbo-go/code1/p13.png | Bin 0 -> 18246 bytes
static/imgs/blog/dubbo-go/code1/p14.png | Bin 0 -> 9163 bytes
static/imgs/blog/dubbo-go/code1/p15.png | Bin 0 -> 8263 bytes
static/imgs/blog/dubbo-go/code1/p16.png | Bin 0 -> 31474 bytes
static/imgs/blog/dubbo-go/code1/p17.png | Bin 0 -> 13336 bytes
static/imgs/blog/dubbo-go/code1/p18.png | Bin 0 -> 54579 bytes
static/imgs/blog/dubbo-go/code1/p19.png | Bin 0 -> 51892 bytes
static/imgs/blog/dubbo-go/code1/p2.png | Bin 0 -> 954481 bytes
static/imgs/blog/dubbo-go/code1/p20.png | Bin 0 -> 19454 bytes
static/imgs/blog/dubbo-go/code1/p21.png | Bin 0 -> 35821 bytes
static/imgs/blog/dubbo-go/code1/p22.png | Bin 0 -> 52245 bytes
static/imgs/blog/dubbo-go/code1/p23.png | Bin 0 -> 17475 bytes
static/imgs/blog/dubbo-go/code1/p24.png | Bin 0 -> 14073 bytes
static/imgs/blog/dubbo-go/code1/p25.png | Bin 0 -> 3549 bytes
static/imgs/blog/dubbo-go/code1/p26.png | Bin 0 -> 2727 bytes
static/imgs/blog/dubbo-go/code1/p27.png | Bin 0 -> 4354 bytes
static/imgs/blog/dubbo-go/code1/p28.png | Bin 0 -> 2188 bytes
static/imgs/blog/dubbo-go/code1/p29.png | Bin 0 -> 21657 bytes
static/imgs/blog/dubbo-go/code1/p3.png | Bin 0 -> 93754 bytes
static/imgs/blog/dubbo-go/code1/p4.png | Bin 0 -> 52318 bytes
static/imgs/blog/dubbo-go/code1/p5.png | Bin 0 -> 6893 bytes
static/imgs/blog/dubbo-go/code1/p6.png | Bin 0 -> 36214 bytes
static/imgs/blog/dubbo-go/code1/p7.png | Bin 0 -> 7640 bytes
static/imgs/blog/dubbo-go/code1/p8.png | Bin 0 -> 15488 bytes
static/imgs/blog/dubbo-go/code1/p9.png | Bin 0 -> 56351 bytes
static/imgs/blog/dubbo-go/nacos/p1.png | Bin 0 -> 231313 bytes
static/imgs/blog/dubbo-go/nacos/p2.png | Bin 0 -> 141605 bytes
static/imgs/blog/dubbo-go/nacos/p3.png | Bin 0 -> 176504 bytes
static/imgs/blog/dubbo-go/nacos/p4.png | Bin 0 -> 238958 bytes
static/imgs/blog/dubbo-go/nacos/p5.png | Bin 0 -> 513996 bytes
static/imgs/blog/dubbo-go/nacos/p6.png | Bin 0 -> 116991 bytes
static/imgs/blog/dubbo-go/nacos/p7.png | Bin 0 -> 263186 bytes
static/imgs/blog/dubbo-go/nacos/p8.png | Bin 0 -> 153733 bytes
static/imgs/blog/dubbo-go/nacos/p9.png | Bin 0 -> 250554 bytes
41 files changed, 1032 insertions(+)
diff --git a/content/zh/blog/news/dubbo-go-codewalk-1.md
b/content/zh/blog/news/dubbo-go-codewalk-1.md
new file mode 100644
index 0000000..afc6634
--- /dev/null
+++ b/content/zh/blog/news/dubbo-go-codewalk-1.md
@@ -0,0 +1,670 @@
+---
+title: "Dubbo-go 源码笔记(一)Server 端开启服务过程"
+linkTitle: "Dubbo-go 源码笔记(一)Server 端开启服务过程"
+date: 2021-01-14
+description: >
+ 随着微服务架构的流行,许多高性能 rpc 框架应运而生,由阿里开源的 dubbo 框架 go 语言版本的 dubbo-go
也成为了众多开发者不错的选择。本文将介绍 dubbo-go 框架的基本使用方法,以及从 export 调用链的角度进行 server
端源码导读,希望能引导读者进一步认识这款框架。
+---
+
+当拿到一款框架之后,一种不错的源码阅读方式大致如下:从运行最基础的 helloworld demo 源码开始 —> 再查看配置文件 —>
开启各种依赖服务(比如zk、consul) —> 开启服务端 —> 再到通过 client 调用服务端 —>
打印完整请求日志和回包。调用成功之后,再根据框架的设计模型,从配置文件解析开始,自顶向下递阅读整个框架的调用栈。
+
+对于 C/S 模式的 rpc 请求来说,整个调用栈被拆成了 client 和 server 两部分,所以可以分别从 server 端的配置文件解析阅读到
server 端的监听启动,从 client 端的配置文件解析阅读到一次 invoker Call 调用。这样一次完整请求就明晰了起来。
+
+## 运行官网提供的 helloworld-demo
+
+**官方 demo
相关链接**:https://github.com/dubbogo/dubbo-samples/tree/master/golang/helloworld/dubbo
+
+### 1. dubbo-go 2.7 版本 QuickStart
+
+#### 1)开启一个 go-server 服务
+
+- 将仓库 clone 到本地
+
+```bash
+$ git clone https://github.com/dubbogo/dubbo-samples.git
+```
+
+- 进入 dubbo 目录
+
+```bash
+$ cd dubbo-samples/golang/helloworld/dubbo
+```
+
+进入目录后可看到四个文件夹,分别支持 go 和 java 的 client 以及 server,我们尝试运行一个 go 的 server。进入 app
子文件夹内,可以看到里面保存了 go 文件。
+
+```bash
+$ cd go-server/app
+```
+
+- sample 文件结构
+
+可以在 go-server 里面看到三个文件夹:app、assembly、profiles。
+
+其中 app 文件夹下保存 go 源码,assembly 文件夹下保存可选的针对特定环境的 build 脚本,profiles 下保存配置文件。对于
dubbo-go 框架,配置文件非常重要,没有文件将导致服务无法启动。
+
+- 设置指向配置文件的环境变量
+
+由于 dubbo-go 框架依赖配置文件启动,让框架定位到配置文件的方式就是通过环境变量来找。对于 server
端需要两个必须配置的环境变量:CONF_PROVIDER_FILE_PATH、APP_LOG_CONF_FILE,分别应该指向服务端配置文件、日志配置文件。
+
+在 sample 里面,我们可以使用 dev 环境,即 profiles/dev/log.yml 和 profiles/dev/server.yml
两个文件。在 app/ 下,通过命令行中指定好这两个文件:
+
+```bash
+$ export CONF_PROVIDER_FILE_PATH="../profiles/dev/server.yml"
+$ export APP_LOG_CONF_FILE="../profiles/dev/log.yml"
+```
+
+- 设置 go 代理并运行服务
+
+```bash
+$ go run .
+```
+
+如果提示 timeout,则需要设置 goproxy 代理。
+
+```bash
+$ export GOPROXY="http://goproxy.io"
+```
+
+再运行 go run 即可开启服务。
+
+#### 2)运行 zookeeper
+
+安装 zookeeper,并运行 zkServer, 默认为 2181 端口。
+
+#### 3)运行 go-client 调用 server 服务
+
+- 进入 go-client 的源码目录
+
+```bash
+$ cd go-client/app
+```
+
+- 同理,在 /app 下配置环境变量
+
+```bash
+$ export CONF_CONSUMER_FILE_PATH="../profiles/dev/client.yml"
+$ export APP_LOG_CONF_FILE="../profiles/dev/log.yml"
+```
+
+配置 go 代理:
+
+```bash
+$ export GOPROXY="http://goproxy.io"
+````
+
+- 运行程序
+
+```bash
+$ go run .
+```
+
+即可在日志中找到打印出的请求结果:
+
+```bash
+response result: &{A001 Alex Stocks 18 2020-10-28 14:52:49.131 +0800 CST}
+```
+
+同样,在运行的 server 中,也可以在日志中找到打印出的请求:
+
+```bash
+req:[]interface {}{"A001"}
+rsp:main.User{Id:"A001", Name:"Alex Stocks", Age:18, Time:time.Time{...}
+```
+
+恭喜!一次基于 dubbo-go 的 rpc 调用成功。
+
+#### 4)常见问题
+
+- 当日志开始部分出现 profiderInit 和 ConsumerInit 均失败的日志,检查环境变量中配置路径是否正确,配置文件是否正确。
+- 当日志中出现 register 失败的情况,一般为向注册中心注册失败,检查注册中心是否开启,检查配置文件中关于 register 的端口是否正确。
+- sample 的默认开启端口为 20000,确保启动前无占用。
+
+### 2. 配置环境变量
+
+```bash
+export APP_LOG_CONF_FILE="../profiles/dev/log.yml"
+export CONF_CONSUMER_FILE_PATH="../profiles/dev/client.yml"
+```
+
+### 3. 服务端源码
+
+#### 1)目录结构
+
+dubbo-go 框架的 example 提供的目录如下:
+
+
+
+- app/ 文件夹下存放源码,可以自己编写环境变量配置脚本 buliddev.sh
+- assembly/ 文件夹下存放不同平台的构建脚本
+- profiles/ 文件夹下存放不同环境的配置文件
+- target/ 文件夹下存放可执行文件
+
+### 2)关键源码
+
+源码放置在 app/ 文件夹下,主要包含 server.go 和 user.go 两个文件,顾名思义,server.go
用于使用框架开启服务以及注册传输协议;user.go 则定义了 rpc-service 结构体,以及传输协议的结构。
+
+- **user.go**
+
+```go
+func init() {
+ config.SetProviderService(new(UserProvider))
+ // ------for hessian2------
+ hessian.RegisterPOJO(&User{})
+}
+type User struct {
+ Id string
+ Name string
+ Age int32
+ Time time.Time
+}
+type UserProvider struct {
+}
+func (u *UserProvider) GetUser(ctx context.Context, req []interface{}) (*User,
error) {
+```
+
+可以看到,user.go 中存在 init 函数,是服务端代码中最先被执行的部分。User 为用户自定义的传输结构体,UserProvider
为用户自定义的 rpc_service;包含一个 rpc 函数,GetUser。当然,用户可以自定义其他的 rpc 功能函数。
+
+在 init 函数中,调用 config 的 SetProviderService 函数,将当前 rpc_service 注册在框架 config 上。
+
+**可以查看 dubbo 官方文档提供的设计图:**
+
+
+
+service 层下面就是 config 层,用户服务会逐层向下注册,最终实现服务端的暴露。
+
+rpc-service 注册完毕之后,调用 hessian 接口注册传输结构体 User。
+
+至此,init 函数执行完毕。
+
+- **server.go**
+
+```go
+// they are necessary:
+// export CONF_PROVIDER_FILE_PATH="xxx"
+// export APP_LOG_CONF_FILE="xxx"
+func main() {
+ hessian.RegisterPOJO(&User{})
+ config.Load()
+ initSignal()
+}
+func initSignal() {
+ signals := make(chan os.Signal, 1)
+ ...
+```
+
+之后执行 main 函数。
+
+main 函数中只进行了两个操作,首先使用 hessian 注册组件将 User 结构体注册(与之前略有重复),从而可以在接下来使用 getty 打解包。
+
+之后调用 config.Load 函数,该函数位于框架 config/config_loader.go
内,这个函数是整个框架服务的启动点,**下面会详细讲这个函数内重要的配置处理过程**。执行完 Load()
函数之后,配置文件会读入框架,之后根据配置文件的内容,将注册的 service 实现到配置结构里,再调用 Export 暴露给特定的
registry,进而开启特定的 service 进行对应端口的 tcp 监听,成功启动并且暴露服务。
+
+最终开启信号监听 initSignal() 优雅地结束一个服务的启动过程。
+
+### 4. 客户端源码
+
+客户端包含 client.go 和 user.go 两个文件,其中 user.go 与服务端完全一致,不再赘述。
+
+- **client.go**
+
+```go
+// they are necessary:
+// export CONF_CONSUMER_FILE_PATH="xxx"
+// export APP_LOG_CONF_FILE="xxx"
+func main() {
+ hessian.RegisterPOJO(&User{})
+ 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)
+ initSignal()
+}
+```
+
+main 函数和服务端也类似,首先将传输结构注册到 hessian 上,再调用 config.Load()
函数。在下文会介绍,客户端和服务端会根据配置类型执行 config.Load() 中特定的函数 loadConsumerConfig() 和
loadProviderConfig(),从而达到“开启服务”、“调用服务”的目的。
+
+加载完配置之后,还是通过实现服务、增加函数 proxy、申请 registry 和 reloadInvoker 指向服务端 ip 等操作,重写了客户端实例
userProvider 的对应函数,这时再通过调用 GetUser 函数,可以直接通过 invoker,调用到已经开启的服务端,实现 rpc 过程。
+
+下面会从 server 端和 client 端两个角度,详细讲解服务启动、registry 注册和调用过程。
+
+### 5. 自定义配置文件(非环境变量)方法
+
+#### 1)服务端自定义配置文件
+
+- var providerConfigStr = `xxxxx`// 配置文件内容,可以参考 log 和
client。在这里你可以定义配置文件的获取方式,比如配置中心,本地文件读取。
+
+> **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
+
+- 在 `config.Load()` 之前设置配置,例如:
+
+```go
+func main() {
+ hessian.RegisterPOJO(&User{})
+ providerConfig := config.ProviderConfig{}
+ yaml.Unmarshal([]byte(providerConfigStr), &providerConfig)
+ config.SetProviderConfig(providerConfig)
+ defaultServerConfig := dubbo.GetDefaultServerConfig()
+ dubbo.SetServerConfig(defaultServerConfig)
+ logger.SetLoggerLevel("warn") // info,warn
+ config.Load()
+ select {
+ }
+}
+```
+
+#### 2)客户端自定义配置文件
+
+- var consumerConfigStr = `xxxxx`// 配置文件内容,可以参考 log 和
clien。在这里你可以定义配置文件的获取方式,比如配置中心,本地文件读取。
+- 在 `config.Load()` 之前设置配置,例如:
+
+```go
+func main() {
+ p := config.ConsumerConfig{}
+ yaml.Unmarshal([]byte(consumerConfigStr), &p)
+ config.SetConsumerConfig(p)
+ defaultClientConfig := dubbo.GetDefaultClientConfig()
+ dubbo.SetClientConf(defaultClientConfig)
+ logger.SetLoggerLevel("warn") // info,warn
+ config.Load()
+
+ user := &User{}
+ err := userProvider.GetUser(context.TODO(), []interface{}{"A001"}, user)
+ if err != nil {
+ log.Print(err)
+ return
+ }
+ log.Print(user)
+}
+```
+
+## Server 端
+
+服务暴露过程涉及到多次原始 rpcService 的封装、暴露,网上其他文章的图感觉太过笼统,在此,简要地绘制了一个用户定义服务的数据流图:
+
+
+
+### 1. 加载配置
+
+#### 1)框架初始化
+
+在加载配置之前,框架提供了很多已定义好的协议、工厂等组件,都会在对应模块 init 函数内注册到 extension 模块上,以供接下来配置文件中进行选用。
+
+其中重要的有:
+
+- **默认函数代理工厂**:common/proxy/proxy_factory/default.go
+
+```go
+func init() {
+ extension.SetProxyFactory("default", NewDefaultProxyFactory)
+}
+```
+
+它的作用是将原始 rpc-service 进行封装,形成 proxy_invoker,更易于实现远程 call 调用,详情可见其 invoke 函数。
+
+- **注册中心注册协议**:
+ registry/protocol/protocol.go
+
+```go
+func init() {
+ extension.SetProtocol("registry", GetProtocol)
+}
+```
+
+它负责将 invoker 暴露给对应注册中心,比如 zk 注册中心。
+
+- **zookeeper 注册协议**:registry/zookeeper/zookeeper.go
+
+```go
+func init() {
+ extension.SetRegistry("zookeeper", newZkRegistry)
+}
+```
+
+它合并了 base_resiger,负责在服务暴露过程中,将服务注册在 zookeeper 注册器上,从而为调用者提供调用方法。
+
+- **dubbo 传输协议**:protocol/dubbo/dubbo.go
+
+```go
+func init() {
+ extension.SetProtocol(DUBBO, GetProtocol)
+}
+```
+
+它负责监听对应端口,将具体的服务暴露,并启动对应的事件 handler,将远程调用的 event 事件传递到 invoker 内部,调用本地 invoker
并获得执行结果返回。
+
+- **filter 包装调用链协议**:protocol/protocolwrapper/protocol_filter_wrapper.go
+
+```go
+func init() {
+ extension.SetProtocol(FILTER, GetProtocol)
+}
+```
+
+它负责在服务暴露过程中,将代理 invoker 打包,通过配置好的 filter 形成调用链,并交付给 dubbo 协议进行暴露。
+
+上述提前注册好的框架已实现的组件,在整个服务暴露调用链中都会用到,会根据配置取其所需。
+
+#### 2)配置文件
+
+服务端需要的重要配置有三个字段:services、protocols、registries。
+
+profiles/dev/server.yml:
+
+```yaml
+registries :
+ "demoZk":
+ protocol: "zookeeper"
+ timeout : "3s"
+ address: "127.0.0.1:2181"
+services:
+ "UserProvider":
+ # 可以指定多个registry,使用逗号隔开;不指定默认向所有注册中心注册
+ registry: "demoZk"
+ protocol : "dubbo"
+ # 相当于dubbo.xml中的interface
+ interface : "com.ikurento.user.UserProvider"
+ loadbalance: "random"
+ warmup: "100"
+ cluster: "failover"
+ methods:
+ - name: "GetUser"
+ retries: 1
+ loadbalance: "random"
+protocols:
+ "dubbo":
+ name: "dubbo"
+ port: 20000
+```
+
+其中 service 指定了要暴露的 rpc-service
名("UserProvider)、暴露的协议名("dubbo")、注册的协议名("demoZk")、暴露的服务所处的
interface、负载均衡策略、集群失败策略及调用的方法等等。
+
+其中,中间服务的协议名需要和 registries 下的 mapkey 对应,暴露的协议名需要和 protocols 下的 mapkey 对应。
+
+可以看到上述例子中,使用了 dubbo 作为暴露协议,使用了 zookeeper 作为中间注册协议,并且给定了端口。如果 zk
需要设置用户名和密码,也可以在配置中写好。
+
+#### 3)配置文件的读入和检查
+
+> config/config_loader.go:: Load()
+
+在上述 example 的 main 函数中,有 config.Load() 函数的直接调用,该函数执行细节如下:
+
+```go
+// Load Dubbo Init
+func Load() {
+ // init router
+ initRouter()
+ // init the global event dispatcher
+ extension.SetAndInitGlobalDispatcher(GetBaseConfig().EventDispatcherType)
+ // start the metadata report if config set
+ if err := startMetadataReport(GetApplicationConfig().MetadataType,
GetBaseConfig().MetadataReportConfig); err != nil {
+ logger.Errorf("Provider starts metadata report error, and the error is
{%#v}", err)
+ return
+ }
+ // reference config
+ loadConsumerConfig()
+ // service config
+ loadProviderConfig()
+ // init the shutdown callback
+ GracefulShutdownInit()
+}
+```
+
+在本文中,我们重点关心 loadConsumerConfig() 和 loadProviderConfig() 两个函数。
+
+对于 provider 端,可以看到 loadProviderConfig() 函数代码如下:
+
+
+
+前半部分是配置的读入和检查,进入 for 循环后,是单个 service 的暴露起始点。
+
+前面提到,在配置文件中已经写好了要暴露的 service 的种种信息,比如服务名、interface 名、method 名等等。在图中 for
循环内,会将所有 service 的服务依次实现。
+
+for 循环的第一行,根据 key 调用 GetProviderService 函数,拿到注册的 rpcService 实例,这里对应上述提到的 init
函数中,用户手动注册的自己实现的 rpc-service 实例:
+
+
+
+这个对象也就成为了 for 循环中的 rpcService 变量,将这个对象注册通过 Implement 函数写到 sys(ServiceConfig
类型)上,设置好 sys 的 key 和协议组,最终调用了 sys 的 Export 方法。
+
+此处对应流程图的部分:
+
+
+
+至此,框架配置结构体已经拿到了所有 service 有关的配置,以及用户定义好的 rpc-service 实例,它触发了 Export
方法,旨在将自己的实例暴露出去。这是 Export 调用链的起始点。
+
+### 2. 原始 service 封装入 proxy_invoker
+
+> config/service_config.go :: Export()
+
+接下来进入 ServiceConfig.Export() 函数.
+
+这个函数进行了一些细碎的操作,比如为不同的协议分配随机端口,如果指定了多个中心注册协议,则会将服务通过多个中心注册协议的 registryProtocol
暴露出去,我们只关心对于一个注册协议是如何操作的。还有一些操作比如生成调用 url 和注册 url,用于为暴露做准备。
+
+#### 1)首先通过配置生成对应 registryUrl 和 serviceUrl
+
+
+
+registryUrl 是用来向中心注册组件发起注册请求的,对于 zookeeper 的话,会传入其 ip 和端口号,以及附加的用户名密码等信息。
+
+这个 regUrl 目前只存有注册(zk)相关信息,后续会补写入 ServiceIvk,即服务调用相关信息,里面包含了方法名,参数等...
+
+#### 2)对于一个注册协议,将传入的 rpc-service 实例注册在 common.ServiceMap
+
+
+
+这个 Register 函数将服务实例注册了两次,一次是以 Interface 为 key 写入接口服务组内,一次是以 interface 和 proto
为 key 写入特定的一个唯一的服务。
+
+后续会从 common.Map 里面取出来这个实例。
+
+#### 3)获取默认代理工厂,将实例封装入代理 invoker
+
+```go
+// 拿到一个proxyInvoker,这个invoker的url是传入的regUrl,这个地方将上面注册的service实例封装成了invoker
+// 这个GetProxyFactory返回的默认是common/proxy/proxy_factory/default.go
+// 这个默认工厂调用GetInvoker获得默认的proxyInvoker,保存了当前注册url
+invoker :=
extension.GetProxyFactory(providerConfig.ProxyFactory).GetInvoker(*regUrl)
+// 暴露出来 生成exporter,开启tcp监听
+// 这里就该跳到registry/protocol/protocol.go registryProtocol
调用的Export,将当前proxyInvoker导出
+exporter = c.cacheProtocol.Export(invoker)
+```
+
+这一步的 GetProxyFactory("default") 方法获取默认代理工厂,通过传入上述构造的 regUrl,将 url 封装入代理
invoker。
+
+可以进入 common/proxy/proxy_factory/default.go::ProxyInvoker.Invoke() 函数里,看到对于
common.Map 取用为 svc 的部分,以及关于 svc 对应 Method 的实际调用 Call 的函数如下:
+
+
+
+到这里,上面 GetInvoker(*regUrl) 返回的 invoker 即为 proxy_invoker,它封装好了用户定义的
rpc_service,并将具体的调用逻辑封装入了 Invoke 函数内。
+
+> 为什么使用 Proxy_invoker 来调用?
+>
+> 通过这个 proxy_invoke 调用用户的功能函数,调用方式将更加抽象化,可以在代码中看到,通过 ins 和 outs
来定义入参和出参,将整个调用逻辑抽象化为 invocation 结构体,而将具体的函数名的选择、参数向下传递和 reflect 反射过程封装在 invoke
函数内,这样的设计更有利于之后远程调用。个人认为这是 dubbo Invoke 调用链的设计思想。
+>
+> 至此,实现了图中对应的部分:
+
+
+
+### 3. registry 协议在 zkRegistry 上暴露上面的 proxy_invoker
+
+上面,我们执行到了 exporter = c.cacheProtocol.Export(invoker)。
+
+这里的 cacheProtocol 为一层缓存设计,对应到原始的 demo 上,这里是默认实现好的 registryProtocol。
+
+> registry/protocol/protocol.go:: Export()
+
+这个函数内构造了多个 EventListener,非常有 java 的设计感。
+
+我们只关心服务暴露的过程,先忽略这些监听器。
+
+#### 1)获取注册 url 和服务 url
+
+
+
+#### 2)获取注册中心实例 zkRegistry
+
+
+
+一层缓存操作,如果 cache 没有需要从 common 里面重新拿 zkRegistry。
+
+#### 3)zkRegistry 调用 Registry 方法,在 zookeeper 上注册 dubboPath
+
+上述拿到了具体的 zkRegistry 实例,该实例的定义在:registry/zookeeper/registry.go。
+
+
+
+该结构体组合了 registry.BaseRegistry 结构,base 结构定义了注册器基础的功能函数,比如 Registry、Subscribe
等,但在这些默认定义的函数内部,还是会调用 facade 层(zkRegistry
层)的具体实现函数,这一设计模型能在保证已有功能函数不需要重复定义的同时,引入外层函数的实现,类似于结构体继承却又复用了代码。这一设计模式值得学习。
+
+我们查看上述 registry/protocol/protocol.go:: Export() 函数,直接调用了:
+
+```go
+// 1. 通过zk注册器,调用Register()函数,将已有@root@rawurl注册到zk上
+ err := reg.Register(*registeredProviderUrl)
+```
+
+将已有 RegistryUrl 注册到了 zkRegistry 上。
+
+这一步调用了 baseRegistry 的 Register 函数,进而调用 zkRegister 的 DoRegister 函数,进而调用:
+
+
+
+在这个函数里,将对应 root 创造一个新的节点。
+
+
+
+并且写入具体 node 信息,node 为 url 经过 encode 的结果,**包含了服务端的调用方式。**
+
+这部分的代码较为复杂,具体可以看 baseRegistry 的 processURL()
函数:http://t.tb.cn/6Xje4bijnsIDNaSmyPc4Ot。
+
+至此,将服务端调用 url 注册到了 zookeeper 上,而客户端如果想获取到这个 url,只需要传入特定的 dubboPath,向 zk
请求即可。目前 client 是可以获取到访问方式了,但服务端的特定服务还没有启动,还没有开启特定协议端口的监听,这也是
registry/protocol/protocol.go:: Export() 函数接下来要做的事情。
+
+#### 4)proxy_invoker 封装入 wrapped_invoker,得到 filter 调用链
+
+```go
+ // invoker封装入warppedInvoker
+ wrappedInvoker := newWrappedInvoker(invoker, providerUrl)
+ // 经过为invoker增加filter调用链,再使用dubbo协议Export,开启service并且返回了Exporter 。
+ // export_1
+ cachedExporter =
extension.GetProtocol(protocolwrapper.FILTER).Export(wrappedInvoker)
+```
+
+新建一个 WrappedInvoker,用于之后链式调用。
+
+拿到提前实现并注册好的 ProtocolFilterWrapper,调用 Export 方法,进一步暴露。
+
+> protocol/protocolwrapped/protocol_filter_wrapper.go:Export()
+
+
+
+> protocol/protocolwrapped/protocol_filter_wrapper.go:buildInvokerChain
+
+
+
+可见,根据配置的内容,通过链式调用的构造,将 proxy_invoker 层层包裹在调用链的最底部,最终返回一个调用链 invoker。
+
+对应图中部分:
+
+
+
+至此,我们已经拿到 filter 调用链,期待将这个 chain 暴露到特定端口,用于相应请求事件。
+
+#### 5)通过 dubbo 协议暴露 wrapped_invoker
+
+> protocol/protocolwrapped/protocol_filter_wrapper.go:Export()
+
+```go
+// 通过dubbo协议Export dubbo_protocol调用的 export_2
+ return pfw.protocol.Export(invoker)
+```
+
+回到上述 Export 函数的最后一行,调用了 dubboProtocol 的 Export 方法,将上述 chain 真正暴露。
+
+该 Export 方法的具体实现在:protocol/dubbo/dubbo_protocol.go: Export()。
+
+
+
+这一函数做了两个事情:构造触发器、启动服务。
+
+- 将传入的 Invoker 调用 chain 进一步封装,封装成一个 exporter,再将这个 export 放入 map 保存。**注意!这里把
exporter 放入了 SetExporterMap中,在下面服务启动的时候,会以注册事件监听器的形式将这个 exporter 取出!**
+- 调用 dubboProtocol 的 openServer 方法,开启一个针对特定端口的监听。
+
+
+
+如上图所示,一个 Session 被传入,开启对应端口的事件监听。
+
+至此构造出了 exporter,完成图中部分:
+
+
+
+### 4. 注册触发动作
+
+上述只是启动了服务,但还没有看到触发事件的细节,点进上面的 s.newSession 可以看到,dubbo 协议为一个 getty 的 session
默认使用了如下配置:
+
+
+
+其中很重要的一个配置是 EventListener,传入的是 dubboServer 的默认 rpcHandler。
+
+> protocol/dubbo/listener.go:OnMessage()
+
+rpcHandler 有一个实现好的 OnMessage 函数,根据 getty 的 API,当 client 调用该端口时,会触发 OnMessage。
+
+```go
+// OnMessage notified when RPC server session got any message in connection
+func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) {
+```
+
+这一函数实现了在 getty session 接收到 rpc 调用后的一系列处理:
+
+- 传入包的解析
+
+
+
+- 根据请求包构造请求 url
+
+
+
+- 拿到对应请求 key,找到要被调用的 exporter
+
+
+
+- 拿到对应的 Invoker
+
+
+
+- 构造 invocation
+
+
+
+- 调用
+
+
+
+- 返回
+
+
+
+整个被调过程一气呵成。实现了从 getty.Session 的调用事件,到经过层层封装的 invoker 的调用。
+
+至此,一次 rpc 调用得以正确返回。
+
+## 小结
+
+- **关于 Invoker 的层层封装**
+
+能把一次调用抽象成一次 invoke;能把一个协议抽象成针对 invoke 的封装;能把针对一次 invoke 所做出的特定改变封装到 invoke
函数内部,可以降低模块之间的耦合性。层层封装逻辑更加清晰。
+
+- **关于 URL 的抽象**
+
+关于 dubbo 的统一化请求对象 URL 的极度抽象是之前没有见过的...
个人认为这样封装能保证请求参数列表的简化和一致。但在开发的过程中,滥用极度抽象的接口可能造成... debug
的困难?以及不知道哪些字段是当前已经封装好的,哪些字段是无用的。
+
+- **关于协议的理解**
+
+之前理解的协议还是太过具体化了,而关于 dubbo-go 对于 dubboProtocol 的协议,我认为是基于 getty
的进一步封装,它定义了客户端和服务端,对于 getty 的 session
应该有哪些特定的操作,从而保证主调和被调的协议一致性,而这种保证也是一种协议的体现,是由 dubbo 协议来规范的。
+
+如果你有任何疑问,欢迎钉钉扫码加入交流群:钉钉群号 23331795!
+
+> 作者简介 **李志信** (GitHubID LaurenceLiZhixin),中山大学软件工程专业在校学生,擅长使用 Java/Go
语言,专注于云原生和微服务等技术方向
\ No newline at end of file
diff --git a/content/zh/blog/news/dubbo-go-nacos.md
b/content/zh/blog/news/dubbo-go-nacos.md
new file mode 100644
index 0000000..9f4706f
--- /dev/null
+++ b/content/zh/blog/news/dubbo-go-nacos.md
@@ -0,0 +1,116 @@
+---
+title: "解构 Dubbo-go 的核心注册引擎 Nacos"
+linkTitle: "解构 Dubbo-go 的核心注册引擎 Nacos"
+date: 2021-01-14
+description: >
+ dubbo-go 选择 Nacos 作为注册中心的原因
+---
+
+近几年,随着Go语言社区逐渐发展和壮大,越来越多的公司开始尝试采用Go搭建微服务体系,也涌现了一批Go的微服务框架,如go-micro、go-kit、Dubbo-go等,跟微服务治理相关的组件也逐渐开始在Go生态发力,如Sentinel、Hystrix等都推出了Go语言版本,而作为微服务框架的核心引擎--注册中心,也是必不可缺少的组件,市面已经有多款注册中心支持Go语言,应该如何选择呢?我们可以对目前主流的支持Go语言的注册中心做个对比。
+
+
+
+根据上表的对比我们可以从以下几个维度得出结论:
+
+- 生态:各注册中心对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接口订阅该应用的服务实例变化,并对的实例发起服务调用。
+
+
+
+## 服务模型
+
+图3是我们Dubbo-go的应用维度服务发现模型,主要有服务和实例两个层级关系,服务实例的属性主要包含实例Id、主机地址、服务端口、激活状态和元数据。图4为Nacos的服务分级存储模型,包含服务、集群和实例三个层次。两者对比,多了一个集群维度的层级,而且实例属性信息能够完全匹配。所以在Dubbo-go将应用服务实例注册到Nacos时,我们只需要将集群设置为默认集群,再填充服务和实例的相关属性,即可完成服务模型上的匹配。此外Nacos可以将服务注册到不同的Namespace下,实现多租户的隔离。
+
+
+
+!
+
+
+## 服务实例心跳维持
+
+Dubbo-go的Provider在向Nacos注册应用服务实例信息后,需要主动上报心跳,让Nacos服务端感知实例的存活与否,以判断是否将该节点从实例列表中移除。维护心跳的工作是在Nacos-SDK-go完成的,从图5代码中可以看到,当Dubbo-go调用RegisterInstance注册一个服务实例时,SDK除了调用Nacos的Register
API之外,还会调用AddBeatInfo,将服务实例信息添加到本地缓存,通过后台协程定期向Nacos发送服务实例信息,保持心跳。当服务下线时,可以通过调用DeRegisterInstance执行反注册,并移除本地的心跳保持任务,Nacos实例列表中也会将该实例移除。
+
+
+
+# 订阅服务实例变化
+
+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服务端推送失败后,仍能感知到变化。
+
+
+
+此外Nacos-SDK-go还支持推空保护,当Nacos推送的实例列表为空时,不更新本地缓存,也不通知Dubbo-go变更,避免Consumer无可用实例调用,造成故障。同时,SDK还支持服务实例信息本地持久化存储,可以保证在Nacos服务故障过程中,Consumer重启也能获取到可用实例,具备容灾效果。
+
+# 范例实践
+
+## 环境准备
+
+dubbo-go
samples代码下载:https://github.com/apache/dubbo-samples/tree/master/golang,基于Nacos注册中心的应用级服务发现的hello
world代码目录在 registry/servicediscovery/nacos。
+
+
+
+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
+
+## 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=)
+
+看到,应用user-info-server已经注册成功。
+
+
+
+## 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端成功。
+
+
+
+> 作者:李志鹏 Github账号:Lzp0412,Nacos-SDK-go作者,Apache/Dubbo-go
Contributor。现就职于阿里云云原生应用平台,主要参与服务发现、CoreDNS、ServiceMesh相关工作,负责推动Nacos Go微服务生态建设。
\ No newline at end of file
diff --git a/content/zh/blog/releases/dubbo-go-hessian2-1.7.md
b/content/zh/blog/releases/dubbo-go-hessian2-1.7.md
new file mode 100644
index 0000000..84112df
--- /dev/null
+++ b/content/zh/blog/releases/dubbo-go-hessian2-1.7.md
@@ -0,0 +1,246 @@
+---
+title: "dubbo-go-hessian2 v1.7.0"
+linkTitle: "dubbo-go-hessian2 v1.7.0"
+date: 2021-01-14
+description: >
+ What's new in Dubbo-go-hessian2 v1.7.0
+---
+
+Dubbo-go-hessian2 v1.7.0已发布,详见
https://github.com/apache/dubbo-go-hessian2/releases/tag/v1.7.0,
以下对这次更新内容进行详细整理。
+
+另外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)
+
+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)
+
+golang中增加一个java中Exception对象的序列化输出方法:
+
+```go
+func JavaException() []byte {
+ e := hessian.NewEncoder()
+ exception := java_exception.NewException("java_exception")
+ e.Encode(exception)
+ return e.Buffer()
+}
+```
+
+在output/output.go 提供调用入口:添加如下函数初始化声明
+
+```go
+func init() {
+ funcMap["JavaException"] = testfuncs.JavaException
+}
+```
+
+java代码中增加调用go方法序列化结果:
+
+**说明**: Assert.assertEquals 不能直接比较Exception对象是否相等
+
+```java
+ /**
+ * 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)
+
+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 提供调用入口:添加函数初始化声明
+
+```go
+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)
+
+为了更好的测试验证hessian库,原来已经支持在golang中测试java的序列化数据,现在增加在java中测试golang的序列化数据,实现双向测试验证。
+
+golang中增加序列化输出方法:
+
+```go
+func HelloWorldString() []byte {
+ e := hessian.NewEncoder()
+ e.Encode("hello world")
+ return e.Buffer()
+}
+```
+
+将该方法注册到output/output.go中
+
+```go
+ // 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方法序列化结果:
+
+```java
+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)
+
+增加了 java 类 java.sql.Time, java.sql.Date 支持,分别对应到hessian.Time 和 hessian.Date,
详见 https://github.com/apache/dubbo-go-hessian2/pull/219/files。
+
+## 2. Enhancement
+
+### 2.1 Export function EncNull.
[#225](https://github.com/apache/dubbo-go-hessian2/pull/225)
+
+开放 hessian.EncNull 方法,以便用户特定情况下使用。
+
+## 3. Bugfixes
+
+### 3.1 fix enum encode error in request.
[#203](https://github.com/apache/dubbo-go-hessian2/pull/203)
+
+原来在 dubbo request 对象中没有判断 enum 类型的情况,此pr增加了判断是不是POJOEnum类型。详见
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)
+
+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)
+
+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)
+
+这个 PR 解决了Duration对象中字段错误定义,原来是"second/nano", 应该是"seconds/nanos"。
+
+同时改善了测试验证数据。之前使用0作为int字段的测试数据,这是不准确的,因为int类型默认值就是0.
\ No newline at end of file
diff --git a/static/imgs/blog/dubbo-go/code1/p1.png
b/static/imgs/blog/dubbo-go/code1/p1.png
new file mode 100644
index 0000000..181e43f
Binary files /dev/null and b/static/imgs/blog/dubbo-go/code1/p1.png differ
diff --git a/static/imgs/blog/dubbo-go/code1/p10.png
b/static/imgs/blog/dubbo-go/code1/p10.png
new file mode 100644
index 0000000..7b1e51d
Binary files /dev/null and b/static/imgs/blog/dubbo-go/code1/p10.png differ
diff --git a/static/imgs/blog/dubbo-go/code1/p11.png
b/static/imgs/blog/dubbo-go/code1/p11.png
new file mode 100644
index 0000000..033fcf6
Binary files /dev/null and b/static/imgs/blog/dubbo-go/code1/p11.png differ
diff --git a/static/imgs/blog/dubbo-go/code1/p12.png
b/static/imgs/blog/dubbo-go/code1/p12.png
new file mode 100644
index 0000000..8e2c05c
Binary files /dev/null and b/static/imgs/blog/dubbo-go/code1/p12.png differ
diff --git a/static/imgs/blog/dubbo-go/code1/p13.png
b/static/imgs/blog/dubbo-go/code1/p13.png
new file mode 100644
index 0000000..a6d9cbf
Binary files /dev/null and b/static/imgs/blog/dubbo-go/code1/p13.png differ
diff --git a/static/imgs/blog/dubbo-go/code1/p14.png
b/static/imgs/blog/dubbo-go/code1/p14.png
new file mode 100644
index 0000000..3ad1fad
Binary files /dev/null and b/static/imgs/blog/dubbo-go/code1/p14.png differ
diff --git a/static/imgs/blog/dubbo-go/code1/p15.png
b/static/imgs/blog/dubbo-go/code1/p15.png
new file mode 100644
index 0000000..64a55d1
Binary files /dev/null and b/static/imgs/blog/dubbo-go/code1/p15.png differ
diff --git a/static/imgs/blog/dubbo-go/code1/p16.png
b/static/imgs/blog/dubbo-go/code1/p16.png
new file mode 100644
index 0000000..38830ac
Binary files /dev/null and b/static/imgs/blog/dubbo-go/code1/p16.png differ
diff --git a/static/imgs/blog/dubbo-go/code1/p17.png
b/static/imgs/blog/dubbo-go/code1/p17.png
new file mode 100644
index 0000000..3fd8d72
Binary files /dev/null and b/static/imgs/blog/dubbo-go/code1/p17.png differ
diff --git a/static/imgs/blog/dubbo-go/code1/p18.png
b/static/imgs/blog/dubbo-go/code1/p18.png
new file mode 100644
index 0000000..962ba41
Binary files /dev/null and b/static/imgs/blog/dubbo-go/code1/p18.png differ
diff --git a/static/imgs/blog/dubbo-go/code1/p19.png
b/static/imgs/blog/dubbo-go/code1/p19.png
new file mode 100644
index 0000000..2da4e07
Binary files /dev/null and b/static/imgs/blog/dubbo-go/code1/p19.png differ
diff --git a/static/imgs/blog/dubbo-go/code1/p2.png
b/static/imgs/blog/dubbo-go/code1/p2.png
new file mode 100644
index 0000000..30f6007
Binary files /dev/null and b/static/imgs/blog/dubbo-go/code1/p2.png differ
diff --git a/static/imgs/blog/dubbo-go/code1/p20.png
b/static/imgs/blog/dubbo-go/code1/p20.png
new file mode 100644
index 0000000..21a7c1a
Binary files /dev/null and b/static/imgs/blog/dubbo-go/code1/p20.png differ
diff --git a/static/imgs/blog/dubbo-go/code1/p21.png
b/static/imgs/blog/dubbo-go/code1/p21.png
new file mode 100644
index 0000000..cb75e8b
Binary files /dev/null and b/static/imgs/blog/dubbo-go/code1/p21.png differ
diff --git a/static/imgs/blog/dubbo-go/code1/p22.png
b/static/imgs/blog/dubbo-go/code1/p22.png
new file mode 100644
index 0000000..0b7e33d
Binary files /dev/null and b/static/imgs/blog/dubbo-go/code1/p22.png differ
diff --git a/static/imgs/blog/dubbo-go/code1/p23.png
b/static/imgs/blog/dubbo-go/code1/p23.png
new file mode 100644
index 0000000..293566e
Binary files /dev/null and b/static/imgs/blog/dubbo-go/code1/p23.png differ
diff --git a/static/imgs/blog/dubbo-go/code1/p24.png
b/static/imgs/blog/dubbo-go/code1/p24.png
new file mode 100644
index 0000000..92bbee9
Binary files /dev/null and b/static/imgs/blog/dubbo-go/code1/p24.png differ
diff --git a/static/imgs/blog/dubbo-go/code1/p25.png
b/static/imgs/blog/dubbo-go/code1/p25.png
new file mode 100644
index 0000000..2db518c
Binary files /dev/null and b/static/imgs/blog/dubbo-go/code1/p25.png differ
diff --git a/static/imgs/blog/dubbo-go/code1/p26.png
b/static/imgs/blog/dubbo-go/code1/p26.png
new file mode 100644
index 0000000..61389a8
Binary files /dev/null and b/static/imgs/blog/dubbo-go/code1/p26.png differ
diff --git a/static/imgs/blog/dubbo-go/code1/p27.png
b/static/imgs/blog/dubbo-go/code1/p27.png
new file mode 100644
index 0000000..16dd437
Binary files /dev/null and b/static/imgs/blog/dubbo-go/code1/p27.png differ
diff --git a/static/imgs/blog/dubbo-go/code1/p28.png
b/static/imgs/blog/dubbo-go/code1/p28.png
new file mode 100644
index 0000000..a9bbac0
Binary files /dev/null and b/static/imgs/blog/dubbo-go/code1/p28.png differ
diff --git a/static/imgs/blog/dubbo-go/code1/p29.png
b/static/imgs/blog/dubbo-go/code1/p29.png
new file mode 100644
index 0000000..567b469
Binary files /dev/null and b/static/imgs/blog/dubbo-go/code1/p29.png differ
diff --git a/static/imgs/blog/dubbo-go/code1/p3.png
b/static/imgs/blog/dubbo-go/code1/p3.png
new file mode 100644
index 0000000..09967ad
Binary files /dev/null and b/static/imgs/blog/dubbo-go/code1/p3.png differ
diff --git a/static/imgs/blog/dubbo-go/code1/p4.png
b/static/imgs/blog/dubbo-go/code1/p4.png
new file mode 100644
index 0000000..094ae5f
Binary files /dev/null and b/static/imgs/blog/dubbo-go/code1/p4.png differ
diff --git a/static/imgs/blog/dubbo-go/code1/p5.png
b/static/imgs/blog/dubbo-go/code1/p5.png
new file mode 100644
index 0000000..8ff1a93
Binary files /dev/null and b/static/imgs/blog/dubbo-go/code1/p5.png differ
diff --git a/static/imgs/blog/dubbo-go/code1/p6.png
b/static/imgs/blog/dubbo-go/code1/p6.png
new file mode 100644
index 0000000..e36eee4
Binary files /dev/null and b/static/imgs/blog/dubbo-go/code1/p6.png differ
diff --git a/static/imgs/blog/dubbo-go/code1/p7.png
b/static/imgs/blog/dubbo-go/code1/p7.png
new file mode 100644
index 0000000..d4b7906
Binary files /dev/null and b/static/imgs/blog/dubbo-go/code1/p7.png differ
diff --git a/static/imgs/blog/dubbo-go/code1/p8.png
b/static/imgs/blog/dubbo-go/code1/p8.png
new file mode 100644
index 0000000..9dd516f
Binary files /dev/null and b/static/imgs/blog/dubbo-go/code1/p8.png differ
diff --git a/static/imgs/blog/dubbo-go/code1/p9.png
b/static/imgs/blog/dubbo-go/code1/p9.png
new file mode 100644
index 0000000..57e97a0
Binary files /dev/null and b/static/imgs/blog/dubbo-go/code1/p9.png differ
diff --git a/static/imgs/blog/dubbo-go/nacos/p1.png
b/static/imgs/blog/dubbo-go/nacos/p1.png
new file mode 100644
index 0000000..dafc150
Binary files /dev/null and b/static/imgs/blog/dubbo-go/nacos/p1.png differ
diff --git a/static/imgs/blog/dubbo-go/nacos/p2.png
b/static/imgs/blog/dubbo-go/nacos/p2.png
new file mode 100644
index 0000000..85b3997
Binary files /dev/null and b/static/imgs/blog/dubbo-go/nacos/p2.png differ
diff --git a/static/imgs/blog/dubbo-go/nacos/p3.png
b/static/imgs/blog/dubbo-go/nacos/p3.png
new file mode 100644
index 0000000..a05602b
Binary files /dev/null and b/static/imgs/blog/dubbo-go/nacos/p3.png differ
diff --git a/static/imgs/blog/dubbo-go/nacos/p4.png
b/static/imgs/blog/dubbo-go/nacos/p4.png
new file mode 100644
index 0000000..11ada49
Binary files /dev/null and b/static/imgs/blog/dubbo-go/nacos/p4.png differ
diff --git a/static/imgs/blog/dubbo-go/nacos/p5.png
b/static/imgs/blog/dubbo-go/nacos/p5.png
new file mode 100644
index 0000000..e66f3bd
Binary files /dev/null and b/static/imgs/blog/dubbo-go/nacos/p5.png differ
diff --git a/static/imgs/blog/dubbo-go/nacos/p6.png
b/static/imgs/blog/dubbo-go/nacos/p6.png
new file mode 100644
index 0000000..a02d9c7
Binary files /dev/null and b/static/imgs/blog/dubbo-go/nacos/p6.png differ
diff --git a/static/imgs/blog/dubbo-go/nacos/p7.png
b/static/imgs/blog/dubbo-go/nacos/p7.png
new file mode 100644
index 0000000..4d97fcf
Binary files /dev/null and b/static/imgs/blog/dubbo-go/nacos/p7.png differ
diff --git a/static/imgs/blog/dubbo-go/nacos/p8.png
b/static/imgs/blog/dubbo-go/nacos/p8.png
new file mode 100644
index 0000000..1a3e683
Binary files /dev/null and b/static/imgs/blog/dubbo-go/nacos/p8.png differ
diff --git a/static/imgs/blog/dubbo-go/nacos/p9.png
b/static/imgs/blog/dubbo-go/nacos/p9.png
new file mode 100644
index 0000000..a5e7503
Binary files /dev/null and b/static/imgs/blog/dubbo-go/nacos/p9.png differ