This is an automated email from the ASF dual-hosted git repository. huxing pushed a commit to branch asf-site in repository https://gitbox.apache.org/repos/asf/incubator-dubbo-website.git
commit 57ace46955ac506c82098ee0405125be8ccb0c08 Author: Huxing Zhang <[email protected]> AuthorDate: Wed Dec 12 13:16:40 2018 +0800 Publish website. --- build/blog.js | 6 +- build/blogDetail.js | 6 +- build/community.js | 6 +- build/documentation.js | 6 +- build/home.js | 6 +- md_json/blog.json | 43 ++++ zh-cn/blog/apachecon-na-2018.html | 34 +++ zh-cn/blog/apachecon-na-2018.json | 6 + zh-cn/blog/dubbo-cluster-error-handling.html | 232 +++++++++++++++++++++ zh-cn/blog/dubbo-cluster-error-handling.json | 10 + zh-cn/blog/dubbo-meetup-beijing-may-12th-2018.html | 41 ++++ zh-cn/blog/dubbo-meetup-beijing-may-12th-2018.json | 10 + zh-cn/blog/first-dubbo-filter.html | 183 ++++++++++++++++ zh-cn/blog/first-dubbo-filter.json | 6 + zh-cn/blog/index.html | 2 +- zh-cn/blog/qcon-beijing-2018.html | 34 +++ zh-cn/blog/qcon-beijing-2018.json | 6 + zh-cn/blog/service-and-version.html | 213 +++++++++++++++++++ zh-cn/blog/service-and-version.json | 6 + zh-cn/blog/test-verification.html | 111 ++++++++++ zh-cn/blog/test-verification.json | 6 + zh-cn/docs/admin/install/admin-console.html | 20 +- zh-cn/docs/admin/install/admin-console.json | 2 +- 23 files changed, 977 insertions(+), 18 deletions(-) diff --git a/build/blog.js b/build/blog.js index d4f01a5..98a7e59 100644 --- a/build/blog.js +++ b/build/blog.js @@ -1,6 +1,6 @@ -!function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var n={};t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="/build/",t(t.s=78) [...] - Copyright (c) 2017 Jed Watson. +!function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var n={};t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="/build/",t(t.s=80) [...] + Copyright (c) 2016 Jed Watson. Licensed under the MIT License (MIT), see http://jedwatson.github.io/classnames */ -!function(){"use strict";function n(){for(var e=[],t=0;t<arguments.length;t++){var r=arguments[t];if(r){var o=typeof r;if("string"===o||"number"===o)e.push(r);else if(Array.isArray(r)&&r.length){var a=n.apply(null,r);a&&e.push(a)}else if("object"===o)for(var u in r)i.call(r,u)&&r[u]&&e.push(u)}}return e.join(" ")}var i={}.hasOwnProperty;void 0!==e&&e.exports?(n.default=n,e.exports=n):(r=[],void 0!==(o=function(){return n}.apply(t,r))&&(e.exports=o))}()},function(e,t,n){"use strict";funct [...] \ No newline at end of file +!function(){"use strict";function n(){for(var e=[],t=0;t<arguments.length;t++){var r=arguments[t];if(r){var o=typeof r;if("string"===o||"number"===o)e.push(r);else if(Array.isArray(r))e.push(n.apply(null,r));else if("object"===o)for(var a in r)i.call(r,a)&&r[a]&&e.push(a)}}return e.join(" ")}var i={}.hasOwnProperty;void 0!==e&&e.exports?e.exports=n:(r=[],void 0!==(o=function(){return n}.apply(t,r))&&(e.exports=o))}()},function(e,t,n){"use strict";function r(e,t,r){var o=r.configurable,u= [...] \ No newline at end of file diff --git a/build/blogDetail.js b/build/blogDetail.js index ec5b66b..d737c9f 100644 --- a/build/blogDetail.js +++ b/build/blogDetail.js @@ -1,6 +1,6 @@ -!function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var n={};t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="/build/",t(t.s=79) [...] - Copyright (c) 2017 Jed Watson. +!function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var n={};t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="/build/",t(t.s=81) [...] + Copyright (c) 2016 Jed Watson. Licensed under the MIT License (MIT), see http://jedwatson.github.io/classnames */ -!function(){"use strict";function n(){for(var e=[],t=0;t<arguments.length;t++){var r=arguments[t];if(r){var o=typeof r;if("string"===o||"number"===o)e.push(r);else if(Array.isArray(r)&&r.length){var a=n.apply(null,r);a&&e.push(a)}else if("object"===o)for(var u in r)i.call(r,u)&&r[u]&&e.push(u)}}return e.join(" ")}var i={}.hasOwnProperty;void 0!==e&&e.exports?(n.default=n,e.exports=n):(r=[],void 0!==(o=function(){return n}.apply(t,r))&&(e.exports=o))}()},function(e,t,n){"use strict";funct [...] \ No newline at end of file +!function(){"use strict";function n(){for(var e=[],t=0;t<arguments.length;t++){var r=arguments[t];if(r){var o=typeof r;if("string"===o||"number"===o)e.push(r);else if(Array.isArray(r))e.push(n.apply(null,r));else if("object"===o)for(var a in r)i.call(r,a)&&r[a]&&e.push(a)}}return e.join(" ")}var i={}.hasOwnProperty;void 0!==e&&e.exports?e.exports=n:(r=[],void 0!==(o=function(){return n}.apply(t,r))&&(e.exports=o))}()},function(e,t,n){"use strict";function r(e,t,r){var o=r.configurable,u= [...] \ No newline at end of file diff --git a/build/community.js b/build/community.js index 4704938..9b45e1b 100644 --- a/build/community.js +++ b/build/community.js @@ -1,6 +1,6 @@ -!function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var n={};t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="/build/",t(t.s=80) [...] - Copyright (c) 2017 Jed Watson. +!function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var n={};t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="/build/",t(t.s=82) [...] + Copyright (c) 2016 Jed Watson. Licensed under the MIT License (MIT), see http://jedwatson.github.io/classnames */ -!function(){"use strict";function n(){for(var e=[],t=0;t<arguments.length;t++){var r=arguments[t];if(r){var o=typeof r;if("string"===o||"number"===o)e.push(r);else if(Array.isArray(r)&&r.length){var a=n.apply(null,r);a&&e.push(a)}else if("object"===o)for(var l in r)i.call(r,l)&&r[l]&&e.push(l)}}return e.join(" ")}var i={}.hasOwnProperty;void 0!==e&&e.exports?(n.default=n,e.exports=n):(r=[],void 0!==(o=function(){return n}.apply(t,r))&&(e.exports=o))}()},function(e,t,n){"use strict";funct [...] \ No newline at end of file +!function(){"use strict";function n(){for(var e=[],t=0;t<arguments.length;t++){var r=arguments[t];if(r){var o=typeof r;if("string"===o||"number"===o)e.push(r);else if(Array.isArray(r))e.push(n.apply(null,r));else if("object"===o)for(var a in r)i.call(r,a)&&r[a]&&e.push(a)}}return e.join(" ")}var i={}.hasOwnProperty;void 0!==e&&e.exports?e.exports=n:(r=[],void 0!==(o=function(){return n}.apply(t,r))&&(e.exports=o))}()},function(e,t,n){"use strict";function r(e,t,r){var o=r.configurable,l= [...] \ No newline at end of file diff --git a/build/documentation.js b/build/documentation.js index b1330c7..6637d67 100644 --- a/build/documentation.js +++ b/build/documentation.js @@ -1,6 +1,6 @@ -!function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var n={};t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="/build/",t(t.s=81) [...] - Copyright (c) 2017 Jed Watson. +!function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var n={};t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="/build/",t(t.s=83) [...] + Copyright (c) 2016 Jed Watson. Licensed under the MIT License (MIT), see http://jedwatson.github.io/classnames */ -!function(){"use strict";function n(){for(var e=[],t=0;t<arguments.length;t++){var r=arguments[t];if(r){var o=typeof r;if("string"===o||"number"===o)e.push(r);else if(Array.isArray(r)&&r.length){var l=n.apply(null,r);l&&e.push(l)}else if("object"===o)for(var a in r)i.call(r,a)&&r[a]&&e.push(a)}}return e.join(" ")}var i={}.hasOwnProperty;void 0!==e&&e.exports?(n.default=n,e.exports=n):(r=[],void 0!==(o=function(){return n}.apply(t,r))&&(e.exports=o))}()},function(e,t,n){"use strict";funct [...] \ No newline at end of file +!function(){"use strict";function n(){for(var e=[],t=0;t<arguments.length;t++){var r=arguments[t];if(r){var o=typeof r;if("string"===o||"number"===o)e.push(r);else if(Array.isArray(r))e.push(n.apply(null,r));else if("object"===o)for(var l in r)i.call(r,l)&&r[l]&&e.push(l)}}return e.join(" ")}var i={}.hasOwnProperty;void 0!==e&&e.exports?e.exports=n:(r=[],void 0!==(o=function(){return n}.apply(t,r))&&(e.exports=o))}()},function(e,t,n){"use strict";function r(e,t,r){var o=r.configurable,a= [...] \ No newline at end of file diff --git a/build/home.js b/build/home.js index d580bc6..cbb9bf5 100644 --- a/build/home.js +++ b/build/home.js @@ -1,6 +1,6 @@ -!function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var n={};t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="/build/",t(t.s=82) [...] - Copyright (c) 2017 Jed Watson. +!function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var n={};t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="/build/",t(t.s=84) [...] + Copyright (c) 2016 Jed Watson. Licensed under the MIT License (MIT), see http://jedwatson.github.io/classnames */ -!function(){"use strict";function n(){for(var e=[],t=0;t<arguments.length;t++){var r=arguments[t];if(r){var o=typeof r;if("string"===o||"number"===o)e.push(r);else if(Array.isArray(r)&&r.length){var i=n.apply(null,r);i&&e.push(i)}else if("object"===o)for(var u in r)a.call(r,u)&&r[u]&&e.push(u)}}return e.join(" ")}var a={}.hasOwnProperty;void 0!==e&&e.exports?(n.default=n,e.exports=n):(r=[],void 0!==(o=function(){return n}.apply(t,r))&&(e.exports=o))}()},function(e,t,n){"use strict";funct [...] \ No newline at end of file +!function(){"use strict";function n(){for(var e=[],t=0;t<arguments.length;t++){var r=arguments[t];if(r){var o=typeof r;if("string"===o||"number"===o)e.push(r);else if(Array.isArray(r))e.push(n.apply(null,r));else if("object"===o)for(var i in r)a.call(r,i)&&r[i]&&e.push(i)}}return e.join(" ")}var a={}.hasOwnProperty;void 0!==e&&e.exports?e.exports=n:(r=[],void 0!==(o=function(){return n}.apply(t,r))&&(e.exports=o))}()},function(e,t,n){"use strict";function r(e,t,r){var o=r.configurable,u= [...] \ No newline at end of file diff --git a/md_json/blog.json b/md_json/blog.json index 12d642c..cd02a27 100644 --- a/md_json/blog.json +++ b/md_json/blog.json @@ -202,6 +202,11 @@ ], "zh-cn": [ { + "filename": "apachecon-na-2018.md", + "link": "/zh-cn/blog/apachecon-na-2018.html", + "meta": {} + }, + { "filename": "download.md", "link": "/zh-cn/blog/download.html", "meta": {} @@ -234,6 +239,15 @@ } }, { + "filename": "dubbo-cluster-error-handling.md", + "link": "/zh-cn/blog/dubbo-cluster-error-handling.html", + "meta": { + "title": "Dubbo集群容错", + "keywords": "Dubbo, RPC, cluster, Error-handling", + "description": "在分布式系统中,集群某个某些节点出现问题是大概率事件,因此在设计分布式RPC框架的过程中,必须要把失败作为设计的一等公民来对待。一次调用失败之后,应该如何选择对失败的选择策略,这是一个见仁见智的问题,每种策略可能都有自己独特的应用场景。因此,作为框架来说,应当针对不同场景提供多种策略,供用户进行选择。本文介绍了Dubbo框架提供的多种错误处理策略,并通过实例说明如何进行配置。" + } + }, + { "filename": "dubbo-compatible.md", "link": "/zh-cn/blog/dubbo-compatible.html", "meta": { @@ -297,6 +311,15 @@ } }, { + "filename": "dubbo-meetup-beijing-may-12th-2018.md", + "link": "/zh-cn/blog/dubbo-meetup-beijing-may-12th-2018.html", + "meta": { + "title": "首届Dubbo开发者沙龙在北京成功举办", + "keywords": "Dubbo, Beijing, meetup", + "description": "首届Dubbo开发者沙龙在北京成功举办" + } + }, + { "filename": "dubbo-meetup-chengdu.md", "link": "/zh-cn/blog/dubbo-meetup-chengdu.html", "meta": { @@ -369,6 +392,11 @@ } }, { + "filename": "first-dubbo-filter.md", + "link": "/zh-cn/blog/first-dubbo-filter.html", + "meta": {} + }, + { "filename": "how-to-involve-dubbo-community.md", "link": "/zh-cn/blog/how-to-involve-dubbo-community.html", "meta": { @@ -438,6 +466,11 @@ } }, { + "filename": "qcon-beijing-2018.md", + "link": "/zh-cn/blog/qcon-beijing-2018.html", + "meta": {} + }, + { "filename": "sentinel-introduction-for-dubbo.md", "link": "/zh-cn/blog/sentinel-introduction-for-dubbo.html", "meta": { @@ -447,6 +480,11 @@ } }, { + "filename": "service-and-version.md", + "link": "/zh-cn/blog/service-and-version.html", + "meta": {} + }, + { "filename": "spring-boot-dubbo-start-stop-analysis.md", "link": "/zh-cn/blog/spring-boot-dubbo-start-stop-analysis.html", "meta": { @@ -456,6 +494,11 @@ } }, { + "filename": "test-verification.md", + "link": "/zh-cn/blog/test-verification.html", + "meta": {} + }, + { "filename": "tracing-with-skywalking.md", "link": "/zh-cn/blog/tracing-with-skywalking.html", "meta": { diff --git a/zh-cn/blog/apachecon-na-2018.html b/zh-cn/blog/apachecon-na-2018.html new file mode 100644 index 0000000..6104fa9 --- /dev/null +++ b/zh-cn/blog/apachecon-na-2018.html @@ -0,0 +1,34 @@ +<!DOCTYPE html> +<html lang="en"> + +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> + <meta name="keywords" content="apachecon-na-2018" /> + <meta name="description" content="apachecon-na-2018" /> + <!-- 网页标签标题 --> + <title>apachecon-na-2018</title> + <link rel="shortcut icon" href="/img/dubbo.ico"/> + <link rel="stylesheet" href="/build/blogDetail.css" /> +</head> +<body> + <div id="root"><div class="blog-detail-page" data-reactroot=""><header class="header-container header-container-normal"><div class="header-body"><a href="/zh-cn/index.html"><img class="logo" src="/img/dubbo_colorful.png"/></a><div class="search search-normal"><span class="icon-search"></span></div><span class="language-switch language-switch-normal">En</span><div class="header-menu"><img class="header-menu-toggle" src="/img/menu_gray.png"/><ul><li class="menu-item menu-item-normal"><a h [...] +<p>罗毅/刘军将在蒙特利尔举办的 ApacheCon 大会上进行题为"Introducing Apache Dubbo(Incubating): What is Dubbo and How it Works"的演讲。请点击<a href="https://apachecon.dukecon.org/acna/2018/#/scheduledEvent/b8db9dc580d85853f">此处</a>查看大会议程,并在<a href="https://www.eventbrite.com/e/apachecon-north-america-2018-registration-43200327342">此处</a>中进行注册。</p> +</section><footer class="footer-container"><div class="footer-body"><img src="/img/dubbo_gray.png"/><img class="apache" src="/img/apache_logo.png"/><div class="cols-container"><div class="col col-12"><h3>Disclaimer</h3><p>Apache Dubbo is an effort undergoing incubation at The Apache Software Foundation (ASF), sponsored by the Incubator. Incubation is required of all newly accepted projects until a further review indicates that the infrastructure, communications, and decision making proce [...] + <script src="https://f.alicdn.com/react/15.4.1/react-with-addons.min.js"></script> + <script src="https://f.alicdn.com/react/15.4.1/react-dom.min.js"></script> + <script> + window.rootPath = ''; + </script> + <script src="/build/blogDetail.js"></script> + <!-- Global site tag (gtag.js) - Google Analytics --> + <script async src="https://www.googletagmanager.com/gtag/js?id=UA-112489517-1"></script> + <script> + window.dataLayer = window.dataLayer || []; + function gtag(){dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'UA-112489517-1'); + </script> +</body> +</html> \ No newline at end of file diff --git a/zh-cn/blog/apachecon-na-2018.json b/zh-cn/blog/apachecon-na-2018.json new file mode 100644 index 0000000..0344e80 --- /dev/null +++ b/zh-cn/blog/apachecon-na-2018.json @@ -0,0 +1,6 @@ +{ + "filename": "apachecon-na-2018.md", + "__html": "<h2>ApacheCon大会议程公布</h2>\n<p>罗毅/刘军将在蒙特利尔举办的 ApacheCon 大会上进行题为"Introducing Apache Dubbo(Incubating): What is Dubbo and How it Works"的演讲。请点击<a href=\"https://apachecon.dukecon.org/acna/2018/#/scheduledEvent/b8db9dc580d85853f\">此处</a>查看大会议程,并在<a href=\"https://www.eventbrite.com/e/apachecon-north-america-2018-registration-43200327342\">此处</a>中进行注册。</p>\n", + "link": "/zh-cn/blog/apachecon-na-2018.html", + "meta": {} +} \ No newline at end of file diff --git a/zh-cn/blog/dubbo-cluster-error-handling.html b/zh-cn/blog/dubbo-cluster-error-handling.html new file mode 100644 index 0000000..ec48a71 --- /dev/null +++ b/zh-cn/blog/dubbo-cluster-error-handling.html @@ -0,0 +1,232 @@ +<!DOCTYPE html> +<html lang="en"> + +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> + <meta name="keywords" content="Dubbo, RPC, cluster, Error-handling" /> + <meta name="description" content="在分布式系统中,集群某个某些节点出现问题是大概率事件,因此在设计分布式RPC框架的过程中,必须要把失败作为设计的一等公民来对待。一次调用失败之后,应该如何选择对失败的选择策略,这是一个见仁见智的问题,每种策略可能都有自己独特的应用场景。因此,作为框架来说,应当针对不同场景提供多种策略,供用户进行选择。本文介绍了Dubbo框架提供的多种错误处理策略,并通过实例说明如何进行配置。" /> + <!-- 网页标签标题 --> + <title>Dubbo集群容错</title> + <link rel="shortcut icon" href="/img/dubbo.ico"/> + <link rel="stylesheet" href="/build/blogDetail.css" /> +</head> +<body> + <div id="root"><div class="blog-detail-page" data-reactroot=""><header class="header-container header-container-normal"><div class="header-body"><a href="/zh-cn/index.html"><img class="logo" src="/img/dubbo_colorful.png"/></a><div class="search search-normal"><span class="icon-search"></span></div><span class="language-switch language-switch-normal">En</span><div class="header-menu"><img class="header-menu-toggle" src="/img/menu_gray.png"/><ul><li class="menu-item menu-item-normal"><a h [...] +<p>在分布式系统中,集群某个某些节点出现问题是大概率事件,因此在设计分布式RPC框架的过程中,必须要把失败作为设计的一等公民来对待。一次调用失败之后,应该如何选择对失败的选择策略,这是一个见仁见智的问题,每种策略可能都有自己独特的应用场景。因此,作为框架来说,应当针对不同场景提供多种策略,供用户进行选择。</p> +<p>在Dubbo设计中,通过Cluster这个接口的抽象,把一组可供调用的Provider信息组合成为一个统一的<code>Invoker</code>供调用方进行调用。经过路由规则过滤,负载均衡选址后,选中一个具体地址进行调用,如果调用失败,则会按照集群配置的容错策略进行容错处理。</p> +<p>Dubbo默认内置了若干容错策略,如果不能满足用户需求,则可以通过自定义容错策略进行配置。</p> +<h3>内置容错策略</h3> +<p>Dubbo主要内置了如下几种策略:</p> +<ul> +<li>Failover(失败自动切换)</li> +<li>Failsafe(失败安全)</li> +<li>Failfast(快速失败)</li> +<li>Failback(失败自动恢复)</li> +<li>Forking(并行调用)</li> +<li>Broadcast(广播调用)</li> +</ul> +<p>这些名称比较相似,概念也比较容易混淆,下面逐一进行解释。</p> +<h4>Failover(失败自动切换)</h4> +<p><code>Failover</code>是高可用系统中的一个常用概念,服务器通常拥有主备两套机器配置,如果主服务器出现故障,则自动切换到备服务器中,从而保证了整体的高可用性。</p> +<p>Dubbo也借鉴了这个思想,并且把它作为Dubbo<code>默认的容错策略</code>。当调用出现失败的时候,根据配置的重试次数,会自动从其他可用地址中重新选择一个可用的地址进行调用,直到调用成功,或者是达到重试的上限位置。</p> +<p>Dubbo里默认配置的重试次数是2,也就是说,算上第一次调用,最多会调用3次。</p> +<p>其配置方法,容错策略既可以在服务提供方配置,也可以服务调用方进行配置。而重试次数的配置则更为灵活,既可以在服务级别进行配置,也可以在方法级别进行配置。具体优先顺序为:</p> +<pre><code>服务调用方方法级配置 > 服务调用方服务级配置 > 服务提供方方法级配置 > 服务提供方服务级配置 +</code></pre> +<p>以XML方式为例,具体配置方法如下:</p> +<p>服务提供方,服务级配置</p> +<pre><code class="language-xml"><span class="hljs-tag"><<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"org.apache.dubbo.demo.DemoService"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"demoService"</span> <span class="hljs-attr">cluster</span>=<span class="hljs-string">"failover"</span> <span class="hljs-attr">retries</span>=<span class="hljs-string">"2"</span> /></span> +</code></pre> +<p>服务提供方,方法级配置</p> +<pre><code class="language-xml"><span class="hljs-tag"><<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"org.apache.dubbo.demo.DemoService"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"demoService"</span><span class="hljs-attr">cluster</span>=<span class="hljs-string">"failover"</span>></span> + <span class="hljs-tag"><<span class="hljs-name">dubbo:method</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"sayHello"</span> <span class="hljs-attr">retries</span>=<span class="hljs-string">"2"</span> /></span> + <span class="hljs-tag"></<span class="hljs-name">dubbo:reference</span>></span> +</code></pre> +<p>服务调用方,服务级配置</p> +<pre><code class="language-xml"><span class="hljs-tag"><<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"demoService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"org.apache.dubbo.demo.DemoService"</span> <span class="hljs-attr">cluster</span>=<span class="hljs-string">"failover"</span> <span class="hljs-attr">retries</span>=<span class="hljs-string">"1"</span>/></span> +</code></pre> +<p>服务调用方,方法级配置:</p> +<pre><code class="language-xml"> <span class="hljs-tag"><<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"demoService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"org.apache.dubbo.demo.DemoService"</span> <span class="hljs-attr">cluster</span>=<span class="hljs-string">"failover"</span>></span> + <span class="hljs-tag"><<span class="hljs-name">dubbo:method</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"sayHello"</span> <span class="hljs-attr">retries</span>=<span class="hljs-string">"3"</span> /></span> + <span class="hljs-tag"></<span class="hljs-name">dubbo:reference</span>></span> +</code></pre> +<p>Failover可以自动对失败进行重试,对调用者屏蔽了失败的细节,但是Failover策略也会带来一些副作用:</p> +<ul> +<li>重试会额外增加一下开销,例如增加资源的使用,在高负载系统下,额外的重试可能让系统雪上加霜。</li> +<li>重试会增加调用的响应时间。</li> +<li>某些情况下,重试甚至会造成资源的浪费。考虑一个调用场景,A->B->C,如果A处设置了超时100ms,再B->C的第一次调用完成时已经超过了100ms,但很不幸B->C失败,这时候会进行重试,但其实这时候重试已经没有意义,因此在A看来这次调用已经超时,A可能已经开始执行其他逻辑。</li> +</ul> +<h4>Failsafe(失败安全)</h4> +<p>失败安全策略的核心是即使失败了也不会影响整个调用流程。通常情况下用于旁路系统或流程中,它的失败不影响核心业务的正确性。在实现上,当出现调用失败时,会忽略此错误,并记录一条日志,同时返回一个空结果,在上游看来调用是成功的。</p> +<p>应用场景,可以用于写入审计日志等操作。</p> +<p>具体配置方法:</p> +<p>服务提供方,服务级配置</p> +<pre><code class="language-xml"><span class="hljs-tag"><<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"org.apache.dubbo.demo.DemoService"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"demoService"</span> <span class="hljs-attr">cluster</span>=<span class="hljs-string">"failsafe"</span> /></span> +</code></pre> +<p>服务调用方,服务级配置</p> +<pre><code class="language-xml"><span class="hljs-tag"><<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"demoService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"org.apache.dubbo.demo.DemoService"</span> <span class="hljs-attr">cluster</span>=<span class="hljs-string">"failsafe"</span>/></span> +</code></pre> +<p>其中服务调用方配置优先于服务提供方配置。</p> +<h4>Failfast(快速失败)</h4> +<p>某些业务场景中,某些操作可能是非幂等的,如果重复发起调用,可能会导致出现脏数据等。例如调用某个服务,其中包含一个数据库的写操作,如果写操作完成,但是在发送结果给调用方的过程中出错了,那么在调用发看来这次调用失败了,但其实数据写入已经完成。这种情况下,重试可能并不是一个好策略,这时候就需要使用到<code>Failfast</code>策略,调用失败立即报错。让调用方来决定下一步的操作并保证业务的幂等性。</p> +<p>具体配置方法:</p> +<p>服务提供方,服务级配置</p> +<pre><code class="language-xml"><span class="hljs-tag"><<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"org.apache.dubbo.demo.DemoService"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"demoService"</span> <span class="hljs-attr">cluster</span>=<span class="hljs-string">"failfast"</span> /></span> +</code></pre> +<p>服务调用方,服务级配置</p> +<pre><code class="language-xml"><span class="hljs-tag"><<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"demoService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"org.apache.dubbo.demo.DemoService"</span> <span class="hljs-attr">cluster</span>=<span class="hljs-string">"failfast"</span>/></span> +</code></pre> +<p>其中服务调用方配置优先于服务提供方配置。</p> +<h4>Failback(失败自动恢复)</h4> +<p><code>Failback</code>通常和<code>Failover</code>两个概念联系在一起。在高可用系统中,当主机发生故障,通过<code>Failover</code>进行主备切换后,待故障恢复后,系统应该具备自动恢复原始配置的能力。</p> +<p>Dubbo中的<code>Failback</code>策略中,如果调用失败,则此次失败相当于<code>Failsafe</code>,将返回一个空结果。而与<code>Failsafe</code>不同的是,Failback策略会将这次调用加入内存中的失败列表中,对于这个列表中的失败调用,会在另一个线程中进行异步重试,重试如果再发生失败,则会忽略,即使重试调用成功,原来的调用方也感知不到了。因此它通常适合于,对于实时性要求不高,且不需要返回值的一些异步操作。</p> +<p>具体配置方法:</p> +<p>服务提供方,服务级配置</p> +<pre><code class="language-xml"><span class="hljs-tag"><<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"org.apache.dubbo.demo.DemoService"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"demoService"</span> <span class="hljs-attr">cluster</span>=<span class="hljs-string">"failsafe"</span> /></span> +</code></pre> +<p>服务调用方,服务级配置</p> +<pre><code class="language-xml"><span class="hljs-tag"><<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"demoService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"org.apache.dubbo.demo.DemoService"</span> <span class="hljs-attr">cluster</span>=<span class="hljs-string">"failsafe"</span>/></span> +</code></pre> +<p>其中服务调用方配置优先于服务提供方配置。</p> +<p>按照目前的实现,Failback策略还有一些局限,例如内存中的失败调用列表没有上限,可能导致堆积,异步重试的执行间隔无法调整,默认是5秒。</p> +<h4>Forking(并行调用)</h4> +<p>上述几种策略中,主要都是针对调用失败发生后如何进行弥补的角度去考虑的,而<code>Forking</code>策略则跟上述几种策略不同,是一种典型的用成本换时间的思路。即第一次调用的时候就同时发起多个调用,只要其中一个调用成功,就认为成功。在资源充足,且对于失败的容忍度较低的场景下,可以采用此策略。</p> +<p>具体配置方法:</p> +<p>服务提供方,服务级配置</p> +<pre><code class="language-xml"><span class="hljs-tag"><<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"org.apache.dubbo.demo.DemoService"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"demoService"</span> <span class="hljs-attr">cluster</span>=<span class="hljs-string">"forking"</span> /></span> +</code></pre> +<p>服务调用方,服务级配置</p> +<pre><code class="language-xml"><span class="hljs-tag"><<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"demoService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"org.apache.dubbo.demo.DemoService"</span> <span class="hljs-attr">cluster</span>=<span class="hljs-string">"forking"</span>/></span> +</code></pre> +<p>其中服务调用方配置优先于服务提供方配置。</p> +<h4>Broadcast(广播调用)</h4> +<p>在某些场景下,可能需要对服务的所有提供者进行操作,此时可以使用广播调用策略。此策略会逐个调用所有提供者,只要任意有一个提供者出错,则认为此次调用出错。通常用于通知所有提供者更新缓存或日志等本地资源信息。</p> +<p>具体配置方法:</p> +<p>服务提供方,服务级配置</p> +<pre><code class="language-xml"><span class="hljs-tag"><<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"org.apache.dubbo.demo.DemoService"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"demoService"</span> <span class="hljs-attr">cluster</span>=<span class="hljs-string">"broadcast"</span> /></span> +</code></pre> +<p>服务调用方,服务级配置</p> +<pre><code class="language-xml"><span class="hljs-tag"><<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"demoService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"org.apache.dubbo.demo.DemoService"</span> <span class="hljs-attr">cluster</span>=<span class="hljs-string">"broadcast"</span>/></span> +</code></pre> +<p>其中服务调用方配置优先于服务提供方配置。</p> +<h4>各种策略对比</h4> +<p>下表对各种策略做一个简单对比,</p> +<table> +<thead> +<tr> +<th>策略名称</th> +<th>优点</th> +<th>缺点</th> +<th>主要应用场景</th> +</tr> +</thead> +<tbody> +<tr> +<td>Failover</td> +<td>对调用者屏蔽调用失败的信息</td> +<td>增加RT,额外资源开销,资源浪费</td> +<td>对调用rt不敏感的场景</td> +</tr> +<tr> +<td>Failfast</td> +<td>业务快速感知失败状态进行自主决策</td> +<td>产生较多报错的信息</td> +<td>非幂等性操作,需要快速感知失败的场景</td> +</tr> +<tr> +<td>Failsafe</td> +<td>即使失败了也不会影响核心流程</td> +<td>对于失败的信息不敏感,需要额外的监控</td> +<td>旁路系统,失败不影响核心流程正确性的场景</td> +</tr> +<tr> +<td>Failback</td> +<td>失败自动异步重试</td> +<td>重试任务可能堆积</td> +<td>对于实时性要求不高,且不需要返回值的一些异步操作</td> +</tr> +<tr> +<td>Forking</td> +<td>并行发起多个调用,降低失败概率</td> +<td>消耗额外的机器资源,需要确保操作幂等性</td> +<td>资源充足,且对于失败的容忍度较低,实时性要求高的场景</td> +</tr> +<tr> +<td>Broadcast</td> +<td>支持对所有的服务提供者进行操作</td> +<td>资源消耗很大</td> +<td>通知所有提供者更新缓存或日志等本地资源信息</td> +</tr> +</tbody> +</table> +<h3>自定义容错策略</h3> +<p>如果上述内置的容错策略无法满足你的需求,还可以通过扩展的方式来实现自定义容错策略。</p> +<h4>扩展接口</h4> +<p><code>com.alibaba.dubbo.rpc.cluster.Cluster</code></p> +<h4>扩展配置</h4> +<pre><code class="language-xml"><span class="hljs-tag"><<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">cluster</span>=<span class="hljs-string">"xxx"</span> /></span> +</code></pre> +<h4>扩展示例</h4> +<p>下面通过一个例子来展示如何使用自定义的容错策略。 +Maven 项目结构:</p> +<pre><code>src + |-main + |-java + |-com + |-xxx + |-XxxCluster.java (实现Cluster接口) + |-resources + |-META-INF + |-dubbo + |-org.apache.dubbo.rpc.cluster.Cluster (纯文本文件,内容为:xxx=com.xxx.XxxCluster) +</code></pre> +<p>XxxCluster.java:</p> +<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx; + +<span class="hljs-keyword">import</span> org.apache.dubbo.rpc.cluster.Cluster; +<span class="hljs-keyword">import</span> org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker; +<span class="hljs-keyword">import</span> org.apache.dubbo.rpc.cluster.Directory; +<span class="hljs-keyword">import</span> org.apache.dubbo.rpc.cluster.LoadBalance; +<span class="hljs-keyword">import</span> org.apache.dubbo.rpc.Invoker; +<span class="hljs-keyword">import</span> org.apache.dubbo.rpc.Invocation; +<span class="hljs-keyword">import</span> org.apache.dubbo.rpc.Result; +<span class="hljs-keyword">import</span> org.apache.dubbo.rpc.RpcException; + +<span class="hljs-keyword">import</span> java.util.List; + +<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxCluster</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Cluster</span> </span>{ + + <span class="hljs-meta">@Override</span> + <span class="hljs-keyword">public</span> <T> <span class="hljs-function">Invoker<T> <span class="hljs-title">join</span><span class="hljs-params">(Directory<T> directory)</span> <span class="hljs-keyword">throws</span> RpcException </span>{ + <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> AbstractClusterInvoker<T>() { + <span class="hljs-meta">@Override</span> + <span class="hljs-function"><span class="hljs-keyword">protected</span> Result <span class="hljs-title">doInvoke</span><span class="hljs-params">(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance)</span> <span class="hljs-keyword">throws</span> RpcException </span>{ + <span class="hljs-comment">// your custimzed fault tolarence strategy goes here</span> + } + }; + } + +} +</code></pre> +<p><code>META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.Cluster</code>文件的内容为</p> +<pre><code>xxx=com.xxx.XxxCluster +</code></pre> +</section><footer class="footer-container"><div class="footer-body"><img src="/img/dubbo_gray.png"/><img class="apache" src="/img/apache_logo.png"/><div class="cols-container"><div class="col col-12"><h3>Disclaimer</h3><p>Apache Dubbo is an effort undergoing incubation at The Apache Software Foundation (ASF), sponsored by the Incubator. Incubation is required of all newly accepted projects until a further review indicates that the infrastructure, communications, and decision making proce [...] + <script src="https://f.alicdn.com/react/15.4.1/react-with-addons.min.js"></script> + <script src="https://f.alicdn.com/react/15.4.1/react-dom.min.js"></script> + <script> + window.rootPath = ''; + </script> + <script src="/build/blogDetail.js"></script> + <!-- Global site tag (gtag.js) - Google Analytics --> + <script async src="https://www.googletagmanager.com/gtag/js?id=UA-112489517-1"></script> + <script> + window.dataLayer = window.dataLayer || []; + function gtag(){dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'UA-112489517-1'); + </script> +</body> +</html> \ No newline at end of file diff --git a/zh-cn/blog/dubbo-cluster-error-handling.json b/zh-cn/blog/dubbo-cluster-error-handling.json new file mode 100644 index 0000000..b158a33 --- /dev/null +++ b/zh-cn/blog/dubbo-cluster-error-handling.json @@ -0,0 +1,10 @@ +{ + "filename": "dubbo-cluster-error-handling.md", + "__html": "<h3>Design For failure</h3>\n<p>在分布式系统中,集群某个某些节点出现问题是大概率事件,因此在设计分布式RPC框架的过程中,必须要把失败作为设计的一等公民来对待。一次调用失败之后,应该如何选择对失败的选择策略,这是一个见仁见智的问题,每种策略可能都有自己独特的应用场景。因此,作为框架来说,应当针对不同场景提供多种策略,供用户进行选择。</p>\n<p>在Dubbo设计中,通过Cluster这个接口的抽象,把一组可供调用的Provider信息组合成为一个统一的<code>Invoker</code>供调用方进行调用。经过路由规则过滤,负载均衡选址后,选中一个具体地址进行调用,如果调用失败,则会按照集群配置的容错策略进行容错处理。</p>\n<p>Dubbo默认内置了若干容错策略,如果不能满足用户需求,则可以通过自定义容错策略进行配置。</p>\n<h3>内置容错 策略</h3>\n<p>Dubbo主要内置了如下几种策略:</p>\n<ul>\n<li>Failover(失败自动切换)</li>\n<li>Failsaf [...] + "link": "/zh-cn/blog/dubbo-cluster-error-handling.html", + "meta": { + "title": "Dubbo集群容错", + "keywords": "Dubbo, RPC, cluster, Error-handling", + "description": "在分布式系统中,集群某个某些节点出现问题是大概率事件,因此在设计分布式RPC框架的过程中,必须要把失败作为设计的一等公民来对待。一次调用失败之后,应该如何选择对失败的选择策略,这是一个见仁见智的问题,每种策略可能都有自己独特的应用场景。因此,作为框架来说,应当针对不同场景提供多种策略,供用户进行选择。本文介绍了Dubbo框架提供的多种错误处理策略,并通过实例说明如何进行配置。" + } +} \ No newline at end of file diff --git a/zh-cn/blog/dubbo-meetup-beijing-may-12th-2018.html b/zh-cn/blog/dubbo-meetup-beijing-may-12th-2018.html new file mode 100644 index 0000000..52bb9f4 --- /dev/null +++ b/zh-cn/blog/dubbo-meetup-beijing-may-12th-2018.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<html lang="en"> + +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> + <meta name="keywords" content="Dubbo, Beijing, meetup" /> + <meta name="description" content="首届Dubbo开发者沙龙在北京成功举办" /> + <!-- 网页标签标题 --> + <title>首届Dubbo开发者沙龙在北京成功举办</title> + <link rel="shortcut icon" href="/img/dubbo.ico"/> + <link rel="stylesheet" href="/build/blogDetail.css" /> +</head> +<body> + <div id="root"><div class="blog-detail-page" data-reactroot=""><header class="header-container header-container-normal"><div class="header-body"><a href="/zh-cn/index.html"><img class="logo" src="/img/dubbo_colorful.png"/></a><div class="search search-normal"><span class="icon-search"></span></div><span class="language-switch language-switch-normal">En</span><div class="header-menu"><img class="header-menu-toggle" src="/img/menu_gray.png"/><ul><li class="menu-item menu-item-normal"><a h [...] +<p>首届Dubbo开发者沙龙在北京成功举办, 超过400位开发者参加。这是一个很好的开始!</p> +<p>分享嘉宾及主体如下:</p> +<ul> +<li>罗军: Dubbo 的现状现状与未来规划 <a href="https://github.com/dubbo/awesome-dubbo/raw/master/slides/meetup/201805%40Beijing/dubbo-present-and-future.pdf">PDF</a></li> +<li>刘军: 第四届阿里中间件性能挑战赛 <a href="https://github.com/dubbo/awesome-dubbo/raw/master/slides/meetup/201805%40Beijing/introduction-to-4th-aliware-performance-challenge.pdf">PDF</a></li> +<li>陈志轩: 通过 Dubbo 和 Spring-boot 快速构建微服务 <a href="https://github.com/dubbo/awesome-dubbo/raw/master/slides/meetup/201805%40Beijing/quickly-building-microservice-with-dubbo-and-springboot.pdf">PDF</a></li> +<li>王欣: Dubbo 和微店的服务化实践历程分享 <a href="https://github.com/dubbo/awesome-dubbo/raw/master/slides/meetup/201805%40Beijing/dubbo-and-weidian's-practice-on-microservice-architecture.pdf">PDF</a></li> +</ul> +</section><footer class="footer-container"><div class="footer-body"><img src="/img/dubbo_gray.png"/><img class="apache" src="/img/apache_logo.png"/><div class="cols-container"><div class="col col-12"><h3>Disclaimer</h3><p>Apache Dubbo is an effort undergoing incubation at The Apache Software Foundation (ASF), sponsored by the Incubator. Incubation is required of all newly accepted projects until a further review indicates that the infrastructure, communications, and decision making proce [...] + <script src="https://f.alicdn.com/react/15.4.1/react-with-addons.min.js"></script> + <script src="https://f.alicdn.com/react/15.4.1/react-dom.min.js"></script> + <script> + window.rootPath = ''; + </script> + <script src="/build/blogDetail.js"></script> + <!-- Global site tag (gtag.js) - Google Analytics --> + <script async src="https://www.googletagmanager.com/gtag/js?id=UA-112489517-1"></script> + <script> + window.dataLayer = window.dataLayer || []; + function gtag(){dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'UA-112489517-1'); + </script> +</body> +</html> \ No newline at end of file diff --git a/zh-cn/blog/dubbo-meetup-beijing-may-12th-2018.json b/zh-cn/blog/dubbo-meetup-beijing-may-12th-2018.json new file mode 100644 index 0000000..2dcc43f --- /dev/null +++ b/zh-cn/blog/dubbo-meetup-beijing-may-12th-2018.json @@ -0,0 +1,10 @@ +{ + "filename": "dubbo-meetup-beijing-may-12th-2018.md", + "__html": "<h2>首届Dubbo开发者沙龙在北京成功举办</h2>\n<p>首届Dubbo开发者沙龙在北京成功举办, 超过400位开发者参加。这是一个很好的开始!</p>\n<p>分享嘉宾及主体如下:</p>\n<ul>\n<li>罗军: Dubbo 的现状现状与未来规划 <a href=\"https://github.com/dubbo/awesome-dubbo/raw/master/slides/meetup/201805%40Beijing/dubbo-present-and-future.pdf\">PDF</a></li>\n<li>刘军: 第四届阿里中间件性能挑战赛 <a href=\"https://github.com/dubbo/awesome-dubbo/raw/master/slides/meetup/201805%40Beijing/introduction-to-4th-aliware-performance-challenge.pdf\">PDF</a></li>\n<li>陈志轩: 通过 Dubbo 和 Spring-b [...] + "link": "/zh-cn/blog/dubbo-meetup-beijing-may-12th-2018.html", + "meta": { + "title": "首届Dubbo开发者沙龙在北京成功举办", + "keywords": "Dubbo, Beijing, meetup", + "description": "首届Dubbo开发者沙龙在北京成功举办" + } +} \ No newline at end of file diff --git a/zh-cn/blog/first-dubbo-filter.html b/zh-cn/blog/first-dubbo-filter.html new file mode 100644 index 0000000..f140f64 --- /dev/null +++ b/zh-cn/blog/first-dubbo-filter.html @@ -0,0 +1,183 @@ +<!DOCTYPE html> +<html lang="en"> + +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> + <meta name="keywords" content="first-dubbo-filter" /> + <meta name="description" content="first-dubbo-filter" /> + <!-- 网页标签标题 --> + <title>first-dubbo-filter</title> + <link rel="shortcut icon" href="/img/dubbo.ico"/> + <link rel="stylesheet" href="/build/blogDetail.css" /> +</head> +<body> + <div id="root"><div class="blog-detail-page" data-reactroot=""><header class="header-container header-container-normal"><div class="header-body"><a href="/zh-cn/index.html"><img class="logo" src="/img/dubbo_colorful.png"/></a><div class="search search-normal"><span class="icon-search"></span></div><span class="language-switch language-switch-normal">En</span><div class="header-menu"><img class="header-menu-toggle" src="/img/menu_gray.png"/><ul><li class="menu-item menu-item-normal"><a h [...] +<h3>概述</h3> +<p>在Dubbo的整体设计中,Filter是一个很重要的概念,包括Dubbo本身的大多数功能,都是基于此扩展点实现的,在每次的调用过程中,Filter的拦截都会被执行。</p> +<h4>Dubbo Filter的加载机制</h4> +<p>Dubbo中已经实现的Filter大概有二十几个,它们的入口都是ProtocolFilterWrapper,ProtocolFilterWrapper对Protocol做了Wrapper,会在加载扩展的时候被加载进来,下面我们来看下然后我们看一下这个Filter链是如何构造的。</p> +<pre><code class="language-java"><span class="hljs-comment">//ProtocolFilterWrapper.java</span> +<span class="hljs-keyword">public</span> <T> <span class="hljs-function">Invoker<T> <span class="hljs-title">refer</span><span class="hljs-params">(Class<T> type, URL url)</span> <span class="hljs-keyword">throws</span> RpcException </span>{ + <span class="hljs-keyword">if</span> (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) { + <span class="hljs-keyword">return</span> protocol.refer(type, url); + } + <span class="hljs-keyword">return</span> buildInvokerChain(protocol.refer(type, url), Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER); + } + + <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <T> <span class="hljs-function">Invoker<T> <span class="hljs-title">buildInvokerChain</span><span class="hljs-params">(<span class="hljs-keyword">final</span> Invoker<T> invoker, String key, String group)</span> </span>{ + Invoker<T> last = invoker; + List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group); + <span class="hljs-keyword">if</span> (filters.size() > <span class="hljs-number">0</span>) { + <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = filters.size() - <span class="hljs-number">1</span>; i >= <span class="hljs-number">0</span>; i --) { + <span class="hljs-keyword">final</span> Filter filter = filters.get(i); + <span class="hljs-keyword">final</span> Invoker<T> next = last; + last = <span class="hljs-keyword">new</span> Invoker<T>() { + + <span class="hljs-function"><span class="hljs-keyword">public</span> Class<T> <span class="hljs-title">getInterface</span><span class="hljs-params">()</span> </span>{ + <span class="hljs-keyword">return</span> invoker.getInterface(); + } + + <span class="hljs-function"><span class="hljs-keyword">public</span> URL <span class="hljs-title">getUrl</span><span class="hljs-params">()</span> </span>{ + <span class="hljs-keyword">return</span> invoker.getUrl(); + } + + <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">isAvailable</span><span class="hljs-params">()</span> </span>{ + <span class="hljs-keyword">return</span> invoker.isAvailable(); + } + + <span class="hljs-function"><span class="hljs-keyword">public</span> Result <span class="hljs-title">invoke</span><span class="hljs-params">(Invocation invocation)</span> <span class="hljs-keyword">throws</span> RpcException </span>{ + <span class="hljs-keyword">return</span> filter.invoke(next, invocation); + } + + <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>{ + invoker.destroy(); + } + + <span class="hljs-meta">@Override</span> + <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">toString</span><span class="hljs-params">()</span> </span>{ + <span class="hljs-keyword">return</span> invoker.toString(); + } + }; + } + } + <span class="hljs-keyword">return</span> last; + } + +</code></pre> +<h4>Dubbo Filter的激活机制</h4> +<p>通过上述代码我们可以看到,在<code>buildInvokerChain</code>中,先获取所有已经激活的调用链,这里的调用链是已经排好序的。再通过Invoker来构造出一个Filter的调用链,最后构建出的调用链大致可以表示为:Filter1->Filter2->Filter3->......->Invoker,下面我们来看一下,第一步中获取已经激活的调用链的详细流程:</p> +<pre><code class="language-java"> <span class="hljs-function"><span class="hljs-keyword">public</span> List<T> <span class="hljs-title">getActivateExtension</span><span class="hljs-params">(URL url, String key, String group)</span> </span>{ + String value = url.getParameter(key); + <span class="hljs-keyword">return</span> getActivateExtension(url, value == <span class="hljs-keyword">null</span> || value.length() == <span class="hljs-number">0</span> ? <span class="hljs-keyword">null</span> : Constants.COMMA_SPLIT_PATTERN.split(value), group); + } + + <span class="hljs-function"><span class="hljs-keyword">public</span> List<T> <span class="hljs-title">getActivateExtension</span><span class="hljs-params">(URL url, String[] values, String group)</span> </span>{ + List<T> exts = <span class="hljs-keyword">new</span> ArrayList<T>(); + + List<String> names = values == <span class="hljs-keyword">null</span> ? <span class="hljs-keyword">new</span> ArrayList<String>(<span class="hljs-number">0</span>) : Arrays.asList(values); + + <span class="hljs-keyword">if</span> (! names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) { + getExtensionClasses(); + <span class="hljs-keyword">for</span> (Map.Entry<String, Activate> entry : cachedActivates.entrySet()) { + String name = entry.getKey(); + Activate activate = entry.getValue(); + <span class="hljs-keyword">if</span> (isMatchGroup(group, activate.group())) { + T ext = getExtension(name); + <span class="hljs-keyword">if</span> (! names.contains(name) && ! names.contains(Constants.REMOVE_VALUE_PREFIX + name) + && isActive(activate, url)) { + exts.add(ext); + } + } + } + Collections.sort(exts, ActivateComparator.COMPARATOR); + } + List<T> usrs = <span class="hljs-keyword">new</span> ArrayList<T>(); + <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < names.size(); i ++) { + String name = names.get(i); + <span class="hljs-keyword">if</span> (! name.startsWith(Constants.REMOVE_VALUE_PREFIX) + && ! names.contains(Constants.REMOVE_VALUE_PREFIX + name)) { + <span class="hljs-keyword">if</span> (Constants.DEFAULT_KEY.equals(name)) { + <span class="hljs-keyword">if</span> (usrs.size() > <span class="hljs-number">0</span>) { + exts.addAll(<span class="hljs-number">0</span>, usrs); + usrs.clear(); + } + } <span class="hljs-keyword">else</span> { + T ext = getExtension(name); + usrs.add(ext); + } + } + } + <span class="hljs-keyword">if</span> (usrs.size() > <span class="hljs-number">0</span>) { + exts.addAll(usrs); + } + <span class="hljs-keyword">return</span> exts; + } +</code></pre> +<p>通过以上代码可以看到,用户自己配置的Filter中,有些是默认激活,有些是需要通过配置文件来激活。而所有Filter的加载顺序,也是先处理Dubbo的默认Filter,再来处理用户自己定义并且配置的Filter。通过"-"配置,可以替换掉Dubbo的原生Filter,通过这样的设计,可以灵活地替换或者修改Filter的加载顺序。</p> +<h4>Dubbo原生的Filter</h4> +<p>Dubbo原生的Filter很多,RpcContext,accesslog等功能都可以通过Dubbo来实现,下面我们来介绍一下Consumer端用于上下文传递的ConsumerContextFilter:</p> +<pre><code class="language-java"><span class="hljs-function"><span class="hljs-keyword">public</span> Result <span class="hljs-title">invoke</span><span class="hljs-params">(Invoker<?> invoker, Invocation invocation)</span> <span class="hljs-keyword">throws</span> RpcException </span>{ + RpcContext.getContext() + .setInvoker(invoker) + .setInvocation(invocation) + .setLocalAddress(NetUtils.getLocalHost(), <span class="hljs-number">0</span>) + .setRemoteAddress(invoker.getUrl().getHost(), + invoker.getUrl().getPort()); + <span class="hljs-keyword">if</span> (invocation <span class="hljs-keyword">instanceof</span> RpcInvocation) { + ((RpcInvocation)invocation).setInvoker(invoker); + } + <span class="hljs-keyword">try</span> { + <span class="hljs-keyword">return</span> invoker.invoke(invocation); + } <span class="hljs-keyword">finally</span> { + RpcContext.getContext().clearAttachments(); + } + } +</code></pre> +<p>此Filter记录了调用过程中的状态信息,并且通过invocation对象将客户端设置的attachments参数传递到服务端。并且在调用完成后清除这些参数,这就是为什么请求状态信息可以按次记录并且进行传递。</p> +<h4>实现一个Dubbo Filter</h4> +<p>得益于Dubbo灵活的设计和良好的可扩展性,我们可以通过实现自己的Dubbo Filter来完成调用链路中的逻辑嵌入,比如,耗时统计,monitor信息统计等,下面我们来实现一个简单的Filter:</p> +<p>Maven 项目结构:</p> +<pre><code>src + |-main + |-java + |-com + |-xxx + |-XxxFilter.java (实现Filter接口) + |-resources + |-META-INF + |-dubbo + |-com.alibaba.dubbo.rpc.Filter (纯文本文件,内容为:xxx=com.xxx.XxxFilter) +</code></pre> +<p>XxxFilter.java:</p> +<pre><code>public class XxxFilter implements Filter { + public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { + // before filter ... + Result result = invoker.invoke(invocation); + // after filter ... + return result; + } +} +</code></pre> +<p>META-INF/dubbo/com.alibaba.dubbo.rpc.Filter:</p> +<pre><code>xxx=com.xxx.XxxFilter +</code></pre> +<p>在before和after中,可以实现自己的业务逻辑来赋予改filter一定的功能。编写和配置完成后,该filter就会被Dubbo框架激活并且在调用链中执行。</p> +</section><footer class="footer-container"><div class="footer-body"><img src="/img/dubbo_gray.png"/><img class="apache" src="/img/apache_logo.png"/><div class="cols-container"><div class="col col-12"><h3>Disclaimer</h3><p>Apache Dubbo is an effort undergoing incubation at The Apache Software Foundation (ASF), sponsored by the Incubator. Incubation is required of all newly accepted projects until a further review indicates that the infrastructure, communications, and decision making proce [...] + <script src="https://f.alicdn.com/react/15.4.1/react-with-addons.min.js"></script> + <script src="https://f.alicdn.com/react/15.4.1/react-dom.min.js"></script> + <script> + window.rootPath = ''; + </script> + <script src="/build/blogDetail.js"></script> + <!-- Global site tag (gtag.js) - Google Analytics --> + <script async src="https://www.googletagmanager.com/gtag/js?id=UA-112489517-1"></script> + <script> + window.dataLayer = window.dataLayer || []; + function gtag(){dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'UA-112489517-1'); + </script> +</body> +</html> \ No newline at end of file diff --git a/zh-cn/blog/first-dubbo-filter.json b/zh-cn/blog/first-dubbo-filter.json new file mode 100644 index 0000000..8cce968 --- /dev/null +++ b/zh-cn/blog/first-dubbo-filter.json @@ -0,0 +1,6 @@ +{ + "filename": "first-dubbo-filter.md", + "__html": "<h1>第一个Dubbo Filter</h1>\n<h3>概述</h3>\n<p>在Dubbo的整体设计中,Filter是一个很重要的概念,包括Dubbo本身的大多数功能,都是基于此扩展点实现的,在每次的调用过程中,Filter的拦截都会被执行。</p>\n<h4>Dubbo Filter的加载机制</h4>\n<p>Dubbo中已经实现的Filter大概有二十几个,它们的入口都是ProtocolFilterWrapper,ProtocolFilterWrapper对Protocol做了Wrapper,会在加载扩展的时候被加载进来,下面我们来看下然后我们看一下这个Filter链是如何构造的。</p>\n<pre><code class=\"language-java\"><span class=\"hljs-comment\">//ProtocolFilterWrapper.java</span>\n<span class=\"hljs-keyword\">public</span> <T> <span class=\"hljs- [...] + "link": "/zh-cn/blog/first-dubbo-filter.html", + "meta": {} +} \ No newline at end of file diff --git a/zh-cn/blog/index.html b/zh-cn/blog/index.html index 491155d..da17259 100644 --- a/zh-cn/blog/index.html +++ b/zh-cn/blog/index.html @@ -12,7 +12,7 @@ <link rel="stylesheet" href="/build/blog.css" /> </head> <body> - <div id="root"><div class="blog-list-page" data-reactroot=""><header class="header-container header-container-normal"><div class="header-body"><a href="/zh-cn/index.html"><img class="logo" src="/img/dubbo_colorful.png"/></a><div class="search search-normal"><span class="icon-search"></span></div><span class="language-switch language-switch-normal">En</span><div class="header-menu"><img class="header-menu-toggle" src="/img/menu_gray.png"/><ul><li class="menu-item menu-item-normal"><a hre [...] + <div id="root"><div class="blog-list-page" data-reactroot=""><header class="header-container header-container-normal"><div class="header-body"><a href="/zh-cn/index.html"><img class="logo" src="/img/dubbo_colorful.png"/></a><div class="search search-normal"><span class="icon-search"></span></div><span class="language-switch language-switch-normal">En</span><div class="header-menu"><img class="header-menu-toggle" src="/img/menu_gray.png"/><ul><li class="menu-item menu-item-normal"><a hre [...] <script src="https://f.alicdn.com/react/15.4.1/react-with-addons.min.js"></script> <script src="https://f.alicdn.com/react/15.4.1/react-dom.min.js"></script> <script> diff --git a/zh-cn/blog/qcon-beijing-2018.html b/zh-cn/blog/qcon-beijing-2018.html new file mode 100644 index 0000000..e8b88a9 --- /dev/null +++ b/zh-cn/blog/qcon-beijing-2018.html @@ -0,0 +1,34 @@ +<!DOCTYPE html> +<html lang="en"> + +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> + <meta name="keywords" content="qcon-beijing-2018" /> + <meta name="description" content="qcon-beijing-2018" /> + <!-- 网页标签标题 --> + <title>qcon-beijing-2018</title> + <link rel="shortcut icon" href="/img/dubbo.ico"/> + <link rel="stylesheet" href="/build/blogDetail.css" /> +</head> +<body> + <div id="root"><div class="blog-detail-page" data-reactroot=""><header class="header-container header-container-normal"><div class="header-body"><a href="/zh-cn/index.html"><img class="logo" src="/img/dubbo_colorful.png"/></a><div class="search search-normal"><span class="icon-search"></span></div><span class="language-switch language-switch-normal">En</span><div class="header-menu"><img class="header-menu-toggle" src="/img/menu_gray.png"/><ul><li class="menu-item menu-item-normal"><a h [...] +<p>罗毅在Qcon Beijing 2018上进行了Dubbo开源现状及未来规划的主题演讲。详细内容请查看<a href="https://github.com/dubbo/awesome-dubbo/raw/master/slides/qcon2018/dubbo-present-and-future.pdf">幻灯片</a>。</p> +</section><footer class="footer-container"><div class="footer-body"><img src="/img/dubbo_gray.png"/><img class="apache" src="/img/apache_logo.png"/><div class="cols-container"><div class="col col-12"><h3>Disclaimer</h3><p>Apache Dubbo is an effort undergoing incubation at The Apache Software Foundation (ASF), sponsored by the Incubator. Incubation is required of all newly accepted projects until a further review indicates that the infrastructure, communications, and decision making proce [...] + <script src="https://f.alicdn.com/react/15.4.1/react-with-addons.min.js"></script> + <script src="https://f.alicdn.com/react/15.4.1/react-dom.min.js"></script> + <script> + window.rootPath = ''; + </script> + <script src="/build/blogDetail.js"></script> + <!-- Global site tag (gtag.js) - Google Analytics --> + <script async src="https://www.googletagmanager.com/gtag/js?id=UA-112489517-1"></script> + <script> + window.dataLayer = window.dataLayer || []; + function gtag(){dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'UA-112489517-1'); + </script> +</body> +</html> \ No newline at end of file diff --git a/zh-cn/blog/qcon-beijing-2018.json b/zh-cn/blog/qcon-beijing-2018.json new file mode 100644 index 0000000..000d43a --- /dev/null +++ b/zh-cn/blog/qcon-beijing-2018.json @@ -0,0 +1,6 @@ +{ + "filename": "qcon-beijing-2018.md", + "__html": "<h2>Dubbo路线图在QCon Beijing 2018上公布</h2>\n<p>罗毅在Qcon Beijing 2018上进行了Dubbo开源现状及未来规划的主题演讲。详细内容请查看<a href=\"https://github.com/dubbo/awesome-dubbo/raw/master/slides/qcon2018/dubbo-present-and-future.pdf\">幻灯片</a>。</p>\n", + "link": "/zh-cn/blog/qcon-beijing-2018.html", + "meta": {} +} \ No newline at end of file diff --git a/zh-cn/blog/service-and-version.html b/zh-cn/blog/service-and-version.html new file mode 100644 index 0000000..bcff661 --- /dev/null +++ b/zh-cn/blog/service-and-version.html @@ -0,0 +1,213 @@ +<!DOCTYPE html> +<html lang="en"> + +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> + <meta name="keywords" content="service-and-version" /> + <meta name="description" content="service-and-version" /> + <!-- 网页标签标题 --> + <title>service-and-version</title> + <link rel="shortcut icon" href="/img/dubbo.ico"/> + <link rel="stylesheet" href="/build/blogDetail.css" /> +</head> +<body> + <div id="root"><div class="blog-detail-page" data-reactroot=""><header class="header-container header-container-normal"><div class="header-body"><a href="/zh-cn/index.html"><img class="logo" src="/img/dubbo_colorful.png"/></a><div class="search search-normal"><span class="icon-search"></span></div><span class="language-switch language-switch-normal">En</span><div class="header-menu"><img class="header-menu-toggle" src="/img/menu_gray.png"/><ul><li class="menu-item menu-item-normal"><a h [...] +<p>我们在调用Dubbo服务的时候,一般只需要将Consumer端的<code>dubbo:reference</code>指定成服务端中<code>dubbo:service</code>暴露的服务,就可以找到服务端,完成调用,也就是说,Dubbo只需要服务接口信息就可以找到服务提供者。 +其实除了服务提供者以外,Dubbo也有服务分组和版本的概念,在客户端去寻找“匹配”的服务端的时候,需要服务接口,版本号,组别这三个信息都匹配,才算是一个有效的服务端:</p> +<pre><code class="language-java"> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">isMatch</span><span class="hljs-params">(URL consumerUrl, URL providerUrl)</span> </span>{ + String consumerInterface = consumerUrl.getServiceInterface(); + String providerInterface = providerUrl.getServiceInterface(); + <span class="hljs-keyword">if</span> (!(Constants.ANY_VALUE.equals(consumerInterface) || StringUtils.isEquals(consumerInterface, providerInterface))) + <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>; + + <span class="hljs-keyword">if</span> (!isMatchCategory(providerUrl.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY), + consumerUrl.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY))) { + <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>; + } + <span class="hljs-keyword">if</span> (!providerUrl.getParameter(Constants.ENABLED_KEY, <span class="hljs-keyword">true</span>) + && !Constants.ANY_VALUE.equals(consumerUrl.getParameter(Constants.ENABLED_KEY))) { + <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>; + } + + String consumerGroup = consumerUrl.getParameter(Constants.GROUP_KEY); + String consumerVersion = consumerUrl.getParameter(Constants.VERSION_KEY); + String consumerClassifier = consumerUrl.getParameter(Constants.CLASSIFIER_KEY, Constants.ANY_VALUE); + + String providerGroup = providerUrl.getParameter(Constants.GROUP_KEY); + String providerVersion = providerUrl.getParameter(Constants.VERSION_KEY); + String providerClassifier = providerUrl.getParameter(Constants.CLASSIFIER_KEY, Constants.ANY_VALUE); + <span class="hljs-keyword">return</span> (Constants.ANY_VALUE.equals(consumerGroup) || StringUtils.isEquals(consumerGroup, providerGroup) || StringUtils.isContains(consumerGroup, providerGroup)) + && (Constants.ANY_VALUE.equals(consumerVersion) || StringUtils.isEquals(consumerVersion, providerVersion)) + && (consumerClassifier == <span class="hljs-keyword">null</span> || Constants.ANY_VALUE.equals(consumerClassifier) || StringUtils.isEquals(consumerClassifier, providerClassifier)); + } +</code></pre> +<p>如果没有配置组别和版本号,默认值为空。服务端和消费端都没有配,只有服务接口,其他两个信息都为空,也是可以“找到”对方的,那服务名和版本号可以如何使用呢?下面我们来看一下具体的场景:</p> +<h3>服务分组</h3> +<p>当一个接口有多种实现时,可以用 group 区分。</p> +<p>服务</p> +<pre><code><dubbo:service group="feedback" interface="com.xxx.IndexService" /> +<dubbo:service group="member" interface="com.xxx.IndexService" /> +</code></pre> +<p>引用</p> +<pre><code><dubbo:reference id="feedbackIndexService" group="feedback" interface="com.xxx.IndexService" /> +<dubbo:reference id="memberIndexService" group="member" interface="com.xxx.IndexService" /> +</code></pre> +<p>任意组</p> +<pre><code><dubbo:reference id="barService" interface="com.foo.BarService" group="*" /> +</code></pre> +<h3>多版本</h3> +<p>当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用。</p> +<p>可以按照以下的步骤进行版本迁移:</p> +<ol> +<li>在低压力时间段,先升级一半提供者为新版本</li> +<li>再将所有消费者升级为新版本</li> +<li>然后将剩下的一半提供者升级为新版本</li> +</ol> +<p>老版本服务提供者配置:</p> +<pre><code><dubbo:service interface="com.foo.BarService" version="1.0.0" /> +</code></pre> +<p>新版本服务提供者配置:</p> +<pre><code><dubbo:service interface="com.foo.BarService" version="2.0.0" /> +</code></pre> +<p>老版本服务消费者配置:</p> +<pre><code><dubbo:reference id="barService" interface="com.foo.BarService" version="1.0.0" /> +</code></pre> +<p>新版本服务消费者配置:</p> +<pre><code><dubbo:reference id="barService" interface="com.foo.BarService" version="2.0.0" /> +</code></pre> +<p>如果不需要区分版本,可以按照以下的方式配置:</p> +<pre><code><dubbo:reference id="barService" interface="com.foo.BarService" version="*" /> +</code></pre> +<h3>分组聚合</h3> +<p>按组合并返回结果,比如菜单服务,接口一样,但有多种实现,用group区分,现在消费方需从每种group中调用一次返回结果,合并结果返回,这样就可以实现聚合菜单项。</p> +<h4>配置</h4> +<p>搜索所有分组</p> +<pre><code><dubbo:reference interface="com.xxx.MenuService" group="*" merger="true" /> +</code></pre> +<p>合并指定分组</p> +<pre><code><dubbo:reference interface="com.xxx.MenuService" group="aaa,bbb" merger="true" /> +</code></pre> +<p>指定方法合并结果,其它未指定的方法,将只调用一个 Group</p> +<pre><code><dubbo:reference interface="com.xxx.MenuService" group="*"> +<dubbo:method name="getMenuItems" merger="true" /> +</dubbo:service> +</code></pre> +<p>某个方法不合并结果,其它都合并结果</p> +<pre><code><dubbo:reference interface="com.xxx.MenuService" group="*" merger="true"> +<dubbo:method name="getMenuItems" merger="false" /> +</dubbo:service> +</code></pre> +<p>指定合并策略,缺省根据返回值类型自动匹配,如果同一类型有两个合并器时,需指定合并器的名称</p> +<pre><code><dubbo:reference interface="com.xxx.MenuService" group="*"> +<dubbo:method name="getMenuItems" merger="mymerge" /> +</dubbo:service> +</code></pre> +<p>指定合并方法,将调用返回结果的指定方法进行合并,合并方法的参数类型必须是返回结果类型本身</p> +<pre><code><dubbo:reference interface="com.xxx.MenuService" group="*"> +<dubbo:method name="getMenuItems" merger=".addAll" /> +</dubbo:service> +</code></pre> +<h4>实现原理</h4> +<p>如果配置了merge,Dubbo会分别调用多个组别的服务提供者,然后把结果聚合,返回给消费端,具体的实现在<code>MergeableClusterInvoker.java</code>里:</p> +<pre><code class="language-java"> <span class="hljs-function"><span class="hljs-keyword">public</span> Result <span class="hljs-title">invoke</span><span class="hljs-params">(<span class="hljs-keyword">final</span> Invocation invocation)</span> <span class="hljs-keyword">throws</span> RpcException </span>{ + List<Invoker<T>> invokers = directory.list(invocation); + + String merger = getUrl().getMethodParameter(invocation.getMethodName(), Constants.MERGER_KEY); + <span class="hljs-keyword">if</span> (ConfigUtils.isEmpty(merger)) { <span class="hljs-comment">// If a method doesn't have a merger, only invoke one Group</span> + <span class="hljs-keyword">for</span> (<span class="hljs-keyword">final</span> Invoker<T> invoker : invokers) { + <span class="hljs-keyword">if</span> (invoker.isAvailable()) { + <span class="hljs-keyword">return</span> invoker.invoke(invocation); + } + } + <span class="hljs-keyword">return</span> invokers.iterator().next().invoke(invocation); + } + + Class<?> returnType; + <span class="hljs-keyword">try</span> { + returnType = getInterface().getMethod( + invocation.getMethodName(), invocation.getParameterTypes()).getReturnType(); + } <span class="hljs-keyword">catch</span> (NoSuchMethodException e) { + returnType = <span class="hljs-keyword">null</span>; + } + + Map<String, Future<Result>> results = <span class="hljs-keyword">new</span> HashMap<String, Future<Result>>(); + <span class="hljs-keyword">for</span> (<span class="hljs-keyword">final</span> Invoker<T> invoker : invokers) { + Future<Result> future = executor.submit(<span class="hljs-keyword">new</span> Callable<Result>() { + <span class="hljs-meta">@Override</span> + <span class="hljs-function"><span class="hljs-keyword">public</span> Result <span class="hljs-title">call</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception </span>{ + <span class="hljs-keyword">return</span> invoker.invoke(<span class="hljs-keyword">new</span> RpcInvocation(invocation, invoker)); + } + }); + results.put(invoker.getUrl().getServiceKey(), future); + } + + Object result = <span class="hljs-keyword">null</span>; + + List<Result> resultList = <span class="hljs-keyword">new</span> ArrayList<Result>(results.size()); + + <span class="hljs-keyword">int</span> timeout = getUrl().getMethodParameter(invocation.getMethodName(), Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT); + <span class="hljs-keyword">for</span> (Map.Entry<String, Future<Result>> entry : results.entrySet()) { + Future<Result> future = entry.getValue(); + <span class="hljs-keyword">try</span> { + Result r = future.get(timeout, TimeUnit.MILLISECONDS); + <span class="hljs-keyword">if</span> (r.hasException()) { + log.error(<span class="hljs-string">"Invoke "</span> + getGroupDescFromServiceKey(entry.getKey()) + + <span class="hljs-string">" failed: "</span> + r.getException().getMessage(), + r.getException()); + } <span class="hljs-keyword">else</span> { + resultList.add(r); + } + } <span class="hljs-keyword">catch</span> (Exception e) { + <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RpcException(<span class="hljs-string">"Failed to invoke service "</span> + entry.getKey() + <span class="hljs-string">": "</span> + e.getMessage(), e); + } + } + + <span class="hljs-keyword">if</span> (resultList.isEmpty()) { + <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> RpcResult((Object) <span class="hljs-keyword">null</span>); + } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (resultList.size() == <span class="hljs-number">1</span>) { + <span class="hljs-keyword">return</span> resultList.iterator().next(); + } + + <span class="hljs-keyword">if</span> (returnType == <span class="hljs-keyword">void</span>.class) { + <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> RpcResult((Object) <span class="hljs-keyword">null</span>); + } +</code></pre> +<p>如果配置了merger,会依次调用,结果都放在results里面,其中value都是future类型,等调用完成之后,再遍历results,通过future.get拿到真正的结果,到此为止,所有调用的结果都放在resultList里面了,接下来要做的是把结果进行聚合:</p> +<pre><code class="language-java"> Merger resultMerger; + <span class="hljs-keyword">if</span> (ConfigUtils.isDefault(merger)) { + resultMerger = MergerFactory.getMerger(returnType); + } <span class="hljs-keyword">else</span> { + resultMerger = ExtensionLoader.getExtensionLoader(Merger.class).getExtension(merger); + } + <span class="hljs-keyword">if</span> (resultMerger != <span class="hljs-keyword">null</span>) { + List<Object> rets = <span class="hljs-keyword">new</span> ArrayList<Object>(resultList.size()); + <span class="hljs-keyword">for</span> (Result r : resultList) { + rets.add(r.getValue()); + } + result = resultMerger.merge( + rets.toArray((Object[]) Array.newInstance(returnType, <span class="hljs-number">0</span>))); + } <span class="hljs-keyword">else</span> { + <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RpcException(<span class="hljs-string">"There is no merger to merge result."</span>) + } + <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> RpcResult(result); +</code></pre> +<p>这里会根据返回值的类型,获取到对应的resultMerger,除了Dubbo默认实现的类型外,也可以自己指定merger类型并且添加相应的扩展,通过实现<code>merge</code>方法类进行结果聚合。</p> +</section><footer class="footer-container"><div class="footer-body"><img src="/img/dubbo_gray.png"/><img class="apache" src="/img/apache_logo.png"/><div class="cols-container"><div class="col col-12"><h3>Disclaimer</h3><p>Apache Dubbo is an effort undergoing incubation at The Apache Software Foundation (ASF), sponsored by the Incubator. Incubation is required of all newly accepted projects until a further review indicates that the infrastructure, communications, and decision making proce [...] + <script src="https://f.alicdn.com/react/15.4.1/react-with-addons.min.js"></script> + <script src="https://f.alicdn.com/react/15.4.1/react-dom.min.js"></script> + <script> + window.rootPath = ''; + </script> + <script src="/build/blogDetail.js"></script> + <!-- Global site tag (gtag.js) - Google Analytics --> + <script async src="https://www.googletagmanager.com/gtag/js?id=UA-112489517-1"></script> + <script> + window.dataLayer = window.dataLayer || []; + function gtag(){dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'UA-112489517-1'); + </script> +</body> +</html> \ No newline at end of file diff --git a/zh-cn/blog/service-and-version.json b/zh-cn/blog/service-and-version.json new file mode 100644 index 0000000..aa6638c --- /dev/null +++ b/zh-cn/blog/service-and-version.json @@ -0,0 +1,6 @@ +{ + "filename": "service-and-version.md", + "__html": "<h1>Dubbo服务分组和版本聚合</h1>\n<p>我们在调用Dubbo服务的时候,一般只需要将Consumer端的<code>dubbo:reference</code>指定成服务端中<code>dubbo:service</code>暴露的服务,就可以找到服务端,完成调用,也就是说,Dubbo只需要服务接口信息就可以找到服务提供者。\n其实除了服务提供者以外,Dubbo也有服务分组和版本的概念,在客户端去寻找“匹配”的服务端的时候,需要服务接口,版本号,组别这三个信息都匹配,才算是一个有效的服务端:</p>\n<pre><code class=\"language-java\"> <span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">static</span> <span class=\"hljs-keyword\">boolean</span> <span class=\"hljs- [...] + "link": "/zh-cn/blog/service-and-version.html", + "meta": {} +} \ No newline at end of file diff --git a/zh-cn/blog/test-verification.html b/zh-cn/blog/test-verification.html new file mode 100644 index 0000000..cd53812 --- /dev/null +++ b/zh-cn/blog/test-verification.html @@ -0,0 +1,111 @@ +<!DOCTYPE html> +<html lang="en"> + +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> + <meta name="keywords" content="test-verification" /> + <meta name="description" content="test-verification" /> + <!-- 网页标签标题 --> + <title>test-verification</title> + <link rel="shortcut icon" href="/img/dubbo.ico"/> + <link rel="stylesheet" href="/build/blogDetail.css" /> +</head> +<body> + <div id="root"><div class="blog-detail-page" data-reactroot=""><header class="header-container header-container-normal"><div class="header-body"><a href="/zh-cn/index.html"><img class="logo" src="/img/dubbo_colorful.png"/></a><div class="search search-normal"><span class="icon-search"></span></div><span class="language-switch language-switch-normal">En</span><div class="header-menu"><img class="header-menu-toggle" src="/img/menu_gray.png"/><ul><li class="menu-item menu-item-normal"><a h [...] +<p>除了线上常规的使用场景以外,我们在日常使用中还需要一些特定的使用方式,比如对正在开发的功能进行验证测试,比如单独调用某台机器的服务,这篇文章就来介绍一下这些场景下的使用方式。</p> +<h3>只订阅</h3> +<p>为方便开发测试,经常会在线下共用一个所有服务可用的注册中心,这时,如果一个正在开发中的服务提供者注册,可能会影响消费者不能正常运行。</p> +<p>可以让服务提供者开发方,只订阅服务(开发的服务可能依赖其它服务),而不注册正在开发的服务,通过直连测试正在开发的服务。<br> +<img src="https://cdn.nlark.com/lark/0/2018/jpeg/6752/1535447102196-eabbff47-287d-4f85-8fea-05b4f4921d75.jpeg" alt="subscribe-only.jpg"> +禁用注册配置</p> +<pre><code><dubbo:registry address="10.20.153.10:9090" register="false" /> +</code></pre> +<p>或者</p> +<pre><code><dubbo:registry address="10.20.153.10:9090?register=false" /> +</code></pre> +<h3>指定IP调用</h3> +<p>在开发及测试环境下,经常需要绕过注册中心,只测试指定服务提供者,这时候可能需要点对点直连,点对点直联方式,将以服务接口为单位,忽略注册中心的提供者列表,A 接口配置点对点,不影响 B 接口从注册中心获取列表<br> +<img src="https://cdn.nlark.com/lark/0/2018/jpeg/6752/1535447028864-8b952768-6fce-4c3c-b7a4-2003f3291d8e.jpeg" alt="dubbo-directly.jpg"></p> +<p>可以通过以下几种配置来指定IP调用</p> +<ul> +<li>XML 配置: 如果是线上需求需要点对点,可在 <a href="dubbo:reference">dubbo:reference</a> 中配置 url 指向提供者,将绕过注册中心,多个地址用分号隔开,配置如下: +<code><dubbo:reference id="xxxService" interface="com.alibaba.xxx.XxxService" url="dubbo://localhost:20890" /></code></li> +<li>通过-D参数指定: 在 JVM 启动参数中加入-D参数映射服务地址,如:<code>java -Dcom.alibaba.xxx.XxxService=dubbo://localhost:20890</code></li> +<li>通过文件映射: 如果服务比较多,也可以用文件映射,用 -Ddubbo.resolve.file 指定映射文件路径,此配置优先级高于 <a href="dubbo:reference">dubbo:reference</a> 中的配置,如: +<code>java -Ddubbo.resolve.file=xxx.properties</code><br> +然后在映射文件 xxx.properties 中加入配置,其中 key 为服务名,value 为服务提供者 URL:<code>com.alibaba.xxx.XxxService=dubbo://localhost:20890</code></li> +</ul> +<h3>回声测试</h3> +<h4>使用方式</h4> +<p>回声测试用于检测服务是否可用,回声测试按照正常请求流程执行,能够测试整个调用是否通畅,可用于监控。</p> +<p>所有服务自动实现 EchoService 接口,只需将任意服务引用强制转型为 EchoService,即可使用。</p> +<p>Spring 配置:</p> +<pre><code><dubbo:reference id="memberService" interface="com.xxx.MemberService" /> +</code></pre> +<p>代码:</p> +<pre><code>// 远程服务引用 +MemberService memberService = ctx.getBean("memberService"); + +EchoService echoService = (EchoService) memberService; // 强制转型为EchoService + +// 回声测试可用性 +String status = echoService.$echo("OK"); + +assert(status.equals("OK")); +</code></pre> +<h4>实现原理</h4> +<p>我们在实现,注册服务的时候,并没有配置EchoService这个接口,为什么可以直接使用呢?原来是Dubbo在生成proxy的时候,已经实现了<code>EchoService这个接口</code></p> +<pre><code class="language-java"> <span class="hljs-meta">@Override</span> + <span class="hljs-keyword">public</span> <T> <span class="hljs-function">T <span class="hljs-title">getProxy</span><span class="hljs-params">(Invoker<T> invoker)</span> <span class="hljs-keyword">throws</span> RpcException </span>{ + Class<?>[] interfaces = <span class="hljs-keyword">null</span>; + String config = invoker.getUrl().getParameter(<span class="hljs-string">"interfaces"</span>); + <span class="hljs-keyword">if</span> (config != <span class="hljs-keyword">null</span> && config.length() > <span class="hljs-number">0</span>) { + String[] types = Constants.COMMA_SPLIT_PATTERN.split(config); + <span class="hljs-keyword">if</span> (types != <span class="hljs-keyword">null</span> && types.length > <span class="hljs-number">0</span>) { + interfaces = <span class="hljs-keyword">new</span> Class<?>[types.length + <span class="hljs-number">2</span>]; + interfaces[<span class="hljs-number">0</span>] = invoker.getInterface(); + interfaces[<span class="hljs-number">1</span>] = EchoService.class; + <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < types.length; i++) { + interfaces[i + <span class="hljs-number">1</span>] = ReflectUtils.forName(types[i]); + } + } + } + <span class="hljs-keyword">if</span> (interfaces == <span class="hljs-keyword">null</span>) { + interfaces = <span class="hljs-keyword">new</span> Class<?>[]{invoker.getInterface(), EchoService.class}; + } + <span class="hljs-keyword">return</span> getProxy(invoker, interfaces); + } +</code></pre> +<p>通过这种方式,任何bean都可以被转换成EchoService的实例,但是并没有实现<code>$echo</code>这个方法,这里,Dubbo使用filter机制做了处理:</p> +<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">EchoFilter</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Filter</span> </span>{ + + <span class="hljs-meta">@Override</span> + <span class="hljs-function"><span class="hljs-keyword">public</span> Result <span class="hljs-title">invoke</span><span class="hljs-params">(Invoker<?> invoker, Invocation inv)</span> <span class="hljs-keyword">throws</span> RpcException </span>{ + <span class="hljs-keyword">if</span> (inv.getMethodName().equals(Constants.$ECHO) && inv.getArguments() != <span class="hljs-keyword">null</span> && inv.getArguments().length == <span class="hljs-number">1</span>) + <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> RpcResult(inv.getArguments()[<span class="hljs-number">0</span>]); + <span class="hljs-keyword">return</span> invoker.invoke(inv); + } + +} +</code></pre> +<p>在经过EchoFilter.invoke方法时,如果调用的是<code>$echo</code>,会中断当前的调用过程,直接返回<code>$echo</code>的入参,否则会继续执行Filter链。<br> +通过动态代理和EchoFilter机制,使得回声测试的整个过程对用户透明,不需要做任何额外的配置,直接调用即可。</p> +</section><footer class="footer-container"><div class="footer-body"><img src="/img/dubbo_gray.png"/><img class="apache" src="/img/apache_logo.png"/><div class="cols-container"><div class="col col-12"><h3>Disclaimer</h3><p>Apache Dubbo is an effort undergoing incubation at The Apache Software Foundation (ASF), sponsored by the Incubator. Incubation is required of all newly accepted projects until a further review indicates that the infrastructure, communications, and decision making proce [...] + <script src="https://f.alicdn.com/react/15.4.1/react-with-addons.min.js"></script> + <script src="https://f.alicdn.com/react/15.4.1/react-dom.min.js"></script> + <script> + window.rootPath = ''; + </script> + <script src="/build/blogDetail.js"></script> + <!-- Global site tag (gtag.js) - Google Analytics --> + <script async src="https://www.googletagmanager.com/gtag/js?id=UA-112489517-1"></script> + <script> + window.dataLayer = window.dataLayer || []; + function gtag(){dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'UA-112489517-1'); + </script> +</body> +</html> \ No newline at end of file diff --git a/zh-cn/blog/test-verification.json b/zh-cn/blog/test-verification.json new file mode 100644 index 0000000..225b52f --- /dev/null +++ b/zh-cn/blog/test-verification.json @@ -0,0 +1,6 @@ +{ + "filename": "test-verification.md", + "__html": "<h1>Dubbo测试验证</h1>\n<p>除了线上常规的使用场景以外,我们在日常使用中还需要一些特定的使用方式,比如对正在开发的功能进行验证测试,比如单独调用某台机器的服务,这篇文章就来介绍一下这些场景下的使用方式。</p>\n<h3>只订阅</h3>\n<p>为方便开发测试,经常会在线下共用一个所有服务可用的注册中心,这时,如果一个正在开发中的服务提供者注册,可能会影响消费者不能正常运行。</p>\n<p>可以让服务提供者开发方,只订阅服务(开发的服务可能依赖其它服务),而不注册正在开发的服务,通过直连测试正在开发的服务。<br>\n<img src=\"https://cdn.nlark.com/lark/0/2018/jpeg/6752/1535447102196-eabbff47-287d-4f85-8fea-05b4f4921d75.jpeg\" alt=\"subscribe-only.jpg\">\n禁用注册配置</p>\n<pre><code><dubbo:registry address="10.20.15 [...] + "link": "/zh-cn/blog/test-verification.html", + "meta": {} +} \ No newline at end of file diff --git a/zh-cn/docs/admin/install/admin-console.html b/zh-cn/docs/admin/install/admin-console.html index 11e0b75..dd79362 100644 --- a/zh-cn/docs/admin/install/admin-console.html +++ b/zh-cn/docs/admin/install/admin-console.html @@ -12,7 +12,25 @@ <link rel="stylesheet" href="/build/documentation.css" /> </head> <body> - <div id="root"><div class="documentation-page" data-reactroot=""><header class="header-container header-container-normal"><div class="header-body"><a href="/zh-cn/index.html"><img class="logo" src="/img/dubbo_colorful.png"/></a><div class="search search-normal"><span class="icon-search"></span></div><span class="language-switch language-switch-normal">En</span><div class="header-menu"><img class="header-menu-toggle" src="/img/menu_gray.png"/><ul><li class="menu-item menu-item-normal"><a [...] + <div id="root"><div class="documentation-page" data-reactroot=""><header class="header-container header-container-normal"><div class="header-body"><a href="/zh-cn/index.html"><img class="logo" src="/img/dubbo_colorful.png"/></a><div class="search search-normal"><span class="icon-search"></span></div><span class="language-switch language-switch-normal">En</span><div class="header-menu"><img class="header-menu-toggle" src="/img/menu_gray.png"/><ul><li class="menu-item menu-item-normal"><a [...] +<h3>管理控制台安装</h3> +<p>======= +###管理控制台安装</p> +<blockquote> +<blockquote> +<blockquote> +<blockquote> +<blockquote> +<blockquote> +<blockquote> +<p>a71d0fc87489096a2e99133c47769ffa750bafa6</p> +</blockquote> +</blockquote> +</blockquote> +</blockquote> +</blockquote> +</blockquote> +</blockquote> <p>目前版本的管理控制台正在开发中,已经完成了服务查询和服务治理的功能,采用前后端分离的模式,具体的安装和使用步骤如下:</p> <p>安装:</p> <pre><code class="language-sh">git <span class="hljs-built_in">clone</span> https://github.com/apache/incubator-dubbo-ops.git /var/tmp/dubbo-ops diff --git a/zh-cn/docs/admin/install/admin-console.json b/zh-cn/docs/admin/install/admin-console.json index a4246cb..f1351ac 100644 --- a/zh-cn/docs/admin/install/admin-console.json +++ b/zh-cn/docs/admin/install/admin-console.json @@ -1,6 +1,6 @@ { "filename": "admin-console.md", - "__html": "<h3>管理控制台安装</h3>\n<p>目前版本的管理控制台正在开发中,已经完成了服务查询和服务治理的功能,采用前后端分离的模式,具体的安装和使用步骤如下:</p>\n<p>安装:</p>\n<pre><code class=\"language-sh\">git <span class=\"hljs-built_in\">clone</span> https://github.com/apache/incubator-dubbo-ops.git /var/tmp/dubbo-ops\n<span class=\"hljs-built_in\">cd</span> /var/tmp/dubbo-ops\nmvn clean package\n</code></pre>\n<p>配置 <sup class=\"footnote-ref\"><a href=\"#fn1\" id=\"fnref1\">[1]</a></sup>:</p>\n<pre><code class=\"language-sh\">配置文件为:\ndubbo-admin- [...] + "__html": "<p><<<<<<< HEAD</p>\n<h3>管理控制台安装</h3>\n<p>=======\n###管理控制台安装</p>\n<blockquote>\n<blockquote>\n<blockquote>\n<blockquote>\n<blockquote>\n<blockquote>\n<blockquote>\n<p>a71d0fc87489096a2e99133c47769ffa750bafa6</p>\n</blockquote>\n</blockquote>\n</blockquote>\n</blockquote>\n</blockquote>\n</blockquote>\n</blockquote>\n<p>目前版本的管理控制台正在开发中,已经完成了服务查询和服务治理的功能,采用前后端分离的模式,具体的安装和使用步骤如下:</p>\n<p>安装:</p>\n<pre><code class=\"language-sh\">git <span class=\"hljs-buil [...] "link": "/zh-cn/docs/admin/install/admin-console.html", "meta": {} } \ No newline at end of file
