<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Go on Apache Dubbo</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/tags/go/</link><description>Recent content in Go on Apache Dubbo</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Sat, 15 Nov 2025 11:38:51 +0800</lastBuildDate><atom:link href="https://deploy-preview-3199--dubbo.netlify.app/zh-cn/tags/go/index.xml" rel="self" type="application/rss+xml"/><item><title>谈谈Pixiu的Filter</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2022/02/19/%E8%B0%88%E8%B0%88pixiu%E7%9A%84filter/</link><pubDate>Sat, 19 Feb 2022 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2022/02/19/%E8%B0%88%E8%B0%88pixiu%E7%9A%84filter/</guid><description>&lt;h2 id="filter的生命周期">&lt;strong>Filter的生命周期&lt;/strong>&lt;/h2>
&lt;p>Pixiu作为一个面向云原生的gateway，通过简单的配置即可代理Http to Dubbo 2、Tripe甚至是Spring Cloud的请求。那Filter是怎样运行的呢？&lt;/p>
&lt;p>首先&lt;strong>Filter Plugin&lt;/strong>向&lt;strong>Filter Manager&lt;/strong>注册自己**，&lt;strong>然后&lt;/strong>Filter Manager&lt;strong>根据配置创建好&lt;/strong>Filter Factory&lt;strong>并持有它们，等待请求来临时，&lt;strong>Manager&lt;/strong>创建一个一次性的用于此次请求的Filter Chain，然后利用&lt;/strong>Factory&lt;strong>创建好&lt;/strong>Decode/Encode Filter&lt;strong>并把它们加入链中，然后按照顺序去运行Decode Filter，然后去请求&lt;/strong>Upstream**，拿到Response再反向运行Encode Filter，让Filter可以访问到Response。&lt;/p>
&lt;p>几个关键的概念：&lt;/p>
&lt;p>&lt;strong>Filter Manager&lt;/strong>&lt;/p>
&lt;blockquote>
&lt;p>Filter的Manger。。。&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// FilterManager manage filters
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">type&lt;/span> FilterManager &lt;span style="color:#268bd2">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> filters &lt;span style="color:#268bd2">map&lt;/span>[&lt;span style="color:#dc322f">string&lt;/span>]HttpFilterFactory
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> filtersArray []&lt;span style="color:#719e07">*&lt;/span>HttpFilterFactory
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Filter Plugin&lt;/strong>：定义了Filter的（唯一的）名字和描述如何去创建一个Filter Factory。&lt;/p>
&lt;blockquote>
&lt;p>其实结合Filter Factory的定义，可以认为Plugin是Filter Factory的Factory&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// HttpFilterPlugin describe plugin
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>HttpFilterPlugin &lt;span style="color:#268bd2">interface&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Kind returns the unique kind name to represent itself.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">Kind&lt;/span>() &lt;span style="color:#dc322f">string&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// CreateFilterFactory return the filter factory
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">CreateFilterFactory&lt;/span>() (HttpFilterFactory, &lt;span style="color:#dc322f">error&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Filter Factory&lt;/strong>：定义了Filter自身的配置，并且在请求来临时创建真实的Filter并把它添加到FilterChain中&lt;/p>
&lt;blockquote>
&lt;ul>
&lt;li>Config() 的目的是能让Filter Manager能够有机会把配置交给Factory（此时golang泛型还没有落地）&lt;/li>
&lt;li>Apply() 在配置被注入到Factory后，有机会对config做一些检查和提前做一些初始化的工作&lt;/li>
&lt;li>PrepareFilterChain() 创建Filter并加入Filter Chain&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// HttpFilterFactory describe http filter
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>HttpFilterFactory &lt;span style="color:#268bd2">interface&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Config Expose the config so that Filter Manger can inject it, so it must be a pointer
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">Config&lt;/span>() &lt;span style="color:#268bd2">interface&lt;/span>{}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// Apply After the config is injected, check it or make it to default
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">Apply&lt;/span>() &lt;span style="color:#dc322f">error&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// PrepareFilterChain create filter and append it to FilterChain
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">//
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// Be Careful !!! Do not pass the Factory&amp;#39;s config pointer to the Filter instance,
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// Factory&amp;#39;s config may be updated by FilterManager
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#268bd2">PrepareFilterChain&lt;/span>(ctx &lt;span style="color:#719e07">*&lt;/span>http.HttpContext, chain FilterChain) &lt;span style="color:#dc322f">error&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Decode/Encode Filter：&lt;strong>Filter分为两个部分，&lt;strong>Decode&lt;/strong>在实际请求&lt;/strong>Upstream&lt;/strong>之前，所以可以做一些鉴权、限流，把请求在gateway层拦截掉。&lt;strong>Eecode&lt;/strong>则运行在获得&lt;strong>Upstream&lt;/strong>的Response之后，所以可以对返回Log甚至修改Response。&lt;/p></description></item><item><title>Dubbo 跨语言调用神兽：dubbo-go-pixiu</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/08/25/dubbo-%E8%B7%A8%E8%AF%AD%E8%A8%80%E8%B0%83%E7%94%A8%E7%A5%9E%E5%85%BDdubbo-go-pixiu/</link><pubDate>Wed, 25 Aug 2021 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/08/25/dubbo-%E8%B7%A8%E8%AF%AD%E8%A8%80%E8%B0%83%E7%94%A8%E7%A5%9E%E5%85%BDdubbo-go-pixiu/</guid><description>&lt;h2 id="pixiu-是什么">Pixiu 是什么&lt;/h2>
&lt;p>在回答 Pixiu 是什么之前，我们简单解释一下 Dubbo 是什么。Dubbo 是一个开源的高性能 RPC 框架，有着丰富的服务治理能力以及优秀的扩展能力。Dubbo 更扩展出 Dubbo-go【1】，为用户提供了 Golang 的 Dubbo 解决方案，打通了两种语言之间的隔阂，使 Dubbo 更加贴近云原生。&lt;/p>
&lt;p>Dubbo-go 作为 Golang 服务，实现与 Dubbo 服务之间的相互调用。然而，在日常使用场景中，用户往往有把 Dubbo 服务以 RESTful 风格向外暴露的需求同时也要兼顾内部 Dubbo 调用。为了解决这种场景，作为 Dubbo API 网关的 Pixiu【2】 (中文: 貔貅， 曾用名 dubbo-go-proxy) 便应运而生。之所以采用 Pixiu 这个名称，是因为 Java 同类产品 Zuul 的意象是一个西方怪兽，Pixiu 作为一个国产产品，就用了我们中国的一个类似的神兽貔貅作为项目名称。也同时表达了 Dubbo 社区希望扩展出一整套云原生生态链的决心。&lt;/p>
&lt;p>目前 Dubbo 多语言生态，发展最好的自然是 Java，其次是 Golang，其他语言都差强人意。dubbo-go-pixiu 项目是一个基于 dubbo-go 发展起来的项目，目前接口协议层支持的是七层的 HTTP 请求调用，计划在未来的 0.5 版本中支持 gRPC 请求调用，其另外一个使命是作为一种新的 dubbo 多语言解决方案。&lt;/p>
&lt;h2 id="为什么使用-pixiu">为什么使用 Pixiu&lt;/h2>
&lt;p>Pixiu 是基于 Dubbogo 的云原生、高性能、可扩展的微服务 API 网关。作为一款网关产品，Pixiu 帮助用户轻松创建、发布、维护、监控和保护任意规模的 API ，接受和处理成千上万个并发 API 调用，包括流量管理、 CORS 支持、授权和访问控制、限制、监控，以及 API 版本管理。除此以外，作为 Dubbo 的衍生产品，Pixiu 可以帮助 Dubbo 用户进行协议转换，实现跨系统、跨协议的服务能力互通。&lt;/p></description></item><item><title>dubbo-go 白话文</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/02/20/dubbo-go-%E7%99%BD%E8%AF%9D%E6%96%87/</link><pubDate>Sat, 20 Feb 2021 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/02/20/dubbo-go-%E7%99%BD%E8%AF%9D%E6%96%87/</guid><description>&lt;h2 id="一前言">一、前言&lt;/h2>
&lt;blockquote>
&lt;p>本文基于 dubbogo &lt;a href="https://github.com/apache/dubbo-go/releases/tag/v1.5.4">1.5.4&lt;/a> 版本&lt;/p>
&lt;/blockquote>
&lt;p>最近开始参与 dubbogo 的一些开发测试，之前都是直接拿 &lt;a href="https://github.com/apache/dubbo-go-samples">samples&lt;/a> 的例子验证功能，而这次为了复现一个功能问题，打算从零开始搭建一个 dubbo-go 和 dubbo 调用的工程，踩到了一些新人使用 dubbogo 的坑，把这个过程记录下供大家参考。&lt;/p>
&lt;p>通过本文你可以了解到：&lt;/p>
&lt;ul>
&lt;li>如何常规配置 dubbogo 消费方去调用 dubbo 和 dubbogo 服务提供方&lt;/li>
&lt;li>通过一个实际的 BUG 介绍解决问题的思路&lt;/li>
&lt;/ul>
&lt;h2 id="二解决问题">二、解决问题&lt;/h2>
&lt;h3 id="21-准备-dubbo-服务提供者">2.1 准备 dubbo 服务提供者&lt;/h3>
&lt;h4 id="211-基本定义">2.1.1 基本定义&lt;/h4>
&lt;p>定义 &lt;code>DemoService&lt;/code> 接口：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">interface&lt;/span> &lt;span style="color:#268bd2">DemoService&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">sayHello&lt;/span>(String name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">sayHello&lt;/span>(User user);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> String &lt;span style="color:#268bd2">sayHello&lt;/span>(User user, String name);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>定义 &lt;code>User&lt;/code> 对象：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">public&lt;/span> &lt;span style="color:#268bd2">class&lt;/span> &lt;span style="color:#268bd2">User&lt;/span> &lt;span style="color:#268bd2">implements&lt;/span> Serializable {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> String name;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">private&lt;/span> &lt;span style="color:#dc322f">int&lt;/span> age;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ......
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="212-启动-dubbo-服务提供者">2.1.2 启动 dubbo 服务提供者&lt;/h4>
&lt;p>用的 &lt;a href="https://deploy-preview-3199--dubbo.netlify.app/zh-cn/docsv2.7/user/configuration/api/">dubbo 官方示例代码&lt;/a>:&lt;/p></description></item><item><title>dubbo-go源码笔记（二）客户端调用过程</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/15/dubbo-go%E6%BA%90%E7%A0%81%E7%AC%94%E8%AE%B0%E4%BA%8C%E5%AE%A2%E6%88%B7%E7%AB%AF%E8%B0%83%E7%94%A8%E8%BF%87%E7%A8%8B/</link><pubDate>Fri, 15 Jan 2021 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/15/dubbo-go%E6%BA%90%E7%A0%81%E7%AC%94%E8%AE%B0%E4%BA%8C%E5%AE%A2%E6%88%B7%E7%AB%AF%E8%B0%83%E7%94%A8%E8%BF%87%E7%A8%8B/</guid><description>&lt;p>随着微服务架构的流行，许多高性能 rpc 框架应运而生，由阿里开源的 dubbo 框架 go 语言版本的 dubbo-go 也成为了众多开发者不错的选择。本文将介绍 dubbo-go 框架的基本使用方法，以及从 export 调用链的角度进行 server 端源码导读，希望能引导读者进一步认识这款框架。&lt;/p>
&lt;h2 id="前言">前言&lt;/h2>
&lt;p>有了上一篇文章&lt;a href="https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/14/dubbo-go-%E6%BA%90%E7%A0%81%E7%AC%94%E8%AE%B0%E4%B8%80server-%E7%AB%AF%E5%BC%80%E5%90%AF%E6%9C%8D%E5%8A%A1%E8%BF%87%E7%A8%8B/">《dubbo-go 源码笔记（一）Server服务暴露过程详解》&lt;/a> 的铺垫，可以大致上类比客户端服务类似于服务端启动过程。其中最大的区别是服务端通过zk注册服务，发布自己的ivkURL并订阅事件开启监听；而服务端应该是通过zk注册组件，&lt;strong>拿到需要调用的serviceURL&lt;/strong>，&lt;strong>更新invoker&lt;/strong>并&lt;strong>重写用户的RPCService&lt;/strong>，从而实现对远程过程调用细节的封装。&lt;/p>
&lt;h2 id="1-配置文件和客户端源码">1. 配置文件和客户端源码&lt;/h2>
&lt;h4 id="11-client配置文件">1.1 client配置文件&lt;/h4>
&lt;p>helloworld提供的demo：profiles/client.yaml&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">registries &lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;demoZk&amp;#34;&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protocol&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;zookeeper&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">timeout &lt;/span>: &lt;span style="color:#2aa198">&amp;#34;3s&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">address&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;127.0.0.1:2181&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">username&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">password&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">references&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;UserProvider&amp;#34;&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75"># 可以指定多个registry，使用逗号隔开;不指定默认向所有注册中心注册&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">registry&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;demoZk&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protocol &lt;/span>: &lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">interface &lt;/span>: &lt;span style="color:#2aa198">&amp;#34;com.ikurento.user.UserProvider&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">cluster&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;failover&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">methods &lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">name&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;GetUser&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">retries&lt;/span>: &lt;span style="color:#2aa198">3&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>可看到配置文件与之前讨论过的server端非常类似，其refrences部分字段就是对当前服务要主调的服务的配置，其中详细说明了调用协议、注册协议、接口id、调用方法、集群策略等，这些配置都会在之后与注册组件交互，重写ivk、调用的过程中使用到。&lt;/p>
&lt;h4 id="12-客户端使用框架源码">1.2 客户端使用框架源码&lt;/h4>
&lt;p>user.go&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">init&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> config.&lt;span style="color:#268bd2">SetConsumerService&lt;/span>(userProvider)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> hessian.&lt;span style="color:#268bd2">RegisterPOJO&lt;/span>(&lt;span style="color:#719e07">&amp;amp;&lt;/span>User{})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>main.go&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">main&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> hessian.&lt;span style="color:#268bd2">RegisterPOJO&lt;/span>(&lt;span style="color:#719e07">&amp;amp;&lt;/span>User{})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> config.&lt;span style="color:#268bd2">Load&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> time.&lt;span style="color:#268bd2">Sleep&lt;/span>(&lt;span style="color:#2aa198">3e9&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b58900">println&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;\n\n\nstart to test dubbo&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> user &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#719e07">&amp;amp;&lt;/span>User{}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> err &lt;span style="color:#719e07">:=&lt;/span> userProvider.&lt;span style="color:#268bd2">GetUser&lt;/span>(context.&lt;span style="color:#268bd2">TODO&lt;/span>(), []&lt;span style="color:#268bd2">interface&lt;/span>{}{&lt;span style="color:#2aa198">&amp;#34;A001&amp;#34;&lt;/span>}, user)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> err &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b58900">panic&lt;/span>(err)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#b58900">println&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;response result: %v\n&amp;#34;&lt;/span>, user)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">initSignal&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>官网提供的helloworld demo的源码。可看到与服务端类似，在user.go内注册了rpc-service，以及需要rpc传输的结构体user。&lt;/p></description></item><item><title>dubbogo 3.0：牵手 gRPC 走向云原生时代</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/15/dubbogo-3.0%E7%89%B5%E6%89%8B-grpc-%E8%B5%B0%E5%90%91%E4%BA%91%E5%8E%9F%E7%94%9F%E6%97%B6%E4%BB%A3/</link><pubDate>Fri, 15 Jan 2021 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/15/dubbogo-3.0%E7%89%B5%E6%89%8B-grpc-%E8%B5%B0%E5%90%91%E4%BA%91%E5%8E%9F%E7%94%9F%E6%97%B6%E4%BB%A3/</guid><description>&lt;p>自从 2011 年 Dubbo 开源之后，被大量中小公司采用，一直是国内最受欢迎的 RPC 框架。2014 年，由于阿里内部组织架构调整，Dubbo 暂停维护了一段时间，之后随着 Spring Cloud 的面世，两个体系在融合中一起助推了微服务的火热。&lt;/p>
&lt;p>不过这世界变化快，自从以 docker 为代表的的容器技术和以 K8s 为代表的容器编排技术登上舞台之后，云原生时代到来了。在云原生时代，不可变的基础设施给原有的中间件带来了不可变的中间件基础设施：gRPC 统一了底层通信层；protobuf 统一了序列化协议；以 envoy + istio 为代表的 service mesh 逐渐统一了服务的控制面与数据面。&lt;/p>
&lt;p>dubbogo 的天然使命是：Bridging the gap between Java and Go。保持 Go 应用与 Java 应用互联互通的同时，借助 Go 语言（事实上的第一云原生语言）的优势拥抱云原生时代。dubbogo 社区 2020 年勠力打造三支箭：&lt;/p>
&lt;ul>
&lt;li>已经发布的对齐 dubbo 2.7 的 dubbogo v1.5 版本；&lt;/li>
&lt;li>近期将要发布的 sidecar 形态的 dubbo-go-proxy 项目；&lt;/li>
&lt;li>以及处于进行时的 dubbogo 3.0。&lt;/li>
&lt;/ul>
&lt;p>用一句话概括 dubbogo 3.0 即是：新通信协议、新序列化协议、新应用注册模型以及新的服务治理能力！本文主要着重讨论 dubbogo 3.0 的新通信协议和应用级服务注册发现模型。&lt;/p>
&lt;h2 id="dubbogo-30-vs-grpc">dubbogo 3.0 vs gRPC&lt;/h2>
&lt;p>知己知彼，方能进步。dubbogo 3.0 的通信层改进主要借鉴了 gRPC。&lt;/p></description></item><item><title>分布式事务框架 seata-golang 通信模型详解</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/15/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A1%86%E6%9E%B6-seata-golang-%E9%80%9A%E4%BF%A1%E6%A8%A1%E5%9E%8B%E8%AF%A6%E8%A7%A3/</link><pubDate>Fri, 15 Jan 2021 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/15/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A1%86%E6%9E%B6-seata-golang-%E9%80%9A%E4%BF%A1%E6%A8%A1%E5%9E%8B%E8%AF%A6%E8%A7%A3/</guid><description>&lt;h2 id="简介">简介&lt;/h2>
&lt;p>Java 的世界里，大家广泛使用一个高性能网络通信框架 —— netty，很多 RPC 框架都是基于 netty 来实现的。在 golang 的世界里，getty 也是一个类似 netty 的高性能网络通信库。getty 最初由 dubbo-go 项目负责人于雨开发，作为底层通信库在 dubbo-go 中使用。随着 dubbo-go 捐献给 apache 基金会，在社区小伙伴的共同努力下，getty 也最终进入到 apache 这个大家庭，并改名 dubbo-getty。&lt;/p>
&lt;p>18 年的时候，我在公司里实践微服务，当时遇到最大的问题就是分布式事务问题。同年，阿里在社区开源他们的分布式事务解决方案，我也很快关注到这个项目，起初还叫 fescar，后来更名 seata。由于我对开源技术很感兴趣，加了很多社区群，当时也很关注 dubbo-go 这个项目，在里面默默潜水。随着对 seata 的了解，逐渐萌生了做一个 go 版本的分布式事务框架的想法。&lt;/p>
&lt;p>要做一个 golang 版的分布式事务框架，首先需要解决的一个问题就是如何实现 RPC 通信。dubbo-go 就是摆在眼前很好的一个例子，遂开始研究 dubbo-go 的底层 getty。&lt;/p>
&lt;h2 id="如何基于-getty-实现-rpc-通信">如何基于 getty 实现 RPC 通信&lt;/h2>
&lt;p>getty 框架的整体模型图如下：&lt;/p>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubbo-go/seata/p1.webp">&lt;/p>
&lt;p>下面结合相关代码，详述 seata-golang 的 RPC 通信过程。&lt;/p>
&lt;h3 id="1-建立连接">1. 建立连接&lt;/h3>
&lt;p>实现 RPC 通信，首先要建立网络连接，这里先从 client.go 开始看起。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (c &lt;span style="color:#719e07">*&lt;/span>client) &lt;span style="color:#268bd2">connect&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">var&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> err &lt;span style="color:#dc322f">error&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ss Session
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> )
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 建立一个 session 连接
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ss = c.&lt;span style="color:#268bd2">dial&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> ss &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// client has been closed
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#719e07">break&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> err = c.&lt;span style="color:#268bd2">newSession&lt;/span>(ss)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">if&lt;/span> err &lt;span style="color:#719e07">==&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 收发报文
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ss.(&lt;span style="color:#719e07">*&lt;/span>session).&lt;span style="color:#268bd2">run&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// 此处省略部分代码
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">break&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75">// don&amp;#39;t distinguish between tcp connection and websocket connection. Because
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> &lt;span style="color:#586e75">// gorilla/websocket/conn.go:(Conn)Close also invoke net.Conn.Close()
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ss.&lt;span style="color:#268bd2">Conn&lt;/span>().&lt;span style="color:#268bd2">Close&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>connect()&lt;/code> 方法通过 &lt;code>dial()&lt;/code> 方法得到了一个 session 连接，进入 &lt;code>dial()&lt;/code> 方法：&lt;/p></description></item><item><title>dubbo-go 可信调用实现</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/14/dubbo-go-%E5%8F%AF%E4%BF%A1%E8%B0%83%E7%94%A8%E5%AE%9E%E7%8E%B0/</link><pubDate>Thu, 14 Jan 2021 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/14/dubbo-go-%E5%8F%AF%E4%BF%A1%E8%B0%83%E7%94%A8%E5%AE%9E%E7%8E%B0/</guid><description>&lt;p>Apache Dubbo/Dubbo-Go 作为阿里巴巴开源的一款服务治理框架，因其适应 Java/Go 开发者面向接口的编程习惯、完全透明的调用方式、优越的性能以及强大的扩展性等优点，在国内使用非常广泛。除此之外，Dubbo 开源版本原生集成了很多开箱即用的服务治理功能，包括链路追踪，路由、负载均衡、服务注册发现、监控、认证等。&lt;/p>
&lt;p>本文将讲解如何在 Dubbo/Dubbo-Go 中实现灵活，安全和高效的身份验证和授权方案。&lt;/p>
&lt;h2 id="可信的目的">可信的目的&lt;/h2>
&lt;p>为什么需要鉴权认证？实际生产中类似支付之类的安全性敏感的业务会有限制匿名系统调用的需求，其他业务在接入该类敏感业务之前，需要通过审批方可正常调用，这就需要对这类敏感服务进行权限管控。尽管 Dubbo 开源版本中支持 Token 方式的鉴权实现，但是该实现方式总体来说安全性并不高，并且无法满足我们需要动态下发以及变更的灵活性需求。&lt;/p>
&lt;p>针对于此，我们内部着重从巩固安全性和拓展灵活性层面重新设计了一套 Dubbo/Dubbo-Go 的服务间调用的鉴权认证功能。本文我们将主要从实现层面讲解其大致实现思路。&lt;/p>
&lt;h2 id="可信方案">可信方案&lt;/h2>
&lt;p>抽象来看鉴权认证主要围绕以下两个问题，&lt;/p>
&lt;ul>
&lt;li>身份认证：指验证应用的身份，每个应用在其生命周期内只有唯一身份，无法变更和伪造。&lt;/li>
&lt;li>权限鉴定：根据身份信息鉴定权限是否满足调用。权限粒度可以进行控制。&lt;/li>
&lt;/ul>
&lt;p>我们通过 Access Key ID/Secret Access Key (后文简称为 AK/SK) 信息标识应用和应用之间的身份关系。例如上游 应用A 依赖下游 服务B 和 C，则 A 对 B 和 C 分别有一套 AK/SK。其相互独立没有任何关系。就算 A服务 的 AK/SK 信息泄漏，也无法通过该 AK/SK 信息调用其他的服务。&lt;/p>
&lt;p>在权限鉴定方面也借鉴了公有云开放 API 常用的 AK/SK 签名机制。 在请求过程中使用 SK 签名生成 SigningKey，并通过 Dubbo 的 attachment 机制将额外的元数据信息以及 SigningKey 传输到服务端，交由服务端计算和验签，验签通过方能正常处理和响应。&lt;/p>
&lt;p>签名过程主要通过如下三个方式进行加强 SigningKey 的可靠性和安全性。&lt;/p>
&lt;ul>
&lt;li>
&lt;p>验证请求者的身份&lt;/p>
&lt;p>签名会通过对应应用的SK作为加密密钥对请求元数据(以及参数)进行加密，保证签名的唯一性和不可伪造性。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>支持对参数进行计算签名，防止非法篡改&lt;/p></description></item><item><title>Dubbo-go 源码笔记（一）Server 端开启服务过程</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/14/dubbo-go-%E6%BA%90%E7%A0%81%E7%AC%94%E8%AE%B0%E4%B8%80server-%E7%AB%AF%E5%BC%80%E5%90%AF%E6%9C%8D%E5%8A%A1%E8%BF%87%E7%A8%8B/</link><pubDate>Thu, 14 Jan 2021 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/14/dubbo-go-%E6%BA%90%E7%A0%81%E7%AC%94%E8%AE%B0%E4%B8%80server-%E7%AB%AF%E5%BC%80%E5%90%AF%E6%9C%8D%E5%8A%A1%E8%BF%87%E7%A8%8B/</guid><description>&lt;p>随着微服务架构的流行，许多高性能 rpc 框架应运而生，由阿里开源的 dubbo 框架 go 语言版本的 dubbo-go 也成为了众多开发者不错的选择。本文将介绍 dubbo-go 框架的基本使用方法，以及从 export 调用链的角度进行 server 端源码导读，希望能引导读者进一步认识这款框架。&lt;/p>
&lt;p>当拿到一款框架之后，一种不错的源码阅读方式大致如下：从运行最基础的 helloworld demo 源码开始 —&amp;gt; 再查看配置文件 —&amp;gt; 开启各种依赖服务（比如zk、consul） —&amp;gt; 开启服务端 —&amp;gt; 再到通过 client 调用服务端 —&amp;gt; 打印完整请求日志和回包。调用成功之后，再根据框架的设计模型，从配置文件解析开始，自顶向下递阅读整个框架的调用栈。&lt;/p>
&lt;p>对于 C/S 模式的 rpc 请求来说，整个调用栈被拆成了 client 和 server 两部分，所以可以分别从 server 端的配置文件解析阅读到 server 端的监听启动，从 client 端的配置文件解析阅读到一次 invoker Call 调用。这样一次完整请求就明晰了起来。&lt;/p>
&lt;h2 id="运行官网提供的-helloworld-demo">运行官网提供的 helloworld-demo&lt;/h2>
&lt;p>&lt;strong>官方 demo 相关链接&lt;/strong>：https://github.com/dubbogo/dubbo-samples/tree/master/golang/helloworld/dubbo&lt;/p>
&lt;h3 id="1-dubbo-go-27-版本-quickstart">1. dubbo-go 2.7 版本 QuickStart&lt;/h3>
&lt;h4 id="1开启一个-go-server-服务">1）开启一个 go-server 服务&lt;/h4>
&lt;ul>
&lt;li>将仓库 clone 到本地&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>$ git clone https://github.com/dubbogo/dubbo-samples.git
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>进入 dubbo 目录&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>$ &lt;span style="color:#b58900">cd&lt;/span> dubbo-samples/golang/helloworld/dubbo
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>进入目录后可看到四个文件夹，分别支持 go 和 java 的 client 以及 server，我们尝试运行一个 go 的 server。进入 app 子文件夹内，可以看到里面保存了 go 文件。&lt;/p></description></item><item><title>dubbo-go 中 REST 协议实现</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/14/dubbo-go-%E4%B8%AD-rest-%E5%8D%8F%E8%AE%AE%E5%AE%9E%E7%8E%B0/</link><pubDate>Thu, 14 Jan 2021 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/14/dubbo-go-%E4%B8%AD-rest-%E5%8D%8F%E8%AE%AE%E5%AE%9E%E7%8E%B0/</guid><description>&lt;h2 id="什么是-rest-协议">什么是 REST 协议&lt;/h2>
&lt;p>REST 是 &lt;strong>RE&lt;/strong>presentational &lt;strong>S&lt;/strong>tate &lt;strong>T&lt;/strong>ransfer（表述性状态转移）的简写，是一种软件架构风格。虽然 REST 架构风格不是一定要基于 HTTP 协议进行传输，但是因为 HTTP
协议的通用性和易用性，现在越来越多的 web 服务采用基于 HTTP 协议实现 RESTful 架构。&lt;/p>
&lt;p>在 dubbo-go 中的 REST 协议指的是一种基于 HTTP 协议的远程调用方式。简单的来讲，REST 协议就是把dubbo 服务发布成 RESTful 风格的 HTTP 接口并且能够能像调用 dubbo 接口一样的方式调用 HTTP
接口。&lt;/p>
&lt;h2 id="为什么要支持-rest-协议">为什么要支持 REST 协议&lt;/h2>
&lt;p>在没有 REST 协议之前，小伙伴们是否一直在苦恼这样几个问题：&lt;/p>
&lt;ol>
&lt;li>传统的 web 服务不能直接调用我们发布的 dubbo 服务&lt;/li>
&lt;li>前端不能直接调用 dubbo 服务&lt;/li>
&lt;li>dubbo 服务不能发布 Open API&lt;/li>
&lt;/ol>
&lt;p>上述问题，就是 REST 协议解决的核心问题。现在我们很多应用场景都是需要与异构的系统进行交互，而 REST 采用的 HTTP 通信协议非常适合用来打通异构系统，如图：&lt;/p>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubbo-go/rest/rest-call.webp">&lt;/p>
&lt;h2 id="rest-协议没那么简单">REST 协议没那么简单&lt;/h2>
&lt;p>REST 协议核心要解决一个问题：&lt;strong>Go 方法到 HTTP 接口的双向映射&lt;/strong>。普通 HTTP 调用 dubbo-go 服务，考虑的是HTTP 到 &lt;strong>Go&lt;/strong> 方法的映射；而 dubbo-go 服务调用 HTTP 服务，则是 **
Go** 方法到 HTTP 接口的映射。&lt;/p></description></item><item><title>dubbo-go 中将 Kubernets 原⽣作为注册中⼼的设计和实现</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/14/dubbo-go-%E4%B8%AD%E5%B0%86-kubernets-%E5%8E%9F%E4%BD%9C%E4%B8%BA%E6%B3%A8%E5%86%8C%E4%B8%AD%E7%9A%84%E8%AE%BE%E8%AE%A1%E5%92%8C%E5%AE%9E%E7%8E%B0/</link><pubDate>Thu, 14 Jan 2021 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/14/dubbo-go-%E4%B8%AD%E5%B0%86-kubernets-%E5%8E%9F%E4%BD%9C%E4%B8%BA%E6%B3%A8%E5%86%8C%E4%B8%AD%E7%9A%84%E8%AE%BE%E8%AE%A1%E5%92%8C%E5%AE%9E%E7%8E%B0/</guid><description>&lt;p>今天这篇⽂章将会介绍 dubbo-go 将 Kubernetes 作为注册中⼼的服务注册的初衷、设计⽅案，以及具体实现。&lt;/p>
&lt;p>到⽬前为⽌该⽅案的实现已经被合并到 dubbo-go 的 master 分⽀。具体实现为关于 Kubernetes 的 &lt;a href="https://github.com/apache/dubbo-go/pull/400">PullRequest&lt;/a> 。&lt;/p>
&lt;h2 id="kubernetes管理资源的哲学">Kubernetes管理资源的哲学&lt;/h2>
&lt;p>Kubernetes 作为容器集群化管理⽅案管理资源的维度可主观的分为服务进程管理和服务接⼊管理。&lt;/p>
&lt;ul>
&lt;li>服务实例管理，主要体现⽅式为 Pod 设计模式加控制器模式，控制器保证具有特定标签 （ Kubernetes-Label ）的 Pod 保持在恒定的数量（多删，少补）。&lt;/li>
&lt;li>服务管理，主要为 Kubernetes-Service ，该 Service 默认为具有特定标签（ Kubernetes-Label ）的 Pod 统⼀提供⼀个 VIP（ Kubernetes-ClusterIP ）所有需要请求该组 Pod 的请求都默认会按照 round-robin 的负载策略转发到真正提供服务的 Pod 。并且 CoreDNS 为该 Kubernetes-Service 提供集群内唯⼀的域名。&lt;/li>
&lt;/ul>
&lt;h2 id="kubernetes的服务发现模型">Kubernetes的服务发现模型&lt;/h2>
&lt;p>为了明确 K8s 在服务接入管理提供的解决方案，我们以 kube-apiserver 提供的 API(HTTPS) 服务为例。K8s 集群为该服务分配了一个集群内有效的 ClusterIP ，并通过 CoreDNS 为其分配了唯一的域名 kubernetes 。如果集群内的 Pod 需要访问该服务时直接通过 https://kubernetes:443 即可完成。&lt;/p>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubbo-go/k8s/k8s-service-discovery.png">&lt;/p>
&lt;p>具体流程如上图所示 ( 红⾊为客户端，绿⾊为 kube-apiserver )：&lt;/p></description></item><item><title>Dubbo-go应用维度注册模型</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/14/dubbo-go%E5%BA%94%E7%94%A8%E7%BB%B4%E5%BA%A6%E6%B3%A8%E5%86%8C%E6%A8%A1%E5%9E%8B/</link><pubDate>Thu, 14 Jan 2021 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/14/dubbo-go%E5%BA%94%E7%94%A8%E7%BB%B4%E5%BA%A6%E6%B3%A8%E5%86%8C%E6%A8%A1%E5%9E%8B/</guid><description>&lt;p>Dubbo 3.0 将至。其最重要的一点就是服务自省，其基础即是应用维度的注册模型，作为目前与 Dubbo 在功能上完全对齐的 Dubbo-go，已于 本年【2020 年】7 月份发布了其 v1.5.0 版本，实现了该模型，为年底实现与 Dubbo 3.0 对齐的新版本奠定了基础。&lt;/p>
&lt;p>Dubbo-go 作为 Dubbo 的 Go 语言版本，因跨语言之故，二者针对同一模型的实现必然有较大差异，故本文注重讨论 Dubbo-go 社区自身对该模型的理解和实现，以及其与 Dubbo 之间的差异。&lt;/p>
&lt;h2 id="1-引语">1 引语&lt;/h2>
&lt;p>在 v1.5 以前，Dubbo-go 注册模型都是以服务为维度的，直观的理解可认为其是接口维度。譬如注册信息，按照服务维度模型其示例如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-json" data-lang="json">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">&amp;#34;com.xxx.User&amp;#34;&lt;/span>:[
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {&lt;span style="color:#268bd2">&amp;#34;name&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;instance1&amp;#34;&lt;/span>, &lt;span style="color:#268bd2">&amp;#34;ip&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;127.0.0.1&amp;#34;&lt;/span>, &lt;span style="color:#268bd2">&amp;#34;metadata&amp;#34;&lt;/span>:{&lt;span style="color:#268bd2">&amp;#34;timeout&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">1000&lt;/span>}},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {&lt;span style="color:#268bd2">&amp;#34;name&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;instance2&amp;#34;&lt;/span>, &lt;span style="color:#268bd2">&amp;#34;ip&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;127.0.0.2&amp;#34;&lt;/span>, &lt;span style="color:#268bd2">&amp;#34;metadata&amp;#34;&lt;/span>:{&lt;span style="color:#268bd2">&amp;#34;timeout&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">2000&lt;/span>}},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {&lt;span style="color:#268bd2">&amp;#34;name&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;instance3&amp;#34;&lt;/span>, &lt;span style="color:#268bd2">&amp;#34;ip&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;127.0.0.3&amp;#34;&lt;/span>, &lt;span style="color:#268bd2">&amp;#34;metadata&amp;#34;&lt;/span>:{&lt;span style="color:#268bd2">&amp;#34;timeout&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">3000&lt;/span>}}, 
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>]
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这种模式的好处是不言而喻的，简单直观，提供了细粒度的服务控制手段。&lt;/p>
&lt;p>而近两年，随着云时代的到来，这种模式就暴露了不足：&lt;/p>
&lt;ol>
&lt;li>主流的注册模型都是应用维度的；&lt;/li>
&lt;li>以服务维度来注册，那么规模与服务数量成正比，大规模集群之下【工行软件中心的接口注册规模达到万级】，注册中心压力非常大；&lt;/li>
&lt;/ol>
&lt;h2 id="2-dubbo-go-v150-的新注册模型">2 Dubbo-go v1.5.0 的新注册模型&lt;/h2>
&lt;p>这次 Dubbo-go 支持了新的注册模型，也就是应用维度的注册模型。简单而言，在应用维度注册下，其注册信息类似：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-json" data-lang="json">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#2aa198">&amp;#34;application1&amp;#34;&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {&lt;span style="color:#268bd2">&amp;#34;name&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;instance1&amp;#34;&lt;/span>, &lt;span style="color:#268bd2">&amp;#34;ip&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;127.0.0.1&amp;#34;&lt;/span>, &lt;span style="color:#268bd2">&amp;#34;metadata&amp;#34;&lt;/span>:{}},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {&lt;span style="color:#268bd2">&amp;#34;name&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;instance2&amp;#34;&lt;/span>, &lt;span style="color:#268bd2">&amp;#34;ip&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;127.0.0.2&amp;#34;&lt;/span>, &lt;span style="color:#268bd2">&amp;#34;metadata&amp;#34;&lt;/span>:{}},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {&lt;span style="color:#268bd2">&amp;#34;name&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;instanceN&amp;#34;&lt;/span>, &lt;span style="color:#268bd2">&amp;#34;ip&amp;#34;&lt;/span>:&lt;span style="color:#2aa198">&amp;#34;127.0.0.3&amp;#34;&lt;/span>, &lt;span style="color:#268bd2">&amp;#34;metadata&amp;#34;&lt;/span>:{}}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>]
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在此模式之下，可以看到注册信息将会大幅度减少，集群规模只与实例数量相关。&lt;/p>
&lt;p>与此同时，在实现这一个功能的时候，Dubbo-go 还希望保持两个目标：&lt;/p>
&lt;ol>
&lt;li>对用户完全兼容，用户迁移无感知；&lt;/li>
&lt;li>保持住原本服务粒度上精细控制的能力——即保留现有的服务维度的元数据；&lt;/li>
&lt;/ol>
&lt;p>因此 Dubbo-go 要着力解决以下几点：&lt;/p>
&lt;ol>
&lt;li>目前 Consumer 的配置是以接口为准的，如何根据接口找到该接口对应的应用？例如，用户配置了 &lt;code>com.xxx.User&lt;/code> 服务，那么，Dubbo-go 怎么知道这个服务是由哪个应用来提供的呢？&lt;/li>
&lt;li>在知道了是哪个应用之后，可以从注册中心拿到应用的注册信息，如实例信息等；那怎么知道 &lt;code>com.xxx.User&lt;/code> 服务自身的元数据呢？&lt;/li>
&lt;/ol>
&lt;p>为了解决这两个问题，在已有的注册模型的基础上，Dubbo-go 引入两个额外的组件：ServiceNameMapping 和 MetadataService。&lt;/p></description></item><item><title>Go 版本入 Dubbo 生态一周年</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/14/go-%E7%89%88%E6%9C%AC%E5%85%A5-dubbo-%E7%94%9F%E6%80%81%E4%B8%80%E5%91%A8%E5%B9%B4/</link><pubDate>Thu, 14 Jan 2021 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/14/go-%E7%89%88%E6%9C%AC%E5%85%A5-dubbo-%E7%94%9F%E6%80%81%E4%B8%80%E5%91%A8%E5%B9%B4/</guid><description>&lt;p>去年 5 月，阿里开源的高性能 RPC 框架 Dubbo 从 ASF 毕业并晋升顶级项目，同时，还宣布 Go 语言版本的 Dubbo-go 正式加入 Dubbo 官方生态。&lt;/p>
&lt;p>经过一年的发展， Dubbo-go 在技术和社区运营方面都已经有了不错的成绩。Dubbo-go 是 Dubbo 的完整 Go 语言实现，在功能实现和技术路径上与 Dubbo 有不同程度的对标，项目团队预计很快便可以追平 Java 版的功能。当然，也是因为基于 Go 语言开发，Dubbo-go 更易上手，未来或将反哺 Dubbo 的云原生化。&lt;/p>
&lt;p>Dubbo-go 近期还实现了 REST 协议以及 gRPC 的支持，打通了 Spring Cloud 和 gRPC 生态，再加上与 Java Dubbo 的互通，应用场景广泛。因此，它被其开发者叫做“all-in-one”的 RPC 框架。&lt;/p>
&lt;p>目前 Dubbo 官方已经投入人力参与 Dubbo-go 的开发，阿里集团今年完成 HSF 和 Dubbo 的融合后，会在集团内逐步推广使用 Dubbo-go。&lt;/p>
&lt;p>开源中国采访了当前正在开发中的 v1.5 版本的主要推进者邓明，回顾 Dubbo-go 的过往，尤其是最近一年的发展情况，并展望项目未来的发展。&lt;/p>
&lt;h2 id="dubbo-go-过去发展回顾">Dubbo-go 过去发展回顾&lt;/h2>
&lt;p>&lt;strong>OSCHINA：&lt;/strong> 作为项目主要推动者之一，可以简单回顾下 Dubbo-go 的发展历程吗？&lt;/p>
&lt;p>&lt;strong>Dubbo-go 邓明：&lt;/strong>&lt;/p></description></item><item><title>记一次在 mosn 对 dubbo、dubbo-go-hessian2 的性能优化</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/14/%E8%AE%B0%E4%B8%80%E6%AC%A1%E5%9C%A8-mosn-%E5%AF%B9-dubbodubbo-go-hessian2-%E7%9A%84%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/</link><pubDate>Thu, 14 Jan 2021 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/14/%E8%AE%B0%E4%B8%80%E6%AC%A1%E5%9C%A8-mosn-%E5%AF%B9-dubbodubbo-go-hessian2-%E7%9A%84%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/</guid><description>&lt;h3 id="背景">背景&lt;/h3>
&lt;p>蚂蚁内部对 Service Mesh 的稳定性和性能要求是比较高的，内部 mosn 广泛用于生产环境。在云上和开源社区，RPC 领域 dubbo 和 spring cloud 同样广泛用于生产环境，我们在 mosn 基础上，支持了 dubbo 和 spring cloud 流量代理。我们发现在支持 dubbo 协议过程中，经过 Mesh 流量代理后，性能有非常大的性能损耗，在大商户落地 Mesh 中也对性能有较高要求，因此本文会重点描述在基于 Go 语言库 &lt;a href="https://github.com/apache/dubbo-go-hessian2">dubbo-go-hessian2&lt;/a> 、dubbo 协议中对 &lt;a href="https://github.com/mosn/mosn">mosn&lt;/a> 所做的性能优化。&lt;/p>
&lt;h3 id="性能优化概述">性能优化概述&lt;/h3>
&lt;p>根据实际业务部署场景，并没有选用高性能机器，使用普通 linux 机器，配置和压测参数如下：&lt;/p>
&lt;ul>
&lt;li>Intel(R) Xeon(R) Platinum 8163 CPU @ 2.50GHz 4 核 16G 。&lt;/li>
&lt;li>pod 配置 &lt;code>2c、1g&lt;/code>，JVM 参数 &lt;code>-server -Xms1024m -Xmx1024m&lt;/code>。&lt;/li>
&lt;li>网络延迟 0.23 ms, 2 台 linux 机器，分别部署 server + mosn, 压测程序 &lt;a href="https://github.com/zonghaishang/rpc-performance">rpc-perfomance&lt;/a>。&lt;/li>
&lt;/ul>
&lt;p>经过 3 轮性能优化后，使用优化版本 mosn 将会获得以下性能收益（框架随机 512 和 1k 字节压测）：&lt;/p></description></item><item><title>解构 Dubbo-go 的核心注册引擎 Nacos</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/14/%E8%A7%A3%E6%9E%84-dubbo-go-%E7%9A%84%E6%A0%B8%E5%BF%83%E6%B3%A8%E5%86%8C%E5%BC%95%E6%93%8E-nacos/</link><pubDate>Thu, 14 Jan 2021 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/14/%E8%A7%A3%E6%9E%84-dubbo-go-%E7%9A%84%E6%A0%B8%E5%BF%83%E6%B3%A8%E5%86%8C%E5%BC%95%E6%93%8E-nacos/</guid><description>&lt;p>近几年，随着Go语言社区逐渐发展和壮大，越来越多的公司开始尝试采用Go搭建微服务体系，也涌现了一批Go的微服务框架，如go-micro、go-kit、Dubbo-go等，跟微服务治理相关的组件也逐渐开始在Go生态发力，如Sentinel、Hystrix等都推出了Go语言版本，而作为微服务框架的核心引擎&amp;ndash;注册中心，也是必不可缺少的组件，市面已经有多款注册中心支持Go语言，应该如何选择呢？我们可以对目前主流的支持Go语言的注册中心做个对比。&lt;/p>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubbo-go/nacos/p1.png">&lt;/p>
&lt;p>根据上表的对比我们可以从以下几个维度得出结论：&lt;/p>
&lt;ul>
&lt;li>生态:各注册中心对Go语言都有支持，但是Nacos、 Consul、Etcd 社区活跃，zookeeper和Eureka社区活跃度较低；&lt;/li>
&lt;li>易用性：Nacos、Eureka、Consul都有现成的管控平台，Etcd、zookeeper本身作为kv存储，没有相应的管控平台，Nacos支持中文界面，比较符合国人使用习惯；&lt;/li>
&lt;li>场景支持：CP模型主要针对强一致场景，如金融类，AP模型适用于高可用场景，Nacos可以同时满足两种场景，Eureka主要满足高可用场景，Consul、Zookeepr、Etcd主要满足强一致场景，此外Nacos支持从其它注册中心同步数据，方便用户注册中心迁移；&lt;/li>
&lt;li>功能完整性：所有注册中心都支持健康检查，Nacos、Consul支持的检查方式较多，满足不同应用场景，Zookeeper通过keep alive方式，能实时感知实例变化；Nacos、Consul和Eureka都支持负载均衡策略，Nacos通过Metadata selector支持更灵活的策略；此外，Nacos、Eureka都支持雪崩保护，避免因为过多的实例不健康对健康的实例造成雪崩效应。&lt;/li>
&lt;/ul>
&lt;p>综合上面各维度的对比，可以了解到Nacos作为注册中心有一定的优势，那么它对Go微服务生态的集成做得如何？接下来我们首先探索下Nacos是如何与Dubbo-go集成。&lt;/p>
&lt;h2 id="引言">引言&lt;/h2>
&lt;p>Dubbo-go目前是Dubbo多语言生态中最火热的一个项目，从2016年发布至今，已经走过5个年头。最近，Dubbo-go发布了v1.5版本，全面兼容Dubbo 2.7.x版本，支持了应用维度的服务注册与发现，和主流的注册模型保持一致，标志着Dubbo-go向云原生迈出了关键的一步。作为驱动服务运转的核心引擎&amp;ndash;注册中心，在切换到应用维度的注册模型后，也需要做相应的适配，本文将解析如何以Nacos为核心引擎实现应用维度的服务注册与发现，并且给出相应的实践案例。此外，本文代码基于Dubbo-go v1.5.1，Nacos-SDK-go v1.0.0和Nacos v1.3.2。&lt;/p>
&lt;h2 id="服务注册与发现架构">服务注册与发现架构&lt;/h2>
&lt;p>从架构中，我们可以看到，与接口级别的服务注册发现不同的是，Dubbo-go的provider启动后会调用Nacos-go-sdk的RegisterInstance接口向Nacos注册服务实例，注册的服务名即为应用名称，而不是接口名称。Conusmer启动后则会调用Subscribe接口订阅该应用的服务实例变化，并对的实例发起服务调用。&lt;/p>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubbo-go/nacos/p2.png">&lt;/p>
&lt;h2 id="服务模型">服务模型&lt;/h2>
&lt;p>图3是我们Dubbo-go的应用维度服务发现模型，主要有服务和实例两个层级关系，服务实例的属性主要包含实例Id、主机地址、服务端口、激活状态和元数据。图4为Nacos的服务分级存储模型，包含服务、集群和实例三个层次。两者对比，多了一个集群维度的层级，而且实例属性信息能够完全匹配。所以在Dubbo-go将应用服务实例注册到Nacos时，我们只需要将集群设置为默认集群，再填充服务和实例的相关属性，即可完成服务模型上的匹配。此外Nacos可以将服务注册到不同的Namespace下，实现多租户的隔离。&lt;/p>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubbo-go/nacos/p3.png">&lt;/p>
&lt;p>!
&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubbo-go/nacos/p4.png">&lt;/p>
&lt;h2 id="服务实例心跳维持">服务实例心跳维持&lt;/h2>
&lt;p>Dubbo-go的Provider在向Nacos注册应用服务实例信息后，需要主动上报心跳，让Nacos服务端感知实例的存活与否，以判断是否将该节点从实例列表中移除。维护心跳的工作是在Nacos-SDK-go完成的，从图5代码中可以看到，当Dubbo-go调用RegisterInstance注册一个服务实例时，SDK除了调用Nacos的Register API之外，还会调用AddBeatInfo，将服务实例信息添加到本地缓存，通过后台协程定期向Nacos发送服务实例信息，保持心跳。当服务下线时，可以通过调用DeRegisterInstance执行反注册，并移除本地的心跳保持任务，Nacos实例列表中也会将该实例移除。&lt;/p>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubbo-go/nacos/p5.png">&lt;/p>
&lt;h1 id="订阅服务实例变化">订阅服务实例变化&lt;/h1>
&lt;p>Dubbo-go的Consumer在启动的时候会调用Nacos-SDK-go的Subscribe接口，该接口入参如图6，订阅的时候只需要传递ServiceName即应用名和回调函数SubscribeCallback，Nacos在服务实例发生变化的时候即可通过回调函数通知Dubbo-go。Nacos-SDK-go是如何感知Nacos的服务实例变化的呢？主要有两种方式：&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Nacos服务端主动推送，Nacos-SDK-go在启动的时候会监听一个UDP端口，该端口在调用Nacos Register API的时候作为参数传递，Nacos会记录Ip和端口，当服务实例发生变化时，Nacos会对所有监听该服务的Ip和端口发送UDP请求，推送变化后的服务实例信息。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Nacos-SDK-go定期查询，SDK会对订阅的服务实例定时调用查询接口，如果查询有变化则通过回调接口通知Dubbo-go。作为兜底策略保证Nacos服务端推送失败后，仍能感知到变化。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubbo-go/nacos/p6.png">&lt;/p>
&lt;p>此外Nacos-SDK-go还支持推空保护，当Nacos推送的实例列表为空时，不更新本地缓存，也不通知Dubbo-go变更，避免Consumer无可用实例调用，造成故障。同时，SDK还支持服务实例信息本地持久化存储，可以保证在Nacos服务故障过程中，Consumer重启也能获取到可用实例，具备容灾效果。&lt;/p>
&lt;h1 id="范例实践">范例实践&lt;/h1>
&lt;h2 id="环境准备">环境准备&lt;/h2>
&lt;p>dubbo-go samples代码下载：https://github.com/apache/dubbo-go-samples/tree/master，基于Nacos注册中心的应用级服务发现的hello world代码目录在 registry/servicediscovery/nacos。&lt;/p>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubbo-go/nacos/p7.png">&lt;/p>
&lt;p>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&lt;/p>
&lt;h2 id="server端搭建">Server端搭建&lt;/h2>
&lt;p>进入registry/servicediscovery/nacos/go-server/profiles文件，可以看到有dev、release和test三个文件夹，分别对应开发、测试和生产配置。我们使用dev配置来搭建开发环境，dev文件下有log.yml和server.yml文件，下面对server.yml配置进行修改。&lt;/p>
&lt;p>remote配置，这里使用公共的Nacos服务，address支持配置多个地址，用逗号分割。params参数配置nacos-sdk的日志目录。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-Yaml" data-lang="Yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">remote&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">nacos&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">address&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;console.nacos.io:80&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">timeout&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;5s&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">params&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">logDir&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;/data/nacos-sdk/log&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>configCenter配置
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">config_center&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protocol&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;nacos&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">address&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;console.nacos.io:80&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>配置server端环境变量&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-Bash" data-lang="Bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#b58900">export&lt;/span> &lt;span style="color:#268bd2">CONF_PROVIDER_FILE_PATH&lt;/span>&lt;span style="color:#719e07">=&lt;/span>server端的server.yml文件路径
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#b58900">export&lt;/span> &lt;span style="color:#268bd2">APP_LOG_CONF_FILE&lt;/span>&lt;span style="color:#719e07">=&lt;/span>server端的log.yml文件路径
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>进入registry/servicediscovery/nacos/go-server/app，运行server.go的main方法，可以从Nacos的控制台（http://console.nacos.io/nacos/#/serviceManagement?dataId=&amp;amp;group=&amp;amp;appName=&amp;amp;namespace=）&lt;/p>
&lt;p>看到，应用user-info-server已经注册成功。&lt;/p>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubbo-go/nacos/p8.png">&lt;/p>
&lt;h2 id="client端搭建">Client端搭建&lt;/h2>
&lt;p>client的配置文件在registry/servicediscovery/nacos/go-server/profiles目录下，需要修改的地方跟server端一样，这里不赘述。&lt;/p>
&lt;p>配置client端环境变量&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-Bash" data-lang="Bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#b58900">export&lt;/span> &lt;span style="color:#268bd2">CONF_CONSUMER_FILE_PATH&lt;/span>&lt;span style="color:#719e07">=&lt;/span>client端的server.yml文件路径
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#b58900">export&lt;/span> &lt;span style="color:#268bd2">APP_LOG_CONF_FILE&lt;/span>&lt;span style="color:#719e07">=&lt;/span>client端的log.yml文件路径
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>进入registry/servicediscovery/nacos/go-client/app，运行client.go的main方法，看到如下日志输出，表示调用server端成功。&lt;/p></description></item><item><title>快速上手 dubbo-go</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/14/%E5%BF%AB%E9%80%9F%E4%B8%8A%E6%89%8B-dubbo-go/</link><pubDate>Thu, 14 Jan 2021 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/14/%E5%BF%AB%E9%80%9F%E4%B8%8A%E6%89%8B-dubbo-go/</guid><description>&lt;h2 id="前言">前言&lt;/h2>
&lt;p>每次技术调研总会发现自己学不动了怎么办？用已有的知识来拓展要学习的新知识就好了~ by LinkinStar&lt;/p>
&lt;p>最近需要调研使用 dubbo，之前完全是 0 基础，对于 dubbo 只存在于听说，今天上手实战一把，告诉你如何快速用 go 上手 dubbo&lt;/p>
&lt;p>PS：以下的学习方式适用于很多新技术&lt;/p>
&lt;h2 id="基本概念">基本概念&lt;/h2>
&lt;p>&lt;strong>首先学习一个技术首先要看看它的整体架构和基本概念&lt;/strong>，每个技术都有着自己的名词解释和实现方式，如果文档齐全就简单很多。&lt;/p>
&lt;p>&lt;a href="https://deploy-preview-3199--dubbo.netlify.app/zh-cn/docs/languages/golang/dubbo-go-3.0/preface/architecture/">基本概念&lt;/a>&lt;/p>
&lt;p>大致浏览了背景、需求、架构之后基本上有一个大致概念&lt;/p>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubbo-architecture.png">&lt;/p>
&lt;p>其实整体架构和很多微服务的架构都是类似的，就是有一个注册中心管理所有的服务列表，服务提供方先向注册中心注册，而消费方向注册中心请求服务列表，通过服务列表调用最终的服务。总的来说 dubbo 将整个过程封装在了里面，而作为使用者的我们来说更加关心业务实现，它帮我们做好了治理的工作。&lt;/p>
&lt;p>然后我抓住了几个我想要知道的重点：&lt;/p>
&lt;ul>
&lt;li>注册中心可替换，官方推荐的是 zk&lt;/li>
&lt;li>如果有变更，注册中心将基于长连接推送变更数据给消费者，注册中心，服务提供者，服务消费者三者之间均为长连接&lt;/li>
&lt;li>基于软负载均衡算法，选一台提供者进行调用，如果调用失败，再选另一台调用&lt;/li>
&lt;li>消费者在本地缓存了提供者列表&lt;/li>
&lt;/ul>
&lt;h2 id="实际上手">实际上手&lt;/h2>
&lt;p>官网文档中也给出如果使用 golang 开发，那么有 &lt;a href="https://links.jianshu.com/go?to=https%3A%2F%2Fgithub.com%2Fapache%2Fdubbo-go">https://github.com/apache/dubbo-go&lt;/a> 可以用，那么废话不多说，上手实践一把先。因为你看再多，都比不上实践一把来学的快。&lt;/p>
&lt;h3 id="环境搭建">环境搭建&lt;/h3>
&lt;p>大多数教程就会跟你说，一个 helloWorld 需要 zookeeper 环境，但是不告诉你如何搭建，因为这对于他们来说太简单了，而我不一样，我是 0 基础，那如何快速搭建一个需要调研项目的环境呢？最好的方式就是 docker。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">version&lt;/span>: &lt;span style="color:#2aa198">&amp;#39;3&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">services&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">zookeeper&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">image&lt;/span>: zookeeper
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">ports&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#2aa198">2181&lt;/span>:&lt;span style="color:#2aa198">2181&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">admin&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">image&lt;/span>: apache/dubbo-admin
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">depends_on&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - zookeeper
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">ports&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#2aa198">8080&lt;/span>:&lt;span style="color:#2aa198">8080&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">environment&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - admin.registry.address=zookeeper://zookeeper:2181
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - admin.config-center=zookeeper://zookeeper:2181
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - admin.metadata-report.address=zookeeper://zookeeper:2181
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">version&lt;/span>: &lt;span style="color:#2aa198">&amp;#39;3&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">services&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">zookeeper&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">image&lt;/span>: zookeeper
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">ports&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#2aa198">2181&lt;/span>:&lt;span style="color:#2aa198">2181&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">admin&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">image&lt;/span>: chenchuxin/dubbo-admin
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">depends_on&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - zookeeper
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">ports&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#2aa198">8080&lt;/span>:&lt;span style="color:#2aa198">8080&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">environment&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - dubbo.registry.address=zookeeper://zookeeper:2181
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - dubbo.admin.root.password=root
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - dubbo.admin.guest.password=guest
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>上面两个 docker-compose 文件一个是官方提供的管理工具，一个包含的是个人修改之后的管理工具，记住这里有个用户名密码是 root-root，看你喜欢&lt;/p></description></item><item><title>涂鸦智能 dubbo-go 亿级流量的实践与探索</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/14/%E6%B6%82%E9%B8%A6%E6%99%BA%E8%83%BD-dubbo-go-%E4%BA%BF%E7%BA%A7%E6%B5%81%E9%87%8F%E7%9A%84%E5%AE%9E%E8%B7%B5%E4%B8%8E%E6%8E%A2%E7%B4%A2/</link><pubDate>Thu, 14 Jan 2021 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/14/%E6%B6%82%E9%B8%A6%E6%99%BA%E8%83%BD-dubbo-go-%E4%BA%BF%E7%BA%A7%E6%B5%81%E9%87%8F%E7%9A%84%E5%AE%9E%E8%B7%B5%E4%B8%8E%E6%8E%A2%E7%B4%A2/</guid><description>&lt;p>dubbo 是一个基于 Java 开发的高性能的轻量级 RPC 框架，dubbo 提供了丰富的服务治理功能和优秀的扩展能力。而 dubbo-go 在 java 与 golang 之间提供统一的服务化能力与标准，是涂鸦智能目前最需要解决的主要问题。本文分为实践和快速接入两部分，分享在涂鸦智能的 &lt;a href="http://github.com/apache/dubbo-go">dubbo-go&lt;/a> 实战经验，意在帮助用户快速接入 dubbo-go RPC 框架，希望能让大家少走些弯路。&lt;/p>
&lt;p>另外，文中的测试代码基于 dubbo-go版本 &lt;a href="https://github.com/apache/dubbo-go/releases/tag/v1.4.0">v1.4.0&lt;/a>。&lt;/p>
&lt;h2 id="dubbo-go-网关实践">dubbo-go 网关实践&lt;/h2>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubbo-go/tuya/p1.png">&lt;/p>
&lt;p>dubbo-go 在涂鸦智能的使用情况如上图，接下来会为大家详细介绍落地细节，希望这些在生产环境中总结的经验能够帮助到大家。&lt;/p>
&lt;h3 id="背景">背景&lt;/h3>
&lt;p>在涂鸦智能，dubbo-go 已经作为了 golang 服务与原有 dubbo 集群打通的首选 RPC 框架。其中比较有代表性的 open-gateway 网关系统（下文统一称 gateway，开源版本见 &lt;a href="https://github.com/dubbogo/dubbo-go-proxy">https://github.com/dubbogo/dubbo-go-proxy&lt;/a>）。该 gateway 动态加载内部 dubbo 接口信息，以HTTP API 的形式对外暴露。该网关意在解决上一代网关的以下痛点。&lt;/p>
&lt;ul>
&lt;li>&lt;code>通过页面配置 dubbo 接口开放规则，步骤繁琐，权限难以把控。&lt;/code>&lt;/li>
&lt;li>&lt;code>接口非 RESTful 风格，对外部开发者不友好。&lt;/code>&lt;/li>
&lt;li>&lt;code>依赖繁重，升级风险大。&lt;/code>&lt;/li>
&lt;li>&lt;code>并发性能问题。&lt;/code>&lt;/li>
&lt;/ul>
&lt;h3 id="架构设计">架构设计&lt;/h3>
&lt;p>针对如上痛点，随即着手准备设计新的 gateway 架构。首先就是语言选型，golang 的协程调用模型使得 golang 非常适合构建 IO 密集型的应用，且应用部署上也较 java 简单。经过调研后我们敲定使用 golang 作为 proxy 的编码语言，并使用 dubbo-go 用于连接 dubbo provider 集群。provider 端的业务应用通过使用 java 的插件，以注解形式配置 API 配置信息，该插件会将配置信息和 dubbo 接口元数据更新到元数据注册中心（下图中的 redis ）。这样一来，配置从管理后台页面转移到了程序代码中。开发人员在编码时，非常方便地看到 dubbo 接口对外的 API 描述，无需从另外一个管理后台配置 API 的使用方式。&lt;/p></description></item><item><title>dubbo-go 中如何实现路由规则功能</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/12/dubbo-go-%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/</link><pubDate>Tue, 12 Jan 2021 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/12/dubbo-go-%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/</guid><description>&lt;h2 id="lets-go">Let‘s Go!&lt;/h2>
&lt;p>最近在 Apache/dubbo-go（以下简称 dubbo-go ）社区中，路由规则突然成了呼声最高的功能之一。那到底为什么需要路由规则？&lt;/p>
&lt;p>先路由规则需要实现的功能：&lt;/p>
&lt;p>路由规则（ routing rule ）是为了改变网络流量所经过的途径而修改路由信息的技术，主要通过改变路由属性（包括可达性）来实现。在发起一次 RPC 调用前起到过滤目标服务器地址的作用，过滤后的地址列表，将作为消费端最终发起 RPC 调用的备选地址。&lt;/p>
&lt;p>试想该下场景：使用 dubbo-go 在生产环境上，排除预发布机。使用路由规则实现不是很合适吗？&lt;/p>
&lt;p>虽然知道了路由规则需要实现什么功能，但还不足以实现一个完整的路由规则功能。除此之外，还需要知道如何方便的管理路由规则。&lt;/p>
&lt;h2 id="目标">目标&lt;/h2>
&lt;p>综上所述，可以总结出以下 &lt;strong>目标&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>支持方便扩展路由规则的配置；&lt;/li>
&lt;li>可以方便的管理路由规则配置，如支持本地与远程配置中心管理；&lt;/li>
&lt;li>与 Dubbo 现有的配置中心内的路由规则配置文件兼容，降低在新增语言栈的学习及使用成本；&lt;/li>
&lt;/ul>
&lt;h2 id="路由规则设计">路由规则设计&lt;/h2>
&lt;p>在设计之初，首先要考虑的是路由规则应该放在整个服务治理周期的哪个阶段呢？&lt;/p>
&lt;p>有些读者可能会有点困惑，我连架构图都不知道，如何考虑在哪个阶段？不怕，下图马上给你解惑。&lt;/p>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubbo-go/dubbo-go-arch-2.png">&lt;/p>
&lt;p>可以看到图中的 Router 就是路由规则插入的位置，目前路由规则主要用于控制 Consumer 到 Provider 之间的网络流量的路由路径。&lt;/p>
&lt;p>除此之外，还有几个问题是需要优先考虑：&lt;/p>
&lt;p>1.需要什么功能？&lt;/p>
&lt;ul>
&lt;li>通过配置信息生成路由规则，包括：读取并解析本地配置文件，读取并解析配置中心的配置。以责任链模式串联起来。&lt;/li>
&lt;li>通过路由规则，匹配本地信息与远端服务信息，过滤出可以调用的远端节点，再进行负载均衡。&lt;/li>
&lt;/ul>
&lt;p>2.如何设计接口？&lt;/p>
&lt;p>通过第一点，我们能设计出以下接口来实现所需的功能。&lt;/p>
&lt;ul>
&lt;li>路由规则接口：用于路由规则过滤出可以调用的远端节点。&lt;/li>
&lt;li>路由规则责任链接口：允许执行多个路由规则。&lt;/li>
&lt;li>配置信息生成路由规则接口：解析内部配置信息（common.URL）生成对应的路由规则。&lt;/li>
&lt;li>配置文件生成路由规则接口：解析配置文件生成对应的路由规则。&lt;/li>
&lt;/ul>
&lt;p>3.如何实现本地与远程路由规则配置加载？&lt;/p>
&lt;ul>
&lt;li>本地路由规则配置：在原配置加载阶段，新增读取路由配置文件。使用 &lt;code>FIleRouterFactory&lt;/code> 解析后，生成对应路由规则，放置到内存中备用。&lt;/li>
&lt;li>远程路由规则配置：在 zookeeper 注册并监听静态资源目录后。读取静态资源，筛选符合路由规则配置信息，通过 &lt;code>RouterFactory&lt;/code> 生成对应路由规则，放置到内存中备用。&lt;/li>
&lt;/ul>
&lt;h3 id="router">Router&lt;/h3>
&lt;p>匹配及过滤远程实例的路由规则。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-golang" data-lang="golang">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// Router
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>&lt;span style="color:#268bd2">type&lt;/span> Router &lt;span style="color:#268bd2">interface&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#586e75">// Route determine the target invoker list.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>	&lt;span style="color:#268bd2">Route&lt;/span>([]protocol.Invoker, &lt;span style="color:#719e07">*&lt;/span>common.URL, protocol.Invocation) []protocol.Invoker
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#586e75">// Priority return priority in router
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>	&lt;span style="color:#586e75">// 0 to ^int(0) is better
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>	&lt;span style="color:#268bd2">Priority&lt;/span>() &lt;span style="color:#dc322f">int64&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#586e75">// URL return URL in router
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>	&lt;span style="color:#268bd2">URL&lt;/span>() common.URL
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>目前已有实现类包括：&lt;/p></description></item><item><title>记一次对 dubbo-go-hessian2 的性能优化</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/12/%E8%AE%B0%E4%B8%80%E6%AC%A1%E5%AF%B9-dubbo-go-hessian2-%E7%9A%84%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/</link><pubDate>Tue, 12 Jan 2021 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/12/%E8%AE%B0%E4%B8%80%E6%AC%A1%E5%AF%B9-dubbo-go-hessian2-%E7%9A%84%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/</guid><description>&lt;blockquote>
&lt;p>dubbo-go-hessian2 是一个用 Go 实现的 hessian 协议 v2.0 版本的序列化库。从项目名称里可以看到主要用在 dubbo-go 这个项目里。hessian 协议作为 dubbo 的默认协议，因此对性能有比较高的要求。&lt;/p>
&lt;/blockquote>
&lt;h2 id="立项">立项&lt;/h2>
&lt;p>譬如有网文 基于Go的马蜂窝旅游网分布式IM系统技术实践 把 dubbo-go 与其他 RPC 框架对比如下：&lt;/p>
&lt;ol>
&lt;li>Go STDPRC: Go 标准库的 RPC，性能最优，但是没有治理；&lt;/li>
&lt;li>RPCX: 性能优势 2*GRPC + 服务治理；&lt;/li>
&lt;li>GRPC: 跨语言，但性能没有 RPCX 好；&lt;/li>
&lt;li>TarsGo: 跨语言，性能 5*GRPC，缺点是框架较大，整合起来费劲；&lt;/li>
&lt;li>Dubbo-Go: 性能稍逊一筹，比较适合 Go 和 Java 间通信场景使用&lt;/li>
&lt;/ol>
&lt;p>有鉴于此，社区便开始组织部分人力，启动了对 dubbo-go 性能优化【同时也欢迎上文作者到钉钉群 23331795 与我们社区交流】。考察 dubbo-go 的各个组件，大家不约而同地决定首先优化比较独立的 dubbo-go-hessian2。&lt;/p>
&lt;h2 id="起步">起步&lt;/h2>
&lt;p>在最开始的时候，并没有太想清楚需要做什么，改哪个地方，要优化到何种程度，所以最简单的办法就是看看现状。&lt;/p>
&lt;p>首先，写了一个简单的例子，把常见的类型到一个结构体里，然后测一下耗时。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">type&lt;/span> Mix &lt;span style="color:#268bd2">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> A &lt;span style="color:#dc322f">int&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> B &lt;span style="color:#dc322f">string&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> CA time.Time
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> CB &lt;span style="color:#dc322f">int64&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> CC &lt;span style="color:#dc322f">string&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> CD []&lt;span style="color:#dc322f">float64&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> D &lt;span style="color:#268bd2">map&lt;/span>[&lt;span style="color:#dc322f">string&lt;/span>]&lt;span style="color:#268bd2">interface&lt;/span>{}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>m &lt;span style="color:#719e07">:=&lt;/span> Mix{A: &lt;span style="color:#b58900">int&lt;/span>(&lt;span style="color:#2aa198">&amp;#39;a&amp;#39;&lt;/span>), B: &lt;span style="color:#2aa198">`hello`&lt;/span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>m.CD = []&lt;span style="color:#dc322f">float64&lt;/span>{&lt;span style="color:#2aa198">1&lt;/span>, &lt;span style="color:#2aa198">2&lt;/span>, &lt;span style="color:#2aa198">3&lt;/span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// 再加一层，使得数据显得复杂一些
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>m.D = &lt;span style="color:#268bd2">map&lt;/span>[&lt;span style="color:#dc322f">string&lt;/span>]&lt;span style="color:#268bd2">interface&lt;/span>{}{&lt;span style="color:#2aa198">`floats`&lt;/span>: m.CD, &lt;span style="color:#2aa198">`A`&lt;/span>: m.A, &lt;span style="color:#2aa198">`m`&lt;/span>: m}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>看起来这个结构体跟真实环境里可能不太一样，但是用来分析瓶颈应该是足够了。&lt;/p></description></item><item><title>Dubbo Go 踩坑记</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/11/dubbo-go-%E8%B8%A9%E5%9D%91%E8%AE%B0/</link><pubDate>Mon, 11 Jan 2021 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/11/dubbo-go-%E8%B8%A9%E5%9D%91%E8%AE%B0/</guid><description>&lt;h2 id="扯淡">扯淡&lt;/h2>
&lt;h3 id="前尘">前尘&lt;/h3>
&lt;p>由于我的一个项目需要做公司用户鉴权，而组内其他小伙伴刚好有一个 &lt;em>dubbo&lt;/em> 的鉴权 &lt;em>rpc&lt;/em> ，一开始我是打算直接的读 &lt;em>redis&lt;/em> 数据然后自己做解密。工作进行到一半，由于考虑到如果以后这个服务有任何变动，我这边要有联动行为，所以改用 &lt;em>go&lt;/em> 来调用 &lt;em>dubbo&lt;/em> 的 &lt;em>rpc&lt;/em> ，于是我在 &lt;em>github&lt;/em> 上找到了 &lt;a href="https://github.com/AlexStocks">雨神&lt;/a> 的 &lt;code>https://github.com/apache/dubbo-go-samples/tree/master&lt;/code> (PS: 这个是 &lt;em>dubbo-go&lt;/em> 前身)。不得不说，雨神是热心的人儿啊，当时还帮着我调试代码。最后也是接入了一个阉割版的吧，主要是当时 &lt;em>hessian2&lt;/em> 对泛型支持的不怎么好。&lt;/p>
&lt;h3 id="现在">现在&lt;/h3>
&lt;p>目前 &lt;a href="https://github.com/apache/dubbo-go">dubbo-go&lt;/a>隶属于 &lt;em>apache&lt;/em> 社区，相比以前做了部分重构，并且维护也很活跃了。&lt;/p>
&lt;h2 id="接入">接入&lt;/h2>
&lt;h3 id="问题">问题&lt;/h3>
&lt;p>目前整个项目在快速的迭代中，很多功能还没有完善，维护人员还没有时间来完善文档，所以在接入的时候要自己看源码或调试。&lt;/p>
&lt;h3 id="说明">说明&lt;/h3>
&lt;p>目前我司在使用 &lt;em>dubbo&lt;/em> 的过程使用的 &lt;em>zookeeper&lt;/em> 作为注册中心，序列化是 &lt;em>hessian2&lt;/em> ，所以我们要做如下初始化：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-golang" data-lang="golang">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">import&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> _ &lt;span style="color:#2aa198">&amp;#34;github.com/apache/dubbo-go/common/proxy/proxy_factory&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> _ &lt;span style="color:#2aa198">&amp;#34;github.com/apache/dubbo-go/registry/protocol&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> _ &lt;span style="color:#2aa198">&amp;#34;github.com/apache/dubbo-go/filter/impl&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> _ &lt;span style="color:#2aa198">&amp;#34;github.com/apache/dubbo-go/cluster/cluster_impl&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> _ &lt;span style="color:#2aa198">&amp;#34;github.com/apache/dubbo-go/cluster/loadbalance&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> _ &lt;span style="color:#2aa198">&amp;#34;github.com/apache/dubbo-go/registry/zookeeper&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> )
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="配置">配置&lt;/h3>
&lt;p>由于我是接入客户端，所以我这边只配置了 &lt;em>ConsumerConfig&lt;/em> 。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">dubbo&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75"># client&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">request_timeout&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;3s&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#586e75"># connect timeout&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">connect_timeout&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;3s&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">check&lt;/span>: &lt;span style="color:#cb4b16">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">application&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">organization&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;dfire.com&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">name&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;soa.sso.ITokenService&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">module&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;dubbogo token service client&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">version&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;1.0.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">owner&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;congbai&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">registries&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;hangzhouzk&amp;#34;&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protocol&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;zookeeper&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">timeout&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;3s&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">address&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;zk1.2dfire-daily.com:2181&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">username&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">password&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">references&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#2aa198">&amp;#34;ITokenService&amp;#34;&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">registry&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;hangzhouzk&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protocol&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;dubbo&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">interface&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;com.dfire.soa.sso.ITokenService&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">version&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;1.0.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">methods&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">name&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;validate&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">retries&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;3&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>我这里是把 &lt;em>dubbo-go&lt;/em> 作为第三方库来用，所以我没使用官方 &lt;a href="https://github.com/apache/dubbo-go-samples/">dubbo-samples&lt;/a> 那样在 &lt;em>init&lt;/em> 函数中读入配置。&lt;/p></description></item><item><title>Dubbo Go 的前世今生</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/11/dubbo-go-%E7%9A%84%E5%89%8D%E4%B8%96%E4%BB%8A%E7%94%9F/</link><pubDate>Mon, 11 Jan 2021 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/11/dubbo-go-%E7%9A%84%E5%89%8D%E4%B8%96%E4%BB%8A%E7%94%9F/</guid><description>&lt;p>&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubbo-go/dubbo-go-history.png">&lt;/p>
&lt;p>dubbo-go 是目前 Dubbo 多语言生态最火热的项目。dubbo-go 最早的版本应该要追溯到 2016 年，由社区于雨同学编写 dubbo-go 的初版。当时很多东西没有现成的轮子，如 Go 语言没有像 netty 一样的基于事件的网络处理引擎、 hessian2 协议没有 Go 语言版本实现，加上当时 Dubbo 也没有开始重新维护。所以从协议库到网络引擎，再到上层 dubbo-go ，其实都是从零开始写的。&lt;/p>
&lt;p>在 2018 年，携程开始做 Go 语言的一些中间件以搭建内部的 Go 语言生态，需要有一个 Go 的服务框架可以与携程的现有 dubbo soa 生态互通。所以由我负责重构了 dubbo－go 并开源出这个版本。当时调研了很多开源的 Go 语言服务框架，当时能够支持 hessian2 协议的并跟 Dubbo 可以打通的仅找到了当时于雨写的 dubbo-go 早期版本。由于携程对社区版本的 Dubbo 做了挺多的扩展，源于对扩展性的需求我们 Go 语言版本需要一个更易于扩展的版本，加上当时这个版本本身的功能也比较简单，所以我们找到了作者合作重构了一个更好的版本。经过了大半年时间，在上图第三阶段 19 年 6 月的时候，基本上已经把 dubbo-go 重构了一遍，总体的思路是参考的 Dubbo 整体的代码架构，用Go语言完全重写了一个完整的具备服务端跟消费端的 Golang rpc/ 微服务框架。&lt;/p>
&lt;p>后来我们将重构后的版本 dubbo-go 1.0 贡献给 Apache 基金会，到现在已经过去了两个多月的时间，近期社区发布了1.1版本。目前为止，已经有包括携程在内的公司已经在生产环境开始了试用和推广。&lt;/p>
&lt;h3 id="start-dubbo-go">Start dubbo-go&lt;/h3>
&lt;p>现在的 dubbo-go 已经能够跟 Java 版本做比较好的融合互通，同时 dubbo-go 自身也是一个完成的 Go 语言 rpc/ 微服务框架，它也可以脱离 java dubbo 来独立使用。&lt;/p></description></item><item><title>冲上云原生，Dubbo 发布 Go 版本</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/11/%E5%86%B2%E4%B8%8A%E4%BA%91%E5%8E%9F%E7%94%9Fdubbo-%E5%8F%91%E5%B8%83-go-%E7%89%88%E6%9C%AC/</link><pubDate>Mon, 11 Jan 2021 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/11/%E5%86%B2%E4%B8%8A%E4%BA%91%E5%8E%9F%E7%94%9Fdubbo-%E5%8F%91%E5%B8%83-go-%E7%89%88%E6%9C%AC/</guid><description>&lt;p>5 月 21 日，经过一年多的孵化，Apache Dubbo 从 Apache 软件基金会毕业，成为 Apache 顶级项目。&lt;/p>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubbo-go/dubbo-tlp-twitter.jpg">&lt;/p>
&lt;p>Dubbo 是阿里于 2011 年开源的一款高性能 RPC 框架，在 Java 生态中具有不小的影响力。当初经历过一段被外界诟病的“停止维护”灰暗时光，后来在 2017 年 Dubbo 浪子回头，官方宣布重新重点维护。&lt;/p>
&lt;p>重新启航的 Dubbo 将首要目标定位于重新激活社区，赢回开发者的信任，并且逐渐将 Dubbo 打造成一个国际化与现代化的项目，目前距离宣布重启已经过了一年半的时间。&lt;/p>
&lt;p>在这个过程中，Dubbo 发布了多个版本，并逐渐从一个 RPC 框架向微服务生态系统转变，18 年年初 Dubbo 入驻 Apache 软件基金会孵化器，开始以 Apache 之道发展社区。&lt;/p>
&lt;p>一年之后，Dubbo 在 Apache 孵化器中发布了重启维护以来的首个里程碑版本 2.7.0，添加了社区呼声很高的异步化支持，以及注册中心与配置中心分离等特性。&lt;/p>
&lt;p>这期间 Dubbo 3.0 的开发工作也被提上了日程，今年 4 月中旬，官方正式公布了 Dubbo 3.0 的进度，此版本新特性包括支持 Filter 链的异步化、响应式编程、云原生/Service Mesh 方向的探索，以及与阿里内外融合。&lt;/p>
&lt;p>然后，Dubbo 毕业了。毕业后的 Dubbo 近期有什么消息呢？生态还在发展，Dubbo 社区在前几日公开了 &lt;a href="https://github.com/dubbo/awesome-dubbo/blob/master/slides/meetup/201905@beijing/DUBBO%20ROADMAP%202019.pdf">Dubbo Roadmap 2019&lt;/a>，计划在 2020 年 2 月份发布 Dubbo 3.0 正式版，感兴趣的同学可以详细查阅。&lt;/p></description></item><item><title>Dubbo Go 快速开始</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/11/dubbo-go-%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B/</link><pubDate>Mon, 11 Jan 2021 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/11/dubbo-go-%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B/</guid><description>&lt;h2 id="环境">环境&lt;/h2>
&lt;ul>
&lt;li>Go编程环境&lt;/li>
&lt;li>启动zookeeper服务，也可以使用远程实例&lt;/li>
&lt;/ul>
&lt;h2 id="从服务端开始">从服务端开始&lt;/h2>
&lt;h3 id="第一步编写-provider-结构体和提供服务的方法">第一步：编写 &lt;code>Provider&lt;/code> 结构体和提供服务的方法&lt;/h3>
&lt;blockquote>
&lt;p>&lt;a href="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&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;ol>
&lt;li>编写需要被编码的结构体，由于使用 &lt;code>Hessian2&lt;/code> 作为编码协议，&lt;code>User&lt;/code> 需要实现 &lt;code>JavaClassName&lt;/code> 方法，它的返回值在dubbo中对应User类的类名。&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">type&lt;/span> User &lt;span style="color:#268bd2">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	Id &lt;span style="color:#dc322f">string&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	Name &lt;span style="color:#dc322f">string&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	Age &lt;span style="color:#dc322f">int32&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	Time time.Time
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (u User) &lt;span style="color:#268bd2">JavaClassName&lt;/span>() &lt;span style="color:#dc322f">string&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;com.ikurento.user.User&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol>
&lt;li>编写业务逻辑，&lt;code>UserProvider&lt;/code> 相当于dubbo中的一个服务实现。需要实现 &lt;code>Reference&lt;/code> 方法，返回值是这个服务的唯一标识，对应dubbo的 &lt;code>beans&lt;/code> 和 &lt;code>path&lt;/code> 字段。&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">type&lt;/span> UserProvider &lt;span style="color:#268bd2">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (u &lt;span style="color:#719e07">*&lt;/span>UserProvider) &lt;span style="color:#268bd2">GetUser&lt;/span>(ctx context.Context, req []&lt;span style="color:#268bd2">interface&lt;/span>{}) (&lt;span style="color:#719e07">*&lt;/span>User, &lt;span style="color:#dc322f">error&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#b58900">println&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;req:%#v&amp;#34;&lt;/span>, req)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	rsp &lt;span style="color:#719e07">:=&lt;/span> User{&lt;span style="color:#2aa198">&amp;#34;A001&amp;#34;&lt;/span>, &lt;span style="color:#2aa198">&amp;#34;hellowworld&amp;#34;&lt;/span>, &lt;span style="color:#2aa198">18&lt;/span>, time.&lt;span style="color:#268bd2">Now&lt;/span>()}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#b58900">println&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;rsp:%#v&amp;#34;&lt;/span>, rsp)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#719e07">&amp;amp;&lt;/span>rsp, &lt;span style="color:#cb4b16">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (u &lt;span style="color:#719e07">*&lt;/span>UserProvider) &lt;span style="color:#268bd2">Reference&lt;/span>() &lt;span style="color:#dc322f">string&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;UserProvider&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol>
&lt;li>注册服务和对象&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">init&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	config.&lt;span style="color:#268bd2">SetProviderService&lt;/span>(&lt;span style="color:#b58900">new&lt;/span>(UserProvider))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#586e75">// ------for hessian2------
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>	hessian.&lt;span style="color:#268bd2">RegisterPOJO&lt;/span>(&lt;span style="color:#719e07">&amp;amp;&lt;/span>User{})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="第二步编写主程序">第二步：编写主程序&lt;/h3>
&lt;blockquote>
&lt;p>&lt;a href="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&lt;/a>&lt;/p></description></item><item><title>Dubbo Go 中 metrics 的设计</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/11/dubbo-go-%E4%B8%AD-metrics-%E7%9A%84%E8%AE%BE%E8%AE%A1/</link><pubDate>Mon, 11 Jan 2021 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/11/dubbo-go-%E4%B8%AD-metrics-%E7%9A%84%E8%AE%BE%E8%AE%A1/</guid><description>&lt;p>最近因为要在 Apache/dubbo-go（以下简称 dubbo-go ）里面实现类似的这个 metrics 功能，于是花了很多时间去了解现在 Dubbo 里面的 metrics 是怎么实现的。该部分，实际上是被放在一个独立的项目里面，即
metrics ，见 &lt;a href="https://github.com/flycash/dubbo-go/tree/feature/MetricsFilter">https://github.com/flycash/dubbo-go/tree/feature/MetricsFilter&lt;/a> 下 metrics 子目录。&lt;/p>
&lt;p>总体上来说，Dubbo 的 metrics 是一个从设计到实现都非常优秀的模块，理论上来说，大部分的 Java 项目是可以直接使用 metrics 的。但也因为兼顾性能、扩展性等各种非功能特性，所以初看代码会有种无从下手的感觉。&lt;/p>
&lt;p>今天这篇文章将会从比较大的概念和抽象上讨论一下 dubbo-go 中的 metrics 模块的设计——实际上也就是 Dubbo 中的 metrics 的设计。因为我仅仅是将 Dubbo 里面的相关内容在 dubbo-go 中复制一份。&lt;/p>
&lt;p>目前 dubbo-go 的 metrics 刚刚开始起步，第一个 PR 是： &lt;a href="https://github.com/apache/dubbo-go/pull/278">https://github.com/apache/dubbo-go/pull/278&lt;/a>&lt;/p>
&lt;h2 id="总体设计">总体设计&lt;/h2>
&lt;h3 id="metrics">Metrics&lt;/h3>
&lt;p>要想理解 metrics 的设计，首先要理解，我们需要收集一些什么数据。我们可以轻易列举出来在 RPC 领域里面我们所关心的各种指标，诸如每个服务的调用次数，响应时间；如果更加细致一点，还有各种响应时间的分布，平均响应时间，999线……&lt;/p>
&lt;p>但是上面列举的是从数据的内容上划分的。 metrics 在抽象上，则是摒弃了这种划分方式，而是结合了数据的特性和表现形式综合划分的。&lt;/p>
&lt;p>从源码里面很容易找到这种划分的抽象。&lt;/p>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubbo-go/metrics/p1.png">&lt;/p>
&lt;p>metrics 设计了 Metric 接口作为所有数据的顶级抽象：&lt;/p>
&lt;p>在 Dubbo 里面，其比较关键的子接口是：&lt;/p>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubbo-go/metrics/p2.webp">&lt;/p>
&lt;p>为了大家理解，这里我抄一下这些接口的用途：&lt;/p>
&lt;ul>
&lt;li>Gauge: 一种实时数据的度量，反映的是瞬态的数据，不具有累加性，例如当前 JVM 的线程数；&lt;/li>
&lt;li>Counter: 计数器型指标，适用于记录调用总量等类型的数据；&lt;/li>
&lt;li>Histogram : 直方分布指标，例如，可以用于统计某个接口的响应时间，可以展示 50%, 70%, 90% 的请求响应时间落在哪个区间内；&lt;/li>
&lt;li>Meter: 一种用于度量一段时间内吞吐率的计量器。例如，一分钟内，五分钟内，十五分钟内的qps指标；&lt;/li>
&lt;li>Timer: Timer相当于Meter+Histogram的组合，同时统计一段代码，一个方法的qps，以及执行时间的分布情况；&lt;/li>
&lt;/ul>
&lt;p>目前 dubbo-go 只实现了 FastCompass ，它也是 Metric 的子类：&lt;/p></description></item><item><title>Dubbo Go 中的 TPS Limit 设计与实现</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/11/dubbo-go-%E4%B8%AD%E7%9A%84-tps-limit-%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0/</link><pubDate>Mon, 11 Jan 2021 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/11/dubbo-go-%E4%B8%AD%E7%9A%84-tps-limit-%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0/</guid><description>&lt;h1 id="前言">前言&lt;/h1>
&lt;p>&lt;a href="https://links.jianshu.com/go?to=http%3A%2F%2Fdubbo.apache.org%2Fen-us%2F">Apache Dubbo&lt;/a>是由阿里开源的一个RPC框架，除了基本的RPC功能以外，还提供了一整套的服务治理相关功能。目前它已经是Apache基金会下的顶级项目。&lt;/p>
&lt;p>而&lt;a href="https://links.jianshu.com/go?to=https%3A%2F%2Fgithub.com%2Fapache%2Fdubbo-go">dubbogo&lt;/a>则是dubbo的go语言实现。&lt;/p>
&lt;p>最近在&lt;code>dubbogo&lt;/code>的&lt;code>todo list&lt;/code>上发现，它还没有实现&lt;code>TPS Limit&lt;/code>的模块，于是就抽空实现了这个部分。&lt;/p>
&lt;p>&lt;code>TPS limit&lt;/code>实际上就是限流，比如说限制一分钟内某个接口只能访问200次，超过这个次数，则会被拒绝服务。在&lt;code>Dubbo&lt;/code>的Java版本上，只有一个实现，就是&lt;code>DefaultTPSLimiter&lt;/code>。&lt;/p>
&lt;p>&lt;code>DefaultTPSLimiter&lt;/code>是在服务级别上进行限流。虽然&lt;code>dubbo&lt;/code>的官方文档里面声称可以在&lt;code>method&lt;/code>级别上进行限流，但是我看了一下它的源码，实际上这个是做不到的。当然，如果自己通过实现&lt;code>Filter&lt;/code>接口来实现&lt;code>method&lt;/code>级别的限流，那么自然是可以的——这样暴露了&lt;code>dubbo&lt;/code>Java版本实现的另外一个问题，就是&lt;code>dubbo&lt;/code>的&lt;code>TpsLimitFilter&lt;/code>实现，是不允许接入自己&lt;code>TpsLimiter&lt;/code>的实现的。这从它的源码也可以看出来：&lt;/p>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubbo-go/tps-limit-filter.png">&lt;/p>
&lt;p>它直接写死了&lt;code>TpsLimiter&lt;/code>的实现。&lt;/p>
&lt;p>这个实现的目前只是合并到了&lt;code>develop&lt;/code>上，等下次发布正式版本的时候才会发布出来。&lt;/p>
&lt;p>Github: &lt;a href="https://links.jianshu.com/go?to=https%3A%2F%2Fgithub.com%2Fapache%2Fdubbo-go%2Fpull%2F237">https://github.com/apache/dubbo-go/pull/237&lt;/a>&lt;/p>
&lt;h1 id="设计思路">设计思路&lt;/h1>
&lt;p>于是我大概参考了一下&lt;code>dubbo&lt;/code>已有的实现，做了一点改进。&lt;/p>
&lt;p>&lt;code>dubbo&lt;/code>里面的核心抽象是&lt;code>TpsLimiter&lt;/code>接口。&lt;code>TpsLimitFilter&lt;/code>只是简单调用了一下这个接口的方法而已：&lt;/p>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubbo-go/tps-limiter.png">&lt;/p>
&lt;p>这个抽象是很棒的。但是还欠缺了一些抽象。&lt;/p>
&lt;p>实际上，一个TPS Limit就要解决三个问题：&lt;/p>
&lt;ol>
&lt;li>对什么东西进行&lt;code>limit&lt;/code>。比如说，对服务进行限流，或者对某个方法进行限流，或者对IP进行限流，或者对用户进行限流；&lt;/li>
&lt;li>如何判断已经&lt;code>over limitation&lt;/code>。这是从算法层面上考虑，即用什么算法来判断某个调用进来的时候，已经超过配置的上限了；&lt;/li>
&lt;li>被拒绝之后该如何处理。如果一个请求被断定为已经&lt;code>over limititation&lt;/code>了，那么该怎么处理；&lt;/li>
&lt;/ol>
&lt;p>所以在&lt;code>TpsLimiter&lt;/code>接口的基础上，我再加了两个抽象：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-golang" data-lang="golang">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">type&lt;/span> TpsLimiter &lt;span style="color:#268bd2">interface&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#586e75">// IsAllowable will check whether this invocation should be enabled for further process
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>	&lt;span style="color:#268bd2">IsAllowable&lt;/span>(&lt;span style="color:#719e07">*&lt;/span>common.URL, protocol.Invocation) &lt;span style="color:#dc322f">bool&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-golang" data-lang="golang">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">type&lt;/span> TpsLimitStrategy &lt;span style="color:#268bd2">interface&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#586e75">// IsAllowable will return true if this invocation is not over limitation
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>	&lt;span style="color:#268bd2">IsAllowable&lt;/span>() &lt;span style="color:#dc322f">bool&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-golang" data-lang="golang">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">type&lt;/span> RejectedExecutionHandler &lt;span style="color:#268bd2">interface&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#586e75">// RejectedExecution will be called if the invocation was rejected by some component.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>	&lt;span style="color:#268bd2">RejectedExecution&lt;/span>(url &lt;span style="color:#719e07">*&lt;/span>common.URL, invocation protocol.Invocation) protocol.Result
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>TpsLimiter&lt;/code>对应到Java的&lt;code>TpsLimiter&lt;/code>，两者是差不多。在我的设想里面，它既是顶级入口，还需要承担解决第一个问题的职责。&lt;/p></description></item><item><title>dubbo-go 中如何实现远程配置管理？</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/11/dubbo-go-%E4%B8%AD%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E8%BF%9C%E7%A8%8B%E9%85%8D%E7%BD%AE%E7%AE%A1%E7%90%86/</link><pubDate>Mon, 11 Jan 2021 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/11/dubbo-go-%E4%B8%AD%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E8%BF%9C%E7%A8%8B%E9%85%8D%E7%BD%AE%E7%AE%A1%E7%90%86/</guid><description>&lt;p>之前在 Apache/dubbo-go（以下简称 dubbo-go ）社区中，有同学希望配置文件不仅可以放于本地，还可以放于配置管理中心里。那么，放在本地和配置管理中心究竟有哪些不一样呢？&lt;/p>
&lt;p>放在本地，每次更新需要重启，配置文件管理困难，无法做到实时更新即刻生效。此外，本地文件还依赖人工版本控制，在微服务的场景下，大大的增加了运维的成本与难度。&lt;/p>
&lt;p>而配置管理中心提供了统一的配置文件管理，支持文件更新、实时同步、统一版本控制、权限管理等功能。&lt;/p>
&lt;h2 id="目标">目标&lt;/h2>
&lt;p>基于以上几个背景，可以总结出以下&lt;strong>目标&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>与 Dubbo 现有的配置中心内的配置文件兼容，降低新增语言栈的学习成本；&lt;/li>
&lt;li>支持多种配置文件格式；&lt;/li>
&lt;li>支持主流配置中心，适应不一样的使用场景，实现高扩展的配置下发；&lt;/li>
&lt;/ul>
&lt;h2 id="配置中心">配置中心&lt;/h2>
&lt;p>配置中心在 dubbo-go 中主要承担以下场景的职责：&lt;/p>
&lt;ol>
&lt;li>作为外部化配置中心，即存储 dubbo.properties 配置文件，此时，key 值通常为文件名如 dubbo.properties , value 则为配置文件内容。&lt;/li>
&lt;li>存储单个配置项，如各种开关项、常量值等。&lt;/li>
&lt;li>存储服务治理规则，此时 key 通常按照 “服务名 + 规则类型” 的格式来组织，而 value 则为具体的治理规则。&lt;/li>
&lt;/ol>
&lt;p>就目前而言，dubbo-go 首要支持的是 Dubbo 中支持的开源配置中心，包括：&lt;/p>
&lt;ol>
&lt;li>Apollo：携程框架部门研发的分布式配置中心，能够集中化管理应用不同环境、不同集群的配置，配置修改后能够实时推送到应用端，并且具备规范的权限、流程治理等特性，适用于微服务配置管理场景。&lt;/li>
&lt;li>ZooKeeper：一个分布式的，开放源码的分布式应用程序协调服务，是 Google 的 Chubby 一个开源的实现，是 Hadoop 和 Hbase 的重要组件。它是一个为分布式应用提供一致性服务的软件，提供的功能包括：配置维护、域名服务、分布式同步、组服务等。&lt;/li>
&lt;li>Nacos: Alibaba 开源的配置管理组件，提供了一组简单易用的特性集，帮助您实现动态服务发现、服务配置管理、服务及流量管理。&lt;/li>
&lt;/ol>
&lt;p>而考虑到某些公司内部有自身的研发的配置中心，又或者当前流行而 Dubbo 尚未支持的配置中心，如 etcd，我们的核心在于设计一套机制，允许我们，也包括用户，可以通过扩展接口新的实现，来快速接入不同的配置中心。&lt;/p>
&lt;p>那在 dubbo-go 中究竟怎么实现呢？我们的答案是：&lt;strong>基于动态的插件机制在启动时按需加载配置中心的不同实现。&lt;/strong>&lt;/p>
&lt;p>实现该部分功能放置于一个独立的子项目中，见： &lt;a href="https://github.com/apache/dubbo-go/tree/master/config_center">https://github.com/apache/dubbo-go/tree/master/config_center&lt;/a>&lt;/p>
&lt;h3 id="dubbo-go-设计">dubbo-go 设计&lt;/h3>
&lt;p>原逻辑为：启动时读取本地配置文件，将其加载进内存，通过配置文件中的配置读取注册中心的信息获取服务提供者，注册服务消费者。&lt;/p>
&lt;p>有些读者会有点困惑，不是说好了使用配置中心的，为什么现在又要读取本地配置呢？答案就是，读取的这部分信息分成两部分：&lt;/p>
&lt;ul>
&lt;li>使用什么作为配置中心；&lt;/li>
&lt;li>该配置中心的元数据，比如说使用 zookeeper 作为配置中心，那么 zookeeper 的链接信息就是元数据，毕竟我们只有在知道了链接信息之后才能连上 zookeeper；&lt;/li>
&lt;/ul>
&lt;p>在改造的时候，需要考虑以下的问题：&lt;/p></description></item><item><title>Dubbo Go Getty 开发日志</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/11/dubbo-go-getty-%E5%BC%80%E5%8F%91%E6%97%A5%E5%BF%97/</link><pubDate>Mon, 11 Jan 2021 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/11/dubbo-go-getty-%E5%BC%80%E5%8F%91%E6%97%A5%E5%BF%97/</guid><description>&lt;h3 id="0-说明">0 说明&lt;/h3>
&lt;p>[getty][3]是一个go语言实现的网络层引擎，可以处理TCP/UDP/websocket三种网络协议。&lt;/p>
&lt;p>2016年6月我在上海做一个即时通讯项目时，接口层的底层网络驱动是当时的同事&lt;a href="https://github.com/sanbit">sanbit&lt;/a>写的，原始网络层实现了TCP
Server，其命名规范学习了著名的netty。当时这个引擎比较简洁，随着我对这个项目的改进这个网络层引擎也就随之进化了（添加了TCP Client、抽象出了 TCP connection 和 TCP
session），至2016年8月份（又添加了websocket）其与原始实现已经大异其趣了，征得原作者和相关领导同意后就放到了github上。&lt;/p>
&lt;p>将近两年的时间我不间断地对其进行改进，年齿渐增但记忆速衰，觉得有必要记录下一些开发过程中遇到的问题以及解决方法，以备将来回忆之参考。&lt;/p>
&lt;h3 id="1-udp-connection">1 UDP connection&lt;/h3>
&lt;p>2018年3月5日 起给 getty 添加了UDP支持。&lt;/p>
&lt;h4 id="11-udp-connect">1.1 UDP connect&lt;/h4>
&lt;p>UDP自身分为unconnected UDP和connected UDP两种，connected UDP的底层原理见下图。&lt;/p>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubbo-go/connected_udp_socket.gif">&lt;/p>
&lt;p>当一端的UDP endpoint调用connect之后，os就会在内部的routing table上把udp socket和另一个endpoint的地址关联起来，在发起connect的udp
endpoint端建立起一个单向的连接四元组：发出的datagram packet只能发往这个endpoint（不管sendto的时候是否指定了地址）且只能接收这个endpoint发来的udp datagram
packet（如图???发来的包会被OS丢弃）。&lt;/p>
&lt;p>UDP endpoint发起connect后，OS并不会进行TCP式的三次握手，操作系统共仅仅记录下UDP socket的peer udp endpoint 地址后就理解返回，仅仅会核查对端地址是否存在网络中。&lt;/p>
&lt;p>至于另一个udp endpoint是否为connected udp则无关紧要，所以称udp connection是单向的连接。如果connect的对端不存在或者对端端口没有进程监听，则发包后对端会返回ICMP “port
unreachable” 错误。&lt;/p>
&lt;p>如果一个POSIX系统的进程发起UDP write时没有指定peer UDP address，则会收到ENOTCONN错误，而非EDESTADDRREQ。&lt;/p>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubbo-go/dns_udp.gif">&lt;/p>
&lt;p>一般发起connect的为 UDP client，典型的场景是DNS系统，DNS client根据/etc/resolv.conf里面指定的DNS server进行connect动作。&lt;/p>
&lt;p>至于 UDP server 发起connect的情形有 TFTP，UDP client 和 UDP server 需要进行长时间的通信， client 和 server 都需要调用 connect 成为 connected UDP。&lt;/p></description></item><item><title>无缝衔接 gRPC 与 dubbo-go</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/11/%E6%97%A0%E7%BC%9D%E8%A1%94%E6%8E%A5-grpc-%E4%B8%8E-dubbo-go/</link><pubDate>Mon, 11 Jan 2021 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/11/%E6%97%A0%E7%BC%9D%E8%A1%94%E6%8E%A5-grpc-%E4%B8%8E-dubbo-go/</guid><description>&lt;p>最近我们 dubbo-go 社区里面，呼声很大的一个 feature 就是对 gRPC 的支持。在某位大佬的不懈努力之下，终于弄出来了。&lt;/p>
&lt;p>今天我就给大家分析一下大佬是怎么连接 dubbo-go 和 gRPC 。&lt;/p>
&lt;h2 id="grpc">gRPC&lt;/h2>
&lt;p>先来简单介绍一下 gRPC 。它是 Google 推出来的一个 RPC 框架。gRPC是通过 IDL ( Interface Definition Language )——接口定义语言——编译成不同语言的客户端来实现的。可以说是RPC理论的一个非常非常标准的实现。&lt;/p>
&lt;p>因而 gRPC 天然就支持多语言。这几年，它几乎成为了跨语言 RPC 框架的标准实现方式了，很多优秀的rpc框架，如 Spring Cloud 和 dubbo ，都支持 gRPC 。&lt;/p>
&lt;p>server 端&lt;/p>
&lt;p>在 Go 里面，server 端的用法是：&lt;/p>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubbo-go/grpc/p1.webp">&lt;/p>
&lt;p>它的关键部分是：s := grpc.NewServer()和pb.RegisterGreeterServer(s, &amp;amp;server{})两个步骤。第一个步骤很容易，唯独第二个步骤RegisterGreeterServer有点麻烦。为什么呢？&lt;/p>
&lt;p>因为pb.RegisterGreeterServer(s, &amp;amp;server{})这个方法是通过用户定义的protobuf编译出来的。&lt;/p>
&lt;p>好在，这个编译出来的方法，本质上是：&lt;/p>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubbo-go/grpc/p2.webp">&lt;/p>
&lt;p>也就是说，如果我们在 dubbo-go 里面拿到这个 _Greeter_serviceDesc ，就可以实现这个 server 的注册。因此，可以看到，在 dubbo-go 里面，要解决的一个关键问题就是如何拿到这个 serviceDesc 。&lt;/p>
&lt;h2 id="client-端">Client 端&lt;/h2>
&lt;p>Client 端的用法是：&lt;/p>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubbo-go/grpc/p3.webp">&lt;/p>
&lt;p>这个东西要复杂一点：1、创建连接：conn, err := grpc.Dial(address)2、创建client：c := pb.NewGreeterClient(conn)3、调用方法：r, err := c.SayHello(ctx, &amp;amp;pb.HelloRequest{Name: name})&lt;/p></description></item><item><title>在dubbo-go中使用sentinel</title><link>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/11/%E5%9C%A8dubbo-go%E4%B8%AD%E4%BD%BF%E7%94%A8sentinel/</link><pubDate>Mon, 11 Jan 2021 00:00:00 +0000</pubDate><guid>https://deploy-preview-3199--dubbo.netlify.app/zh-cn/blog/2021/01/11/%E5%9C%A8dubbo-go%E4%B8%AD%E4%BD%BF%E7%94%A8sentinel/</guid><description>&lt;p>时至今日，Apache/dubbo-go（以下简称 dubbo-go ）项目在功能上已经逐步对齐java版本，稳定性也在不同的生产环境得到了验证。社区便开始再服务治理、监控等方向发力。随着 1.2和1.3 版本发布， dubbo-go 新增了大量此类新feature。&lt;/p>
&lt;p>今天我们聊一聊限流相关话题，此前dubbo-go已经支持了&lt;a href="https://github.com/apache/dubbo-go/pull/237">tps limit&lt;/a>、&lt;a href="https://github.com/apache/dubbo-go/pull/246">execute limit &lt;/a>、&lt;a href="https://github.com/apache/dubbo-go/pull/133">hystrix&lt;/a> 的内置filter，用户只要简单配置就能马上用上。但我们知道，在 java 的 dubbo 生态中，有一项限流工具被广泛使用，那就是sentinel。sentinel因为强大的动态规划配置、优秀的dashboard以及对dubbo的良好适配，成为众多使用dubbo的企业选用限流工具的不二之选。&lt;/p>
&lt;p>就在前些日子，社区非常高兴得知 Sentinel Golang 首个版本 0.1.0 正式发布，这使得 dubbo-go也可以使用 sentinel 作为工具进行一些服务治理、监控的工作了。随着sentinel golang的健壮，我们相信用户马上可以像sentinel管理java dubbo服务那样管理dubbo-go的服务了。&lt;/p>
&lt;p>完成sentinel golang的dubbo-adapter其实非常简单，这得益于dubbo-go早就完成了filter链的构造，用户可以自定义filter，并且灵活的安排其执行顺序。在1.3发布后，增加了filter中的context传递，构建sentinel/adapter/dubbo更为方便。&lt;/p>
&lt;p>我们以其中的provider filter适配为例:&lt;/p>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubbo-go/sentinel/dubbo-go-sentinel-provider-filter.png">&lt;/p>
&lt;p>此 filter 实现了 dubbo-go的filter接口，只要用户在服务启动时将此filter加载到dubbo-go中，即可使用此filter。&lt;/p>
&lt;p>&lt;img alt="img" src="https://deploy-preview-3199--dubbo.netlify.app/imgs/blog/dubbo-go/sentinel/sentinel-golang.png">&lt;/p>
&lt;p>sentinel实现原理与其他限流、熔断库大同小异，底层是用的滑动窗口算法。与hystrix等框架相比不同点是设计理念，Sentinel 的设计理念是让您自由选择控制的角度，并进行灵活组合，从而达到想要的效果。&lt;/p>
&lt;p>下面我整理了完整的使用流程：(注意：dubbo-go版本请使用1.3.0-rc3及其以上版本)&lt;/p>
&lt;p>在dubbo-go中使用sentinel主要分为以下几步：&lt;/p>
&lt;ol>
&lt;li>初始化sentinel&lt;/li>
&lt;li>将sentinel注入dubbo-go的filter&lt;/li>
&lt;li>初始化dubbo-go&lt;/li>
&lt;li>配置规划&lt;/li>
&lt;/ol>
&lt;h2 id="初始化sentinel">初始化sentinel&lt;/h2>
&lt;p>示例代码：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#719e07">import&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	sentinel &lt;span style="color:#2aa198">&amp;#34;github.com/alibaba/sentinel-golang/api&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> &lt;span style="color:#268bd2">initSentinel&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	err &lt;span style="color:#719e07">:=&lt;/span> sentinel.&lt;span style="color:#268bd2">InitWithLogDir&lt;/span>(confPath, logDir)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#719e07">if&lt;/span> err &lt;span style="color:#719e07">!=&lt;/span> &lt;span style="color:#cb4b16">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#586e75">// 初始化 Sentinel 失败
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>	}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="将sentinel注入dubbo-go的filter">将sentinel注入dubbo-go的filter&lt;/h2>
&lt;p>你可以通过import包的形式执行，执行其中的init()来注入filter&lt;/p></description></item></channel></rss>