You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@dubbo.apache.org by GitBox <gi...@apache.org> on 2018/07/10 06:48:57 UTC

[GitHub] ralf0131 closed pull request #34: add dubbo meetup shanghai blog

ralf0131 closed pull request #34: add dubbo meetup shanghai blog
URL: https://github.com/apache/incubator-dubbo-website/pull/34
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/blog/en-us/dubbo-meetup-shanghai-jun-23rd-2018.md b/blog/en-us/dubbo-meetup-shanghai-jun-23rd-2018.md
new file mode 100644
index 0000000..842a697
--- /dev/null
+++ b/blog/en-us/dubbo-meetup-shanghai-jun-23rd-2018.md
@@ -0,0 +1,10 @@
+Dubbo Shanghai meetup has been held successfully
+---
+
+The sencond Dubbo meetup has successfully been held in Shanghai, over 700 people submitted registration, and over 300 were present, more than 10,000 watched the live online. A great event again! 
+
+Please enjoy the slides of the topics:
+  * Jerrick Zhu: Dubbo Status and Roadmap (Chinese) [slides](https://github.com/dubbo/awesome-dubbo/blob/master/slides/meetup/201806%40Shanghai/dubbo-status-and-roadmap.pdf)
+  * Mercy Ma: Dubbo Cloud Native Practices and Thoughts (Chinese) [slides](https://github.com/dubbo/awesome-dubbo/blob/master/slides/meetup/201806%40Shanghai/dubbo-cloud-native-practices-and-thoughts.pdf)
+  * Ping Guo: Nacos Open Source Initiative (Chinese) [slides](https://github.com/dubbo/awesome-dubbo/blob/master/slides/meetup/201806%40Shanghai/nacos-open-source-initiative.pdf)
+  * Zhiwei Pan: Dubbo Practices on Internet Finance Industries (Chinese) [slides](https://github.com/dubbo/awesome-dubbo/blob/master/slides/meetup/201806%40Shanghai/dubbo-practices-on-internet-finance-industries.pdf) 
\ No newline at end of file
diff --git a/blog/zh-cn/dubbo-meetup-shanghai-jun-23rd-2018.md b/blog/zh-cn/dubbo-meetup-shanghai-jun-23rd-2018.md
new file mode 100644
index 0000000..9f58089
--- /dev/null
+++ b/blog/zh-cn/dubbo-meetup-shanghai-jun-23rd-2018.md
@@ -0,0 +1,10 @@
+第二届Dubbo开发者沙龙在上海成功举办
+---
+
+第二届Dubbo开发者沙龙在上海成功举办,超过700位开发者报名,现场参与人数300+,通过阿里云天池、云栖社区、大咖说引导线上直播观看次数10000+
+
+分享嘉宾及PPT:
+  * 朱勇: Dubbo开源现状与未来规划 (中文) [slides](https://github.com/dubbo/awesome-dubbo/blob/master/slides/meetup/201806%40Shanghai/dubbo-status-and-roadmap.pdf)
+  * 小马哥: Dubbo Cloud Native 之路的实践与思考 (中文) [slides](https://github.com/dubbo/awesome-dubbo/blob/master/slides/meetup/201806%40Shanghai/dubbo-cloud-native-practices-and-thoughts.pdf)
+  * 郭平: Nacos - 贡献Dubbo生态,阿里巴巴注册中心和配置中心开源计划 (中文) [slides](https://github.com/dubbo/awesome-dubbo/blob/master/slides/meetup/201806%40Shanghai/nacos-open-source-initiative.pdf)
+  * 潘志伟: Dubbo在互金行业的应用场景 (中文) [slides](https://github.com/dubbo/awesome-dubbo/blob/master/slides/meetup/201806%40Shanghai/dubbo-practices-on-internet-finance-industries.pdf) 
\ No newline at end of file
diff --git a/img/blog/dubbo-shanghai-meetup.jpeg b/img/blog/dubbo-shanghai-meetup.jpeg
new file mode 100644
index 0000000..0a08316
Binary files /dev/null and b/img/blog/dubbo-shanghai-meetup.jpeg differ
diff --git a/md_json/blog.json b/md_json/blog.json
index 901c46a..de1c9d5 100644
--- a/md_json/blog.json
+++ b/md_json/blog.json
@@ -12,6 +12,10 @@
       "filename": "dubbo-invoke.md",
       "__html": "<h1>Dubbo 关于同步/异步调用的几种方式</h1>\n<p>我们知道,Dubbo 缺省协议采用单一长连接,底层实现是 Netty 的 NIO 异步通讯机制;基于这种机制,Dubbo 实现了以下几种调用方式:</p>\n<ul>\n<li>同步调用</li>\n<li>异步调用</li>\n<li>参数回调</li>\n<li>事件通知</li>\n</ul>\n<h3>同步调用</h3>\n<p>同步调用是一种阻塞式的调用方式,即 Consumer 端代码一直阻塞等待,直到 Provider 端返回为止;</p>\n<p>通常,一个典型的同步调用过程如下:</p>\n<ol>\n<li>Consumer 业务线程调用远程接口,向 Provider 发送请求,同时当前线程处于<code>阻塞</code>状态;</li>\n<li>Provider 接到 Consumer 的请求后,开始处理请求,将结果返回给 Consumer;</li>\n<li>Consumer 收到结果后,当前线程继续往后执行。</li>\n</ol>\n<p>这里有 2 个问题:</p>\n<ol>\n<li>Consumer 业务线程是怎么进入<code>阻塞</code>状态的?</li>\n<li>Consumer 收到结果后,如果唤醒业务线程往后执行的?</li>\n</ol>\n<p>其实,Dubbo 的底层 IO 操作都是异步的。Consumer 端发起调用后,得到一个 Future 对象。对于同步调用,业务线程通过<code>Future#get(timeout)</code>,阻塞等待 Provider 端将结果返回;<code>timeout</code>则是 Consumer 端定义的超时时间。当结果返回后,会设置到此 Future,并唤醒阻塞的业务线程;当超时时间到结果还未返回时,业务线程将会异常返回。</p>\n<h3>异步调用</h3>\n<p>基于 Dubbo 底层的异步 NIO 实现异步调用,对于 Provider 响应时间较长的场景是必须的,它能有效利用 Consumer 端的资源,相对于 Consumer 端使用多线程来说开销较小。</p>\n<p>异步调用,对于 Provider 端不需要做特别的配置。下面的例子中,Provider 端接口定义如下:</p>\n<pre><code class=\"language-java\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">interface</span> <span class=\"hljs-title\">AsyncService</span> </span>{\n    <span class=\"hljs-function\">String <span class=\"hljs-title\">goodbye</span><span class=\"hljs-params\">(String name)</span></span>;\n}\n</code></pre>\n<h5>Consumer 配置</h5>\n<pre><code class=\"language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dubbo:reference</span> <span class=\"hljs-attr\">id</span>=<span class=\"hljs-string\">\"asyncService\"</span> <span class=\"hljs-attr\">interface</span>=<span class=\"hljs-string\">\"com.alibaba.dubbo.samples.async.api.AsyncService\"</span>&gt;</span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dubbo:method</span> <span class=\"hljs-attr\">name</span>=<span class=\"hljs-string\">\"goodbye\"</span> <span class=\"hljs-attr\">async</span>=<span class=\"hljs-string\">\"true\"</span>/&gt;</span>\n<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">dubbo:reference</span>&gt;</span>\n</code></pre>\n<p>需要异步调用的方法,均需要使用 <code>&lt;dubbo:method/&gt;</code>标签进行描述。</p>\n<h5>Consumer 端发起调用</h5>\n<pre><code class=\"language-java\">AsyncService service = ...;\nString result = service.goodbye(<span class=\"hljs-string\">\"samples\"</span>);<span class=\"hljs-comment\">// 这里的返回值为空,请不要使用</span>\nFuture&lt;String&gt; future = RpcContext.getContext().getFuture();\n... <span class=\"hljs-comment\">// 业务线程可以开始做其他事情</span>\nresult = future.get(); <span class=\"hljs-comment\">// 阻塞需要获取异步结果时,也可以使用 get(timeout, unit) 设置超时时间</span>\n</code></pre>\n<p>Dubbo Consumer 端发起调用后,同时通过<code>RpcContext.getContext().getFuture()</code>获取跟返回结果关联的<code>Future</code>对象,然后就可以开始处理其他任务;当需要这次异步调用的结果时,可以在任意时刻通过<code>future.get(timeout)</code>来获取。</p>\n<p>一些特殊场景下,为了尽快调用返回,可以设置是否等待消息发出:</p>\n<ul>\n<li><code>sent=&quot;true&quot;</code> 等待消息发出,消息发送失败将抛出异常;</li>\n<li><code>sent=&quot;false&quot;</code> 不等待消息发出,将消息放入 IO 队列,即刻返回。</li>\n</ul>\n<p>默认为<code>fase</code>。配置方式如下:</p>\n<pre><code class=\"language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dubbo:method</span> <span class=\"hljs-attr\">name</span>=<span class=\"hljs-string\">\"goodbye\"</span> <span class=\"hljs-attr\">async</span>=<span class=\"hljs-string\">\"true\"</span> <span class=\"hljs-attr\">sent</span>=<span class=\"hljs-string\">\"true\"</span> /&gt;</span>\n</code></pre>\n<p>如果你只是想异步,完全忽略返回值,可以配置 <code>return=&quot;false&quot;</code>,以减少 Future 对象的创建和管理成本:</p>\n<pre><code class=\"language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dubbo:method</span> <span class=\"hljs-attr\">name</span>=<span class=\"hljs-string\">\"goodbye\"</span> <span class=\"hljs-attr\">async</span>=<span class=\"hljs-string\">\"true\"</span> <span class=\"hljs-attr\">return</span>=<span class=\"hljs-string\">\"false\"</span>/&gt;</span>\n</code></pre>\n<p>此时,<code>RpcContext.getContext().getFuture()</code>将返回<code>null</code>。</p>\n<p>整个异步调用的时序图如下:</p>\n<p><img src=\"../../img/blog/dubbo-async.svg\" alt=\"异步调用\"></p>\n<p>此示例代码位于:<a href=\"https://github.com/dubbo/dubbo-samples/tree/master/dubbo-samples-async\">https://github.com/dubbo/dubbo-samples/tree/master/dubbo-samples-async</a></p>\n<h3>参数回调</h3>\n<p>参数回调有点类似于本地 Callback 机制,但 Callback 并不是 Dubbo 内部的类或接口,而是由 Provider 端自定义的;Dubbo 将基于长连接生成反向代理,从而实现从 Provider 端调用 Consumer 端的逻辑。</p>\n<h5>Provider 端定义 Service 和 Callback</h5>\n<pre><code class=\"language-java\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">interface</span> <span class=\"hljs-title\">CallbackService</span> </span>{\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">addListener</span><span class=\"hljs-params\">(String key, CallbackListener listener)</span></span>;\n}\n\n<span class=\"hljs-keyword\">public</span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">interface</span> <span class=\"hljs-title\">CallbackListener</span> </span>{\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">changed</span><span class=\"hljs-params\">(String msg)</span></span>;\n}\n</code></pre>\n<h5>Provider 端 Service 实现</h5>\n<pre><code class=\"language-java\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span> <span class=\"hljs-title\">CallbackServiceImpl</span> <span class=\"hljs-keyword\">implements</span> <span class=\"hljs-title\">CallbackService</span> </span>{\n\n    <span class=\"hljs-keyword\">private</span> <span class=\"hljs-keyword\">final</span> Map&lt;String, CallbackListener&gt; listeners = <span class=\"hljs-keyword\">new</span> ConcurrentHashMap&lt;String, CallbackListener&gt;();\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-title\">CallbackServiceImpl</span><span class=\"hljs-params\">()</span> </span>{\n        Thread t = <span class=\"hljs-keyword\">new</span> Thread(<span class=\"hljs-keyword\">new</span> Runnable() {\n            <span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">run</span><span class=\"hljs-params\">()</span> </span>{\n                <span class=\"hljs-keyword\">while</span> (<span class=\"hljs-keyword\">true</span>) {\n                    <span class=\"hljs-keyword\">try</span> {\n                        <span class=\"hljs-keyword\">for</span> (Map.Entry&lt;String, CallbackListener&gt; entry : listeners.entrySet()) {\n                            <span class=\"hljs-keyword\">try</span> {\n                                entry.getValue().changed(getChanged(entry.getKey()));\n                            } <span class=\"hljs-keyword\">catch</span> (Throwable t) {\n                                listeners.remove(entry.getKey());\n                            }\n                        }\n                        Thread.sleep(<span class=\"hljs-number\">5000</span>); <span class=\"hljs-comment\">// timely trigger change event</span>\n                    } <span class=\"hljs-keyword\">catch</span> (Throwable t) {\n                        t.printStackTrace();\n                    }\n                }\n            }\n        });\n        t.setDaemon(<span class=\"hljs-keyword\">true</span>);\n        t.start();\n    }\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">addListener</span><span class=\"hljs-params\">(String key, CallbackListener listener)</span> </span>{\n        listeners.put(key, listener);\n        listener.changed(getChanged(key)); <span class=\"hljs-comment\">// send notification for change</span>\n    }\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">private</span> String <span class=\"hljs-title\">getChanged</span><span class=\"hljs-params\">(String key)</span> </span>{\n        <span class=\"hljs-keyword\">return</span> <span class=\"hljs-string\">\"Changed: \"</span> + <span class=\"hljs-keyword\">new</span> SimpleDateFormat(<span class=\"hljs-string\">\"yyyy-MM-dd HH:mm:ss\"</span>).format(<span class=\"hljs-keyword\">new</span> Date());\n    }\n}\n</code></pre>\n<h5>Provider 端暴露服务</h5>\n<pre><code class=\"language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">bean</span> <span class=\"hljs-attr\">id</span>=<span class=\"hljs-string\">\"callbackService\"</span> <span class=\"hljs-attr\">class</span>=<span class=\"hljs-string\">\"com.alibaba.dubbo.samples.callback.impl.CallbackServiceImpl\"</span>/&gt;</span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dubbo:service</span> <span class=\"hljs-attr\">interface</span>=<span class=\"hljs-string\">\"com.alibaba.dubbo.samples.callback.api.CallbackService\"</span> <span class=\"hljs-attr\">ref</span>=<span class=\"hljs-string\">\"callbackService\"</span> <span class=\"hljs-attr\">connections</span>=<span class=\"hljs-string\">\"1\"</span> <span class=\"hljs-attr\">callbacks</span>=<span class=\"hljs-string\">\"1000\"</span>&gt;</span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dubbo:method</span> <span class=\"hljs-attr\">name</span>=<span class=\"hljs-string\">\"addListener\"</span>&gt;</span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dubbo:argument</span> <span class=\"hljs-attr\">index</span>=<span class=\"hljs-string\">\"1\"</span> <span class=\"hljs-attr\">callback</span>=<span class=\"hljs-string\">\"true\"</span>/&gt;</span>\n        <span class=\"hljs-comment\">&lt;!--&lt;dubbo:argument type=\"com.demo.CallbackListener\" callback=\"true\" /&gt;--&gt;</span>\n    <span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">dubbo:method</span>&gt;</span>\n<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">dubbo:service</span>&gt;</span>\n</code></pre>\n<p>这里,Provider 需要在方法中声明哪个参数是 Callback 参数。</p>\n<h5>Consumer 端实现 Callback 接口</h5>\n<pre><code class=\"language-java\">CallbackService callbackService = ...;\ncallbackService.addListener(<span class=\"hljs-string\">\"foo.bar\"</span>, <span class=\"hljs-keyword\">new</span> CallbackListener() {\n        <span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">changed</span><span class=\"hljs-params\">(String msg)</span> </span>{\n            System.out.println(<span class=\"hljs-string\">\"callback1:\"</span> + msg);\n        }\n});\n</code></pre>\n<p>Callback 接口的实现类在 Consumer 端,当方法发生调用时,Consumer 端会自动 export 一个 Callback 服务。而 Provider 端在处理调用时,判断如果参数是 Callback,则生成了一个 proxy,因此服务实现类里在调用 Callback 方法的时候,会被传递到 Consumer 端执行 Callback 实现类的代码。</p>\n<p>上述示例代码位于:此示例代码位于:<a href=\"https://github.com/dubbo/dubbo-samples/tree/master/dubbo-samples-callback\">https://github.com/dubbo/dubbo-samples/tree/master/dubbo-samples-callback</a></p>\n<p>这种调用方式有点像消息的发布和订阅,但又有区别。比如当 Consumer 端 完成了Callback 服务的 export 后,如果后续重启了,这时 Provider 端就会调不通;同时 Provider 端如何清理掉这个 proxy 也是一个问题。</p>\n<h3>事件通知</h3>\n<p>事件通知允许 Consumer 端在调用之前、调用正常返回之后或调用出现异常时,触发 <code>oninvoke</code>、<code>onreturn</code>、<code>onthrow</code> 三个事件。</p>\n<p>可以通过在配置 Consumer 时,指定事件需要通知的方法,如:</p>\n<pre><code class=\"language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">bean</span> <span class=\"hljs-attr\">id</span>=<span class=\"hljs-string\">\"demoCallback\"</span> <span class=\"hljs-attr\">class</span>=<span class=\"hljs-string\">\"com.alibaba.dubbo.samples.notify.impl.NotifyImpl\"</span> /&gt;</span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dubbo:reference</span> <span class=\"hljs-attr\">id</span>=<span class=\"hljs-string\">\"demoService\"</span> <span class=\"hljs-attr\">check</span>=<span class=\"hljs-string\">\"false\"</span> <span class=\"hljs-attr\">interface</span>=<span class=\"hljs-string\">\"com.alibaba.dubbo.samples.notify.api.DemoService\"</span> <span class=\"hljs-attr\">version</span>=<span class=\"hljs-string\">\"1.0.0\"</span> <span class=\"hljs-attr\">group</span>=<span class=\"hljs-string\">\"cn\"</span>&gt;</span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dubbo:method</span> <span class=\"hljs-attr\">name</span>=<span class=\"hljs-string\">\"sayHello\"</span> <span class=\"hljs-attr\">onreturn</span>=<span class=\"hljs-string\">\"demoCallback.onreturn\"</span> <span class=\"hljs-attr\">onthrow</span>=<span class=\"hljs-string\">\"demoCallback.onthrow\"</span>/&gt;</span>\n<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">dubbo:reference</span>&gt;</span>\n</code></pre>\n<p>其中,NotifyImpl 的代码如下:</p>\n<pre><code class=\"language-java\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span> <span class=\"hljs-title\">NotifyImpl</span> <span class=\"hljs-keyword\">implements</span> <span class=\"hljs-title\">Notify</span></span>{\n\n    <span class=\"hljs-keyword\">public</span> Map&lt;Integer, String&gt; ret = <span class=\"hljs-keyword\">new</span> HashMap&lt;Integer, String&gt;();\n    \n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">onreturn</span><span class=\"hljs-params\">(String name, <span class=\"hljs-keyword\">int</span> id)</span> </span>{\n        ret.put(id, name);\n        System.out.println(<span class=\"hljs-string\">\"onreturn: \"</span> + name);\n    }\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">onthrow</span><span class=\"hljs-params\">(Throwable ex, String name, <span class=\"hljs-keyword\">int</span> id)</span> </span>{\n        System.out.println(<span class=\"hljs-string\">\"onthrow: \"</span> + name);\n    }\n}\n</code></pre>\n<p>这里要强调一点,自定义 Notify 接口中的三个方法的参数规则如下:</p>\n<ul>\n<li><code>oninvoke</code> 方法参数与调用方法的参数相同;</li>\n<li><code>onreturn</code>方法第一个参数为调用方法的返回值,其余为调用方法的参数;</li>\n<li><code>onthrow</code>方法第一个参数为调用异常,其余为调用方法的参数。</li>\n</ul>\n<p>上述配置中,<code>sayHello</code>方法为同步调用,因此事件通知方法的执行也是同步执行。可以配置 <code>async=true</code>让方法调用为异步,这时事件通知的方法也是异步执行的。特别强调一下,<code>oninvoke</code>方法不管是否异步调用,都是同步执行的。</p>\n<p>事件通知的示例代码请参考:<a href=\"https://github.com/dubbo/dubbo-samples/tree/master/dubbo-samples-notify\">https://github.com/dubbo/dubbo-samples/tree/master/dubbo-samples-notify</a></p>\n"
     },
+    {
+      "filename": "dubbo-meetup-shanghai-jun-23rd-2018.md",
+      "__html": "<h2>第二届Dubbo开发者沙龙在上海成功举办</h2>\n<p>第二届Dubbo开发者沙龙在上海成功举办,超过700位开发者报名,现场参与人数300+,通过阿里云天池、云栖社区、大咖说引导线上直播观看次数10000+</p>\n<p>分享嘉宾及PPT:</p>\n<ul>\n<li>朱勇: Dubbo开源现状与未来规划 (中文) <a href=\"https://github.com/dubbo/awesome-dubbo/blob/master/slides/meetup/201806%40Shanghai/dubbo-status-and-roadmap.pdf\">slides</a></li>\n<li>小马哥: Dubbo Cloud Native 之路的实践与思考 (中文) <a href=\"https://github.com/dubbo/awesome-dubbo/blob/master/slides/meetup/201806%40Shanghai/dubbo-cloud-native-practices-and-thoughts.pdf\">slides</a></li>\n<li>郭平: Nacos - 贡献Dubbo生态,阿里巴巴注册中心和配置中心开源计划 (中文) <a href=\"https://github.com/dubbo/awesome-dubbo/blob/master/slides/meetup/201806%40Shanghai/nacos-open-source-initiative.pdf\">slides</a></li>\n<li>潘志伟: Dubbo在互金行业的应用场景 (中文) <a href=\"https://github.com/dubbo/awesome-dubbo/blob/master/slides/meetup/201806%40Shanghai/dubbo-practices-on-internet-finance-industries.pdf\">slides</a></li>\n</ul>\n"
+    },
     {
       "filename": "introduction-to-dubbo-spi-2.md",
       "__html": "<h1>Dubbo可扩展机制源码解析</h1>\n<hr>\n<p>在<a href=\"#/blog/introduction-to-dubbo-spi.md\">Dubbo可扩展机制实战</a>中,我们了解了Dubbo扩展机制的一些概念,初探了Dubbo中LoadBalance的实现,并自己实现了一个LoadBalance。是不是觉得Dubbo的扩展机制很不错呀,接下来,我们就深入Dubbo的源码,一睹庐山真面目。</p>\n<h1>ExtensionLoader</h1>\n<p>ExtentionLoader是最核心的类,负责扩展点的加载和生命周期管理。我们就以这个类开始吧。\nExtension的方法比较多,比较常用的方法有:</p>\n<ul>\n<li><code>public static &lt;T&gt; ExtensionLoader&lt;T&gt; getExtensionLoader(Class&lt;T&gt; type)</code></li>\n<li><code>public T getExtension(String name)</code></li>\n<li><code>public T getAdaptiveExtension()</code></li>\n</ul>\n<p>比较常见的用法有:</p>\n<ul>\n<li><code>LoadBalance lb = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(loadbalanceName)</code></li>\n<li><code>RouterFactory routerFactory = ExtensionLoader.getExtensionLoader(RouterFactory.class).getAdaptiveExtension()</code></li>\n</ul>\n<p>说明:在接下来展示的源码中,我会将无关的代码(比如日志,异常捕获等)去掉,方便大家阅读和理解。</p>\n<ol>\n<li>getExtensionLoader方法\n这是一个静态工厂方法,入参是一个可扩展的接口,返回一个该接口的ExtensionLoader实体类。通过这个实体类,可以根据name获得具体的扩展,也可以获得一个自适应扩展。</li>\n</ol>\n<pre><code class=\"language-java\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">static</span> &lt;T&gt; <span class=\"hljs-function\">ExtensionLoader&lt;T&gt; <span class=\"hljs-title\">getExtensionLoader</span><span class=\"hljs-params\">(Class&lt;T&gt; type)</span> </span>{\n        <span class=\"hljs-comment\">// 扩展点必须是接口</span>\n        <span class=\"hljs-keyword\">if</span> (!type.isInterface()) {\n            <span class=\"hljs-keyword\">throw</span> <span class=\"hljs-keyword\">new</span> IllegalArgumentException(<span class=\"hljs-string\">\"Extension type(\"</span> + type + <span class=\"hljs-string\">\") is not interface!\"</span>);\n        }\n        <span class=\"hljs-comment\">// 必须要有@SPI注解</span>\n        <span class=\"hljs-keyword\">if</span> (!withExtensionAnnotation(type)) {\n            <span class=\"hljs-keyword\">throw</span> <span class=\"hljs-keyword\">new</span> IllegalArgumentException(<span class=\"hljs-string\">\"Extension type without @SPI Annotation!\"</span>);\n        }\n        <span class=\"hljs-comment\">// 从缓存中根据接口获取对应的ExtensionLoader</span>\n        <span class=\"hljs-comment\">// 每个扩展只会被加载一次</span>\n        ExtensionLoader&lt;T&gt; loader = (ExtensionLoader&lt;T&gt;) EXTENSION_LOADERS.get(type);\n        <span class=\"hljs-keyword\">if</span> (loader == <span class=\"hljs-keyword\">null</span>) {\n            <span class=\"hljs-comment\">// 初始化扩展</span>\n            EXTENSION_LOADERS.putIfAbsent(type, <span class=\"hljs-keyword\">new</span> ExtensionLoader&lt;T&gt;(type));\n            loader = (ExtensionLoader&lt;T&gt;) EXTENSION_LOADERS.get(type);\n        }\n        <span class=\"hljs-keyword\">return</span> loader;\n    }\n    \n<span class=\"hljs-function\"><span class=\"hljs-keyword\">private</span> <span class=\"hljs-title\">ExtensionLoader</span><span class=\"hljs-params\">(Class&lt;?&gt; type)</span> </span>{\n        <span class=\"hljs-keyword\">this</span>.type = type;\n        objectFactory = (type == ExtensionFactory.class ? <span class=\"hljs-keyword\">null</span> : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());\n    }\n</code></pre>\n<ol start=\"2\">\n<li>getExtension方法</li>\n</ol>\n<pre><code class=\"language-java\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> T <span class=\"hljs-title\">getExtension</span><span class=\"hljs-params\">(String name)</span> </span>{\n        Holder&lt;Object&gt; holder = cachedInstances.get(name);\n        <span class=\"hljs-keyword\">if</span> (holder == <span class=\"hljs-keyword\">null</span>) {\n            cachedInstances.putIfAbsent(name, <span class=\"hljs-keyword\">new</span> Holder&lt;Object&gt;());\n            holder = cachedInstances.get(name);\n        }\n        Object instance = holder.get();\n        <span class=\"hljs-comment\">// 从缓存中获取,如果不存在就创建</span>\n        <span class=\"hljs-keyword\">if</span> (instance == <span class=\"hljs-keyword\">null</span>) {\n            <span class=\"hljs-keyword\">synchronized</span> (holder) {\n                instance = holder.get();\n                <span class=\"hljs-keyword\">if</span> (instance == <span class=\"hljs-keyword\">null</span>) {\n                    instance = createExtension(name);\n                    holder.set(instance);\n                }\n            }\n        }\n        <span class=\"hljs-keyword\">return</span> (T) instance;\n    }\n</code></pre>\n<p>getExtention方法中做了一些判断和缓存,主要的逻辑在createExtension方法中。我们继续看createExtention方法。</p>\n<pre><code class=\"language-java\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">private</span> T <span class=\"hljs-title\">createExtension</span><span class=\"hljs-params\">(String name)</span> </span>{\n        <span class=\"hljs-comment\">// 根据扩展点名称得到扩展类,比如对于LoadBalance,根据random得到RandomLoadBalance类</span>\n        Class&lt;?&gt; clazz = getExtensionClasses().get(name);\n        \n        T instance = (T) EXTENSION_INSTANCES.get(clazz);\n        <span class=\"hljs-keyword\">if</span> (instance == <span class=\"hljs-keyword\">null</span>) {\n              <span class=\"hljs-comment\">// 使用反射调用nesInstance来创建扩展类的一个示例</span>\n            EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());\n            instance = (T) EXTENSION_INSTANCES.get(clazz);\n        }\n        <span class=\"hljs-comment\">// 对扩展类示例进行依赖注入</span>\n        injectExtension(instance);\n        <span class=\"hljs-comment\">// 如果有wrapper,添加wrapper</span>\n        Set&lt;Class&lt;?&gt;&gt; wrapperClasses = cachedWrapperClasses;\n        <span class=\"hljs-keyword\">if</span> (wrapperClasses != <span class=\"hljs-keyword\">null</span> &amp;&amp; !wrapperClasses.isEmpty()) {\n            <span class=\"hljs-keyword\">for</span> (Class&lt;?&gt; wrapperClass : wrapperClasses) {\n                instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));\n            }\n        }\n        <span class=\"hljs-keyword\">return</span> instance;\n}\n</code></pre>\n<p>createExtension方法做了以下事情:</p>\n<ol>\n<li>先根据name来得到对应的扩展类。从ClassPath下<code>META-INF</code>文件夹下读取扩展点配置文件。</li>\n<li>使用反射创建一个扩展类的实例</li>\n<li>对扩展类实例的属性进行依赖注入,即IoC。</li>\n<li>如果有wrapper,添加wrapper。即AoP。</li>\n</ol>\n<p>下面我们来重点看下这4个过程</p>\n<ol>\n<li>根据name获取对应的扩展类\n先看代码:</li>\n</ol>\n<pre><code class=\"language-java\"><span class=\"hljs-keyword\">private</span> Map&lt;String, Class&lt;?&gt;&gt; getExtensionClasses() {\n        Map&lt;String, Class&lt;?&gt;&gt; classes = cachedClasses.get();\n        <span class=\"hljs-keyword\">if</span> (classes == <span class=\"hljs-keyword\">null</span>) {\n            <span class=\"hljs-keyword\">synchronized</span> (cachedClasses) {\n                classes = cachedClasses.get();\n                <span class=\"hljs-keyword\">if</span> (classes == <span class=\"hljs-keyword\">null</span>) {\n                    classes = loadExtensionClasses();\n                    cachedClasses.set(classes);\n                }\n            }\n        }\n        <span class=\"hljs-keyword\">return</span> classes;\n    }\n\n    <span class=\"hljs-comment\">// synchronized in getExtensionClasses</span>\n    <span class=\"hljs-keyword\">private</span> Map&lt;String, Class&lt;?&gt;&gt; loadExtensionClasses() {\n        <span class=\"hljs-keyword\">final</span> SPI defaultAnnotation = type.getAnnotation(SPI.class);\n        <span class=\"hljs-keyword\">if</span> (defaultAnnotation != <span class=\"hljs-keyword\">null</span>) {\n            String value = defaultAnnotation.value();\n            <span class=\"hljs-keyword\">if</span> (value != <span class=\"hljs-keyword\">null</span> &amp;&amp; (value = value.trim()).length() &gt; <span class=\"hljs-number\">0</span>) {\n                String[] names = NAME_SEPARATOR.split(value);\n                <span class=\"hljs-keyword\">if</span> (names.length &gt; <span class=\"hljs-number\">1</span>) {\n                    <span class=\"hljs-keyword\">throw</span> <span class=\"hljs-keyword\">new</span> IllegalStateException(<span class=\"hljs-string\">\"more than 1 default extension name on extension \"</span> + type.getName());\n                }\n                <span class=\"hljs-keyword\">if</span> (names.length == <span class=\"hljs-number\">1</span>) cachedDefaultName = names[<span class=\"hljs-number\">0</span>];\n            }\n        }\n\n        Map&lt;String, Class&lt;?&gt;&gt; extensionClasses = <span class=\"hljs-keyword\">new</span> HashMap&lt;String, Class&lt;?&gt;&gt;();\n        loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);\n        loadFile(extensionClasses, DUBBO_DIRECTORY);\n        loadFile(extensionClasses, SERVICES_DIRECTORY);\n        <span class=\"hljs-keyword\">return</span> extensionClasses;\n    }\n</code></pre>\n<p>过程很简单,先从缓存中获取,如果没有,就从配置文件中加载。配置文件的路径就是之前提到的:</p>\n<ul>\n<li><code>META-INF/dubbo/internal</code></li>\n<li><code>META-INF/dubbo</code></li>\n<li><code>META-INF/services</code></li>\n</ul>\n<ol start=\"2\">\n<li>使用反射创建扩展实例\n这个过程很简单,使用<code>clazz.newInstance())</code>来完成。创建的扩展实例的属性都是空值。</li>\n<li>扩展实例自动装配\n在实际的场景中,类之间都是有依赖的。扩展实例中也会引用一些依赖,比如简单的Java类,另一个Dubbo的扩展或一个Spring Bean等。依赖的情况很复杂,Dubbo的处理也相对复杂些。我们稍后会有专门的章节对其进行说明,现在,我们只需要知道,Dubbo可以正确的注入扩展点中的普通依赖,Dubbo扩展依赖或Spring依赖等。</li>\n<li>扩展实例自动包装\n自动包装就是要实现类似于Spring的AOP功能。Dubbo利用它在内部实现一些通用的功能,比如日志,监控等。关于扩展实例自动包装的内容,也会在后面单独讲解。</li>\n</ol>\n<p>经过上面的4步,Dubbo就创建并初始化了一个扩展实例。这个实例的依赖被注入了,也根据需要被包装了。到此为止,这个扩展实例就可以被使用了。</p>\n<h1>Dubbo SPI高级用法之自动装配</h1>\n<p>自动装配的相关代码在injectExtension方法中:</p>\n<pre><code class=\"language-java\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">private</span> T <span class=\"hljs-title\">injectExtension</span><span class=\"hljs-params\">(T instance)</span> </span>{\n    <span class=\"hljs-keyword\">for</span> (Method method : instance.getClass().getMethods()) {\n        <span class=\"hljs-keyword\">if</span> (method.getName().startsWith(<span class=\"hljs-string\">\"set\"</span>)\n                &amp;&amp; method.getParameterTypes().length == <span class=\"hljs-number\">1</span>\n                &amp;&amp; Modifier.isPublic(method.getModifiers())) {\n            Class&lt;?&gt; pt = method.getParameterTypes()[<span class=\"hljs-number\">0</span>];\n          \n            String property = method.getName().length() &gt; <span class=\"hljs-number\">3</span> ? method.getName().substring(<span class=\"hljs-number\">3</span>, <span class=\"hljs-number\">4</span>).toLowerCase() + method.getName().substring(<span class=\"hljs-number\">4</span>) : <span class=\"hljs-string\">\"\"</span>;\n            Object object = objectFactory.getExtension(pt, property);\n            <span class=\"hljs-keyword\">if</span> (object != <span class=\"hljs-keyword\">null</span>) {\n                method.invoke(instance, object);\n            }\n        }\n    }\n    <span class=\"hljs-keyword\">return</span> instance;\n}\n</code></pre>\n<p>要实现对扩展实例的依赖的自动装配,首先需要知道有哪些依赖,这些依赖的类型是什么。Dubbo的方案是查找Java标准的setter方法。即方法名以set开始,只有一个参数。如果扩展类中有这样的set方法,Dubbo会对其进行依赖注入,类似于Spring的set方法注入。\n但是Dubbo中的依赖注入比Spring要复杂,因为Spring注入的都是Spring bean,都是由Spring容器来管理的。而Dubbo的依赖注入中,需要注入的可能是另一个Dubbo的扩展,也可能是一个Spring Bean,或是Google guice的组件,或其他任何一个框架中的组件。Dubbo需要能够从任何一个场景中加载扩展。在injectExtension方法中,是用<code>Object object = objectFactory.getExtension(pt, property)</code>来实现的。objectFactory是ExtensionFactory类型的,在创建ExtensionLoader时被初始化:</p>\n<pre><code class=\"language-java\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">private</span> <span class=\"hljs-title\">ExtensionLoader</span><span class=\"hljs-params\">(Class&lt;?&gt; type)</span> </span>{\n        <span class=\"hljs-keyword\">this</span>.type = type;\n        objectFactory = (type == ExtensionFactory.class ? <span class=\"hljs-keyword\">null</span> : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());\n    }\n</code></pre>\n<p>objectFacory本身也是一个扩展,通过<code>ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension())</code>来获取。</p>\n<p><img src=\"https://raw.githubusercontent.com/vangoleo/wiki/master/dubbo/dubbo-extensionfactory.png\" alt=\"Dubbo-ExtensionFactory | left\"></p>\n<p>ExtensionLoader有三个实现:</p>\n<ol>\n<li>SpiExtensionLoader:Dubbo自己的Spi去加载Extension</li>\n<li>SpringExtensionLoader:从Spring容器中去加载Extension</li>\n<li>AdaptiveExtensionLoader: 自适应的AdaptiveExtensionLoader</li>\n</ol>\n<p>这里要注意AdaptiveExtensionLoader,源码如下:</p>\n<pre><code class=\"language-java\"><span class=\"hljs-meta\">@Adaptive</span>\n<span class=\"hljs-keyword\">public</span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span> <span class=\"hljs-title\">AdaptiveExtensionFactory</span> <span class=\"hljs-keyword\">implements</span> <span class=\"hljs-title\">ExtensionFactory</span> </span>{\n\n    <span class=\"hljs-keyword\">private</span> <span class=\"hljs-keyword\">final</span> List&lt;ExtensionFactory&gt; factories;\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-title\">AdaptiveExtensionFactory</span><span class=\"hljs-params\">()</span> </span>{\n        ExtensionLoader&lt;ExtensionFactory&gt; loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);\n        List&lt;ExtensionFactory&gt; list = <span class=\"hljs-keyword\">new</span> ArrayList&lt;ExtensionFactory&gt;();\n        <span class=\"hljs-keyword\">for</span> (String name : loader.getSupportedExtensions()) {\n            list.add(loader.getExtension(name));\n        }\n        factories = Collections.unmodifiableList(list);\n    }\n\n    <span class=\"hljs-keyword\">public</span> &lt;T&gt; <span class=\"hljs-function\">T <span class=\"hljs-title\">getExtension</span><span class=\"hljs-params\">(Class&lt;T&gt; type, String name)</span> </span>{\n        <span class=\"hljs-keyword\">for</span> (ExtensionFactory factory : factories) {\n            T extension = factory.getExtension(type, name);\n            <span class=\"hljs-keyword\">if</span> (extension != <span class=\"hljs-keyword\">null</span>) {\n                <span class=\"hljs-keyword\">return</span> extension;\n            }\n        }\n        <span class=\"hljs-keyword\">return</span> <span class=\"hljs-keyword\">null</span>;\n    }\n}\n</code></pre>\n<p>AdaptiveExtensionLoader类有@Adaptive注解。前面提到了,Dubbo会为每一个扩展创建一个自适应实例。如果扩展类上有@Adaptive,会使用该类作为自适应类。如果没有,Dubbo会为我们创建一个。所以<code>ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension())</code>会返回一个AdaptiveExtensionLoader实例,作为自适应扩展实例。\nAdaptiveExtentionLoader会遍历所有的ExtensionFactory实现,尝试着去加载扩展。如果找到了,返回。如果没有,在下一个ExtensionFactory中继续找。Dubbo内置了两个ExtensionFactory,分别从Dubbo自身的扩展机制和Spring容器中去寻找。由于ExtensionFactory本身也是一个扩展点,我们可以实现自己的ExtensionFactory,让Dubbo的自动装配支持我们自定义的组件。比如,我们在项目中使用了Google的guice这个IoC容器。我们可以实现自己的GuiceExtensionFactory,让Dubbo支持从guice容器中加载扩展。</p>\n<h1>Dubbo SPI高级用法之AoP</h1>\n<p>在用Spring的时候,我们经常会用到AOP功能。在目标类的方法前后插入其他逻辑。比如通常使用Spring AOP来实现日志,监控和鉴权等功能。\nDubbo的扩展机制,是否也支持类似的功能呢?答案是yes。在Dubbo中,有一种特殊的类,被称为Wrapper类。通过装饰者模式,使用包装类包装原始的扩展点实例。在原始扩展点实现前后插入其他逻辑,实现AOP功能。</p>\n<h3>什么是Wrapper类</h3>\n<p>那什么样类的才是Dubbo扩展机制中的Wrapper类呢?Wrapper类是一个有复制构造函数的类,也是典型的装饰者模式。下面就是一个Wrapper类:</p>\n<pre><code class=\"language-java\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span> <span class=\"hljs-title\">A</span></span>{\n    <span class=\"hljs-keyword\">private</span> A a;\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-title\">A</span><span class=\"hljs-params\">(A a)</span></span>{\n        <span class=\"hljs-keyword\">this</span>.a = a;\n    }\n}\n</code></pre>\n<p>类A有一个构造函数<code>public A(A a)</code>,构造函数的参数是A本身。这样的类就可以成为Dubbo扩展机制中的一个Wrapper类。Dubbo中这样的Wrapper类有ProtocolFilterWrapper, ProtocolListenerWrapper等, 大家可以查看源码加深理解。</p>\n<h3>怎么配置Wrapper类</h3>\n<p>在Dubbo中Wrapper类也是一个扩展点,和其他的扩展点一样,也是在<code>META-INF</code>文件夹中配置的。比如前面举例的ProtocolFilterWrapper和ProtocolListenerWrapper就是在路径<code>dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol</code>中配置的:</p>\n<pre><code class=\"language-text\">filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper\nlistener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper\nmock=com.alibaba.dubbo.rpc.support.MockProtocol\n</code></pre>\n<p>在Dubbo加载扩展配置文件时,有一段如下的代码:</p>\n<pre><code class=\"language-java\"><span class=\"hljs-keyword\">try</span> {  \n  clazz.getConstructor(type);    \n  Set&lt;Class&lt;?&gt;&gt; wrappers = cachedWrapperClasses;\n  <span class=\"hljs-keyword\">if</span> (wrappers == <span class=\"hljs-keyword\">null</span>) {\n    cachedWrapperClasses = <span class=\"hljs-keyword\">new</span> ConcurrentHashSet&lt;Class&lt;?&gt;&gt;();\n    wrappers = cachedWrapperClasses;\n  }\n  wrappers.add(clazz);\n} <span class=\"hljs-keyword\">catch</span> (NoSuchMethodException e) {}\n</code></pre>\n<p>这段代码的意思是,如果扩展类有复制构造函数,就把该类存起来,供以后使用。有复制构造函数的类就是Wrapper类。通过<code>clazz.getConstructor(type)</code>来获取参数是扩展点接口的构造函数。注意构造函数的参数类型是扩展点接口,而不是扩展类。\n以Protocol为例。配置文件<code>dubbo-rpc/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol</code>中定义了<code>filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper</code>。\nProtocolFilterWrapper代码如下:</p>\n<pre><code class=\"language-java\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span> <span class=\"hljs-title\">ProtocolFilterWrapper</span> <span class=\"hljs-keyword\">implements</span> <span class=\"hljs-title\">Protocol</span> </span>{\n\n    <span class=\"hljs-keyword\">private</span> <span class=\"hljs-keyword\">final</span> Protocol protocol;\n\n    <span class=\"hljs-comment\">// 有一个参数是Protocol的复制构造函数</span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-title\">ProtocolFilterWrapper</span><span class=\"hljs-params\">(Protocol protocol)</span> </span>{\n        <span class=\"hljs-keyword\">if</span> (protocol == <span class=\"hljs-keyword\">null</span>) {\n            <span class=\"hljs-keyword\">throw</span> <span class=\"hljs-keyword\">new</span> IllegalArgumentException(<span class=\"hljs-string\">\"protocol == null\"</span>);\n        }\n        <span class=\"hljs-keyword\">this</span>.protocol = protocol;\n    }\n</code></pre>\n<p>ProtocolFilterWrapper有一个构造函数<code>public ProtocolFilterWrapper(Protocol protocol)</code>,参数是扩展点Protocol,所以它是一个Dubbo扩展机制中的Wrapper类。ExtensionLoader会把它缓存起来,供以后创建Extension实例的时候,使用这些包装类依次包装原始扩展点。</p>\n<h1>扩展点自适应</h1>\n<p>前面讲到过,Dubbo需要在运行时根据方法参数来决定该使用哪个扩展,所以有了扩展点自适应实例。其实是一个扩展点的代理,将扩展的选择从Dubbo启动时,延迟到RPC调用时。Dubbo中每一个扩展点都有一个自适应类,如果没有显式提供,Dubbo会自动为我们创建一个,默认使用Javaassist。\n先来看下创建自适应扩展类的代码:</p>\n<pre><code class=\"language-java\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> T <span class=\"hljs-title\">getAdaptiveExtension</span><span class=\"hljs-params\">()</span> </span>{\n    Object instance = cachedAdaptiveInstance.get();\n    <span class=\"hljs-keyword\">if</span> (instance == <span class=\"hljs-keyword\">null</span>) {\n            <span class=\"hljs-keyword\">synchronized</span> (cachedAdaptiveInstance) {\n                instance = cachedAdaptiveInstance.get();\n                <span class=\"hljs-keyword\">if</span> (instance == <span class=\"hljs-keyword\">null</span>) {\n                      instance = createAdaptiveExtension();\n                      cachedAdaptiveInstance.set(instance); \n                }\n            }        \n    }\n\n    <span class=\"hljs-keyword\">return</span> (T) instance;\n}\n</code></pre>\n<p>继续看createAdaptiveExtension方法</p>\n<pre><code class=\"language-java\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">private</span> T <span class=\"hljs-title\">createAdaptiveExtension</span><span class=\"hljs-params\">()</span> </span>{        \n    <span class=\"hljs-keyword\">return</span> injectExtension((T) getAdaptiveExtensionClass().newInstance());\n}\n</code></pre>\n<p>继续看getAdaptiveExtensionClass方法</p>\n<pre><code class=\"language-java\"><span class=\"hljs-keyword\">private</span> Class&lt;?&gt; getAdaptiveExtensionClass() {\n        getExtensionClasses();\n        <span class=\"hljs-keyword\">if</span> (cachedAdaptiveClass != <span class=\"hljs-keyword\">null</span>) {\n            <span class=\"hljs-keyword\">return</span> cachedAdaptiveClass;\n        }\n        <span class=\"hljs-keyword\">return</span> cachedAdaptiveClass = createAdaptiveExtensionClass();\n    }\n</code></pre>\n<p>继续看createAdaptiveExtensionClass方法,绕了一大圈,终于来到了具体的实现了。看这个createAdaptiveExtensionClass方法,它首先会生成自适应类的Java源码,然后再将源码编译成Java的字节码,加载到JVM中。</p>\n<pre><code class=\"language-java\"><span class=\"hljs-keyword\">private</span> Class&lt;?&gt; createAdaptiveExtensionClass() {\n        String code = createAdaptiveExtensionClassCode();\n        ClassLoader classLoader = findClassLoader();\n        com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();\n        <span class=\"hljs-keyword\">return</span> compiler.compile(code, classLoader);\n    }\n</code></pre>\n<p>Compiler的代码,默认实现是javassist。</p>\n<pre><code class=\"language-java\"><span class=\"hljs-meta\">@SPI</span>(<span class=\"hljs-string\">\"javassist\"</span>)\n<span class=\"hljs-keyword\">public</span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">interface</span> <span class=\"hljs-title\">Compiler</span> </span>{\n    Class&lt;?&gt; compile(String code, ClassLoader classLoader);\n}\n</code></pre>\n<p>createAdaptiveExtensionClassCode()方法中使用一个StringBuilder来构建自适应类的Java源码。方法实现比较长,这里就不贴代码了。这种生成字节码的方式也挺有意思的,先生成Java源代码,然后编译,加载到jvm中。通过这种方式,可以更好的控制生成的Java类。而且这样也不用care各个字节码生成框架的api等。因为xxx.java文件是Java通用的,也是我们最熟悉的。只是代码的可读性不强,需要一点一点构建xx.java的内容。\n下面是使用createAdaptiveExtensionClassCode方法为Protocol创建的自适应类的Java代码范例:</p>\n<pre><code class=\"language-java\"><span class=\"hljs-keyword\">package</span> com.alibaba.dubbo.rpc;\n\n<span class=\"hljs-keyword\">import</span> com.alibaba.dubbo.common.extension.ExtensionLoader;\n\n<span class=\"hljs-keyword\">public</span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span> <span class=\"hljs-title\">Protocol</span>$<span class=\"hljs-title\">Adpative</span> <span class=\"hljs-keyword\">implements</span> <span class=\"hljs-title\">com</span>.<span class=\"hljs-title\">alibaba</span>.<span class=\"hljs-title\">dubbo</span>.<span class=\"hljs-title\">rpc</span>.<span class=\"hljs-title\">Protocol</span> </span>{\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">destroy</span><span class=\"hljs-params\">()</span> </span>{\n        <span class=\"hljs-keyword\">throw</span> <span class=\"hljs-keyword\">new</span> UnsupportedOperationException(<span class=\"hljs-string\">\"method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!\"</span>);\n    }\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">int</span> <span class=\"hljs-title\">getDefaultPort</span><span class=\"hljs-params\">()</span> </span>{\n        <span class=\"hljs-keyword\">throw</span> <span class=\"hljs-keyword\">new</span> UnsupportedOperationException(<span class=\"hljs-string\">\"method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!\"</span>);\n    }\n\n    <span class=\"hljs-keyword\">public</span> com.alibaba.dubbo.rpc.<span class=\"hljs-function\">Exporter <span class=\"hljs-title\">export</span><span class=\"hljs-params\">(com.alibaba.dubbo.rpc.Invoker arg0)</span> <span class=\"hljs-keyword\">throws</span> com.alibaba.dubbo.rpc.RpcException </span>{\n        <span class=\"hljs-keyword\">if</span> (arg0 == <span class=\"hljs-keyword\">null</span>) <span class=\"hljs-keyword\">throw</span> <span class=\"hljs-keyword\">new</span> IllegalArgumentException(<span class=\"hljs-string\">\"com.alibaba.dubbo.rpc.Invoker argument == null\"</span>);\n        <span class=\"hljs-keyword\">if</span> (arg0.getUrl() == <span class=\"hljs-keyword\">null</span>)\n            <span class=\"hljs-keyword\">throw</span> <span class=\"hljs-keyword\">new</span> IllegalArgumentException(<span class=\"hljs-string\">\"com.alibaba.dubbo.rpc.Invoker argument getUrl() == null\"</span>);\n        com.alibaba.dubbo.common.URL url = arg0.getUrl();\n        String extName = (url.getProtocol() == <span class=\"hljs-keyword\">null</span> ? <span class=\"hljs-string\">\"dubbo\"</span> : url.getProtocol());\n        <span class=\"hljs-keyword\">if</span> (extName == <span class=\"hljs-keyword\">null</span>)\n            <span class=\"hljs-keyword\">throw</span> <span class=\"hljs-keyword\">new</span> IllegalStateException(<span class=\"hljs-string\">\"Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(\"</span> + url.toString() + <span class=\"hljs-string\">\") use keys([protocol])\"</span>);\n        com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);\n        <span class=\"hljs-keyword\">return</span> extension.export(arg0);\n    }\n\n    <span class=\"hljs-keyword\">public</span> com.alibaba.dubbo.rpc.<span class=\"hljs-function\">Invoker <span class=\"hljs-title\">refer</span><span class=\"hljs-params\">(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1)</span> <span class=\"hljs-keyword\">throws</span> com.alibaba.dubbo.rpc.RpcException </span>{\n        <span class=\"hljs-keyword\">if</span> (arg1 == <span class=\"hljs-keyword\">null</span>) <span class=\"hljs-keyword\">throw</span> <span class=\"hljs-keyword\">new</span> IllegalArgumentException(<span class=\"hljs-string\">\"url == null\"</span>);\n        com.alibaba.dubbo.common.URL url = arg1;\n        String extName = (url.getProtocol() == <span class=\"hljs-keyword\">null</span> ? <span class=\"hljs-string\">\"dubbo\"</span> : url.getProtocol());\n        <span class=\"hljs-keyword\">if</span> (extName == <span class=\"hljs-keyword\">null</span>)\n            <span class=\"hljs-keyword\">throw</span> <span class=\"hljs-keyword\">new</span> IllegalStateException(<span class=\"hljs-string\">\"Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(\"</span> + url.toString() + <span class=\"hljs-string\">\") use keys([protocol])\"</span>);\n        com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);\n        <span class=\"hljs-keyword\">return</span> extension.refer(arg0, arg1);\n    }\n}\n</code></pre>\n<p>大致的逻辑和开始说的一样,通过url解析出参数,解析的逻辑由@Adaptive的value参数控制,然后再根据得到的扩展点名获取扩展点实现,然后进行调用。如果大家想知道具体的构建.java代码的逻辑,可以看<code>createAdaptiveExtensionClassCode</code>的完整实现。\n在生成的Protocol$Adpative中,发现getDefaultPort和destroy方法都是直接抛出异常的,这是为什么呢?来看看Protocol的源码</p>\n<pre><code class=\"language-java\"><span class=\"hljs-meta\">@SPI</span>(<span class=\"hljs-string\">\"dubbo\"</span>)\n<span class=\"hljs-keyword\">public</span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">interface</span> <span class=\"hljs-title\">Protocol</span> </span>{\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">int</span> <span class=\"hljs-title\">getDefaultPort</span><span class=\"hljs-params\">()</span></span>;\n\n    <span class=\"hljs-meta\">@Adaptive</span>\n    &lt;T&gt; <span class=\"hljs-function\">Exporter&lt;T&gt; <span class=\"hljs-title\">export</span><span class=\"hljs-params\">(Invoker&lt;T&gt; invoker)</span> <span class=\"hljs-keyword\">throws</span> RpcException</span>;\n\n    <span class=\"hljs-meta\">@Adaptive</span>\n    &lt;T&gt; <span class=\"hljs-function\">Invoker&lt;T&gt; <span class=\"hljs-title\">refer</span><span class=\"hljs-params\">(Class&lt;T&gt; type, URL url)</span> <span class=\"hljs-keyword\">throws</span> RpcException</span>;\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">destroy</span><span class=\"hljs-params\">()</span></span>;\n</code></pre>\n<p>可以看到Protocol接口中有4个方法,但只有export和refer两个方法使用了@Adaptive注解。Dubbo自动生成的自适应实例,只有@Adaptive修饰的方法才有具体的实现。所以,Protocol$Adpative类中,也只有export和refer这两个方法有具体的实现,其余方法都是抛出异常。</p>\n"
@@ -38,6 +42,10 @@
       "filename": "dubbo-meetup-beijing-may-12th-2018.md",
       "__html": "<h2>The first Dubbo meetup has been held in Beijing</h2>\n<p>The first Dubbo meetup has successfully been held in Beijing, over 400+ people were present. What a great event!</p>\n<p>Please enjoy the slides of the topics:</p>\n<ul>\n<li>Ian Luo: Dubbo's present and future (Chinese) <a href=\"https://github.com/dubbo/awesome-dubbo/raw/master/slides/meetup/201805%40Beijing/dubbo-present-and-future.pdf\">slides</a></li>\n<li>Jun Liu: Introduction to the 4th Aliware Performance Challenge (Chinese) <a href=\"https://github.com/dubbo/awesome-dubbo/raw/master/slides/meetup/201805%40Beijing/introduction-to-4th-aliware-performance-challenge.pdf\">slides</a></li>\n<li>Zhixuan Chen: Quickly building Microservice with Dubbo and Spring-boot (Chinese) <a href=\"https://github.com/dubbo/awesome-dubbo/raw/master/slides/meetup/201805%40Beijing/quickly-building-microservice-with-dubbo-and-springboot.pdf\">slides</a></li>\n<li>Xin Wang: Dubbo and Weidian's Practice on Microservice Architecture (Chinese) <a href=\"https://github.com/dubbo/awesome-dubbo/raw/master/slides/meetup/201805%40Beijing/dubbo-and-weidian's-practice-on-microservice-architecture.pdf\">slides</a></li>\n</ul>\n"
     },
+    {
+      "filename": "dubbo-meetup-shanghai-jun-23rd-2018.md",
+      "__html": "<h2>Dubbo Shanghai meetup has been held successfully</h2>\n<p>The sencond Dubbo meetup has successfully been held in Shanghai, over 700 people submitted registration, and over 300 were present, more than 10,000 watched the live online. A great event again!</p>\n<p>Please enjoy the slides of the topics:</p>\n<ul>\n<li>Jerrick Zhu: Dubbo Status and Roadmap (Chinese) <a href=\"https://github.com/dubbo/awesome-dubbo/blob/master/slides/meetup/201806%40Shanghai/dubbo-status-and-roadmap.pdf\">slides</a></li>\n<li>Mercy Ma: Dubbo Cloud Native Practices and Thoughts (Chinese) <a href=\"https://github.com/dubbo/awesome-dubbo/blob/master/slides/meetup/201806%40Shanghai/dubbo-cloud-native-practices-and-thoughts.pdf\">slides</a></li>\n<li>Ping Guo: Nacos Open Source Initiative (Chinese) <a href=\"https://github.com/dubbo/awesome-dubbo/blob/master/slides/meetup/201806%40Shanghai/nacos-open-source-initiative.pdf\">slides</a></li>\n<li>Zhiwei Pan: Dubbo Practices on Internet Finance Industries (Chinese) <a href=\"https://github.com/dubbo/awesome-dubbo/blob/master/slides/meetup/201806%40Shanghai/dubbo-practices-on-internet-finance-industries.pdf\">slides</a></li>\n</ul>\n"
+    },
     {
       "filename": "gsoc-2018.md",
       "__html": "<h2>The GSoC(Google Summer of Code) 2018 projects has been announced</h2>\n<p>The GSoC(Google Summer of Code) 2018 projects has been announced, Raghu Reddy's project &quot;Extending Serialization protocols support for Apache Dubbo&quot; has been <a href=\"https://summerofcode.withgoogle.com/projects/#4747840161579008\">accepted</a>! Congratulations!</p>\n"
diff --git a/site_config/community.jsx b/site_config/community.jsx
index be45004..1a622a0 100644
--- a/site_config/community.jsx
+++ b/site_config/community.jsx
@@ -6,6 +6,13 @@ export default {
     events: {
       title: 'Events & News',
       list: [
+        {
+          img: './img/blog/dubbo-shanghai-meetup.jpeg',
+          title: 'Dubbo Shanghai meetup has been held successfully',
+          content: 'The Dubbo meetup has successfully been held in Shanghai, over 700 people submitted registration, and over 300 were present, more than 10,000 watched the live online.',
+          dateStr: 'June 23rd,2018',
+          link: '/blog/dubbo-meetup-shanghai-jun-23rd-2018.md',
+        },
         {
           img: './img/blog/dubbo-beijing-meetup.png',
           title: 'The first Dubbo meetup has successfully been held in Beijing',
@@ -369,6 +376,13 @@ export default {
     events: {
       title: '事件 & 新闻',
       list: [
+        {
+          img: './img/blog/dubbo-shanghai-meetup.jpeg',
+          title: '第二届Dubbo开发者沙龙在上海成功举办',
+          content: '第二届Dubbo开发者沙龙在上海成功举办,超过700位开发者报名,现场参与人数300+,通过阿里云天池、云栖社区、大咖说引导线上直播观看次数10000+',
+          dateStr: 'Jun 23rd,2018',
+          link: '/blog/dubbo-meetup-shanghai-jun-23rd-2018.md',
+        },
         {
           img: './img/blog/dubbo-beijing-meetup.png',
           title: '首届Dubbo开发者沙龙在北京成功举办',


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services

---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org