blob: 0d2c50aeeb93d72e1704547a0474470c0d586347 [file] [log] [blame]
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Apache Dubbo – Pixiu</title><link>https://dubbo.apache.org/zh-cn/tags/pixiu/</link><description>Recent content in Pixiu on Apache Dubbo</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Sat, 19 Feb 2022 00:00:00 +0000</lastBuildDate><atom:link href="https://dubbo.apache.org/zh-cn/tags/pixiu/index.xml" rel="self" type="application/rss+xml"/><item><title>Blog: 谈谈Pixiu的Filter</title><link>https://dubbo.apache.org/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://dubbo.apache.org/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>
&lt;blockquote>
&lt;p>一个Filter可以即是Decode Filter,又是Encode Filter,没有限制!&lt;/p>
&lt;p>假设有A、B、C三个Filter,都是Decode/Encode Filter,如果配置的顺序是A、B、C,那么运行将会是下面这样&lt;/p>
&lt;p>在Decode阶段 A-&amp;gt;B-&amp;gt;C,而在Encode阶段,顺序将会反过来!C-&amp;gt;B-&amp;gt;A&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">// decode filters will be invoked in the config order: A、B、C, and decode filters will be
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// invoked in the reverse order: C、B、A
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>HttpDecodeFilter &lt;span style="color:#268bd2">interface&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">Decode&lt;/span>(ctx &lt;span style="color:#719e07">*&lt;/span>http.HttpContext) FilterStatus
&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:#586e75">// HttpEncodeFilter after invoke upstream,
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">// decode filters will be invoked in the reverse order
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span>HttpEncodeFilter &lt;span style="color:#268bd2">interface&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">Encode&lt;/span>(ctx &lt;span style="color:#719e07">*&lt;/span>http.HttpContext) FilterStatus
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>更详细的,每个Decode/Encode Filter可以返回一个FilterStatus来决定继续还是就在这里停下!比如JWT鉴权,token无效时就要及时把401返回给Downstream。当然Decode Filter发出的停止命令只会终止Decode阶段,至于为什么?想想如何做一个Access Log Filter,能在请求失败时也把失败的结果记录下吧来!&lt;/p>
&lt;h2 id="怎样编写一个自定义filter">&lt;strong>怎样编写一个自定义Filter&lt;/strong>&lt;/h2>
&lt;p>我们来尝试写一个简单的Filter,这个Filter将会有简单的配置,在Decode阶段把请求的Body Log出来,并翻转后作为Mock的返回值。最后在Encode阶段根据配置把返回值Log出来。&lt;/p>
&lt;p>1.首先创建一个Filter&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> DemoFilter &lt;span style="color:#268bd2">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logPrefix &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>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (f &lt;span style="color:#719e07">*&lt;/span>DemoFilter) &lt;span style="color:#268bd2">Decode&lt;/span>(ctx &lt;span style="color:#719e07">*&lt;/span>contexthttp.HttpContext) filter.FilterStatus {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> body, _ &lt;span style="color:#719e07">:=&lt;/span> ioutil.&lt;span style="color:#268bd2">ReadAll&lt;/span>(ctx.Request.Body)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger.&lt;span style="color:#268bd2">Infof&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;request body: %s&amp;#34;&lt;/span>, body)
&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">//reverse res str
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> runes &lt;span style="color:#719e07">:=&lt;/span> []&lt;span style="color:#b58900">rune&lt;/span>(&lt;span style="color:#b58900">string&lt;/span>(body))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">for&lt;/span> i &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#2aa198">0&lt;/span>; i &amp;lt; &lt;span style="color:#b58900">len&lt;/span>(runes)&lt;span style="color:#719e07">/&lt;/span>&lt;span style="color:#2aa198">2&lt;/span>; i &lt;span style="color:#719e07">+=&lt;/span> &lt;span style="color:#2aa198">1&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> runes[i], runes[&lt;span style="color:#b58900">len&lt;/span>(runes)&lt;span style="color:#719e07">-&lt;/span>&lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">-&lt;/span>i] = runes[&lt;span style="color:#b58900">len&lt;/span>(runes)&lt;span style="color:#719e07">-&lt;/span>&lt;span style="color:#2aa198">1&lt;/span>&lt;span style="color:#719e07">-&lt;/span>i], runes[i]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> reverse &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#b58900">string&lt;/span>(runes)
&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">//mock response
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> ctx.&lt;span style="color:#268bd2">SendLocalReply&lt;/span>(&lt;span style="color:#2aa198">200&lt;/span>, []&lt;span style="color:#b58900">byte&lt;/span>(reverse))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> filter.Stop
&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> (f &lt;span style="color:#719e07">*&lt;/span>DemoFilter) &lt;span style="color:#268bd2">Encode&lt;/span>(ctx &lt;span style="color:#719e07">*&lt;/span>contexthttp.HttpContext) filter.FilterStatus {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> res &lt;span style="color:#719e07">:=&lt;/span> ctx.SourceResp.(&lt;span style="color:#dc322f">string&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> logger.&lt;span style="color:#268bd2">Infof&lt;/span>(&lt;span style="color:#2aa198">&amp;#34;%s: %s&amp;#34;&lt;/span>, f.logPrefix, res)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> filter.Continue
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>2.创建Filter Factory&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> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> DemoFilterFactory &lt;span style="color:#268bd2">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> conf &lt;span style="color:#719e07">*&lt;/span>Config
&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">// Config describe the config of Filter
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#586e75">&lt;/span> Config &lt;span style="color:#268bd2">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> LogPrefix &lt;span style="color:#dc322f">string&lt;/span> &lt;span style="color:#2aa198">`yaml:&amp;#34;logPrefix,omitempty&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>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#268bd2">func&lt;/span> (f &lt;span style="color:#719e07">*&lt;/span>DemoFilterFactory) &lt;span style="color:#268bd2">PrepareFilterChain&lt;/span>(ctx &lt;span style="color:#719e07">*&lt;/span>contexthttp.HttpContext, chain filter.FilterChain) &lt;span style="color:#dc322f">error&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> demo &lt;span style="color:#719e07">:=&lt;/span> &lt;span style="color:#719e07">&amp;amp;&lt;/span>DemoFilter{logPrefix: f.conf.LogPrefix}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> chain.&lt;span style="color:#268bd2">AppendDecodeFilters&lt;/span>(demo)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> chain.&lt;span style="color:#268bd2">AppendEncodeFilters&lt;/span>(demo)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#719e07">return&lt;/span> &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> (f &lt;span style="color:#719e07">*&lt;/span>DemoFilterFactory) &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 style="color:#719e07">return&lt;/span> f.conf
&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> (f &lt;span style="color:#719e07">*&lt;/span>DemoFilterFactory) &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 style="color:#719e07">return&lt;/span> &lt;span style="color:#cb4b16">nil&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>3.创建Filter Plugin,并注册自己&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:#586e75">//important
&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">func&lt;/span> &lt;span style="color:#268bd2">init&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> filter.&lt;span style="color:#268bd2">RegisterHttpFilter&lt;/span>(&lt;span style="color:#719e07">&amp;amp;&lt;/span>Plugin{})
&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">type&lt;/span> Plugin &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> (p &lt;span style="color:#719e07">*&lt;/span>Plugin) &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 style="color:#719e07">return&lt;/span> &lt;span style="color:#2aa198">&amp;#34;dgp.filters.demo&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> (p &lt;span style="color:#719e07">*&lt;/span>Plugin) &lt;span style="color:#268bd2">CreateFilterFactory&lt;/span>() (filter.HttpFilterFactory, &lt;span style="color:#dc322f">error&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:#719e07">&amp;amp;&lt;/span>DemoFilterFactory{conf: &lt;span style="color:#719e07">&amp;amp;&lt;/span>Config{}}, &lt;span style="color:#cb4b16">nil&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>4.配置文件中配置此Filter,并启动Pixiu&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">static_resources&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">listeners&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;net/http&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">protocol_type&lt;/span>: &lt;span style="color:#2aa198">&amp;#34;HTTP&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>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">socket_address&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;0.0.0.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">port&lt;/span>: &lt;span style="color:#2aa198">8888&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">filter_chains&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">filters&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">name&lt;/span>: dgp.filter.httpconnectionmanager
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">config&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">route_config&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">routes&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">match&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">prefix&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">http_filters&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#268bd2">name&lt;/span>: dgp.filters.demo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#268bd2">config&lt;/span>:
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>5.访问并查看日志与结果&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-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>curl localhost:8888/demo -d &lt;span style="color:#2aa198">&amp;#34;eiv al tse’c&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>c’est la vie%
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&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-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>2022-02-19T20:20:11.900+0800 INFO demo/demo.go:62 request body: eiv al tse’c
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>2022-02-19T20:20:11.900+0800 INFO demo/demo.go:71 : eiv al tse’c
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Blog: Dubbo 跨语言调用神兽:dubbo-go-pixiu</title><link>https://dubbo.apache.org/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://dubbo.apache.org/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>
&lt;p>Pixiu 的整体设计遵守以下原则:&lt;/p>
&lt;ul>
&lt;li>High performance: 高吞吐量以及毫秒级的延时。&lt;/li>
&lt;li>可扩展: 通过 go-plugin,用户可以根据自己的需求延展 Pixiu 的功能。&lt;/li>
&lt;li>简单可用: 用户通过少量配置,即可上线。&lt;/li>
&lt;/ul>
&lt;h2 id="pixiu-的特性及核心功能">Pixiu 的特性及核心功能&lt;/h2>
&lt;ul>
&lt;li>为 RESTful API 和 Dubbo API 提供支持&lt;/li>
&lt;/ul>
&lt;p>非 RESTful 风格的 API 和 Dubbo 协议的服务往往需要修改才可以以 RESTful API 风格对外开放。Pixiu 提供协议转换功能,通过 Pixiu,开发者可以将自己的 HTTP API 或 Dubbo API 通过配置,以 RESTful API 风格对外开放。v0.2.1 版本已支持基于泛化调用的 HTTP 至 Dubbo 的协议转换以及 HTTP 协议的转发。在后续的版本,社区将会增加对 gRPC 和 http2 协议的支持。&lt;/p>
&lt;ul>
&lt;li>面向用户的配置方式&lt;/li>
&lt;/ul>
&lt;p>一般的网关的配置往往繁琐且复杂。Pixiu,目标作为一款易用的网关产品,在设计上拥有三层配置层级,Gateway 层全局配置, API resource 层配置以及 HTTP verbs 方法层配置。通过三个不同层级的配置,既可以实现深度的定制,亦支持统一的默认配置;同时,支持本地的配置文件,亦可使用统一配置服务器。另外,还提供控制台模块,通过控制台模块,支持配置的热更新。Pixiu 配套配套的控制台界面也在同步开发中。&lt;/p>
&lt;ul>
&lt;li>通用功能的集成&lt;/li>
&lt;/ul>
&lt;p>重试、熔断、流量控制、访问控制等通用功能不再需要在每个后端服务上重复实现。使用 Pixiu,通过配置 filter ,开发者可以进行全局的控制,亦可以根据 API 配置各自的规则。因此开发者可以专注于业务逻辑和服务,而不是将时间用在维护基础设施上。&lt;/p>
&lt;ul>
&lt;li>可扩展&lt;/li>
&lt;/ul>
&lt;p>不同的使用场景有着各自独特的需求。为满足不同用户的定制化需求,Pixiu 使用了插件模式。开发者可以通过编写 go plugin,将自身特有的业务逻辑以 filter 形式内嵌至 Pixiu 网关中,实现诸如企业登录鉴权等功能。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/1/01/01/dubbo-go-pixiu/fd38da297d095e4c3af1c89b18804ef1.webp" alt="img">&lt;/p>
&lt;p>图 1: Pixiu 核心功能列表&lt;/p>
&lt;h2 id="pixiu-的架构设计">Pixiu 的架构设计&lt;/h2>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/1/01/01/dubbo-go-pixiu/2b2fd6ea1cc0375392919d9e0c181f2b.webp" alt="img">&lt;/p>
&lt;p>图 2: Pixiu 架构&lt;/p>
&lt;p>貔貅: 即 dubbo-go-pixiu,由四个主要模块:Listener、Router、Filters 和 Clients 组成;&lt;/p>
&lt;ul>
&lt;li>Dubbo Cluster: Dubbo 服务所在集群,包含一个或多个 Dubbo Services;&lt;/li>
&lt;li>Other Cluster: Dubbo 以外的服务所在集群,现支持 HTTP 服务,未来将拓展支持 gRPC 等其他服务;&lt;/li>
&lt;li>Registry Center: 注册中心,维护每个业务服务的调用地址信息;&lt;/li>
&lt;li>Metadata Center: 元数据中心,维护每个业务服务的配置信息以及存储 Pixiu 本身的配置信息。&lt;/li>
&lt;/ul>
&lt;p>作为 Dubbo 所衍生的 API 网关,Pixiu 使用 Golang 搭建,主要因为: 1. Golang 的 G-M-P,net poller 等特性使 Golang 非常适合构建 IO 密集型应用;2. 使用 Golang 可以直接引入 Dubbo-go 中的一些组建,简化开发。&lt;/p>
&lt;p>整个 Pixiu 大致可以拆分为四个主要模块:Listener、Router、Filters 和 Client。&lt;/p>
&lt;h3 id="1listener">1、Listener&lt;/h3>
&lt;p>在 Pixiu 中,Listener 代表外部可以访问 Pixiu 的方式。通过配置指定协议类型,地址,端口等属性,暴露 Gateway。现阶段暂支持 HTTP 协议,未来将会加入 gRPC。&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-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>listeners:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - name: &amp;#34;net/http&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> address:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> socket_address:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> protocol_type: &amp;#34;HTTP&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> address: &amp;#34;0.0.0.0&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> port: 8888
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> config:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> idle_timeout: 5s
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> read_timeout: 5s
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> write_timeout: 5s
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="2router">2、Router&lt;/h3>
&lt;p>Router 是 Pixiu 的路由组件。根据配置文件,Pixiu 将对外暴露的 URLs 以树的形势存储于内存中,当请求到了 router 组件时,即会根据 URL 及 HTTP 方法查找到对应的后端服务及其 API 配置,并将信息封装于请求中,为后续 filter,及 client 的调用提供足够的内容。&lt;/p>
&lt;p>现阶段,Router 提供以下功能:&lt;/p>
&lt;ul>
&lt;li>支持请求一对一转发路由配置或 wildcard 路由配置。&lt;/li>
&lt;li>支持 HTTP 请求的转发到后端 HTTP 服务。&lt;/li>
&lt;li>支持 HTTP 请求转化为 dubbo 泛化调用请求。&lt;/li>
&lt;/ul>
&lt;h3 id="3filters">3、Filters&lt;/h3>
&lt;p>Filter 是 Pixiu 实现额外功能及其扩展性的主要组件。其实现类似于 Dubbo-go 中的 filter,根据配置中 filter 的指定,生成调用链,从而在调用后端服务前,将各 filter 中的逻辑运行一遍,实现节流,日志等功能。&lt;/p>
&lt;p>用户如果需要客制化的 filter,可通过编写 go-plugin 实现。在配置中,可通过类似如下配置,加载 .so 文件,并在 API config 中指定使用的 plugin group,plugin name 实现。&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-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>pluginFilePath: &amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>pluginsGroup:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - groupName: &amp;#34;group1&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> plugins:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - name: &amp;#34;rate limit&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> version: &amp;#34;0.0.1&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> priority: 1000
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> externalLookupName: &amp;#34;ExternalPluginRateLimit&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - name: &amp;#34;access&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> version: &amp;#34;0.0.1&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> priority: 1000
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> externalLookupName: &amp;#34;ExternalPluginAccess&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - groupName: &amp;#34;group2&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> plugins:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - name: &amp;#34;blacklist&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> version: &amp;#34;0.0.1&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> priority: 1000
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> externalLookupName: &amp;#34;ExternalPluginBlackList&amp;#34;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="4client">4、Client&lt;/h3>
&lt;p>Client 负责调用具体服务。现阶段,Pixiu 支持 HTTP 与 Dubbo 的后端服务。社区将逐渐增加 gRPC 等其他 Client 以满足不同的协议。&lt;/p>
&lt;p>HTTP client 的实现相对简单,根据 Router 中获取的后端服务信息,通过 Golang 官方包 net/http 生成请求并调用。&lt;/p>
&lt;p>Dubbo client 的实现对比 HTTP client 会稍微复杂,其基础为 Dubbo 服务的泛化调用。泛化调用技术是 Dubbo 提供的一个很基础的功能只需要知道调用的方法名、参数类型和返回值类型,即可发起服务调用。客户端对服务端的泛化调用既可以通过注册中心发现服务,也可以直连服务端,实现对服务的动态调用。&lt;/p>
&lt;p>如下面代码所示,Pixiu 通过动态配置 referenceConfig,然后通过 GetRPCService 生成 Dubbo 的 Generic 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-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>referenceConfig := dg.NewReferenceConfig(irequest.Interface, context.TODO())
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> referenceConfig.InterfaceName = irequest.Interface
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> referenceConfig.Cluster = constant.DEFAULT_CLUSTER
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> var registers []string
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> for k := range dgCfg.Registries {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> registers = append(registers, k)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> referenceConfig.Registry = strings.Join(registers, &amp;#34;,&amp;#34;)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> if len(irequest.DubboBackendConfig.Protocol) == 0 {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> referenceConfig.Protocol = dubbo.DUBBO
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } else {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> referenceConfig.Protocol = irequest.DubboBackendConfig.Protocol
&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> referenceConfig.Version = irequest.DubboBackendConfig.Version
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> referenceConfig.Group = irequest.Group
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> referenceConfig.Generic = true
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> if len(irequest.DubboBackendConfig.Retries) == 0 {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> referenceConfig.Retries = &amp;#34;3&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } else {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> referenceConfig.Retries = irequest.DubboBackendConfig.Retries
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> dc.lock.Lock()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> defer dc.lock.Unlock()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> referenceConfig.GenericLoad(key)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> clientService := referenceConfig.GetRPCService().(*dg.GenericService)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>实际上,在泛化调用的客户端中,实际执行泛化调用的关键步骤是 Dubbo-go 中的 generic_filter (如下代码片段)。在调用 generic_filter 的 Invoke 时,约定 invocation 参数列表第一个为方法名,第二个为参数类型列表,第三个为参数值列表。generic_filter 将用户请求的参数值列表转化为统一格式的 map(代码中的 struct2MapAll ),将类( golang 中为 struct )的正反序列化操作变成 map 的正反序列化操作。这使得无需 POJO 描述通过硬编码注入 hessain 库,从而完成 Dubbo 服务的泛化调用。&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-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>func (ef *GenericFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> if invocation.MethodName() == constant.GENERIC &amp;amp;&amp;amp; len(invocation.Arguments()) == 3 {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> oldArguments := invocation.Arguments()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> if oldParams, ok := oldArguments[2].([]interface{}); ok {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> newParams := make([]hessian.Object, 0, len(oldParams))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> for i := range oldParams {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> newParams = append(newParams, hessian.Object(struct2MapAll(oldParams[i])))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> newArguments := []interface{}{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> oldArguments[0],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> oldArguments[1],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> newParams,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> newInvocation := invocation2.NewRPCInvocation(invocation.MethodName(), newArguments, invocation.Attachments())
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> newInvocation.SetReply(invocation.Reply())
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> return invoker.Invoke(ctx, newInvocation)
&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> return invoker.Invoke(ctx, invocation)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="总结">总结&lt;/h2>
&lt;p>通过上面的四个模块以及注册中心的简单介绍不难发现,当请求通过 listener 被 Pixiu 接收后,请求被传入 router 中。router 根据接口的配置,从原请求中找到目标后端服务连同相关 API 配置下发到 filter 组件。filter 组件根据原请求、 API 配置等信息顺序执行,最终请求到达 client, 通过 client 调用后端服务。&lt;/p>
&lt;h3 id="pixiu-的未来">Pixiu 的未来&lt;/h3>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/1/01/01/dubbo-go-pixiu/e57050f224f658b96cd6bd917050b259.webp" alt="img">
图 3: Pixiu 迭代里程碑&lt;/p>
&lt;p>Pixiu 作为网关产品外,其衍生项目也会在我们的未来计划中,主要目的是提供更好的可用性。例如,由于 Golang 语言缺乏原生的注解, 因此 Dubbo-go 需要通过配置文件方式生成服务的元数据写入注册中心。开课啦教育公司相关同学写了一个扫描代码的工具 &lt;a href="https://github.com/jack15083/dubbo-go-proxy-tool">https://github.com/jack15083/dubbo-go-proxy-tool&lt;/a>,在每个 RPC 服务方法前加上对应的注释,从而在服务启动前通过扫描注释生成元数据。Pixiu 也计划在未来的版本上通过提供 package,允许服务通过注释借助 &lt;a href="https://github.com/MarcGrol/golangAnnotations">https://github.com/MarcGrol/golangAnnotations&lt;/a> 生成 API 配置并注册到 Pixiu 上。&lt;/p>
&lt;p>Pixiu 目前的定位是一个七层协议网关,其最初版本是被定义成一个 Dubbo 的服务网关。作为云时代的产品,Pixiu 的发展方向必然是面向云原生的。现在的版本为 0.2.1, 已经实现基本的 Dubbo/Http 服务代理和部分的网关通用功能。目前正在开发中的 0.4 及其后续版本支持 gRPC 和 Spring Cloud 服务调用, 后续还将提供 MQ 服务支持。另外,社区将继续优化配置方式,降低用户的使用难度,继续优化官方的 filter,使 Pixiu 可以在官方层面实现更多的网关通用功能。&lt;/p>
&lt;p>&lt;img src="https://dubbo.apache.org/imgs/blog/1/01/01/dubbo-go-pixiu/0c1afe00699eb3e5cc022e48966ef5a6.webp" alt="img">&lt;/p>
&lt;p>在未来的一年内,社区计划支持 xDS API,将 Pixiu 演化为 Dubbo mesh 的 sidecar。其最终目的就是:在现有的 dubbo mesh 形态中演化出 Proxy Service Mesh 形态。基于这个形态,Js、Python、PHP、Ruby 和 Perl 等脚本语言程序除了收获 dubbo mesh 原有的技术红利之外,大概率还能收获性能上的提升。&lt;/p>
&lt;p>Pixiu 在 Dubbo Mesh 中的终极目的是:把东西向和南北向数据面流量逐步统一 Pixiu 中的同时,让它逐步具备 Application Runtime 的能力,作为 Dubbo 多语言生态的关键解决方案。&lt;/p>
&lt;p>相关链接:&lt;/p>
&lt;p>【1】Dubbo-go:https://github.com/apache/dubbo-go&lt;/p>
&lt;p>【2】Pixiu:https://github.com/apache/dubbo-go-pixiu&lt;/p>
&lt;p>冯振宇,Apache Dubbo Committer,目前负责管理香港一家消费品公司的 IT 部门整个团队。2020 年夏天 偶然看到了介绍 dubbogo 的文章后加入了 dubbogo 社区,目前在主导 Pixiu 0.4.0 版本的开发。&lt;/p></description></item></channel></rss>