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/12 06:09:00 UTC

[GitHub] ralf0131 closed pull request #44: Fix background logo on first screen wider than parent div (#43)

ralf0131 closed pull request #44: Fix background logo on first screen wider than parent div (#43)
URL: https://github.com/apache/incubator-dubbo-website/pull/44
 
 
   

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/build/232598f226cf6f343d6b.js b/build/232598f226cf6f343d6b.js
new file mode 100644
index 0000000..8e23cda
--- /dev/null
+++ b/build/232598f226cf6f343d6b.js
@@ -0,0 +1,6 @@
+webpackJsonp([0],[,,,,,,,,,function(s,n,a){"use strict";function e(s){return s&&s.__esModule?s:{default:s}}function t(s,n){if(!(s instanceof n))throw new TypeError("Cannot call a class as a function")}function l(s,n){if(!s)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!n||"object"!=typeof n&&"function"!=typeof n?s:n}function o(s,n){if("function"!=typeof n&&null!==n)throw new TypeError("Super expression must either be null or a function, not "+typeof n);s.prototype=Object.create(n&&n.prototype,{constructor:{value:s,enumerable:!1,writable:!0,configurable:!0}}),n&&(Object.setPrototypeOf?Object.setPrototypeOf(s,n):s.__proto__=n)}Object.defineProperty(n,"__esModule",{value:!0});var r=function(){function s(s,n){for(var a=0;a<n.length;a++){var e=n[a];e.enumerable=e.enumerable||!1,e.configurable=!0,"value"in e&&(e.writable=!0),Object.defineProperty(s,e.key,e)}}return function(n,a,e){return a&&s(n.prototype,a),e&&s(n,e),n}}(),p=a(0),c=e(p),i=a(18),d=e(i),h=a(70),u=a(63),g=e(u),m=a(21),b=e(m),j=a(1),f=a(25),v=e(f),y=a(24),x=e(y),k=a(55),w=e(k),S=a(82),C=e(S),R=a(23),P=e(R),T=a(17),I=e(T),E=a(79),A=e(E),M=a(76),L=e(M);a(98);var D=/^#[^\/]/,O=/^((\.{1,2}\/)|([\w-]+[\/.]))/,F=function(s){function n(){return t(this,n),l(this,(n.__proto__||Object.getPrototypeOf(n)).apply(this,arguments))}return o(n,s),r(n,[{key:"componentDidMount",value:function(){if(this.markdownContainer){window.scrollTo&&window.scrollTo(0,0);var s=this.props.match.url.split("?")[0].split("/").slice(2).join("/"),n=s.split("/").slice(0,-1).join("/"),a=window.location.hash.split("?"),e=b.default.parse(a[1]||""),t=e.lang||d.default.get("docsite_language")||I.default.defaultLanguage,l=Array.from(this.markdownContainer.querySelectorAll("img")),o=Array.from(this.markdownContainer.querySelectorAll("a"));l.forEach(function(s){var a=s.getAttribute("src");O.test(a)&&(s.src=window.location.protocol+"//"+window.location.host+g.default.join(window.location.pathname,"./docs",t,n,a))}),o.forEach(function(s){var a=s.getAttribute("href");O.test(a)&&(s.href=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search+"#/"+g.default.join("./docs",n,a))}),this.markdownContainer.addEventListener("click",function(s){if("a"===s.target.nodeName.toLowerCase()&&s.target.getAttribute("href")&&D.test(s.target.getAttribute("href"))){s.preventDefault();var n=s.target.getAttribute("href").slice(1);h.scroller.scrollTo(n,{duration:1e3,smooth:"easeInOutQuint"})}})}}},{key:"componentDidUpdate",value:function(){this.componentDidMount()}},{key:"render",value:function(){var s=this,n=window.location.hash.split("?"),a=b.default.parse(n[1]||""),e=a.lang||d.default.get("docsite_language")||I.default.defaultLanguage;if("en-us"!==e&&"zh-cn"!==e&&(e=I.default.defaultLanguage),e!==d.default.get("docsite_language")&&d.default.set("docsite_language",e,{expires:365,path:""}),!a.lang)return c.default.createElement(j.Redirect,{to:this.props.match.url+"?lang="+e});var t=A.default[e],l=this.props.match.url.split("/").slice(2).join("/"),o=L.default[e].find(function(s){return s.filename===l}),r=o&&o.__html?o.__html:"";return c.default.createElement("div",{className:"documentation-page"},c.default.createElement(x.default,{type:"normal",logo:"./img/dubbo_colorful.png",language:e,onLanguageChange:this.onLanguageChange}),c.default.createElement(w.default,{img:"./img/docs.png",text:t.barText}),c.default.createElement("section",{className:"content-section"},c.default.createElement(C.default,{dataSource:t.sidemenu}),c.default.createElement("div",{className:"doc-content markdown-body",ref:function(n){s.markdownContainer=n},dangerouslySetInnerHTML:{__html:r}})),c.default.createElement(P.default,{logo:"./img/dubbo_gray.png"}))}}]),n}(v.default);n.default=F},,,,,,function(s,n,a){"use strict";function e(s,n,a,e){a&&Object.defineProperty(s,n,{enumerable:a.enumerable,configurable:a.configurable,writable:a.writable,value:a.initializer?a.initializer.call(e):void 0})}function t(s,n){if(!(s instanceof n))throw new TypeError("Cannot call a class as a function")}function l(s,n,a,e,t){var l={};return Object.keys(e).forEach(function(s){l[s]=e[s]}),l.enumerable=!!l.enumerable,l.configurable=!!l.configurable,("value"in l||l.initializer)&&(l.writable=!0),l=a.slice().reverse().reduce(function(a,e){return e(s,n,a)||a},l),t&&void 0!==l.initializer&&(l.value=l.initializer?l.initializer.call(t):void 0,l.initializer=void 0),void 0===l.initializer&&(Object.defineProperty(s,n,l),l=null),l}function o(s){if(Array.isArray(s)){for(var n=0,a=Array(s.length);n<s.length;n++)a[n]=s[n];return a}return Array.from(s)}function r(s){if(!s||!s.hasOwnProperty)return!1;for(var n=["value","initializer","get","set"],a=0,e=n.length;a<e;a++)if(s.hasOwnProperty(n[a]))return!0;return!1}function p(s,n){return r(n[n.length-1])?s.apply(void 0,o(n).concat([[]])):function(){return s.apply(void 0,o(Array.prototype.slice.call(arguments)).concat([n]))}}function c(s){return!1===s.hasOwnProperty(P)&&k(s,P,{value:new R}),s[P]}function i(s){var n={};return T(s).forEach(function(a){return n[a]=w(s,a)}),n}function d(s){return function(n){return Object.defineProperty(this,s,{configurable:!0,writable:!0,enumerable:!0,value:n}),n}}function h(s,n){return s.bind?s.bind(n):function(){return s.apply(n,arguments)}}function u(s){!0!==E[s]&&(E[s]=!0,I("DEPRECATION: "+s))}n.d=p,n.c=c,a.d(n,"g",function(){return T}),n.f=i,n.e=d,n.a=h,a.d(n,"b",function(){return I}),n.h=u;var g,m,b,j,f,v,y=a(20),x="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(s){return typeof s}:function(s){return s&&"function"==typeof Symbol&&s.constructor===Symbol&&s!==Symbol.prototype?"symbol":typeof s},k=Object.defineProperty,w=Object.getOwnPropertyDescriptor,S=Object.getOwnPropertyNames,C=Object.getOwnPropertySymbols,R=(g=function s(){t(this,s),e(this,"debounceTimeoutIds",m,this),e(this,"throttleTimeoutIds",b,this),e(this,"throttlePreviousTimestamps",j,this),e(this,"throttleTrailingArgs",f,this),e(this,"profileLastRan",v,this)},m=l(g.prototype,"debounceTimeoutIds",[y.a],{enumerable:!0,initializer:function(){return{}}}),b=l(g.prototype,"throttleTimeoutIds",[y.a],{enumerable:!0,initializer:function(){return{}}}),j=l(g.prototype,"throttlePreviousTimestamps",[y.a],{enumerable:!0,initializer:function(){return{}}}),f=l(g.prototype,"throttleTrailingArgs",[y.a],{enumerable:!0,initializer:function(){return null}}),v=l(g.prototype,"profileLastRan",[y.a],{enumerable:!0,initializer:function(){return null}}),g),P="function"==typeof Symbol?Symbol("__core_decorators__"):"__core_decorators__",T=C?function(s){return S(s).concat(C(s))}:S,I=function(){return"object"===("undefined"==typeof console?"undefined":x(console))&&console&&"function"==typeof console.warn?h(console.warn,console):function(){}}(),E={}},function(s,n,a){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var e=a(37);a.d(n,"override",function(){return e.a});var t=a(30);a.d(n,"deprecate",function(){return t.a}),a.d(n,"deprecated",function(){return t.a});var l=a(40);a.d(n,"suppressWarnings",function(){return l.a});var o=a(33);a.d(n,"memoize",function(){return o.a});var r=a(27);a.d(n,"autobind",function(){return r.a});var p=a(39);a.d(n,"readonly",function(){return p.a});var c=a(31);a.d(n,"enumerable",function(){return c.a});var i=a(36);a.d(n,"nonenumerable",function(){return i.a});var d=a(35);a.d(n,"nonconfigurable",function(){return d.a});var h=a(28);a.d(n,"debounce",function(){return h.a});var u=a(41);a.d(n,"throttle",function(){return u.a});var g=a(29);a.d(n,"decorate",function(){return g.a});var m=a(34);a.d(n,"mixin",function(){return m.a}),a.d(n,"mixins",function(){return m.a});var b=a(20);a.d(n,"lazyInitialize",function(){return b.a});var j=a(42);a.d(n,"time",function(){return j.a});var f=a(32);a.d(n,"extendDescriptor",function(){return f.a});var v=a(38);a.d(n,"profile",function(){return v.a});var y=a(26);a.d(n,"applyDecorators",function(){return y.a})},function(s,n,a){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default={defaultLanguage:"en-us","en-us":{pageMenu:[{text:"HOME",link:"/"},{text:"DOCS",link:"/docs/user/quick-start.md"},{text:"BLOG",link:"/blog"},{text:"COMMUNITY",link:"/community"}],disclaimer:{title:"Disclaimer",content:"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 process have stabilized in a manner consistent with other successful ASF projects. While incubation status is not necessarily a reflection of the completeness or stability of the code, it does indicate that the project has yet to be fully endorsed by the ASF."},documentation:{title:"Documentation",list:[{text:"Quick start",link:"/docs/user/quick-start.md"},{text:"Developer guide",link:"/docs/dev/build.md"},{text:"Admin manual",link:"/docs/admin/ops/dubbo-ops.md"}]},resources:{title:"Resources",list:[{text:"Blog",link:"/blog"},{text:"Community",link:"/community"}]},copyright:"Copyright © 2018 The Apache Software Foundation. Apache and the Apache feather logo are trademarks of The Apache Software Foundation."},"zh-cn":{pageMenu:[{text:"首页",link:"/"},{text:"文档",link:"/docs/user/quick-start.md"},{text:"博客",link:"/blog"},{text:"社区",link:"/community"}],disclaimer:{title:"Disclaimer",content:"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 process have stabilized in a manner consistent with other successful ASF projects. While incubation status is not necessarily a reflection of the completeness or stability of the code, it does indicate that the project has yet to be fully endorsed by the ASF."},documentation:{title:"文档",list:[{text:"快速开始",link:"/docs/user/quick-start.md"},{text:"开发者指南",link:"/docs/dev/build.md"},{text:"运维管理",link:"/docs/admin/ops/dubbo-ops.md"}]},resources:{title:"资源",list:[{text:"博客",link:"/blog"},{text:"社区",link:"/community"}]},copyright:"Copyright © 2018 The Apache Software Foundation. Apache and the Apache feather logo are trademarks of The Apache Software Foundation."}}},function(s,n,a){var e,t;!function(l){var o=!1;if(e=l,void 0!==(t="function"==typeof e?e.call(n,a,n,s):e)&&(s.exports=t),o=!0,s.exports=l(),o=!0,!o){var r=window.Cookies,p=window.Cookies=l();p.noConflict=function(){return window.Cookies=r,p}}}(function(){function s(){for(var s=0,n={};s<arguments.length;s++){var a=arguments[s];for(var e in a)n[e]=a[e]}return n}function n(a){function e(n,t,l){var o;if("undefined"!=typeof document){if(arguments.length>1){if(l=s({path:"/"},e.defaults,l),"number"==typeof l.expires){var r=new Date;r.setMilliseconds(r.getMilliseconds()+864e5*l.expires),l.expires=r}l.expires=l.expires?l.expires.toUTCString():"";try{o=JSON.stringify(t),/^[\{\[]/.test(o)&&(t=o)}catch(s){}t=a.write?a.write(t,n):encodeURIComponent(String(t)).replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g,decodeURIComponent),n=encodeURIComponent(String(n)),n=n.replace(/%(23|24|26|2B|5E|60|7C)/g,decodeURIComponent),n=n.replace(/[\(\)]/g,escape);var p="";for(var c in l)l[c]&&(p+="; "+c,!0!==l[c]&&(p+="="+l[c]));return document.cookie=n+"="+t+p}n||(o={});for(var i=document.cookie?document.cookie.split("; "):[],d=/(%[0-9A-Z]{2})+/g,h=0;h<i.length;h++){var u=i[h].split("="),g=u.slice(1).join("=");this.json||'"'!==g.charAt(0)||(g=g.slice(1,-1));try{var m=u[0].replace(d,decodeURIComponent);if(g=a.read?a.read(g,m):a(g,m)||g.replace(d,decodeURIComponent),this.json)try{g=JSON.parse(g)}catch(s){}if(n===m){o=g;break}n||(o[m]=g)}catch(s){}}return o}}return e.set=e,e.get=function(s){return e.call(e,s)},e.getJSON=function(){return e.apply({json:!0},[].slice.call(arguments))},e.defaults={},e.remove=function(n,a){e(n,"",s(a,{expires:-1}))},e.withConverter=n,e}return n(function(){})})},function(s,n,a){var e,t;/*!
+  Copyright (c) 2017 Jed Watson.
+  Licensed under the MIT License (MIT), see
+  http://jedwatson.github.io/classnames
+*/
+!function(){"use strict";function a(){for(var s=[],n=0;n<arguments.length;n++){var e=arguments[n];if(e){var t=typeof e;if("string"===t||"number"===t)s.push(e);else if(Array.isArray(e)&&e.length){var o=a.apply(null,e);o&&s.push(o)}else if("object"===t)for(var r in e)l.call(e,r)&&e[r]&&s.push(r)}}return s.join(" ")}var l={}.hasOwnProperty;void 0!==s&&s.exports?(a.default=a,s.exports=a):(e=[],void 0!==(t=function(){return a}.apply(n,e))&&(s.exports=t))}()},function(s,n,a){"use strict";function e(s,n,e){var t=e.configurable,r=e.enumerable,p=e.initializer,c=e.value;return{configurable:t,enumerable:r,get:function(){if(this!==s){var a=p?p.call(this):c;return o(this,n,{configurable:t,enumerable:r,writable:!0,value:a}),a}},set:a.i(l.e)(n)}}function t(){for(var s=arguments.length,n=Array(s),t=0;t<s;t++)n[t]=arguments[t];return a.i(l.d)(e,n)}n.a=t;var l=a(15),o=Object.defineProperty},function(s,n,a){"use strict";n.decode=n.parse=a(46),n.encode=n.stringify=a(47)},function(s,n,a){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var e=function(s){if(s=s?0===s.indexOf("#")?s:"#"+s:"",history.pushState){var n=window.location;history.pushState(null,null,s?n.pathname+n.search+s:n.pathname+n.search)}else location.hash=s},t=function(){return window.location.hash.replace(/^#/,"")},l=function(s){return function(n){return s.contains?s!=n&&s.contains(n):!!(16&s.compareDocumentPosition(n))}},o=function(s,n){return s===document?n.getBoundingClientRect().top+(window.scrollY||window.pageYOffset):"relative"===getComputedStyle(s).position?n.offsetTop:n.getBoundingClientRect().top+s.scrollTop};n.default={pushHash:e,getHash:t,filterElementInContainer:l,scrollOffset:o}},function(s,n,a){"use strict";function e(s){return s&&s.__esModule?s:{default:s}}Object.defineProperty(n,"__esModule",{value:!0});var t=a(0),l=e(t),o=a(18),r=e(o),p=a(1),c=a(17),i=e(c);a(43);var d=function(s){var n=r.default.get("docsite_language")||i.default.defaultLanguage,a=i.default[n];return l.default.createElement("footer",{className:"footer-container"},l.default.createElement("div",{className:"footer-body"},l.default.createElement("img",{src:s.logo}),l.default.createElement("img",{className:"apache",src:"./img/apache_logo.png"}),l.default.createElement("div",{className:"cols-container"},l.default.createElement("div",{className:"col col-12"},l.default.createElement("h3",null,a.disclaimer.title),l.default.createElement("p",null,a.disclaimer.content)),l.default.createElement("div",{className:"col col-6"},l.default.createElement("dl",null,l.default.createElement("dt",null,a.documentation.title),a.documentation.list.map(function(s,n){return l.default.createElement("dd",{key:n},l.default.createElement(p.Link,{to:s.link},s.text))}))),l.default.createElement("div",{className:"col col-6"},l.default.createElement("dl",null,l.default.createElement("dt",null,a.resources.title),a.resources.list.map(function(s,n){return l.default.createElement("dd",{key:n},l.default.createElement(p.Link,{to:s.link},s.text))})))),l.default.createElement("div",{className:"copyright"},l.default.createElement("span",null,a.copyright))))};n.default=d},function(s,n,a){"use strict";function e(s){return s&&s.__esModule?s:{default:s}}function t(s,n,a){return n in s?Object.defineProperty(s,n,{value:a,enumerable:!0,configurable:!0,writable:!0}):s[n]=a,s}function l(s,n){if(!(s instanceof n))throw new TypeError("Cannot call a class as a function")}function o(s,n){if(!s)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!n||"object"!=typeof n&&"function"!=typeof n?s:n}function r(s,n){if("function"!=typeof n&&null!==n)throw new TypeError("Super expression must either be null or a function, not "+typeof n);s.prototype=Object.create(n&&n.prototype,{constructor:{value:s,enumerable:!1,writable:!0,configurable:!0}}),n&&(Object.setPrototypeOf?Object.setPrototypeOf(s,n):s.__proto__=n)}function p(s,n,a,e,t){var l={};return Object.keys(e).forEach(function(s){l[s]=e[s]}),l.enumerable=!!l.enumerable,l.configurable=!!l.configurable,("value"in l||l.initializer)&&(l.writable=!0),l=a.slice().reverse().reduce(function(a,e){return e(s,n,a)||a},l),t&&void 0!==l.initializer&&(l.value=l.initializer?l.initializer.call(t):void 0,l.initializer=void 0),void 0===l.initializer&&(Object.defineProperty(s,n,l),l=null),l}Object.defineProperty(n,"__esModule",{value:!0});var c,i=function(){function s(s,n){for(var a=0;a<n.length;a++){var e=n[a];e.enumerable=e.enumerable||!1,e.configurable=!0,"value"in e&&(e.writable=!0),Object.defineProperty(s,e.key,e)}}return function(n,a,e){return a&&s(n.prototype,a),e&&s(n,e),n}}(),d=a(0),h=e(d),u=a(1),g=a(19),m=e(g),b=a(16),j=a(17),f=e(j);a(44);var v=[{text:"中",value:"en-us"},{text:"En",value:"zh-cn"}],y=function(){},x={type:"primary",language:"en-us",onLanguageChange:y},k=(c=function(s){function n(s){l(this,n);var a=o(this,(n.__proto__||Object.getPrototypeOf(n)).call(this,s));return a.state={menuBodyVisible:!1,language:s.language},a}return r(n,s),i(n,[{key:"toggleMenu",value:function(){this.setState({menuBodyVisible:!this.state.menuBodyVisible})}},{key:"switchLang",value:function(){var s=void 0;s="zh-cn"===this.state.language?"en-us":"zh-cn",this.setState({language:s}),this.props.onLanguageChange(s)}},{key:"componentWillReceiveProps",value:function(s){this.setState({language:s.language})}},{key:"render",value:function(){var s=this.props,n=s.type,a=s.logo,e=s.onLanguageChange,l=this.state,o=l.menuBodyVisible,r=l.language;return h.default.createElement("header",{className:(0,m.default)(t({"header-container":!0},"header-container-"+n,!0))},h.default.createElement("div",{className:"header-body"},h.default.createElement(u.Link,{to:"/"},h.default.createElement("img",{className:"logo",alt:f.default.name,title:f.default.name,src:a})),e!==y?h.default.createElement("span",{className:(0,m.default)(t({"language-switch":!0},"language-switch-"+n,!0)),onClick:this.switchLang},v.find(function(s){return s.value===r}).text):null,h.default.createElement("div",{className:(0,m.default)({"header-menu":!0,"header-menu-open":o})},h.default.createElement("img",{className:"header-menu-toggle",onClick:this.toggleMenu,src:"primary"===n?"./img/menu_white.png":"./img/menu_gray.png"}),h.default.createElement("ul",null,f.default[r].pageMenu.map(function(s){var a;return h.default.createElement("li",{className:(0,m.default)((a={"menu-item":!0},t(a,"menu-item-"+n,!0),t(a,"menu-item-"+n+"-active",window.location.hash.split("?")[0].slice(1).split("/")[1]===s.link.split("/")[1]),a))},h.default.createElement(u.Link,{to:s.link},s.text))})))))}}]),n}(h.default.Component),p(c.prototype,"toggleMenu",[b.autobind],Object.getOwnPropertyDescriptor(c.prototype,"toggleMenu"),c.prototype),p(c.prototype,"switchLang",[b.autobind],Object.getOwnPropertyDescriptor(c.prototype,"switchLang"),c.prototype),c);k.defaultProps=x,n.default=k},function(s,n,a){"use strict";function e(s){return s&&s.__esModule?s:{default:s}}function t(s,n){if(!(s instanceof n))throw new TypeError("Cannot call a class as a function")}function l(s,n){if(!s)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!n||"object"!=typeof n&&"function"!=typeof n?s:n}function o(s,n){if("function"!=typeof n&&null!==n)throw new TypeError("Super expression must either be null or a function, not "+typeof n);s.prototype=Object.create(n&&n.prototype,{constructor:{value:s,enumerable:!1,writable:!0,configurable:!0}}),n&&(Object.setPrototypeOf?Object.setPrototypeOf(s,n):s.__proto__=n)}Object.defineProperty(n,"__esModule",{value:!0}),n.default=void 0;var r,p=function(){function s(s,n){for(var a=0;a<n.length;a++){var e=n[a];e.enumerable=e.enumerable||!1,e.configurable=!0,"value"in e&&(e.writable=!0),Object.defineProperty(s,e.key,e)}}return function(n,a,e){return a&&s(n.prototype,a),e&&s(n,e),n}}(),c=a(0),i=e(c),d=a(16),h=a(18),u=e(h),g=a(21),m=e(g),b=(r=function(s){function n(){return t(this,n),l(this,(n.__proto__||Object.getPrototypeOf(n)).apply(this,arguments))}return o(n,s),p(n,[{key:"onLanguageChange",value:function(s){this.props.location;u.default.set("docsite_language",s,{expires:365,path:""});var n=window.location.hash.split("?");if(n&&n.length){var a=m.default.parse(n[1]||"");a.lang=s,window.location.hash=(n[0]||"")+"?"+m.default.stringify(a)}this.forceUpdate()}}]),n}(i.default.Component),function(s,n,a,e,t){var l={};return Object.keys(e).forEach(function(s){l[s]=e[s]}),l.enumerable=!!l.enumerable,l.configurable=!!l.configurable,("value"in l||l.initializer)&&(l.writable=!0),l=a.slice().reverse().reduce(function(a,e){return e(s,n,a)||a},l),t&&void 0!==l.initializer&&(l.value=l.initializer?l.initializer.call(t):void 0,l.initializer=void 0),void 0===l.initializer&&(Object.defineProperty(s,n,l),l=null),l}(r.prototype,"onLanguageChange",[d.autobind],Object.getOwnPropertyDescriptor(r.prototype,"onLanguageChange"),r.prototype),r);n.default=b},function(s,n,a){"use strict";function e(s,n){var a=s.prototype;for(var e in n)for(var o=n[e],r=0,p=o.length;r<p;r++){var c=o[r];t(a,e,c(a,e,l(a,e)))}return s}n.a=e;var t=Object.defineProperty,l=Object.getOwnPropertyDescriptor},function(s,n,a){"use strict";function e(s){if(Array.isArray(s)){for(var n=0,a=Array(s.length);n<s.length;n++)a[n]=s[n];return a}return Array.from(s)}function t(s,n){if("undefined"==typeof WeakMap)throw new Error("Using @autobind on "+n.name+"() requires WeakMap support due to its use of super."+n.name+"()\n      See https://github.com/jayphelps/core-decorators.js/issues/20");h||(h=new WeakMap),!1===h.has(s)&&h.set(s,new WeakMap);var e=h.get(s);return!1===e.has(n)&&e.set(n,a.i(c.a)(n,s)),e.get(n)}function l(s){for(var n=a.i(c.f)(s.prototype),e=a.i(c.g)(n),t=0,l=e.length;t<l;t++){var r=e[t],p=n[r];"function"==typeof p.value&&"constructor"!==r&&i(s.prototype,r,o(s.prototype,r,p))}}function o(s,n,e){var l=e.value,o=e.configurable,r=e.enumerable;if("function"!=typeof l)throw new SyntaxError("@autobind can only be used on functions, not: "+l);var p=s.constructor;return{configurable:o,enumerable:r,get:function(){if(this===s)return l;if(this.constructor!==p&&d(this).constructor===p)return l;if(this.constructor!==p&&n in this.constructor.prototype)return t(this,l);var e=a.i(c.a)(l,this);return i(this,n,{configurable:!0,writable:!0,enumerable:!1,value:e}),e},set:a.i(c.e)(n)}}function r(s){return 1===s.length?l.apply(void 0,e(s)):o.apply(void 0,e(s))}function p(){for(var s=arguments.length,n=Array(s),a=0;a<s;a++)n[a]=arguments[a];return 0===n.length?function(){return r(arguments)}:r(n)}n.a=p;var c=a(15),i=Object.defineProperty,d=Object.getPrototypeOf,h=void 0},function(s,n,a){"use strict";function e(s,n,e,t){var c=r(t,2),i=c[0],d=void 0===i?p:i,h=c[1],u=void 0!==h&&h,g=e.value;if("function"!=typeof g)throw new SyntaxError("Only functions can be debounced");return o({},e,{value:function(){var s=this,e=a.i(l.c)(this),t=e.debounceTimeoutIds,o=t[n],r=u&&!o,p=arguments;clearTimeout(o),t[n]=setTimeout(function(){delete t[n],u||g.apply(s,p)},d),r&&g.apply(this,p)}})}function t(){a.i(l.h)("@debounce is deprecated and will be removed shortly. Use @debounce from lodash-decorators.\n\n  https://www.npmjs.com/package/lodash-decorators");for(var s=arguments.length,n=Array(s),t=0;t<s;t++)n[t]=arguments[t];return a.i(l.d)(e,n)}n.a=t;var l=a(15),o=Object.assign||function(s){for(var n=1;n<arguments.length;n++){var a=arguments[n];for(var e in a)Object.prototype.hasOwnProperty.call(a,e)&&(s[e]=a[e])}return s},r=function(){function s(s,n){var a=[],e=!0,t=!1,l=void 0;try{for(var o,r=s[Symbol.iterator]();!(e=(o=r.next()).done)&&(a.push(o.value),!n||a.length!==n);e=!0);}catch(s){t=!0,l=s}finally{try{!e&&r.return&&r.return()}finally{if(t)throw l}}return a}return function(n,a){if(Array.isArray(n))return n;if(Symbol.iterator in Object(n))return s(n,a);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),p=300},function(s,n,a){"use strict";function e(s){if(Array.isArray(s)){for(var n=0,a=Array(s.length);n<s.length;n++)a[n]=s[n];return a}return Array.from(s)}function t(s){return Array.isArray(s)?s:Array.from(s)}function l(s,n,l,o){var c=t(o),i=c[0],d=c.slice(1),h=l.configurable,u=l.enumerable,g=l.writable,m=l.get,b=l.set,j=l.value,f=!!m;return{configurable:h,enumerable:u,get:function(){var s=f?m.call(this):j,a=i.call.apply(i,[this,s].concat(e(d)));if(f)return a;var t={configurable:h,enumerable:u};return t.value=a,t.writable=g,p(this,n,t),a},set:f?b:a.i(r.e)()}}function o(){for(var s=arguments.length,n=Array(s),e=0;e<s;e++)n[e]=arguments[e];return a.i(r.d)(l,n)}n.a=o;var r=a(15),p=Object.defineProperty},function(s,n,a){"use strict";function e(s,n,e,t){var c=r(t,2),i=c[0],d=void 0===i?p:i,h=c[1],u=void 0===h?{}:h;if("function"!=typeof e.value)throw new SyntaxError("Only functions can be marked as deprecated");var g=s.constructor.name+"#"+n;return u.url&&(d+="\n\n    See "+u.url+" for more details.\n\n"),o({},e,{value:function(){return a.i(l.b)("DEPRECATION "+g+": "+d),e.value.apply(this,arguments)}})}function t(){for(var s=arguments.length,n=Array(s),t=0;t<s;t++)n[t]=arguments[t];return a.i(l.d)(e,n)}n.a=t;var l=a(15),o=Object.assign||function(s){for(var n=1;n<arguments.length;n++){var a=arguments[n];for(var e in a)Object.prototype.hasOwnProperty.call(a,e)&&(s[e]=a[e])}return s},r=function(){function s(s,n){var a=[],e=!0,t=!1,l=void 0;try{for(var o,r=s[Symbol.iterator]();!(e=(o=r.next()).done)&&(a.push(o.value),!n||a.length!==n);e=!0);}catch(s){t=!0,l=s}finally{try{!e&&r.return&&r.return()}finally{if(t)throw l}}return a}return function(n,a){if(Array.isArray(n))return n;if(Symbol.iterator in Object(n))return s(n,a);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),p="This function will be removed in future versions."},function(s,n,a){"use strict";function e(s,n,a){return a.enumerable=!0,a}function t(){for(var s=arguments.length,n=Array(s),t=0;t<s;t++)n[t]=arguments[t];return a.i(l.d)(e,n)}n.a=t;var l=a(15)},function(s,n,a){"use strict";function e(s,n,a){var e=r(s),t=p(e,n);return o({},t,{value:a.value,initializer:a.initializer,get:a.get||t.get,set:a.set||t.set})}function t(){for(var s=arguments.length,n=Array(s),t=0;t<s;t++)n[t]=arguments[t];return a.i(l.d)(e,n)}n.a=t;var l=a(15),o=Object.assign||function(s){for(var n=1;n<arguments.length;n++){var a=arguments[n];for(var e in a)Object.prototype.hasOwnProperty.call(a,e)&&(s[e]=a[e])}return s},r=Object.getPrototypeOf,p=Object.getOwnPropertyDescriptor},function(s,n,a){"use strict";function e(s,n,a){return n in s?Object.defineProperty(s,n,{value:a,enumerable:!0,configurable:!0,writable:!0}):s[n]=a,s}function t(s,n){return n===Object(n)?n:s[n]||(s[n]={})}function l(s,n,a,e,t){var l=n.apply(s,a);return e[t]=l,l}function o(s){var n=void 0,a=void 0;return s.value?(n=s.value,a="value"):s.get?(n=s.get,a="get"):s.set&&(n=s.set,a="set"),{fn:n,wrapKey:a}}function r(s,n,a){var r=o(a),p=r.fn,c=r.wrapKey,d=new WeakMap,h=Object.create(null),u=Object.create(null),g=0;return i({},a,e({},c,function(){for(var s=arguments.length,n=Array(s),a=0;a<s;a++)n[a]=arguments[a];for(var e="0",o=0,r=n.length;o<r;o++){var c=n[o],i=t(u,c),m=d.get(i);void 0===m&&(m=++g,d.set(i,m)),e+=m}return h[e]||l(this,p,arguments,h,e)}))}function p(){a.i(c.h)("@memoize is deprecated and will be removed shortly. Use @memoize from lodash-decorators.\n\n  https://www.npmjs.com/package/lodash-decorators");for(var s=arguments.length,n=Array(s),e=0;e<s;e++)n[e]=arguments[e];return a.i(c.d)(r,n)}n.a=p;var c=a(15),i=Object.assign||function(s){for(var n=1;n<arguments.length;n++){var a=arguments[n];for(var e in a)Object.prototype.hasOwnProperty.call(a,e)&&(s[e]=a[e])}return s}},function(s,n,a){"use strict";function e(s){return"[object Symbol]"===Object.prototype.toString.call(s)&&"object"===(void 0===s?"undefined":p(s))}function t(s,n){if(e(s)){do{if(n===Object.prototype)return void 0!==n[s];if(n.hasOwnProperty(s))return!0}while(n=i(n));return!1}return s in n}function l(s,n){if(!n.length)throw new SyntaxError("@mixin() class "+s.name+" requires at least one mixin as an argument");for(var e=0,l=n.length;e<l;e++)for(var o=a.i(r.f)(n[e]),p=a.i(r.g)(o),i=0,d=p.length;i<d;i++){var h=p[i];t(h,s.prototype)||c(s.prototype,h,o[h])}}function o(){for(var s=arguments.length,n=Array(s),e=0;e<s;e++)n[e]=arguments[e];return a.i(r.h)("@mixin is deprecated and will be removed shortly. Use @mixin from lodash-decorators.\n\n  https://www.npmjs.com/package/lodash-decorators"),"function"==typeof n[0]?l(n[0],[]):function(s){return l(s,n)}}n.a=o;var r=a(15),p="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(s){return typeof s}:function(s){return s&&"function"==typeof Symbol&&s.constructor===Symbol&&s!==Symbol.prototype?"symbol":typeof s},c=Object.defineProperty,i=Object.getPrototypeOf},function(s,n,a){"use strict";function e(s,n,a){return a.configurable=!1,a}function t(){for(var s=arguments.length,n=Array(s),t=0;t<s;t++)n[t]=arguments[t];return a.i(l.d)(e,n)}n.a=t;var l=a(15)},function(s,n,a){"use strict";function e(s,n,a){return a.enumerable=!1,a}function t(){for(var s=arguments.length,n=Array(s),t=0;t<s;t++)n[t]=arguments[t];return a.i(l.d)(e,n)}n.a=t;var l=a(15)},function(s,n,a){"use strict";function e(s,n){if(!(s instanceof n))throw new TypeError("Cannot call a class as a function")}function t(s){return s.hasOwnProperty("value")?"data":s.hasOwnProperty("get")||s.hasOwnProperty("set")?"accessor":"data"}function l(s,n,a){a.assert(s.length===n.length)}function o(s,n,a){var e=u(s.value),t=u(n.value);if("undefined"===e&&"undefined"===t&&a.error("descriptor values are both undefined. (class properties are are not currently supported)'"),e!==t){("function"===t&&void 0===e||void 0!==e)&&a.error('value types do not match. {parent} is "'+e+'", {child} is "'+t+'"')}switch(t){case"function":l(s.value,n.value,a);break;default:a.error('Unexpected error. Please file a bug with: {parent} is "'+e+'", {child} is "'+t+'"')}}function r(s,n,a){var e="function"==typeof s.get,t="function"==typeof n.get,o="function"==typeof s.set,r="function"==typeof n.set;(e||t)&&(!e&&o&&a.error("{parent} is setter but {child} is getter"),!t&&r&&a.error("{parent} is getter but {child} is setter"),l(s.get,n.get,a)),(o||r)&&(!o&&e&&a.error("{parent} is getter but {child} is setter"),!r&&t&&a.error("{parent} is setter but {child} is getter"),l(s.set,n.set,a))}function p(s,n,a){var e=t(s),l=t(n);switch(e!==l&&a.error('descriptor types do not match. {parent} is "'+e+'", {child} is "'+l+'"'),l){case"data":o(s,n,a);break;case"accessor":r(s,n,a)}}function c(s,n){for(var a=0,e=j.length;a<e;a++){var t=j[a],l=t(n);if(l in s)return l}return null}function i(s,n,a){a.key=n;var e=Object.getPrototypeOf(s),t=Object.getOwnPropertyDescriptor(e,n),l=new b(e,s,t,a);if(void 0===t){var o=c(e,n),r=o?'\n\n  Did you mean "'+o+'"?':"";l.error("No descriptor matching {child} was found on the prototype chain."+r)}return p(t,a,l),a}function d(){for(var s=arguments.length,n=Array(s),e=0;e<s;e++)n[e]=arguments[e];return a.i(h.d)(i,n)}n.a=d;var h=a(15),u="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(s){return typeof s}:function(s){return s&&"function"==typeof Symbol&&s.constructor===Symbol&&s!==Symbol.prototype?"symbol":typeof s},g=function(){function s(s,n){for(var a=0;a<n.length;a++){var e=n[a];e.enumerable=e.enumerable||!1,e.configurable=!0,"value"in e&&(e.writable=!0),Object.defineProperty(s,e.key,e)}}return function(n,a,e){return a&&s(n.prototype,a),e&&s(n,e),n}}(),m=/^function ([_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*)?(\([^\)]*\))[\s\S]+$/,b=function(){function s(n,a,t,l){e(this,s),this.parentKlass=n,this.childKlass=a,this.parentDescriptor=t,this.childDescriptor=l}return g(s,[{key:"_getTopic",value:function(s){return void 0===s?null:"value"in s?s.value:"get"in s?s.get:"set"in s?s.set:void 0}},{key:"_extractTopicSignature",value:function(s){switch(void 0===s?"undefined":u(s)){case"function":return this._extractFunctionSignature(s);default:return this.key}}},{key:"_extractFunctionSignature",value:function(s){var n=this;return s.toString().replace(m,function(s){return(arguments.length>1&&void 0!==arguments[1]?arguments[1]:n.key)+arguments[2]})}},{key:"key",get:function(){return this.childDescriptor.key}},{key:"parentNotation",get:function(){return this.parentKlass.constructor.name+"#"+this.parentPropertySignature}},{key:"childNotation",get:function(){return this.childKlass.constructor.name+"#"+this.childPropertySignature}},{key:"parentTopic",get:function(){return this._getTopic(this.parentDescriptor)}},{key:"childTopic",get:function(){return this._getTopic(this.childDescriptor)}},{key:"parentPropertySignature",get:function(){return this._extractTopicSignature(this.parentTopic)}},{key:"childPropertySignature",get:function(){return this._extractTopicSignature(this.childTopic)}}]),g(s,[{key:"assert",value:function(s){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";!0!==s&&this.error("{child} does not properly override {parent}"+n)}},{key:"error",value:function(s){var n=this;throw s=s.replace("{parent}",function(s){return n.parentNotation}).replace("{child}",function(s){return n.childNotation}),new SyntaxError(s)}}]),s}(),j=[function(s){return s.toLowerCase()},function(s){return s.toUpperCase()},function(s){return s+"s"},function(s){return s.slice(0,-1)},function(s){return s.slice(1,s.length)}]},function(s,n,a){"use strict";function e(s,n,e,c){var i=r(c,3),d=i[0],h=void 0===d?null:d,u=i[1],g=void 0!==u&&u,m=i[2],b=void 0===m?p:m;if(!t.__enabled)return t.__warned||(b.warn("console.profile is not supported. All @profile decorators are disabled."),t.__warned=!0),e;var j=e.value;if(null===h&&(h=s.constructor.name+"."+n),"function"!=typeof j)throw new SyntaxError("@profile can only be used on functions, not: "+j);return o({},e,{value:function(){var s=Date.now(),n=a.i(l.c)(this);(!0===g&&!n.profileLastRan||!1===g||"number"==typeof g&&s-n.profileLastRan>g||"function"==typeof g&&g.apply(this,arguments))&&(b.profile(h),n.profileLastRan=s);try{return j.apply(this,arguments)}finally{b.profileEnd(h)}}})}function t(){for(var s=arguments.length,n=Array(s),t=0;t<s;t++)n[t]=arguments[t];return a.i(l.d)(e,n)}n.a=t;var l=a(15),o=Object.assign||function(s){for(var n=1;n<arguments.length;n++){var a=arguments[n];for(var e in a)Object.prototype.hasOwnProperty.call(a,e)&&(s[e]=a[e])}return s},r=function(){function s(s,n){var a=[],e=!0,t=!1,l=void 0;try{for(var o,r=s[Symbol.iterator]();!(e=(o=r.next()).done)&&(a.push(o.value),!n||a.length!==n);e=!0);}catch(s){t=!0,l=s}finally{try{!e&&r.return&&r.return()}finally{if(t)throw l}}return a}return function(n,a){if(Array.isArray(n))return n;if(Symbol.iterator in Object(n))return s(n,a);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),p=(console,{profile:console.profile?a.i(l.a)(console.profile,console):function(){},profileEnd:console.profileEnd?a.i(l.a)(console.profileEnd,console):function(){},warn:l.b});t.__enabled=!!console.profile,t.__warned=!1},function(s,n,a){"use strict";function e(s,n,a){return a.writable=!1,a}function t(){for(var s=arguments.length,n=Array(s),t=0;t<s;t++)n[t]=arguments[t];return a.i(l.d)(e,n)}n.a=t;var l=a(15)},function(s,n,a){"use strict";function e(){}function t(s,n,a){if("object"===("undefined"==typeof console?"undefined":c(console))){var t=console.warn;console.warn=e;var l=n.apply(s,a);return console.warn=t,l}return n.apply(s,a)}function l(s,n,a){return p({},a,{value:function(){return t(this,a.value,arguments)}})}function o(){for(var s=arguments.length,n=Array(s),e=0;e<s;e++)n[e]=arguments[e];return a.i(r.d)(l,n)}n.a=o;var r=a(15),p=Object.assign||function(s){for(var n=1;n<arguments.length;n++){var a=arguments[n];for(var e in a)Object.prototype.hasOwnProperty.call(a,e)&&(s[e]=a[e])}return s},c="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(s){return typeof s}:function(s){return s&&"function"==typeof Symbol&&s.constructor===Symbol&&s!==Symbol.prototype?"symbol":typeof s}},function(s,n,a){"use strict";function e(s,n,e,t){var c=r(t,2),i=c[0],d=void 0===i?p:i,h=c[1],u=void 0===h?{}:h,g=e.value;if("function"!=typeof g)throw new SyntaxError("Only functions can be throttled");return!1!==u.leading&&(u.leading=!0),!1!==u.trailing&&(u.trailing=!0),o({},e,{value:function(){var s=this,e=a.i(l.c)(this),t=e.throttleTimeoutIds,o=e.throttlePreviousTimestamps,r=t[n],p=o[n]||0,c=Date.now();u.trailing&&(e.throttleTrailingArgs=arguments),p||!1!==u.leading||(p=c);var i=d-(c-p);i<=0?(clearTimeout(r),delete t[n],o[n]=c,g.apply(this,arguments)):!r&&u.trailing&&(t[n]=setTimeout(function(){o[n]=!1===u.leading?0:Date.now(),delete t[n],g.apply(s,e.throttleTrailingArgs),e.throttleTrailingArgs=null},i))}})}function t(){a.i(l.h)("@throttle is deprecated and will be removed shortly. Use @throttle from lodash-decorators.\n\n  https://www.npmjs.com/package/lodash-decorators");for(var s=arguments.length,n=Array(s),t=0;t<s;t++)n[t]=arguments[t];return a.i(l.d)(e,n)}n.a=t;var l=a(15),o=Object.assign||function(s){for(var n=1;n<arguments.length;n++){var a=arguments[n];for(var e in a)Object.prototype.hasOwnProperty.call(a,e)&&(s[e]=a[e])}return s},r=function(){function s(s,n){var a=[],e=!0,t=!1,l=void 0;try{for(var o,r=s[Symbol.iterator]();!(e=(o=r.next()).done)&&(a.push(o.value),!n||a.length!==n);e=!0);}catch(s){t=!0,l=s}finally{try{!e&&r.return&&r.return()}finally{if(t)throw l}}return a}return function(n,a){if(Array.isArray(n))return n;if(Symbol.iterator in Object(n))return s(n,a);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),p=300},function(s,n,a){"use strict";function e(s,n,a,e){var t=r(e,2),l=t[0],p=void 0===l?null:l,d=t[1],h=void 0===d?c:d,u=a.value;if(null===p&&(p=s.constructor.name+"."+n),"function"!=typeof u)throw new SyntaxError("@time can only be used on functions, not: "+u);return o({},a,{value:function(){var s=p+"-"+i;i++,h.time(s);try{return u.apply(this,arguments)}finally{h.timeEnd(s)}}})}function t(){for(var s=arguments.length,n=Array(s),t=0;t<s;t++)n[t]=arguments[t];return a.i(l.d)(e,n)}n.a=t;var l=a(15),o=Object.assign||function(s){for(var n=1;n<arguments.length;n++){var a=arguments[n];for(var e in a)Object.prototype.hasOwnProperty.call(a,e)&&(s[e]=a[e])}return s},r=function(){function s(s,n){var a=[],e=!0,t=!1,l=void 0;try{for(var o,r=s[Symbol.iterator]();!(e=(o=r.next()).done)&&(a.push(o.value),!n||a.length!==n);e=!0);}catch(s){t=!0,l=s}finally{try{!e&&r.return&&r.return()}finally{if(t)throw l}}return a}return function(n,a){if(Array.isArray(n))return n;if(Symbol.iterator in Object(n))return s(n,a);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),p={},c={time:console.time?console.time.bind(console):function(s){p[s]=new Date},timeEnd:console.timeEnd?console.timeEnd.bind(console):function(s){var n=new Date,a=n-p[s];delete p[s],console.log(s+": "+a+"ms")}},i=0},function(s,n,a){a(14)(a(48))},function(s,n,a){a(14)(a(49))},function(s,n,a){s.exports=a(65)()},function(s,n,a){"use strict";function e(s,n){return Object.prototype.hasOwnProperty.call(s,n)}s.exports=function(s,n,a,l){n=n||"&",a=a||"=";var o={};if("string"!=typeof s||0===s.length)return o;var r=/\+/g;s=s.split(n);var p=1e3;l&&"number"==typeof l.maxKeys&&(p=l.maxKeys);var c=s.length;p>0&&c>p&&(c=p);for(var i=0;i<c;++i){var d,h,u,g,m=s[i].replace(r,"%20"),b=m.indexOf(a);b>=0?(d=m.substr(0,b),h=m.substr(b+1)):(d=m,h=""),u=decodeURIComponent(d),g=decodeURIComponent(h),e(o,u)?t(o[u])?o[u].push(g):o[u]=[o[u],g]:o[u]=g}return o};var t=Array.isArray||function(s){return"[object Array]"===Object.prototype.toString.call(s)}},function(s,n,a){"use strict";function e(s,n){if(s.map)return s.map(n);for(var a=[],e=0;e<s.length;e++)a.push(n(s[e],e));return a}var t=function(s){switch(typeof s){case"string":return s;case"boolean":return s?"true":"false";case"number":return isFinite(s)?s:"";default:return""}};s.exports=function(s,n,a,r){return n=n||"&",a=a||"=",null===s&&(s=void 0),"object"==typeof s?e(o(s),function(o){var r=encodeURIComponent(t(o))+a;return l(s[o])?e(s[o],function(s){return r+encodeURIComponent(t(s))}).join(n):r+encodeURIComponent(t(s[o]))}).join(n):r?encodeURIComponent(t(r))+a+encodeURIComponent(t(s)):""};var l=Array.isArray||function(s){return"[object Array]"===Object.prototype.toString.call(s)},o=Object.keys||function(s){var n=[];for(var a in s)Object.prototype.hasOwnProperty.call(s,a)&&n.push(a);return n}},function(s,n){s.exports=".footer-container {\n  background: #F8F8F8; }\n  .footer-container .footer-body {\n    max-width: 1280px;\n    margin: 0 auto;\n    box-sizing: border-box;\n    padding: 40px 40px 0; }\n    @media screen and (max-width: 640px) {\n      .footer-container .footer-body {\n        padding-left: 20px;\n        padding-right: 20px; } }\n    .footer-container .footer-body img {\n      width: 125px;\n      height: 26px;\n      margin-bottom: 28px;\n      margin-right: 20px;\n      vertical-align: middle; }\n    .footer-container .footer-body .apache {\n      width: 50px;\n      height: 50px; }\n    .footer-container .footer-body .cols-container .col {\n      display: inline-block;\n      box-sizing: border-box;\n      vertical-align: top; }\n    .footer-container .footer-body .cols-container .col-12 {\n      width: 50%;\n      padding-right: 125px; }\n    .footer-container .footer-body .cols-container .col-6 {\n      width: 25%; }\n    .footer-container .footer-body .cols-container h3 {\n      font-family: Avenir-Heavy;\n      font-size: 18px;\n      color: #333;\n      line-height: 18px; }\n    .footer-container .footer-body .cols-container p {\n      font-family: Avenir-Medium;\n      font-size: 12px;\n      color: #999;\n      line-height: 18px; }\n    .footer-container .footer-body .cols-container dl {\n      font-family: Avenir-Heavy;\n      line-height: 18px; }\n    .footer-container .footer-body .cols-container dt {\n      font-weight: bold;\n      font-size: 18px;\n      color: #333;\n      margin-bottom: 20px; }\n    .footer-container .footer-body .cols-container dd {\n      padding: 0;\n      margin: 0; }\n      .footer-container .footer-body .cols-container dd a {\n        text-decoration: none;\n        display: block;\n        font-size: 14px;\n        color: #999;\n        margin: 10px 0; }\n      .footer-container .footer-body .cols-container dd a:hover {\n        color: #2DACEC; }\n    .footer-container .footer-body .copyright {\n      border-top: 1px solid #ccc;\n      min-height: 60px;\n      line-height: 20px;\n      text-align: center;\n      font-family: Avenir-Medium;\n      font-size: 12px;\n      color: #999;\n      display: flex;\n      align-items: center; }\n      .footer-container .footer-body .copyright span {\n        display: inline-block;\n        margin: 0 auto; }\n\n@media screen and (max-width: 640px) {\n  .footer-container .footer-body .cols-container .col {\n    width: 100%;\n    text-align: center;\n    padding: 0; } }\n"},function(s,n){s.exports=".header-container {\n  position: fixed;\n  left: 0;\n  top: 0;\n  width: 100%;\n  z-index: 1000;\n  background-color: #fff; }\n  .header-container-primary {\n    background-color: transparent; }\n  .header-container-normal {\n    background-color: #fff;\n    box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.08); }\n  .header-container .header-body {\n    max-width: 1280px;\n    margin: 0 auto;\n    height: 66px;\n    line-height: 66px; }\n    .header-container .header-body .logo {\n      margin-left: 40px;\n      width: 96px;\n      vertical-align: sub; }\n    .header-container .header-body .header-menu {\n      float: right; }\n      .header-container .header-body .header-menu .header-menu-toggle {\n        display: none;\n        width: 19px;\n        margin-right: 40px;\n        margin-top: 18px;\n        cursor: pointer; }\n    .header-container .header-body ul {\n      padding: 0;\n      margin: 0; }\n    .header-container .header-body li {\n      display: inline-block;\n      margin-right: 40px; }\n    .header-container .header-body .menu-item {\n      font-family: Avenir-Heavy;\n      font-size: 14px; }\n    .header-container .header-body .menu-item-primary a {\n      color: #fff;\n      opacity: 0.6;\n      font-family: Avenir-Medium; }\n    .header-container .header-body .menu-item-primary:hover a {\n      opacity: 1; }\n    .header-container .header-body .menu-item-primary-active a {\n      opacity: 1; }\n    .header-container .header-body .menu-item-normal a {\n      color: #333;\n      opacity: 0.6;\n      font-family: Avenir-Medium; }\n    .header-container .header-body .menu-item-normal:hover a {\n      opacity: 1; }\n    .header-container .header-body .menu-item-normal-active a {\n      opacity: 1; }\n    .header-container .header-body .language-switch {\n      float: right;\n      display: inline-block;\n      box-sizing: border-box;\n      width: 24px;\n      height: 24px;\n      line-height: 20px;\n      margin-top: 21px;\n      margin-right: 40px;\n      text-align: center;\n      border-radius: 2px;\n      cursor: pointer;\n      font-family: PingFangSC-Medium;\n      font-size: 14px;\n      opacity: 0.6; }\n      .header-container .header-body .language-switch:hover {\n        opacity: 1; }\n    .header-container .header-body .language-switch-primary {\n      border: 1px solid #FFF;\n      color: #FFF; }\n    .header-container .header-body .language-switch-normal {\n      border: 1px solid #333;\n      color: #333; }\n\n@media screen and (max-width: 640px) {\n  .header-container .header-body .logo {\n    margin-left: 20px; }\n  .header-container .header-body .language-switch {\n    margin-right: 20px; }\n  .header-container .header-body .header-menu ul {\n    display: none; }\n  .header-container .header-body .header-menu .header-menu-toggle {\n    display: inline-block;\n    margin-right: 20px; }\n  .header-container .header-body .header-menu-open ul {\n    background-color: #f8f8f8;\n    display: inline-block;\n    position: absolute;\n    right: 0;\n    top: 66px;\n    z-index: 100; }\n  .header-container .header-body .header-menu-open li {\n    width: 200px;\n    display: list-item;\n    padding-left: 30px;\n    list-style: none;\n    line-height: 40px;\n    margin-right: 0; }\n    .header-container .header-body .header-menu-open li a {\n      color: #333;\n      display: inline-block;\n      width: 100%; }\n    .header-container .header-body .header-menu-open li:hover {\n      background: #8755FF; }\n      .header-container .header-body .header-menu-open li:hover a {\n        color: #fff;\n        opactiy: 1; }\n  .header-container .header-body .header-menu-open .menu-item-primary-active, .header-container .header-body .header-menu-open .menu-item-normal-active {\n    background: #8755FF; }\n    .header-container .header-body .header-menu-open .menu-item-primary-active a, .header-container .header-body .header-menu-open .menu-item-normal-active a {\n      color: #fff;\n      opactiy: 1; } }\n"},function(s,n,a){"use strict";function e(s){return s&&s.__esModule?s:{default:s}}Object.defineProperty(n,"__esModule",{value:!0});var t=Object.assign||function(s){for(var n=1;n<arguments.length;n++){var a=arguments[n];for(var e in a)Object.prototype.hasOwnProperty.call(a,e)&&(s[e]=a[e])}return s},l=a(22),o=e(l),r=a(59),p=e(r),c=a(52),i=e(c),d={},h=void 0;n.default={unmount:function(){d={}},register:function(s,n){d[s]=n},unregister:function(s){delete d[s]},get:function(s){return d[s]||document.getElementById(s)||document.getElementsByName(s)[0]||document.getElementsByClassName(s)[0]},setActiveLink:function(s){return h=s},getActiveLink:function(){return h},scrollTo:function(s,n){var a=this.get(s);if(!a)return void console.warn("target Element not found");n=t({},n,{absolute:!1});var e=n.containerId,l=n.container,r=void 0;r=e?document.getElementById(e):l&&l.nodeType?l:document,i.default.registered.begin&&i.default.registered.begin(s,a),n.absolute=!0;var c=o.default.scrollOffset(r,a)+(n.offset||0);if(!n.smooth)return r===document?window.scrollTo(0,c):r.scrollTop=c,void(i.default.registered.end&&i.default.registered.end(s,a));p.default.animateTopScroll(c,n,s,a)}}},function(s,n,a){"use strict";Object.defineProperty(n,"__esModule",{value:!0});n.addPassiveEventListener=function(s,n,a){var e=function(){var s=!1;try{var n=Object.defineProperty({},"passive",{get:function(){s=!0}});window.addEventListener("test",null,n)}catch(s){}return s}();s.addEventListener(n,a,!!e&&{passive:!0})},n.removePassiveEventListener=function(s,n,a){s.removeEventListener(n,a)}},function(s,n,a){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var e={registered:{},scrollEvent:{register:function(s,n){e.registered[s]=n},remove:function(s){e.registered[s]=null}}};n.default=e},function(s,n,a){"use strict";function e(s){return s&&s.__esModule?s:{default:s}}function t(s,n){if(!(s instanceof n))throw new TypeError("Cannot call a class as a function")}function l(s,n){if(!s)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!n||"object"!=typeof n&&"function"!=typeof n?s:n}function o(s,n){if("function"!=typeof n&&null!==n)throw new TypeError("Super expression must either be null or a function, not "+typeof n);s.prototype=Object.create(n&&n.prototype,{constructor:{value:s,enumerable:!1,writable:!0,configurable:!0}}),n&&(Object.setPrototypeOf?Object.setPrototypeOf(s,n):s.__proto__=n)}Object.defineProperty(n,"__esModule",{value:!0});var r=Object.assign||function(s){for(var n=1;n<arguments.length;n++){var a=arguments[n];for(var e in a)Object.prototype.hasOwnProperty.call(a,e)&&(s[e]=a[e])}return s},p=function(){function s(s,n){for(var a=0;a<n.length;a++){var e=n[a];e.enumerable=e.enumerable||!1,e.configurable=!0,"value"in e&&(e.writable=!0),Object.defineProperty(s,e.key,e)}}return function(n,a,e){return a&&s(n.prototype,a),e&&s(n,e),n}}(),c=a(0),i=e(c),d=a(2),h=(e(d),a(22)),u=(e(h),a(54)),g=e(u),m=a(50),b=e(m),j=a(45),f=e(j),v=a(61),y=e(v),x={to:f.default.string.isRequired,containerId:f.default.string,container:f.default.object,activeClass:f.default.string,spy:f.default.bool,smooth:f.default.oneOfType([f.default.bool,f.default.string]),offset:f.default.number,delay:f.default.number,isDynamic:f.default.bool,onClick:f.default.func,duration:f.default.oneOfType([f.default.number,f.default.func]),absolute:f.default.bool,onSetActive:f.default.func,onSetInactive:f.default.func,ignoreCancelEvents:f.default.bool,hashSpy:f.default.bool};n.default=function(s,n){var a=n||b.default,e=function(n){function e(s){t(this,e);var n=l(this,(e.__proto__||Object.getPrototypeOf(e)).call(this,s));return c.call(n),n.state={active:!1},n}return o(e,n),p(e,[{key:"getScrollSpyContainer",value:function(){var s=this.props.containerId,n=this.props.container;return s&&!n?document.getElementById(s):n&&n.nodeType?n:document}},{key:"componentDidMount",value:function(){if(this.props.spy||this.props.hashSpy){var s=this.getScrollSpyContainer();g.default.isMounted(s)||g.default.mount(s),this.props.hashSpy&&(y.default.isMounted()||y.default.mount(a),y.default.mapContainer(this.props.to,s)),g.default.addSpyHandler(this.spyHandler,s),this.setState({container:s})}}},{key:"componentWillUnmount",value:function(){g.default.unmount(this.stateHandler,this.spyHandler)}},{key:"render",value:function(){var n="";n=this.state&&this.state.active?((this.props.className||"")+" "+(this.props.activeClass||"active")).trim():this.props.className;var a=r({},this.props);for(var e in x)a.hasOwnProperty(e)&&delete a[e];return a.className=n,a.onClick=this.handleClick,i.default.createElement(s,a)}}]),e}(i.default.PureComponent),c=function(){var s=this;this.scrollTo=function(n,e){a.scrollTo(n,r({},s.state,e))},this.handleClick=function(n){s.props.onClick&&s.props.onClick(n),n.stopPropagation&&n.stopPropagation(),n.preventDefault&&n.preventDefault(),s.scrollTo(s.props.to,s.props)},this.spyHandler=function(n){var e=s.getScrollSpyContainer();if(!y.default.isMounted()||y.default.isInitialized()){var t=s.props.to,l=null,o=0,r=0,p=0;if(e.getBoundingClientRect){p=e.getBoundingClientRect().top}if(!l||s.props.isDynamic){if(!(l=a.get(t)))return;var c=l.getBoundingClientRect();o=c.top-p+n,r=o+c.height}var i=n-s.props.offset,d=i>=Math.floor(o)&&i<Math.floor(r),h=i<Math.floor(o)||i>=Math.floor(r),u=a.getActiveLink();h&&(t===u&&a.setActiveLink(void 0),s.props.hashSpy&&y.default.getHash()===t&&y.default.changeHash(),s.props.spy&&s.state.active&&(s.setState({active:!1}),s.props.onSetInactive&&s.props.onSetInactive(t,l))),!d||u===t&&!1!==s.state.active||(a.setActiveLink(t),s.props.hashSpy&&y.default.changeHash(t),s.props.spy&&(s.setState({active:!0}),s.props.onSetActive&&s.props.onSetActive(t,l)))}}};return e.propTypes=x,e.defaultProps={offset:0},e}},function(s,n,a){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var e=a(62),t=function(s){return s&&s.__esModule?s:{default:s}}(e),l=a(51),o=function(s){return(0,t.default)(s,66)},r={spyCallbacks:[],spySetState:[],scrollSpyContainers:[],mount:function(s){if(s){var n=o(function(n){r.scrollHandler(s)});r.scrollSpyContainers.push(s),(0,l.addPassiveEventListener)(s,"scroll",n)}},isMounted:function(s){return-1!==r.scrollSpyContainers.indexOf(s)},currentPositionY:function(s){if(s===document){var n=void 0!==window.pageXOffset,a="CSS1Compat"===(document.compatMode||"");return n?window.pageYOffset:a?document.documentElement.scrollTop:document.body.scrollTop}return s.scrollTop},scrollHandler:function(s){(r.scrollSpyContainers[r.scrollSpyContainers.indexOf(s)].spyCallbacks||[]).forEach(function(n){return n(r.currentPositionY(s))})},addStateHandler:function(s){r.spySetState.push(s)},addSpyHandler:function(s,n){var a=r.scrollSpyContainers[r.scrollSpyContainers.indexOf(n)];a.spyCallbacks||(a.spyCallbacks=[]),a.spyCallbacks.push(s),s(r.currentPositionY(n))},updateStates:function(){r.spySetState.forEach(function(s){return s()})},unmount:function(s,n){r.scrollSpyContainers.forEach(function(s){return s.spyCallbacks&&s.spyCallbacks.length&&s.spyCallbacks.splice(s.spyCallbacks.indexOf(n),1)}),r.spySetState&&r.spySetState.length&&r.spySetState.splice(r.spySetState.indexOf(s),1),document.removeEventListener("scroll",r.scrollHandler)},update:function(){return r.scrollSpyContainers.forEach(function(s){return r.scrollHandler(s)})}};n.default=r},function(s,n,a){"use strict";function e(s){return s&&s.__esModule?s:{default:s}}function t(s){var n=s.text,a=s.img,e=(0,p.default)({bar:!0});return o.default.createElement("div",{className:e},o.default.createElement("div",{className:"bar-body"},o.default.createElement("img",{src:a,className:"front-img"}),o.default.createElement("span",null,n),o.default.createElement("img",{src:a,className:"back-img"})))}Object.defineProperty(n,"__esModule",{value:!0}),n.default=t;var l=a(0),o=e(l),r=a(19),p=e(r);a(57)},,function(s,n,a){a(14)(a(58))},function(s,n){s.exports=".bar {\n  margin-top: 66px;\n  background-image: linear-gradient(-90deg, #03DDE4 0%, #30AFED 51%, #8755FF 100%); }\n  .bar .bar-body {\n    max-width: 1280px;\n    margin: 0 auto;\n    height: 200px;\n    line-height: 200px;\n    font-family: Avenir-Heavy;\n    font-size: 36px;\n    color: #FFF;\n    position: relative; }\n    .bar .bar-body::before {\n      content: '';\n      height: 100%;\n      position: absolute;\n      left: 42px;\n      top: 0;\n      opacity: 0.3;\n      border-left: 1px solid #FFFFFF; }\n    .bar .bar-body::after {\n      content: '';\n      height: 16px;\n      position: absolute;\n      left: 40px;\n      top: 50%;\n      margin: auto 0;\n      border-left: 4px solid #FFFFFF; }\n    .bar .bar-body .front-img {\n      width: 80px;\n      height: 80px;\n      vertical-align: middle;\n      margin: 0 28px 0 70px; }\n    .bar .bar-body .back-img {\n      width: 160px;\n      height: 160px;\n      position: absolute;\n      right: 168px;\n      bottom: 0;\n      opacity: 0.15; }\n    @media screen and (max-width: 640px) {\n      .bar .bar-body::before {\n        left: 22px; }\n      .bar .bar-body::after {\n        left: 20px; }\n      .bar .bar-body .front-img {\n        margin-left: 50px; } }\n"},function(s,n,a){"use strict";function e(s){return s&&s.__esModule?s:{default:s}}Object.defineProperty(n,"__esModule",{value:!0});var t=Object.assign||function(s){for(var n=1;n<arguments.length;n++){var a=arguments[n];for(var e in a)Object.prototype.hasOwnProperty.call(a,e)&&(s[e]=a[e])}return s},l=a(22),o=(e(l),a(73)),r=e(o),p=a(72),c=e(p),i=a(52),d=e(i),h=function(s){return r.default[s.smooth]||r.default.defaultEasing},u=function(s){return"function"==typeof s?s:function(){return s}},g=function(){if("undefined"!=typeof window)return window.requestAnimationFrame||window.webkitRequestAnimationFrame},m=function(){return g()||function(s,n,a){window.setTimeout(s,a||1e3/60,(new Date).getTime())}}(),b=function(){return{currentPositionY:0,startPositionY:0,targetPositionY:0,progress:0,duration:0,cancel:!1,target:null,containerElement:null,to:null,start:null,deltaTop:null,percent:null,delayTimeout:null}},j=function(s){var n=s.data.containerElement;if(n&&n!==document&&n!==document.body)return n.scrollTop;var a=void 0!==window.pageXOffset,e="CSS1Compat"===(document.compatMode||"");return a?window.pageYOffset:e?document.documentElement.scrollTop:document.body.scrollTop},f=function(s){var n=s.data.containerElement;if(n&&n!==document&&n!==document.body)return Math.max(n.scrollHeight,n.offsetHeight,n.clientHeight);var a=document.body,e=document.documentElement;return Math.max(a.scrollHeight,a.offsetHeight,e.clientHeight,e.scrollHeight,e.offsetHeight)},v=function s(n,a,e){var t=a.data;if(!a.ignoreCancelEvents&&t.cancel)return void(d.default.registered.end&&d.default.registered.end(t.to,t.target,t.currentPositionY));if(t.deltaTop=Math.round(t.targetPositionY-t.startPositionY),null===t.start&&(t.start=e),t.progress=e-t.start,t.percent=t.progress>=t.duration?1:n(t.progress/t.duration),t.currentPositionY=t.startPositionY+Math.ceil(t.deltaTop*t.percent),t.containerElement&&t.containerElement!==document&&t.containerElement!==document.body?t.containerElement.scrollTop=t.currentPositionY:window.scrollTo(0,t.currentPositionY),t.percent<1){var l=s.bind(null,n,a);return void m.call(window,l)}d.default.registered.end&&d.default.registered.end(t.to,t.target,t.currentPositionY)},y=function(s){s.data.containerElement=s?s.containerId?document.getElementById(s.containerId):s.container&&s.container.nodeType?s.container:document:null},x=function(s,n,a,e){if(n.data=n.data||b(),window.clearTimeout(n.data.delayTimeout),c.default.subscribe(function(){n.data.cancel=!0}),y(n),n.data.start=null,n.data.cancel=!1,n.data.startPositionY=j(n),n.data.targetPositionY=n.absolute?s:s+n.data.startPositionY,n.data.startPositionY===n.data.targetPositionY)return void(d.default.registered.end&&d.default.registered.end(n.data.to,n.data.target,n.data.currentPositionY));n.data.deltaTop=Math.round(n.data.targetPositionY-n.data.startPositionY),n.data.duration=u(n.duration)(n.data.deltaTop),n.data.duration=isNaN(parseFloat(n.data.duration))?1e3:parseFloat(n.data.duration),n.data.to=a,n.data.target=e;var t=h(n),l=v.bind(null,t,n);if(n&&n.delay>0)return void(n.data.delayTimeout=window.setTimeout(function(){m.call(window,l)},n.delay));m.call(window,l)},k=function(s){return s=t({},s),s.data=s.data||b(),s.absolute=!0,s},w=function(s){x(0,k(s))},S=function(s,n){x(s,k(n))},C=function(s){s=k(s),y(s),x(f(s),s)},R=function(s,n){n=k(n),y(n),x(j(n)+s,n)};n.default={animateTopScroll:x,getAnimationType:h,scrollToTop:w,scrollToBottom:C,scrollTo:S,scrollMore:R}},function(s,n,a){"use strict";function e(s){return s&&s.__esModule?s:{default:s}}function t(s,n){if(!(s instanceof n))throw new TypeError("Cannot call a class as a function")}function l(s,n){if(!s)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!n||"object"!=typeof n&&"function"!=typeof n?s:n}function o(s,n){if("function"!=typeof n&&null!==n)throw new TypeError("Super expression must either be null or a function, not "+typeof n);s.prototype=Object.create(n&&n.prototype,{constructor:{value:s,enumerable:!1,writable:!0,configurable:!0}}),n&&(Object.setPrototypeOf?Object.setPrototypeOf(s,n):s.__proto__=n)}Object.defineProperty(n,"__esModule",{value:!0});var r=Object.assign||function(s){for(var n=1;n<arguments.length;n++){var a=arguments[n];for(var e in a)Object.prototype.hasOwnProperty.call(a,e)&&(s[e]=a[e])}return s},p=function(){function s(s,n){for(var a=0;a<n.length;a++){var e=n[a];e.enumerable=e.enumerable||!1,e.configurable=!0,"value"in e&&(e.writable=!0),Object.defineProperty(s,e.key,e)}}return function(n,a,e){return a&&s(n.prototype,a),e&&s(n,e),n}}(),c=a(0),i=e(c),d=a(2),h=(e(d),a(50)),u=e(h),g=a(45),m=e(g);n.default=function(s){var n=function(n){function a(s){t(this,a);var n=l(this,(a.__proto__||Object.getPrototypeOf(a)).call(this,s));return n.childBindings={domNode:null},n}return o(a,n),p(a,[{key:"componentDidMount",value:function(){if("undefined"==typeof window)return!1;this.registerElems(this.props.name)}},{key:"componentWillReceiveProps",value:function(s){this.props.name!==s.name&&this.registerElems(s.name)}},{key:"componentWillUnmount",value:function(){if("undefined"==typeof window)return!1;u.default.unregister(this.props.name)}},{key:"registerElems",value:function(s){u.default.register(s,this.childBindings.domNode)}},{key:"render",value:function(){return i.default.createElement(s,r({},this.props,{parentBindings:this.childBindings}))}}]),a}(i.default.Component);return n.propTypes={name:m.default.string,id:m.default.string},n}},function(s,n,a){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var e=(a(51),a(22)),t=function(s){return s&&s.__esModule?s:{default:s}}(e),l={mountFlag:!1,initialized:!1,scroller:null,containers:{},mount:function(s){this.scroller=s,this.handleHashChange=this.handleHashChange.bind(this),window.addEventListener("hashchange",this.handleHashChange),this.initStateFromHash(),this.mountFlag=!0},mapContainer:function(s,n){this.containers[s]=n},isMounted:function(){return this.mountFlag},isInitialized:function(){return this.initialized},initStateFromHash:function(){var s=this,n=this.getHash();n?window.setTimeout(function(){s.scrollTo(n,!0),s.initialized=!0},10):this.initialized=!0},scrollTo:function(s,n){var a=this.scroller;if(a.get(s)&&(n||s!==a.getActiveLink())){var e=this.containers[s]||document;a.scrollTo(s,{container:e})}},getHash:function(){return t.default.getHash()},changeHash:function(s){this.isInitialized()&&t.default.getHash()!==s&&t.default.pushHash(s)},handleHashChange:function(){this.scrollTo(this.getHash())},unmount:function(){this.scroller=null,this.containers=null,window.removeEventListener("hashchange",this.handleHashChange)}};n.default=l},function(s,n,a){(function(n){function a(s,n,a){function e(n){var a=m,e=b;return m=b=void 0,S=n,f=s.apply(e,a)}function l(s){return S=s,v=setTimeout(i,n),C?e(s):f}function o(s){var a=s-y,e=s-S,t=n-a;return R?k(t,j-e):t}function c(s){var a=s-y,e=s-S;return void 0===y||a>=n||a<0||R&&e>=j}function i(){var s=w();if(c(s))return d(s);v=setTimeout(i,o(s))}function d(s){return v=void 0,P&&m?e(s):(m=b=void 0,f)}function h(){void 0!==v&&clearTimeout(v),S=0,m=y=b=v=void 0}function u(){return void 0===v?f:d(w())}function g(){var s=w(),a=c(s);if(m=arguments,b=this,y=s,a){if(void 0===v)return l(y);if(R)return v=setTimeout(i,n),e(y)}return void 0===v&&(v=setTimeout(i,n)),f}var m,b,j,f,v,y,S=0,C=!1,R=!1,P=!0;if("function"!=typeof s)throw new TypeError(p);return n=r(n)||0,t(a)&&(C=!!a.leading,R="maxWait"in a,j=R?x(r(a.maxWait)||0,n):j,P="trailing"in a?!!a.trailing:P),g.cancel=h,g.flush=u,g}function e(s,n,e){var l=!0,o=!0;if("function"!=typeof s)throw new TypeError(p);return t(e)&&(l="leading"in e?!!e.leading:l,o="trailing"in e?!!e.trailing:o),a(s,n,{leading:l,maxWait:n,trailing:o})}function t(s){var n=typeof s;return!!s&&("object"==n||"function"==n)}function l(s){return!!s&&"object"==typeof s}function o(s){return"symbol"==typeof s||l(s)&&y.call(s)==i}function r(s){if("number"==typeof s)return s;if(o(s))return c;if(t(s)){var n="function"==typeof s.valueOf?s.valueOf():s;s=t(n)?n+"":n}if("string"!=typeof s)return 0===s?s:+s;s=s.replace(d,"");var a=u.test(s);return a||g.test(s)?m(s.slice(2),a?2:8):h.test(s)?c:+s}var p="Expected a function",c=NaN,i="[object Symbol]",d=/^\s+|\s+$/g,h=/^[-+]0x[0-9a-f]+$/i,u=/^0b[01]+$/i,g=/^0o[0-7]+$/i,m=parseInt,b="object"==typeof n&&n&&n.Object===Object&&n,j="object"==typeof self&&self&&self.Object===Object&&self,f=b||j||Function("return this")(),v=Object.prototype,y=v.toString,x=Math.max,k=Math.min,w=function(){return f.Date.now()};s.exports=e}).call(n,a(74))},function(s,n,a){(function(s){function a(s,n){for(var a=0,e=s.length-1;e>=0;e--){var t=s[e];"."===t?s.splice(e,1):".."===t?(s.splice(e,1),a++):a&&(s.splice(e,1),a--)}if(n)for(;a--;a)s.unshift("..");return s}function e(s,n){if(s.filter)return s.filter(n);for(var a=[],e=0;e<s.length;e++)n(s[e],e,s)&&a.push(s[e]);return a}var t=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/,l=function(s){return t.exec(s).slice(1)};n.resolve=function(){for(var n="",t=!1,l=arguments.length-1;l>=-1&&!t;l--){var o=l>=0?arguments[l]:s.cwd();if("string"!=typeof o)throw new TypeError("Arguments to path.resolve must be strings");o&&(n=o+"/"+n,t="/"===o.charAt(0))}return n=a(e(n.split("/"),function(s){return!!s}),!t).join("/"),(t?"/":"")+n||"."},n.normalize=function(s){var t=n.isAbsolute(s),l="/"===o(s,-1);return s=a(e(s.split("/"),function(s){return!!s}),!t).join("/"),s||t||(s="."),s&&l&&(s+="/"),(t?"/":"")+s},n.isAbsolute=function(s){return"/"===s.charAt(0)},n.join=function(){var s=Array.prototype.slice.call(arguments,0);return n.normalize(e(s,function(s,n){if("string"!=typeof s)throw new TypeError("Arguments to path.join must be strings");return s}).join("/"))},n.relative=function(s,a){function e(s){for(var n=0;n<s.length&&""===s[n];n++);for(var a=s.length-1;a>=0&&""===s[a];a--);return n>a?[]:s.slice(n,a-n+1)}s=n.resolve(s).substr(1),a=n.resolve(a).substr(1);for(var t=e(s.split("/")),l=e(a.split("/")),o=Math.min(t.length,l.length),r=o,p=0;p<o;p++)if(t[p]!==l[p]){r=p;break}for(var c=[],p=r;p<t.length;p++)c.push("..");return c=c.concat(l.slice(r)),c.join("/")},n.sep="/",n.delimiter=":",n.dirname=function(s){var n=l(s),a=n[0],e=n[1];return a||e?(e&&(e=e.substr(0,e.length-1)),a+e):"."},n.basename=function(s,n){var a=l(s)[2];return n&&a.substr(-1*n.length)===n&&(a=a.substr(0,a.length-n.length)),a},n.extname=function(s){return l(s)[3]};var o="b"==="ab".substr(-1)?function(s,n,a){return s.substr(n,a)}:function(s,n,a){return n<0&&(n=s.length+n),s.substr(n,a)}}).call(n,a(64))},function(s,n){function a(){throw new Error("setTimeout has not been defined")}function e(){throw new Error("clearTimeout has not been defined")}function t(s){if(i===setTimeout)return setTimeout(s,0);if((i===a||!i)&&setTimeout)return i=setTimeout,setTimeout(s,0);try{return i(s,0)}catch(n){try{return i.call(null,s,0)}catch(n){return i.call(this,s,0)}}}function l(s){if(d===clearTimeout)return clearTimeout(s);if((d===e||!d)&&clearTimeout)return d=clearTimeout,clearTimeout(s);try{return d(s)}catch(n){try{return d.call(null,s)}catch(n){return d.call(this,s)}}}function o(){m&&u&&(m=!1,u.length?g=u.concat(g):b=-1,g.length&&r())}function r(){if(!m){var s=t(o);m=!0;for(var n=g.length;n;){for(u=g,g=[];++b<n;)u&&u[b].run();b=-1,n=g.length}u=null,m=!1,l(s)}}function p(s,n){this.fun=s,this.array=n}function c(){}var i,d,h=s.exports={};!function(){try{i="function"==typeof setTimeout?setTimeout:a}catch(s){i=a}try{d="function"==typeof clearTimeout?clearTimeout:e}catch(s){d=e}}();var u,g=[],m=!1,b=-1;h.nextTick=function(s){var n=new Array(arguments.length-1);if(arguments.length>1)for(var a=1;a<arguments.length;a++)n[a-1]=arguments[a];g.push(new p(s,n)),1!==g.length||m||t(r)},p.prototype.run=function(){this.fun.apply(null,this.array)},h.title="browser",h.browser=!0,h.env={},h.argv=[],h.version="",h.versions={},h.on=c,h.addListener=c,h.once=c,h.off=c,h.removeListener=c,h.removeAllListeners=c,h.emit=c,h.prependListener=c,h.prependOnceListener=c,h.listeners=function(s){return[]},h.binding=function(s){throw new Error("process.binding is not supported")},h.cwd=function(){return"/"},h.chdir=function(s){throw new Error("process.chdir is not supported")},h.umask=function(){return 0}},function(s,n,a){"use strict";function e(){}var t=a(66);s.exports=function(){function s(s,n,a,e,l,o){if(o!==t){var r=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw r.name="Invariant Violation",r}}function n(){return s}s.isRequired=s;var a={array:s,bool:s,func:s,number:s,object:s,string:s,symbol:s,any:s,arrayOf:n,element:s,instanceOf:n,node:s,objectOf:n,oneOf:n,oneOfType:n,shape:n,exact:n};return a.checkPropTypes=e,a.PropTypes=a,a}},function(s,n,a){"use strict";s.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},function(s,n,a){"use strict";function e(s){return s&&s.__esModule?s:{default:s}}function t(s,n){if(!(s instanceof n))throw new TypeError("Cannot call a class as a function")}function l(s,n){if(!s)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!n||"object"!=typeof n&&"function"!=typeof n?s:n}function o(s,n){if("function"!=typeof n&&null!==n)throw new TypeError("Super expression must either be null or a function, not "+typeof n);s.prototype=Object.create(n&&n.prototype,{constructor:{value:s,enumerable:!1,writable:!0,configurable:!0}}),n&&(Object.setPrototypeOf?Object.setPrototypeOf(s,n):s.__proto__=n)}Object.defineProperty(n,"__esModule",{value:!0});var r=function(){function s(s,n){for(var a=0;a<n.length;a++){var e=n[a];e.enumerable=e.enumerable||!1,e.configurable=!0,"value"in e&&(e.writable=!0),Object.defineProperty(s,e.key,e)}}return function(n,a,e){return a&&s(n.prototype,a),e&&s(n,e),n}}(),p=a(0),c=e(p),i=a(53),d=e(i),h=function(s){function n(){return t(this,n),l(this,(n.__proto__||Object.getPrototypeOf(n)).apply(this,arguments))}return o(n,s),r(n,[{key:"render",value:function(){return c.default.createElement("input",this.props,this.props.children)}}]),n}(c.default.Component);n.default=(0,d.default)(h)},function(s,n,a){"use strict";function e(s){return s&&s.__esModule?s:{default:s}}function t(s,n){if(!(s instanceof n))throw new TypeError("Cannot call a class as a function")}function l(s,n){if(!s)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!n||"object"!=typeof n&&"function"!=typeof n?s:n}function o(s,n){if("function"!=typeof n&&null!==n)throw new TypeError("Super expression must either be null or a function, not "+typeof n);s.prototype=Object.create(n&&n.prototype,{constructor:{value:s,enumerable:!1,writable:!0,configurable:!0}}),n&&(Object.setPrototypeOf?Object.setPrototypeOf(s,n):s.__proto__=n)}Object.defineProperty(n,"__esModule",{value:!0});var r=Object.assign||function(s){for(var n=1;n<arguments.length;n++){var a=arguments[n];for(var e in a)Object.prototype.hasOwnProperty.call(a,e)&&(s[e]=a[e])}return s},p=function(){function s(s,n){for(var a=0;a<n.length;a++){var e=n[a];e.enumerable=e.enumerable||!1,e.configurable=!0,"value"in e&&(e.writable=!0),Object.defineProperty(s,e.key,e)}}return function(n,a,e){return a&&s(n.prototype,a),e&&s(n,e),n}}(),c=a(0),i=e(c),d=a(60),h=e(d),u=a(45),g=e(u),m=function(s){function n(){return t(this,n),l(this,(n.__proto__||Object.getPrototypeOf(n)).apply(this,arguments))}return o(n,s),p(n,[{key:"render",value:function(){var s=this,n=r({},this.props);return n.parentBindings&&delete n.parentBindings,i.default.createElement("div",r({},n,{ref:function(n){s.props.parentBindings.domNode=n}}),this.props.children)}}]),n}(i.default.Component);m.propTypes={name:g.default.string,id:g.default.string},n.default=(0,h.default)(m)},function(s,n,a){"use strict";function e(s){return s&&s.__esModule?s:{default:s}}function t(s,n){if(!(s instanceof n))throw new TypeError("Cannot call a class as a function")}function l(s,n){if(!s)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!n||"object"!=typeof n&&"function"!=typeof n?s:n}function o(s,n){if("function"!=typeof n&&null!==n)throw new TypeError("Super expression must either be null or a function, not "+typeof n);s.prototype=Object.create(n&&n.prototype,{constructor:{value:s,enumerable:!1,writable:!0,configurable:!0}}),n&&(Object.setPrototypeOf?Object.setPrototypeOf(s,n):s.__proto__=n)}Object.defineProperty(n,"__esModule",{value:!0});var r=a(0),p=e(r),c=a(53),i=e(c),d=function(s){function n(){var s,a,e,o;t(this,n);for(var r=arguments.length,c=Array(r),i=0;i<r;i++)c[i]=arguments[i];return a=e=l(this,(s=n.__proto__||Object.getPrototypeOf(n)).call.apply(s,[this].concat(c))),e.render=function(){return p.default.createElement("a",e.props,e.props.children)},o=a,l(e,o)}return o(n,s),n}(p.default.Component);n.default=(0,i.default)(d)},function(s,n,a){"use strict";function e(s){return s&&s.__esModule?s:{default:s}}Object.defineProperty(n,"__esModule",{value:!0}),n.Helpers=n.ScrollElement=n.ScrollLink=n.animateScroll=n.scrollSpy=n.Events=n.scroller=n.Element=n.Button=n.Link=void 0;var t=a(69),l=e(t),o=a(67),r=e(o),p=a(68),c=e(p),i=a(50),d=e(i),h=a(52),u=e(h),g=a(54),m=e(g),b=a(59),j=e(b),f=a(53),v=e(f),y=a(60),x=e(y),k=a(71),w=e(k);n.Link=l.default,n.Button=r.default,n.Element=c.default,n.scroller=d.default,n.Events=u.default,n.scrollSpy=m.default,n.animateScroll=j.default,n.ScrollLink=v.default,n.ScrollElement=x.default,n.Helpers=w.default,n.default={Link:l.default,Button:r.default,Element:c.default,scroller:d.default,Events:u.default,scrollSpy:m.default,animateScroll:j.default,ScrollLink:v.default,ScrollElement:x.default,Helpers:w.default}},function(s,n,a){"use strict";function e(s,n){if(!(s instanceof n))throw new TypeError("Cannot call a class as a function")}function t(s,n){if(!s)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!n||"object"!=typeof n&&"function"!=typeof n?s:n}function l(s,n){if("function"!=typeof n&&null!==n)throw new TypeError("Super expression must either be null or a function, not "+typeof n);s.prototype=Object.create(n&&n.prototype,{constructor:{value:s,enumerable:!1,writable:!0,configurable:!0}}),n&&(Object.setPrototypeOf?Object.setPrototypeOf(s,n):s.__proto__=n)}var o=Object.assign||function(s){for(var n=1;n<arguments.length;n++){var a=arguments[n];for(var e in a)Object.prototype.hasOwnProperty.call(a,e)&&(s[e]=a[e])}return s},r=function(){function s(s,n){for(var a=0;a<n.length;a++){var e=n[a];e.enumerable=e.enumerable||!1,e.configurable=!0,"value"in e&&(e.writable=!0),Object.defineProperty(s,e.key,e)}}return function(n,a,e){return a&&s(n.prototype,a),e&&s(n,e),n}}(),p=a(0),c=(a(2),a(22),a(54)),i=a(50),d=a(45),h=a(61),u={to:d.string.isRequired,containerId:d.string,container:d.object,activeClass:d.string,spy:d.bool,smooth:d.oneOfType([d.bool,d.string]),offset:d.number,delay:d.number,isDynamic:d.bool,onClick:d.func,duration:d.oneOfType([d.number,d.func]),absolute:d.bool,onSetActive:d.func,onSetInactive:d.func,ignoreCancelEvents:d.bool,hashSpy:d.bool},g={Scroll:function(s,n){console.warn("Helpers.Scroll is deprecated since v1.7.0");var a=n||i,d=function(n){function i(s){e(this,i);var n=t(this,(i.__proto__||Object.getPrototypeOf(i)).call(this,s));return g.call(n),n.state={active:!1},n}return l(i,n),r(i,[{key:"getScrollSpyContainer",value:function(){var s=this.props.containerId,n=this.props.container;return s?document.getElementById(s):n&&n.nodeType?n:document}},{key:"componentDidMount",value:function(){if(this.props.spy||this.props.hashSpy){var s=this.getScrollSpyContainer();c.isMounted(s)||c.mount(s),this.props.hashSpy&&(h.isMounted()||h.mount(a),h.mapContainer(this.props.to,s)),this.props.spy&&c.addStateHandler(this.stateHandler),c.addSpyHandler(this.spyHandler,s),this.setState({container:s})}}},{key:"componentWillUnmount",value:function(){c.unmount(this.stateHandler,this.spyHandler)}},{key:"render",value:function(){var n="";n=this.state&&this.state.active?((this.props.className||"")+" "+(this.props.activeClass||"active")).trim():this.props.className;var a=o({},this.props);for(var e in u)a.hasOwnProperty(e)&&delete a[e];return a.className=n,a.onClick=this.handleClick,p.createElement(s,a)}}]),i}(p.Component),g=function(){var s=this;this.scrollTo=function(n,e){a.scrollTo(n,o({},s.state,e))},this.handleClick=function(n){s.props.onClick&&s.props.onClick(n),n.stopPropagation&&n.stopPropagation(),n.preventDefault&&n.preventDefault(),s.scrollTo(s.props.to,s.props)},this.stateHandler=function(){a.getActiveLink()!==s.props.to&&(null!==s.state&&s.state.active&&s.props.onSetInactive&&s.props.onSetInactive(),s.setState({active:!1}))},this.spyHandler=function(n){var e=s.getScrollSpyContainer();if(!h.isMounted()||h.isInitialized()){var t=s.props.to,l=null,o=0,r=0,p=0;if(e.getBoundingClientRect){p=e.getBoundingClientRect().top}if(!l||s.props.isDynamic){if(!(l=a.get(t)))return;var i=l.getBoundingClientRect();o=i.top-p+n,r=o+i.height}var d=n-s.props.offset,u=d>=Math.floor(o)&&d<Math.floor(r),g=d<Math.floor(o)||d>=Math.floor(r),m=a.getActiveLink();return g?(t===m&&a.setActiveLink(void 0),s.props.hashSpy&&h.getHash()===t&&h.changeHash(),s.props.spy&&s.state.active&&(s.setState({active:!1}),s.props.onSetInactive&&s.props.onSetInactive()),c.updateStates()):u&&m!==t?(a.setActiveLink(t),s.props.hashSpy&&h.changeHash(t),s.props.spy&&(s.setState({active:!0}),s.props.onSetActive&&s.props.onSetActive(t)),c.updateStates()):void 0}}};return d.propTypes=u,d.defaultProps={offset:0},d},Element:function(s){console.warn("Helpers.Element is deprecated since v1.7.0");var n=function(n){function a(s){e(this,a);var n=t(this,(a.__proto__||Object.getPrototypeOf(a)).call(this,s));return n.childBindings={domNode:null},n}return l(a,n),r(a,[{key:"componentDidMount",value:function(){if("undefined"==typeof window)return!1;this.registerElems(this.props.name)}},{key:"componentWillReceiveProps",value:function(s){this.props.name!==s.name&&this.registerElems(s.name)}},{key:"componentWillUnmount",value:function(){if("undefined"==typeof window)return!1;i.unregister(this.props.name)}},{key:"registerElems",value:function(s){i.register(s,this.childBindings.domNode)}},{key:"render",value:function(){return p.createElement(s,o({},this.props,{parentBindings:this.childBindings}))}}]),a}(p.Component);return n.propTypes={name:d.string,id:d.string},n}};s.exports=g},function(s,n,a){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var e=a(51),t=["mousedown","mousewheel","touchmove","keydown"];n.default={subscribe:function(s){return"undefined"!=typeof document&&t.forEach(function(n){return(0,e.addPassiveEventListener)(document,n,s)})}}},function(s,n,a){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default={defaultEasing:function(s){return s<.5?Math.pow(2*s,2)/2:1-Math.pow(2*(1-s),2)/2},linear:function(s){return s},easeInQuad:function(s){return s*s},easeOutQuad:function(s){return s*(2-s)},easeInOutQuad:function(s){return s<.5?2*s*s:(4-2*s)*s-1},easeInCubic:function(s){return s*s*s},easeOutCubic:function(s){return--s*s*s+1},easeInOutCubic:function(s){return s<.5?4*s*s*s:(s-1)*(2*s-2)*(2*s-2)+1},easeInQuart:function(s){return s*s*s*s},easeOutQuart:function(s){return 1- --s*s*s*s},easeInOutQuart:function(s){return s<.5?8*s*s*s*s:1-8*--s*s*s*s},easeInQuint:function(s){return s*s*s*s*s},easeOutQuint:function(s){return 1+--s*s*s*s*s},easeInOutQuint:function(s){return s<.5?16*s*s*s*s*s:1+16*--s*s*s*s*s}}},function(s,n){var a;a=function(){return this}();try{a=a||Function("return this")()||(0,eval)("this")}catch(s){"object"==typeof window&&(a=window)}s.exports=a},,function(s,n,a){"use strict";s.exports={"zh-cn":[{filename:"admin/README.md",__html:"<p>这篇文档详细讲解了 dubbo 注册中心、管理控制台的安装和使用。</p>\n"},{filename:"admin/SUMMARY.md",__html:'<ul>\n<li><a href="install/introduction.md">1 安装手册</a>\n<ul>\n<li><a href="install/provider-demo.md">1.1 示例提供者安装</a></li>\n<li><a href="install/consumer-demo.md">1.2 示例消费者安装</a></li>\n<li><a href="install/zookeeper.md">1.3 Zookeeper 注册中心安装</a></li>\n<li><a href="install/redis.md">1.4 Redis 注册中心安装</a></li>\n<li><a href="install/simple-registry-center.md">1.5 Simple 注册中心安装</a></li>\n<li><a href="install/simple-monitor-center.md">1.6 Simple 监控中心安装</a></li>\n<li><a href="install/admin-console.md">1.7 管理控制台安装</a></li>\n</ul>\n</li>\n<li><a href="ops/introduction.md">2 运维手册</a>\n<ul>\n<li><a href="ops/dubbo-ops.md">2.1 管理控制台运维</a></li>\n</ul>\n</li>\n</ul>\n'},{filename:"admin/install/admin-console.md",__html:'<h1>管理控制台安装</h1>\n<p>管理控制台为内部裁剪版本,开源部分主要包含:路由规则,动态配置,服务降级,访问控制,权重调整,负载均衡,等管理功能。</p>\n<p>安装:</p>\n<pre><code class="language-sh">wget http://apache.etoak.com/tomcat/tomcat-6/v6.0.35/bin/apache-tomcat-6.0.35.tar.gz\ntar zxvf apache-tomcat-6.0.35.tar.gz\n<span class="hljs-built_in">cd</span> apache-tomcat-6.0.35\nrm -rf webapps/ROOT\n\ngit <span class="hljs-built_in">clone</span> https://github.com/dubbo/dubbo-ops.git /var/tmp/dubbo-ops\n<span class="hljs-built_in">pushd</span> /var/tmp/dubbo-ops\nmvn clean package\n<span class="hljs-built_in">popd</span>\n\nunzip /var/tmp/dubbo-ops/dubbo-admin/target/dubbo-admin-2.0.0.war -d webapps/ROOT\n</code></pre>\n<p>配置 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>:</p>\n<pre><code class="language-sh">vi webapps/ROOT/WEB-INF/dubbo.properties\ndubbo.properties\ndubbo.registry.address=zookeeper://127.0.0.1:2181\ndubbo.admin.root.password=root\ndubbo.admin.guest.password=guest\n</code></pre>\n<p>启动:</p>\n<pre><code class="language-sh">./bin/startup.sh\n</code></pre>\n<p>停止:</p>\n<pre><code class="language-sh">./bin/shutdown.sh\n</code></pre>\n<p>访问 <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>:</p>\n<pre><code>http://127.0.0.1:8080/\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>或将 <code>dubbo.properties</code> 放在当前用户目录下 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>用户: root, 密码: root 或者 用户: guest, 密码: guest <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"admin/install/consumer-demo.md",__html:'<h1>示例消费者安装</h1>\n<p>安装:</p>\n<pre><code class="language-sh">git <span class="hljs-built_in">clone</span> https://github.com/apache/incubator-dubbo.git\n<span class="hljs-built_in">cd</span> incubator-dubbo\n运行 dubbo-demo-consumer中的com.alibaba.dubbo.demo.consumer.Consumer\n请确保先启动Provider\n如果使用Intellij Idea 请加上-Djava.net.preferIPv4Stack=<span class="hljs-literal">true</span>\n</code></pre>\n<p>配置:</p>\n<pre><code class="language-sh">resource/META-INFO.spring/dubbo-demo-consumer.xml\n修改其中的dubbo:registery,替换成Provider提供的注册中心地址\n</code></pre>\n'},{filename:"admin/install/introduction.md",__html:'<h1>安装手册</h1>\n<p>你可以只运行 Demo Provider 和 Demo Consumer,它们缺省配置为通过 Multicast  <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> 注册中心广播互相发现,建议在不同机器上运行,如果在同一机器上,需设置 <code>unicast=false</code>:即: <code>multicast://224.5.6.7:1234?unicast=false</code>,否则发给消费者的单播消息可能被提供者抢占,两个消费者在同一台机器也一样,只有 multicast 注册中心有此问题。</p>\n<p>你也可以运行多个 Demo Provider 和 Demo Consumer,来验证软负载均衡,Demo Consumer 可以直接启动多个实例,而多个 Demo Provider 因有端口冲突,可在不同机器上运行,或者修改 Demo Provider 安装目录下 <code>conf/dubbo.properties</code> 配置中的 <code>dubbo.protocol.port</code> 的值。</p>\n<p>你也可以增加运行 Simple Monitor 监控中心,它缺省配置为通过 Multicast 注册中心广播发现 Provider 和 Consumer,并展示出它们的依赖关系,以及它们之间调用的次数和时间。</p>\n<p>你也可以将 Multicast 注册中心换成 Zookeeper 注册中心,安装 Zookeeper Registry 后,修改 Demo Proivder,Demo Consumer,Simple Monitor 三者安装目录下的 <code>conf/dubbo.properties</code>,将 <code>dubbo.registry.address</code> 的值改为 <code>zookeeper://127.0.0.1:2181</code>,同理,如果换成 Redis Registry,值改为 <code>redis://127.0.0.1:6379</code>,如果换成 Simple Registry,值改为 <code>dubbo://127.0.0.1:9090</code></p>\n<p>推荐使用 Zookeeper 注册中心</p>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>注意:multicast 地址不能配成 127.0.0.1,也不能配成机器的 IP 地址,必须是 D 段广播地址,也就是:224.0.0.0 到 239.255.255.255 之间的任意地址 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"admin/install/provider-demo.md",__html:'<h1>示例提供者安装</h1>\n<p>安装:</p>\n<pre><code class="language-sh">git <span class="hljs-built_in">clone</span> https://github.com/apache/incubator-dubbo.git\n<span class="hljs-built_in">cd</span> incubator-dubbo\n运行 dubbo-demo-provider中的com.alibaba.dubbo.demo.provider.Provider\n如果使用Intellij Idea 请加上-Djava.net.preferIPv4Stack=<span class="hljs-literal">true</span>\n</code></pre>\n<p>配置:</p>\n<pre><code class="language-sh">resource/META-INFO.spring/dubbo-demo-provider.xml\n修改其中的dubbo:registery,替换成真实的注册中心地址,推荐使用zookeeper\n</code></pre>\n'},{filename:"admin/install/redis.md",__html:'<h1>Redis 注册中心安装</h1>\n<p>Redis <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> 使用方式参见: <a href="http://dubbo.apache.org/books/dubbo-user-book/references/registry/redis.html">Redis 注册中心参考手册</a>。</p>\n<p>只需搭一个原生的 Redis 服务器,并将<a href="http://dubbo.apache.org/books/dubbo-user-book/quick-start.html">快速启动</a>中 Provider 和 Consumer 里的 <code>conf/dubbo.properties</code> 中的 <code>dubbo.registry.addrss</code> 的值改为 <code>redis://127.0.0.1:6379</code> 即可使用。</p>\n<p>Redis 注册中心集群 <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup> 采用在客户端同时写入多个服务器,读取单个服务器的策略实现。</p>\n<p>安装:</p>\n<pre><code class="language-sh">wget http://redis.googlecode.com/files/redis-2.4.8.tar.gz\ntar xzf redis-2.4.8.tar.gz\n<span class="hljs-built_in">cd</span> redis-2.4.8\nmake\n</code></pre>\n<p>配置:</p>\n<pre><code class="language-sh">vi redis.conf\n</code></pre>\n<p>启动:</p>\n<pre><code class="language-sh">nohup ./src/redis-server redis.conf &amp;\n</code></pre>\n<p>停止:</p>\n<pre><code class="language-sh">killall redis-server\n</code></pre>\n<ul>\n<li>命令行 <sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup>:</li>\n</ul>\n<pre><code class="language-sh">./src/redis-cli\nhgetall /dubbo/com.foo.BarService/providers\n</code></pre>\n<p>或者:</p>\n<pre><code class="language-sh">telnet 127.0.0.1 6379\nhgetall /dubbo/com.foo.BarService/providers\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>Redis 是一个高效的 KV 存储服务器,参见:<a href="http://redis.io/topics/quickstart">http://redis.io/topics/quickstart</a> <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p><code>2.1.0</code> 以上版本支持 <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn3" class="footnote-item"><p>参见: <a href="http://redis.io/commands">http://redis.io/commands</a> <a href="#fnref3" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"admin/install/simple-monitor-center.md",__html:'<h1>Simple 监控中心安装</h1>\n<h2>安装步骤</h2>\n<p>安装:</p>\n<pre><code class="language-sh">git <span class="hljs-built_in">clone</span> https://github.com/apache/incubator-dubbo-ops\n<span class="hljs-built_in">cd</span> incubator-dubbo-ops &amp;&amp; mvn package\n<span class="hljs-built_in">cd</span> dubbo-monitor-simple/target &amp;&amp; tar xvf dubbo-monitor-simple-2.0.0-assembly.tar.gz\n<span class="hljs-built_in">cd</span> dubbo-monitor-simple-2.0.0\n</code></pre>\n<p>配置:</p>\n<pre><code class="language-sh">vi conf/dubbo.properties\n</code></pre>\n<p>启动:</p>\n<pre><code class="language-sh">./assembly.bin/start.sh\n</code></pre>\n<p>停止:</p>\n<pre><code class="language-sh">./assembly.bin/stop.sh\n</code></pre>\n<p>重启:</p>\n<pre><code class="language-sh">./assembly.bin/restart.sh\n</code></pre>\n<p>调试:</p>\n<pre><code class="language-sh">./assembly.bin/start.sh debug\n</code></pre>\n<p>系统状态:</p>\n<pre><code class="language-sh">./assembly.bin/dump.sh\n</code></pre>\n<p>总控入口:</p>\n<pre><code class="language-sh">./assembly.bin/server.sh start\n./assembly.bin/server.sh stop\n./assembly.bin/server.sh restart\n./assembly.bin/server.sh debug\n./assembly.bin/server.sh dump\n</code></pre>\n<p>标准输出:</p>\n<pre><code class="language-sh">tail -f logs/stdout.log\n</code></pre>\n<p>命令行 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>:</p>\n<pre><code class="language-sh">telnet 127.0.0.1 7070\n<span class="hljs-built_in">help</span>\n</code></pre>\n<p>或者:</p>\n<pre><code class="language-sh"><span class="hljs-built_in">echo</span> status | nc -i 1 127.0.0.1 7070\n</code></pre>\n<p>访问:</p>\n<pre><code>http://127.0.0.1:8080\n</code></pre>\n<p><img src="../sources/images/dubbo-monitor-simple.jpg" alt="/admin-guide/images/dubbo-monitor-simple.jpg"></p>\n<h2>注意事项</h2>\n<p>Simple Monitor 挂掉不会影响到 Consumer 和 Provider 之间的调用,所以用于生产环境不会有风险。</p>\n<p>Simple Monitor 采用磁盘存储统计信息,请注意安装机器的磁盘限制,如果要集群,建议用mount共享磁盘。</p>\n<p>charts 目录必须放在 <code>jetty.directory</code> 下,否则页面上访问不了。</p>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>请参考 <a href="http://dubbo.apache.org/books/dubbo-user-book/references/telnet.html">Telnet 命令参考手册</a> <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"admin/install/simple-registry-center.md",__html:'<h1>Simple 注册中心安装</h1>\n<p>Simple Registry 没有经过严格测试,可能不健状,并且不支持集群,不建议用于生产环境。</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\n<span class="hljs-built_in">cd</span> incubator-dubbo-ops &amp;&amp; mvn package\n<span class="hljs-built_in">cd</span> dubbo-registry-simple/target &amp;&amp; tar xvf dubbo-registry-simple-2.0.0-assembly.tar.gz\n<span class="hljs-built_in">cd</span> dubbo-registry-simple-2.0.0\n</code></pre>\n<p>配置:</p>\n<pre><code class="language-sh">vi conf/dubbo.properties\n</code></pre>\n<p>启动:</p>\n<pre><code class="language-sh">./assembly.bin/start.sh\n</code></pre>\n<p>停止:</p>\n<pre><code class="language-sh">./assembly.bin/stop.sh\n</code></pre>\n<p>重启:</p>\n<pre><code class="language-sh">./assembly.bin/restart.sh\n</code></pre>\n<p>调试:</p>\n<pre><code class="language-sh">./assembly.bin/start.sh debug\n</code></pre>\n<p>系统状态:</p>\n<pre><code class="language-sh">./assembly.bin/dump.sh\n</code></pre>\n<p>总控入口:</p>\n<pre><code class="language-sh">./assembly.bin/server.sh start\n./assembly.bin/server.sh stop\n./assembly.bin/server.sh restart\n./assembly.bin/server.sh debug\n./assembly.bin/server.sh dump\n</code></pre>\n<p>标准输出:</p>\n<pre><code class="language-sh">tail -f logs/stdout.log\n</code></pre>\n<p>命令行 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>:</p>\n<pre><code class="language-shell">telnet 127.0.0.1 9090\nhelp\n</code></pre>\n<p>或者:</p>\n<pre><code class="language-sh"><span class="hljs-built_in">echo</span> status | nc -i 1 127.0.0.1 9090\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>请参考 <a href="http://dubbo.apache.org/books/dubbo-user-book/references/telnet.html">Telnet 命令参考手册</a> <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"admin/install/zookeeper.md",__html:'<h1>Zookeeper 注册中心安装</h1>\n<p>建议使用 <code>dubbo-2.3.3</code> 以上版本的 zookeeper <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> 注册中心客户端。</p>\n<p>Dubbo 未对 Zookeeper 服务器端做任何侵入修改,只需安装原生的 Zookeeper 服务器即可,所有注册中心逻辑适配都在调用 Zookeeper 客户端时完成。</p>\n<p>安装:</p>\n<pre><code class="language-sh">wget http://archive.apache.org/dist/zookeeper/zookeeper-3.3.3/zookeeper-3.3.3.tar.gz\ntar zxvf zookeeper-3.3.3.tar.gz\n<span class="hljs-built_in">cd</span> zookeeper-3.3.3\ncp conf/zoo_sample.cfg conf/zoo.cfg\n</code></pre>\n<p>配置:</p>\n<pre><code class="language-sh">vi conf/zoo.cfg\n</code></pre>\n<p>如果不需要集群,<code>zoo.cfg</code> 的内容如下 <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>:</p>\n<pre><code class="language-properties">tickTime=2000\ninitLimit=10\nsyncLimit=5\ndataDir=/home/dubbo/zookeeper-3.3.3/data\nclientPort=2181\n</code></pre>\n<p>如果需要集群,<code>zoo.cfg</code> 的内容如下 <sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup>:</p>\n<pre><code class="language-properties">tickTime=2000\ninitLimit=10\nsyncLimit=5\ndataDir=/home/dubbo/zookeeper-3.3.3/data\nclientPort=2181\nserver.1=10.20.153.10:2555:3555\nserver.2=10.20.153.11:2555:3555\n</code></pre>\n<p>并在 data 目录 <sup class="footnote-ref"><a href="#fn4" id="fnref4">[4]</a></sup> 下放置 myid 文件:</p>\n<pre><code class="language-sh">mkdir data\nvi myid\n</code></pre>\n<p>myid 指明自己的 id,对应上面 <code>zoo.cfg</code> 中 <code>server.</code> 后的数字,第一台的内容为 1,第二台的内容为 2,内容如下:</p>\n<pre><code>1\n</code></pre>\n<p>启动:</p>\n<pre><code class="language-sh">./bin/zkServer.sh start\n</code></pre>\n<p>停止:</p>\n<pre><code class="language-sh">./bin/zkServer.sh stop\n</code></pre>\n<p>命令行 <sup class="footnote-ref"><a href="#fn5" id="fnref5">[5]</a></sup>:</p>\n<pre><code class="language-sh">telnet 127.0.0.1 2181\ndump\n</code></pre>\n<p>或者:</p>\n<pre><code class="language-shell">echo dump | nc 127.0.0.1 2181\n</code></pre>\n<p>用法:</p>\n<pre><code class="language-xml">dubbo.registry.address=zookeeper://10.20.153.10:2181?backup=10.20.153.11:2181\n</code></pre>\n<p>或者:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"zookeeper"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.153.10:2181,10.20.153.11:2181"</span> /&gt;</span>\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>Zookeeper是 Apache Hadoop 的子项目,强度相对较好,建议生产环境使用该注册中心 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>其中 data 目录需改成你真实输出目录 <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn3" class="footnote-item"><p>其中 data 目录和 server 地址需改成你真实部署机器的信息 <a href="#fnref3" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn4" class="footnote-item"><p>上面 <code>zoo.cfg</code> 中的 <code>dataDir</code> <a href="#fnref4" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn5" class="footnote-item"><p><a href="http://zookeeper.apache.org/doc/r3.3.3/zookeeperAdmin.html">http://zookeeper.apache.org/doc/r3.3.3/zookeeperAdmin.html</a> <a href="#fnref5" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"admin/ops/dubbo-ops.md",__html:'<h1>管理控制台运维</h1>\n<h2>搜索页面</h2>\n<p>当你需要管理 Dubbo 的服务时,首先要搜索到这个服务,然后打开它的管理页面</p>\n<p><img src="../sources/images/dubbo-search.png" alt="/admin-guide/images/dubbo-search.png"></p>\n<h2>服务提供者页面</h2>\n<p><img src="../sources/images/dubbo-providers.png" alt="/admin-guide/images/dubbo-providers.png"></p>\n<h2>服务消费者页面</h2>\n<p><img src="../sources/images/dubbo-consumers.png" alt="/admin-guide/images/dubbo-consumers.png"></p>\n<h2>服务应用页面</h2>\n<p><img src="../sources/images/dubbo-applications.png" alt="/admin-guide/images/dubbo-applications.png"></p>\n<h2>添加路由规则页面</h2>\n<p><img src="../sources/images/dubbo-add-route.png" alt="/admin-guide/images/dubbo-add-route.png"></p>\n<h2>添加动态配置页面</h2>\n<p><img src="../sources/images/dubbo-add-config.png" alt="/admin-guide/images/dubbo-add-config.png"></p>\n<h5>服务注册</h5>\n<h5>服务降级</h5>\n<h5>路由规则</h5>\n<h5>访问控制</h5>\n<h5>动态配置</h5>\n<h5>权重调节</h5>\n<h5>负载均衡</h5>\n<h5>服务负责人</h5>\n'},{filename:"admin/ops/introduction.md",__html:"<h1>运维手册</h1>\n"},{filename:"admin/ops/pinpoint.md",__html:'<h1>使用Pinpoint做分布式跟踪</h1>\n<p>在使用Dubbo进行服务化或者整合应用后,假设某个服务后台日志显示有异常,这个服务又被多个应用调用的情况下,我们通常很难判断是哪个应用调用的,问题的起因是什么,因此我们需要一套分布式跟踪系统来快速定位问题,Pinpoint可以帮助我们快速定位问题(当然,解决方案也不止这一种)。</p>\n<h2>什么是Pinpoint(摘自<a href="https://skyao.gitbooks.io/learning-pinpoint/">Pinpoint学习笔记</a>)</h2>\n<p><a href="https://github.com/naver/pinpoint">Pinpoint</a>是一个开源的 APM (Application Performance Management/应用性能管理)工具,用于基于java的大规模分布式系统。\n仿照Google Dapper,Pinpoint通过跟踪分布式应用之间的调用来提供解决方案,以帮助分析系统的总体结构和内部模块之间如何相互联系。</p>\n<blockquote>\n<p>注:对于各个模块之间的通讯英文原文中用的是transaction一词,但是我觉得如果翻译为&quot;事务&quot;容易引起误解,所以替换为&quot;交互&quot;或者&quot;调用&quot;这种比较直白的字眼。</p>\n</blockquote>\n<p>在使用上力图简单高效:</p>\n<ul>\n<li>安装agent,不需要修改哪怕一行代码</li>\n<li>最小化性能损失</li>\n</ul>\n<h3>服务器地图(ServerMap)</h3>\n<p>通过可视化分布式系统的模块和他们之间的相互联系来理解系统拓扑。点击某个节点会展示这个模块的详情,比如它当前的状态和请求数量。</p>\n<h3>实时活动线程图表(Realtime Active Thread Chart)</h3>\n<p>实时监控应用内部的活动线程。</p>\n<h3>请求/应答分布图表(Request/Response Scatter Chart)</h3>\n<p>长期可视化请求数量和应答模式来定位潜在问题。通过在图表上拉拽可以选择请求查看更多的详细信息。</p>\n<h3>调用栈(CallStack)</h3>\n<p>在分布式环境中为每个调用生成代码级别的可视图,在单个视图中定位瓶颈和失败点。</p>\n<h3>巡查(Inspector)</h3>\n<p>查看应用上的其他详细信息,比如CPU使用率,内存/垃圾回收,TPS,和JVM参数。</p>\n<h3>支持模块</h3>\n<ul>\n<li>JDK 6+</li>\n<li>Tomcat 6/7/8, Jetty 8/9, JBoss EAP 6, Resin 4, Websphere 6/7/8, Vertx 3.3/3.4/3.5</li>\n<li>Spring, Spring Boot (Embedded Tomcat, Jetty)</li>\n<li>Apache HTTP Client 3.x/4.x, JDK HttpConnector, GoogleHttpClient, OkHttpClient, NingAsyncHttpClient</li>\n<li>Thrift Client, Thrift Service, DUBBO PROVIDER, DUBBO CONSUMER</li>\n<li>ActiveMQ, RabbitMQ</li>\n<li>MySQL, Oracle, MSSQL, CUBRID,POSTGRESQL, MARIA</li>\n<li>Arcus, Memcached, Redis, CASSANDRA</li>\n<li>iBATIS, MyBatis</li>\n<li>DBCP, DBCP2, HIKARICP</li>\n<li>gson, Jackson, Json Lib</li>\n<li>log4j, Logback</li>\n<li>自定义模块</li>\n</ul>\n<h2>Pinpoint与Dubbo的结合</h2>\n<h3>启动Pinpoint</h3>\n<p>参考Pinpoint的<a href="http://naver.github.io/pinpoint/quickstart.html">Quick start</a>搭建环境(不需要启动TestApp)</p>\n<h3>准备Dubbo示例程序</h3>\n<h4>创建API包</h4>\n<p>pom.xml</p>\n<pre><code class="language-xml"><span class="php"><span class="hljs-meta">&lt;?</span>xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"UTF-8"</span><span class="hljs-meta">?&gt;</span></span>\n<span class="hljs-tag">&lt;<span class="hljs-name">project</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://maven.apache.org/POM/4.0.0"</span>\n         <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>\n         <span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">modelVersion</span>&gt;</span>4.0.0<span class="hljs-tag">&lt;/<span class="hljs-name">modelVersion</span>&gt;</span>\n\n    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.example<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>demo-api<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>0.0.1-SNAPSHOT<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">project</span>&gt;</span>\n</code></pre>\n<p>新建API接口:</p>\n<pre><code>package com.example.demoapi;\n\npublic interface HelloService {\n    String sayHello(String name);\n}\n</code></pre>\n<h4>实现 Dubbo 服务提供方</h4>\n<p>pom.xml</p>\n<pre><code class="language-xml"><span class="php"><span class="hljs-meta">&lt;?</span>xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"UTF-8"</span><span class="hljs-meta">?&gt;</span></span>\n<span class="hljs-tag">&lt;<span class="hljs-name">project</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://maven.apache.org/POM/4.0.0"</span> <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>\n\t<span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</span>&gt;</span>\n\t<span class="hljs-tag">&lt;<span class="hljs-name">modelVersion</span>&gt;</span>4.0.0<span class="hljs-tag">&lt;/<span class="hljs-name">modelVersion</span>&gt;</span>\n\n\t<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.example<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n\t<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>demo-provider<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n\t<span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>0.0.1-SNAPSHOT<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n\t<span class="hljs-tag">&lt;<span class="hljs-name">packaging</span>&gt;</span>jar<span class="hljs-tag">&lt;/<span class="hljs-name">packaging</span>&gt;</span>\n\n\t<span class="hljs-tag">&lt;<span class="hljs-name">name</span>&gt;</span>demo-provider<span class="hljs-tag">&lt;/<span class="hljs-name">name</span>&gt;</span>\n\n\t<span class="hljs-tag">&lt;<span class="hljs-name">parent</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-parent<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>2.0.3.RELEASE<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">relativePath</span>/&gt;</span> <span class="hljs-comment">&lt;!-- lookup parent from repository --&gt;</span>\n\t<span class="hljs-tag">&lt;/<span class="hljs-name">parent</span>&gt;</span>\n\n\t<span class="hljs-tag">&lt;<span class="hljs-name">properties</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">project.build.sourceEncoding</span>&gt;</span>UTF-8<span class="hljs-tag">&lt;/<span class="hljs-name">project.build.sourceEncoding</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">project.reporting.outputEncoding</span>&gt;</span>UTF-8<span class="hljs-tag">&lt;/<span class="hljs-name">project.reporting.outputEncoding</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">java.version</span>&gt;</span>1.8<span class="hljs-tag">&lt;/<span class="hljs-name">java.version</span>&gt;</span>\n\t<span class="hljs-tag">&lt;/<span class="hljs-name">properties</span>&gt;</span>\n\n\t<span class="hljs-tag">&lt;<span class="hljs-name">repositories</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">repository</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">id</span>&gt;</span>sonatype-nexus-snapshots<span class="hljs-tag">&lt;/<span class="hljs-name">id</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">url</span>&gt;</span>https://oss.sonatype.org/content/repositories/snapshots<span class="hljs-tag">&lt;/<span class="hljs-name">url</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">releases</span>&gt;</span>\n\t\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">enabled</span>&gt;</span>false<span class="hljs-tag">&lt;/<span class="hljs-name">enabled</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">releases</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">snapshots</span>&gt;</span>\n\t\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">enabled</span>&gt;</span>true<span class="hljs-tag">&lt;/<span class="hljs-name">enabled</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">snapshots</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">repository</span>&gt;</span>\n\t<span class="hljs-tag">&lt;/<span class="hljs-name">repositories</span>&gt;</span>\n\n\t<span class="hljs-tag">&lt;<span class="hljs-name">dependencies</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.alibaba.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>dubbo-spring-boot-starter<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>0.2.0<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.example<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>demo-api<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>0.0.1-SNAPSHOT<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>\n\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-test<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">scope</span>&gt;</span>test<span class="hljs-tag">&lt;/<span class="hljs-name">scope</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>\n\t<span class="hljs-tag">&lt;/<span class="hljs-name">dependencies</span>&gt;</span>\n\n\t<span class="hljs-tag">&lt;<span class="hljs-name">build</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">plugins</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">plugin</span>&gt;</span>\n\t\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n\t\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-maven-plugin<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">plugin</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">plugins</span>&gt;</span>\n\t<span class="hljs-tag">&lt;/<span class="hljs-name">build</span>&gt;</span>\n\n<span class="hljs-tag">&lt;/<span class="hljs-name">project</span>&gt;</span>\n</code></pre>\n<ol>\n<li>实现 <code>HelloService</code> 接口:</li>\n</ol>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.example.demoprovider.provider;\n\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.config.annotation.Service;\n<span class="hljs-keyword">import</span> com.example.demoapi.HelloService;\n\n<span class="hljs-meta">@Service</span>(version = <span class="hljs-string">"${demo.service.version}"</span>,\n        application = <span class="hljs-string">"${dubbo.application.id}"</span>,\n        protocol = <span class="hljs-string">"${dubbo.protocol.id}"</span>,\n        registry = <span class="hljs-string">"${dubbo.registry.id}"</span>)\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HelloServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">HelloService</span> </span>{\n    <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>;\n    <span class="hljs-meta">@Override</span>\n    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">sayHello</span><span class="hljs-params">(String name)</span> </span>{\n        i++;\n        <span class="hljs-keyword">if</span> (i % <span class="hljs-number">3</span> == <span class="hljs-number">0</span>) {\n            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RuntimeException(<span class="hljs-string">"ex"</span>);\n        }\n        <span class="hljs-keyword">return</span> <span class="hljs-string">"Hello "</span> + name + <span class="hljs-string">"!"</span>;\n    }\n}\n</code></pre>\n<ol start="2">\n<li>编写 Spring Boot 引导程序:</li>\n</ol>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.example.demoprovider;\n\n<span class="hljs-keyword">import</span> org.springframework.boot.SpringApplication;\n<span class="hljs-keyword">import</span> org.springframework.boot.autoconfigure.SpringBootApplication;\n\n<span class="hljs-meta">@SpringBootApplication</span>\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DemoProviderApplication</span> </span>{\n\n\t<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{\n\t\tSpringApplication.run(DemoProviderApplication.class, args);\n\t}\n}\n</code></pre>\n<ol start="3">\n<li>配置 <code>application.properties</code>:</li>\n</ol>\n<pre><code class="language-properties"># Spring boot application\nspring.application.name = dubbo-provider-demo\nserver.port = 9090\nmanagement.port = 9091\n\n# Service version\ndemo.service.version = 1.0.0\n\n# Base packages to scan Dubbo Components (e.g @Service , @Reference)\ndubbo.scan.basePackages  = com.example.demoprovider\n\n# Dubbo Config properties\n## ApplicationConfig Bean\ndubbo.application.id = dubbo-provider-demo\ndubbo.application.name = dubbo-provider-demo\n\n## ProtocolConfig Bean\ndubbo.protocol.id = dubbo\ndubbo.protocol.name = dubbo\ndubbo.protocol.port = 12345\n\n## RegistryConfig Bean\ndubbo.registry.id = my-registry\ndubbo.registry.address = N/A\n</code></pre>\n<h4>实现 Dubbo 服务消费方</h4>\n<p>pom.xml</p>\n<pre><code class="language-xml"><span class="php"><span class="hljs-meta">&lt;?</span>xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"UTF-8"</span><span class="hljs-meta">?&gt;</span></span>\n<span class="hljs-tag">&lt;<span class="hljs-name">project</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://maven.apache.org/POM/4.0.0"</span> <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>\n\t<span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</span>&gt;</span>\n\t<span class="hljs-tag">&lt;<span class="hljs-name">modelVersion</span>&gt;</span>4.0.0<span class="hljs-tag">&lt;/<span class="hljs-name">modelVersion</span>&gt;</span>\n\n\t<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.example<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n\t<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>demo-consumer<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n\t<span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>0.0.1-SNAPSHOT<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n\t<span class="hljs-tag">&lt;<span class="hljs-name">packaging</span>&gt;</span>jar<span class="hljs-tag">&lt;/<span class="hljs-name">packaging</span>&gt;</span>\n\n\t<span class="hljs-tag">&lt;<span class="hljs-name">name</span>&gt;</span>demo-consumer<span class="hljs-tag">&lt;/<span class="hljs-name">name</span>&gt;</span>\n\n\t<span class="hljs-tag">&lt;<span class="hljs-name">parent</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-parent<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>2.0.3.RELEASE<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">relativePath</span>/&gt;</span> <span class="hljs-comment">&lt;!-- lookup parent from repository --&gt;</span>\n\t<span class="hljs-tag">&lt;/<span class="hljs-name">parent</span>&gt;</span>\n\n\t<span class="hljs-tag">&lt;<span class="hljs-name">properties</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">project.build.sourceEncoding</span>&gt;</span>UTF-8<span class="hljs-tag">&lt;/<span class="hljs-name">project.build.sourceEncoding</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">project.reporting.outputEncoding</span>&gt;</span>UTF-8<span class="hljs-tag">&lt;/<span class="hljs-name">project.reporting.outputEncoding</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">java.version</span>&gt;</span>1.8<span class="hljs-tag">&lt;/<span class="hljs-name">java.version</span>&gt;</span>\n\t<span class="hljs-tag">&lt;/<span class="hljs-name">properties</span>&gt;</span>\n\n\t<span class="hljs-tag">&lt;<span class="hljs-name">repositories</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">repository</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">id</span>&gt;</span>sonatype-nexus-snapshots<span class="hljs-tag">&lt;/<span class="hljs-name">id</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">url</span>&gt;</span>https://oss.sonatype.org/content/repositories/snapshots<span class="hljs-tag">&lt;/<span class="hljs-name">url</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">releases</span>&gt;</span>\n\t\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">enabled</span>&gt;</span>false<span class="hljs-tag">&lt;/<span class="hljs-name">enabled</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">releases</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">snapshots</span>&gt;</span>\n\t\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">enabled</span>&gt;</span>true<span class="hljs-tag">&lt;/<span class="hljs-name">enabled</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">snapshots</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">repository</span>&gt;</span>\n\t<span class="hljs-tag">&lt;/<span class="hljs-name">repositories</span>&gt;</span>\n\n\t<span class="hljs-tag">&lt;<span class="hljs-name">dependencies</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-web<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.alibaba.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>dubbo-spring-boot-starter<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>0.2.0<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.example<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>demo-api<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>0.0.1-SNAPSHOT<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>\n\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-test<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">scope</span>&gt;</span>test<span class="hljs-tag">&lt;/<span class="hljs-name">scope</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>\n\t<span class="hljs-tag">&lt;/<span class="hljs-name">dependencies</span>&gt;</span>\n\n\t<span class="hljs-tag">&lt;<span class="hljs-name">build</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">plugins</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">plugin</span>&gt;</span>\n\t\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n\t\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-maven-plugin<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n\t\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">configuration</span>&gt;</span>\n\t\t\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">classifier</span>&gt;</span>exec<span class="hljs-tag">&lt;/<span class="hljs-name">classifier</span>&gt;</span>\n\t\t\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">configuration</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">plugin</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">plugins</span>&gt;</span>\n\t<span class="hljs-tag">&lt;/<span class="hljs-name">build</span>&gt;</span>\n\n<span class="hljs-tag">&lt;/<span class="hljs-name">project</span>&gt;</span>\n</code></pre>\n<ol>\n<li>通过 <code>@Reference</code> 注入 <code>HelloService</code></li>\n</ol>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.example.democonsumer.controller;\n\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.config.annotation.Reference;\n<span class="hljs-keyword">import</span> com.example.demoapi.HelloService;\n<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.RequestMapping;\n<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.RequestParam;\n<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.RestController;\n\n<span class="hljs-meta">@RestController</span>\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DemoConsumerController</span> </span>{\n    <span class="hljs-meta">@Reference</span>(version = <span class="hljs-string">"${demo.service.version}"</span>,\n            application = <span class="hljs-string">"${dubbo.application.id}"</span>,\n            url = <span class="hljs-string">"dubbo://&lt;注意,这里填写具体IP&gt;:12345"</span>)\n    <span class="hljs-keyword">private</span> HelloService helloService;\n\n    <span class="hljs-meta">@RequestMapping</span>(<span class="hljs-string">"/sayHello"</span>)\n    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">sayHello</span><span class="hljs-params">(@RequestParam String name)</span> </span>{\n        <span class="hljs-keyword">return</span> helloService.sayHello(name);\n    }\n}\n</code></pre>\n<blockquote>\n<p>直连提供者调用需要填写具体IP地址,如果写localhost也可以,但是会被Pinpoint额外识别为一个未知服务</p>\n</blockquote>\n<ol start="2">\n<li>编写 Spring Boot 引导程序(Web 应用):</li>\n</ol>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.example.democonsumer;\n\n<span class="hljs-keyword">import</span> org.springframework.boot.SpringApplication;\n<span class="hljs-keyword">import</span> org.springframework.boot.autoconfigure.SpringBootApplication;\n\n<span class="hljs-meta">@SpringBootApplication</span>\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DemoConsumerApplication</span> </span>{\n\n\t<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{\n\t\tSpringApplication.run(DemoConsumerApplication.class, args);\n\t}\n}\n</code></pre>\n<ol start="3">\n<li>配置 <code>application.properties</code>:</li>\n</ol>\n<pre><code class="language-properties"># Spring boot application\nspring.application.name=dubbo-consumer-demo\nserver.port=8080\nmanagement.port=8081\n\n# Service Version\ndemo.service.version=1.0.0\n\n# Dubbo Config properties\n## ApplicationConfig Bean\ndubbo.application.id=dubbo-consumer-demo\ndubbo.application.name=dubbo-consumer-demo\n\n## ProtocolConfig Bean\ndubbo.protocol.id=dubbo\ndubbo.protocol.name=dubbo\ndubbo.protocol.port=12345\n</code></pre>\n<h3>使用Pinpoint-agent启动服务提供方和服务消费方</h3>\n<h4>启动服务提供方</h4>\n<ol>\n<li>编译打包</li>\n</ol>\n<pre><code>mvn clean package\n</code></pre>\n<ol start="2">\n<li>附加参数启动服务提供方</li>\n</ol>\n<pre><code>java -jar -javaagent:$AGENT_PATH/pinpoint-bootstrap-$VERSION.jar -Dpinpoint.agentId=demo-provider -Dpinpoint.applicationName=DP target/demo-provider-0.0.1-SNAPSHOT.jar\n</code></pre>\n<ol start="3">\n<li>附加参数启动服务消费方</li>\n</ol>\n<pre><code>java -jar -javaagent:$AGENT_PATH/pinpoint-bootstrap-$VERSION.jar -Dpinpoint.agentId=demo-consumer -Dpinpoint.applicationName=DC target/demo-comsumer-0.0.1-SNAPSHOT-exec.jar\n</code></pre>\n<ol start="4">\n<li>访问消费方地址模拟用户请求</li>\n</ol>\n<p><code>http://localhost:8080/sayHello?name=ABC</code></p>\n<h2>使用Pinpoint快速定位问题</h2>\n<h3>首页</h3>\n<p><img src="../sources/images/pinpoint-home.png" alt="/admin-guide/images/pinpoint-home.png"></p>\n<blockquote>\n<p>这里的用户请求是请求DubboProvider数量的双倍,原因是记录了favicon.ico图标请求导致的</p>\n</blockquote>\n<h3>调用树</h3>\n<p><img src="../sources/images/pinpoint-calltree.png" alt="/admin-guide/images/pinpoint-calltree.png"></p>\n<h3>深入跟踪</h3>\n<p><img src="../sources/images/pinpoint-mixedview.png" alt="/admin-guide/images/pinpoint-mixedview.png"></p>\n<h3>其他</h3>\n<p>示例简单的模拟了Dubbo的提供和调用,并没有进行数据库等其他中间件的应用,详细使用请参照Pinpoint文档。</p>\n'},{filename:"dev/README.md",__html:"<p>这篇文档的目标读者是对 dubbo 源码、设计有兴趣的,或者有意愿加入 dubbo 开发的人群。主要涵盖了 dubbo 的框架设计、扩展机制、编码规范、版本管理、构建等话题。</p>\n"},{filename:"dev/SPI.md",__html:'<h1>扩展点加载</h1>\n<h2>扩展点配置</h2>\n<h3>来源:</h3>\n<p>Dubbo 的扩展点加载从 JDK 标准的 SPI (Service Provider Interface) 扩展点发现机制加强而来。</p>\n<p>Dubbo 改进了 JDK 标准的 SPI 的以下问题:</p>\n<ul>\n<li>JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源。</li>\n<li>如果扩展点加载失败,连扩展点的名称都拿不到了。比如:JDK 标准的 ScriptEngine,通过 <code>getName()</code> 获取脚本类型的名称,但如果 RubyScriptEngine 因为所依赖的 jruby.jar 不存在,导致 RubyScriptEngine 类加载失败,这个失败原因被吃掉了,和 ruby 对应不起来,当用户执行 ruby  脚本时,会报不支持 ruby,而不是真正失败的原因。</li>\n<li>增加了对扩展点 IoC 和 AOP 的支持,一个扩展点可以直接 setter 注入其它扩展点。</li>\n</ul>\n<h3>约定:</h3>\n<p>在扩展类的 jar 包内 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>,放置扩展点配置文件 <code>META-INF/dubbo/接口全限定名</code>,内容为:<code>配置名=扩展实现类全限定名</code>,多个实现类用换行符分隔。</p>\n<h3>示例:</h3>\n<p>以扩展 Dubbo 的协议为例,在协议的实现 jar 包内放置文本文件:<code>META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol</code>,内容为:</p>\n<pre><code class="language-properties">xxx=com.alibaba.xxx.XxxProtocol\n</code></pre>\n<p>实现类内容 <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.alibaba.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Protocol;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxProtocol</span> <span class="hljs-title">implemenets</span> <span class="hljs-title">Protocol</span> </span>{ \n    <span class="hljs-comment">// ...</span>\n}\n</code></pre>\n<h3>配置模块中的配置</h3>\n<p>Dubbo 配置模块中,扩展点均有对应配置属性或标签,通过配置指定使用哪个扩展实现。比如:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n</code></pre>\n<h2>扩展点特性</h2>\n<h3>扩展点自动包装</h3>\n<p>自动包装扩展点的 Wrapper 类。<code>ExtensionLoader</code> 在加载扩展点时,如果加载到的扩展点有拷贝构造函数,则判定为扩展点 Wrapper 类。</p>\n<p>Wrapper类内容:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.alibaba.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Protocol;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxProtocolWrapper</span> <span class="hljs-title">implemenets</span> <span class="hljs-title">Protocol</span> </span>{\n    Protocol impl;\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">XxxProtocol</span><span class="hljs-params">(Protocol protocol)</span> </span>{ impl = protocol; }\n \n    <span class="hljs-comment">// 接口方法做一个操作后,再调用extension的方法</span>\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">refer</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-comment">//... 一些操作</span>\n        impl.refer();\n        <span class="hljs-comment">// ... 一些操作</span>\n    }\n \n    <span class="hljs-comment">// ...</span>\n}\n</code></pre>\n<p>Wrapper 类同样实现了扩展点接口,但是 Wrapper 不是扩展点的真正实现。它的用途主要是用于从 <code>ExtensionLoader</code> 返回扩展点时,包装在真正的扩展点实现外。即从 <code>ExtensionLoader</code> 中返回的实际上是 Wrapper 类的实例,Wrapper 持有了实际的扩展点实现类。</p>\n<p>扩展点的 Wrapper 类可以有多个,也可以根据需要新增。</p>\n<p>通过 Wrapper 类可以把所有扩展点公共逻辑移至 Wrapper 中。新加的 Wrapper 在所有的扩展点上添加了逻辑,有些类似 AOP,即 Wrapper 代理了扩展点。</p>\n<h3>扩展点自动装配</h3>\n<p>加载扩展点时,自动注入依赖的扩展点。加载扩展点时,扩展点实现类的成员如果为其它扩展点类型,<code>ExtensionLoader</code> 在会自动注入依赖的扩展点。<code>ExtensionLoader</code> 通过扫描扩展点实现类的所有 setter 方法来判定其成员。即 <code>ExtensionLoader</code> 会执行扩展点的拼装操作。</p>\n<p>示例:有两个为扩展点 <code>CarMaker</code>(造车者)、<code>WheelMaker</code> (造轮者)</p>\n<p>接口类如下:</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">CarMaker</span> </span>{\n    <span class="hljs-function">Car <span class="hljs-title">makeCar</span><span class="hljs-params">()</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">WheelMaker</span> </span>{\n    <span class="hljs-function">Wheel <span class="hljs-title">makeWheel</span><span class="hljs-params">()</span></span>;\n}\n</code></pre>\n<p><code>CarMaker</code> 的一个实现类:</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">RaceCarMaker</span> <span class="hljs-title">implemenets</span> <span class="hljs-title">CarMaker</span> </span>{\n    WheelMaker wheelMaker;\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">setWheelMaker</span><span class="hljs-params">(WheelMaker wheelMaker)</span> </span>{\n        <span class="hljs-keyword">this</span>.wheelMaker = wheelMaker;\n    }\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> Car <span class="hljs-title">makeCar</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-comment">// ...</span>\n        Wheel wheel = wheelMaker.makeWheel();\n        <span class="hljs-comment">// ...</span>\n        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> RaceCar(wheel, ...);\n    }\n}\n</code></pre>\n<p><code>ExtensionLoader</code> 加载 <code>CarMaker</code> 的扩展点实现 <code>RaceCar</code> 时,<code>setWheelMaker</code> 方法的 <code>WheelMaker</code> 也是扩展点则会注入 <code>WheelMaker</code> 的实现。</p>\n<p>这里带来另一个问题,<code>ExtensionLoader</code> 要注入依赖扩展点时,如何决定要注入依赖扩展点的哪个实现。在这个示例中,即是在多个<code>WheelMaker</code> 的实现中要注入哪个。</p>\n<p>这个问题在下面一点 <a href="#%E6%89%A9%E5%B1%95%E7%82%B9%E8%87%AA%E9%80%82%E5%BA%94">扩展点自适应</a> 中说明。</p>\n<h3>扩展点自适应</h3>\n<p><code>ExtensionLoader</code> 注入的依赖扩展点是一个 <code>Adaptive</code> 实例,直到扩展点方法执行时才决定调用是一个扩展点实现。</p>\n<p>Dubbo 使用 URL 对象(包含了Key-Value)传递配置信息。</p>\n<p>扩展点方法调用会有URL参数(或是参数有URL成员)</p>\n<p>这样依赖的扩展点也可以从URL拿到配置信息,所有的扩展点自己定好配置的Key后,配置信息从URL上从最外层传入。URL在配置传递上即是一条总线。</p>\n<p>示例:有两个为扩展点 <code>CarMaker</code>、<code>WheelMaker</code></p>\n<p>接口类如下:</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">CarMaker</span> </span>{\n    <span class="hljs-function">Car <span class="hljs-title">makeCar</span><span class="hljs-params">(URL url)</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">WheelMaker</span> </span>{\n    <span class="hljs-function">Wheel <span class="hljs-title">makeWheel</span><span class="hljs-params">(URL url)</span></span>;\n}\n</code></pre>\n<p><code>CarMaker</code> 的一个实现类:</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">RaceCarMaker</span> <span class="hljs-title">implemenets</span> <span class="hljs-title">CarMaker</span> </span>{\n    WheelMaker wheelMaker;\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">setWheelMaker</span><span class="hljs-params">(WheelMaker wheelMaker)</span> </span>{\n        <span class="hljs-keyword">this</span>.wheelMaker = wheelMaker;\n    }\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> Car <span class="hljs-title">makeCar</span><span class="hljs-params">(URL url)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n        Wheel wheel = wheelMaker.makeWheel(url);\n        <span class="hljs-comment">// ...</span>\n        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> RaceCar(wheel, ...);\n    }\n}\n</code></pre>\n<p>当上面执行</p>\n<pre><code class="language-java"><span class="hljs-comment">// ...</span>\nWheel wheel = wheelMaker.makeWheel(url);\n<span class="hljs-comment">// ...</span>\n</code></pre>\n<p>时,注入的 <code>Adaptive</code> 实例可以提取约定 Key 来决定使用哪个 <code>WheelMaker</code> 实现来调用对应实现的真正的 <code>makeWheel</code> 方法。如提取 <code>wheel.type</code>, key 即 <code>url.get(&quot;wheel.type&quot;)</code> 来决定 <code>WheelMake</code> 实现。<code>Adaptive</code> 实例的逻辑是固定,指定提取的 URL 的 Key,即可以代理真正的实现类上,可以动态生成。</p>\n<p>在 Dubbo 的 <code>ExtensionLoader</code> 的扩展点类对应的 <code>Adaptive</code> 实现是在加载扩展点里动态生成。指定提取的 URL 的 Key 通过 <code>@Adaptive</code> 注解在接口方法上提供。</p>\n<p>下面是 Dubbo 的 Transporter 扩展点的代码:</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">Transporter</span> </span>{\n    <span class="hljs-meta">@Adaptive</span>({<span class="hljs-string">"server"</span>, <span class="hljs-string">"transport"</span>})\n    <span class="hljs-function">Server <span class="hljs-title">bind</span><span class="hljs-params">(URL url, ChannelHandler handler)</span> <span class="hljs-keyword">throws</span> RemotingException</span>;\n \n    <span class="hljs-meta">@Adaptive</span>({<span class="hljs-string">"client"</span>, <span class="hljs-string">"transport"</span>})\n    <span class="hljs-function">Client <span class="hljs-title">connect</span><span class="hljs-params">(URL url, ChannelHandler handler)</span> <span class="hljs-keyword">throws</span> RemotingException</span>;\n}\n</code></pre>\n<p>对于 bind() 方法,Adaptive 实现先查找 <code>server</code> key,如果该 Key 没有值则找 <code>transport</code> key 值,来决定代理到哪个实际扩展点。</p>\n<h3>扩展点自动激活</h3>\n<p>对于集合类扩展点,比如:<code>Filter</code>, <code>InvokerListener</code>, <code>ExportListener</code>, <code>TelnetHandler</code>, <code>StatusChecker</code> 等,可以同时加载多个实现,此时,可以用自动激活来简化配置,如:</p>\n<pre><code class="language-java"><span class="hljs-keyword">import</span> com.alibaba.dubbo.common.extension.Activate;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Filter;\n \n<span class="hljs-meta">@Activate</span> <span class="hljs-comment">// 无条件自动激活</span>\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxFilter</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Filter</span> </span>{\n    <span class="hljs-comment">// ...</span>\n}\n</code></pre>\n<p>或:</p>\n<pre><code class="language-java"><span class="hljs-keyword">import</span> com.alibaba.dubbo.common.extension.Activate;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Filter;\n \n<span class="hljs-meta">@Activate</span>(<span class="hljs-string">"xxx"</span>) <span class="hljs-comment">// 当配置了xxx参数,并且参数为有效值时激活,比如配了cache="lru",自动激活CacheFilter。</span>\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxFilter</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Filter</span> </span>{\n    <span class="hljs-comment">// ...</span>\n}\n</code></pre>\n<p>或:</p>\n<pre><code class="language-java"><span class="hljs-keyword">import</span> com.alibaba.dubbo.common.extension.Activate;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Filter;\n \n<span class="hljs-meta">@Activate</span>(group = <span class="hljs-string">"provider"</span>, value = <span class="hljs-string">"xxx"</span>) <span class="hljs-comment">// 只对提供方激活,group可选"provider"或"consumer"</span>\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxFilter</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Filter</span> </span>{\n    <span class="hljs-comment">// ...</span>\n}\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>注意:这里的配置文件是放在你自己的 jar 包内,不是 dubbo 本身的 jar 包内,Dubbo 会全 ClassPath 扫描所有 jar 包内同名的这个文件,然后进行合并 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>注意:扩展点使用单一实例加载(请确保扩展实现的线程安全性),缓存在 <code>ExtensionLoader</code> 中 <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"dev/SUMMARY.md",__html:'<h1>Summary</h1>\n<ul>\n<li><a href="./build.md">1 源码构建</a></li>\n<li><a href="./design.md">2 框架设计</a></li>\n<li><a href="./SPI.md">3 扩展点加载</a></li>\n<li><a href="./implementation.md">4 实现细节</a></li>\n<li><a href="./impls/introduction.md">5 SPI 扩展实现</a>\n<ul>\n<li><a href="./impls/protocol.md">5.1 协议扩展</a></li>\n<li><a href="./impls/filter.md">5.2 调用拦截扩展</a></li>\n<li><a href="./impls/invoker-listener.md">5.3 引用监听扩展</a></li>\n<li><a href="./impls/exporter-listener.md">5.4 暴露监听扩展</a></li>\n<li><a href="./impls/cluster.md">5.5 集群扩展</a></li>\n<li><a href="./impls/router.md">5.6 路由扩展</a></li>\n<li><a href="./impls/load-balance.md">5.7 负载均衡扩展</a></li>\n<li><a href="./impls/merger.md">5.8 合并结果扩展</a></li>\n<li><a href="./impls/registry.md">5.9 注册中心扩展</a></li>\n<li><a href="./impls/monitor.md">5.10 监控中心扩展</a></li>\n<li><a href="./impls/extension-factory.md">5.11 扩展点加载扩展</a></li>\n<li><a href="./impls/proxy-factory.md">5.12 动态代理扩展</a></li>\n<li><a href="./impls/compiler.md">5.13 编译器扩展</a></li>\n<li><a href="./impls/dispatcher.md">5.14 消息派发扩展</a></li>\n<li><a href="./impls/threadpool.md">5.15 线程池扩展</a></li>\n<li><a href="./impls/serialize.md">5.16 序列化扩展</a></li>\n<li><a href="./impls/remoting.md">5.17 网络传输扩展</a></li>\n<li><a href="./impls/exchanger.md">5.18 信息交换扩展</a></li>\n<li><a href="./impls/networker.md">5.19 组网扩展</a></li>\n<li><a href="./impls/telnet-handler.md">5.20 Telnet 命令扩展</a></li>\n<li><a href="./impls/status-checker.md">5.21 状态检查扩展</a></li>\n<li><a href="./impls/container.md">5.22 容器扩展</a></li>\n<li><a href="./impls/page.md">5.23 页面扩展</a></li>\n<li><a href="./impls/cache.md">5.24 缓存扩展</a></li>\n<li><a href="./impls/validation.md">5.25 验证扩展</a></li>\n<li><a href="./impls/logger-adapter.md">5.26 日志适配扩展</a></li>\n</ul>\n</li>\n<li><a href="./contract.md">6 公共契约</a></li>\n<li><a href="./coding.md">7 编码约定</a></li>\n<li><a href="./principals/introduction.md">8 设计原则</a>\n<ul>\n<li><a href="./principals/code-detail.md">8.1 魔鬼在细节</a></li>\n<li><a href="./principals/general-knowledge.md">8.2 一些设计上的基本常识</a></li>\n<li><a href="./principals/expansibility.md">8.3 谈谈扩充式扩展与增量式扩展</a></li>\n<li><a href="./principals/configuration.md">8.4 配置设计</a></li>\n<li><a href="./principals/robustness.md">8.5 设计实现的健壮性</a></li>\n<li><a href="./principals/dummy.md">8.6 防痴呆设计</a></li>\n<li><a href="./principals/extension.md">8.7 扩展点重构</a></li>\n</ul>\n</li>\n<li><a href="./release.md">9 版本管理</a></li>\n<li><a href="./contribution.md">10 贡献</a></li>\n<li><a href="./checklist.md">11 检查列表</a></li>\n<li><a href="./code-smell.md">12 坏味道</a></li>\n<li><a href="./TCK.md">13 技术兼容性测试</a></li>\n</ul>\n'},{filename:"dev/TCK.md",__html:'<h1>技术兼容性测试</h1>\n<p>Dubbo 的协议,通讯,序列化,注册中心,负载均策等扩展点,都有多种可选策略,以应对不同应用场景,而我们的测试用例很分散,当用户自己需要加一种新的实现时,总是不确定能否满足扩展点的完整契约。</p>\n<p>所以,我们需要对核心扩展点写 <a href="http://en.wikipedia.org/wiki/Technology_Compatibility_Kit">TCK</a> (Technology Compatibility Kit),用户增加一种扩展实现,只需通过 TCK,即可确保与框架的其它部分兼容运行,可以有效提高整体健状性,也方便第三方扩展者接入,加速开源社区的成熟。</p>\n<p>开源社区的行知同学已着手研究这一块,他的初步想法是借鉴 JBoss 的 CDI-TCK,做一个 Dubbo 的 TCK 基础框架,在此之上实现 Dubbo 的扩展点 TCK 用例。</p>\n<p>参见:<a href="http://docs.jboss.org/cdi/tck/reference/1.0.1-Final/html/introduction.html">http://docs.jboss.org/cdi/tck/reference/1.0.1-Final/html/introduction.html</a></p>\n<p>如果大家有兴趣,也可以一起研究,和行知一块讨论。</p>\n<h4>Protocol TCK</h4>\n<blockquote>\n<p>TODO</p>\n</blockquote>\n<h4>Registry TCK</h4>\n<blockquote>\n<p>TODO</p>\n</blockquote>\n'},{filename:"dev/build.md",__html:'<h1>源码构建</h1>\n<h2>代码签出</h2>\n<p>通过以下的这个命令签出最新的项目源码 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>:</p>\n<pre><code class="language-sh">git <span class="hljs-built_in">clone</span> https://github.com/apache/incubator-dubbo.git dubbo\n</code></pre>\n<h2>分支</h2>\n<p>我们使用 master 作为主干版本的开发,使用分支作为维护版本。可以通过 <a href="https://github.com/apache/incubator-dubbo/tags">https://github.com/apache/incubator-dubbo/tags</a> 来查看所有版本的标签。</p>\n<h2>构建</h2>\n<p>Dubbo 使用 <a href="http://maven.apache.org">maven</a> 作为构建工具。</p>\n<p>要求</p>\n<ul>\n<li>Java 1.5 以上的版本</li>\n<li>Maven 2.2.1 或者以上的版本</li>\n</ul>\n<p>构建之前需要配置以下的 <code>MAVEN_OPTS</code></p>\n<pre><code class="language-sh"><span class="hljs-built_in">export</span> MAVEN_OPTS=-Xmx1024m -XX:MaxPermSize=512m\n</code></pre>\n<p>使用以下命令做一次构建</p>\n<pre><code class="language-sh">mvn clean install\n</code></pre>\n<p>可以通过以下的构建命令来跳过单元测试</p>\n<pre><code class="language-sh">mvn install -Dmaven.test.skip\n</code></pre>\n<h2>构建源代码 jar 包</h2>\n<p>通过以下命令以构建 Dubbo 的源代码 jar 包,方便用来调试 Dubbo 源代码</p>\n<pre><code class="language-sh">mvn clean <span class="hljs-built_in">source</span>:jar install -Dmaven.test.skip\n</code></pre>\n<h2>IDE 支持</h2>\n<p>使用以下命令来生成 IDE 的工程</p>\n<h3>Intellij Idea</h3>\n<pre><code class="language-sh">mvn idea:idea\n</code></pre>\n<h3>eclipse</h3>\n<pre><code class="language-sh">mvn eclipse:eclipse\n</code></pre>\n<p>在 eclipse 中导入</p>\n<p>首先,需要在 eclipse 中配置 maven 仓库。通过 Preferences -&gt; Java -&gt; Build Path -&gt; Classpath 定义 <code>M2_REPO</code> 的 classpath 变量指向本地的 maven 仓库。 <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup></p>\n<p>也可以通过以下的 maven 命令配置:</p>\n<pre><code class="language-sh">mvn eclipse:configure-workspace -Declipse.workspace=/path/to/the/workspace/\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>也可以直接在 <a href="https://github.com/apache/incubator-dubbo">https://github.com/apache/incubator-dubbo</a> 上浏览源代码 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>UNIX 下的路径是 ${HOME}/.m2/repository, Windows 下的路径是 C:\\Documents and Settings&lt;user&gt;.m2\\repository <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"dev/checklist.md",__html:"<h1>检查列表</h1>\n<h2>发布前 checklist</h2>\n<ul>\n<li>jira ticket 过一遍</li>\n<li>svn change list</li>\n<li>ticket 关联 code</li>\n<li>test code</li>\n<li>find bugs</li>\n</ul>\n<h2>修复时 checklist</h2>\n<ul>\n<li>修复代码前先建 ticket</li>\n<li>修复代码前先写测试用例</li>\n<li>需要伙伴检查</li>\n<li>test code(正常流程/异常流程)</li>\n<li>讲一遍逻辑</li>\n<li>契约文档化</li>\n<li>以上内容都写到ticket的评论上</li>\n<li>代码注释写清楚,用中文无妨</li>\n<li>每个版本要有 owner,确保 scope 和 check</li>\n</ul>\n<h2>Partner Check</h2>\n<ul>\n<li>Partner 以用户的方式运行一下功能</li>\n<li>Partner 发现问题、添加测试(集成测试)复现总是;Owner 完成实现。(保证两方的时间投入 PatternerCheck 的给予时间保证)</li>\n<li>Owner 向 Partner 讲述一遍实现。</li>\n</ul>\n"},{filename:"dev/code-smell.md",__html:'<h1>坏味道</h1>\n<p>这里记录的是 Dubbo 设计或实现不优雅的地方。</p>\n<h2>URL 转换</h2>\n<h3>1. 点对点暴露和引用服务</h3>\n<p>直接暴露服务:</p>\n<pre><code>EXPORT(dubbo://provider-address/com.xxx.XxxService?version=1.0.0&quot;)\n</code></pre>\n<p>点对点直连服务:</p>\n<pre><code>REFER(dubbo://provider-address/com.xxx.XxxService?version=1.0.0)\n</code></pre>\n<h3>2. 通过注册中心暴露服务</h3>\n<p>向注册中心暴露服务:</p>\n<pre><code>EXPORT(registry://registry-address/com.alibaba.dubbo.registry.RegistrySerevice?registry=dubbo&amp;export=ENCODE(dubbo://provider-address/com.xxx.XxxService?version=1.0.0))\n</code></pre>\n<p>获取注册中心:</p>\n<pre><code>url.setProtocol(url.getParameter(&quot;registry&quot;, &quot;dubbo&quot;))\nGETREGISTRY(dubbo://registry-address/com.alibaba.dubbo.registry.RegistrySerevice)\n</code></pre>\n<p>注册服务地址:</p>\n<pre><code>url.getParameterAndDecoded(&quot;export&quot;))\nREGISTER(dubbo://provider-address/com.xxx.XxxService?version=1.0.0)\n</code></pre>\n<h3>3. 通过注册中心引用服务</h3>\n<p>从注册中心订阅服务:</p>\n<pre><code>REFER(registry://registry-address/com.alibaba.dubbo.registry.RegistrySerevice?registry=dubbo&amp;refer=ENCODE(version=1.0.0))\n</code></pre>\n<p>获取注册中心:</p>\n<pre><code>url.setProtocol(url.getParameter(&quot;registry&quot;, &quot;dubbo&quot;))\nGETREGISTRY(dubbo://registry-address/com.alibaba.dubbo.registry.RegistrySerevice)\n</code></pre>\n<p>订阅服务地址:</p>\n<pre><code>url.addParameters(url.getParameterAndDecoded(&quot;refer&quot;))\nSUBSCRIBE(dubbo://registry-address/com.xxx.XxxService?version=1.0.0)\n</code></pre>\n<p>通知服务地址:</p>\n<pre><code>url.addParameters(url.getParameterAndDecoded(&quot;refer&quot;))\nNOTIFY(dubbo://provider-address/com.xxx.XxxService?version=1.0.0)\n</code></pre>\n<h3>4. 注册中心推送路由规则</h3>\n<p>注册中心路由规则推送:</p>\n<pre><code>NOTIFY(route://registry-address/com.xxx.XxxService?router=script&amp;type=js&amp;rule=ENCODE(function{...}))\n</code></pre>\n<p>获取路由器:</p>\n<pre><code>url.setProtocol(url.getParameter(&quot;router&quot;, &quot;script&quot;))\nGETROUTE(script://registry-address/com.xxx.XxxService?type=js&amp;rule=ENCODE(function{...}))\n</code></pre>\n<h3>5. 从文件加载路由规则</h3>\n<p>从文件加载路由规则:</p>\n<pre><code>GETROUTE(file://path/file.js?router=script)\n</code></pre>\n<p>获取路由器:</p>\n<pre><code>url.setProtocol(url.getParameter(&quot;router&quot;, &quot;script&quot;)).addParameter(&quot;type&quot;, SUFFIX(file)).addParameter(&quot;rule&quot;, READ(file))\nGETROUTE(script://path/file.js?type=js&amp;rule=ENCODE(function{...}))\n</code></pre>\n<h2>调用参数</h2>\n<ul>\n<li>path 服务路径</li>\n<li>group 服务分组</li>\n<li>version 服务版本</li>\n<li>dubbo 使用的 dubbo 版本</li>\n<li>token 验证令牌</li>\n<li>timeout 调用超时</li>\n</ul>\n<h2>扩展点的加载</h2>\n<h3>1. 自适应扩展点</h3>\n<p>ExtensionLoader 加载扩展点时,会检查扩展点的属性(通过set方法判断),如该属性是扩展点类型,则会注入扩展点对象。因为注入时不能确定使用哪个扩展点(在使用时确定),所以注入的是一个自适应扩展(一个代理)。自适应扩展点调用时,选取一个真正的扩展点,并代理到其上完成调用。Dubbo 是根据调用方法参数(上面有调用哪个扩展点的信息)来选取一个真正的扩展点。</p>\n<p>在 Dubbo 给定所有的扩展点上调用都有 URL 参数(整个扩展点网的上下文信息)。自适应扩展即是从 URL 确定要调用哪个扩展点实现。URL 哪个 Key 的 Value 用来确定使用哪个扩展点,这个信息通过的 <code>@Adaptive</code> 注解在方法上说明。</p>\n<pre><code class="language-java"><span class="hljs-meta">@Extension</span>\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Car</span> </span>{\n    <span class="hljs-meta">@Adaptive</span>({<span class="hljs-string">"http://10.20.160.198/wiki/display/dubbo/car.type"</span>, <span class="hljs-string">"http://10.20.160.198/wiki/display/dubbo/transport.type"</span>})\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">run</span><span class="hljs-params">(URL url, Type1 arg1, Type2 arg2)</span></span>;\n}\n</code></pre>\n<p>由于自适应扩展点的上面的约定,ExtensionLoader 会为扩展点自动生成自适应扩展点类(通过字节码),并将其实例注入。</p>\n<p>ExtensionLoader 生成的自适应扩展点类如下:</p>\n<pre><code class="language-java">package &lt;扩展点接口所在包&gt;;\n \npublic class &lt;扩展点接口名&gt;$Adpative implements &lt;扩展点接口&gt; {\n    public &lt;有@Adaptive注解的接口方法&gt;(&lt;方法参数&gt;) {\n        if(是否有URL类型方法参数?) 使用该URL参数\n        else if(是否有方法类型上有URL属性) 使用该URL属性\n        # &lt;else 在加载扩展点生成自适应扩展点类时抛异常,即加载扩展点失败!&gt;\n         \n        if(获取的URL == null) {\n            throw new IllegalArgumentException("url == null");\n        }\n \n        根据@Adaptive注解上声明的Key的顺序,从URL获致Value,作为实际扩展点名。\n        如URL没有Value,则使用缺省扩展点实现。如没有扩展点, throw new IllegalStateException("Fail to get extension");\n \n        在扩展点实现调用该方法,并返回结果。\n    }\n \n    public &lt;有@Adaptive注解的接口方法&gt;(&lt;方法参数&gt;) {\n        throw new UnsupportedOperationException("is not adaptive method!");\n    }\n}\n</code></pre>\n<p><code>@Adaptive</code> 注解使用如下:</p>\n<p>如果 URL 这些 Key 都没有 Value,使用缺省的扩展(在接口的 Default 中设定的值)。比如,String[] {&quot;key1&quot;, &quot;key2&quot;},表示先在 URL 上找 key1 的 Value 作为要 Adapt 成的 Extension 名;key1 没有 Value,则使用 key2 的 Value 作为要 Adapt 成的 Extension 名。 key2 没有 Value,使用缺省的扩展。如果没有设定缺省扩展,则方法调用会抛出 IllegalStateException。如果不设置则缺省使用 Extension 接口类名的点分隔小写字串。即对于 Extension 接口 <code>com.alibaba.dubbo.xxx.YyyInvokerWrapper</code> 的缺省值为 <code>new String[] {&quot;yyy.invoker.wrapper&quot;}</code></p>\n<h2>Callback 功能</h2>\n<h3>1. 参数回调</h3>\n<p>主要原理: 在一个 consumer-&gt;provider 的长连接上,自动在 Consumer 端暴露一个服务(实现方法参数上声明的接口A),provider 端便可反向调用到 consumer 端的接口实例。</p>\n<p>实现细节:</p>\n<ul>\n<li>为了在传输时能够对回调接口实例进行转换,自动暴露与自动引用目前在 DubboCodec 中实现。此处需要考虑将此逻辑与 codec 逻辑分离。</li>\n<li>在根据 invocation 信息获取 exporter 时,需要判断是否是回调,如果是回调,会从 attachments 中取得回调服务实例的 id,在获取 exporter,此处用于 consumer 端可以对同一个 callback 接口做不同的实现。</li>\n</ul>\n<h3>2. 事件通知</h3>\n<p>主要原理:Consumer 在 invoke 方法时,判断如果有配置 onreturn/onerror... 则将 onreturn 对应的参数值(实例方法)加入到异步调用的回调列表中。</p>\n<p>实现细节:参数的传递采用 URL,但 URL 中没有支持 string-object,所以将实例方法存储在 staticMap 中,此处实现需要进行改造。</p>\n<h2>Lazy连接</h2>\n<p>DubboProtocol 特有功能,默认关闭。</p>\n<p>当客户端与服务端创建代理时,暂不建立 tcp 长连接,当有数据请求时在做连接初始化。</p>\n<p>此项功能自动关闭连接重试功能,开启发送重试功能(即发送数据时如果连接已断开,尝试重新建立连接)</p>\n<h2>共享连接</h2>\n<p>DubboProtocol 特有功能,默认开启。</p>\n<p>JVM A 暴露了多个服务,JVM B 引用了 A 中的多个服务,共享连接是说 A 与 B 多个服务调用是通过同一个 TCP 长连接进行数据传输,已达到减少服务端连接数的目的.</p>\n<p>实现细节:对于同一个地址由于使用了共享连接,那 invoker 的 destroy 就需要特别注意,一方面要满足对同一个地址 refer 的 invoker 全部 destroy 后,连接需要关闭,另一方面还需要注意如何避免部分 invoker destroy 时不能关闭连接。在实现中采用了引用计数的方案,但为了防范,在连接关闭时,重新建立了一个 Lazy connection (称为幽灵连接), 用于当出现异常场景时,避免影响业务逻辑的正常调用.</p>\n<h2>sticky 策略</h2>\n<p>有多个服务提供者的情况下,配置了 sticky 后,在提供者可用的情况下,调用会继续发送到上一次的服务提供者。sticky 策略默认开启了连接的 lazy 选项, 用于避免开启无用的连接.</p>\n<h2>服务提供者选择逻辑</h2>\n<ol start="0">\n<li>存在多个服务提供者的情况下,首先根据 Loadbalance 进行选择,如果选择的 provider 处于可用状态,则进行后续调用</li>\n<li>如果第一步选择的服务提供者不可用,则从剩余服务提供者列表中继续选择,如果可用,进行后续调用</li>\n<li>如果所有的服务提供者都不可用,重新遍历整个列表(优先从没有选过的列表中选择),判断是否有可用的服务提供者(选择过程中,不可用的服务提供者可能会恢复到可用状态),如果有,则进行后续调用</li>\n<li>如果第三步没有选择出可用的服务提供者,会选第一步选出的 invoker 中的下一个(如果不是最后一个),避免碰撞。</li>\n</ol>\n'},{filename:"dev/coding.md",__html:'<h1>编码约定</h1>\n<h2>代码风格</h2>\n<p>Dubbo 的源代码和 JavaDoc 遵循以下的规范:</p>\n<ul>\n<li><a href="http://www.oracle.com/technetwork/java/codeconvtoc-136057.html">Code Conventions for the Java Programming Language</a></li>\n<li><a href="http://www.oracle.com/technetwork/java/javase/documentation/index-137868.html">How to Write Doc Comments for the Javadoc Tool</a></li>\n</ul>\n<h2>异常和日志</h2>\n<ul>\n<li>尽可能携带完整的上下文信息,比如出错原因,出错的机器地址,调用对方的地址,连的注册中心地址,使用 Dubbo 的版本等。</li>\n<li>尽量将直接原因写在最前面,所有上下文信息,在原因后用键值对显示。</li>\n<li>抛出异常的地方不用打印日志,由最终处理异常者决定打印日志的级别,吃掉异常必需打印日志。</li>\n<li>打印 <code>ERROR</code> 日志表示需要报警,打印 <code>WARN</code> 日志表示可以自动恢复,打印 <code>INFO</code> 表示正常信息或完全不影响运行。</li>\n<li>建议应用方在监控中心配置 <code>ERROR</code> 日志实时报警,<code>WARN</code> 日志每周汇总发送通知。</li>\n<li><code>RpcException</code> 是 Dubbo 对外的唯一异常类型,所有内部异常,如果要抛出给用户,必须转为 <code>RpcException</code>。</li>\n<li><code>RpcException</code> 不能有子类型,所有类型信息用 ErrorCode 标识,以便保持兼容。</li>\n</ul>\n<h2>配置和 URL</h2>\n<ul>\n<li>配置对象属性首字母小写,多个单词用驼峰命名 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>。</li>\n<li>配置属性全部用小写,多个单词用&quot;-&quot;号分隔 <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>。</li>\n<li>URL参数全部用小写,多个单词用&quot;.&quot;号分隔 <sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup>。</li>\n<li>尽可能用 URL 传参,不要自定义 Map 或其它上下文格式,配置信息也转成 URL 格式使用。</li>\n<li>尽量减少 URL 嵌套,保持 URL 的简洁性。</li>\n</ul>\n<h2>单元和集成测试</h2>\n<ul>\n<li>单元测试统一用 JUnit 和 EasyMock,集成测试用 TestNG,数据库测试用 DBUnit。</li>\n<li>保持单元测试用例的运行速度,不要将性能和大的集成用例放在单元测试中。</li>\n<li>保持单元测试的每个用例都用 <code>try...finally</code> 或 <code>tearDown</code> 释放资源。</li>\n<li>减少 while 循环等待结果的测试用例,对定时器和网络的测试,用以将定时器中的逻辑抽为方法测试。</li>\n<li>对于容错行为的测试,比如 failsafe 的测试,统一用 <code>LogUtil</code> 断言日志输出。</li>\n</ul>\n<h2>扩展点基类与 AOP</h2>\n<ul>\n<li>AOP 类都命名为 <code>XxxWrapper</code>,基类都命名为 <code>AbstractXxx</code>。</li>\n<li>扩展点之间的组合将关系由 AOP 完成,<code>ExtensionLoader</code> 只负载加载扩展点,包括 AOP 扩展。</li>\n<li>尽量采用 IoC 注入扩展点之间的依赖,不要直接依赖 <code>ExtensionLoader</code> 的工厂方法。</li>\n<li>尽量采用 AOP 实现扩展点的通用行为,而不要用基类,比如负载均衡之前的 <code>isAvailable</code> 检查,它是独立于负载均衡之外的,不需要检查的是URL参数关闭。</li>\n<li>对多种相似类型的抽象,用基类实现,比如 RMI, Hessian 等第三方协议都已生成了接口代理,只需将将接口代理转成 <code>Invoker</code> 即可完成桥接,它们可以用公共基类实现此逻辑。</li>\n<li>基类也是 SPI 的一部分,每个扩展点都应该有方便使用的基类支持。</li>\n</ul>\n<h2>模块与分包</h2>\n<ul>\n<li>基于复用度分包,总是一起使用的放在同一包下,将接口和基类分成独立模块,大的实现也使用独立模块。</li>\n<li>所有接口都放在模块的根包下,基类放在 support 子包下,不同实现用放在以扩展点名字命名的子包下。</li>\n<li>尽量保持子包依赖父包,而不要反向。</li>\n</ul>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>Java 约定 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>Spring 约定 <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn3" class="footnote-item"><p>Dubbo 约定 <a href="#fnref3" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"dev/contract.md",__html:"<h1>公共契约</h1>\n<p>这里记录的是 Dubbo 公共契约,希望所有扩展点遵守。</p>\n<h2>URL</h2>\n<ul>\n<li>所有扩展点参数都包含 URL 参数,URL 作为上下文信息贯穿整个扩展点设计体系。</li>\n<li>URL 采用标准格式:<code>protocol://username:password@host:port/path?key=value&amp;key=value</code></li>\n</ul>\n<h2>日志</h2>\n<ul>\n<li>如果不可恢复或需要报警,打印 ERROR 日志。</li>\n<li>如果可恢复异常,或瞬时的状态不一致,打印 WARN 日志。</li>\n<li>正常运行时的中间状态提示,打印 INFO 日志。</li>\n</ul>\n"},{filename:"dev/contribution.md",__html:"<h1>贡献</h1>\n<h2>流程</h2>\n<ul>\n<li>如果是扩展功能,直接新增工程,黑盒依赖 Dubbo 进行扩展。</li>\n<li>如果是改 BUG,或修改框架本身,可以从 Dubb 的 GitHub 上 Fork 工程。</li>\n<li>修改后通过 Push Request 反馈修改。</li>\n</ul>\n<h2>任务</h2>\n<table>\n<thead>\n<tr>\n<th>功能</th>\n<th>分类</th>\n<th>优先级</th>\n<th>状态</th>\n<th>认领者</th>\n<th>计划完成时间</th>\n<th>进度</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>《用户指南》翻译</td>\n<td>文档</td>\n<td>高</td>\n<td>未认领</td>\n<td>待定</td>\n<td>待定</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>《开发指南》翻译</td>\n<td>文档</td>\n<td>高</td>\n<td>未认领</td>\n<td>待定</td>\n<td>待定</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>扩展点兼容性测试</td>\n<td>测试</td>\n<td>高</td>\n<td>已认领</td>\n<td>罗立树</td>\n<td>待定</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>性能基准测试</td>\n<td>测试</td>\n<td>高</td>\n<td>未认领</td>\n<td>待定</td>\n<td>待定</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>功能单元测试</td>\n<td>测试</td>\n<td>高</td>\n<td>未认领</td>\n<td>待定</td>\n<td>待定</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>JTA/XA分布式事务</td>\n<td>拦截扩展</td>\n<td>高</td>\n<td>未认领</td>\n<td>待定</td>\n<td>待定</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>Thrift</td>\n<td>协议扩展</td>\n<td>高</td>\n<td>开发完成</td>\n<td>闾刚</td>\n<td>2012-04-27</td>\n<td>90%</td>\n</tr>\n<tr>\n<td>ICE</td>\n<td>协议扩展</td>\n<td>高</td>\n<td>未认领</td>\n<td>待定</td>\n<td>待定</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>ACE</td>\n<td>协议扩展</td>\n<td>低</td>\n<td>未认领</td>\n<td>待定</td>\n<td>待定</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>JSON-RPC</td>\n<td>协议扩展</td>\n<td>低</td>\n<td>未认领</td>\n<td>待定</td>\n<td>待定</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>XML-RPC</td>\n<td>协议扩展</td>\n<td>低</td>\n<td>未认领</td>\n<td>待定</td>\n<td>待定</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>JSR181&amp;CXF(WebService)</td>\n<td>协议扩展</td>\n<td>高</td>\n<td>开发完成</td>\n<td>白文志</td>\n<td>2012-04-27</td>\n<td>90%</td>\n</tr>\n<tr>\n<td>JSR311&amp;JSR339(RestfulWebService)</td>\n<td>协议扩展</td>\n<td>高</td>\n<td>未认领</td>\n<td>待定</td>\n<td>待定</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>JMS&amp;ActiveMQ</td>\n<td>协议扩展</td>\n<td>高</td>\n<td>未认领</td>\n<td>待定</td>\n<td>待定</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>Protobuf</td>\n<td>序列化扩展</td>\n<td>高</td>\n<td>调研</td>\n<td>朱启恒</td>\n<td>2012-02-30</td>\n<td>20%</td>\n</tr>\n<tr>\n<td>Avro</td>\n<td>序列化扩展</td>\n<td>低</td>\n<td>未认领</td>\n<td>待定</td>\n<td>待定</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>XSocket</td>\n<td>传输扩展</td>\n<td>低</td>\n<td>未认领</td>\n<td>待定</td>\n<td>待定</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>CGLib</td>\n<td>动态代理扩展</td>\n<td>低</td>\n<td>未认领</td>\n<td>待定</td>\n<td>待定</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>JNDI</td>\n<td>注册中心扩展</td>\n<td>高</td>\n<td>未认领</td>\n<td>待定</td>\n<td>待定</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>LDAP</td>\n<td>注册中心扩展</td>\n<td>低</td>\n<td>未认领</td>\n<td>待定</td>\n<td>待定</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>JSR140&amp;SLP</td>\n<td>注册中心扩展</td>\n<td>高</td>\n<td>未认领</td>\n<td>待定</td>\n<td>待定</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>UDDI</td>\n<td>注册中心扩展</td>\n<td>高</td>\n<td>未认领</td>\n<td>待定</td>\n<td>待定</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>JMX</td>\n<td>监控中心扩展</td>\n<td>高</td>\n<td>未认领</td>\n<td>待定</td>\n<td>待定</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>SNMP</td>\n<td>监控中心扩展</td>\n<td>高</td>\n<td>未认领</td>\n<td>待定</td>\n<td>待定</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>Cacti</td>\n<td>监控中心扩展</td>\n<td>高</td>\n<td>未认领</td>\n<td>待定</td>\n<td>待定</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>Nagios</td>\n<td>监控中心扩展</td>\n<td>高</td>\n<td>未认领</td>\n<td>待定</td>\n<td>待定</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>Logstash</td>\n<td>监控中心扩展</td>\n<td>高</td>\n<td>未认领</td>\n<td>待定</td>\n<td>待定</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>JRobin</td>\n<td>监控中心扩展</td>\n<td>高</td>\n<td>未认领</td>\n<td>待定</td>\n<td>待定</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>Maven</td>\n<td>服务安装包仓库</td>\n<td>低</td>\n<td>未认领</td>\n<td>待定</td>\n<td>待定</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>Subversion</td>\n<td>服务安装包仓库</td>\n<td>低</td>\n<td>未认领</td>\n<td>待定</td>\n<td>待定</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>JCR/JSR283</td>\n<td>服务安装包仓库</td>\n<td>低</td>\n<td>未认领</td>\n<td>待定</td>\n<td>待定</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>SimpleDeployer</td>\n<td>本地部署代理</td>\n<td>低</td>\n<td>未认领</td>\n<td>待定</td>\n<td>待定</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>SimpleScheduler</td>\n<td>资源调度器</td>\n<td>低</td>\n<td>未认领</td>\n<td>待定</td>\n<td>待定</td>\n<td>0%</td>\n</tr>\n</tbody>\n</table>\n"},{filename:"dev/design.md",__html:'<h1>框架设计</h1>\n<h2>整体设计</h2>\n<p><img src="sources/images/dubbo-framework.jpg" alt="/dev-guide/images/dubbo-framework.jpg"></p>\n<p>图例说明:</p>\n<ul>\n<li>图中左边淡蓝背景的为服务消费方使用的接口,右边淡绿色背景的为服务提供方使用的接口,位于中轴线上的为双方都用到的接口。</li>\n<li>图中从下至上分为十层,各层均为单向依赖,右边的黑色箭头代表层之间的依赖关系,每一层都可以剥离上层被复用,其中,Service 和 Config 层为 API,其它各层均为 SPI。</li>\n<li>图中绿色小块的为扩展接口,蓝色小块为实现类,图中只显示用于关联各层的实现类。</li>\n<li>图中蓝色虚线为初始化过程,即启动时组装链,红色实线为方法调用过程,即运行时调时链,紫色三角箭头为继承,可以把子类看作父类的同一个节点,线上的文字为调用的方法。</li>\n</ul>\n<h2>各层说明</h2>\n<ul>\n<li><strong>config 配置层</strong>:对外配置接口,以 <code>ServiceConfig</code>, <code>ReferenceConfig</code> 为中心,可以直接初始化配置类,也可以通过 spring 解析配置生成配置类</li>\n<li><strong>proxy 服务代理层</strong>:服务接口透明代理,生成服务的客户端 Stub 和服务器端 Skeleton, 以 <code>ServiceProxy</code> 为中心,扩展接口为 <code>ProxyFactory</code></li>\n<li><strong>registry 注册中心层</strong>:封装服务地址的注册与发现,以服务 URL 为中心,扩展接口为 <code>RegistryFactory</code>, <code>Registry</code>, <code>RegistryService</code></li>\n<li><strong>cluster 路由层</strong>:封装多个提供者的路由及负载均衡,并桥接注册中心,以 <code>Invoker</code> 为中心,扩展接口为 <code>Cluster</code>, <code>Directory</code>, <code>Router</code>, <code>LoadBalance</code></li>\n<li><strong>monitor 监控层</strong>:RPC 调用次数和调用时间监控,以 <code>Statistics</code> 为中心,扩展接口为 <code>MonitorFactory</code>, <code>Monitor</code>, <code>MonitorService</code></li>\n<li><strong>protocol 远程调用层</strong>:封装 RPC 调用,以 <code>Invocation</code>, <code>Result</code> 为中心,扩展接口为 <code>Protocol</code>, <code>Invoker</code>, <code>Exporter</code></li>\n<li><strong>exchange 信息交换层</strong>:封装请求响应模式,同步转异步,以 <code>Request</code>, <code>Response</code> 为中心,扩展接口为 <code>Exchanger</code>, <code>ExchangeChannel</code>, <code>ExchangeClient</code>, <code>ExchangeServer</code></li>\n<li><strong>transport 网络传输层</strong>:抽象 mina 和 netty 为统一接口,以 <code>Message</code> 为中心,扩展接口为 <code>Channel</code>, <code>Transporter</code>, <code>Client</code>, <code>Server</code>, <code>Codec</code></li>\n<li><strong>serialize 数据序列化层</strong>:可复用的一些工具,扩展接口为 <code>Serialization</code>, <code>ObjectInput</code>, <code>ObjectOutput</code>, <code>ThreadPool</code></li>\n</ul>\n<h2>关系说明</h2>\n<ul>\n<li>在 RPC 中,Protocol 是核心层,也就是只要有 Protocol + Invoker + Exporter 就可以完成非透明的 RPC 调用,然后在 Invoker 的主过程上 Filter 拦截点。</li>\n<li>图中的 Consumer 和 Provider 是抽象概念,只是想让看图者更直观的了解哪些类分属于客户端与服务器端,不用 Client 和 Server 的原因是 Dubbo 在很多场景下都使用 Provider, Consumer, Registry, Monitor 划分逻辑拓普节点,保持统一概念。</li>\n<li>而 Cluster 是外围概念,所以 Cluster 的目的是将多个 Invoker 伪装成一个 Invoker,这样其它人只要关注 Protocol 层 Invoker 即可,加上 Cluster 或者去掉 Cluster 对其它层都不会造成影响,因为只有一个提供者时,是不需要 Cluster 的。</li>\n<li>Proxy 层封装了所有接口的透明化代理,而在其它层都以 Invoker 为中心,只有到了暴露给用户使用时,才用 Proxy 将 Invoker 转成接口,或将接口实现转成 Invoker,也就是去掉 Proxy 层 RPC 是可以 Run 的,只是不那么透明,不那么看起来像调本地服务一样调远程服务。</li>\n<li>而 Remoting 实现是 Dubbo 协议的实现,如果你选择 RMI 协议,整个 Remoting 都不会用上,Remoting 内部再划为 Transport 传输层和 Exchange 信息交换层,Transport 层只负责单向消息传输,是对 Mina, Netty, Grizzly 的抽象,它也可以扩展 UDP 传输,而 Exchange 层是在传输层之上封装了 Request-Response 语义。</li>\n<li>Registry 和 Monitor 实际上不算一层,而是一个独立的节点,只是为了全局概览,用层的方式画在一起。</li>\n</ul>\n<h2>模块分包</h2>\n<p><img src="sources/images/dubbo-modules.jpg" alt="/dev-guide/images/dubbo-modules.jpg"></p>\n<p>模块说明:</p>\n<ul>\n<li><strong>dubbo-common 公共逻辑模块</strong>:包括 Util 类和通用模型。</li>\n<li><strong>dubbo-remoting 远程通讯模块</strong>:相当于 Dubbo 协议的实现,如果 RPC 用 RMI协议则不需要使用此包。</li>\n<li><strong>dubbo-rpc 远程调用模块</strong>:抽象各种协议,以及动态代理,只包含一对一的调用,不关心集群的管理。</li>\n<li><strong>dubbo-cluster 集群模块</strong>:将多个服务提供方伪装为一个提供方,包括:负载均衡, 容错,路由等,集群的地址列表可以是静态配置的,也可以是由注册中心下发。</li>\n<li><strong>dubbo-registry 注册中心模块</strong>:基于注册中心下发地址的集群方式,以及对各种注册中心的抽象。</li>\n<li><strong>dubbo-monitor 监控模块</strong>:统计服务调用次数,调用时间的,调用链跟踪的服务。</li>\n<li><strong>dubbo-config 配置模块</strong>:是 Dubbo 对外的 API,用户通过 Config 使用D ubbo,隐藏 Dubbo 所有细节。</li>\n<li><strong>dubbo-container 容器模块</strong>:是一个 Standlone 的容器,以简单的 Main 加载 Spring 启动,因为服务通常不需要 Tomcat/JBoss 等 Web 容器的特性,没必要用 Web 容器去加载服务。</li>\n</ul>\n<p>整体上按照分层结构进行分包,与分层的不同点在于:</p>\n<ul>\n<li>container 为服务容器,用于部署运行服务,没有在层中画出。</li>\n<li>protocol 层和 proxy 层都放在 rpc 模块中,这两层是 rpc 的核心,在不需要集群也就是只有一个提供者时,可以只使用这两层完成 rpc 调用。</li>\n<li>transport 层和 exchange 层都放在 remoting 模块中,为 rpc 调用的通讯基础。</li>\n<li>serialize 层放在 common 模块中,以便更大程度复用。</li>\n</ul>\n<h2>依赖关系</h2>\n<p><img src="sources/images/dubbo-relation.jpg" alt="/dev-guide/images/dubbo-relation.jpg"></p>\n<p>图例说明:</p>\n<ul>\n<li>图中小方块 Protocol, Cluster, Proxy, Service, Container, Registry, Monitor 代表层或模块,蓝色的表示与业务有交互,绿色的表示只对 Dubbo 内部交互。</li>\n<li>图中背景方块 Consumer, Provider, Registry, Monitor 代表部署逻辑拓扑节点。</li>\n<li>图中蓝色虚线为初始化时调用,红色虚线为运行时异步调用,红色实线为运行时同步调用。</li>\n<li>图中只包含 RPC 的层,不包含 Remoting 的层,Remoting 整体都隐含在 Protocol 中。</li>\n</ul>\n<h2>调用链</h2>\n<p>展开总设计图的红色调用链,如下:</p>\n<p><img src="sources/images/dubbo-extension.jpg" alt="/dev-guide/images/dubbo-extension.jpg"></p>\n<h2>暴露服务时序</h2>\n<p>展开总设计图左边服务提供方暴露服务的蓝色初始化链,时序图如下:</p>\n<p><img src="sources/images/dubbo-export.jpg" alt="/dev-guide/images/dubbo-export.jpg"></p>\n<h2>引用服务时序</h2>\n<p>展开总设计图右边服务消费方引用服务的蓝色初始化链,时序图如下:</p>\n<p><img src="sources/images/dubbo-refer.jpg" alt="/dev-guide/images/dubbo-refer.jpg"></p>\n<h2>领域模型</h2>\n<p>在 Dubbo 的核心领域模型中:</p>\n<ul>\n<li>Protocol 是服务域,它是 Invoker 暴露和引用的主功能入口,它负责 Invoker 的生命周期管理。</li>\n<li>Invoker 是实体域,它是 Dubbo 的核心模型,其它模型都向它靠扰,或转换成它,它代表一个可执行体,可向它发起 invoke 调用,它有可能是一个本地的实现,也可能是一个远程的实现,也可能一个集群实现。</li>\n<li>Invocation 是会话域,它持有调用过程中的变量,比如方法名,参数等。</li>\n</ul>\n<h2>基本设计原则</h2>\n<ul>\n<li>采用 Microkernel + Plugin 模式,Microkernel 只负责组装 Plugin,Dubbo 自身的功能也是通过扩展点实现的,也就是 Dubbo 的所有功能点都可被用户自定义扩展所替换。</li>\n<li>采用 URL 作为配置信息的统一格式,所有扩展点都通过传递 URL 携带配置信息。</li>\n</ul>\n<p>更多设计原则参见:<a href="./principals/introduction.md">框架设计原则</a></p>\n'},{filename:"dev/implementation.md",__html:'<h1>实现细节</h1>\n<h2>初始化过程细节</h2>\n<h3>解析服务</h3>\n<p>基于 dubbo.jar 内的 <code>META-INF/spring.handlers</code> 配置,Spring 在遇到 dubbo 名称空间时,会回调 <code>DubboNamespaceHandler</code>。</p>\n<p>所有 dubbo 的标签,都统一用 <code>DubboBeanDefinitionParser</code> 进行解析,基于一对一属性映射,将 XML 标签解析为 Bean 对象。</p>\n<p>在 <code>ServiceConfig.export()</code> 或 <code>ReferenceConfig.get()</code> 初始化时,将 Bean 对象转换 URL 格式,所有 Bean 属性转成 URL 的参数。</p>\n<p>然后将 URL 传给 <a href="./impls/protocol.md">协议扩展点</a>,基于扩展点的 <a href="./SPI.md">扩展点自适应机制</a>,根据 URL 的协议头,进行不同协议的服务暴露或引用。</p>\n<h3>暴露服务</h3>\n<h4>1. 只暴露服务端口:</h4>\n<p>在没有注册中心,直接暴露提供者的情况下 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>,<code>ServiceConfig</code> 解析出的 URL 的格式为:\n<code>dubbo://service-host/com.foo.FooService?version=1.0.0</code>。</p>\n<p>基于扩展点自适应机制,通过 URL 的 <code>dubbo://</code> 协议头识别,直接调用 <code>DubboProtocol</code>的 <code>export()</code> 方法,打开服务端口。</p>\n<h4>2. 向注册中心暴露服务:</h4>\n<p>在有注册中心,需要注册提供者地址的情况下 <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>,<code>ServiceConfig</code> 解析出的 URL 的格式为: <code>registry://registry-host/com.alibaba.dubbo.registry.RegistryService?export=URL.encode(&quot;dubbo://service-host/com.foo.FooService?version=1.0.0&quot;)</code>,</p>\n<p>基于扩展点自适应机制,通过 URL 的 <code>registry://</code> 协议头识别,就会调用  <code>RegistryProtocol</code> 的 <code>export()</code> 方法,将 <code>export</code> 参数中的提供者 URL,先注册到注册中心。</p>\n<p>再重新传给 <code>Protocol</code> 扩展点进行暴露: <code>dubbo://service-host/com.foo.FooService?version=1.0.0</code>,然后基于扩展点自适应机制,通过提供者 URL 的 <code>dubbo://</code> 协议头识别,就会调用 <code>DubboProtocol</code> 的 <code>export()</code> 方法,打开服务端口。</p>\n<h3>引用服务</h3>\n<h4>1. 直连引用服务:</h4>\n<p>在没有注册中心,直连提供者的情况下 <sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup>,<code>ReferenceConfig</code> 解析出的 URL 的格式为:<code>dubbo://service-host/com.foo.FooService?version=1.0.0</code>。</p>\n<p>基于扩展点自适应机制,通过 URL 的 <code>dubbo://</code> 协议头识别,直接调用 <code>DubboProtocol</code> 的 <code>refer()</code> 方法,返回提供者引用。</p>\n<h4>2. 从注册中心发现引用服务:</h4>\n<p>在有注册中心,通过注册中心发现提供者地址的情况下 <sup class="footnote-ref"><a href="#fn4" id="fnref4">[4]</a></sup>,<code>ReferenceConfig</code> 解析出的 URL 的格式为:\n<code>registry://registry-host/com.alibaba.dubbo.registry.RegistryService?refer=URL.encode(&quot;consumer://consumer-host/com.foo.FooService?version=1.0.0&quot;)</code>。</p>\n<p>基于扩展点自适应机制,通过 URL 的 <code>registry://</code> 协议头识别,就会调用 <code>RegistryProtocol</code> 的 <code>refer()</code> 方法,基于 <code>refer</code> 参数中的条件,查询提供者 URL,如:\n<code>dubbo://service-host/com.foo.FooService?version=1.0.0</code>。</p>\n<p>基于扩展点自适应机制,通过提供者 URL 的 <code>dubbo://</code> 协议头识别,就会调用 <code>DubboProtocol</code> 的 <code>refer()</code> 方法,得到提供者引用。</p>\n<p>然后 <code>RegistryProtocol</code> 将多个提供者引用,通过 <code>Cluster</code> 扩展点,伪装成单个提供者引用返回。</p>\n<h3>拦截服务</h3>\n<p>基于扩展点自适应机制,所有的 <code>Protocol</code> 扩展点都会自动套上 <code>Wrapper</code> 类。</p>\n<p>基于 <code>ProtocolFilterWrapper</code> 类,将所有 <code>Filter</code> 组装成链,在链的最后一节调用真实的引用。</p>\n<p>基于 <code>ProtocolListenerWrapper</code> 类,将所有 <code>InvokerListener</code> 和 <code>ExporterListener</code> 组装集合,在暴露和引用前后,进行回调。</p>\n<p>包括监控在内,所有附加功能,全部通过 <code>Filter</code> 拦截实现。</p>\n<h2>远程调用细节</h2>\n<h3>服务提供者暴露一个服务的详细过程</h3>\n<p><img src="sources/images/dubbo_rpc_export.jpg" alt="/dev-guide/images/dubbo_rpc_export.jpg"></p>\n<p>上图是服务提供者暴露服务的主过程:</p>\n<p>首先 <code>ServiceConfig</code> 类拿到对外提供服务的实际类 ref(如:HelloWorldImpl),然后通过 <code>ProxyFactory</code> 类的 <code>getInvoker</code> 方法使用 ref 生成一个 <code>AbstractProxyInvoker</code> 实例,到这一步就完成具体服务到 <code>Invoker</code> 的转化。接下来就是 <code>Invoker</code> 转换到 <code>Exporter</code> 的过程。</p>\n<p>Dubbo 处理服务暴露的关键就在 <code>Invoker</code> 转换到 <code>Exporter</code> 的过程,上图中的红色部分。下面我们以 Dubbo 和 RMI 这两种典型协议的实现来进行说明:</p>\n<h4>Dubbo 的实现</h4>\n<p>Dubbo 协议的 <code>Invoker</code> 转为 <code>Exporter</code> 发生在 <code>DubboProtocol</code> 类的 <code>export</code> 方法,它主要是打开 socket 侦听服务,并接收客户端发来的各种请求,通讯细节由 Dubbo 自己实现。</p>\n<h4>RMI 的实现</h4>\n<p>RMI 协议的 <code>Invoker</code> 转为 <code>Exporter</code> 发生在 <code>RmiProtocol</code>类的 <code>export</code> 方法,它通过 Spring 或 Dubbo 或 JDK 来实现 RMI 服务,通讯细节这一块由 JDK 底层来实现,这就省了不少工作量。</p>\n<h3>服务消费者消费一个服务的详细过程</h3>\n<p><img src="sources/images/dubbo_rpc_refer.jpg" alt="/dev-guide/images/dubbo_rpc_refer.jpg"></p>\n<p>上图是服务消费的主过程:</p>\n<p>首先 <code>ReferenceConfig</code> 类的 <code>init</code> 方法调用 <code>Protocol</code> 的 <code>refer</code> 方法生成 <code>Invoker</code> 实例(如上图中的红色部分),这是服务消费的关键。接下来把 <code>Invoker</code> 转换为客户端需要的接口(如:HelloWorld)。</p>\n<p>关于每种协议如 RMI/Dubbo/Web service 等它们在调用 <code>refer</code> 方法生成 <code>Invoker</code> 实例的细节和上一章节所描述的类似。</p>\n<h3>满眼都是 Invoker</h3>\n<p>由于 <code>Invoker</code> 是 Dubbo 领域模型中非常重要的一个概念,很多设计思路都是向它靠拢。这就使得 <code>Invoker</code> 渗透在整个实现代码里,对于刚开始接触 Dubbo 的人,确实容易给搞混了。\n下面我们用一个精简的图来说明最重要的两种 <code>Invoker</code>:服务提供 <code>Invoker</code> 和服务消费 <code>Invoker</code>:</p>\n<p><img src="sources/images/dubbo_rpc_invoke.jpg" alt="/dev-guide/images/dubbo_rpc_invoke.jpg"></p>\n<p>为了更好的解释上面这张图,我们结合服务消费和提供者的代码示例来进行说明:</p>\n<p>服务消费者代码:</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">DemoClientAction</span> </span>{\n \n    <span class="hljs-keyword">private</span> DemoService demoService;\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setDemoService</span><span class="hljs-params">(DemoService demoService)</span> </span>{\n        <span class="hljs-keyword">this</span>.demoService = demoService;\n    }\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">start</span><span class="hljs-params">()</span> </span>{\n        String hello = demoService.sayHello(<span class="hljs-string">"world"</span> + i);\n    }\n}\n</code></pre>\n<p>上面代码中的 <code>DemoService</code> 就是上图中服务消费端的 proxy,用户代码通过这个 proxy 调用其对应的 <code>Invoker</code> <sup class="footnote-ref"><a href="#fn5" id="fnref5">[5]</a></sup>,而该 <code>Invoker</code> 实现了真正的远程服务调用。</p>\n<p>服务提供者代码:</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">DemoServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">DemoService</span> </span>{\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">sayHello</span><span class="hljs-params">(String name)</span> <span class="hljs-keyword">throws</span> RemoteException </span>{\n        <span class="hljs-keyword">return</span> <span class="hljs-string">"Hello "</span> + name;\n    }\n}\n</code></pre>\n<p>上面这个类会被封装成为一个 <code>AbstractProxyInvoker</code> 实例,并新生成一个 <code>Exporter</code> 实例。这样当网络通讯层收到一个请求后,会找到对应的 <code>Exporter</code> 实例,并调用它所对应的 <code>AbstractProxyInvoker</code> 实例,从而真正调用了服务提供者的代码。Dubbo 里还有一些其他的 <code>Invoker</code> 类,但上面两种是最重要的。</p>\n<h2>远程通讯细节</h2>\n<h3>协议头约定</h3>\n<p><img src="sources/images/dubbo_protocol_header.jpg" alt="/dev-guide/images/dubbo_protocol_header.jpg"></p>\n<h3>线程派发模型</h3>\n<p><img src="sources/images/dubbo-protocol.jpg" alt="/dev-guide/images/dubbo-protocol.jpg"></p>\n<ul>\n<li>Dispather: <code>all</code>, <code>direct</code>, <code>message</code>, <code>execution</code>, <code>connection</code></li>\n<li>ThreadPool: <code>fixed</code>, <code>cached</code></li>\n</ul>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>即:<code>&lt;dubbo:service regisrty=&quot;N/A&quot; /&gt;</code> 或者 <code>&lt;dubbo:registry address=&quot;N/A&quot; /&gt;</code> <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>即: <code>&lt;dubbo:registry address=&quot;zookeeper://10.20.153.10:2181&quot; /&gt;</code> <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn3" class="footnote-item"><p>即:<code>&lt;dubbo:reference url=&quot;dubbo://service-host/com.foo.FooService?version=1.0.0&quot; /&gt;</code> <a href="#fnref3" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn4" class="footnote-item"><p>即:<code>&lt;dubbo:registry address=&quot;zookeeper://10.20.153.10:2181&quot; /&gt;</code> <a href="#fnref4" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn5" class="footnote-item"><p><code>DubboInvoker</code>、 <code>HessianRpcInvoker</code>、 <code>InjvmInvoker</code>、 <code>RmiInvoker</code>、 <code>WebServiceInvoker</code> 中的任何一个 <a href="#fnref5" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"dev/impls/cache.md",__html:'<h1>缓存扩展</h1>\n<h2>扩展说明</h2>\n<p>用请求参数作为 key,缓存返回结果。</p>\n<h2>扩展接口</h2>\n<p><code>com.alibaba.dubbo.cache.CacheFactory</code></p>\n<h2>扩展配置</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">cache</span>=<span class="hljs-string">"lru"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- 方法级缓存 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:method</span> <span class="hljs-attr">cache</span>=<span class="hljs-string">"lru"</span> /&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:service</span>&gt;</span> \n<span class="hljs-comment">&lt;!-- 缺省值设置,当&lt;dubbo:service&gt;没有配置cache属性时,使用此配置 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">cache</span>=<span class="hljs-string">"xxx,yyy"</span> /&gt;</span> \n</code></pre>\n<h2>已知扩展</h2>\n<ul>\n<li><code>com.alibaba.dubbo.cache.support.lru.LruCacheFactory</code></li>\n<li><code>com.alibaba.dubbo.cache.support.threadlocal.ThreadLocalCacheFactory</code></li>\n<li><code>com.alibaba.dubbo.cache.support.jcache.JCacheFactory</code></li>\n</ul>\n<h2>扩展示例</h2>\n<p>Maven 项目结构:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxCacheFactory.java (实现StatusChecker接口)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.cache.CacheFactory (纯文本文件,内容为:xxx=com.xxx.XxxCacheFactory)\n</code></pre>\n<p>XxxCacheFactory.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.cache.CacheFactory;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxCacheFactory</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">CacheFactory</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Cache <span class="hljs-title">getCache</span><span class="hljs-params">(URL url, String name)</span> </span>{\n        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> XxxCache(url, name);\n    }\n}\n</code></pre>\n<p>XxxCacheFactory.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.cache.Cache;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxCache</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Cache</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Cache</span><span class="hljs-params">(URL url, String name)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">put</span><span class="hljs-params">(Object key, Object value)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Object <span class="hljs-title">get</span><span class="hljs-params">(Object key)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.cache.CacheFactory:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxCacheFactory\n</code></pre>\n'},{filename:"dev/impls/cluster.md",__html:'<h1>集群扩展</h1>\n<h2>扩展说明</h2>\n<p>当有多个服务提供方时,将多个服务提供方组织成一个集群,并伪装成一个提供方。</p>\n<h2>扩展接口</h2>\n<p><code>com.alibaba.dubbo.rpc.cluster.Cluster</code></p>\n<h2>扩展配置</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">cluster</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- 缺省值配置,如果&lt;dubbo:protocol&gt;没有配置cluster时,使用此配置 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">cluster</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n</code></pre>\n<h2>已知扩展</h2>\n<ul>\n<li><code>com.alibaba.dubbo.rpc.cluster.support.FailoverCluster</code></li>\n<li><code>com.alibaba.dubbo.rpc.cluster.support.FailfastCluster</code></li>\n<li><code>com.alibaba.dubbo.rpc.cluster.support.FailsafeCluster</code></li>\n<li><code>com.alibaba.dubbo.rpc.cluster.support.FailbackCluster</code></li>\n<li><code>com.alibaba.dubbo.rpc.cluster.support.ForkingCluster</code></li>\n<li><code>com.alibaba.dubbo.rpc.cluster.support.AvailableCluster</code></li>\n</ul>\n<h2>扩展示例</h2>\n<p>Maven 项目结构:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxCluster.java (实现Cluster接口)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.rpc.cluster.Cluster (纯文本文件,内容为:xxx=com.xxx.XxxCluster)\n</code></pre>\n<p>XxxCluster.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.cluster.Cluster;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.cluster.support.AbstractClusterInvoker;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.cluster.Directory;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.cluster.LoadBalance;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Invoker;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Invocation;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Result;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.RpcException;\n \n<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>{\n    <span class="hljs-keyword">public</span> &lt;T&gt; <span class="hljs-function">Invoker&lt;T&gt; <span class="hljs-title">merge</span><span class="hljs-params">(Directory&lt;T&gt; directory)</span> <span class="hljs-keyword">throws</span> RpcException </span>{\n        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> AbstractClusterInvoker&lt;T&gt;(directory) {\n            <span class="hljs-function"><span class="hljs-keyword">public</span> Result <span class="hljs-title">doInvoke</span><span class="hljs-params">(Invocation invocation, List&lt;Invoker&lt;T&gt;&gt; invokers, LoadBalance loadbalance)</span> <span class="hljs-keyword">throws</span> RpcException </span>{\n                <span class="hljs-comment">// ...</span>\n            }\n        };\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.Cluster:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxCluster\n</code></pre>\n'},{filename:"dev/impls/compiler.md",__html:'<h1>编译器扩展</h1>\n<h2>扩展说明</h2>\n<p>Java 代码编译器,用于动态生成字节码,加速调用。</p>\n<h2>扩展接口</h2>\n<p><code>com.alibaba.dubbo.common.compiler.Compiler</code></p>\n<h2>扩展配置</h2>\n<p>自动加载</p>\n<h2>已知扩展</h2>\n<ul>\n<li><code>com.alibaba.dubbo.common.compiler.support.JdkCompiler</code></li>\n<li><code>com.alibaba.dubbo.common.compiler.support.JavassistCompiler</code></li>\n</ul>\n<h2>扩展示例</h2>\n<p>Maven 项目结构:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxCompiler.java (实现Compiler接口)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.common.compiler.Compiler (纯文本文件,内容为:xxx=com.xxx.XxxCompiler)\n</code></pre>\n<p>XxxCompiler.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.common.compiler.Compiler;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxCompiler</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Compiler</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Object <span class="hljs-title">getExtension</span><span class="hljs-params">(Class&lt;?&gt; type, String name)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.common.compiler.Compiler:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxCompiler\n</code></pre>\n'},{filename:"dev/impls/container.md",__html:'<h1>容器扩展</h1>\n<h2>扩展说明</h2>\n<p>服务容器扩展,用于自定义加载内容。</p>\n<h2>扩展接口</h2>\n<p><code>com.alibaba.dubbo.container.Container</code></p>\n<h2>扩展配置</h2>\n<pre><code class="language-sh">java com.alibaba.dubbo.container.Main spring jetty log4j\n</code></pre>\n<h2>已知扩展</h2>\n<ul>\n<li><code>com.alibaba.dubbo.container.spring.SpringContainer</code></li>\n<li><code>com.alibaba.dubbo.container.spring.JettyContainer</code></li>\n<li><code>com.alibaba.dubbo.container.spring.Log4jContainer</code></li>\n</ul>\n<h2>扩展示例</h2>\n<p>Maven 项目结构:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxContainer.java (实现Container接口)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.container.Container (纯文本文件,内容为:xxx=com.xxx.XxxContainer)\n</code></pre>\n<p>XxxContainer.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \ncom.alibaba.dubbo.container.Container;\n \n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxContainer</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Container</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Status <span class="hljs-title">start</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Status <span class="hljs-title">stop</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.container.Container:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxContainer\n</code></pre>\n'},{filename:"dev/impls/dispatcher.md",__html:'<h1>消息派发扩展</h1>\n<h2>扩展说明</h2>\n<p>通道信息派发器,用于指定线程池模型。</p>\n<h2>扩展接口</h2>\n<p><code>com.alibaba.dubbo.remoting.Dispatcher</code></p>\n<h2>扩展配置</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">dispatcher</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- 缺省值设置,当&lt;dubbo:protocol&gt;没有配置dispatcher属性时,使用此配置 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">dispatcher</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n</code></pre>\n<h2>已知扩展</h2>\n<ul>\n<li><code>com.alibaba.dubbo.remoting.transport.dispatcher.all.AllDispatcher</code></li>\n<li><code>com.alibaba.dubbo.remoting.transport.dispatcher.direct.DirectDispatcher</code></li>\n<li><code>com.alibaba.dubbo.remoting.transport.dispatcher.message.MessageOnlyDispatcher</code></li>\n<li><code>com.alibaba.dubbo.remoting.transport.dispatcher.execution.ExecutionDispatcher</code></li>\n<li><code>com.alibaba.dubbo.remoting.transport.dispatcher.connection.ConnectionOrderedDispatcher</code></li>\n</ul>\n<h2>扩展示例</h2>\n<p>Maven 项目结构:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxDispatcher.java (实现Dispatcher接口)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.remoting.Dispatcher (纯文本文件,内容为:xxx=com.xxx.XxxDispatcher)\n</code></pre>\n<p>XxxDispatcher.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.remoting.Dispatcher;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxDispatcher</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Dispatcher</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Group <span class="hljs-title">lookup</span><span class="hljs-params">(URL url)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.remoting.Dispatcher:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxDispatcher\n</code></pre>\n'},{filename:"dev/impls/exchanger.md",__html:'<h1>信息交换扩展</h1>\n<h2>扩展说明</h2>\n<p>基于传输层之上,实现 Request-Response 信息交换语义。</p>\n<h2>扩展接口</h2>\n<ul>\n<li><code>com.alibaba.dubbo.remoting.exchange.Exchanger</code></li>\n<li><code>com.alibaba.dubbo.remoting.exchange.ExchangeServer</code></li>\n<li><code>com.alibaba.dubbo.remoting.exchange.ExchangeClient</code></li>\n</ul>\n<h2>扩展配置</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">exchanger</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- 缺省值设置,当&lt;dubbo:protocol&gt;没有配置exchanger属性时,使用此配置 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">exchanger</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n</code></pre>\n<h2>已知扩展</h2>\n<p><code>com.alibaba.dubbo.remoting.exchange.exchanger.HeaderExchanger</code></p>\n<h2>扩展示例</h2>\n<p>Maven 项目结构:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxExchanger.java (实现Exchanger接口)\n                |-XxxExchangeServer.java (实现ExchangeServer接口)\n                |-XxxExchangeClient.java (实现ExchangeClient接口)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.remoting.exchange.Exchanger (纯文本文件,内容为:xxx=com.xxx.XxxExchanger)\n</code></pre>\n<p>XxxExchanger.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.remoting.exchange.Exchanger;\n \n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxExchanger</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Exchanger</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> ExchangeServer <span class="hljs-title">bind</span><span class="hljs-params">(URL url, ExchangeHandler handler)</span> <span class="hljs-keyword">throws</span> RemotingException </span>{\n        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> XxxExchangeServer(url, handler);\n    }\n    <span class="hljs-function"><span class="hljs-keyword">public</span> ExchangeClient <span class="hljs-title">connect</span><span class="hljs-params">(URL url, ExchangeHandler handler)</span> <span class="hljs-keyword">throws</span> RemotingException </span>{\n        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> XxxExchangeClient(url, handler);\n    }\n}\n</code></pre>\n<p>XxxExchangeServer.java:</p>\n<pre><code class="language-java">\n<span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.remoting.exchange.ExchangeServer;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxExchangeServer</span> <span class="hljs-title">impelements</span> <span class="hljs-title">ExchangeServer</span> </span>{\n    <span class="hljs-comment">// ...</span>\n}\n</code></pre>\n<p>XxxExchangeClient.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.remoting.exchange.ExchangeClient;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxExchangeClient</span> <span class="hljs-title">impelments</span> <span class="hljs-title">ExchangeClient</span> </span>{\n    <span class="hljs-comment">// ...</span>\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.remoting.exchange.Exchanger:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxExchanger\n</code></pre>\n'},{filename:"dev/impls/exporter-listener.md",__html:'<h1>暴露监听扩展</h1>\n<h2>扩展说明</h2>\n<p>当有服务暴露时,触发该事件。</p>\n<h2>扩展接口</h2>\n<p><code>com.alibaba.dubbo.rpc.ExporterListener</code></p>\n<h2>扩展配置</h2>\n<pre><code class="language-xml"><span class="hljs-comment">&lt;!-- 暴露服务监听 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">listener</span>=<span class="hljs-string">"xxx,yyy"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- 暴露服务缺省监听器 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">listener</span>=<span class="hljs-string">"xxx,yyy"</span> /&gt;</span>\n</code></pre>\n<h2>已知扩展</h2>\n<p><code>com.alibaba.dubbo.registry.directory.RegistryExporterListener</code></p>\n<h2>扩展示例</h2>\n<p>Maven 项目结构:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxExporterListener.java (实现ExporterListener接口)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.rpc.ExporterListener (纯文本文件,内容为:xxx=com.xxx.XxxExporterListener)\n</code></pre>\n<p>XxxExporterListener.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.ExporterListener;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Exporter;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.RpcException;\n \n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxExporterListener</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">ExporterListener</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">exported</span><span class="hljs-params">(Exporter&lt;?&gt; exporter)</span> <span class="hljs-keyword">throws</span> RpcException </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">unexported</span><span class="hljs-params">(Exporter&lt;?&gt; exporter)</span> <span class="hljs-keyword">throws</span> RpcException </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.rpc.ExporterListener:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxExporterListener\n</code></pre>\n'},{filename:"dev/impls/extension-factory.md",__html:'<h1>扩展点加载扩展</h1>\n<h2>扩展说明</h2>\n<p>扩展点本身的加载容器,可从不同容器加载扩展点。</p>\n<h2>扩展接口</h2>\n<p><code>com.alibaba.dubbo.common.extension.ExtensionFactory</code></p>\n<h2>扩展配置</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:application</span> <span class="hljs-attr">compiler</span>=<span class="hljs-string">"jdk"</span> /&gt;</span>\n</code></pre>\n<h2>已知扩展</h2>\n<ul>\n<li><code>com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory</code></li>\n<li><code>com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory</code></li>\n</ul>\n<h2>扩展示例</h2>\n<p>Maven 项目结构:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxExtensionFactory.java (实现ExtensionFactory接口)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.common.extension.ExtensionFactory (纯文本文件,内容为:xxx=com.xxx.XxxExtensionFactory)\n</code></pre>\n<p>XxxExtensionFactory.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.common.extension.ExtensionFactory;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxExtensionFactory</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">ExtensionFactory</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Object <span class="hljs-title">getExtension</span><span class="hljs-params">(Class&lt;?&gt; type, String name)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.common.extension.ExtensionFactory:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxExtensionFactory\n</code></pre>\n'},{filename:"dev/impls/filter.md",__html:'<h1>调用拦截扩展</h1>\n<h2>扩展说明</h2>\n<p>服务提供方和服务消费方调用过程拦截,Dubbo 本身的大多功能均基于此扩展点实现,每次远程方法执行,该拦截都会被执行,请注意对性能的影响。</p>\n<p>约定:</p>\n<ul>\n<li>用户自定义 filter 默认在内置 filter 之后。</li>\n<li>特殊值 <code>default</code>,表示缺省扩展点插入的位置。比如:<code>filter=&quot;xxx,default,yyy&quot;</code>,表示 <code>xxx</code> 在缺省 filter 之前,<code>yyy</code> 在缺省 filter 之后。</li>\n<li>特殊符号 <code>-</code>,表示剔除。比如:<code>filter=&quot;-foo1&quot;</code>,剔除添加缺省扩展点 <code>foo1</code>。比如:<code>filter=&quot;-default&quot;</code>,剔除添加所有缺省扩展点。</li>\n<li>provider 和 service 同时配置的 filter 时,累加所有 filter,而不是覆盖。比如:<code>&lt;dubbo:provider filter=&quot;xxx,yyy&quot;/&gt;</code> 和 <code>&lt;dubbo:service filter=&quot;aaa,bbb&quot; /&gt;</code>,则 <code>xxx</code>,<code>yyy</code>,<code>aaa</code>,<code>bbb</code> 均会生效。如果要覆盖,需配置:<code>&lt;dubbo:service filter=&quot;-xxx,-yyy,aaa,bbb&quot; /&gt;</code></li>\n</ul>\n<h2>扩展接口</h2>\n<p><code>com.alibaba.dubbo.rpc.Filter</code></p>\n<h2>扩展配置</h2>\n<pre><code class="language-xml"><span class="hljs-comment">&lt;!-- 消费方调用过程拦截 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">filter</span>=<span class="hljs-string">"xxx,yyy"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- 消费方调用过程缺省拦截器,将拦截所有reference --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:consumer</span> <span class="hljs-attr">filter</span>=<span class="hljs-string">"xxx,yyy"</span>/&gt;</span>\n<span class="hljs-comment">&lt;!-- 提供方调用过程拦截 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">filter</span>=<span class="hljs-string">"xxx,yyy"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- 提供方调用过程缺省拦截器,将拦截所有service --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">filter</span>=<span class="hljs-string">"xxx,yyy"</span>/&gt;</span>\n</code></pre>\n<h2>已知扩展</h2>\n<ul>\n<li><code>com.alibaba.dubbo.rpc.filter.EchoFilter</code></li>\n<li><code>com.alibaba.dubbo.rpc.filter.GenericFilter</code></li>\n<li><code>com.alibaba.dubbo.rpc.filter.GenericImplFilter</code></li>\n<li><code>com.alibaba.dubbo.rpc.filter.TokenFilter</code></li>\n<li><code>com.alibaba.dubbo.rpc.filter.AccessLogFilter</code></li>\n<li><code>com.alibaba.dubbo.rpc.filter.CountFilter</code></li>\n<li><code>com.alibaba.dubbo.rpc.filter.ActiveLimitFilter</code></li>\n<li><code>com.alibaba.dubbo.rpc.filter.ClassLoaderFilter</code></li>\n<li><code>com.alibaba.dubbo.rpc.filter.ContextFilter</code></li>\n<li><code>com.alibaba.dubbo.rpc.filter.ConsumerContextFilter</code></li>\n<li><code>com.alibaba.dubbo.rpc.filter.ExceptionFilter</code></li>\n<li><code>com.alibaba.dubbo.rpc.filter.ExecuteLimitFilter</code></li>\n<li><code>com.alibaba.dubbo.rpc.filter.DeprecatedFilter</code></li>\n</ul>\n<h2>扩展示例</h2>\n<p>Maven 项目结构:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxFilter.java (实现Filter接口)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.rpc.Filter (纯文本文件,内容为:xxx=com.xxx.XxxFilter)\n</code></pre>\n<p>XxxFilter.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Filter;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Invoker;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Invocation;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Result;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.RpcException;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxFilter</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Filter</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Result <span class="hljs-title">invoke</span><span class="hljs-params">(Invoker&lt;?&gt; invoker, Invocation invocation)</span> <span class="hljs-keyword">throws</span> RpcException </span>{\n        <span class="hljs-comment">// before filter ...</span>\n        Result result = invoker.invoke(invocation);\n        <span class="hljs-comment">// after filter ...</span>\n        <span class="hljs-keyword">return</span> result;\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.rpc.Filter:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxFilter\n</code></pre>\n'},{filename:"dev/impls/introduction.md",__html:"<h1>SPI 扩展实现</h1>\n<p>SPI 扩展接口仅用于系统集成,或 Contributor 扩展功能插件。</p>\n"},{filename:"dev/impls/invoker-listener.md",__html:'<h1>引用监听扩展</h1>\n<h2>扩展说明</h2>\n<p>当有服务引用时,触发该事件。</p>\n<h2>扩展接口</h2>\n<p><code>com.alibaba.dubbo.rpc.InvokerListener</code></p>\n<h2>扩展配置</h2>\n<pre><code class="language-xml"><span class="hljs-comment">&lt;!-- 引用服务监听 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">listener</span>=<span class="hljs-string">"xxx,yyy"</span> /&gt;</span> \n<span class="hljs-comment">&lt;!-- 引用服务缺省监听器 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:consumer</span> <span class="hljs-attr">listener</span>=<span class="hljs-string">"xxx,yyy"</span> /&gt;</span> \n</code></pre>\n<h2>已知扩展</h2>\n<p><code>com.alibaba.dubbo.rpc.listener.DeprecatedInvokerListener</code></p>\n<h2>扩展示例</h2>\n<p>Maven 项目结构:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxInvokerListener.java (实现InvokerListener接口)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.rpc.InvokerListener (纯文本文件,内容为:xxx=com.xxx.XxxInvokerListener)\n</code></pre>\n<p>XxxInvokerListener.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.InvokerListener;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Invoker;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.RpcException;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxInvokerListener</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">InvokerListener</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">referred</span><span class="hljs-params">(Invoker&lt;?&gt; invoker)</span> <span class="hljs-keyword">throws</span> RpcException </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">destroyed</span><span class="hljs-params">(Invoker&lt;?&gt; invoker)</span> <span class="hljs-keyword">throws</span> RpcException </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.rpc.InvokerListener:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxInvokerListener\n</code></pre>\n'},{filename:"dev/impls/load-balance.md",__html:'<h1>负载均衡扩展</h1>\n<h2>扩展说明</h2>\n<p>从多个服务提者方中选择一个进行调用</p>\n<h2>扩展接口</h2>\n<p><code>com.alibaba.dubbo.rpc.cluster.LoadBalance</code></p>\n<h2>扩展配置</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">loadbalance</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- 缺省值设置,当&lt;dubbo:protocol&gt;没有配置loadbalance时,使用此配置 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">loadbalance</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n</code></pre>\n<h2>已知扩展</h2>\n<ul>\n<li><code>com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance</code></li>\n<li><code>com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance</code></li>\n<li><code>com.alibaba.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance</code></li>\n</ul>\n<h2>扩展示例</h2>\n<p>Maven 项目结构:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxLoadBalance.java (实现LoadBalance接口)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.rpc.cluster.LoadBalance (纯文本文件,内容为:xxx=com.xxx.XxxLoadBalance)\n</code></pre>\n<p>XxxLoadBalance.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.cluster.LoadBalance;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Invoker;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Invocation;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.RpcException; \n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxLoadBalance</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">LoadBalance</span> </span>{\n    <span class="hljs-keyword">public</span> &lt;T&gt; <span class="hljs-function">Invoker&lt;T&gt; <span class="hljs-title">select</span><span class="hljs-params">(List&lt;Invoker&lt;T&gt;&gt; invokers, Invocation invocation)</span> <span class="hljs-keyword">throws</span> RpcException </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.LoadBalance:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxLoadBalance\n</code></pre>\n'},{filename:"dev/impls/logger-adapter.md",__html:'<h1>日志适配扩展</h1>\n<h2>扩展说明</h2>\n<p>日志输出适配扩展点。</p>\n<h2>扩展接口</h2>\n<p><code>com.alibaba.dubbo.common.logger.LoggerAdapter</code></p>\n<h2>扩展配置</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:application</span> <span class="hljs-attr">logger</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n</code></pre>\n<p>或者:</p>\n<pre><code class="language-sh">-Ddubbo:application.logger=xxx\n</code></pre>\n<h2>已知扩展</h2>\n<ul>\n<li><code>com.alibaba.dubbo.common.logger.slf4j.Slf4jLoggerAdapter</code></li>\n<li><code>com.alibaba.dubbo.common.logger.jcl.JclLoggerAdapter</code></li>\n<li><code>com.alibaba.dubbo.common.logger.log4j.Log4jLoggerAdapter</code></li>\n<li><code>com.alibaba.dubbo.common.logger.jdk.JdkLoggerAdapter</code></li>\n</ul>\n<h2>扩展示例</h2>\n<p>Maven 项目结构:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxLoggerAdapter.java (实现LoggerAdapter接口)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.common.logger.LoggerAdapter (纯文本文件,内容为:xxx=com.xxx.XxxLoggerAdapter)\n</code></pre>\n<p>XxxLoggerAdapter.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.common.logger.LoggerAdapter;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxLoggerAdapter</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">LoggerAdapter</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Logger <span class="hljs-title">getLogger</span><span class="hljs-params">(URL url)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>XxxLogger.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.common.logger.Logger;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxLogger</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Logger</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">XxxLogger</span><span class="hljs-params">(URL url)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">info</span><span class="hljs-params">(String msg)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-comment">// ...</span>\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.common.logger.LoggerAdapter:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxLoggerAdapter\n</code></pre>\n'},{filename:"dev/impls/merger.md",__html:'<h1>合并结果扩展</h1>\n<h2>扩展说明</h2>\n<p>合并返回结果,用于分组聚合。</p>\n<h2>扩展接口</h2>\n<p><code>com.alibaba.dubbo.rpc.cluster.Merger</code></p>\n<h2>扩展配置</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:method</span> <span class="hljs-attr">merger</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n</code></pre>\n<h2>已知扩展</h2>\n<ul>\n<li><code>com.alibaba.dubbo.rpc.cluster.merger.ArrayMerger</code></li>\n<li><code>com.alibaba.dubbo.rpc.cluster.merger.ListMerger</code></li>\n<li><code>com.alibaba.dubbo.rpc.cluster.merger.SetMerger</code></li>\n<li><code>com.alibaba.dubbo.rpc.cluster.merger.MapMerger</code></li>\n</ul>\n<h2>扩展示例</h2>\n<p>Maven 项目结构:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxMerger.java (实现Merger接口)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.rpc.cluster.Merger (纯文本文件,内容为:xxx=com.xxx.XxxMerger)\n</code></pre>\n<p>XxxMerger.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.cluster.Merger;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxMerger</span>&lt;<span class="hljs-title">T</span>&gt; <span class="hljs-keyword">implements</span> <span class="hljs-title">Merger</span>&lt;<span class="hljs-title">T</span>&gt; </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> T <span class="hljs-title">merge</span><span class="hljs-params">(T... results)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.Merger:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxMerger\n</code></pre>\n'},{filename:"dev/impls/monitor.md",__html:'<h1>监控中心扩展</h1>\n<h2>扩展说明</h2>\n<p>负责服务调用次和调用时间的监控。</p>\n<h2>扩展接口</h2>\n<ul>\n<li><code>com.alibaba.dubbo.monitor.MonitorFactory</code></li>\n<li><code>com.alibaba.dubbo.monitor.Monitor</code></li>\n</ul>\n<h2>扩展配置</h2>\n<pre><code class="language-xml"><span class="hljs-comment">&lt;!-- 定义监控中心 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:monitor</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"xxx://ip:port"</span> /&gt;</span>\n</code></pre>\n<h2>已知扩展</h2>\n<p>com.alibaba.dubbo.monitor.support.dubbo.DubboMonitorFactory</p>\n<h2>扩展示例</h2>\n<p>Maven 项目结构:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxMonitorFactoryjava (实现MonitorFactory接口)\n                |-XxxMonitor.java (实现Monitor接口)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.monitor.MonitorFactory (纯文本文件,内容为:xxx=com.xxx.XxxMonitorFactory)\n</code></pre>\n<p>XxxMonitorFactory.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.monitor.MonitorFactory;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.monitor.Monitor;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.common.URL;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxMonitorFactory</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">MonitorFactory</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Monitor <span class="hljs-title">getMonitor</span><span class="hljs-params">(URL url)</span> </span>{\n        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> XxxMonitor(url);\n    }\n}\n</code></pre>\n<p>XxxMonitor.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.monitor.Monitor;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxMonitor</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Monitor</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">count</span><span class="hljs-params">(URL statistics)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.monitor.MonitorFactory:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxMonitorFactory\n</code></pre>\n'},{filename:"dev/impls/networker.md",__html:'<h1>组网扩展</h1>\n<h2>扩展说明</h2>\n<p>对等网络节点组网器。</p>\n<h2>扩展接口</h2>\n<p><code>com.alibaba.dubbo.remoting.p2p.Networker</code></p>\n<h2>扩展配置</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">networker</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- 缺省值设置,当&lt;dubbo:protocol&gt;没有配置networker属性时,使用此配置 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">networker</span>=<span class="hljs-string">"xxx"</span> /&gt;</span> \n</code></pre>\n<h2>已知扩展</h2>\n<ul>\n<li><code>com.alibaba.dubbo.remoting.p2p.support.MulticastNetworker</code></li>\n<li><code>com.alibaba.dubbo.remoting.p2p.support.FileNetworker</code></li>\n</ul>\n<h2>扩展示例</h2>\n<p>Maven 项目结构:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxNetworker.java (实现Networker接口)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.remoting.p2p.Networker (纯文本文件,内容为:xxx=com.xxx.XxxNetworker)\n</code></pre>\n<p>XxxNetworker.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.remoting.p2p.Networker;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxNetworker</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Networker</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Group <span class="hljs-title">lookup</span><span class="hljs-params">(URL url)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.remoting.p2p.Networker:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxNetworker\n</code></pre>\n'},{filename:"dev/impls/page.md",__html:'<h1>页面扩展</h1>\n<h2>扩展说明</h2>\n<p>对等网络节点组网器。</p>\n<h2>扩展接口</h2>\n<p><code>com.alibaba.dubbo.container.page.PageHandler</code></p>\n<h2>扩展配置</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">page</span>=<span class="hljs-string">"xxx,yyy"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- 缺省值设置,当&lt;dubbo:protocol&gt;没有配置page属性时,使用此配置 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">page</span>=<span class="hljs-string">"xxx,yyy"</span> /&gt;</span>\n</code></pre>\n<h2>已知扩展</h2>\n<ul>\n<li><code>com.alibaba.dubbo.container.page.pages.HomePageHandler</code></li>\n<li><code>com.alibaba.dubbo.container.page.pages.StatusPageHandler</code></li>\n<li><code>com.alibaba.dubbo.container.page.pages.LogPageHandler</code></li>\n<li><code>com.alibaba.dubbo.container.page.pages.SystemPageHandler</code></li>\n</ul>\n<h2>扩展示例</h2>\n<p>Maven 项目结构:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxPageHandler.java (实现PageHandler接口)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.container.page.PageHandler (纯文本文件,内容为:xxx=com.xxx.XxxPageHandler)\n</code></pre>\n<p>XxxPageHandler.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.container.page.PageHandler;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxPageHandler</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">PageHandler</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Group <span class="hljs-title">lookup</span><span class="hljs-params">(URL url)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.container.page.PageHandler:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxPageHandler\n</code></pre>\n'},{filename:"dev/impls/protocol.md",__html:'<h1>协议扩展</h1>\n<h2>扩展说明</h2>\n<p>RPC 协议扩展,封装远程调用细节。</p>\n<p>契约:</p>\n<ul>\n<li>当用户调用 <code>refer()</code> 所返回的 <code>Invoker</code> 对象的 <code>invoke()</code> 方法时,协议需相应执行同 URL 远端 <code>export()</code> 传入的 <code>Invoker</code> 对象的 <code>invoke()</code> 方法。</li>\n<li>其中,<code>refer()</code> 返回的 <code>Invoker</code> 由协议实现,协议通常需要在此 <code>Invoker</code> 中发送远程请求,<code>export()</code> 传入的 <code>Invoker</code> 由框架实现并传入,协议不需要关心。</li>\n</ul>\n<p>注意:</p>\n<ul>\n<li>协议不关心业务接口的透明代理,以 <code>Invoker</code> 为中心,由外层将 <code>Invoker</code> 转换为业务接口。</li>\n<li>协议不一定要是 TCP 网络通讯,比如通过共享文件,IPC 进程间通讯等。</li>\n</ul>\n<h2>扩展接口</h2>\n<ul>\n<li><code>com.alibaba.dubbo.rpc.Protocol</code></li>\n<li><code>com.alibaba.dubbo.rpc.Exporter</code></li>\n<li><code>com.alibaba.dubbo.rpc.Invoker</code></li>\n</ul>\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">Protocol</span> </span>{\n    <span class="hljs-comment">/**\n     * 暴露远程服务:&lt;br&gt;\n     * 1. 协议在接收请求时,应记录请求来源方地址信息:RpcContext.getContext().setRemoteAddress();&lt;br&gt;\n     * 2. export()必须是幂等的,也就是暴露同一个URL的Invoker两次,和暴露一次没有区别。&lt;br&gt;\n     * 3. export()传入的Invoker由框架实现并传入,协议不需要关心。&lt;br&gt;\n     * \n     * <span class="hljs-doctag">@param</span> &lt;T&gt; 服务的类型\n     * <span class="hljs-doctag">@param</span> invoker 服务的执行体\n     * <span class="hljs-doctag">@return</span> exporter 暴露服务的引用,用于取消暴露\n     * <span class="hljs-doctag">@throws</span> RpcException 当暴露服务出错时抛出,比如端口已占用\n     */</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-comment">/**\n     * 引用远程服务:&lt;br&gt;\n     * 1. 当用户调用refer()所返回的Invoker对象的invoke()方法时,协议需相应执行同URL远端export()传入的Invoker对象的invoke()方法。&lt;br&gt;\n     * 2. refer()返回的Invoker由协议实现,协议通常需要在此Invoker中发送远程请求。&lt;br&gt;\n     * 3. 当url中有设置check=false时,连接失败不能抛出异常,需内部自动恢复。&lt;br&gt;\n     * \n     * <span class="hljs-doctag">@param</span> &lt;T&gt; 服务的类型\n     * <span class="hljs-doctag">@param</span> type 服务的类型\n     * <span class="hljs-doctag">@param</span> url 远程服务的URL地址\n     * <span class="hljs-doctag">@return</span> invoker 服务的本地代理\n     * <span class="hljs-doctag">@throws</span> RpcException 当连接服务提供方失败时抛出\n     */</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}\n</code></pre>\n<h2>扩展配置</h2>\n<pre><code class="language-xml"><span class="hljs-comment">&lt;!-- 声明协议,如果没有配置id,将以name为id --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"xxx1"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- 引用协议,如果没有配置protocol属性,将在ApplicationContext中自动扫描protocol配置 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"xxx1"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- 引用协议缺省值,当&lt;dubbo:service&gt;没有配置prototol属性时,使用此配置 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"xxx1"</span> /&gt;</span>\n</code></pre>\n<h2>已知扩展</h2>\n<ul>\n<li><code>com.alibaba.dubbo.rpc.injvm.InjvmProtocol</code></li>\n<li><code>com.alibaba.dubbo.rpc.dubbo.DubboProtocol</code></li>\n<li><code>com.alibaba.dubbo.rpc.rmi.RmiProtocol</code></li>\n<li><code>com.alibaba.dubbo.rpc.http.HttpProtocol</code></li>\n<li><code>com.alibaba.dubbo.rpc.http.hessian.HessianProtocol</code></li>\n</ul>\n<h2>扩展示例</h2>\n<p>Maven项目结构:</p>\n<pre><code>\nsrc\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxProtocol.java (实现Protocol接口)\n                |-XxxExporter.java (实现Exporter接口)\n                |-XxxInvoker.java (实现Invoker接口)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.rpc.Protocol (纯文本文件,内容为:xxx=com.xxx.XxxProtocol)\n</code></pre>\n<p>XxxProtocol.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Protocol;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxProtocol</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Protocol</span> </span>{\n    <span class="hljs-keyword">public</span> &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        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> XxxExporter(invoker);\n    }\n    <span class="hljs-keyword">public</span> &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        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> XxxInvoker(type, url);\n    }\n}\n</code></pre>\n<p>XxxExporter.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.support.AbstractExporter;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxExporter</span>&lt;<span class="hljs-title">T</span>&gt; <span class="hljs-keyword">extends</span> <span class="hljs-title">AbstractExporter</span>&lt;<span class="hljs-title">T</span>&gt; </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">XxxExporter</span><span class="hljs-params">(Invoker&lt;T&gt; invoker)</span> <span class="hljs-keyword">throws</span> RemotingException</span>{\n        <span class="hljs-keyword">super</span>(invoker);\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">unexport</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-keyword">super</span>.unexport();\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>XxxInvoker.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.support.AbstractInvoker;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxInvoker</span>&lt;<span class="hljs-title">T</span>&gt; <span class="hljs-keyword">extends</span> <span class="hljs-title">AbstractInvoker</span>&lt;<span class="hljs-title">T</span>&gt; </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">XxxInvoker</span><span class="hljs-params">(Class&lt;T&gt; type, URL url)</span> <span class="hljs-keyword">throws</span> RemotingException</span>{\n        <span class="hljs-keyword">super</span>(type, url);\n    }\n    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">abstract</span> Object <span class="hljs-title">doInvoke</span><span class="hljs-params">(Invocation invocation)</span> <span class="hljs-keyword">throws</span> Throwable </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxProtocol\n</code></pre>\n'},{filename:"dev/impls/proxy-factory.md",__html:'<h1>动态代理扩展</h1>\n<h2>扩展说明</h2>\n<p>将 <code>Invoker</code> 接口转换成业务接口。</p>\n<h2>扩展接口</h2>\n<p><code>com.alibaba.dubbo.rpc.ProxyFactory</code></p>\n<h2>扩展配置</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">proxy</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- 缺省值配置,当&lt;dubbo:protocol&gt;没有配置proxy属性时,使用此配置 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">proxy</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n</code></pre>\n<h2>已知扩展</h2>\n<ul>\n<li><code>com.alibaba.dubbo.rpc.proxy.JdkProxyFactory</code></li>\n<li><code>com.alibaba.dubbo.rpc.proxy.JavassistProxyFactory</code></li>\n</ul>\n<h2>扩展示例</h2>\n<p>Maven 项目结构:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxProxyFactory.java (实现ProxyFactory接口)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.rpc.ProxyFactory (纯文本文件,内容为:xxx=com.xxx.XxxProxyFactory)\n</code></pre>\n<p>XxxProxyFactory.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.ProxyFactory;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Invoker;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.RpcException;\n \n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxProxyFactory</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">ProxyFactory</span> </span>{\n    <span class="hljs-keyword">public</span> &lt;T&gt; <span class="hljs-function">T <span class="hljs-title">getProxy</span><span class="hljs-params">(Invoker&lt;T&gt; invoker)</span> <span class="hljs-keyword">throws</span> RpcException </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-keyword">public</span> &lt;T&gt; <span class="hljs-function">Invoker&lt;T&gt; <span class="hljs-title">getInvoker</span><span class="hljs-params">(T proxy, Class&lt;T&gt; type, URL url)</span> <span class="hljs-keyword">throws</span> RpcException </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.rpc.ProxyFactory:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxProxyFactory\n</code></pre>\n'},{filename:"dev/impls/registry.md",__html:'<h1>注册中心扩展</h1>\n<h2>扩展说明</h2>\n<p>负责服务的注册与发现。</p>\n<h2>扩展接口</h2>\n<ul>\n<li><code>com.alibaba.dubbo.registry.RegistryFactory</code></li>\n<li><code>com.alibaba.dubbo.registry.Registry</code></li>\n</ul>\n<h2>扩展配置</h2>\n<pre><code class="language-xml"><span class="hljs-comment">&lt;!-- 定义注册中心 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"xxx1"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"xxx://ip:port"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- 引用注册中心,如果没有配置registry属性,将在ApplicationContext中自动扫描registry配置 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">registry</span>=<span class="hljs-string">"xxx1"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- 引用注册中心缺省值,当&lt;dubbo:service&gt;没有配置registry属性时,使用此配置 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">registry</span>=<span class="hljs-string">"xxx1"</span> /&gt;</span>\n</code></pre>\n<h2>扩展契约</h2>\n<p>RegistryFactory.java:</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">RegistryFactory</span> </span>{\n    <span class="hljs-comment">/**\n     * 连接注册中心.\n     * \n     * 连接注册中心需处理契约:&lt;br&gt;\n     * 1. 当设置check=false时表示不检查连接,否则在连接不上时抛出异常。&lt;br&gt;\n     * 2. 支持URL上的username:password权限认证。&lt;br&gt;\n     * 3. 支持backup=10.20.153.10备选注册中心集群地址。&lt;br&gt;\n     * 4. 支持file=registry.cache本地磁盘文件缓存。&lt;br&gt;\n     * 5. 支持timeout=1000请求超时设置。&lt;br&gt;\n     * 6. 支持session=60000会话超时或过期设置。&lt;br&gt;\n     * \n     * <span class="hljs-doctag">@param</span> url 注册中心地址,不允许为空\n     * <span class="hljs-doctag">@return</span> 注册中心引用,总不返回空\n     */</span>\n    <span class="hljs-function">Registry <span class="hljs-title">getRegistry</span><span class="hljs-params">(URL url)</span></span>; \n}\n</code></pre>\n<p>RegistryService.java:</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">RegistryService</span> </span>{ <span class="hljs-comment">// Registry extends RegistryService </span>\n    <span class="hljs-comment">/**\n     * 注册服务.\n     * \n     * 注册需处理契约:&lt;br&gt;\n     * 1. 当URL设置了check=false时,注册失败后不报错,在后台定时重试,否则抛出异常。&lt;br&gt;\n     * 2. 当URL设置了dynamic=false参数,则需持久存储,否则,当注册者出现断电等情况异常退出时,需自动删除。&lt;br&gt;\n     * 3. 当URL设置了category=overrides时,表示分类存储,缺省类别为providers,可按分类部分通知数据。&lt;br&gt;\n     * 4. 当注册中心重启,网络抖动,不能丢失数据,包括断线自动删除数据。&lt;br&gt;\n     * 5. 允许URI相同但参数不同的URL并存,不能覆盖。&lt;br&gt;\n     * \n     * <span class="hljs-doctag">@param</span> url 注册信息,不允许为空,如:dubbo://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&amp;application=kylin\n     */</span>\n    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">register</span><span class="hljs-params">(URL url)</span></span>;\n \n    <span class="hljs-comment">/**\n     * 取消注册服务.\n     * \n     * 取消注册需处理契约:&lt;br&gt;\n     * 1. 如果是dynamic=false的持久存储数据,找不到注册数据,则抛IllegalStateException,否则忽略。&lt;br&gt;\n     * 2. 按全URL匹配取消注册。&lt;br&gt;\n     * \n     * <span class="hljs-doctag">@param</span> url 注册信息,不允许为空,如:dubbo://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&amp;application=kylin\n     */</span>\n    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">unregister</span><span class="hljs-params">(URL url)</span></span>;\n \n    <span class="hljs-comment">/**\n     * 订阅服务.\n     * \n     * 订阅需处理契约:&lt;br&gt;\n     * 1. 当URL设置了check=false时,订阅失败后不报错,在后台定时重试。&lt;br&gt;\n     * 2. 当URL设置了category=overrides,只通知指定分类的数据,多个分类用逗号分隔,并允许星号通配,表示订阅所有分类数据。&lt;br&gt;\n     * 3. 允许以interface,group,version,classifier作为条件查询,如:interface=com.alibaba.foo.BarService&amp;version=1.0.0&lt;br&gt;\n     * 4. 并且查询条件允许星号通配,订阅所有接口的所有分组的所有版本,或:interface=*&amp;group=*&amp;version=*&amp;classifier=*&lt;br&gt;\n     * 5. 当注册中心重启,网络抖动,需自动恢复订阅请求。&lt;br&gt;\n     * 6. 允许URI相同但参数不同的URL并存,不能覆盖。&lt;br&gt;\n     * 7. 必须阻塞订阅过程,等第一次通知完后再返回。&lt;br&gt;\n     * \n     * <span class="hljs-doctag">@param</span> url 订阅条件,不允许为空,如:consumer://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&amp;application=kylin\n     * <span class="hljs-doctag">@param</span> listener 变更事件监听器,不允许为空\n     */</span>\n    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">subscribe</span><span class="hljs-params">(URL url, NotifyListener listener)</span></span>;\n \n    <span class="hljs-comment">/**\n     * 取消订阅服务.\n     * \n     * 取消订阅需处理契约:&lt;br&gt;\n     * 1. 如果没有订阅,直接忽略。&lt;br&gt;\n     * 2. 按全URL匹配取消订阅。&lt;br&gt;\n     * \n     * <span class="hljs-doctag">@param</span> url 订阅条件,不允许为空,如:consumer://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&amp;application=kylin\n     * <span class="hljs-doctag">@param</span> listener 变更事件监听器,不允许为空\n     */</span>\n    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">unsubscribe</span><span class="hljs-params">(URL url, NotifyListener listener)</span></span>;\n \n    <span class="hljs-comment">/**\n     * 查询注册列表,与订阅的推模式相对应,这里为拉模式,只返回一次结果。\n     * \n     * <span class="hljs-doctag">@see</span> com.alibaba.dubbo.registry.NotifyListener#notify(List)\n     * <span class="hljs-doctag">@param</span> url 查询条件,不允许为空,如:consumer://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&amp;application=kylin\n     * <span class="hljs-doctag">@return</span> 已注册信息列表,可能为空,含义同{<span class="hljs-doctag">@link</span> com.alibaba.dubbo.registry.NotifyListener#notify(List&lt;URL&gt;)}的参数。\n     */</span>\n    <span class="hljs-function">List&lt;URL&gt; <span class="hljs-title">lookup</span><span class="hljs-params">(URL url)</span></span>;\n \n}\n</code></pre>\n<p>NotifyListener.java:</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">NotifyListener</span> </span>{ \n    <span class="hljs-comment">/**\n     * 当收到服务变更通知时触发。\n     * \n     * 通知需处理契约:&lt;br&gt;\n     * 1. 总是以服务接口和数据类型为维度全量通知,即不会通知一个服务的同类型的部分数据,用户不需要对比上一次通知结果。&lt;br&gt;\n     * 2. 订阅时的第一次通知,必须是一个服务的所有类型数据的全量通知。&lt;br&gt;\n     * 3. 中途变更时,允许不同类型的数据分开通知,比如:providers, consumers, routes, overrides,允许只通知其中一种类型,但该类型的数据必须是全量的,不是增量的。&lt;br&gt;\n     * 4. 如果一种类型的数据为空,需通知一个empty协议并带category参数的标识性URL数据。&lt;br&gt;\n     * 5. 通知者(即注册中心实现)需保证通知的顺序,比如:单线程推送,队列串行化,带版本对比。&lt;br&gt;\n     * \n     * <span class="hljs-doctag">@param</span> urls 已注册信息列表,总不为空,含义同{<span class="hljs-doctag">@link</span> com.alibaba.dubbo.registry.RegistryService#lookup(URL)}的返回值。\n     */</span>\n    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">notify</span><span class="hljs-params">(List&lt;URL&gt; urls)</span></span>;\n \n}\n</code></pre>\n<h2>已知扩展</h2>\n<p><code>com.alibaba.dubbo.registry.support.dubbo.DubboRegistryFactory</code></p>\n<h2>扩展示例</h2>\n<p>Maven 项目结构:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxRegistryFactoryjava (实现RegistryFactory接口)\n                |-XxxRegistry.java (实现Registry接口)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.registry.RegistryFactory (纯文本文件,内容为:xxx=com.xxx.XxxRegistryFactory)\n</code></pre>\n<p>XxxRegistryFactory.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.registry.RegistryFactory;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.registry.Registry;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.common.URL;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxRegistryFactory</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">RegistryFactory</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Registry <span class="hljs-title">getRegistry</span><span class="hljs-params">(URL url)</span> </span>{\n        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> XxxRegistry(url);\n    }\n}\n</code></pre>\n<p>XxxRegistry.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.registry.Registry;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.registry.NotifyListener;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.common.URL;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxRegistry</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Registry</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">register</span><span class="hljs-params">(URL url)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">unregister</span><span class="hljs-params">(URL url)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">subscribe</span><span class="hljs-params">(URL url, NotifyListener listener)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">unsubscribe</span><span class="hljs-params">(URL url, NotifyListener listener)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.registry.RegistryFactory:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxRegistryFactory\n</code></pre>\n'},{filename:"dev/impls/remoting.md",__html:'<h1>网络传输扩展</h1>\n<h2>扩展说明</h2>\n<p>远程通讯的服务器及客户端传输实现。</p>\n<h2>扩展接口</h2>\n<ul>\n<li><code>com.alibaba.dubbo.remoting.Transporter</code></li>\n<li><code>com.alibaba.dubbo.remoting.Server</code></li>\n<li><code>com.alibaba.dubbo.remoting.Client</code></li>\n</ul>\n<h2>扩展配置</h2>\n<pre><code class="language-xml"><span class="hljs-comment">&lt;!-- 服务器和客户端使用相同的传输实现 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">transporter</span>=<span class="hljs-string">"xxx"</span> /&gt;</span> \n<span class="hljs-comment">&lt;!-- 服务器和客户端使用不同的传输实现 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"xxx"</span> <span class="hljs-attr">client</span>=<span class="hljs-string">"xxx"</span> /&gt;</span> \n<span class="hljs-comment">&lt;!-- 缺省值设置,当&lt;dubbo:protocol&gt;没有配置transporter/server/client属性时,使用此配置 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">transporter</span>=<span class="hljs-string">"xxx"</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"xxx"</span> <span class="hljs-attr">client</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n</code></pre>\n<h2>已知扩展</h2>\n<ul>\n<li><code>com.alibaba.dubbo.remoting.transport.transporter.netty.NettyTransporter</code></li>\n<li><code>com.alibaba.dubbo.remoting.transport.transporter.mina.MinaTransporter</code></li>\n<li><code>com.alibaba.dubbo.remoting.transport.transporter.grizzly.GrizzlyTransporter</code></li>\n</ul>\n<h2>扩展示例</h2>\n<p>Maven 项目结构:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxTransporter.java (实现Transporter接口)\n                |-XxxServer.java (实现Server接口)\n                |-XxxClient.java (实现Client接口)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.remoting.Transporter (纯文本文件,内容为:xxx=com.xxx.XxxTransporter)\n</code></pre>\n<p>XxxTransporter.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.remoting.Transporter;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxTransporter</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Transporter</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Server <span class="hljs-title">bind</span><span class="hljs-params">(URL url, ChannelHandler handler)</span> <span class="hljs-keyword">throws</span> RemotingException </span>{\n        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> XxxServer(url, handler);\n    }\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Client <span class="hljs-title">connect</span><span class="hljs-params">(URL url, ChannelHandler handler)</span> <span class="hljs-keyword">throws</span> RemotingException </span>{\n        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> XxxClient(url, handler);\n    }\n}\n</code></pre>\n<p>XxxServer.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.remoting.transport.transporter.AbstractServer;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxServer</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">AbstractServer</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">XxxServer</span><span class="hljs-params">(URL url, ChannelHandler handler)</span> <span class="hljs-keyword">throws</span> RemotingException</span>{\n        <span class="hljs-keyword">super</span>(url, handler);\n    }\n    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">doOpen</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Throwable </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">doClose</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Throwable </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Collection&lt;Channel&gt; <span class="hljs-title">getChannels</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Channel <span class="hljs-title">getChannel</span><span class="hljs-params">(InetSocketAddress remoteAddress)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>XxxClient.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.remoting.transport.transporter.AbstractClient;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxClient</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">AbstractClient</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">XxxServer</span><span class="hljs-params">(URL url, ChannelHandler handler)</span> <span class="hljs-keyword">throws</span> RemotingException</span>{\n        <span class="hljs-keyword">super</span>(url, handler);\n    }\n    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">doOpen</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Throwable </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">doClose</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Throwable </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">doConnect</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Throwable </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Channel <span class="hljs-title">getChannel</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.remoting.Transporter:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxTransporter\n</code></pre>\n'},{filename:"dev/impls/router.md",__html:'<h1>路由扩展</h1>\n<h2>扩展说明</h2>\n<p>从多个服务提者方中选择一个进行调用。</p>\n<h2>扩展接口</h2>\n<ul>\n<li><code>com.alibaba.dubbo.rpc.cluster.RouterFactory</code></li>\n<li><code>com.alibaba.dubbo.rpc.cluster.Router</code></li>\n</ul>\n<h2>已知扩展</h2>\n<ul>\n<li><code>com.alibaba.dubbo.rpc.cluster.router.ScriptRouterFactory</code></li>\n<li><code>com.alibaba.dubbo.rpc.cluster.router.FileRouterFactory</code></li>\n</ul>\n<h2>扩展示例</h2>\n<p>Maven 项目结构:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxRouterFactory.java (实现LoadBalance接口)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.rpc.cluster.RouterFactory (纯文本文件,内容为:xxx=com.xxx.XxxRouterFactory)\n\n</code></pre>\n<p>XxxRouterFactory.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.cluster.RouterFactory;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Invoker;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Invocation;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.RpcException;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxRouterFactory</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">RouterFactory</span> </span>{\n    <span class="hljs-keyword">public</span> &lt;T&gt; List&lt;Invoker&lt;T&gt;&gt; select(List&lt;Invoker&lt;T&gt;&gt; invokers, Invocation invocation) <span class="hljs-keyword">throws</span> RpcException {\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.RouterFactory:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxRouterFactory\n</code></pre>\n'},{filename:"dev/impls/serialize.md",__html:'<h1>序列化扩展</h1>\n<h2>扩展说明</h2>\n<p>将对象转成字节流,用于网络传输,以及将字节流转为对象,用于在收到字节流数据后还原成对象。</p>\n<h2>扩展接口</h2>\n<ul>\n<li><code>com.alibaba.dubbo.common.serialize.Serialization</code></li>\n<li><code>com.alibaba.dubbo.common.serialize.ObjectInput</code></li>\n<li><code>com.alibaba.dubbo.common.serialize.ObjectOutput</code></li>\n</ul>\n<h2>扩展配置</h2>\n<pre><code class="language-xml"><span class="hljs-comment">&lt;!-- 协议的序列化方式 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">serialization</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- 缺省值设置,当&lt;dubbo:protocol&gt;没有配置serialization时,使用此配置 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">serialization</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n</code></pre>\n<h2>已知扩展</h2>\n<ul>\n<li><code>com.alibaba.dubbo.common.serialize.dubbo.DubboSerialization</code></li>\n<li><code>com.alibaba.dubbo.common.serialize.hessian.Hessian2Serialization</code></li>\n<li><code>com.alibaba.dubbo.common.serialize.java.JavaSerialization</code></li>\n<li><code>com.alibaba.dubbo.common.serialize.java.CompactedJavaSerialization</code></li>\n</ul>\n<h2>扩展示例</h2>\n<p>Maven 项目结构:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxSerialization.java (实现Serialization接口)\n                |-XxxObjectInput.java (实现ObjectInput接口)\n                |-XxxObjectOutput.java (实现ObjectOutput接口)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.common.serialize.Serialization (纯文本文件,内容为:xxx=com.xxx.XxxSerialization)\n</code></pre>\n<p>XxxSerialization.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.common.serialize.Serialization;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.common.serialize.ObjectInput;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.common.serialize.ObjectOutput;\n \n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxSerialization</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Serialization</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> ObjectOutput <span class="hljs-title">serialize</span><span class="hljs-params">(Parameters parameters, OutputStream output)</span> <span class="hljs-keyword">throws</span> IOException </span>{\n        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> XxxObjectOutput(output);\n    }\n    <span class="hljs-function"><span class="hljs-keyword">public</span> ObjectInput <span class="hljs-title">deserialize</span><span class="hljs-params">(Parameters parameters, InputStream input)</span> <span class="hljs-keyword">throws</span> IOException </span>{\n        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> XxxObjectInput(input);\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.common.serialize.Serialization:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxSerialization\n</code></pre>\n'},{filename:"dev/impls/status-checker.md",__html:'<h1>状态检查扩展</h1>\n<h2>扩展说明</h2>\n<p>检查服务依赖各种资源的状态,此状态检查可同时用于 telnet 的 status 命令和 hosting 的 status 页面。</p>\n<h2>扩展接口</h2>\n<p><code>com.alibaba.dubbo.common.status.StatusChecker</code></p>\n<h2>扩展配置</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">status</span>=<span class="hljs-string">"xxx,yyy"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- 缺省值设置,当&lt;dubbo:protocol&gt;没有配置status属性时,使用此配置 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">status</span>=<span class="hljs-string">"xxx,yyy"</span> /&gt;</span>\n</code></pre>\n<h2>已知扩展</h2>\n<ul>\n<li><code>com.alibaba.dubbo.common.status.support.MemoryStatusChecker</code></li>\n<li><code>com.alibaba.dubbo.common.status.support.LoadStatusChecker</code></li>\n<li><code>com.alibaba.dubbo.rpc.dubbo.status.ServerStatusChecker</code></li>\n<li><code>com.alibaba.dubbo.rpc.dubbo.status.ThreadPoolStatusChecker</code></li>\n<li><code>com.alibaba.dubbo.registry.directory.RegistryStatusChecker</code></li>\n<li><code>com.alibaba.dubbo.rpc.config.spring.status.SpringStatusChecker</code></li>\n<li><code>com.alibaba.dubbo.rpc.config.spring.status.DataSourceStatusChecker</code></li>\n</ul>\n<h2>扩展示例</h2>\n<p>Maven 项目结构:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxStatusChecker.java (实现StatusChecker接口)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.common.status.StatusChecker (纯文本文件,内容为:xxx=com.xxx.XxxStatusChecker)\n</code></pre>\n<p>XxxStatusChecker.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.common.status.StatusChecker;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxStatusChecker</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">StatusChecker</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Status <span class="hljs-title">check</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxStatusChecker\n</code></pre>\n'},{filename:"dev/impls/telnet-handler.md",__html:'<h1>Telnet 命令扩展</h1>\n<h2>扩展说明</h2>\n<p>所有服务器均支持 telnet 访问,用于人工干预。</p>\n<h2>扩展接口</h2>\n<p><code>com.alibaba.dubbo.remoting.telnet.TelnetHandler</code></p>\n<h2>扩展配置</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">telnet</span>=<span class="hljs-string">"xxx,yyy"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- 缺省值设置,当&lt;dubbo:protocol&gt;没有配置telnet属性时,使用此配置 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">telnet</span>=<span class="hljs-string">"xxx,yyy"</span> /&gt;</span>\n</code></pre>\n<h2>已知扩展</h2>\n<ul>\n<li><code>com.alibaba.dubbo.remoting.telnet.support.ClearTelnetHandler</code></li>\n<li><code>com.alibaba.dubbo.remoting.telnet.support.ExitTelnetHandler</code></li>\n<li><code>com.alibaba.dubbo.remoting.telnet.support.HelpTelnetHandler</code></li>\n<li><code>com.alibaba.dubbo.remoting.telnet.support.StatusTelnetHandler</code></li>\n<li><code>com.alibaba.dubbo.rpc.dubbo.telnet.ListTelnetHandler</code></li>\n<li><code>com.alibaba.dubbo.rpc.dubbo.telnet.ChangeTelnetHandler</code></li>\n<li><code>com.alibaba.dubbo.rpc.dubbo.telnet.CurrentTelnetHandler</code></li>\n<li><code>com.alibaba.dubbo.rpc.dubbo.telnet.InvokeTelnetHandler</code></li>\n<li><code>com.alibaba.dubbo.rpc.dubbo.telnet.TraceTelnetHandler</code></li>\n<li><code>com.alibaba.dubbo.rpc.dubbo.telnet.CountTelnetHandler</code></li>\n<li><code>com.alibaba.dubbo.rpc.dubbo.telnet.PortTelnetHandler</code></li>\n</ul>\n<h2>扩展示例</h2>\n<p>Maven 项目结构:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxTelnetHandler.java (实现TelnetHandler接口)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.remoting.telnet.TelnetHandler (纯文本文件,内容为:xxx=com.xxx.XxxTelnetHandler)\n</code></pre>\n<p>XxxTelnetHandler.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.remoting.telnet.TelnetHandler;\n \n<span class="hljs-meta">@Help</span>(parameter=<span class="hljs-string">"..."</span>, summary=<span class="hljs-string">"..."</span>, detail=<span class="hljs-string">"..."</span>)\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxTelnetHandler</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">TelnetHandler</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">telnet</span><span class="hljs-params">(Channel channel, String message)</span> <span class="hljs-keyword">throws</span> RemotingException </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.remoting.telnet.TelnetHandler:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxTelnetHandler\n</code></pre>\n<h2>用法</h2>\n<pre><code class="language-sh">telnet 127.0.0.1 20880\ndubbo&gt; xxx args\n</code></pre>\n'},{filename:"dev/impls/threadpool.md",__html:'<h1>线程池扩展</h1>\n<h2>扩展说明</h2>\n<p>服务提供方线程程实现策略,当服务器收到一个请求时,需要在线程池中创建一个线程去执行服务提供方业务逻辑。</p>\n<h2>扩展接口</h2>\n<p><code>com.alibaba.dubbo.common.threadpool.ThreadPool</code></p>\n<h2>扩展配置</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">threadpool</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- 缺省值设置,当&lt;dubbo:protocol&gt;没有配置threadpool时,使用此配置 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">threadpool</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n</code></pre>\n<h2>已知扩展</h2>\n<ul>\n<li><code>com.alibaba.dubbo.common.threadpool.FixedThreadPool</code></li>\n<li><code>com.alibaba.dubbo.common.threadpool.CachedThreadPool</code></li>\n</ul>\n<h2>扩展示例</h2>\n<p>Maven 项目结构:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxThreadPool.java (实现ThreadPool接口)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.common.threadpool.ThreadPool (纯文本文件,内容为:xxx=com.xxx.XxxThreadPool)\n</code></pre>\n<p>XxxThreadPool.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.common.threadpool.ThreadPool;\n<span class="hljs-keyword">import</span> java.util.concurrent.Executor;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxThreadPool</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">ThreadPool</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Executor <span class="hljs-title">getExecutor</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.common.threadpool.ThreadPool:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxThreadPool\n</code></pre>\n'},{filename:"dev/impls/validation.md",__html:'<h1>验证扩展</h1>\n<h2>扩展说明</h2>\n<p>参数验证扩展点。</p>\n<h2>扩展接口</h2>\n<p><code>com.alibaba.dubbo.validation.Validation</code></p>\n<h2>扩展配置</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">validation</span>=<span class="hljs-string">"xxx,yyy"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- 缺省值设置,当&lt;dubbo:service&gt;没有配置validation属性时,使用此配置 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">validation</span>=<span class="hljs-string">"xxx,yyy"</span> /&gt;</span>\n</code></pre>\n<h2>已知扩展</h2>\n<p><code>com.alibaba.dubbo.validation.support.jvalidation.JValidation</code></p>\n<h2>扩展示例</h2>\n<p>Maven 项目结构:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxValidation.java (实现Validation接口)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.validation.Validation (纯文本文件,内容为:xxx=com.xxx.XxxValidation)\n</code></pre>\n<p>XxxValidation.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.validation.Validation;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxValidation</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Validation</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Object <span class="hljs-title">getValidator</span><span class="hljs-params">(URL url)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>XxxValidator.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.validation.Validator;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxValidator</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Validator</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">XxxValidator</span><span class="hljs-params">(URL url)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">validate</span><span class="hljs-params">(Invocation invocation)</span> <span class="hljs-keyword">throws</span> Exception </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.validation.Validation:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxValidation\n</code></pre>\n'},{filename:"dev/introduction.md",__html:"<p>这里增加《开发指南》的内容</p>\n"},{filename:"dev/principals/code-detail.md",__html:'<h1>魔鬼在细节</h1>\n<blockquote>\n<p><a href="http://javatar.iteye.com/blog/1056664">http://javatar.iteye.com/blog/1056664</a></p>\n</blockquote>\n<p>最近一直担心 Dubbo 分布式服务框架后续如果维护人员增多或变更,会出现质量的下降, 我在想,有没有什么是需要大家共同遵守的,根据平时写代码时的一习惯,总结了一下在写代码过程中,尤其是框架代码,要时刻牢记的细节。可能下面要讲的这些,大家都会觉得很简单,很基础,但要做到时刻牢记。在每一行代码中都考虑这些因素,是需要很大耐心的, 大家经常说,魔鬼在细节中,确实如此。</p>\n<h2>防止空指针和下标越界</h2>\n<p>这是我最不喜欢看到的异常,尤其在核心框架中,我更愿看到信息详细的参数不合法异常。这也是一个健状的程序开发人员,在写每一行代码都应在潜意识中防止的异常。基本上要能确保一次写完的代码,在不测试的情况,都不会出现这两个异常才算合格。</p>\n<h2>保证线程安全性和可见性</h2>\n<p>对于框架的开发人员,对线程安全性和可见性的深入理解是最基本的要求。需要开发人员,在写每一行代码时都应在潜意识中确保其正确性。因为这种代码,在小并发下做功能测试时,会显得很正常。但在高并发下就会出现莫明其妙的问题,而且场景很难重现,极难排查。</p>\n<h2>尽早失败和前置断言</h2>\n<p>尽早失败也应该成为潜意识,在有传入参数和状态变化时,均在入口处全部断言。一个不合法的值和状态,在第一时间就应报错,而不是等到要用时才报错。因为等到要用时,可能前面已经修改其它相关状态,而在程序中很少有人去处理回滚逻辑。这样报错后,其实内部状态可能已经混乱,极易在一个隐蔽分支上引发程序不可恢复。</p>\n<h2>分离可靠操作和不可靠操作</h2>\n<p>这里的可靠是狭义的指是否会抛出异常或引起状态不一致,比如,写入一个线程安全的 Map,可以认为是可靠的,而写入数据库等,可以认为是不可靠的。开发人员必须在写每一行代码时,都注意它的可靠性与否,在代码中尽量划分开,并对失败做异常处理,并为容错,自我保护,自动恢复或切换等补偿逻辑提供清晰的切入点,保证后续增加的代码不至于放错位置,而导致原先的容错处理陷入混乱。</p>\n<h2>异常防御,但不忽略异常</h2>\n<p>这里讲的异常防御,指的是对非必须途径上的代码进行最大限度的容忍,包括程序上的 BUG,比如:获取程序的版本号,会通过扫描 Manifest 和 jar 包名称抓取版本号,这个逻辑是辅助性的,但代码却不少,初步测试也没啥问题,但应该在整个 getVersion() 中加上一个全函数的 try-catch 打印错误日志,并返回基本版本,因为 getVersion() 可能存在未知特定场景异常,或被其他的开发人员误修改逻辑(但一般人员不会去掉 try-catch),而如果它抛出异常会导致主流程异常,这是我们不希望看到的。但这里要控制个度,不要随意 try-catch,更不要无声无息的吃掉异常。</p>\n<h2>缩小可变域和尽量 final</h2>\n<p>如果一个类可以成为不变类(Immutable Class),就优先将它设计成不变类。不变类有天然的并发共享优势,减少同步或复制,而且可以有效帮忙分析线程安全的范围。就算是可变类,对于从构造函数传入的引用,在类中持有时,最好将字段 final,以免被中途误修改引用。不要以为这个字段是私有的,这个类的代码都是我自己写的,不会出现对这个字段的重新赋值。要考虑的一个因素是,这个代码可能被其他人修改,他不知道你的这个弱约定,final 就是一个不变契约。</p>\n<h2>降低修改时的误解性,不埋雷</h2>\n<p>前面不停的提到代码被其他人修改,这也开发人员要随时紧记的。这个其他人包括未来的自己,你要总想着这个代码可能会有人去改它。我应该给修改的人一点什么提示,让他知道我现在的设计意图,而不要在程序里面加潜规则,或埋一些容易忽视的雷,比如:你用 null 表示不可用,size 等于 0 表示黑名单,这就是一个雷,下一个修改者,包括你自己,都不会记得有这样的约定,可能后面为了改某个其它 BUG,不小心改到了这里,直接引爆故障。对于这个例子,一个原则就是永远不要区分 null 引用和 empty 值。</p>\n<h2>提高代码的可测性</h2>\n<p>这里的可测性主要指 Mock 的容易程度,和测试的隔离性。至于测试的自动性,可重复性,非偶然性,无序性,完备性(全覆盖),轻量性(可快速执行),一般开发人员,加上 JUnit 等工具的辅助基本都能做到,也能理解它的好处,只是工作量问题。这里要特别强调的是测试用例的单一性(只测目标类本身)和隔离性(不传染失败)。现在的测试代码,过于强调完备性,大量重复交叉测试,看起来没啥坏处,但测试代码越多,维护代价越高。经常出现的问题是,修改一行代码或加一个判断条件,引起 100 多个测试用例不通过。时间一紧,谁有这个闲功夫去改这么多形态各异的测试用例?久而久之,这个测试代码就已经不能真实反应代码现在的状况,很多时候会被迫绕过。最好的情况是,修改一行代码,有且只有一行测试代码不通过。如果修改了代码而测试用例还能通过,那也不行,表示测试没有覆盖到。另外,可 Mock 性是隔离的基础,把间接依赖的逻辑屏蔽掉。可 Mock 性的一个最大的杀手就是静态方法,尽量少用。</p>\n'},{filename:"dev/principals/configuration.md",__html:'<h1>配置设计</h1>\n<blockquote>\n<p><a href="http://javatar.iteye.com/blog/949527">http://javatar.iteye.com/blog/949527</a></p>\n</blockquote>\n<p>Dubbo 现在的设计是完全无侵入,也就是使用者只依赖于配置契约。经过多个版本的发展,为了满足各种需求场景,配置越来越多。为了保持兼容,配置只增不减,里面潜伏着各种风格,约定,规则。新版本也将配置做了一次调整,去掉了 dubbo.properties,改为全 spring 配置。将想到的一些记在这,备忘。</p>\n<h2>配置分类</h2>\n<p>首先,配置的用途是有多种的,大致可以分为:</p>\n<ol start="0">\n<li>环境配置,比如:连接数,超时等配置。</li>\n<li>描述配置,比如:服务接口描述,服务版本等。</li>\n<li>扩展配置,比如:协议扩展,策略扩展等。</li>\n</ol>\n<h2>配置格式</h2>\n<p>通常环境配置,用 properties 配置会比较方便,因为都是一些离散的简单值,用 key-value 配置可以减少配置的学习成本。</p>\n<p>而描述配置,通常信息比较多,甚至有层次关系,用 xml 配置会比较方便,因为树结构的配置表现力更强。如果非常复杂,也可以考自定义 DSL 做为配置。有时候这类配置也可以用 Annotation 代替, 因为这些配置和业务逻辑相关,放在代码里也是合理的。</p>\n<p>另外扩展配置,可能不尽相同。如果只是策略接口实现类替换,可以考虑 properties 等结构。如果有复杂的生命周期管理,可能需要 XML 等配置。有时候扩展会通过注册接口的方式提供。</p>\n<h2>配置加载</h2>\n<p>对于环境配置,在 java 世界里,比较常规的做法,是在 classpath 下约定一个以项目为名称的 properties 配置,比如:log4j.properties,velocity.properties等。产品在初始化时,自动从 classpath 下加载该配置。我们平台的很多项目也使用类似策略,如:dubbo.properties,comsat.xml 等。这样有它的优势,就是基于约定,简化了用户对配置加载过程的干预。但同样有它的缺点,当 classpath 存在同样的配置时,可能误加载,以及在 ClassLoader 隔离时,可能找不到配置,并且,当用户希望将配置放到统一的目录时,不太方便。</p>\n<p>Dubbo 新版本去掉了 dubbo.properties,因为该约定经常造成配置冲突。</p>\n<p>而对于描述配置,因为要参与业务逻辑,通常会嵌到应用的生命周期管理中。现在使用 spring 的项目越来越多,直接使用 spring 配置的比较普遍,而且 spring 允许自定义 schema,配置简化后很方便。当然,也有它的缺点,就是强依赖 spring,可以提编程接口做了配套方案。</p>\n<p>在 Dubbo 即存在描述配置,也有环境配置。一部分用 spring 的 schame 配置加载,一部分从 classpath 扫描 properties 配置加载。用户感觉非常不便,所以在新版本中进行了合并,统一放到 spring 的 schame 配置加载,也增加了配置的灵活性。</p>\n<p>扩展配置,通常对配置的聚合要求比较高。因为产品需要发现第三方实现,将其加入产品内部。在 java 世界里,通常是约定在每个 jar 包下放一个指定文件加载,比如:eclipse 的 plugin.xml,struts2 的 struts-plugin.xml 等,这类配置可以考虑 java 标准的服务发现机制,即在 jar 包的 META-INF/services 下放置接口类全名文件,内容为每行一个实现类类名,就像 jdk 中的加密算法扩展,脚本引擎扩展,新的 JDBC 驱动等,都是采用这种方式。参见:<a href="http://download.oracle.com/javase/1.4.2/docs/guide/jar/jar.html#Service%20Provider">ServiceProvider 规范</a>。</p>\n<p>Dubbo 旧版本通过约定在每个 jar 包下,放置名为 dubbo-context.xml 的 spring 配置进行扩展与集成,新版本改成用 jdk 自带的 META-INF/services 方式,去掉过多的 spring 依赖。</p>\n<h2>可编程配置</h2>\n<p>配置的可编程性是非常必要的,不管你以何种方式加载配置文件,都应该提供一个编程的配置方式,允许用户不使用配置文件,直接用代码完成配置过程。因为一个产品,尤其是组件类产品,通常需要和其它产品协作使用,当用户集成你的产品时,可能需要适配配置方式。</p>\n<p>Dubbo 新版本提供了与 xml 配置一对一的配置类,如:ServiceConfig 对应 <code>&lt;dubbo:service /&gt;</code>,并且属性也一对一,这样有利于文件配置与编程配置的一致性理解,减少学习成本。</p>\n<h2>配置缺省值</h2>\n<p>配置的缺省值,通常是设置一个常规环境的合理值,这样可以减少用户的配置量。通常建议以线上环境为参考值,开发环境可以通过修改配置适应。缺省值的设置,最好在最外层的配置加载就做处理。程序底层如果发现配置不正确,就应该直接报错,容错在最外层做。如果在程序底层使用时,发现配置值不合理,就填一个缺省值,很容易掩盖表面问题,而引发更深层次的问题。并且配置的中间传递层,很可能并不知道底层使用了一个缺省值,一些中间的检测条件就可能失效。Dubbo 就出现过这样的问题,中间层用“地址”做为缓存 Key, 而底层,给“地址”加了一个缺省端口号,导致不加端口号的“地址”和加了缺省端口的“地址”并没有使用相同的缓存。</p>\n<h2>配置一致性</h2>\n<p>配置总会隐含一些风格或潜规则,应尽可能保持其一致性。比如:很多功能都有开关,然后有一个配置值:</p>\n<ol start="0">\n<li>是否使用注册中心,注册中心地址。</li>\n<li>是否允许重试,重试次数。</li>\n</ol>\n<p>你可以约定:</p>\n<ol start="0">\n<li>每个都是先配置一个 boolean 类型的开关,再配置一个值。</li>\n<li>用一个无效值代表关闭,N/A地址,0重试次数等。</li>\n</ol>\n<p>不管选哪种方式,所有配置项,都应保持同一风格,Dubbo 选的是第二种。相似的还有,超时时间,重试时间,定时器间隔时间。如果一个单位是秒,另一个单位是毫秒(C3P0的配置项就是这样),配置人员会疯掉。</p>\n<h2>配置覆盖</h2>\n<p>提供配置时,要同时考虑开发人员,测试人员,配管人员,系统管理员。测试人员是不能修改代码的,而测试的环境很可能较为复杂,需要为测试人员留一些“后门”,可以在外围修改配置项。就像 spring 的 PropertyPlaceholderConfigurer 配置,支持 <code>SYSTEM_PROPERTIES_MODE_OVERRIDE</code>,可以通过 JVM 的 -D 参数,或者像 hosts 一样约定一个覆盖配置文件,在程序外部,修改部分配置,便于测试。</p>\n<p>Dubbo 支持通过 JVM 参数 <code>-Dcom.xxx.XxxService=dubbo://10.1.1.1:1234</code> 直接使远程服务调用绕过注册中心,进行点对点测试。还有一种情况,开发人员增加配置时,都会按线上的部署情况做配置,如:<code>&lt;dubbo:registry address=&quot;${dubbo.registry.address}&quot; /&gt;</code> 因为线上只有一个注册中心,这样的配置是没有问题的,而测试环境可能有两个注册中心,测试人员不可能去修改配置,改为:\n<code>&lt;dubbo:registry address=&quot;${dubbo.registry.address1}&quot; /&gt;</code>,\n<code>&lt;dubbo:registry address=&quot;${dubbo.registry.address2}&quot; /&gt;</code>,所以这个地方,Dubbo 支持在 ${dubbo.registry.address} 的值中,通过竖号分隔多个注册中心地址,用于表示多注册中心地址。</p>\n<h2>配置继承</h2>\n<p>配置也存在“重复代码”,也存在“泛化与精化”的问题。比如:Dubbo 的超时时间设置,每个服务,每个方法,都应该可以设置超时时间。但很多服务不关心超时,如果要求每个方法都配置,是不现实的。所以 Dubbo 采用了方法超时继承服务超时,服务超时再继承缺省超时,没配置时,一层层向上查找。</p>\n<p>另外,Dubbo 旧版本所有的超时时间,重试次数,负载均衡策略等都只能在服务消费方配置。但实际使用过程中发现,服务提供方比消费方更清楚,但这些配置项是在消费方执行时才用到的。新版本,就加入了在服务提供方也能配这些参数,通过注册中心传递到消费方,\n做为参考值,如果消费方没有配置,就以提供方的配置为准,相当于消费方继承了提供方的建议配置值。而注册中心在传递配置时,也可以在中途修改配置,这样就达到了治理的目的,继承关系相当于:服务消费者 --&gt; 注册中心 --&gt; 服务提供者</p>\n<p><img src="../sources/images/configuration-override.png" alt="configuration-override"></p>\n<h2>配置向后兼容</h2>\n<p>向前兼容很好办,你只要保证配置只增不减,就基本上能保证向前兼容。但向后兼容,也是要注意的,要为后续加入新的配置项做好准备。如果配置出现一个特殊配置,就应该为这个“特殊”情况约定一个兼容规则,因为这个特殊情况,很有可能在以后还会发生。比如:有一个配置文件是保存“服务=地址”映射关系的,其中有一行特殊,保存的是“注册中心=地址”。现在程序加载时,约定“注册中心”这个Key是特殊的,做特别处理,其它的都是“服务”。然而,新版本发现,要加一项“监控中心=地址”,这时,旧版本的程序会把“监控中心”做为“服务”处理,因为旧代码是不能改的,兼容性就很会很麻烦。如果先前约定“特殊标识+XXX”为特殊处理,后续就会方便很多。</p>\n<p>向后兼容性,可以多向HTML5学习,参见:<a href="http://javatar.iteye.com/blog/949390">HTML5设计原理</a></p>\n'},{filename:"dev/principals/dummy.md",__html:'<h1>防痴呆设计</h1>\n<blockquote>\n<p><a href="http://javatar.iteye.com/blog/804187">http://javatar.iteye.com/blog/804187</a></p>\n</blockquote>\n<p>最近有点痴呆,因为解决了太多的痴呆问题。服务框架实施面超来超广,已有 50 多个项目在使用,每天都要去帮应用查问题,来来回回,发现大部分都是配置错误,或者重复的文件或类,或者网络不通等,所以准备在新版本中加入防痴呆设计。估且这么叫吧,可能很简单,但对排错速度还是有点帮助,希望能抛砖引玉,也希望大家多给力,想出更多的防范措施共享出来。</p>\n<h2>检查重复的jar包</h2>\n<p>最痴呆的问题,就是有多个版本的相同jar包,会出现新版本的 A 类,调用了旧版本的 B 类,而且和JVM加载顺序有关,问题带有偶然性,误导性,遇到这种莫名其妙的问题,最头疼,所以,第一条,先把它防住,在每个 jar 包中挑一个一定会加载的类,加上重复类检查,给个示例:</p>\n<pre><code class="language-java"><span class="hljs-keyword">static</span> {  \n    Duplicate.checkDuplicate(Xxx.class);  \n}  \n</code></pre>\n<p>检查重复工具类:</p>\n<pre><code class="language-java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Duplicate</span> </span>{  \n  \n    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-title">Duplicate</span><span class="hljs-params">()</span> </span>{}  \n  \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">checkDuplicate</span><span class="hljs-params">(Class cls)</span> </span>{  \n        checkDuplicate(cls.getName().replace(<span class="hljs-string">\'.\'</span>, <span class="hljs-string">\'/\'</span>) + <span class="hljs-string">".class"</span>);  \n    }  \n  \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">checkDuplicate</span><span class="hljs-params">(String path)</span> </span>{  \n        <span class="hljs-keyword">try</span> {  \n            <span class="hljs-comment">// 在ClassPath搜文件  </span>\n            Enumeration urls = Thread.currentThread().getContextClassLoader().getResources(path);  \n            Set files = <span class="hljs-keyword">new</span> HashSet();  \n            <span class="hljs-keyword">while</span> (urls.hasMoreElements()) {  \n                URL url = urls.nextElement();  \n                <span class="hljs-keyword">if</span> (url != <span class="hljs-keyword">null</span>) {  \n                    String file = url.getFile();  \n                    <span class="hljs-keyword">if</span> (file != <span class="hljs-keyword">null</span> &amp;amp;&amp;amp; file.length() &amp;gt; <span class="hljs-number">0</span>) {  \n                        files.add(file);  \n                    }  \n                }  \n            }  \n            <span class="hljs-comment">// 如果有多个,就表示重复  </span>\n            <span class="hljs-keyword">if</span> (files.size() &amp;gt; <span class="hljs-number">1</span>) {  \n                logger.error(<span class="hljs-string">"Duplicate class "</span> + path + <span class="hljs-string">" in "</span> + files.size() + <span class="hljs-string">" jar "</span> + files);  \n            }  \n        } <span class="hljs-keyword">catch</span> (Throwable e) { <span class="hljs-comment">// 防御性容错  </span>\n            logger.error(e.getMessage(), e);  \n        }  \n    }  \n  \n}  \n</code></pre>\n<h2>检查重复的配置文件</h2>\n<p>配置文件加载错,也是经常碰到的问题。用户通常会和你说:“我配置的很正确啊,不信我发给你看下,但就是报错”。然后查一圈下来,原来他发过来的配置根本没加载,平台很多产品都会在 classpath 下放一个约定的配置,如果项目中有多个,通常会取JVM加载的第一个,为了不被这么低级的问题折腾,和上面的重复jar包一样,在配置加载的地方,加上:</p>\n<pre><code class="language-java">Duplicate.checkDuplicate(<span class="hljs-string">"xxx.properties"</span>); \n</code></pre>\n<h2>检查所有可选配置</h2>\n<p>必填配置估计大家都会检查,因为没有的话,根本没法运行。但对一些可选参数,也应该做一些检查,比如:服务框架允许通过注册中心关联服务消费者和服务提供者,也允许直接配置服务提供者地址点对点直连,这时候,注册中心地址是可选的,但如果没有配点对点直连配置,注册中心地址就一定要配,这时候也要做相应检查。</p>\n<h2>异常信息给出解决方案</h2>\n<p>在给应用排错时,最怕的就是那种只有简单的一句错误描述,啥信息都没有的异常信息。比如上次碰到一个 Failed to get session 异常,就这几个单词,啥都没有,哪个 session 出错? 什么原因 Failed? 看了都快疯掉,因是线上环境不好调试,而且有些场景不是每次都能重现。异常最基本要带有上下文信息,包括操作者,操作目标,原因等,最好的异常信息,应给出解决方案,比如上面可以给出:&quot;从 10.20.16.3 到 10.20.130.20:20880 之间的网络不通,请在 10.20.16.3 使用 telnet 10.20.130.20 20880 测试一下网络,如果是跨机房调用,可能是防火墙阻挡,请联系 SA 开通访问权限&quot; 等等,上面甚至可以根据 IP 段判断是不是跨机房。另外一个例子,是 spring-web 的 context 加载,如果在 getBean 时 spring 没有被启动,spring 会报一个错,错误信息写着:请在 web.xml 中加入: <code>&lt;listener&gt;...&lt;init-param&gt;...</code>,多好的同学,看到错误的人复制一下就完事了,我们该学学。可以把常见的错误故意犯一遍,看看错误信息能否自我搞定问题,\n或者把平时支持应用时遇到的问题及解决办法都写到异常信息里。</p>\n<h2>日志信息包含环境信息</h2>\n<p>每次应用一出错,应用的开发或测试就会把出错信息发过来,询问原因,这时候我都会问一大堆套话,用的哪个版本呀?是生产环境还是开发测试环境?哪个注册中心呀?哪个项目中的?哪台机器呀?哪个服务? 累啊,最主要的是,有些开发或测试人员根本分不清,没办法,只好提供上门服务,浪费的时间可不是浮云,所以,日志中最好把需要的环境信息一并打进去,最好给日志输出做个包装,统一处理掉,免得忘了。包装Logger接口如:</p>\n<pre><code class="language-java"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">error</span><span class="hljs-params">(String msg, Throwable e)</span> </span>{  \n    delegate.error(msg + <span class="hljs-string">" on server "</span> + InetAddress.getLocalHost() + <span class="hljs-string">" using version "</span> + Version.getVersion(), e);  \n}  \n</code></pre>\n<p>获取版本号工具类:</p>\n<pre><code class="language-java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Version</span> </span>{  \n  \n    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-title">Version</span><span class="hljs-params">()</span> </span>{}  \n  \n    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Logger logger = LoggerFactory.getLogger(Version.class);  \n  \n    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Pattern VERSION_PATTERN = Pattern.compile(<span class="hljs-string">"([0-9][0-9\\\\.\\\\-]*)\\\\.jar"</span>);  \n  \n    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> String VERSION = getVersion(Version.class, <span class="hljs-string">"2.0.0"</span>);  \n  \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">getVersion</span><span class="hljs-params">()</span></span>{  \n        <span class="hljs-keyword">return</span> VERSION;  \n    }  \n  \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">getVersion</span><span class="hljs-params">(Class cls, String defaultVersion)</span> </span>{  \n        <span class="hljs-keyword">try</span> {  \n            <span class="hljs-comment">// 首先查找MANIFEST.MF规范中的版本号  </span>\n            String version = cls.getPackage().getImplementationVersion();  \n            <span class="hljs-keyword">if</span> (version == <span class="hljs-keyword">null</span> || version.length() == <span class="hljs-number">0</span>) {  \n                version = cls.getPackage().getSpecificationVersion();  \n            }  \n            <span class="hljs-keyword">if</span> (version == <span class="hljs-keyword">null</span> || version.length() == <span class="hljs-number">0</span>) {  \n                <span class="hljs-comment">// 如果MANIFEST.MF规范中没有版本号,基于jar包名获取版本号  </span>\n                String file = cls.getProtectionDomain().getCodeSource().getLocation().getFile();  \n                <span class="hljs-keyword">if</span> (file != <span class="hljs-keyword">null</span> &amp;amp;&amp;amp; file.length() &amp;gt; <span class="hljs-number">0</span> &amp;amp;&amp;amp; file.endsWith(<span class="hljs-string">".jar"</span>)) {  \n                    Matcher matcher = VERSION_PATTERN.matcher(file);  \n                    <span class="hljs-keyword">while</span> (matcher.find() &amp;amp;&amp;amp; matcher.groupCount() &amp;gt; <span class="hljs-number">0</span>) {  \n                        version = matcher.group(<span class="hljs-number">1</span>);  \n                    }  \n                }  \n            }  \n            <span class="hljs-comment">// 返回版本号,如果为空返回缺省版本号  </span>\n            <span class="hljs-keyword">return</span> version == <span class="hljs-keyword">null</span> || version.length() == <span class="hljs-number">0</span> ? defaultVersion : version;  \n        } <span class="hljs-keyword">catch</span> (Throwable e) { <span class="hljs-comment">// 防御性容错  </span>\n            <span class="hljs-comment">// 忽略异常,返回缺省版本号  </span>\n            logger.error(e.getMessage(), e);  \n            <span class="hljs-keyword">return</span> defaultVersion;  \n        }  \n    }  \n  \n}\n</code></pre>\n<h2>kill 之前先 dump</h2>\n<p>每次线上环境一出问题,大家就慌了,通常最直接的办法回滚重启,以减少故障时间,这样现场就被破坏了,要想事后查问题就麻烦了,有些问题必须在线上的大压力下才会发生,线下测试环境很难重现,不太可能让开发或 Appops 在重启前,先手工将出错现场所有数据备份一下,所以最好在 kill 脚本之前调用 dump,进行自动备份,这样就不会有人为疏忽。dump脚本示例:</p>\n<pre><code class="language-sh">JAVA_HOME=/usr/java  \nOUTPUT_HOME=~/output  \nDEPLOY_HOME=`dirname <span class="hljs-variable">$0</span>`  \nHOST_NAME=`hostname`  \n  \nDUMP_PIDS=`ps  --no-heading -C java -f --width 1000 | grep <span class="hljs-string">"<span class="hljs-variable">$DEPLOY_HOME</span>"</span> |awk <span class="hljs-string">\'{print $2}\'</span>`  \n<span class="hljs-keyword">if</span> [ -z <span class="hljs-string">"<span class="hljs-variable">$DUMP_PIDS</span>"</span> ]; <span class="hljs-keyword">then</span>  \n    <span class="hljs-built_in">echo</span> <span class="hljs-string">"The server <span class="hljs-variable">$HOST_NAME</span> is not started!"</span>  \n    <span class="hljs-built_in">exit</span> 1;  \n<span class="hljs-keyword">fi</span>  \n  \nDUMP_ROOT=<span class="hljs-variable">$OUTPUT_HOME</span>/dump  \n<span class="hljs-keyword">if</span> [ ! -d <span class="hljs-variable">$DUMP_ROOT</span> ]; <span class="hljs-keyword">then</span>  \n    mkdir <span class="hljs-variable">$DUMP_ROOT</span>  \n<span class="hljs-keyword">fi</span>  \n  \nDUMP_DATE=`date +%Y%m%d%H%M%S`  \nDUMP_DIR=<span class="hljs-variable">$DUMP_ROOT</span>/dump-<span class="hljs-variable">$DUMP_DATE</span>  \n<span class="hljs-keyword">if</span> [ ! -d <span class="hljs-variable">$DUMP_DIR</span> ]; <span class="hljs-keyword">then</span>  \n    mkdir <span class="hljs-variable">$DUMP_DIR</span>  \n<span class="hljs-keyword">fi</span>  \n  \n<span class="hljs-built_in">echo</span> -e <span class="hljs-string">"Dumping the server <span class="hljs-variable">$HOST_NAME</span> ...\\c"</span>  \n<span class="hljs-keyword">for</span> PID <span class="hljs-keyword">in</span> <span class="hljs-variable">$DUMP_PIDS</span> ; <span class="hljs-keyword">do</span>  \n    <span class="hljs-variable">$JAVA_HOME</span>/bin/jstack <span class="hljs-variable">$PID</span> &gt; <span class="hljs-variable">$DUMP_DIR</span>/jstack-<span class="hljs-variable">$PID</span>.dump 2&gt;&amp;1  \n    <span class="hljs-built_in">echo</span> -e <span class="hljs-string">".\\c"</span>  \n    <span class="hljs-variable">$JAVA_HOME</span>/bin/jinfo <span class="hljs-variable">$PID</span> &gt; <span class="hljs-variable">$DUMP_DIR</span>/jinfo-<span class="hljs-variable">$PID</span>.dump 2&gt;&amp;1  \n    <span class="hljs-built_in">echo</span> -e <span class="hljs-string">".\\c"</span>  \n    <span class="hljs-variable">$JAVA_HOME</span>/bin/jstat -gcutil <span class="hljs-variable">$PID</span> &gt; <span class="hljs-variable">$DUMP_DIR</span>/jstat-gcutil-<span class="hljs-variable">$PID</span>.dump 2&gt;&amp;1  \n    <span class="hljs-built_in">echo</span> -e <span class="hljs-string">".\\c"</span>  \n    <span class="hljs-variable">$JAVA_HOME</span>/bin/jstat -gccapacity <span class="hljs-variable">$PID</span> &gt; <span class="hljs-variable">$DUMP_DIR</span>/jstat-gccapacity-<span class="hljs-variable">$PID</span>.dump 2&gt;&amp;1  \n    <span class="hljs-built_in">echo</span> -e <span class="hljs-string">".\\c"</span>  \n    <span class="hljs-variable">$JAVA_HOME</span>/bin/jmap <span class="hljs-variable">$PID</span> &gt; <span class="hljs-variable">$DUMP_DIR</span>/jmap-<span class="hljs-variable">$PID</span>.dump 2&gt;&amp;1  \n    <span class="hljs-built_in">echo</span> -e <span class="hljs-string">".\\c"</span>  \n    <span class="hljs-variable">$JAVA_HOME</span>/bin/jmap -heap <span class="hljs-variable">$PID</span> &gt; <span class="hljs-variable">$DUMP_DIR</span>/jmap-heap-<span class="hljs-variable">$PID</span>.dump 2&gt;&amp;1  \n    <span class="hljs-built_in">echo</span> -e <span class="hljs-string">".\\c"</span>  \n    <span class="hljs-variable">$JAVA_HOME</span>/bin/jmap -histo <span class="hljs-variable">$PID</span> &gt; <span class="hljs-variable">$DUMP_DIR</span>/jmap-histo-<span class="hljs-variable">$PID</span>.dump 2&gt;&amp;1  \n    <span class="hljs-built_in">echo</span> -e <span class="hljs-string">".\\c"</span>  \n    <span class="hljs-keyword">if</span> [ -r /usr/sbin/lsof ]; <span class="hljs-keyword">then</span>  \n    /usr/sbin/lsof -p <span class="hljs-variable">$PID</span> &gt; <span class="hljs-variable">$DUMP_DIR</span>/lsof-<span class="hljs-variable">$PID</span>.dump  \n    <span class="hljs-built_in">echo</span> -e <span class="hljs-string">".\\c"</span>  \n    <span class="hljs-keyword">fi</span>  \n<span class="hljs-keyword">done</span>  \n<span class="hljs-keyword">if</span> [ -r /usr/bin/sar ]; <span class="hljs-keyword">then</span>  \n/usr/bin/sar &gt; <span class="hljs-variable">$DUMP_DIR</span>/sar.dump  \n<span class="hljs-built_in">echo</span> -e <span class="hljs-string">".\\c"</span>  \n<span class="hljs-keyword">fi</span>  \n<span class="hljs-keyword">if</span> [ -r /usr/bin/uptime ]; <span class="hljs-keyword">then</span>  \n/usr/bin/uptime &gt; <span class="hljs-variable">$DUMP_DIR</span>/uptime.dump  \n<span class="hljs-built_in">echo</span> -e <span class="hljs-string">".\\c"</span>  \n<span class="hljs-keyword">fi</span>  \n<span class="hljs-keyword">if</span> [ -r /usr/bin/free ]; <span class="hljs-keyword">then</span>  \n/usr/bin/free -t &gt; <span class="hljs-variable">$DUMP_DIR</span>/free.dump  \n<span class="hljs-built_in">echo</span> -e <span class="hljs-string">".\\c"</span>  \n<span class="hljs-keyword">fi</span>  \n<span class="hljs-keyword">if</span> [ -r /usr/bin/vmstat ]; <span class="hljs-keyword">then</span>  \n/usr/bin/vmstat &gt; <span class="hljs-variable">$DUMP_DIR</span>/vmstat.dump  \n<span class="hljs-built_in">echo</span> -e <span class="hljs-string">".\\c"</span>  \n<span class="hljs-keyword">fi</span>  \n<span class="hljs-keyword">if</span> [ -r /usr/bin/mpstat ]; <span class="hljs-keyword">then</span>  \n/usr/bin/mpstat &gt; <span class="hljs-variable">$DUMP_DIR</span>/mpstat.dump  \n<span class="hljs-built_in">echo</span> -e <span class="hljs-string">".\\c"</span>  \n<span class="hljs-keyword">fi</span>  \n<span class="hljs-keyword">if</span> [ -r /usr/bin/iostat ]; <span class="hljs-keyword">then</span>  \n/usr/bin/iostat &gt; <span class="hljs-variable">$DUMP_DIR</span>/iostat.dump  \n<span class="hljs-built_in">echo</span> -e <span class="hljs-string">".\\c"</span>  \n<span class="hljs-keyword">fi</span>  \n<span class="hljs-keyword">if</span> [ -r /bin/netstat ]; <span class="hljs-keyword">then</span>  \n/bin/netstat &gt; <span class="hljs-variable">$DUMP_DIR</span>/netstat.dump  \n<span class="hljs-built_in">echo</span> -e <span class="hljs-string">".\\c"</span>  \n<span class="hljs-keyword">fi</span>  \n<span class="hljs-built_in">echo</span> <span class="hljs-string">"OK!"</span>\n</code></pre>\n'},{filename:"dev/principals/expansibility.md",__html:'<h1>谈谈扩充式扩展与增量式扩展</h1>\n<blockquote>\n<p><a href="http://javatar.iteye.com/blog/690845">http://javatar.iteye.com/blog/690845</a></p>\n</blockquote>\n<p>我们平台的产品越来越多,产品的功能也越来越多。平台的产品为了适应各 BU 和部门以及产品线的需求,势必会将很多不相干的功能凑在一起,客户可以选择性的使用。为了兼容更多的需求,每个产品,每个框架,都在不停的扩展,而我们经常会选择一些扩展的扩展方式,也就是将新旧功能扩展成一个通用实现。我想讨论是,有些情况下也可以考虑增量式的扩展方式,也就是保留原功能的简单性,新功能独立实现。我最近一直做分布式服务框架的开发,就拿我们项目中的问题开涮吧。</p>\n<p>比如:远程调用框架,肯定少不了序列化功能,功能很简单,就是把流转成对象,对象转成流。但因有些地方可能会使用 osgi,这样序列化时,IO 所在的 ClassLoader 可能和业务方的 ClassLoader 是隔离的。需要将流转换成 byte[] 数组,然后传给业务方的 ClassLoader 进行序列化。为了适应 osgi 需求,把原来非 osgi 与 osgi 的场景扩展了一下,这样,不管是不是 osgi 环境,都先将流转成 byte[] 数组,拷贝一次。然而,大部分场景都用不上 osgi,却为 osgi 付出了代价。而如果采用增量式扩展方式,非 osgi 的代码原封不动,再加一个 osgi 的实现,要用 osgi 的时候,直接依赖 osgi 实现即可。</p>\n<p>再比如:最开始,远程服务都是基于接口方法,进行透明化调用的。这样,扩展接口就是, invoke(Method method, Object[] args),后来,有了无接口调用的需求,就是没有接口方法也能调用,并将 POJO 对象都转换成 Map 表示。因为 Method 对象是不能直接 new 出来的,我们不自觉选了一个扩展式扩展,把扩展接口改成了 invoke(String methodName, String[] parameterTypes, String returnTypes, Object[] args),导致不管是不是无接口调用,都得把 parameterTypes 从 Class[] 转成 String[]。如果选用增量式扩展,应该是保持原有接口不变,增加一个 GeneralService 接口,里面有一个通用的 invoke() 方法,和其它正常业务上的接口一样的调用方式,扩展接口也不用变,只是 GeneralServiceImpl 的 invoke() 实现会将收到的调用转给目标接口,这样就能将新功能增量到旧功能上,并保持原来结构的简单性。</p>\n<p>再再比如:无状态消息发送,很简单,序列化一个对象发过去就行。后来有了同步消息发送需求,需要一个 Request/Response 进行配对,采用扩展式扩展,自然想到,无状态消息其实是一个没有 Response 的 Request,所以在 Request 里加一个 boolean 状态,表示要不要返回 Response。如果再来一个会话消息发送需求,那就再加一个 Session 交互,然后发现,原来同步消息发送是会话消息的一种特殊情况,所有场景都传 Session,不需要 Session 的地方无视即可。</p>\n<p><img src="../sources/images/open-expand.jpg" alt="open-expand"></p>\n<p>如果采用增量式扩展,无状态消息发送原封不动,同步消息发送,在无状态消息基础上加一个 Request/Response 处理,会话消息发送,再加一个 SessionRequest/SessionResponse 处理。</p>\n<p><img src="../sources/images/close-expand.jpg" alt="close-expand"></p>\n'},{filename:"dev/principals/extension.md",__html:'<h1>扩展点重构</h1>\n<blockquote>\n<p><a href="http://javatar.iteye.com/blog/1041832">http://javatar.iteye.com/blog/1041832</a></p>\n</blockquote>\n<p>随着服务化的推广,网站对Dubbo服务框架的需求逐渐增多,Dubbo 的现有开发人员能实现的需求有限,很多需求都被 delay,而网站的同学也希望参与进来,加上领域的推动,所以平台计划将部分项目对公司内部开放,让大家一起来实现,Dubbo 为试点项目之一。</p>\n<p>既然要开放,那 Dubbo 就要留一些扩展点,让参与者尽量黑盒扩展,而不是白盒的修改代码,否则分支,质量,合并,冲突都会很难管理。</p>\n<p>先看一下 Dubbo 现有的设计:</p>\n<p><img src="../sources/images/design-step1.png" alt="design-step-1"></p>\n<p>这里面虽然有部分扩展接口,但并不能很好的协作,而且扩展点的加载和配置都没有统一处理,所以下面对它进行重构。</p>\n<h2>第一步,微核心,插件式,平等对待第三方</h2>\n<p>即然要扩展,扩展点的加载方式,首先要统一,微核心+插件式,是比较能达到 OCP 原则的思路。</p>\n<p>由一个插件生命周期管理容器,构成微核心,核心不包括任何功能,这样可以确保所有功能都能被替换,并且,框架作者能做到的功能,扩展者也一定要能做到,以保证平等对待第三方,所以,框架自身的功能也要用插件的方式实现,不能有任何硬编码。</p>\n<p>通常微核心都会采用 Factory、IoC、OSGi 等方式管理插件生命周期。考虑 Dubbo 的适用面,不想强依赖 Spring 等 IoC 容器。自已造一个小的 IoC 容器,也觉得有点过度设计,所以打算采用最简单的 Factory 方式管理插件。</p>\n<p>最终决定采用的是 JDK 标准的 SPI 扩展机制,参见:<code>java.util.ServiceLoader</code>,也就是扩展者在 jar 包的 <code>META-INF/services/</code> 目录下放置与接口同名的文本文件,内容为接口实现类名,多个实现类名用换行符分隔。比如,需要扩展 Dubbo 的协议,只需在 xxx.jar 中放置文件:<code>META-INF/services/com.alibaba.dubbo.rpc.Protocol</code>,内容为 <code>com.alibaba.xxx.XxxProtocol</code>。Dubbo 通过 ServiceLoader 扫描到所有 Protocol 实现。</p>\n<p>并约定所有插件,都必须标注:<code>@Extension(&quot;name&quot;)</code>,作为加载后的标识性名称,用于配置选择。</p>\n<h2>第二步,每个扩展点只封装一个变化因子,最大化复用</h2>\n<p>每个扩展点的实现者,往往都只是关心一件事,现在的扩展点,并没有完全分离。比如:Failover, Route, LoadBalance, Directory 没有完全分开,全由 RoutingInvokerGroup 写死了。</p>\n<p>再比如,协议扩展,扩展者可能只是想替换序列化方式,或者只替换传输方式,并且 Remoting 和 Http 也能复用序列化等实现。这样,需为传输方式,客户端实现,服务器端实现,协议头解析,数据序列化,都留出不同扩展点。</p>\n<p>拆分后,设计如下:</p>\n<p><img src="../sources/images/design-step2.png" alt="design-step-2"></p>\n<h2>第三步,全管道式设计,框架自身逻辑,均使用截面拦截实现</h2>\n<p>现在很多的逻辑,都是放在基类中实现,然后通过模板方法回调子类的实现,包括:local, mock, generic, echo, token, accesslog, monitor, count, limit 等等,可以全部拆分使用 Filter 实现,每个功能都是调用链上的一环。 比如:(基类模板方法)</p>\n<pre><code class="language-java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">abstract</span> AbstractInvoker implements Invoker {  \n  \n    <span class="hljs-function"><span class="hljs-keyword">public</span> Result <span class="hljs-title">invoke</span><span class="hljs-params">(Invocation inv)</span> <span class="hljs-keyword">throws</span> RpcException </span>{  \n        <span class="hljs-comment">// 伪代码  </span>\n        active ++;  \n        <span class="hljs-keyword">if</span> (active &gt; max)  \n            wait();  \n          \n        doInvoke(inv);  \n          \n        active --;  \n        notify();  \n    }  \n      \n    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">abstract</span> Result <span class="hljs-title">doInvoke</span><span class="hljs-params">(Invocation inv)</span> <span class="hljs-keyword">throws</span> RpcException  \n  \n}  \n</span></code></pre>\n<p>改成:(链式过滤器)</p>\n<pre><code class="language-java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">abstract</span> LimitFilter implements Filter {  \n  \n    <span class="hljs-function"><span class="hljs-keyword">public</span> Result <span class="hljs-title">invoke</span><span class="hljs-params">(Invoker chain, Invocation inv)</span> <span class="hljs-keyword">throws</span> RpcException </span>{  \n         <span class="hljs-comment">// 伪代码  </span>\n        active ++;  \n        <span class="hljs-keyword">if</span> (active &gt; max)  \n            wait();  \n          \n        chain.invoke(inv);  \n          \n        active --;  \n        notify();  \n    }  \n  \n}\n</code></pre>\n<h2>第四步,最少概念,一致性概念模型</h2>\n<p>保持尽可能少的概念,有助于理解,对于开放的系统尤其重要。另外,各接口都使用一致的概念模型,能相互指引,并减少模型转换,</p>\n<p>比如,Invoker 的方法签名为:</p>\n<pre><code class="language-java"><span class="hljs-function">Result <span class="hljs-title">invoke</span><span class="hljs-params">(Invocation invocation)</span> <span class="hljs-keyword">throws</span> RpcException</span>;\n</code></pre>\n<p>而 Exporter 的方法签名为:</p>\n<pre><code class="language-java"><span class="hljs-function">Object <span class="hljs-title">invoke</span><span class="hljs-params">(Method method, Object[] args)</span> <span class="hljs-keyword">throws</span> Throwable</span>;  \n</code></pre>\n<p>但它们的作用是一样的,只是一个在客户端,一个在服务器端,却采用了不一样的模型类。</p>\n<p>再比如,URL 以字符串传递,不停的解析和拼装,没有一个 URL 模型类, 而 URL 的参数,却时而 Map, 时而 Parameters 类包装,</p>\n<pre><code class="language-java">export(String url)  \ncreateExporter(String host, <span class="hljs-keyword">int</span> port, Parameters params);  \n</code></pre>\n<p>使用一致模型:</p>\n<pre><code class="language-java">export(URL url)  \ncreateExporter(URL url);  \n</code></pre>\n<p>再比如,现有的:Invoker, Exporter, InvocationHandler, FilterChain\n其实都是 invoke 行为的不同阶段,完全可以抽象掉,统一为 Invoker,减少概念。</p>\n<h2>第五步,分层,组合式扩展,而不是泛化式扩展</h2>\n<p>原因参见:<a href="../principals/expansibility.md">谈谈扩充式扩展与增量式扩展</a>。</p>\n<p>泛化式扩展指:将扩展点逐渐抽象,取所有功能并集,新加功能总是套入并扩充旧功能的概念。</p>\n<p>组合式扩展指:将扩展点正交分解,取所有功能交集,新加功能总是基于旧功能之上实现。</p>\n<p>上面的设计,不自觉的就将 Dubbo 现有功能都当成了核心功能。上面的概念包含了 Dubbo 现有 RPC 的所有功能,包括:Proxy, Router, Failover, LoadBalance, Subscriber, Publisher, Invoker, Exporter, Filter 等,\n但这些都是核心吗?踢掉哪些,RPC 一样可以 Run?而哪些又是不能踢掉的?基于这样考虑,可以将 RPC 分解成两个层次,只是 Protocol 和 Invoker 才是 RPC 的核心。其它,包括 Router, Failover, Loadbalance, Subscriber, Publisher 都不核心,而是 Routing。所以,将 Routing 作为 Rpc 核心的一个扩展,设计如下:</p>\n<p><img src="../sources/images/design-step3.png" alt="design-step-3"></p>\n<h2>第六步,整理,梳理关系</h2>\n<p>整理后,设计如下:</p>\n<p><img src="../sources/images/design-step4.png" alt="design-step-4"></p>\n'},{filename:"dev/principals/general-knowledge.md",__html:'<h1>一些设计上的基本常识</h1>\n<blockquote>\n<p><a href="http://javatar.iteye.com/blog/706098">http://javatar.iteye.com/blog/706098</a></p>\n</blockquote>\n<p>最近给团队新人讲了一些设计上的常识,可能会对其它的新人也有些帮助,把暂时想到的几条,先记在这里。</p>\n<h2>API 与 SPI 分离</h2>\n<p>框架或组件通常有两类客户,一个是使用者,一个是扩展者。API (Application Programming Interface) 是给使用者用的,而 SPI (Service Provide Interface) 是给扩展者用的。在设计时,尽量把它们隔离开,而不要混在一起。也就是说,使用者是看不到扩展者写的实现的。</p>\n<p>比如:一个 Web 框架,它有一个 API 接口叫 Action,里面有个 execute() 方法,是给使用者用来写业务逻辑的。然后,Web 框架有一个 SPI 接口给扩展者控制输出方式,比如用 velocity 模板输出还是用 json 输出等。如果这个 Web 框架使用一个都继承 Action 的 VelocityAction 和一个 JsonAction 做为扩展方式,要用 velocity 模板输出的就继承 VelocityAction,要用 json 输出的就继承 JsonAction,这就是 API 和 SPI 没有分离的反面例子,SPI 接口混在了 API 接口中。</p>\n<p><img src="../sources/images/mix-api-spi.jpg" alt="mix-api-spi"></p>\n<p>合理的方式是,有一个单独的 Renderer 接口,有 VelocityRenderer 和 JsonRenderer 实现,Web 框架将 Action 的输出转交给 Renderer 接口做渲染输出。</p>\n<p><img src="../sources/images/seperate-api-spi.jpg" alt="seperate-api-spi"></p>\n<h2>服务域/实体域/会话域分离</h2>\n<p>任何框架或组件,总会有核心领域模型,比如:Spring 的 Bean,Struts 的 Action,Dubbo 的 Service,Napoli 的 Queue 等等。这个核心领域模型及其组成部分称为实体域,它代表着我们要操作的目标本身。实体域通常是线程安全的,不管是通过不变类,同步状态,或复制的方式。</p>\n<p>服务域也就是行为域,它是组件的功能集,同时也负责实体域和会话域的生命周期管理,\n比如 Spring 的 ApplicationContext,Dubbo 的 ServiceManager 等。服务域的对象通常会比较重,而且是线程安全的,并以单一实例服务于所有调用。</p>\n<p>什么是会话?就是一次交互过程。会话中重要的概念是上下文,什么是上下文?比如我们说:“老地方见”,这里的“老地方”就是上下文信息。为什么说“老地方”对方会知道,因为我们前面定义了“老地方”的具体内容。所以说,上下文通常持有交互过程中的状态变量等。会话对象通常较轻,每次请求都重新创建实例,请求结束后销毁。简而言之:把元信息交由实体域持有,把一次请求中的临时状态由会话域持有,由服务域贯穿整个过程。</p>\n<p><img src="../sources/images/ddd.jpg" alt="ddd"></p>\n<h2>在重要的过程上设置拦截接口</h2>\n<p>如果你要写个远程调用框架,那远程调用的过程应该有一个统一的拦截接口。如果你要写一个 ORM 框架,那至少 SQL 的执行过程,Mapping 过程要有拦截接口;如果你要写一个 Web 框架,那请求的执行过程应该要有拦截接口,等等。没有哪个公用的框架可以 Cover 住所有需求,允许外置行为,是框架的基本扩展方式。这样,如果有人想在远程调用前,验证下令牌,验证下黑白名单,统计下日志;如果有人想在 SQL 执行前加下分页包装,做下数据权限控制,统计下 SQL 执行时间;如果有人想在请求执行前检查下角色,包装下输入输出流,统计下请求量,等等,就可以自行完成,而不用侵入框架内部。拦截接口,通常是把过程本身用一个对象封装起来,传给拦截器链,比如:远程调用主过程为 invoke(),那拦截器接口通常为 invoke(Invocation),Invocation 对象封装了本来要执行过程的上下文,并且 Invocation 里有一个 invoke() 方法,由拦截器决定什么时候执行,同时,Invocation 也代表拦截器行为本身,这样上一拦截器的 Invocation 其实是包装的下一拦截器的过程,直到最后一个拦截器的 Invocation 是包装的最终的 invoke() 过程;同理,SQL 主过程为 execute(),那拦截器接口通常为 execute(Execution),原理一样。当然,实现方式可以任意,上面只是举例。</p>\n<p><img src="../sources/images/filter-chain.jpg" alt="filter-chain"></p>\n<h2>重要的状态的变更发送事件并留出监听接口</h2>\n<p>这里先要讲一个事件和上面拦截器的区别,拦截器是干预过程的,它是过程的一部分,是基于过程行为的,而事件是基于状态数据的,任何行为改变的相同状态,对事件应该是一致的。事件通常是事后通知,是一个 Callback 接口,方法名通常是过去式的,比如 onChanged()。比如远程调用框架,当网络断开或连上应该发出一个事件,当出现错误也可以考虑发出一个事件,这样外围应用就有可能观察到框架内部的变化,做相应适应。</p>\n<p><img src="../sources/images/event-listener.jpg" alt="event-listener"></p>\n<h2>扩展接口职责尽可能单一,具有可组合性</h2>\n<p>比如,远程调用框架它的协议是可以替换的。如果只提供一个总的扩展接口,当然可以做到切换协议,但协议支持是可以细分为底层通讯,序列化,动态代理方式等等。如果将接口拆细,正交分解,会更便于扩展者复用已有逻辑,而只是替换某部分实现策略。当然这个分解的粒度需要把握好。</p>\n<h2>微核插件式,平等对待第三方</h2>\n<p>大凡发展的比较好的框架,都遵守微核的理念。Eclipse 的微核是 OSGi, Spring 的微核是 BeanFactory,Maven 的微核是 Plexus。通常核心是不应该带有功能性的,而是一个生命周期和集成容器,这样各功能可以通过相同的方式交互及扩展,并且任何功能都可以被替换。如果做不到微核,至少要平等对待第三方,即原作者能实现的功能,扩展者应该可以通过扩展的方式全部做到。原作者要把自己也当作扩展者,这样才能保证框架的可持续性及由内向外的稳定性。</p>\n<h2>不要控制外部对象的生命周期</h2>\n<p>比如上面说的 Action 使用接口和 Renderer 扩展接口。框架如果让使用者或扩展者把 Action 或 Renderer 实现类的类名或类元信息报上来,然后在内部通过反射 newInstance() 创建一个实例,这样框架就控制了 Action 或 Renderer 实现类的生命周期,Action 或 Renderer 的生老病死,框架都自己做了,外部扩展或集成都无能为力。好的办法是让使用者或扩展者把 Action 或 Renderer 实现类的实例报上来,框架只是使用这些实例,这些对象是怎么创建的,怎么销毁的,都和框架无关,框架最多提供工具类辅助管理,而不是绝对控制。</p>\n<h2>可配置一定可编程,并保持友好的 CoC 约定</h2>\n<p>因为使用环境的不确定因素很多,框架总会有一些配置,一般都会到 classpath 直扫某个指定名称的配置,或者启动时允许指定配置路径。做为一个通用框架,应该做到凡是能配置文件做的一定要能通过编程方式进行,否则当使用者需要将你的框架与另一个框架集成时就会带来很多不必要的麻烦。</p>\n<p>另外,尽可能做一个标准约定,如果用户按某种约定做事时,就不需要该配置项。比如:配置模板位置,你可以约定,如果放在 templates 目录下就不用配了,如果你想换个目录,就配置下。</p>\n<h2>区分命令与查询,明确前置条件与后置条件</h2>\n<p>这个是契约式设计的一部分,尽量遵守有返回值的方法是查询方法,void 返回的方法是命令。查询方法通常是幂等性的,无副作用的,也就是不改变任何状态,调 n 次结果都是一样的,比如 get 某个属性值,或查询一条数据库记录。命令是指有副作用的,也就是会修改状态,比如 set 某个值,或 update 某条数据库记录。如果你的方法即做了修改状态的操作,又做了查询返回,如果可能,将其拆成写读分离的两个方法,比如:User deleteUser(id),删除用户并返回被删除的用户,考虑改为 getUser() 和 void 的 deleteUser()。 另外,每个方法都尽量前置断言传入参数的合法性,后置断言返回结果的合法性,并文档化。</p>\n<h2>增量式扩展,而不要扩充原始核心概念</h2>\n<p>参见:<a href="./principals/expansibility.md">谈谈扩充式扩展与增量式扩展</a></p>\n'},{filename:"dev/principals/introduction.md",__html:"<h1>设计原则</h1>\n<p>本章节的设计原则摘录自梁飞在 javaeye 上发表的系列文章。</p>\n"},{filename:"dev/principals/robustness.md",__html:'<h1>设计实现的健壮性</h1>\n<blockquote>\n<p><a href="http://oldratlee.com/380/tech/java/robustness-of-implement.html">http://oldratlee.com/380/tech/java/robustness-of-implement.html</a></p>\n</blockquote>\n<p>Dubbo 作为远程服务暴露、调用和治理的解决方案,是应用运转的经络,其本身实现健壮性的重要程度是不言而喻的。</p>\n<p>这里列出一些 Dubbo 用到的原则和方法。</p>\n<h2>日志</h2>\n<p>日志是发现问题、查看问题一个最常用的手段。日志质量往往被忽视,没有日志使用上的明确约定。重视 Log 的使用,提高 Log 的信息浓度。日志过多、过于混乱,会导致有用的信息被淹没。</p>\n<p>要有效利用这个工具要注意:</p>\n<h3>严格约定WARN、ERROR级别记录的内容</h3>\n<ul>\n<li>WARN 表示可以恢复的问题,无需人工介入。</li>\n<li>ERROR 表示需要人工介入问题。</li>\n</ul>\n<p>有了这样的约定,监管系统发现日志文件的中出现 ERROR 字串就报警,又尽量减少了发生。过多的报警会让人疲倦,使人对报警失去警惕性,使 ERROR 日志失去意义。再辅以人工定期查看 WARN 级别信息,以评估系统的“亚健康”程度。</p>\n<h3>日志中,尽量多的收集关键信息</h3>\n<p>哪些是关键信息呢?</p>\n<ul>\n<li>出问题时的现场信息,即排查问题要用到的信息。如服务调用失败时,要给出使用 Dubbo 的版本、服务提供者的 IP、使用的是哪个注册中心;调用的是哪个服务、哪个方法等等。这些信息如果不给出,那么事后人工收集的,问题过后现场可能已经不能复原,加大排查问题的难度。</li>\n<li>如果可能,给出问题的原因和解决方法。这让维护和问题解决变得简单,而不是寻求精通者(往往是实现者)的帮助。</li>\n</ul>\n<h3>同一个或是一类问题不要重复记录多次</h3>\n<p>同一个或是一类异常日志连续出现几十遍的情况,还是常常能看到的。人眼很容易漏掉淹没在其中不一样的重要日志信息。要尽量避免这种情况。在可以预见会出现的情况,有必要加一些逻辑来避免。</p>\n<p>如为一个问题准备一个标志,出问题后打日志后设置标志,避免重复打日志。问题恢复后清除标志。</p>\n<p>虽然有点麻烦,但是这样做保证日志信息浓度,让监控更有效。</p>\n<h2>界限设置</h2>\n<p>资源是有限的,CPU、内存、IO 等等。不要因为外部的请求、数据不受限的而崩溃。</p>\n<h3>线程池(ExectorService)的大小和饱和策略</h3>\n<p>Server 端用于处理请求的 ExectorService 设置上限。ExecutorService 的任务等待队列使用有限队列,避免资源耗尽。当任务等待队列饱和时,选择一个合适的饱和策略。这样保证平滑劣化。</p>\n<p>在 Dubbo 中,饱和策略是丢弃数据,等待结果也只是请求的超时。</p>\n<p>达到饱和时,说明已经达到服务提供方的负荷上限,要在饱和策略的操作中日志记录这个问题,以发出监控警报。记得注意不要重复多次记录哦。(注意,缺省的饱和策略不会有这些附加的操作。)根据警报的频率,已经决定扩容调整等等,避免系统问题被忽略。</p>\n<h3>集合容量</h3>\n<p>如果确保进入集合的元素是可控的且是足够少,则可以放心使用。这是大部分的情况。如果不能保证,则使用有有界的集合。当到达界限时,选择一个合适的丢弃策略。</p>\n<h2>容错-重试-恢复</h2>\n<p>高可用组件要容忍其依赖组件的失败。</p>\n<h3>Dubbo 的服务注册中心</h3>\n<p>目前服务注册中心使用了数据库来保存服务提供者和消费者的信息。注册中心集群不同注册中心也通过数据库来之间同步数据,以感知其它注册中心上提供者。注册中心会内存中保证一份提供者和消费者数据,数据库不可用时,注册中心独立对外正常运转,只是拿不到其它注册中心的数据。当数据库恢复时,重试逻辑会内存中修改的数据写回数据库,并拿到数据库中新数据。</p>\n<h3>服务的消费者</h3>\n<p>服务消息者从注册中心拿到提供者列表后,会保存提供者列表到内存和磁盘文件中。这样注册中心宕后消费者可以正常运转,甚至可以在注册中心宕机过程中重启消费者。消费者启动时,发现注册中心不可用,会读取保存在磁盘文件中提供者列表。重试逻辑保证注册中心恢复后,更新信息。</p>\n<h2>重试延迟策略</h2>\n<p>上一点的子问题。Dubbo 中碰到有两个相关的场景。</p>\n<h3>数据库上的活锁</h3>\n<p>注册中心会定时更新数据库一条记录的时间戳,这样集群中其它的注册中心感知它是存活。过期注册中心和它的相关数据 会被清除。数据库正常时,这个机制运行良好。但是数据库负荷高时,其上的每个操作都会很慢。这就出现:</p>\n<p>A 注册中心认为 B 过期,删除 B 的数据。 B 发现自己的数据没有了,重新写入自己的数据的反复操作。这些反复的操作又加重了数据库的负荷,恶化问题。</p>\n<p>可以使用下面逻辑:</p>\n<p>当 B 发现自己数据被删除时(写入失败),选择等待这段时间再重试。重试时间可以选择指数级增长,如第一次等 1 分钟,第二次 10 分钟、第三次 100 分钟。</p>\n<p>这样操作减少后,保证数据库可以冷却(Cool Down)下来。</p>\n<h3>Client 重连注册中心</h3>\n<p>当一个注册中心停机时,其它的 Client 会同时接收事件,而去重连另一个注册中心。Client 数量相对比较多,会对注册中心造成冲击。避免方法可以是 Client 重连时随机延时 3 分钟,把重连分散开。</p>\n'},{filename:"dev/release.md",__html:"<h1>版本管理</h1>\n<p><strong>新功能的开发</strong> 和 <strong>稳定性的提高</strong> 对产品都很重要。但是添加新功能会影响稳定性,Dubbo 使用如下的版本开发模式来保障两者。</p>\n<h2>2 个版本并行开发</h2>\n<ul>\n<li>BugFix 版本:低版本,比如 <code>2.4.x</code>。是 GA 版本,线上使用的版本,只会 BugFix,升级第三位版本号。</li>\n<li>新功能版本:高版本,比如 <code>2.5.x</code>。加新功能的版本,会给对新功能有需求的应用试用。</li>\n</ul>\n<p><code>2.5.x</code> 的新功能基本稳定后,进入 <code>2.5.x</code> 试用阶段。找足够多的应用试用 <code>2.5.x</code> 版本。</p>\n<p>在 <code>2.5.x</code> 够稳定后:</p>\n<ul>\n<li><code>2.5.x</code> 成为 GA 版本,只 BugFix,推广使用此版本。如何可行,可以推进应用在期望的时间点内升级到 GA 版本。</li>\n<li><code>2.4.x</code> 不再开发,应用碰到 Bug 让直接升级。(这个称为“夕阳条款”)</li>\n<li>从 <code>2.5.x</code> 拉成分支 <code>2.6.0</code>,作为新功能开发版本。</li>\n</ul>\n<h2>优势</h2>\n<ul>\n<li>保持 GA 版本是稳定的!因为:\n<ul>\n<li>只会作 BugFix</li>\n<li>成为 GA 版本前有试用阶段</li>\n</ul>\n</li>\n<li>新功能可以高版本中快速响应,并让应用能试用新功能。</li>\n<li>不会版本过多,导致开发和维护成本剧增</li>\n</ul>\n<h2>用户要配合的职责</h2>\n<p>由于开发只会 BugFix GA 版本,所以用户需要积极跟进升级到 GA 版本,以 Fix 发现的问题。</p>\n<p>定期升级版本用户带来了不安。这是一个假命题,说明如下:</p>\n<ul>\n<li>GA 经过一个试用阶段保持稳定。</li>\n<li>GA 版本有 Bug 会火速 Fix</li>\n<li>相对出问题才升级到 GA 版本(可以跨了多个版本)定期升级平摊风险(类似小步快跑)。经历过周期长的大项目的同学会有这样的经历,三方库版本长时间不升级,结果出了问题不得不升级到新版本(跨了多个版本)风险巨大。</li>\n</ul>\n"},{filename:"user/README.md",__html:"<p>这篇文档详细讲解了<code>dubbo</code>的使用,基本涵盖<code>dubbo</code>的所有功能特性。</p>\n<p>如果你正依赖<code>dubbo</code>作为你业务工程的RPC通信框架,这里可以作为你的参考手册</p>\n"},{filename:"user/SUMMARY.md",__html:'<h1>Summary</h1>\n<ul>\n<li><a href="./preface/index.md">1 入门</a>\n<ul>\n<li><a href="./preface/background.md">1.1 背景</a></li>\n<li><a href="./preface/requirements.md">1.2 需求</a></li>\n<li><a href="./preface/architecture.md">1.3 架构</a></li>\n<li><a href="./preface/usage.md">1.4 用法</a></li>\n</ul>\n</li>\n<li><a href="./quick-start.md">2 快速启动</a></li>\n<li><a href="./dependencies.md">3 依赖</a></li>\n<li><a href="./maturity.md">4 成熟度</a></li>\n<li><a href="./configuration/index.md">5 配置</a>\n<ul>\n<li><a href="./configuration/xml.md">5.1 XML 配置</a></li>\n<li><a href="./configuration/properties.md">5.2 属性配置</a></li>\n<li><a href="./configuration/api.md">5.3 API 配置</a></li>\n<li><a href="./configuration/annotation.md">5.4 注解配置</a></li>\n</ul>\n</li>\n<li><a href="./demos/index.md">6 示例</a>\n<ul>\n<li><a href="./demos/preflight-check.md">6.1 启动时检查</a></li>\n<li><a href="./demos/fault-tolerent-strategy.md">6.2 集群容错</a></li>\n<li><a href="./demos/loadbalance.md">6.3 负载均衡</a></li>\n<li><a href="./demos/thread-model.md">6.4 线程模型</a></li>\n<li><a href="./demos/explicit-target.md">6.5 直连提供者</a></li>\n<li><a href="./demos/subscribe-only.md">6.6 只订阅</a></li>\n<li><a href="./demos/registry-only.md">6.7 只注册</a></li>\n<li><a href="./demos/static-service.md">6.8 静态服务</a></li>\n<li><a href="./demos/multi-protocols.md">6.9 多协议</a></li>\n<li><a href="./demos/multi-registry.md">6.10 多注册中心</a></li>\n<li><a href="./demos/service-group.md">6.11 服务分组</a></li>\n<li><a href="./demos/multi-versions.md">6.12 多版本</a></li>\n<li><a href="./demos/group-merger.md">6.13 分组聚合</a></li>\n<li><a href="./demos/parameter-validation.md">6.14 参数验证</a></li>\n<li><a href="./demos/result-cache.md">6.15 结果缓存</a></li>\n<li><a href="./demos/generic-reference.md">6.16 泛化引用</a></li>\n<li><a href="./demos/generic-service.md">6.17 泛化实现</a></li>\n<li><a href="./demos/echo-service.md">6.18 回声测试</a></li>\n<li><a href="./demos/context.md">6.19 上下文信息</a></li>\n<li><a href="./demos/attachment.md">6.20 隐式参数</a></li>\n<li><a href="./demos/async-call.md">6.21 异步调用</a></li>\n<li><a href="./demos/local-call.md">6.22 本地调用</a></li>\n<li><a href="./demos/callback-parameter.md">6.23 参数回调</a></li>\n<li><a href="./demos/events-notify.md">6.24 事件通知</a></li>\n<li><a href="./demos/local-stub.md">6.25 本地存根</a></li>\n<li><a href="./demos/local-mock.md">6.26 本地伪装</a></li>\n<li><a href="./demos/delay-publish.md">6.27 延迟暴露</a></li>\n<li><a href="./demos/concurrency-control.md">6.28 并发控制</a></li>\n<li><a href="./demos/config-connections.md">6.29 连接控制</a></li>\n<li><a href="./demos/lazy-connect.md">6.30 延迟连接</a></li>\n<li><a href="./demos/stickiness.md">6.31 粘滞连接</a></li>\n<li><a href="./demos/token-authorization.md">6.32 令牌验证</a></li>\n<li><a href="./demos/routing-rule.md">6.33 路由规则</a></li>\n<li><a href="./demos/config-rule.md">6.34 配置规则</a></li>\n<li><a href="./demos/service-downgrade.md">6.35 服务降级</a></li>\n<li><a href="./demos/graceful-shutdown.md">6.36 优雅停机</a></li>\n<li><a href="./demos/hostname-binding.md">6.37 主机绑定</a></li>\n<li><a href="./demos/logger-strategy.md">6.38 日志适配</a></li>\n<li><a href="./demos/accesslog.md">6.39 访问日志</a></li>\n<li><a href="./demos/service-container.md">6.40 服务容器</a></li>\n<li><a href="./demos/reference-config-cache.md">6.41 Reference Config 缓存</a></li>\n<li><a href="./demos/distributed-transaction.md">6.42 分布式事务</a></li>\n<li><a href="./demos/dump.md">6.43 线程栈自动dump</a></li>\n<li><a href="./demos/netty4.md">6.44 Netty4</a></li>\n<li><a href="./demos/serialization.md">6.45 Kryo和FST序列化</a></li>\n</ul>\n</li>\n<li><a href="./references/api.md">7 API 参考手册</a></li>\n<li><a href="./references/xml/introduction.md">8 schema 配置参考手册</a>\n<ul>\n<li><a href="./references/xml/dubbo-service.md">8.1 dubbo:service</a></li>\n<li><a href="./references/xml/dubbo-reference.md">8.2 dubbo:reference</a></li>\n<li><a href="./references/xml/dubbo-protocol.md">8.3 dubbo:protocol</a></li>\n<li><a href="./references/xml/dubbo-registry.md">8.4 dubbo:registry</a></li>\n<li><a href="./references/xml/dubbo-monitor.md">8.5 dubbo:monitor</a></li>\n<li><a href="./references/xml/dubbo-application.md">8.6 dubbo:application</a></li>\n<li><a href="./references/xml/dubbo-module.md">8.7 dubbo:module</a></li>\n<li><a href="./references/xml/dubbo-provider.md">8.8 dubbo:provider</a></li>\n<li><a href="./references/xml/dubbo-consumer.md">8.9 dubbo:consumer</a></li>\n<li><a href="./references/xml/dubbo-method.md">8.10 dubbo:method</a></li>\n<li><a href="./references/xml/dubbo-argument.md">8.11 dubbo:argument</a></li>\n<li><a href="./references/xml/dubbo-parameter.md">8.12 dubbo:parameter</a></li>\n</ul>\n</li>\n<li><a href="./references/protocol/introduction.md">9 协议参考手册</a>\n<ul>\n<li><a href="./references/protocol/dubbo.md">9.1 dubbo://</a></li>\n<li><a href="./references/protocol/rmi.md">9.2 rmi//</a></li>\n<li><a href="./references/protocol/hessian.md">9.3 hessian://</a></li>\n<li><a href="./references/protocol/http.md">9.4 http://</a></li>\n<li><a href="./references/protocol/webservice.md">9.5 webservice://</a></li>\n<li><a href="./references/protocol/thrift.md">9.6 thrift://</a></li>\n<li><a href="./references/protocol/memcached.md">9.7 memcached://</a></li>\n<li><a href="./references/protocol/redis.md">9.8 redis://</a></li>\n<li><a href="./references/protocol/rest.md">9.9 rest://</a></li>\n</ul>\n</li>\n<li><a href="./references/registry/introduction.md">10 注册中心参考手册</a>\n<ul>\n<li><a href="./references/registry/multicast.md">10.1 Multicast 注册中心</a></li>\n<li><a href="./references/registry/zookeeper.md">10.2 Zookeeper 注册中心</a></li>\n<li><a href="./references/registry/redis.md">10.3 Redis 注册中心</a></li>\n<li><a href="./references/registry/simple.md">10.4 Simple 注册中心</a></li>\n</ul>\n</li>\n<li><a href="./references/telnet.md">11 telnet 命令参考手册</a></li>\n<li><a href="./references/qos.md">12 在线运维命令-QOS</a></li>\n<li><a href="./references/maven.md">13 maven 插件参考手册</a></li>\n<li><a href="./best-practice.md">14 服务化最佳实践</a></li>\n<li><a href="./recommend.md">15 推荐用法</a></li>\n<li><a href="./capacity-plan.md">16 容量规划</a></li>\n<li><a href="./perf-test.md">17 性能测试报告</a></li>\n<li><a href="./coveragence.md">18 测试覆盖率报告</a></li>\n</ul>\n'},{filename:"user/benchmark-tool.md",__html:'<h1>基准测试工具包</h1>\n<ul>\n<li>下载源码: git clone <a href="https://github.com/apache/incubator-dubbo.git">https://github.com/apache/incubator-dubbo.git</a></li>\n<li>编译benchmark: cd incubator-dubbo/dubbo-test/dubbo-test-benchmark; mvn clean install</li>\n<li>解压 benchmark压缩包: incubator-dubbo/dubbo-test/dubbo-test-benchmark/target/dubbo-test-benchmark-2.6.2-SNAPSHOT.tar.gz</li>\n</ul>\n<p>阅读ReadMe.txt(内容如下,请以压缩包内的为准)</p>\n<ul>\n<li>\n<p>新建一个benchmark工程,如demo.benchmark</p>\n</li>\n<li>\n<p>导入自己服务的接口api包和dubbo.benchmark.jar(解压dubbo.benchmark.tar.gz,在lib目录下)</p>\n</li>\n<li>\n<p>新建一个类,实现AbstractClientRunnable</p>\n<ul>\n<li>实现父类的构造函数</li>\n<li>实现invoke方法,通过serviceFactory创建本地接口代理,并实现自己的业务逻辑,如下</li>\n</ul>\n<pre><code class="language-java">    <span class="hljs-function"><span class="hljs-keyword">public</span> Object <span class="hljs-title">invoke</span><span class="hljs-params">(ServiceFactory serviceFactory)</span> </span>{\n        DemoService demoService = (DemoService) serviceFactory.get(DemoService.class);\n        <span class="hljs-keyword">return</span> demoService.sendRequest(<span class="hljs-string">"hello"</span>);\n    }\n</code></pre>\n</li>\n<li>\n<p>将自己的benchmark工程打成jar包,如demo.benchmark.jar</p>\n</li>\n<li>\n<p>将demo.benchmark.jar 和服务的api包放到dubbo.benchmark/lib目录下</p>\n</li>\n<li>\n<p>配置duubo.properties</p>\n</li>\n<li>\n<p>运行run.bat(windows)<a href="http://xn--run-du5f.sh">或run.sh</a>(linux)</p>\n</li>\n</ul>\n<p>如想测试dubbo的不同版本,直接替换lib下的dubbo的jar包即可。</p>\n'},{filename:"user/best-practice.md",__html:'<h1>服务化最佳实践</h1>\n<h2>分包</h2>\n<p>建议将服务接口,服务模型,服务异常等均放在 API 包中,因为服务模型及异常也是 API 的一部分,同时,这样做也符合分包原则:重用发布等价原则(REP),共同重用原则(CRP)。</p>\n<p>如果需要,也可以考虑在 API 包中放置一份 spring 的引用配置,这样使用方,只需在 spring 加载过程中引用此配置即可,配置建议放在模块的包目录下,以免冲突,如:<code>com/alibaba/china/xxx/dubbo-reference.xml</code>。</p>\n<h2>粒度</h2>\n<p>服务接口尽可能大粒度,每个服务方法应代表一个功能,而不是某功能的一个步骤,否则将面临分布式事务问题,Dubbo 暂未提供分布式事务支持。</p>\n<p>服务接口建议以业务场景为单位划分,并对相近业务做抽象,防止接口数量爆炸。</p>\n<p>不建议使用过于抽象的通用接口,如:<code>Map query(Map)</code>,这样的接口没有明确语义,会给后期维护带来不便。</p>\n<h2>版本</h2>\n<p>每个接口都应定义版本号,为后续不兼容升级提供可能,如: <code>&lt;dubbo:service interface=&quot;com.xxx.XxxService&quot; version=&quot;1.0&quot; /&gt;</code>。</p>\n<p>建议使用两位版本号,因为第三位版本号通常表示兼容升级,只有不兼容时才需要变更服务版本。</p>\n<p>当不兼容时,先升级一半提供者为新版本,再将消费者全部升为新版本,然后将剩下的一半提供者升为新版本。</p>\n<h2>兼容性</h2>\n<p>服务接口增加方法,或服务模型增加字段,可向后兼容,删除方法或删除字段,将不兼容,枚举类型新增字段也不兼容,需通过变更版本号升级。</p>\n<p>各协议的兼容性不同,参见: <a href="./references/protocol/introduction.md">服务协议</a></p>\n<h2>枚举值</h2>\n<p>如果是完备集,可以用 <code>Enum</code>,比如:<code>ENABLE</code>, <code>DISABLE</code>。</p>\n<p>如果是业务种类,以后明显会有类型增加,不建议用 <code>Enum</code>,可以用 <code>String</code> 代替。</p>\n<p>如果是在返回值中用了 <code>Enum</code>,并新增了 <code>Enum</code> 值,建议先升级服务消费方,这样服务提供方不会返回新值。</p>\n<p>如果是在传入参数中用了 <code>Enum</code>,并新增了 <code>Enum</code> 值,建议先升级服务提供方,这样服务消费方不会传入新值。</p>\n<h2>序列化</h2>\n<p>服务参数及返回值建议使用 POJO 对象,即通过 <code>setter</code>, <code>getter</code> 方法表示属性的对象。</p>\n<p>服务参数及返回值不建议使用接口,因为数据模型抽象的意义不大,并且序列化需要接口实现类的元信息,并不能起到隐藏实现的意图。</p>\n<p>服务参数及返回值都必需是 byValue 的,而不能是 byReference 的,消费方和提供方的参数或返回值引用并不是同一个,只是值相同,Dubbo 不支持引用远程对象。</p>\n<h2>异常</h2>\n<p>建议使用异常汇报错误,而不是返回错误码,异常信息能携带更多信息,以及语义更友好。</p>\n<p>如果担心性能问题,在必要时,可以通过 override 掉异常类的 <code>fillInStackTrace()</code> 方法为空方法,使其不拷贝栈信息。</p>\n<p>查询方法不建议抛出 checked 异常,否则调用方在查询时将过多的 <code>try...catch</code>,并且不能进行有效处理。</p>\n<p>服务提供方不应将 DAO 或 SQL 等异常抛给消费方,应在服务实现中对消费方不关心的异常进行包装,否则可能出现消费方无法反序列化相应异常。</p>\n<h2>调用</h2>\n<p>不要只是因为是 Dubbo 调用,而把调用 <code>try...catch</code> 起来。<code>try...catch</code> 应该加上合适的回滚边界上。</p>\n<p>对于输入参数的校验逻辑在 Provider 端要有。如有性能上的考虑,服务实现者可以考虑在 API 包上加上服务 Stub 类来完成检验。</p>\n'},{filename:"user/capacity-plan.md",__html:"<h1>容量规划</h1>\n<p>以下数据供参考:</p>\n<h2>使用 Dubbo 的会员服务项目</h2>\n<ul>\n<li>每天接收 4 亿次远程调用</li>\n<li>使用 12 台网站标配机器提供服务(8 核 CPU, 8G 内存)</li>\n<li>平均负载在 1 以下(对于 8 核 CPU 负载很低)</li>\n<li>平均响应时间 2.3 到 2.5 毫秒,网络开销约占 1.5 到 1.6 毫秒(和数据包大小有关)</li>\n</ul>\n<h2>使用 Dubbo 的产品授权服务项目</h2>\n<ul>\n<li>每天接收 3 亿次远程调用</li>\n<li>使用 8 台网站标配机器提供服务(8 核CPU,8G 内存)</li>\n<li>平均负载在 1 以下(对于 8 核 CPU 负载很低)</li>\n<li>平均响应时间 1.4 到 2.8 毫秒,网络开销约占 1.0 到 1.1 毫秒(和数据包大小有关)</li>\n</ul>\n"},{filename:"user/configuration/annotation.md",__html:'<h1>注解配置</h1>\n<p>需要 <code>2.5.7</code> 及以上版本支持</p>\n<h2>服务提供方</h2>\n<h3><code>Service</code>注解暴露服务</h3>\n<pre><code class="language-java"><span class="hljs-keyword">import</span> com.alibaba.dubbo.config.annotation.Service;\n \n<span class="hljs-meta">@Service</span>(timeout = <span class="hljs-number">5000</span>)\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AnnotateServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">AnnotateService</span> </span>{ \n    <span class="hljs-comment">// ...</span>\n}\n</code></pre>\n<h3>javaconfig形式配置公共模块</h3>\n<pre><code class="language-java"><span class="hljs-meta">@Configuration</span>\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DubboConfiguration</span> </span>{\n\n    <span class="hljs-meta">@Bean</span>\n    <span class="hljs-function"><span class="hljs-keyword">public</span> ApplicationConfig <span class="hljs-title">applicationConfig</span><span class="hljs-params">()</span> </span>{\n        ApplicationConfig applicationConfig = <span class="hljs-keyword">new</span> ApplicationConfig();\n        applicationConfig.setName(<span class="hljs-string">"provider-test"</span>);\n        <span class="hljs-keyword">return</span> applicationConfig;\n    }\n\n    <span class="hljs-meta">@Bean</span>\n    <span class="hljs-function"><span class="hljs-keyword">public</span> RegistryConfig <span class="hljs-title">registryConfig</span><span class="hljs-params">()</span> </span>{\n        RegistryConfig registryConfig = <span class="hljs-keyword">new</span> RegistryConfig();\n        registryConfig.setAddress(<span class="hljs-string">"zookeeper://127.0.0.1:2181"</span>);\n        registryConfig.setClient(<span class="hljs-string">"curator"</span>);\n        <span class="hljs-keyword">return</span> registryConfig;\n    }\n}\n</code></pre>\n<h3>指定dubbo扫描路径</h3>\n<pre><code class="language-java"><span class="hljs-meta">@SpringBootApplication</span>\n<span class="hljs-meta">@DubboComponentScan</span>(basePackages = <span class="hljs-string">"com.alibaba.dubbo.test.service.impl"</span>)\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ProviderTestApp</span> </span>{\n    <span class="hljs-comment">// ...</span>\n}\n</code></pre>\n<h2>服务消费方</h2>\n<h3><code>Reference</code>注解引用服务</h3>\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">AnnotationConsumeService</span> </span>{\n\n    <span class="hljs-meta">@com</span>.alibaba.dubbo.config.annotation.Reference\n    <span class="hljs-keyword">public</span> AnnotateService annotateService;\n    \n    <span class="hljs-comment">// ...</span>\n}\n\n</code></pre>\n<h3>javaconfig形式配置公共模块</h3>\n<pre><code class="language-java"><span class="hljs-meta">@Configuration</span>\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DubboConfiguration</span> </span>{\n\n    <span class="hljs-meta">@Bean</span>\n    <span class="hljs-function"><span class="hljs-keyword">public</span> ApplicationConfig <span class="hljs-title">applicationConfig</span><span class="hljs-params">()</span> </span>{\n        ApplicationConfig applicationConfig = <span class="hljs-keyword">new</span> ApplicationConfig();\n        applicationConfig.setName(<span class="hljs-string">"consumer-test"</span>);\n        <span class="hljs-keyword">return</span> applicationConfig;\n    }\n\n    <span class="hljs-meta">@Bean</span>\n    <span class="hljs-function"><span class="hljs-keyword">public</span> ConsumerConfig <span class="hljs-title">consumerConfig</span><span class="hljs-params">()</span> </span>{\n        ConsumerConfig consumerConfig = <span class="hljs-keyword">new</span> ConsumerConfig();\n        consumerConfig.setTimeout(<span class="hljs-number">3000</span>);\n        <span class="hljs-keyword">return</span> consumerConfig;\n    }\n\n    <span class="hljs-meta">@Bean</span>\n    <span class="hljs-function"><span class="hljs-keyword">public</span> RegistryConfig <span class="hljs-title">registryConfig</span><span class="hljs-params">()</span> </span>{\n        RegistryConfig registryConfig = <span class="hljs-keyword">new</span> RegistryConfig();\n        registryConfig.setAddress(<span class="hljs-string">"zookeeper://127.0.0.1:2181"</span>);\n        registryConfig.setClient(<span class="hljs-string">"curator"</span>);\n        <span class="hljs-keyword">return</span> registryConfig;\n    }\n}\n</code></pre>\n<h3>指定dubbo扫描路径</h3>\n<pre><code class="language-java"><span class="hljs-meta">@SpringBootApplication</span>\n<span class="hljs-meta">@DubboComponentScan</span>(basePackages = <span class="hljs-string">"com.alibaba.dubbo.test.service"</span>)\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ConsumerTestApp</span> </span>{\n    <span class="hljs-comment">// ...</span>\n}\n</code></pre>\n<h2>注意</h2>\n<p>如果你曾使用旧版annotation配置,请删除所有相关配置,我们将在下个版本删除所有旧版配置项。</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:annotation</span> <span class="hljs-attr">package</span>=<span class="hljs-string">"com.alibaba.dubbo.test.service"</span> /&gt;</span> \n</code></pre>\n'},{filename:"user/configuration/api.md",__html:'<h1>API 配置</h1>\n<p>API 属性与配置项一对一,各属性含义,请参见:<a href="../references/xml/introduction.md">配置参考手册</a>,比如:<code>ApplicationConfig.setName(&quot;xxx&quot;)</code> 对应  <code>&lt;dubbo:application name=&quot;xxx&quot; /&gt;</code> <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup></p>\n<h2>服务提供者</h2>\n<pre><code class="language-java"><span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.config.ApplicationConfig;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.config.RegistryConfig;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.config.ProviderConfig;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.config.ServiceConfig;\n<span class="hljs-keyword">import</span> com.xxx.XxxService;\n<span class="hljs-keyword">import</span> com.xxx.XxxServiceImpl;\n \n<span class="hljs-comment">// 服务实现</span>\nXxxService xxxService = <span class="hljs-keyword">new</span> XxxServiceImpl();\n \n<span class="hljs-comment">// 当前应用配置</span>\nApplicationConfig application = <span class="hljs-keyword">new</span> ApplicationConfig();\napplication.setName(<span class="hljs-string">"xxx"</span>);\n \n<span class="hljs-comment">// 连接注册中心配置</span>\nRegistryConfig registry = <span class="hljs-keyword">new</span> RegistryConfig();\nregistry.setAddress(<span class="hljs-string">"10.20.130.230:9090"</span>);\nregistry.setUsername(<span class="hljs-string">"aaa"</span>);\nregistry.setPassword(<span class="hljs-string">"bbb"</span>);\n \n<span class="hljs-comment">// 服务提供者协议配置</span>\nProtocolConfig protocol = <span class="hljs-keyword">new</span> ProtocolConfig();\nprotocol.setName(<span class="hljs-string">"dubbo"</span>);\nprotocol.setPort(<span class="hljs-number">12345</span>);\nprotocol.setThreads(<span class="hljs-number">200</span>);\n \n<span class="hljs-comment">// 注意:ServiceConfig为重对象,内部封装了与注册中心的连接,以及开启服务端口</span>\n \n<span class="hljs-comment">// 服务提供者暴露服务配置</span>\nServiceConfig&lt;XxxService&gt; service = <span class="hljs-keyword">new</span> ServiceConfig&lt;XxxService&gt;(); <span class="hljs-comment">// 此实例很重,封装了与注册中心的连接,请自行缓存,否则可能造成内存和连接泄漏</span>\nservice.setApplication(application);\nservice.setRegistry(registry); <span class="hljs-comment">// 多个注册中心可以用setRegistries()</span>\nservice.setProtocol(protocol); <span class="hljs-comment">// 多个协议可以用setProtocols()</span>\nservice.setInterface(XxxService.class);\nservice.setRef(xxxService);\nservice.setVersion(<span class="hljs-string">"1.0.0"</span>);\n \n<span class="hljs-comment">// 暴露及注册服务</span>\nservice.export();\n</code></pre>\n<h2>服务消费者</h2>\n<pre><code class="language-java"><span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.config.ApplicationConfig;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.config.RegistryConfig;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.config.ConsumerConfig;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.config.ReferenceConfig;\n<span class="hljs-keyword">import</span> com.xxx.XxxService;\n \n<span class="hljs-comment">// 当前应用配置</span>\nApplicationConfig application = <span class="hljs-keyword">new</span> ApplicationConfig();\napplication.setName(<span class="hljs-string">"yyy"</span>);\n \n<span class="hljs-comment">// 连接注册中心配置</span>\nRegistryConfig registry = <span class="hljs-keyword">new</span> RegistryConfig();\nregistry.setAddress(<span class="hljs-string">"10.20.130.230:9090"</span>);\nregistry.setUsername(<span class="hljs-string">"aaa"</span>);\nregistry.setPassword(<span class="hljs-string">"bbb"</span>);\n \n<span class="hljs-comment">// 注意:ReferenceConfig为重对象,内部封装了与注册中心的连接,以及与服务提供方的连接</span>\n \n<span class="hljs-comment">// 引用远程服务</span>\nReferenceConfig&lt;XxxService&gt; reference = <span class="hljs-keyword">new</span> ReferenceConfig&lt;XxxService&gt;(); <span class="hljs-comment">// 此实例很重,封装了与注册中心的连接以及与提供者的连接,请自行缓存,否则可能造成内存和连接泄漏</span>\nreference.setApplication(application);\nreference.setRegistry(registry); <span class="hljs-comment">// 多个注册中心可以用setRegistries()</span>\nreference.setInterface(XxxService.class);\nreference.setVersion(<span class="hljs-string">"1.0.0"</span>);\n \n<span class="hljs-comment">// 和本地bean一样使用xxxService</span>\nXxxService xxxService = reference.get(); <span class="hljs-comment">// 注意:此代理对象内部封装了所有通讯细节,对象较重,请缓存复用</span>\n</code></pre>\n<h2>特殊场景</h2>\n<p>下面只列出不同的地方,其它参见上面的写法</p>\n<h3>方法级设置</h3>\n<pre><code class="language-java">...\n \n<span class="hljs-comment">// 方法级配置</span>\nList&lt;MethodConfig&gt; methods = <span class="hljs-keyword">new</span> ArrayList&lt;MethodConfig&gt;();\nMethodConfig method = <span class="hljs-keyword">new</span> MethodConfig();\nmethod.setName(<span class="hljs-string">"createXxx"</span>);\nmethod.setTimeout(<span class="hljs-number">10000</span>);\nmethod.setRetries(<span class="hljs-number">0</span>);\nmethods.add(method);\n \n<span class="hljs-comment">// 引用远程服务</span>\nReferenceConfig&lt;XxxService&gt; reference = <span class="hljs-keyword">new</span> ReferenceConfig&lt;XxxService&gt;(); <span class="hljs-comment">// 此实例很重,封装了与注册中心的连接以及与提供者的连接,请自行缓存,否则可能造成内存和连接泄漏</span>\n...\nreference.setMethods(methods); <span class="hljs-comment">// 设置方法级配置</span>\n \n...\n</code></pre>\n<h3>点对点直连</h3>\n<pre><code class="language-java">\n...\n \nReferenceConfig&lt;XxxService&gt; reference = <span class="hljs-keyword">new</span> ReferenceConfig&lt;XxxService&gt;(); <span class="hljs-comment">// 此实例很重,封装了与注册中心的连接以及与提供者的连接,请自行缓存,否则可能造成内存和连接泄漏</span>\n<span class="hljs-comment">// 如果点对点直连,可以用reference.setUrl()指定目标地址,设置url后将绕过注册中心,</span>\n<span class="hljs-comment">// 其中,协议对应provider.setProtocol()的值,端口对应provider.setPort()的值,</span>\n<span class="hljs-comment">// 路径对应service.setPath()的值,如果未设置path,缺省path为接口名</span>\nreference.setUrl(<span class="hljs-string">"dubbo://10.20.130.230:20880/com.xxx.XxxService"</span>); \n \n...\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>API使用范围说明:API 仅用于 OpenAPI, ESB, Test, Mock 等系统集成,普通服务提供方或消费方,请采用<a href="../configuration/xml.md">XML 配置</a>方式使用 Dubbo <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/configuration/index.md",__html:"<h1>配置</h1>\n"},{filename:"user/configuration/properties.md",__html:'<h1>属性配置</h1>\n<p>如果公共配置很简单,没有多注册中心,多协议等情况,或者想多个 Spring 容器想共享配置,可以使用 dubbo.properties 作为缺省配置。</p>\n<p>Dubbo 将自动加载 classpath 根目录下的 dubbo.properties,可以通过JVM启动参数 <code>-Ddubbo.properties.file=xxx.properties</code> 改变缺省配置位置。<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup></p>\n<h2>映射规则</h2>\n<p>将 XML 配置的标签名,加属性名,用点分隔,多个属性拆成多行</p>\n<ul>\n<li>比如:<code>dubbo.application.name=foo</code>等价于<code>&lt;dubbo:application name=&quot;foo&quot; /&gt;</code></li>\n<li>比如:<code>dubbo.registry.address=10.20.153.10:9090</code>等价于<code>&lt;dubbo:registry address=&quot;10.20.153.10:9090&quot; /&gt;</code></li>\n</ul>\n<p>如果 XML 有多行同名标签配置,可用 id 号区分,如果没有 id 号将对所有同名标签生效</p>\n<ul>\n<li>比如:<code>dubbo.protocol.rmi.port=1234</code>等价于<code>&lt;dubbo:protocol id=&quot;rmi&quot; name=&quot;rmi&quot; port=&quot;1099&quot; /&gt;</code> <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup></li>\n<li>比如:<code>dubbo.registry.china.address=10.20.153.10:9090</code>等价于<code>&lt;dubbo:registry id=&quot;china&quot; address=&quot;10.20.153.10:9090&quot; /&gt;</code></li>\n</ul>\n<p>下面是 dubbo.properties 的一个典型配置:</p>\n<pre><code class="language-properties">dubbo.application.name=foo\ndubbo.application.owner=bar\ndubbo.registry.address=10.20.153.10:9090\n</code></pre>\n<h2>覆盖策略</h2>\n<p><img src="../sources/images/dubbo-properties-override.jpg" alt="properties-override"></p>\n<p>JVM 启动 -D 参数优先,这样可以使用户在部署和启动时进行参数重写,比如在启动时需改变协议的端口。</p>\n<p>XML 次之,如果在 XML 中有配置,则 dubbo.properties 中的相应配置项无效。</p>\n<p>Properties 最后,相当于缺省值,只有 XML 没有配置时,dubbo.properties 的相应配置项才会生效,通常用于共享公共配置,比如应用名。</p>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>如果 classpath 根目录下存在多个 dubbo.properties,比如多个 jar 包中有 dubbo.properties,Dubbo 会任意加载,并打印 Error 日志,后续可能改为抛异常。 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>协议的 id 没配时,缺省使用协议名作为 id <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/configuration/xml.md",__html:'<h1>XML 配置</h1>\n<p>有关 XML 的详细配置项,请参见:<a href="../references/xml/introduction.md">配置参考手册</a>。如果不想使用 Spring 配置,而希望通过 API 的方式进行调用,请参见:<a href="./api.md">API配置</a>。想知道如何使用配置,请参见:<a href="../quick-start.md">快速启动</a>。</p>\n<h2>provider.xml 示例</h2>\n<pre><code class="language-xml"><span class="php"><span class="hljs-meta">&lt;?</span>xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"UTF-8"</span><span class="hljs-meta">?&gt;</span></span>\n<span class="hljs-tag">&lt;<span class="hljs-name">beans</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans"</span>\n    <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>\n    <span class="hljs-attr">xmlns:dubbo</span>=<span class="hljs-string">"http://dubbo.apache.org/schema/dubbo"</span>\n    <span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"</span>&gt;</span>  \n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:application</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"hello-world-app"</span>  /&gt;</span>  \n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"multicast://224.5.6.7:1234"</span> /&gt;</span>  \n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"20880"</span> /&gt;</span>  \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.demo.DemoService"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"demoServiceLocal"</span> /&gt;</span>  \n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"demoServiceRemote"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.alibaba.dubbo.demo.DemoService"</span> /&gt;</span>  \n<span class="hljs-tag">&lt;/<span class="hljs-name">beans</span>&gt;</span>\n</code></pre>\n<p>所有标签都支持自定义参数,用于不同扩展点实现的特殊配置,如:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"jms"</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:parameter</span> <span class="hljs-attr">key</span>=<span class="hljs-string">"queue"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"your_queue"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:protocol</span>&gt;</span>\n</code></pre>\n<p>或: <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup></p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">beans</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans"</span>\n    <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>\n    <span class="hljs-attr">xmlns:dubbo</span>=<span class="hljs-string">"http://dubbo.apache.org/schema/dubbo"</span>\n    <span class="hljs-attr">xmlns:p</span>=<span class="hljs-string">"http://www.springframework.org/schema/p"</span>\n    <span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"</span>&gt;</span>  \n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"jms"</span> <span class="hljs-attr">p:queue</span>=<span class="hljs-string">"your_queue"</span> /&gt;</span>  \n<span class="hljs-tag">&lt;/<span class="hljs-name">beans</span>&gt;</span>\n</code></pre>\n<h2>配置之间的关系</h2>\n<p><img src="../sources/images/dubbo-config.jpg" alt="dubbo-config"></p>\n<table>\n<thead>\n<tr>\n<th>标签</th>\n<th>用途</th>\n<th>解释</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>&lt;dubbo:service/&gt;</code></td>\n<td>服务配置</td>\n<td>用于暴露一个服务,定义服务的元信息,一个服务可以用多个协议暴露,一个服务也可以注册到多个注册中心</td>\n</tr>\n<tr>\n<td><code>&lt;dubbo:reference/&gt;</code> <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup></td>\n<td>引用配置</td>\n<td>用于创建一个远程服务代理,一个引用可以指向多个注册中心</td>\n</tr>\n<tr>\n<td><code>&lt;dubbo:protocol/&gt;</code></td>\n<td>协议配置</td>\n<td>用于配置提供服务的协议信息,协议由提供方指定,消费方被动接受</td>\n</tr>\n<tr>\n<td><code>&lt;dubbo:application/&gt;</code></td>\n<td>应用配置</td>\n<td>用于配置当前应用信息,不管该应用是提供者还是消费者</td>\n</tr>\n<tr>\n<td><code>&lt;dubbo:module/&gt;</code></td>\n<td>模块配置</td>\n<td>用于配置当前模块信息,可选</td>\n</tr>\n<tr>\n<td><code>&lt;dubbo:registry/&gt;</code></td>\n<td>注册中心配置</td>\n<td>用于配置连接注册中心相关信息</td>\n</tr>\n<tr>\n<td><code>&lt;dubbo:monitor/&gt;</code></td>\n<td>监控中心配置</td>\n<td>用于配置连接监控中心相关信息,可选</td>\n</tr>\n<tr>\n<td><code>&lt;dubbo:provider/&gt;</code></td>\n<td>提供方配置</td>\n<td>当 ProtocolConfig 和 ServiceConfig 某属性没有配置时,采用此缺省值,可选</td>\n</tr>\n<tr>\n<td><code>&lt;dubbo:consumer/&gt;</code></td>\n<td>消费方配置</td>\n<td>当 ReferenceConfig 某属性没有配置时,采用此缺省值,可选</td>\n</tr>\n<tr>\n<td><code>&lt;dubbo:method/&gt;</code></td>\n<td>方法配置</td>\n<td>用于 ServiceConfig 和 ReferenceConfig 指定方法级的配置信息</td>\n</tr>\n<tr>\n<td><code>&lt;dubbo:argument/&gt;</code></td>\n<td>参数配置</td>\n<td>用于指定方法参数配置</td>\n</tr>\n</tbody>\n</table>\n<h2>配置覆盖关系</h2>\n<p>以 timeout 为例,显示了配置的查找顺序,其它 retries, loadbalance, actives 等类似:</p>\n<ul>\n<li>方法级优先,接口级次之,全局配置再次之。</li>\n<li>如果级别一样,则消费方优先,提供方次之。</li>\n</ul>\n<p>其中,服务提供方配置,通过 URL 经由注册中心传递给消费方。</p>\n<p><img src="../sources/images/dubbo-config-override.jpg" alt="dubbo-config-override"></p>\n<p>建议由服务提供方设置超时,因为一个方法需要执行多长时间,服务提供方更清楚,如果一个消费方同时引用多个服务,就不需要关心每个服务的超时设置。</p>\n<p>理论上 ReferenceConfig 的非服务标识配置,在 ConsumerConfig,ServiceConfig, ProviderConfig 均可以缺省配置。</p>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p><code>2.1.0</code> 开始支持,注意声明:<code>xmlns:p=&quot;http://www.springframework.org/schema/p&quot;</code> <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>引用缺省是延迟初始化的,只有引用被注入到其它 Bean,或被 <code>getBean()</code> 获取,才会初始化。如果需要饥饿加载,即没有人引用也立即生成动态代理,可以配置:<code>&lt;dubbo:reference ... init=&quot;true&quot; /&gt;</code> <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/coveragence.md",__html:'<h1>测试覆盖率报告</h1>\n<p>基于 <code>2.0.12</code> 版本,统计于 2012-02-03</p>\n<p><img src="sources/images/code-quality1.jpg" alt="/sources/images/code-quality1.jpg"></p>\n<p><img src="sources/images/code-quality5.jpg" alt="/sources/images/code-quality5.jpg"></p>\n<p><img src="sources/images/code-coverage.jpg" alt="/sources/images/code-coverage.jpg"></p>\n<p><img src="sources/images/code-tendency.jpg" alt="/sources/images/code-tendency.jpg"></p>\n<p><img src="sources/images/code-dependency.jpg" alt="/sources/images/code-dependency.jpg"></p>\n'},{filename:"user/demos/accesslog.md",__html:'<h1>访问日志</h1>\n<p>如果你想记录每一次请求信息,可开启访问日志,类似于apache的访问日志。<strong>注意</strong>:此日志量比较大,请注意磁盘容量。</p>\n<p>将访问日志输出到当前应用的log4j日志:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">accesslog</span>=<span class="hljs-string">"true"</span> /&gt;</span>\n</code></pre>\n<p>将访问日志输出到指定文件:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">accesslog</span>=<span class="hljs-string">"http://10.20.160.198/wiki/display/dubbo/foo/bar.log"</span> /&gt;</span>\n</code></pre>\n'},{filename:"user/demos/async-call.md",__html:'<h1>异步调用</h1>\n<p>基于 NIO 的非阻塞实现并行调用,客户端不需要启动多线程即可完成并行调用多个远程服务,相对多线程开销较小。 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup></p>\n<p><img src="../sources/images/future.jpg" alt="/user-guide/images/future.jpg"></p>\n<p>在 consumer.xml 中配置:</p>\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">"fooService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.alibaba.foo.FooService"</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">"findFoo"</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<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"barService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.alibaba.bar.BarService"</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">"findBar"</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>调用代码:</p>\n<pre><code class="language-java"><span class="hljs-comment">// 此调用会立即返回null</span>\nfooService.findFoo(fooId);\n<span class="hljs-comment">// 拿到调用的Future引用,当结果返回后,会被通知和设置到此Future</span>\nFuture&lt;Foo&gt; fooFuture = RpcContext.getContext().getFuture(); \n \n<span class="hljs-comment">// 此调用会立即返回null</span>\nbarService.findBar(barId);\n<span class="hljs-comment">// 拿到调用的Future引用,当结果返回后,会被通知和设置到此Future</span>\nFuture&lt;Bar&gt; barFuture = RpcContext.getContext().getFuture(); \n \n<span class="hljs-comment">// 此时findFoo和findBar的请求同时在执行,客户端不需要启动多线程来支持并行,而是借助NIO的非阻塞完成</span>\n \n<span class="hljs-comment">// 如果foo已返回,直接拿到返回值,否则线程wait住,等待foo返回后,线程会被notify唤醒</span>\nFoo foo = fooFuture.get(); \n<span class="hljs-comment">// 同理等待bar返回</span>\nBar bar = barFuture.get(); \n \n<span class="hljs-comment">// 如果foo需要5秒返回,bar需要6秒返回,实际只需等6秒,即可获取到foo和bar,进行接下来的处理。</span>\n</code></pre>\n<p>你也可以设置是否等待消息发出: <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup></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<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">"findFoo"</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">"findFoo"</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<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p><code>2.0.6</code> 及其以上版本支持 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>异步总是不等待返回 <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/demos/attachment.md",__html:'<h1>隐式参数</h1>\n<p>可以通过 <code>RpcContext</code> 上的 <code>setAttachment</code> 和 <code>getAttachment</code> 在服务消费方和提供方之间进行参数的隐式传递。 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup></p>\n<p><img src="../sources/images/context.png" alt="/user-guide/images/context.png"></p>\n<h4>在服务消费方端设置隐式参数</h4>\n<p><code>setAttachment</code> 设置的 KV 对,在完成下面一次远程调用会被清空,即多次远程调用要多次设置。</p>\n<pre><code class="language-xml">RpcContext.getContext().setAttachment("index", "1"); // 隐式传参,后面的远程调用都会隐式将这些参数发送到服务器端,类似cookie,用于框架集成,不建议常规业务使用\nxxxService.xxx(); // 远程调用\n// ...\n</code></pre>\n<h4>在服务提供方端获取隐式参数</h4>\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">XxxServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">XxxService</span> </span>{\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">xxx</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-comment">// 获取客户端隐式传入的参数,用于框架集成,不建议常规业务使用</span>\n        String index = RpcContext.getContext().getAttachment(<span class="hljs-string">"index"</span>); \n    }\n}\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>注意:path, group, version, dubbo, token, timeout 几个 key 是保留字段,请使用其它值。 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/demos/callback-parameter.md",__html:'<h1>参数回调</h1>\n<p>参数回调方式与调用本地 callback 或 listener 相同,只需要在 Spring 的配置文件中声明哪个参数是 callback 类型即可。Dubbo 将基于长连接生成反向代理,这样就可以从服务器端调用客户端逻辑 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>。可以参考 <a href="https://github.com/apache/incubator-dubbo/tree/master/dubbo-test/dubbo-test-examples/src/main/java/com/alibaba/dubbo/examples/callback">dubbo 项目中的示例代码</a>。</p>\n<h4>服务接口示例</h4>\n<h6>CallbackService.java</h6>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.callback;\n \n<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</code></pre>\n<h6>CallbackListener.java</h6>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.callback;\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<h4>服务提供者接口实现示例</h4>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.callback.impl;\n \n<span class="hljs-keyword">import</span> java.text.SimpleDateFormat;\n<span class="hljs-keyword">import</span> java.util.Date;\n<span class="hljs-keyword">import</span> java.util.Map;\n<span class="hljs-keyword">import</span> java.util.concurrent.ConcurrentHashMap;\n \n<span class="hljs-keyword">import</span> com.callback.CallbackListener;\n<span class="hljs-keyword">import</span> com.callback.CallbackService;\n \n<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">// 定时触发变更通知</span>\n                    } <span class="hljs-keyword">catch</span> (Throwable t) { <span class="hljs-comment">// 防御容错</span>\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">// 发送变更通知</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<h4>服务提供者配置示例</h4>\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.callback.impl.CallbackServiceImpl"</span> /&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.callback.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;!--也可以通过指定类型的方式--&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<h4>服务消费者配置示例</h4>\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">"callbackService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.callback.CallbackService"</span> /&gt;</span>\n</code></pre>\n<h4>服务消费者调用示例</h4>\n<pre><code class="language-java">ClassPathXmlApplicationContext context = <span class="hljs-keyword">new</span> ClassPathXmlApplicationContext(<span class="hljs-string">"classpath:consumer.xml"</span>);\ncontext.start();\n \nCallbackService callbackService = (CallbackService) context.getBean(<span class="hljs-string">"callbackService"</span>);\n \ncallbackService.addListener(<span class="hljs-string">"http://10.20.160.198/wiki/display/dubbo/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<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p><code>2.0.6</code> 及其以上版本支持 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/demos/concurrency-control.md",__html:'<h1>并发控制</h1>\n<h2>配置样例</h2>\n<h3>样例 1</h3>\n<p>限制 <code>com.foo.BarService</code> 的每个方法,服务器端并发执行(或占用线程池线程数)不能超过 10 个:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">executes</span>=<span class="hljs-string">"10"</span> /&gt;</span>\n</code></pre>\n<h3>样例 2</h3>\n<p>限制 <code>com.foo.BarService</code> 的 <code>sayHello</code> 方法,服务器端并发执行(或占用线程池线程数)不能超过 10 个:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</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">executes</span>=<span class="hljs-string">"10"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:service</span>&gt;</span>\n</code></pre>\n<h3>样例 3</h3>\n<p>限制 <code>com.foo.BarService</code> 的每个方法,每客户端并发执行(或占用连接的请求数)不能超过 10 个:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">actives</span>=<span class="hljs-string">"10"</span> /&gt;</span>\n</code></pre>\n<p>或</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">actives</span>=<span class="hljs-string">"10"</span> /&gt;</span>\n</code></pre>\n<h3>样例 4</h3>\n<p>限制 <code>com.foo.BarService</code> 的 <code>sayHello</code> 方法,每客户端并发执行(或占用连接的请求数)不能超过 10 个:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</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">actives</span>=<span class="hljs-string">"10"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:service</span>&gt;</span>\n</code></pre>\n<p>或</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</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">actives</span>=<span class="hljs-string">"10"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:service</span>&gt;</span>\n</code></pre>\n<p>如果 <code>&lt;dubbo:service&gt;</code> 和 <code>&lt;dubbo:reference&gt;</code> 都配了actives,<code>&lt;dubbo:reference&gt;</code> 优先,参见:<a href="../configuration/xml.md#%E9%85%8D%E7%BD%AE%E8%A6%86%E7%9B%96%E5%85%B3%E7%B3%BB">配置的覆盖策略</a>。</p>\n<h2>Load Balance 均衡</h2>\n<p>配置服务的客户端的 <code>loadbalance</code> 属性为 <code>leastactive</code>,此 Loadbalance 会调用并发数最小的 Provider(Consumer端并发数)。</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">loadbalance</span>=<span class="hljs-string">"leastactive"</span> /&gt;</span>\n</code></pre>\n<p>或</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">loadbalance</span>=<span class="hljs-string">"leastactive"</span> /&gt;</span>\n</code></pre>\n'},{filename:"user/demos/config-connections.md",__html:'<h1>连接控制</h1>\n<h2>服务端连接控制</h2>\n<p>限制服务器端接受的连接不能超过 10 个 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">accepts</span>=<span class="hljs-string">"10"</span> /&gt;</span>\n</code></pre>\n<p>或</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">accepts</span>=<span class="hljs-string">"10"</span> /&gt;</span>\n</code></pre>\n<h2>客户端连接控制</h2>\n<p>限制客户端服务使用连接不能超过 10 个 <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">connections</span>=<span class="hljs-string">"10"</span> /&gt;</span>\n</code></pre>\n<p>或</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">connections</span>=<span class="hljs-string">"10"</span> /&gt;</span>\n</code></pre>\n<p>如果 <code>&lt;dubbo:service&gt;</code> 和 <code>&lt;dubbo:reference&gt;</code> 都配了 connections,<code>&lt;dubbo:reference&gt;</code> 优先,参见:<a href="../configuration/xml.md">配置的覆盖策略</a></p>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>因为连接在 Server上,所以配置在 Provider 上 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>如果是长连接,比如 Dubbo 协议,connections 表示该服务对每个提供者建立的长连接数 <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/demos/config-rule.md",__html:'<h1>配置规则</h1>\n<p>向注册中心写入动态配置覆盖规则 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>。该功能通常由监控中心或治理中心的页面完成。</p>\n<pre><code class="language-java">RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();\nRegistry registry = registryFactory.getRegistry(URL.valueOf(<span class="hljs-string">"zookeeper://10.20.153.10:2181"</span>));\nregistry.register(URL.valueOf(<span class="hljs-string">"override://0.0.0.0/com.foo.BarService?category=configurators&amp;dynamic=false&amp;application=foo&amp;timeout=1000"</span>));\n</code></pre>\n<p>其中:</p>\n<ul>\n<li><code>override://</code> 表示数据采用覆盖方式,支持 <code>override</code> 和 <code>absent</code>,可扩展,<strong>必填</strong>。</li>\n<li><code>0.0.0.0</code> 表示对所有 IP 地址生效,如果只想覆盖某个 IP 的数据,请填入具体 IP,<strong>必填</strong>。</li>\n<li><code>com.foo.BarService</code> 表示只对指定服务生效,<strong>必填</strong>。</li>\n<li><code>category=configurators</code> 表示该数据为动态配置类型,<strong>必填</strong>。</li>\n<li><code>dynamic=false</code> 表示该数据为持久数据,当注册方退出时,数据依然保存在注册中心,<strong>必填</strong>。</li>\n<li><code>enabled=true</code> 覆盖规则是否生效,可不填,缺省生效。</li>\n<li><code>application=foo</code> 表示只对指定应用生效,可不填,表示对所有应用生效。</li>\n<li><code>timeout=1000</code> 表示将满足以上条件的 <code>timeout</code> 参数的值覆盖为 1000。如果想覆盖其它参数,直接加在 <code>override</code> 的 URL 参数上。</li>\n</ul>\n<p>示例:</p>\n<ol>\n<li>\n<p>禁用提供者:(通常用于临时踢除某台提供者机器,相似的,禁止消费者访问请使用路由规则)</p>\n<pre><code>override://10.20.153.10/com.foo.BarService?category=configurators&amp;dynamic=false&amp;disbaled=true\n</code></pre>\n</li>\n<li>\n<p>调整权重:(通常用于容量评估,缺省权重为 100)</p>\n<pre><code>override://10.20.153.10/com.foo.BarService?category=configurators&amp;dynamic=false&amp;weight=200\n</code></pre>\n</li>\n<li>\n<p>调整负载均衡策略:(缺省负载均衡策略为 random)</p>\n<pre><code>override://10.20.153.10/com.foo.BarService?category=configurators&amp;dynamic=false&amp;loadbalance=leastactive\n</code></pre>\n</li>\n<li>\n<p>服务降级:(通常用于临时屏蔽某个出错的非关键服务)</p>\n<pre><code>override://0.0.0.0/com.foo.BarService?category=configurators&amp;dynamic=false&amp;application=foo&amp;mock=force:return+null\n</code></pre>\n</li>\n</ol>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p><code>2.2.0</code> 以上版本支持 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/demos/context.md",__html:'<h1>上下文信息</h1>\n<p>上下文中存放的是当前调用过程中所需的环境信息。所有配置信息都将转换为 URL 的参数,参见 <a href="../references/xml/introduction.md">schema 配置参考手册</a> 中的<strong>对应URL参数</strong>一列。</p>\n<p>RpcContext 是一个 ThreadLocal 的临时状态记录器,当接收到 RPC 请求,或发起 RPC 请求时,RpcContext 的状态都会变化。比如:A 调 B,B 再调 C,则 B 机器上,在 B 调 C 之前,RpcContext 记录的是 A 调 B 的信息,在 B 调 C 之后,RpcContext 记录的是 B 调 C 的信息。</p>\n<h2>服务消费方</h2>\n<pre><code class="language-java"><span class="hljs-comment">// 远程调用</span>\nxxxService.xxx();\n<span class="hljs-comment">// 本端是否为消费端,这里会返回true</span>\n<span class="hljs-keyword">boolean</span> isConsumerSide = RpcContext.getContext().isConsumerSide();\n<span class="hljs-comment">// 获取最后一次调用的提供方IP地址</span>\nString serverIP = RpcContext.getContext().getRemoteHost();\n<span class="hljs-comment">// 获取当前服务配置信息,所有配置信息都将转换为URL的参数</span>\nString application = RpcContext.getContext().getUrl().getParameter(<span class="hljs-string">"application"</span>);\n<span class="hljs-comment">// 注意:每发起RPC调用,上下文状态会变化</span>\nyyyService.yyy();\n</code></pre>\n<h2>服务提供方</h2>\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">XxxServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">XxxService</span> </span>{\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">xxx</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-comment">// 本端是否为提供端,这里会返回true</span>\n        <span class="hljs-keyword">boolean</span> isProviderSide = RpcContext.getContext().isProviderSide();\n        <span class="hljs-comment">// 获取调用方IP地址</span>\n        String clientIP = RpcContext.getContext().getRemoteHost();\n        <span class="hljs-comment">// 获取当前服务配置信息,所有配置信息都将转换为URL的参数</span>\n        String application = RpcContext.getContext().getUrl().getParameter(<span class="hljs-string">"application"</span>);\n        <span class="hljs-comment">// 注意:每发起RPC调用,上下文状态会变化</span>\n        yyyService.yyy();\n        <span class="hljs-comment">// 此时本端变成消费端,这里会返回false</span>\n        <span class="hljs-keyword">boolean</span> isProviderSide = RpcContext.getContext().isProviderSide();\n    } \n}\n</code></pre>\n'},{filename:"user/demos/delay-publish.md",__html:'<h1>延迟暴露</h1>\n<p>如果你的服务需要预热时间,比如初始化缓存,等待相关资源就位等,可以使用 delay 进行延迟暴露。</p>\n<h2>延迟 5 秒暴露服务</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">delay</span>=<span class="hljs-string">"5000"</span> /&gt;</span>\n</code></pre>\n<h2>延迟到 Spring 初始化完成后,再暴露服务 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup></h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">delay</span>=<span class="hljs-string">"-1"</span> /&gt;</span>\n</code></pre>\n<h3>Spring 2.x 初始化死锁问题</h3>\n<h4>触发条件</h4>\n<p>在 Spring 解析到 <code>&lt;dubbo:service /&gt;</code> 时,就已经向外暴露了服务,而 Spring 还在接着初始化其它 Bean。如果这时有请求进来,并且服务的实现类里有调用 <code>applicationContext.getBean()</code> 的用法。</p>\n<ol>\n<li>\n<p>请求线程的 applicationContext.getBean() 调用,先同步 singletonObjects 判断 Bean 是否存在,不存在就同步 beanDefinitionMap 进行初始化,并再次同步 singletonObjects 写入 Bean 实例缓存。</p>\n<p><img src="../sources/images/lock-get-bean.jpg" alt="deadlock"></p>\n</li>\n<li>\n<p>而 Spring 初始化线程,因不需要判断 Bean 的存在,直接同步 beanDefinitionMap 进行初始化,并同步 singletonObjects 写入 Bean 实例缓存。</p>\n<p><img src="../sources/images/lock-init-context.jpg" alt="/user-guide/images/lock-init-context.jpg"></p>\n<p>这样就导致 getBean 线程,先锁 singletonObjects,再锁 beanDefinitionMap,再次锁 singletonObjects。<br>\n而 Spring 初始化线程,先锁 beanDefinitionMap,再锁 singletonObjects。反向锁导致线程死锁,不能提供服务,启动不了。</p>\n</li>\n</ol>\n<h4>规避办法</h4>\n<ol>\n<li>强烈建议不要在服务的实现类中有 applicationContext.getBean() 的调用,全部采用 IoC 注入的方式使用 Spring的Bean。</li>\n<li>如果实在要调 getBean(),可以将 Dubbo 的配置放在 Spring 的最后加载。</li>\n<li>如果不想依赖配置顺序,可以使用 <code>&lt;dubbo:provider delay=”-1” /&gt;</code>,使 Dubbo 在 Spring 容器初始化完后,再暴露服务。</li>\n<li>如果大量使用 getBean(),相当于已经把 Spring 退化为工厂模式在用,可以将 Dubbo 的服务隔离单独的 Spring 容器。</li>\n</ol>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>基于 Spring 的 ContextRefreshedEvent 事件触发暴露 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/demos/distributed-transaction.md",__html:'<h1>分布式事务</h1>\n<p>分布式事务基于 JTA/XA 规范实现 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>。</p>\n<p>两阶段提交:</p>\n<p><img src="../sources/images/jta-xa.jpg" alt="/user-guide/images/jta-xa.jpg"></p>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>本功能暂未实现 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/demos/dump.md",__html:'<p>当业务线程池满时,我们需要知道线程都在等待哪些资源、条件,以找到系统的瓶颈点或异常点。dubbo通过Jstack自动导出线程堆栈来保留现场,方便排查问题</p>\n<p>默认策略:</p>\n<ul>\n<li>导出路径,user.home标识的用户主目录</li>\n<li>导出间隔,最短间隔允许每隔10分钟导出一次</li>\n</ul>\n<p>指定导出路径:</p>\n<pre><code class="language-properties"># dubbo.properties\ndubbo.application.dump.directory=/tmp\n</code></pre>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:application</span> <span class="hljs-attr">...</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:parameter</span> <span class="hljs-attr">key</span>=<span class="hljs-string">"dump.directory"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"/tmp"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:application</span>&gt;</span>\n</code></pre>\n'},{filename:"user/demos/echo-service.md",__html:'<h1>回声测试</h1>\n<p>回声测试用于检测服务是否可用,回声测试按照正常请求流程执行,能够测试整个调用是否通畅,可用于监控。</p>\n<p>所有服务自动实现 <code>EchoService</code> 接口,只需将任意服务引用强制转型为 <code>EchoService</code>,即可使用。</p>\n<p>Spring 配置:</p>\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">"memberService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.xxx.MemberService"</span> /&gt;</span>\n</code></pre>\n<p>代码:</p>\n<pre><code class="language-java"><span class="hljs-comment">// 远程服务引用</span>\nMemberService memberService = ctx.getBean(<span class="hljs-string">"memberService"</span>); \n \nEchoService echoService = (EchoService) memberService; <span class="hljs-comment">// 强制转型为EchoService</span>\n\n<span class="hljs-comment">// 回声测试可用性</span>\nString status = echoService.$echo(<span class="hljs-string">"OK"</span>); \n \n<span class="hljs-keyword">assert</span>(status.equals(<span class="hljs-string">"OK"</span>));\n</code></pre>\n'},{filename:"user/demos/events-notify.md",__html:'<h1>事件通知</h1>\n<p>在调用之前、调用之后、出现异常时,会触发 <code>oninvoke</code>、<code>onreturn</code>、<code>onthrow</code> 三个事件,可以配置当事件发生时,通知哪个类的哪个方法 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>。</p>\n<h4>服务提供者与消费者共享服务接口</h4>\n<pre><code class="language-java"><span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">IDemoService</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Person <span class="hljs-title">get</span><span class="hljs-params">(<span class="hljs-keyword">int</span> id)</span></span>;\n}\n</code></pre>\n<h4>服务提供者实现</h4>\n<pre><code class="language-java"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">NormalDemoService</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">IDemoService</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Person <span class="hljs-title">get</span><span class="hljs-params">(<span class="hljs-keyword">int</span> id)</span> </span>{\n        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Person(id, <span class="hljs-string">"charles`son"</span>, <span class="hljs-number">4</span>);\n    }\n}\n</code></pre>\n<h4>服务提供者配置</h4>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:application</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rpc-callback-demo"</span> /&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"http://10.20.160.198/wiki/display/dubbo/10.20.153.186"</span> /&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">bean</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"demoService"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"com.alibaba.dubbo.callback.implicit.NormalDemoService"</span> /&gt;</span>\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.callback.implicit.IDemoService"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"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</code></pre>\n<h4>服务消费者 Callback 接口</h4>\n<pre><code class="language-java"><span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Notify</span> </span>{\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">(Person msg, Integer id)</span></span>;\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, Integer id)</span></span>;\n}\n</code></pre>\n<h4>服务消费者 Callback 实现</h4>\n<pre><code class="language-java"><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    <span class="hljs-keyword">public</span> Map&lt;Integer, Person&gt;    ret    = <span class="hljs-keyword">new</span> HashMap&lt;Integer, Person&gt;();\n    <span class="hljs-keyword">public</span> Map&lt;Integer, Throwable&gt; errors = <span class="hljs-keyword">new</span> HashMap&lt;Integer, Throwable&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">(Person msg, Integer id)</span> </span>{\n        System.out.println(<span class="hljs-string">"onreturn:"</span> + msg);\n        ret.put(id, msg);\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, Integer id)</span> </span>{\n        errors.put(id, ex);\n    }\n}\n</code></pre>\n<h4>服务消费者 Callback 配置</h4>\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.callback.implicit.NofifyImpl"</span> /&gt;</span>\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">interface</span>=<span class="hljs-string">"com.alibaba.dubbo.callback.implicit.IDemoService"</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">"get"</span> <span class="hljs-attr">async</span>=<span class="hljs-string">"true"</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><code>callback</code> 与 <code>async</code> 功能正交分解,<code>async=true</code> 表示结果是否马上返回,<code>onreturn</code> 表示是否需要回调。</p>\n<p>两者叠加存在以下几种组合情况 <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>:</p>\n<ul>\n<li>异步回调模式:<code>async=true onreturn=&quot;xxx&quot;</code></li>\n<li>同步回调模式:<code>async=false onreturn=&quot;xxx&quot;</code></li>\n<li>异步无回调 :<code>async=true</code></li>\n<li>同步无回调 :<code>async=false</code></li>\n</ul>\n<h4>测试代码</h4>\n<pre><code class="language-java">IDemoService demoService = (IDemoService) context.getBean(<span class="hljs-string">"demoService"</span>);\nNofifyImpl notify = (NofifyImpl) context.getBean(<span class="hljs-string">"demoCallback"</span>);\n<span class="hljs-keyword">int</span> requestId = <span class="hljs-number">2</span>;\nPerson ret = demoService.get(requestId);\nAssert.assertEquals(<span class="hljs-keyword">null</span>, ret);\n<span class="hljs-comment">//for Test:只是用来说明callback正常被调用,业务具体实现自行决定.</span>\n<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">10</span>; i++) {\n    <span class="hljs-keyword">if</span> (!notify.ret.containsKey(requestId)) {\n        Thread.sleep(<span class="hljs-number">200</span>);\n    } <span class="hljs-keyword">else</span> {\n        <span class="hljs-keyword">break</span>;\n    }\n}\nAssert.assertEquals(requestId, notify.ret.get(requestId).getId());\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>支持版本:<code>2.0.7</code> 之后 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p><code>async=false</code> 默认 <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/demos/explicit-target.md",__html:'<h1>直连提供者</h1>\n<p>在开发及测试环境下,经常需要绕过注册中心,只测试指定服务提供者,这时候可能需要点对点直连,点对点直联方式,将以服务接口为单位,忽略注册中心的提供者列表,A 接口配置点对点,不影响 B 接口从注册中心获取列表。</p>\n<p><img src="../sources/images/dubbo-directly.jpg" alt="/user-guide/images/dubbo-directly.jpg"></p>\n<h2>通过 XML 配置</h2>\n<p>如果是线上需求需要点对点,可在 <code>&lt;dubbo:reference&gt;</code> 中配置 url 指向提供者,将绕过注册中心,多个地址用分号隔开,配置如下  <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>:</p>\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">"xxxService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.alibaba.xxx.XxxService"</span> <span class="hljs-attr">url</span>=<span class="hljs-string">"dubbo://localhost:20890"</span> /&gt;</span>\n</code></pre>\n<h2>通过 -D 参数指定</h2>\n<p>在 JVM 启动参数中加入-D参数映射服务地址 <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>,如:</p>\n<pre><code class="language-sh">java -Dcom.alibaba.xxx.XxxService=dubbo://localhost:20890\n</code></pre>\n<h2>通过文件映射</h2>\n<p>如果服务比较多,也可以用文件映射,用 <code>-Ddubbo.resolve.file</code> 指定映射文件路径,此配置优先级高于 <code>&lt;dubbo:reference&gt;</code> 中的配置 <sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup>,如:</p>\n<pre><code class="language-sh">java -Ddubbo.resolve.file=xxx.properties\n</code></pre>\n<p>然后在映射文件 <code>xxx.properties</code> 中加入配置,其中 key 为服务名,value 为服务提供者 URL:</p>\n<pre><code class="language-properties">com.alibaba.xxx.XxxService=dubbo://localhost:20890\n</code></pre>\n<p><strong>注意</strong>  为了避免复杂化线上环境,不要在线上使用这个功能,只应在测试阶段使用。</p>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p><code>1.0.6</code> 及以上版本支持 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>key 为服务名,value 为服务提供者 url,此配置优先级最高,<code>1.0.15</code> 及以上版本支持 <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn3" class="footnote-item"><p><code>1.0.15</code> 及以上版本支持,<code>2.0</code> 以上版本自动加载 ${user.home}/dubbo-resolve.properties文件,不需要配置 <a href="#fnref3" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/demos/fault-tolerent-strategy.md",__html:'<h1>集群容错</h1>\n<p>在集群调用失败时,Dubbo 提供了多种容错方案,缺省为 failover 重试。</p>\n<p><img src="../sources/images/cluster.jpg" alt="cluster"></p>\n<p>各节点关系:</p>\n<ul>\n<li>这里的 <code>Invoker</code> 是 <code>Provider</code> 的一个可调用 <code>Service</code> 的抽象,<code>Invoker</code> 封装了 <code>Provider</code> 地址及 <code>Service</code> 接口信息</li>\n<li><code>Directory</code> 代表多个 <code>Invoker</code>,可以把它看成 <code>List&lt;Invoker&gt;</code> ,但与 <code>List</code> 不同的是,它的值可能是动态变化的,比如注册中心推送变更</li>\n<li><code>Cluster</code> 将 <code>Directory</code> 中的多个 <code>Invoker</code> 伪装成一个 <code>Invoker</code>,对上层透明,伪装过程包含了容错逻辑,调用失败后,重试另一个</li>\n<li><code>Router</code> 负责从多个 <code>Invoker</code> 中按路由规则选出子集,比如读写分离,应用隔离等</li>\n<li><code>LoadBalance</code> 负责从多个 <code>Invoker</code> 中选出具体的一个用于本次调用,选的过程包含了负载均衡算法,调用失败后,需要重选</li>\n</ul>\n<h2>集群容错模式</h2>\n<p>可以自行扩展集群容错策略,参见:<a href="http://dubbo.apache.org/books/dubbo-dev-book/impls/cluster.html">集群扩展</a></p>\n<h3>Failover Cluster</h3>\n<p>失败自动切换,当出现失败,重试其它服务器 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>。通常用于读操作,但重试会带来更长延迟。可通过 <code>retries=&quot;2&quot;</code> 来设置重试次数(不含第一次)。</p>\n<p>重试次数配置如下:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">retries</span>=<span class="hljs-string">"2"</span> /&gt;</span>\n</code></pre>\n<p>或</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">retries</span>=<span class="hljs-string">"2"</span> /&gt;</span>\n</code></pre>\n<p>或</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</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">"findFoo"</span> <span class="hljs-attr">retries</span>=<span class="hljs-string">"2"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:reference</span>&gt;</span>\n</code></pre>\n<h3>Failfast Cluster</h3>\n<p>快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。</p>\n<h3>Failsafe Cluster</h3>\n<p>失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。</p>\n<h3>Failback Cluster</h3>\n<p>失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。</p>\n<h3>Forking Cluster</h3>\n<p>并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 <code>forks=&quot;2&quot;</code> 来设置最大并行数。</p>\n<h3>Broadcast Cluster</h3>\n<p>广播调用所有提供者,逐个调用,任意一台报错则报错 <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>。通常用于通知所有提供者更新缓存或日志等本地资源信息。</p>\n<h2>集群模式配置</h2>\n<p>按照以下示例在服务提供方和消费方配置集群模式</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">cluster</span>=<span class="hljs-string">"failsafe"</span> /&gt;</span>\n</code></pre>\n<p>或</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">cluster</span>=<span class="hljs-string">"failsafe"</span> /&gt;</span>\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>该配置为缺省配置 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p><code>2.1.0</code> 开始支持 <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/demos/generic-reference.md",__html:'<h1>使用泛化调用</h1>\n<p>泛化接口调用方式主要用于客户端没有 API 接口及模型类元的情况,参数及返回值中的所有 POJO 均用 <code>Map</code> 表示,通常用于框架集成,比如:实现一个通用的服务测试框架,可通过 <code>GenericService</code> 调用所有服务实现。</p>\n<h2>通过 Spring 使用泛化调用</h2>\n<p>在 Spring 配置申明 <code>generic=&quot;true&quot;</code>:</p>\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">"barService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">generic</span>=<span class="hljs-string">"true"</span> /&gt;</span>\n</code></pre>\n<p>在 Java 代码获取 barService 并开始泛化调用:</p>\n<pre><code class="language-java">GenericService barService = (GenericService) applicationContext.getBean(<span class="hljs-string">"barService"</span>);\nObject result = barService.$invoke(<span class="hljs-string">"sayHello"</span>, <span class="hljs-keyword">new</span> String[] { <span class="hljs-string">"java.lang.String"</span> }, <span class="hljs-keyword">new</span> Object[] { <span class="hljs-string">"World"</span> });\n</code></pre>\n<h2>通过 API 方式使用泛化调用</h2>\n<pre><code class="language-java"><span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.service.GenericService; \n... \n \n<span class="hljs-comment">// 引用远程服务 </span>\n<span class="hljs-comment">// 该实例很重量,里面封装了所有与注册中心及服务提供方连接,请缓存</span>\nReferenceConfig&lt;GenericService&gt; reference = <span class="hljs-keyword">new</span> ReferenceConfig&lt;GenericService&gt;(); \n<span class="hljs-comment">// 弱类型接口名</span>\nreference.setInterface(<span class="hljs-string">"com.xxx.XxxService"</span>);  \nreference.setVersion(<span class="hljs-string">"1.0.0"</span>);\n<span class="hljs-comment">// 声明为泛化接口 </span>\nreference.setGeneric(<span class="hljs-keyword">true</span>);  \n\n<span class="hljs-comment">// 用com.alibaba.dubbo.rpc.service.GenericService可以替代所有接口引用  </span>\nGenericService genericService = reference.get(); \n \n<span class="hljs-comment">// 基本类型以及Date,List,Map等不需要转换,直接调用 </span>\nObject result = genericService.$invoke(<span class="hljs-string">"sayHello"</span>, <span class="hljs-keyword">new</span> String[] {<span class="hljs-string">"java.lang.String"</span>}, <span class="hljs-keyword">new</span> Object[] {<span class="hljs-string">"world"</span>}); \n \n<span class="hljs-comment">// 用Map表示POJO参数,如果返回值为POJO也将自动转成Map </span>\nMap&lt;String, Object&gt; person = <span class="hljs-keyword">new</span> HashMap&lt;String, Object&gt;(); \nperson.put(<span class="hljs-string">"name"</span>, <span class="hljs-string">"xxx"</span>); \nperson.put(<span class="hljs-string">"password"</span>, <span class="hljs-string">"yyy"</span>); \n<span class="hljs-comment">// 如果返回POJO将自动转成Map </span>\nObject result = genericService.$invoke(<span class="hljs-string">"findPerson"</span>, <span class="hljs-keyword">new</span> String[]\n{<span class="hljs-string">"com.xxx.Person"</span>}, <span class="hljs-keyword">new</span> Object[]{person}); \n \n...\n</code></pre>\n<h2>有关泛化类型的进一步解释</h2>\n<p>假设存在 POJO 如:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PersonImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Person</span> </span>{\n    <span class="hljs-keyword">private</span> String name;\n    <span class="hljs-keyword">private</span> String password;\n\n    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getName</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-keyword">return</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">setName</span><span class="hljs-params">(String name)</span> </span>{\n        <span class="hljs-keyword">this</span>.name = name;\n    }\n\n    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getPassword</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-keyword">return</span> password;\n    }\n\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setPassword</span><span class="hljs-params">(String password)</span> </span>{\n        <span class="hljs-keyword">this</span>.password = password;\n    }\n}\n</code></pre>\n<p>则 POJO 数据:</p>\n<pre><code class="language-java">Person person = <span class="hljs-keyword">new</span> PersonImpl(); \nperson.setName(<span class="hljs-string">"xxx"</span>); \nperson.setPassword(<span class="hljs-string">"yyy"</span>);\n</code></pre>\n<p>可用下面 Map 表示:</p>\n<pre><code class="language-java">Map&lt;String, Object&gt; map = <span class="hljs-keyword">new</span> HashMap&lt;String, Object&gt;(); \n<span class="hljs-comment">// 注意:如果参数类型是接口,或者List等丢失泛型,可通过class属性指定类型。</span>\nmap.put(<span class="hljs-string">"class"</span>, <span class="hljs-string">"com.xxx.PersonImpl"</span>); \nmap.put(<span class="hljs-string">"name"</span>, <span class="hljs-string">"xxx"</span>); \nmap.put(<span class="hljs-string">"password"</span>, <span class="hljs-string">"yyy"</span>);\n</code></pre>\n'},{filename:"user/demos/generic-service.md",__html:'<h1>实现泛化调用</h1>\n<p>泛接口实现方式主要用于服务器端没有API接口及模型类元的情况,参数及返回值中的所有POJO均用Map表示,通常用于框架集成,比如:实现一个通用的远程服务Mock框架,可通过实现GenericService接口处理所有服务请求。</p>\n<p>在 Java 代码中实现 <code>GenericService</code> 接口:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.foo;\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyGenericService</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">GenericService</span> </span>{\n \n    <span class="hljs-keyword">public</span> Object $invoke(String methodName, String[] parameterTypes, Object[] args) <span class="hljs-keyword">throws</span> GenericException {\n        <span class="hljs-keyword">if</span> (<span class="hljs-string">"sayHello"</span>.equals(methodName)) {\n            <span class="hljs-keyword">return</span> <span class="hljs-string">"Welcome "</span> + args[<span class="hljs-number">0</span>];\n        }\n    }\n}\n</code></pre>\n<h2>通过 Spring 暴露泛化实现</h2>\n<p>在 Spring 配置申明服务的实现:</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">"genericService"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"com.foo.MyGenericService"</span> /&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"genericService"</span> /&gt;</span>\n</code></pre>\n<h2>通过 API 方式暴露泛化实现</h2>\n<pre><code class="language-java">... \n<span class="hljs-comment">// 用com.alibaba.dubbo.rpc.service.GenericService可以替代所有接口实现 </span>\nGenericService xxxService = <span class="hljs-keyword">new</span> XxxGenericService(); \n\n<span class="hljs-comment">// 该实例很重量,里面封装了所有与注册中心及服务提供方连接,请缓存 </span>\nServiceConfig&lt;GenericService&gt; service = <span class="hljs-keyword">new</span> ServiceConfig&lt;GenericService&gt;();\n<span class="hljs-comment">// 弱类型接口名 </span>\nservice.setInterface(<span class="hljs-string">"com.xxx.XxxService"</span>);  \nservice.setVersion(<span class="hljs-string">"1.0.0"</span>); \n<span class="hljs-comment">// 指向一个通用服务实现 </span>\nservice.setRef(xxxService); \n \n<span class="hljs-comment">// 暴露及注册服务 </span>\nservice.export();\n</code></pre>\n'},{filename:"user/demos/graceful-shutdown.md",__html:'<h1>优雅停机</h1>\n<p>Dubbo 是通过 JDK 的 ShutdownHook 来完成优雅停机的,所以如果用户使用 <code>kill -9 PID</code> 等强制关闭指令,是不会执行优雅停机的,只有通过 <code>kill PID</code> 时,才会执行。</p>\n<h2>原理</h2>\n<h3>服务提供方</h3>\n<ul>\n<li>停止时,先标记为不接收新请求,新请求过来时直接报错,让客户端重试其它机器。</li>\n<li>然后,检测线程池中的线程是否正在运行,如果有,等待所有线程执行完成,除非超时,则强制关闭。</li>\n</ul>\n<h3>服务消费方</h3>\n<ul>\n<li>停止时,不再发起新的调用请求,所有新的调用在客户端即报错。</li>\n<li>然后,检测有没有请求的响应还没有返回,等待响应返回,除非超时,则强制关闭。</li>\n</ul>\n<h2>设置方式</h2>\n<p>设置优雅停机超时时间,缺省超时时间是 10 秒,如果超时则强制关闭。</p>\n<pre><code class="language-properties"># dubbo.properties\ndubbo.service.shutdown.wait=15000\n</code></pre>\n<p>如果 ShutdownHook 不能生效,可以自行调用,<strong>使用tomcat等容器部署的場景,建议通过扩展ContextListener等自行调用以下代码实现优雅停机</strong>:</p>\n<pre><code class="language-java">ProtocolConfig.destroyAll();\n</code></pre>\n'},{filename:"user/demos/group-merger.md",__html:'<h1>分组聚合</h1>\n<p>按组合并返回结果 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>,比如菜单服务,接口一样,但有多种实现,用group区分,现在消费方需从每种group中调用一次返回结果,合并结果返回,这样就可以实现聚合菜单项。</p>\n<p>相关代码可以参考 <a href="https://github.com/apache/incubator-dubbo/tree/master/dubbo-test/dubbo-test-examples/src/main/java/com/alibaba/dubbo/examples/merge">dubbo 项目中的示例</a></p>\n<h2>配置</h2>\n<p>搜索所有分组</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.xxx.MenuService"</span> <span class="hljs-attr">group</span>=<span class="hljs-string">"*"</span> <span class="hljs-attr">merger</span>=<span class="hljs-string">"true"</span> /&gt;</span>\n</code></pre>\n<p>合并指定分组</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.xxx.MenuService"</span> <span class="hljs-attr">group</span>=<span class="hljs-string">"aaa,bbb"</span> <span class="hljs-attr">merger</span>=<span class="hljs-string">"true"</span> /&gt;</span>\n</code></pre>\n<p>指定方法合并结果,其它未指定的方法,将只调用一个 Group</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.xxx.MenuService"</span> <span class="hljs-attr">group</span>=<span class="hljs-string">"*"</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">"getMenuItems"</span> <span class="hljs-attr">merger</span>=<span class="hljs-string">"true"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:service</span>&gt;</span>\n</code></pre>\n<p>某个方法不合并结果,其它都合并结果</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.xxx.MenuService"</span> <span class="hljs-attr">group</span>=<span class="hljs-string">"*"</span> <span class="hljs-attr">merger</span>=<span class="hljs-string">"true"</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">"getMenuItems"</span> <span class="hljs-attr">merger</span>=<span class="hljs-string">"false"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:service</span>&gt;</span>\n</code></pre>\n<p>指定合并策略,缺省根据返回值类型自动匹配,如果同一类型有两个合并器时,需指定合并器的名称 <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup></p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.xxx.MenuService"</span> <span class="hljs-attr">group</span>=<span class="hljs-string">"*"</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">"getMenuItems"</span> <span class="hljs-attr">merger</span>=<span class="hljs-string">"mymerge"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:service</span>&gt;</span>\n</code></pre>\n<p>指定合并方法,将调用返回结果的指定方法进行合并,合并方法的参数类型必须是返回结果类型本身</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.xxx.MenuService"</span> <span class="hljs-attr">group</span>=<span class="hljs-string">"*"</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">"getMenuItems"</span> <span class="hljs-attr">merger</span>=<span class="hljs-string">".addAll"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:service</span>&gt;</span>\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>从 <code>2.1.0</code> 版本开始支持 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>参见:<a href="http://dubbo.apache.org/books/dubbo-dev-book/impls/merger.html">合并结果扩展</a> <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/demos/hostname-binding.md",__html:'<h1>主机绑定</h1>\n<h2>查找顺序</h2>\n<p>缺省主机 IP 查找顺序:</p>\n<ul>\n<li>通过 <code>LocalHost.getLocalHost()</code> 获取本机地址。</li>\n<li>如果是 <code>127.*</code> 等 loopback 地址,则扫描各网卡,获取网卡 IP。</li>\n</ul>\n<h2>主机配置</h2>\n<p>注册的地址如果获取不正确,比如需要注册公网地址,可以:</p>\n<ol>\n<li>\n<p>可以在 <code>/etc/hosts</code> 中加入:机器名 公网 IP,比如:</p>\n<pre><code>test1 205.182.23.201\n</code></pre>\n</li>\n<li>\n<p>在 <code>dubbo.xml</code> 中加入主机地址的配置:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">host</span>=<span class="hljs-string">"205.182.23.201"</span>&gt;</span>\n</code></pre>\n</li>\n<li>\n<p>或在 <code>dubbo.properties</code> 中加入主机地址的配置:</p>\n<pre><code class="language-properties">dubbo.protocol.host=205.182.23.201\n</code></pre>\n</li>\n</ol>\n<h2>端口配置</h2>\n<p>缺省主机端口与协议相关:</p>\n<table>\n<thead>\n<tr>\n<th>协议</th>\n<th>端口</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>dubbo</td>\n<td>20880</td>\n</tr>\n<tr>\n<td>rmi</td>\n<td>1099</td>\n</tr>\n<tr>\n<td>http</td>\n<td>80</td>\n</tr>\n<tr>\n<td>hessian</td>\n<td>80</td>\n</tr>\n<tr>\n<td>webservice</td>\n<td>80</td>\n</tr>\n<tr>\n<td>memcached</td>\n<td>11211</td>\n</tr>\n<tr>\n<td>redis</td>\n<td>6379</td>\n</tr>\n</tbody>\n</table>\n<p>可以按照下面的方式配置端口:</p>\n<ol>\n<li>\n<p>在 <code>dubbo.xml</code> 中加入主机地址的配置:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"20880"</span>&gt;</span>\n</code></pre>\n</li>\n<li>\n<p>或在 <code>dubbo.properties</code> 中加入主机地址的配置:</p>\n<pre><code class="language-properties">dubbo.protocol.dubbo.port=20880\n</code></pre>\n</li>\n</ol>\n'},{filename:"user/demos/index.md",__html:"<h1>示例</h1>\n"},{filename:"user/demos/introduction.md",__html:'<blockquote>\n<p><img src="../sources/images/check.gif" alt="warning">想完整的运行起来,请参见:<a href="quickstart.md">快速启动</a>,这里只列出各种场景的配置方式。\n<img src="../sources/images/check.gif" alt="warning">以下示例全部使用基于Spring的<a href="../configuration/xml.md">Xml配置</a>作为参考,如果不想使用Spring,而希望通过API的方式进行调用,请参见:<a href="../configuration/api.md">API配置</a></p>\n</blockquote>\n'},{filename:"user/demos/lazy-connect.md",__html:'<h1>延迟连接</h1>\n<p>延迟连接用于减少长连接数。当有调用发起时,再创建长连接。<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup></p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">lazy</span>=<span class="hljs-string">"true"</span> /&gt;</span>\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>注意:该配置只对使用长连接的 dubbo 协议生效。 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/demos/loadbalance.md",__html:'<h1>负载均衡</h1>\n<p>在集群负载均衡时,Dubbo 提供了多种均衡策略,缺省为 <code>random</code> 随机调用。</p>\n<p>可以自行扩展负载均衡策略,参见:<a href="http://dubbo.apache.org/books/dubbo-dev-book/impls/load-balance.html">负载均衡扩展</a></p>\n<h2>负载均衡策略</h2>\n<h3>Random LoadBalance</h3>\n<ul>\n<li><strong>随机</strong>,按权重设置随机概率。</li>\n<li>在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。</li>\n</ul>\n<h3>RoundRobin LoadBalance</h3>\n<ul>\n<li><strong>轮循</strong>,按公约后的权重设置轮循比率。</li>\n<li>存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。</li>\n</ul>\n<h3>LeastActive LoadBalance</h3>\n<ul>\n<li><strong>最少活跃调用数</strong>,相同活跃数的随机,活跃数指调用前后计数差。</li>\n<li>使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。</li>\n</ul>\n<h3>ConsistentHash LoadBalance</h3>\n<ul>\n<li><strong>一致性 Hash</strong>,相同参数的请求总是发到同一提供者。</li>\n<li>当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。</li>\n<li>算法参见:<a href="http://en.wikipedia.org/wiki/Consistent_hashing">http://en.wikipedia.org/wiki/Consistent_hashing</a></li>\n<li>缺省只对第一个参数 Hash,如果要修改,请配置 <code>&lt;dubbo:parameter key=&quot;hash.arguments&quot; value=&quot;0,1&quot; /&gt;</code></li>\n<li>缺省用 160 份虚拟节点,如果要修改,请配置 <code>&lt;dubbo:parameter key=&quot;hash.nodes&quot; value=&quot;320&quot; /&gt;</code></li>\n</ul>\n<h2>配置</h2>\n<h3>服务端服务级别</h3>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"..."</span> <span class="hljs-attr">loadbalance</span>=<span class="hljs-string">"roundrobin"</span> /&gt;</span>\n</code></pre>\n<h3>客户端服务级别</h3>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"..."</span> <span class="hljs-attr">loadbalance</span>=<span class="hljs-string">"roundrobin"</span> /&gt;</span>\n</code></pre>\n<h3>服务端方法级别</h3>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"..."</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">"..."</span> <span class="hljs-attr">loadbalance</span>=<span class="hljs-string">"roundrobin"</span>/&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:service</span>&gt;</span>\n</code></pre>\n<h3>客户端方法级别</h3>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"..."</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">"..."</span> <span class="hljs-attr">loadbalance</span>=<span class="hljs-string">"roundrobin"</span>/&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:reference</span>&gt;</span>\n</code></pre>\n'},{filename:"user/demos/local-call.md",__html:'<h1>本地调用</h1>\n<p>本地调用使用了 injvm 协议,是一个伪协议,它不开启端口,不发起远程调用,只在 JVM 内直接关联,但执行 Dubbo 的 Filter 链。</p>\n<h2>配置</h2>\n<p>定义 injvm 协议</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"injvm"</span> /&gt;</span>\n</code></pre>\n<p>设置默认协议</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"injvm"</span> /&gt;</span>\n</code></pre>\n<p>设置服务协议</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"injvm"</span> /&gt;</span>\n</code></pre>\n<p>优先使用 injvm</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:consumer</span> <span class="hljs-attr">injvm</span>=<span class="hljs-string">"true"</span> <span class="hljs-attr">...</span>/&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">injvm</span>=<span class="hljs-string">"true"</span> <span class="hljs-attr">...</span>/&gt;</span>\n</code></pre>\n<p>或</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">injvm</span>=<span class="hljs-string">"true"</span> <span class="hljs-attr">...</span>/&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">injvm</span>=<span class="hljs-string">"true"</span> <span class="hljs-attr">...</span>/&gt;</span>\n</code></pre>\n<p>注意:服务暴露与服务引用都需要声明 <code>injvm=&quot;true&quot;</code></p>\n<h2>自动暴露、引用本地服务</h2>\n<p>从 <code>2.2.0</code> 开始,每个服务默认都会在本地暴露。在引用服务的时候,默认优先引用本地服务。如果希望引用远程服务可以使用一下配置强制引用远程服务。</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">...</span> <span class="hljs-attr">scope</span>=<span class="hljs-string">"remote"</span> /&gt;</span>\n</code></pre>\n'},{filename:"user/demos/local-mock.md",__html:'<h1>本地伪装</h1>\n<p>本地伪装 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> 通常用于服务降级,比如某验权服务,当服务提供方全部挂掉后,客户端不抛出异常,而是通过 Mock 数据返回授权失败。</p>\n<p>在 spring 配置文件中按以下方式配置:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">mock</span>=<span class="hljs-string">"true"</span> /&gt;</span>\n</code></pre>\n<p>或</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">mock</span>=<span class="hljs-string">"com.foo.BarServiceMock"</span> /&gt;</span>\n</code></pre>\n<p>在工程中提供 Mock 实现 <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.foo;\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BarServiceMock</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">BarService</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">sayHello</span><span class="hljs-params">(String name)</span> </span>{\n        <span class="hljs-comment">// 你可以伪造容错数据,此方法只在出现RpcException时被执行</span>\n        <span class="hljs-keyword">return</span> <span class="hljs-string">"容错数据"</span>;\n    }\n}\n</code></pre>\n<p>如果服务的消费方经常需要 try-catch 捕获异常,如:</p>\n<pre><code class="language-java">Offer offer = <span class="hljs-keyword">null</span>;\n<span class="hljs-keyword">try</span> {\n    offer = offerService.findOffer(offerId);\n} <span class="hljs-keyword">catch</span> (RpcException e) {\n   logger.error(e);\n}\n</code></pre>\n<p>请考虑改为 Mock 实现,并在 Mock 实现中 return null。如果只是想简单的忽略异常,在 <code>2.0.11</code> 以上版本可用:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">mock</span>=<span class="hljs-string">"return null"</span> /&gt;</span>\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>Mock 是 Stub 的一个子集,便于服务提供方在客户端执行容错逻辑,因经常需要在出现 RpcException (比如网络失败,超时等)时进行容错,而在出现业务异常(比如登录用户名密码错误)时不需要容错,如果用 Stub,可能就需要捕获并依赖 RpcException 类,而用 Mock 就可以不依赖 RpcException,因为它的约定就是只有出现 RpcException 时才执行。 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>在 interface 旁放一个 Mock 实现,它实现 BarService 接口,并有一个无参构造函数 <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/demos/local-stub.md",__html:'<h1>本地存根</h1>\n<p>远程服务后,客户端通常只剩下接口,而实现全在服务器端,但提供方有些时候想在客户端也执行部分逻辑,比如:做 ThreadLocal 缓存,提前验证参数,调用失败后伪造容错数据等等,此时就需要在 API 中带上 Stub,客户端生成 Proxy 实例,会把 Proxy 通过构造函数传给 Stub <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>,然后把 Stub 暴露给用户,Stub 可以决定要不要去调 Proxy。</p>\n<p><img src="../sources/images/stub.jpg" alt="/user-guide/images/stub.jpg"></p>\n<p>在 spring 配置文件中按以下方式配置:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">stub</span>=<span class="hljs-string">"true"</span> /&gt;</span>\n</code></pre>\n<p>或</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">stub</span>=<span class="hljs-string">"com.foo.BarServiceStub"</span> /&gt;</span>\n</code></pre>\n<p>提供 Stub 的实现 <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.foo;\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BarServiceStub</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">BarService</span> </span>{ \n    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> BarService barService;\n    \n    <span class="hljs-comment">// 构造函数传入真正的远程代理对象</span>\n    <span class="hljs-keyword">public</span> (BarService barService) {\n        <span class="hljs-keyword">this</span>.barService = barService;\n    }\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">sayHello</span><span class="hljs-params">(String name)</span> </span>{\n        <span class="hljs-comment">// 此代码在客户端执行, 你可以在客户端做ThreadLocal本地缓存,或预先验证参数是否合法,等等</span>\n        <span class="hljs-keyword">try</span> {\n            <span class="hljs-keyword">return</span> barService.sayHello(name);\n        } <span class="hljs-keyword">catch</span> (Exception e) {\n            <span class="hljs-comment">// 你可以容错,可以做任何AOP拦截事项</span>\n            <span class="hljs-keyword">return</span> <span class="hljs-string">"容错数据"</span>;\n        }\n    }\n}\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>Stub 必须有可传入 Proxy 的构造函数。 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>在 interface 旁边放一个 Stub 实现,它实现 BarService 接口,并有一个传入远程 BarService 实例的构造函数 <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/demos/logger-strategy.md",__html:'<h1>日志适配</h1>\n<p>自 <code>2.2.1</code> 开始,dubbo 开始内置 log4j、slf4j、jcl、jdk 这些日志框架的适配 [^1],也可以通过以下方式显示配置日志输出策略:</p>\n<ol start="0">\n<li>\n<p>命令行</p>\n<pre><code class="language-sh"></code></pre>\n</li>\n</ol>\n<p>java -Ddubbo.application.logger=log4j</p>\n<pre><code>\n0. 在 `dubbo.properties` 中指定\n\n    ```\ndubbo.application.logger=log4j\n</code></pre>\n<ol start="0">\n<li>\n<p>在 <code>dubbo.xml</code> 中配置</p>\n<pre><code class="language-xml"></code></pre>\n</li>\n</ol>\n<p>&lt;dubbo:application logger=&quot;log4j&quot; /&gt;</p>\n<pre><code>\n[^1]: 自定义扩展可以参考[日志适配扩展](http://dubbo.apache.org/books/dubbo-dev-book/impls/logger-adapter.html)\n</code></pre>\n'},{filename:"user/demos/multi-protocols.md",__html:'<h1>多协议</h1>\n<p>Dubbo 允许配置多协议,在不同服务上支持不同协议或者同一服务上同时支持多种协议。</p>\n<h2>不同服务不同协议</h2>\n<p>不同服务在性能上适用不同协议进行传输,比如大数据用短连接协议,小数据大并发用长连接协议</p>\n<pre><code class="language-xml"><span class="php"><span class="hljs-meta">&lt;?</span>xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"UTF-8"</span><span class="hljs-meta">?&gt;</span></span>\n<span class="hljs-tag">&lt;<span class="hljs-name">beans</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans"</span>\n    <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>\n    <span class="hljs-attr">xmlns:dubbo</span>=<span class="hljs-string">"http://dubbo.apache.org/schema/dubbo"</span>\n    <span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"</span>&gt;</span> \n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:application</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"world"</span>  /&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"registry"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.141.150:9090"</span> <span class="hljs-attr">username</span>=<span class="hljs-string">"admin"</span> <span class="hljs-attr">password</span>=<span class="hljs-string">"hello1234"</span> /&gt;</span>\n    <span class="hljs-comment">&lt;!-- 多协议配置 --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"20880"</span> /&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rmi"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"1099"</span> /&gt;</span>\n    <span class="hljs-comment">&lt;!-- 使用dubbo协议暴露服务 --&gt;</span>\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.hello.api.HelloService"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0.0"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"helloService"</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"dubbo"</span> /&gt;</span>\n    <span class="hljs-comment">&lt;!-- 使用rmi协议暴露服务 --&gt;</span>\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.hello.api.DemoService"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0.0"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"demoService"</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"rmi"</span> /&gt;</span> \n<span class="hljs-tag">&lt;/<span class="hljs-name">beans</span>&gt;</span>\n</code></pre>\n<h2>多协议暴露服务</h2>\n<p>需要与 http 客户端互操作</p>\n<pre><code class="language-xml"><span class="php"><span class="hljs-meta">&lt;?</span>xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"UTF-8"</span><span class="hljs-meta">?&gt;</span></span>\n<span class="hljs-tag">&lt;<span class="hljs-name">beans</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans"</span>\n    <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>\n    <span class="hljs-attr">xmlns:dubbo</span>=<span class="hljs-string">"http://dubbo.apache.org/schema/dubbo"</span>\n    <span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:application</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"world"</span>  /&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"registry"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.141.150:9090"</span> <span class="hljs-attr">username</span>=<span class="hljs-string">"admin"</span> <span class="hljs-attr">password</span>=<span class="hljs-string">"hello1234"</span> /&gt;</span>\n    <span class="hljs-comment">&lt;!-- 多协议配置 --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"20880"</span> /&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"hessian"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"8080"</span> /&gt;</span>\n    <span class="hljs-comment">&lt;!-- 使用多个协议暴露服务 --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"helloService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.alibaba.hello.api.HelloService"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0.0"</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"dubbo,hessian"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">beans</span>&gt;</span>\n</code></pre>\n'},{filename:"user/demos/multi-registry.md",__html:'<h1>多注册中心</h1>\n<p>Dubbo 支持同一服务向多注册中心同时注册,或者不同服务分别注册到不同的注册中心上去,甚至可以同时引用注册在不同注册中心上的同名服务。另外,注册中心是支持自定义扩展的 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>。</p>\n<h2>多注册中心注册</h2>\n<p>比如:中文站有些服务来不及在青岛部署,只在杭州部署,而青岛的其它应用需要引用此服务,就可以将服务同时注册到两个注册中心。</p>\n<pre><code class="language-xml"><span class="php"><span class="hljs-meta">&lt;?</span>xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"UTF-8"</span><span class="hljs-meta">?&gt;</span></span>\n<span class="hljs-tag">&lt;<span class="hljs-name">beans</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans"</span>\n    <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>\n    <span class="hljs-attr">xmlns:dubbo</span>=<span class="hljs-string">"http://dubbo.apache.org/schema/dubbo"</span>\n    <span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:application</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"world"</span>  /&gt;</span>\n    <span class="hljs-comment">&lt;!-- 多注册中心配置 --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"hangzhouRegistry"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.141.150:9090"</span> /&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"qingdaoRegistry"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.141.151:9010"</span> <span class="hljs-attr">default</span>=<span class="hljs-string">"false"</span> /&gt;</span>\n    <span class="hljs-comment">&lt;!-- 向多个注册中心注册 --&gt;</span>\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.hello.api.HelloService"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0.0"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"helloService"</span> <span class="hljs-attr">registry</span>=<span class="hljs-string">"hangzhouRegistry,qingdaoRegistry"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">beans</span>&gt;</span>\n</code></pre>\n<h2>不同服务使用不同注册中心</h2>\n<p>比如:CRM 有些服务是专门为国际站设计的,有些服务是专门为中文站设计的。</p>\n<pre><code class="language-xml"><span class="php"><span class="hljs-meta">&lt;?</span>xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"UTF-8"</span><span class="hljs-meta">?&gt;</span></span>\n<span class="hljs-tag">&lt;<span class="hljs-name">beans</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans"</span>\n    <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>\n    <span class="hljs-attr">xmlns:dubbo</span>=<span class="hljs-string">"http://dubbo.apache.org/schema/dubbo"</span>\n    <span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:application</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"world"</span>  /&gt;</span>\n    <span class="hljs-comment">&lt;!-- 多注册中心配置 --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"chinaRegistry"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.141.150:9090"</span> /&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"intlRegistry"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.154.177:9010"</span> <span class="hljs-attr">default</span>=<span class="hljs-string">"false"</span> /&gt;</span>\n    <span class="hljs-comment">&lt;!-- 向中文站注册中心注册 --&gt;</span>\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.hello.api.HelloService"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0.0"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"helloService"</span> <span class="hljs-attr">registry</span>=<span class="hljs-string">"chinaRegistry"</span> /&gt;</span>\n    <span class="hljs-comment">&lt;!-- 向国际站注册中心注册 --&gt;</span>\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.hello.api.DemoService"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0.0"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"demoService"</span> <span class="hljs-attr">registry</span>=<span class="hljs-string">"intlRegistry"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">beans</span>&gt;</span>\n</code></pre>\n<h2>多注册中心引用</h2>\n<p>比如:CRM 需同时调用中文站和国际站的 PC2 服务,PC2 在中文站和国际站均有部署,接口及版本号都一样,但连的数据库不一样。</p>\n<pre><code class="language-xml"><span class="php"><span class="hljs-meta">&lt;?</span>xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"UTF-8"</span><span class="hljs-meta">?&gt;</span></span>\n<span class="hljs-tag">&lt;<span class="hljs-name">beans</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans"</span>\n    <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>\n    <span class="hljs-attr">xmlns:dubbo</span>=<span class="hljs-string">"http://dubbo.apache.org/schema/dubbo"</span>\n    <span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:application</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"world"</span>  /&gt;</span>\n    <span class="hljs-comment">&lt;!-- 多注册中心配置 --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"chinaRegistry"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.141.150:9090"</span> /&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"intlRegistry"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.154.177:9010"</span> <span class="hljs-attr">default</span>=<span class="hljs-string">"false"</span> /&gt;</span>\n    <span class="hljs-comment">&lt;!-- 引用中文站服务 --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"chinaHelloService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.alibaba.hello.api.HelloService"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0.0"</span> <span class="hljs-attr">registry</span>=<span class="hljs-string">"chinaRegistry"</span> /&gt;</span>\n    <span class="hljs-comment">&lt;!-- 引用国际站站服务 --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"intlHelloService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.alibaba.hello.api.HelloService"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0.0"</span> <span class="hljs-attr">registry</span>=<span class="hljs-string">"intlRegistry"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">beans</span>&gt;</span>\n</code></pre>\n<p>如果只是测试环境临时需要连接两个不同注册中心,使用竖号分隔多个不同注册中心地址:</p>\n<pre><code class="language-xml"><span class="php"><span class="hljs-meta">&lt;?</span>xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"UTF-8"</span><span class="hljs-meta">?&gt;</span></span>\n<span class="hljs-tag">&lt;<span class="hljs-name">beans</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans"</span>\n    <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>\n    <span class="hljs-attr">xmlns:dubbo</span>=<span class="hljs-string">"http://dubbo.apache.org/schema/dubbo"</span>\n    <span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:application</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"world"</span>  /&gt;</span>\n    <span class="hljs-comment">&lt;!-- 多注册中心配置,竖号分隔表示同时连接多个不同注册中心,同一注册中心的多个集群地址用逗号分隔 --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.141.150:9090|10.20.154.177:9010"</span> /&gt;</span>\n    <span class="hljs-comment">&lt;!-- 引用服务 --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"helloService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.alibaba.hello.api.HelloService"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0.0"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">beans</span>&gt;</span>\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>可以自行扩展注册中心,参见:<a href="http://dubbo.apache.org/books/dubbo-dev-book/impls/registry.html">注册中心扩展</a> <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/demos/multi-versions.md",__html:'<h1>多版本</h1>\n<p>当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用。</p>\n<p>可以按照以下的步骤进行版本迁移:</p>\n<ol start="0">\n<li>在低压力时间段,先升级一半提供者为新版本</li>\n<li>再将所有消费者升级为新版本</li>\n<li>然后将剩下的一半提供者升级为新版本</li>\n</ol>\n<p>老版本服务提供者配置:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0.0"</span> /&gt;</span>\n</code></pre>\n<p>新版本服务提供者配置:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"2.0.0"</span> /&gt;</span>\n</code></pre>\n<p>老版本服务消费者配置:</p>\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">"barService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0.0"</span> /&gt;</span>\n</code></pre>\n<p>新版本服务消费者配置:</p>\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">"barService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"2.0.0"</span> /&gt;</span>\n</code></pre>\n<p>如果不需要区分版本,可以按照以下的方式配置 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>:</p>\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">"barService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"*"</span> /&gt;</span>\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p><code>2.2.0</code> 以上版本支持 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/demos/netty4.md",__html:'<p>dubbo 2.5.6版本新增了对netty4通信模块的支持,启用方式如下</p>\n<p>provider端:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"netty4"</span> /&gt;</span>\n</code></pre>\n<p>或</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"netty4"</span> /&gt;</span>\n</code></pre>\n<p>consumer端:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:consumer</span> <span class="hljs-attr">client</span>=<span class="hljs-string">"netty4"</span> /&gt;</span>\n\n</code></pre>\n<blockquote>\n<p><strong>注意</strong></p>\n<ol>\n<li>provider端如需不同的协议使用不同的通信层框架,请配置多个protocol分别设置</li>\n<li>consumer端请使用如下形式:</li>\n</ol>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:consumer</span> <span class="hljs-attr">client</span>=<span class="hljs-string">"netty"</span>&gt;</span>\n  <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:consumer</span>&gt;</span>\n</code></pre>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:consumer</span> <span class="hljs-attr">client</span>=<span class="hljs-string">"netty4"</span>&gt;</span>\n  <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:consumer</span>&gt;</span>\n</code></pre>\n</blockquote>\n<blockquote>\n<p>接下来我们会继续完善:</p>\n<ol>\n<li>性能测试指标及与netty3版本的性能测试对比,我们会提供一份参考数据</li>\n</ol>\n</blockquote>\n'},{filename:"user/demos/parameter-validation.md",__html:'<h1>参数验证</h1>\n<p>参数验证功能 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> 是基于 <a href="https://jcp.org/en/jsr/detail?id=303">JSR303</a> 实现的,用户只需标识 JSR303 标准的验证 annotation,并通过声明 filter 来实现验证 <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>。</p>\n<h2>Maven 依赖</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>javax.validation<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>validation-api<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>1.0.0.GA<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.hibernate<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>hibernate-validator<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>4.2.0.Final<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>\n</code></pre>\n<h2>示例</h2>\n<h3>参数标注示例</h3>\n<pre><code class="language-java"><span class="hljs-keyword">import</span> java.io.Serializable;\n<span class="hljs-keyword">import</span> java.util.Date;\n \n<span class="hljs-keyword">import</span> javax.validation.constraints.Future;\n<span class="hljs-keyword">import</span> javax.validation.constraints.Max;\n<span class="hljs-keyword">import</span> javax.validation.constraints.Min;\n<span class="hljs-keyword">import</span> javax.validation.constraints.NotNull;\n<span class="hljs-keyword">import</span> javax.validation.constraints.Past;\n<span class="hljs-keyword">import</span> javax.validation.constraints.Pattern;\n<span class="hljs-keyword">import</span> javax.validation.constraints.Size;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ValidationParameter</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Serializable</span> </span>{\n    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">long</span> serialVersionUID = <span class="hljs-number">7158911668568000392L</span>;\n \n    <span class="hljs-meta">@NotNull</span> <span class="hljs-comment">// 不允许为空</span>\n    <span class="hljs-meta">@Size</span>(min = <span class="hljs-number">1</span>, max = <span class="hljs-number">20</span>) <span class="hljs-comment">// 长度或大小范围</span>\n    <span class="hljs-keyword">private</span> String name;\n \n    <span class="hljs-meta">@NotNull</span>(groups = ValidationService.Save.class) <span class="hljs-comment">// 保存时不允许为空,更新时允许为空 ,表示不更新该字段</span>\n    <span class="hljs-meta">@Pattern</span>(regexp = <span class="hljs-string">"^\\\\s*\\\\w+(?:\\\\.{0,1}[\\\\w-]+)*@[a-zA-Z0-9]+(?:[-.][a-zA-Z0-9]+)*\\\\.[a-zA-Z]+\\\\s*$"</span>)\n    <span class="hljs-keyword">private</span> String email;\n \n    <span class="hljs-meta">@Min</span>(<span class="hljs-number">18</span>) <span class="hljs-comment">// 最小值</span>\n    <span class="hljs-meta">@Max</span>(<span class="hljs-number">100</span>) <span class="hljs-comment">// 最大值</span>\n    <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> age;\n \n    <span class="hljs-meta">@Past</span> <span class="hljs-comment">// 必须为一个过去的时间</span>\n    <span class="hljs-keyword">private</span> Date loginDate;\n \n    <span class="hljs-meta">@Future</span> <span class="hljs-comment">// 必须为一个未来的时间</span>\n    <span class="hljs-keyword">private</span> Date expiryDate;\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getName</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-keyword">return</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">setName</span><span class="hljs-params">(String name)</span> </span>{\n        <span class="hljs-keyword">this</span>.name = name;\n    }\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getEmail</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-keyword">return</span> email;\n    }\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setEmail</span><span class="hljs-params">(String email)</span> </span>{\n        <span class="hljs-keyword">this</span>.email = email;\n    }\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getAge</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-keyword">return</span> age;\n    }\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setAge</span><span class="hljs-params">(<span class="hljs-keyword">int</span> age)</span> </span>{\n        <span class="hljs-keyword">this</span>.age = age;\n    }\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> Date <span class="hljs-title">getLoginDate</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-keyword">return</span> loginDate;\n    }\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setLoginDate</span><span class="hljs-params">(Date loginDate)</span> </span>{\n        <span class="hljs-keyword">this</span>.loginDate = loginDate;\n    }\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> Date <span class="hljs-title">getExpiryDate</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-keyword">return</span> expiryDate;\n    }\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setExpiryDate</span><span class="hljs-params">(Date expiryDate)</span> </span>{\n        <span class="hljs-keyword">this</span>.expiryDate = expiryDate;\n    }\n}\n</code></pre>\n<h3>分组验证示例</h3>\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">ValidationService</span> </span>{ <span class="hljs-comment">// 缺省可按服务接口区分验证场景,如:@NotNull(groups = ValidationService.class)   </span>\n    <span class="hljs-meta">@interface</span> Save{} <span class="hljs-comment">// 与方法同名接口,首字母大写,用于区分验证场景,如:@NotNull(groups = ValidationService.Save.class),可选</span>\n    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">save</span><span class="hljs-params">(ValidationParameter parameter)</span></span>;\n    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">update</span><span class="hljs-params">(ValidationParameter parameter)</span></span>;\n}\n</code></pre>\n<h3>关联验证示例</h3>\n<pre><code class="language-java"><span class="hljs-keyword">import</span> javax.validation.GroupSequence;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">ValidationService</span> </span>{   \n    <span class="hljs-meta">@GroupSequence</span>(Update.class) <span class="hljs-comment">// 同时验证Update组规则</span>\n    <span class="hljs-meta">@interface</span> Save{}\n    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">save</span><span class="hljs-params">(ValidationParameter parameter)</span></span>;\n \n    <span class="hljs-meta">@interface</span> Update{} \n    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">update</span><span class="hljs-params">(ValidationParameter parameter)</span></span>;\n}\n</code></pre>\n<h3>参数验证示例</h3>\n<pre><code class="language-java"><span class="hljs-keyword">import</span> javax.validation.constraints.Min;\n<span class="hljs-keyword">import</span> javax.validation.constraints.NotNull;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">ValidationService</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">save</span><span class="hljs-params">(@NotNull ValidationParameter parameter)</span></span>; <span class="hljs-comment">// 验证参数不为空</span>\n    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">delete</span><span class="hljs-params">(@Min(<span class="hljs-number">1</span>)</span> <span class="hljs-keyword">int</span> id)</span>; <span class="hljs-comment">// 直接对基本类型参数验证</span>\n}\n</code></pre>\n<h2>配置</h2>\n<h3>在客户端验证参数</h3>\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">"validationService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.alibaba.dubbo.examples.validation.api.ValidationService"</span> <span class="hljs-attr">validation</span>=<span class="hljs-string">"true"</span> /&gt;</span>\n</code></pre>\n<h3>在服务器端验证参数</h3>\n<pre><code class="language-xml"><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.examples.validation.api.ValidationService"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"validationService"</span> <span class="hljs-attr">validation</span>=<span class="hljs-string">"true"</span> /&gt;</span>\n</code></pre>\n<h2>验证异常信息</h2>\n<pre><code class="language-java"><span class="hljs-keyword">import</span> javax.validation.ConstraintViolationException;\n<span class="hljs-keyword">import</span> javax.validation.ConstraintViolationException;\n \n<span class="hljs-keyword">import</span> org.springframework.context.support.ClassPathXmlApplicationContext;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.examples.validation.api.ValidationParameter;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.examples.validation.api.ValidationService;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.RpcException;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ValidationConsumer</span> </span>{   \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> Exception </span>{\n        String config = ValidationConsumer.class.getPackage().getName().replace(<span class="hljs-string">\'.\'</span>, <span class="hljs-string">\'/\'</span>) + <span class="hljs-string">"/validation-consumer.xml"</span>;\n        ClassPathXmlApplicationContext context = <span class="hljs-keyword">new</span> ClassPathXmlApplicationContext(config);\n        context.start();\n        ValidationService validationService = (ValidationService)context.getBean(<span class="hljs-string">"validationService"</span>);\n        <span class="hljs-comment">// Error</span>\n        <span class="hljs-keyword">try</span> {\n            parameter = <span class="hljs-keyword">new</span> ValidationParameter();\n            validationService.save(parameter);\n            System.out.println(<span class="hljs-string">"Validation ERROR"</span>);\n        } <span class="hljs-keyword">catch</span> (RpcException e) { <span class="hljs-comment">// 抛出的是RpcException</span>\n            ConstraintViolationException ve = (ConstraintViolationException) e.getCause(); <span class="hljs-comment">// 里面嵌了一个ConstraintViolationException</span>\n            Set&lt;ConstraintViolation&lt;?&gt;&gt; violations = ve.getConstraintViolations(); <span class="hljs-comment">// 可以拿到一个验证错误详细信息的集合</span>\n            System.out.println(violations);\n        }\n    } \n}\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>自 <code>2.1.0</code> 版本开始支持, 如何使用可以参考 <a href="https://github.com/apache/incubator-dubbo/tree/master/dubbo-test/dubbo-test-examples/src/main/java/com/alibaba/dubbo/examples/validation">dubbo 项目中的示例代码</a> <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>验证方式可扩展,扩展方式参见开发者手册中的<a href="http://dubbo.apache.org/books/dubbo-dev-book/impls/validation.html">验证扩展</a> <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/demos/preflight-check.md",__html:'<h1>启动时检查</h1>\n<p>Dubbo 缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,以便上线时,能及早发现问题,默认  <code>check=&quot;true&quot;</code>。</p>\n<p>可以通过 <code>check=&quot;false&quot;</code> 关闭检查,比如,测试时,有些服务不关心,或者出现了循环依赖,必须有一方先启动。</p>\n<p>另外,如果你的 Spring 容器是懒加载的,或者通过 API 编程延迟引用服务,请关闭 check,否则服务临时不可用时,会抛出异常,拿到 null 引用,如果 <code>check=&quot;false&quot;</code>,总是会返回引用,当服务恢复时,能自动连上。</p>\n<h2>示例</h2>\n<h3>通过 spring 配置文件</h3>\n<p>关闭某个服务的启动时检查 (没有提供者时报错):</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">check</span>=<span class="hljs-string">"false"</span> /&gt;</span>\n</code></pre>\n<p>关闭所有服务的启动时检查 (没有提供者时报错):</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:consumer</span> <span class="hljs-attr">check</span>=<span class="hljs-string">"false"</span> /&gt;</span>\n</code></pre>\n<p>关闭注册中心启动时检查 (注册订阅失败时报错):</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">check</span>=<span class="hljs-string">"false"</span> /&gt;</span>\n</code></pre>\n<h3>通过 dubbo.properties</h3>\n<pre><code class="language-properties">dubbo.reference.com.foo.BarService.check=false\ndubbo.reference.check=false\ndubbo.consumer.check=false\ndubbo.registry.check=false\n</code></pre>\n<h3>通过 -D 参数</h3>\n<pre><code class="language-sh">java -Ddubbo.reference.com.foo.BarService.check=<span class="hljs-literal">false</span>\njava -Ddubbo.reference.check=<span class="hljs-literal">false</span>\njava -Ddubbo.consumer.check=<span class="hljs-literal">false</span> \njava -Ddubbo.registry.check=<span class="hljs-literal">false</span>\n</code></pre>\n<h2>配置的含义</h2>\n<p><code>dubbo.reference.check=false</code>,强制改变所有 reference 的 check 值,就算配置中有声明,也会被覆盖。</p>\n<p><code>dubbo.consumer.check=false</code>,是设置 check 的缺省值,如果配置中有显式的声明,如:<code>&lt;dubbo:reference check=&quot;true&quot;/&gt;</code>,不会受影响。</p>\n<p><code>dubbo.registry.check=false</code>,前面两个都是指订阅成功,但提供者列表是否为空是否报错,如果注册订阅失败时,也允许启动,需使用此选项,将在后台定时重试。</p>\n'},{filename:"user/demos/reference-config-cache.md",__html:'<h1>ReferenceConfig 缓存</h1>\n<p><code>ReferenceConfig</code> 实例很重,封装了与注册中心的连接以及与提供者的连接,需要缓存。否则重复生成 <code>ReferenceConfig</code> 可能造成性能问题并且会有内存和连接泄漏。在 API 方式编程时,容易忽略此问题。</p>\n<p>因此,自 <code>2.4.0</code> 版本开始, dubbo 提供了简单的工具类 <code>ReferenceConfigCache</code>用于缓存 <code>ReferenceConfig</code> 实例。</p>\n<p>使用方式如下:</p>\n<pre><code class="language-java">ReferenceConfig&lt;XxxService&gt; reference = <span class="hljs-keyword">new</span> ReferenceConfig&lt;XxxService&gt;();\nreference.setInterface(XxxService.class);\nreference.setVersion(<span class="hljs-string">"1.0.0"</span>);\n......\nReferenceConfigCache cache = ReferenceConfigCache.getCache();\n<span class="hljs-comment">// cache.get方法中会缓存 Reference对象,并且调用ReferenceConfig.get方法启动ReferenceConfig</span>\nXxxService xxxService = cache.get(reference);\n<span class="hljs-comment">// 注意! Cache会持有ReferenceConfig,不要在外部再调用ReferenceConfig的destroy方法,导致Cache内的ReferenceConfig失效!</span>\n<span class="hljs-comment">// 使用xxxService对象</span>\nxxxService.sayHello();\n</code></pre>\n<p>消除 Cache 中的 <code>ReferenceConfig</code>,将销毁 <code>ReferenceConfig</code> 并释放对应的资源。</p>\n<pre><code class="language-java">ReferenceConfigCache cache = ReferenceConfigCache.getCache();\ncache.destroy(reference);\n</code></pre>\n<p>缺省 <code>ReferenceConfigCache</code> 把相同服务 Group、接口、版本的 <code>ReferenceConfig</code> 认为是相同,缓存一份。即以服务 Group、接口、版本为缓存的 Key。</p>\n<p>可以修改这个策略,在 <code>ReferenceConfigCache.getCache</code> 时,传一个 <code>KeyGenerator</code>。详见 <code>ReferenceConfigCache</code> 类的方法。</p>\n<pre><code class="language-java">KeyGenerator keyGenerator = <span class="hljs-keyword">new</span> ...\nReferenceConfigCache cache = ReferenceConfigCache.getCache(keyGenerator );\n</code></pre>\n'},{filename:"user/demos/registry-only.md",__html:'<h1>只注册</h1>\n<p>如果有两个镜像环境,两个注册中心,有一个服务只在其中一个注册中心有部署,另一个注册中心还没来得及部署,而两个注册中心的其它应用都需要依赖此服务。这个时候,可以让服务提供者方只注册服务到另一注册中心,而不从另一注册中心订阅服务。</p>\n<p>禁用订阅配置</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"hzRegistry"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.153.10:9090"</span> /&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"qdRegistry"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.141.150:9090"</span> <span class="hljs-attr">subscribe</span>=<span class="hljs-string">"false"</span> /&gt;</span>\n</code></pre>\n<p>或者</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"hzRegistry"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.153.10:9090"</span> /&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"qdRegistry"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.141.150:9090?subscribe=false"</span> /&gt;</span>\n</code></pre>\n'},{filename:"user/demos/result-cache.md",__html:'<h1>结果缓存</h1>\n<p>结果缓存 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>,用于加速热门数据的访问速度,Dubbo 提供声明式缓存,以减少用户加缓存的工作量 <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>。</p>\n<h2>缓存类型</h2>\n<ul>\n<li><code>lru</code> 基于最近最少使用原则删除多余缓存,保持最热的数据被缓存。</li>\n<li><code>threadlocal</code> 当前线程缓存,比如一个页面渲染,用到很多 portal,每个 portal 都要去查用户信息,通过线程缓存,可以减少这种多余访问。</li>\n<li><code>jcache</code> 与 <a href="http://jcp.org/en/jsr/detail?id=107%27">JSR107</a> 集成,可以桥接各种缓存实现。</li>\n</ul>\n<p>缓存类型可扩展,参见:<a href="http://dubbo.apache.org/books/dubbo-dev-book/impls/cache.html">缓存扩展</a></p>\n<h2>配置</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">cache</span>=<span class="hljs-string">"lru"</span> /&gt;</span>\n</code></pre>\n<p>或:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</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">"findBar"</span> <span class="hljs-attr">cache</span>=<span class="hljs-string">"lru"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:reference</span>&gt;</span>\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p><code>2.1.0</code> 以上版本支持 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p><a href="https://github.com/apache/incubator-dubbo/tree/master/dubbo-test/dubbo-test-examples/src/main/java/com/alibaba/dubbo/examples/cache">示例代码</a> <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/demos/routing-rule.md",__html:'<h1>路由规则</h1>\n<p>路由规则 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> 决定一次 dubbo 服务调用的目标服务器,分为条件路由规则和脚本路由规则,并且支持可扩展 <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>。</p>\n<h2>写入路由规则</h2>\n<p>向注册中心写入路由规则的操作通常由监控中心或治理中心的页面完成</p>\n<pre><code class="language-java">RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();\nRegistry registry = registryFactory.getRegistry(URL.valueOf("zookeeper://10.20.153.10:2181"));\nregistry.register(URL.valueOf("condition://0.0.0.0/com.foo.BarService?category=routers&amp;dynamic=false&amp;rule=" + URL.encode("host = 10.20.153.10 =&gt; host = 10.20.153.11") + "));\n</code></pre>\n<p>其中:</p>\n<ul>\n<li><code>condition://</code> 表示路由规则的类型,支持条件路由规则和脚本路由规则,可扩展,<strong>必填</strong>。</li>\n<li><code>0.0.0.0</code> 表示对所有 IP 地址生效,如果只想对某个 IP 的生效,请填入具体 IP,<strong>必填</strong>。</li>\n<li><code>com.foo.BarService</code> 表示只对指定服务生效,<strong>必填</strong>。</li>\n<li><code>group=foo</code> 对指定服务的指定group生效,不填表示对未配置group的指定服务生效</li>\n<li><code>version=1.0</code>对指定服务的指定version生效,不填表示对未配置version的指定服务生效</li>\n<li><code>category=routers</code> 表示该数据为动态配置类型,<strong>必填</strong>。</li>\n<li><code>dynamic=false</code> 表示该数据为持久数据,当注册方退出时,数据依然保存在注册中心,<strong>必填</strong>。</li>\n<li><code>enabled=true</code> 覆盖规则是否生效,可不填,缺省生效。</li>\n<li><code>force=false</code> 当路由结果为空时,是否强制执行,如果不强制执行,路由结果为空的路由规则将自动失效,可不填,缺省为 <code>flase</code>。</li>\n<li><code>runtime=false</code> 是否在每次调用时执行路由规则,否则只在提供者地址列表变更时预先执行并缓存结果,调用时直接从缓存中获取路由结果。如果用了参数路由,必须设为 <code>true</code>,需要注意设置会影响调用的性能,可不填,缺省为 <code>flase</code>。</li>\n<li><code>priority=1</code> 路由规则的优先级,用于排序,优先级越大越靠前执行,可不填,缺省为 <code>0</code>。</li>\n<li><code>rule=URL.encode(&quot;host = 10.20.153.10 =&gt; host = 10.20.153.11&quot;)</code> 表示路由规则的内容,<strong>必填</strong>。</li>\n</ul>\n<h2>条件路由规则</h2>\n<p>基于条件表达式的路由规则,如:<code>host = 10.20.153.10 =&gt; host = 10.20.153.11</code></p>\n<h3>规则:</h3>\n<ul>\n<li><code>=&gt;</code> 之前的为消费者匹配条件,所有参数和消费者的 URL 进行对比,当消费者满足匹配条件时,对该消费者执行后面的过滤规则。</li>\n<li><code>=&gt;</code> 之后为提供者地址列表的过滤条件,所有参数和提供者的 URL 进行对比,消费者最终只拿到过滤后的地址列表。</li>\n<li>如果匹配条件为空,表示对所有消费方应用,如:<code>=&gt; host != 10.20.153.11</code></li>\n<li>如果过滤条件为空,表示禁止访问,如:<code>host = 10.20.153.10 =&gt;</code></li>\n</ul>\n<h3>表达式:</h3>\n<p>参数支持:</p>\n<ul>\n<li>服务调用信息,如:method, argument 等,暂不支持参数路由</li>\n<li>URL 本身的字段,如:protocol, host, port 等</li>\n<li>以及 URL 上的所有参数,如:application, organization 等</li>\n</ul>\n<p>条件支持:</p>\n<ul>\n<li>等号 <code>=</code> 表示&quot;匹配&quot;,如:<code>host = 10.20.153.10</code></li>\n<li>不等号 <code>!=</code> 表示&quot;不匹配&quot;,如:<code>host != 10.20.153.10</code></li>\n</ul>\n<p>值支持:</p>\n<ul>\n<li>以逗号 <code>,</code> 分隔多个值,如:<code>host != 10.20.153.10,10.20.153.11</code></li>\n<li>以星号 <code>*</code> 结尾,表示通配,如:<code>host != 10.20.*</code></li>\n<li>以美元符 <code>$</code> 开头,表示引用消费者参数,如:<code>host = $host</code></li>\n</ul>\n<h3>示例:</h3>\n<ol start="0">\n<li>\n<p>排除预发布机:</p>\n<pre><code>=&gt; host != 172.22.3.91\n</code></pre>\n</li>\n<li>\n<p>白名单 <sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup>:</p>\n<pre><code>host != 10.20.153.10,10.20.153.11 =&gt;\n</code></pre>\n</li>\n<li>\n<p>黑名单:</p>\n<pre><code>host = 10.20.153.10,10.20.153.11 =&gt;\n</code></pre>\n</li>\n<li>\n<p>服务寄宿在应用上,只暴露一部分的机器,防止整个集群挂掉:</p>\n<pre><code>=&gt; host = 172.22.3.1*,172.22.3.2*\n</code></pre>\n</li>\n<li>\n<p>为重要应用提供额外的机器:</p>\n<pre><code>application != kylin =&gt; host != 172.22.3.95,172.22.3.96\n</code></pre>\n</li>\n<li>\n<p>读写分离:</p>\n<pre><code>method = find*,list*,get*,is* =&gt; host = 172.22.3.94,172.22.3.95,172.22.3.96\nmethod != find*,list*,get*,is* =&gt; host = 172.22.3.97,172.22.3.98\n</code></pre>\n</li>\n<li>\n<p>前后台分离:</p>\n<pre><code>application = bops =&gt; host = 172.22.3.91,172.22.3.92,172.22.3.93\napplication != bops =&gt; host = 172.22.3.94,172.22.3.95,172.22.3.96\n</code></pre>\n</li>\n<li>\n<p>隔离不同机房网段:</p>\n<pre><code>host != 172.22.3.* =&gt; host != 172.22.3.*\n</code></pre>\n</li>\n<li>\n<p>提供者与消费者部署在同集群内,本机只访问本机的服务:</p>\n<pre><code>=&gt; host = $host\n</code></pre>\n</li>\n</ol>\n<h2>脚本路由规则</h2>\n<p>脚本路由规则 <sup class="footnote-ref"><a href="#fn4" id="fnref4">[4]</a></sup> 支持 JDK 脚本引擎的所有脚本,比如:javascript, jruby, groovy 等,通过 <code>type=javascript</code> 参数设置脚本类型,缺省为 javascript。</p>\n<pre><code>&quot;script://0.0.0.0/com.foo.BarService?category=routers&amp;dynamic=false&amp;rule=&quot; + URL.encode(&quot;(function route(invokers) { ... } (invokers))&quot;)\n</code></pre>\n<p>基于脚本引擎的路由规则,如:</p>\n<pre><code class="language-javascript">(<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">route</span>(<span class="hljs-params">invokers</span>) </span>{\n    <span class="hljs-keyword">var</span> result = <span class="hljs-keyword">new</span> java.util.ArrayList(invokers.size());\n    <span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i &lt; invokers.size(); i ++) {\n        <span class="hljs-keyword">if</span> (<span class="hljs-string">"10.20.153.10"</span>.equals(invokers.get(i).getUrl().getHost())) {\n            result.add(invokers.get(i));\n        }\n    }\n    <span class="hljs-keyword">return</span> result;\n} (invokers)); <span class="hljs-comment">// 表示立即执行方法</span>\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p><code>2.2.0</code> 以上版本支持 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>路由规则扩展点:<a href="http://dubbo.apache.org/books/dubbo-dev-book/impls/router.html">路由扩展</a> <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn3" class="footnote-item"><p>注意:一个服务只能有一条白名单规则,否则两条规则交叉,就都被筛选掉了 <a href="#fnref3" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn4" class="footnote-item"><p>注意:脚本没有沙箱约束,可执行任意代码,存在后门风险 <a href="#fnref4" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/demos/serialization.md",__html:'<h1>在Dubbo中使用高效的Java序列化(Kryo和FST)</h1>\n<h2>启用Kryo和FST</h2>\n<p>使用Kryo和FST非常简单,只需要在dubbo RPC的XML配置中添加一个属性即可:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">serialization</span>=<span class="hljs-string">"kryo"</span>/&gt;</span>\n</code></pre>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">serialization</span>=<span class="hljs-string">"fst"</span>/&gt;</span>\n</code></pre>\n<h2>注册被序列化类</h2>\n<p>要让Kryo和FST完全发挥出高性能,最好将那些需要被序列化的类注册到dubbo系统中,例如,我们可以实现如下回调接口:</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">SerializationOptimizerImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">SerializationOptimizer</span> </span>{\n\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Collection&lt;Class&gt; <span class="hljs-title">getSerializableClasses</span><span class="hljs-params">()</span> </span>{\n        List&lt;Class&gt; classes = <span class="hljs-keyword">new</span> LinkedList&lt;Class&gt;();\n        classes.add(BidRequest.class);\n        classes.add(BidResponse.class);\n        classes.add(Device.class);\n        classes.add(Geo.class);\n        classes.add(Impression.class);\n        classes.add(SeatBid.class);\n        <span class="hljs-keyword">return</span> classes;\n    }\n}\n</code></pre>\n<p>然后在XML配置中添加:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">serialization</span>=<span class="hljs-string">"kryo"</span> <span class="hljs-attr">optimizer</span>=<span class="hljs-string">"com.alibaba.dubbo.demo.SerializationOptimizerImpl"</span>/&gt;</span>\n</code></pre>\n<p>在注册这些类后,序列化的性能可能被大大提升,特别针对小数量的嵌套对象的时候。</p>\n<p>当然,在对一个类做序列化的时候,可能还级联引用到很多类,比如Java集合类。针对这种情况,我们已经自动将JDK中的常用类进行了注册,所以你不需要重复注册它们(当然你重复注册了也没有任何影响),包括:</p>\n<pre><code>GregorianCalendar\nInvocationHandler\nBigDecimal\nBigInteger\nPattern\nBitSet\nURI\nUUID\nHashMap\nArrayList\nLinkedList\nHashSet\nTreeSet\nHashtable\nDate\nCalendar\nConcurrentHashMap\nSimpleDateFormat\nVector\nBitSet\nStringBuffer\nStringBuilder\nObject\nObject[]\nString[]\nbyte[]\nchar[]\nint[]\nfloat[]\ndouble[]\n</code></pre>\n<p>由于注册被序列化的类仅仅是出于性能优化的目的,所以即使你忘记注册某些类也没有关系。事实上,即使不注册任何类,Kryo和FST的性能依然普遍优于hessian和dubbo序列化。</p>\n'},{filename:"user/demos/service-container.md",__html:'<h1>服务容器</h1>\n<p>服务容器是一个 standalone 的启动程序,因为后台服务不需要 Tomcat 或 JBoss 等 Web 容器的功能,如果硬要用 Web 容器去加载服务提供方,增加复杂性,也浪费资源。</p>\n<p>服务容器只是一个简单的 Main 方法,并加载一个简单的 Spring 容器,用于暴露服务。</p>\n<p>服务容器的加载内容可以扩展,内置了 spring, jetty, log4j 等加载,可通过<a href="http://dubbo.apache.org/books/dubbo-dev-book/impls/container.html">容器扩展点</a>进行扩展。配置配在 java 命令的 -D 参数或者 <code>dubbo.properties</code> 中。</p>\n<h2>容器类型</h2>\n<h3>Spring Container</h3>\n<ul>\n<li>\n<p>自动加载 <code>META-INF/spring</code> 目录下的所有 Spring 配置。</p>\n</li>\n<li>\n<p>配置 spring 配置加载位置:</p>\n<pre><code class="language-properties"></code></pre>\n</li>\n</ul>\n<p>dubbo.spring.config=classpath*:META-INF/spring/*.xml</p>\n<pre><code>\n### Jetty Container\n\n* 启动一个内嵌 Jetty,用于汇报状态。\n* 配置:\n    * `dubbo.jetty.port=8080`:配置 jetty 启动端口\n    * `dubbo.jetty.directory=/foo/bar`:配置可通过 jetty 直接访问的目录,用于存放静态文件\n    * `dubbo.jetty.page=log,status,system`:配置显示的页面,缺省加载所有页面\n\n\n### Log4j Container\n\n* 自动配置 log4j 的配置,在多进程启动时,自动给日志文件按进程分目录。\n* 配置:\n    * `dubbo.log4j.file=/foo/bar.log`:配置日志文件路径\n    * `dubbo.log4j.level=WARN`:配置日志级别\n    * `dubbo.log4j.subdirectory=20880`:配置日志子目录,用于多进程启动,避免冲突\n\n## 容器启动\n\n缺省只加载 spring\n\n```sh\njava com.alibaba.dubbo.container.Main\n</code></pre>\n<p>通过 main 函数参数传入要加载的容器</p>\n<pre><code class="language-sh">java com.alibaba.dubbo.container.Main spring jetty log4j\n</code></pre>\n<p>通过 JVM 启动参数传入要加载的容器</p>\n<pre><code class="language-sh">java com.alibaba.dubbo.container.Main -Ddubbo.container=spring,jetty,log4j\n</code></pre>\n<p>通过 classpath 下的 <code>dubbo.properties</code> 配置传入要加载的容器</p>\n<pre><code>dubbo.container=spring,jetty,log4j\n</code></pre>\n'},{filename:"user/demos/service-downgrade.md",__html:'<h1>服务降级</h1>\n<p>可以通过服务降级功能 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> 临时屏蔽某个出错的非关键服务,并定义降级后的返回策略。</p>\n<p>向注册中心写入动态配置覆盖规则:</p>\n<pre><code class="language-java">RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();\nRegistry registry = registryFactory.getRegistry(URL.valueOf(<span class="hljs-string">"zookeeper://10.20.153.10:2181"</span>));\nregistry.register(URL.valueOf(<span class="hljs-string">"override://0.0.0.0/com.foo.BarService?category=configurators&amp;dynamic=false&amp;application=foo&amp;mock=force:return+null"</span>));\n</code></pre>\n<p>其中:</p>\n<ul>\n<li><code>mock=force:return+null</code> 表示消费方对该服务的方法调用都直接返回 null 值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。</li>\n<li>还可以改为 <code>mock=fail:return+null</code> 表示消费方对该服务的方法调用在失败后,再返回 null 值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。</li>\n</ul>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p><code>2.2.0</code> 以上版本支持 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/demos/service-group.md",__html:'<h1>服务分组</h1>\n<p>当一个接口有多种实现时,可以用 group 区分。</p>\n<h2>服务</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">group</span>=<span class="hljs-string">"feedback"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.xxx.IndexService"</span> /&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">group</span>=<span class="hljs-string">"member"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.xxx.IndexService"</span> /&gt;</span>\n</code></pre>\n<h2>引用</h2>\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">"feedbackIndexService"</span> <span class="hljs-attr">group</span>=<span class="hljs-string">"feedback"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.xxx.IndexService"</span> /&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"memberIndexService"</span> <span class="hljs-attr">group</span>=<span class="hljs-string">"member"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.xxx.IndexService"</span> /&gt;</span>\n</code></pre>\n<p>任意组 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>:</p>\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">"barService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">group</span>=<span class="hljs-string">"*"</span> /&gt;</span>\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p><code>2.2.0</code> 以上版本支持,总是只调一个可用组的实现 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/demos/static-service.md",__html:'<h1>静态服务</h1>\n<p>有时候希望人工管理服务提供者的上线和下线,此时需将注册中心标识为非动态管理模式。</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.141.150:9090"</span> <span class="hljs-attr">dynamic</span>=<span class="hljs-string">"false"</span> /&gt;</span>\n</code></pre>\n<p>或者</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.141.150:9090?dynamic=false"</span> /&gt;</span>\n</code></pre>\n<p>服务提供者初次注册时为禁用状态,需人工启用。断线时,将不会被自动删除,需人工禁用。</p>\n<p>如果是一个第三方独立提供者,比如 memcached,可以直接向注册中心写入提供者地址信息,消费者正常使用 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>:</p>\n<pre><code class="language-java">RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();\nRegistry registry = registryFactory.getRegistry(URL.valueOf(<span class="hljs-string">"zookeeper://10.20.153.10:2181"</span>));\nregistry.register(URL.valueOf(<span class="hljs-string">"memcached://10.20.153.11/com.foo.BarService?category=providers&amp;dynamic=false&amp;application=foo"</span>));\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>通常由脚本监控中心页面等调用 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/demos/stickiness.md",__html:'<h1>粘滞连接</h1>\n<p>粘滞连接用于有状态服务,尽可能让客户端总是向同一提供者发起调用,除非该提供者挂了,再连另一台。</p>\n<p>粘滞连接将自动开启<a href="./lazy-connect.md">延迟连接</a>,以减少长连接数。</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">sticky</span>=<span class="hljs-string">"true"</span> /&gt;</span>\n</code></pre>\n'},{filename:"user/demos/subscribe-only.md",__html:'<h1>只订阅</h1>\n<p>为方便开发测试,经常会在线下共用一个所有服务可用的注册中心,这时,如果一个正在开发中的服务提供者注册,可能会影响消费者不能正常运行。</p>\n<p>可以让服务提供者开发方,只订阅服务(开发的服务可能依赖其它服务),而不注册正在开发的服务,通过直连测试正在开发的服务。</p>\n<p><img src="../sources/images/subscribe-only.jpg" alt="/user-guide/images/subscribe-only.jpg"></p>\n<p>禁用注册配置</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.153.10:9090"</span> <span class="hljs-attr">register</span>=<span class="hljs-string">"false"</span> /&gt;</span>\n</code></pre>\n<p>或者</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.153.10:9090?register=false"</span> /&gt;</span>\n</code></pre>\n'},{filename:"user/demos/thread-model.md",__html:'<h1>线程模型</h1>\n<p>如果事件处理的逻辑能迅速完成,并且不会发起新的 IO 请求,比如只是在内存中记个标识,则直接在 IO 线程上处理更快,因为减少了线程池调度。</p>\n<p>但如果事件处理逻辑较慢,或者需要发起新的 IO 请求,比如需要查询数据库,则必须派发到线程池,否则 IO 线程阻塞,将导致不能接收其它请求。</p>\n<p>如果用 IO 线程处理事件,又在事件处理过程中发起新的 IO 请求,比如在连接事件中发起登录请求,会报“可能引发死锁”异常,但不会真死锁。</p>\n<p><img src="../sources/images/dubbo-protocol.jpg" alt="dubbo-protocol"></p>\n<p>因此,需要通过不同的派发策略和不同的线程池配置的组合来应对不同的场景:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">dispatcher</span>=<span class="hljs-string">"all"</span> <span class="hljs-attr">threadpool</span>=<span class="hljs-string">"fixed"</span> <span class="hljs-attr">threads</span>=<span class="hljs-string">"100"</span> /&gt;</span>\n</code></pre>\n<p>Dispatcher</p>\n<ul>\n<li><code>all</code> 所有消息都派发到线程池,包括请求,响应,连接事件,断开事件,心跳等。</li>\n<li><code>direct</code> 所有消息都不派发到线程池,全部在 IO 线程上直接执行。</li>\n<li><code>message</code> 只有请求响应消息派发到线程池,其它连接断开事件,心跳等消息,直接在 IO 线程上执行。</li>\n<li><code>execution</code> 只请求消息派发到线程池,不含响应,响应和其它连接断开事件,心跳等消息,直接在 IO 线程上执行。</li>\n<li><code>connection</code> 在 IO 线程上,将连接断开事件放入队列,有序逐个执行,其它消息派发到线程池。</li>\n</ul>\n<p>ThreadPool</p>\n<ul>\n<li><code>fixed</code> 固定大小线程池,启动时建立线程,不关闭,一直持有。(缺省)</li>\n<li><code>cached</code> 缓存线程池,空闲一分钟自动删除,需要时重建。</li>\n<li><code>limited</code> 可伸缩线程池,但池中的线程数只会增长不会收缩。只增长不收缩的目的是为了避免收缩时突然来了大流量引起的性能问题。</li>\n<li><code>eager</code> 优先创建<code>Worker</code>线程池。在任务数量大于<code>corePoolSize</code>但是小于<code>maximumPoolSize</code>时,优先创建<code>Worker</code>来处理任务。当任务数量大于<code>maximumPoolSize</code>时,将任务放入阻塞队列中。阻塞队列充满时抛出<code>RejectedExecutionException</code>。(相比于<code>cached</code>:<code>cached</code>在任务数量超过<code>maximumPoolSize</code>时直接抛出异常而不是将任务放入阻塞队列)</li>\n</ul>\n'},{filename:"user/demos/token-authorization.md",__html:'<h1>令牌验证</h1>\n<p>通过令牌验证在注册中心控制权限,以决定要不要下发令牌给消费者,可以防止消费者绕过注册中心访问提供者,另外通过注册中心可灵活改变授权方式,而不需修改或升级提供者</p>\n<p><img src="../sources/images/dubbo-token.jpg" alt="/user-guide/images/dubbo-token.jpg"></p>\n<p>可以全局设置开启令牌验证:</p>\n<pre><code class="language-xml"><span class="hljs-comment">&lt;!--随机token令牌,使用UUID生成--&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">token</span>=<span class="hljs-string">"true"</span> /&gt;</span>\n</code></pre>\n<p>或</p>\n<pre><code class="language-xml"><span class="hljs-comment">&lt;!--固定token令牌,相当于密码--&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">token</span>=<span class="hljs-string">"123456"</span> /&gt;</span>\n</code></pre>\n<p>也可在服务级别设置:</p>\n<pre><code class="language-xml"><span class="hljs-comment">&lt;!--随机token令牌,使用UUID生成--&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">token</span>=<span class="hljs-string">"true"</span> /&gt;</span>\n</code></pre>\n<p>或</p>\n<pre><code class="language-xml"><span class="hljs-comment">&lt;!--固定token令牌,相当于密码--&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">token</span>=<span class="hljs-string">"123456"</span> /&gt;</span>\n</code></pre>\n<p>还可在协议级别设置:</p>\n<pre><code class="language-xml"><span class="hljs-comment">&lt;!--随机token令牌,使用UUID生成--&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">token</span>=<span class="hljs-string">"true"</span> /&gt;</span>\n</code></pre>\n<p>或</p>\n<pre><code class="language-xml"><span class="hljs-comment">&lt;!--固定token令牌,相当于密码--&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">token</span>=<span class="hljs-string">"123456"</span> /&gt;</span>\n</code></pre>\n'},{filename:"user/dependencies.md",__html:'<h1>依赖</h1>\n<h2>必须依赖</h2>\n<p>JDK 1.6+ <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup></p>\n<h2>缺省依赖</h2>\n<p>通过 <code>mvn dependency:tree &gt; dep.log</code> 命令分析,Dubbo 缺省依赖以下三方库:</p>\n<pre><code>[INFO] +- com.alibaba:dubbo:jar:2.5.9-SNAPSHOT:compile\n[INFO] |  +- org.springframework:spring-context:jar:4.3.10.RELEASE:compile\n[INFO] |  +- org.javassist:javassist:jar:3.21.0-GA:compile\n[INFO] |  \\- org.jboss.netty:netty:jar:3.2.5.Final:compile\n</code></pre>\n<p>这里所有依赖都是换照 Dubbo 缺省配置选的,这些缺省值是基于稳定性和性能考虑的。</p>\n<ul>\n<li>javassist.jar <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>: 如果 <code>&lt;dubbo:provider proxy=&quot;jdk&quot; /&gt;</code> 或 <code>&lt;dubbo:consumer proxy=&quot;jdk&quot; /&gt;</code>,以及 <code>&lt;dubbo:application compiler=&quot;jdk&quot; /&gt;</code>,则不需要。</li>\n<li>spring-context.jar <sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup>: 如果用 <code>ServiceConfig</code> 和 <code>ReferenceConfig</code> 的 API 调用,则不需要。</li>\n<li>netty.jar <sup class="footnote-ref"><a href="#fn4" id="fnref4">[4]</a></sup>: 如果 <code>&lt;dubbo:protocol server=&quot;mina&quot;/&gt;</code> 或 <code>&lt;dubbo:protocol server=&quot;grizzly&quot;/&gt;</code>,则换成 mina.jar 或 grizzly.jar。如果 <code>&lt;protocol name=&quot;rmi&quot;/&gt;</code>,则不需要。</li>\n</ul>\n<h2>可选依赖</h2>\n<p>以下依赖,在主动配置使用相应实现策略时用到,需自行加入依赖。</p>\n<ul>\n<li>netty-all 4.0.35.Final</li>\n<li>mina: 1.1.7</li>\n<li>grizzly: 2.1.4</li>\n<li>httpclient: 4.5.3</li>\n<li>hessian_lite: 3.2.1-fixed</li>\n<li>fastjson: 1.2.31</li>\n<li>zookeeper: 3.4.9</li>\n<li>jedis: 2.9.0</li>\n<li>xmemcached: 1.3.6</li>\n<li>hessian: 4.0.38</li>\n<li>jetty: 6.1.26</li>\n<li>hibernate-validator: 5.4.1.Final</li>\n<li>zkclient: 0.2</li>\n<li>curator: 2.12.0</li>\n<li>cxf: 3.0.14</li>\n<li>thrift: 0.8.0</li>\n<li>servlet: 3.0 <sup class="footnote-ref"><a href="#fn5" id="fnref5">[5]</a></sup></li>\n<li>validation-api: <a href="http://1.1.0.GA">1.1.0.GA</a> <sup class="footnote-ref"><a href="#fn5" id="fnref5:1">[5:1]</a></sup></li>\n<li>jcache: 1.0.0 <sup class="footnote-ref"><a href="#fn5" id="fnref5:2">[5:2]</a></sup></li>\n<li>javax.el: 3.0.1-b08 <sup class="footnote-ref"><a href="#fn5" id="fnref5:3">[5:3]</a></sup></li>\n<li>kryo: 4.0.1</li>\n<li>kryo-serializers: 0.42</li>\n<li>fst: 2.48-jdk-6</li>\n<li>resteasy: 3.0.19.Final</li>\n<li>tomcat-embed-core: 8.0.11</li>\n<li>slf4j: 1.7.25</li>\n<li>log4j: 1.2.16</li>\n</ul>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>理论上 Dubbo 可以只依赖 JDK,不依赖于任何三方库运行,只需配置使用 JDK 相关实现策略 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>字节码生成 <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn3" class="footnote-item"><p>配置解析 <a href="#fnref3" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn4" class="footnote-item"><p>网络传输 <a href="#fnref4" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn5" class="footnote-item"><p>JEE <a href="#fnref5" class="footnote-backref">↩︎</a> <a href="#fnref5:1" class="footnote-backref">↩︎</a> <a href="#fnref5:2" class="footnote-backref">↩︎</a> <a href="#fnref5:3" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/maturity.md",__html:"<h1>成熟度</h1>\n<h2>功能成熟度</h2>\n<table>\n<thead>\n<tr>\n<th>Feature</th>\n<th>Maturity</th>\n<th>Strength</th>\n<th>Problem</th>\n<th>Advise</th>\n<th>User</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>并发控制</td>\n<td>Tested</td>\n<td>并发控制</td>\n<td></td>\n<td>试用</td>\n<td></td>\n</tr>\n<tr>\n<td>连接控制</td>\n<td>Tested</td>\n<td>连接数控制</td>\n<td></td>\n<td>试用</td>\n<td></td>\n</tr>\n<tr>\n<td>直连提供者</td>\n<td>Tested</td>\n<td>点对点直连服务提供方,用于测试</td>\n<td></td>\n<td>测试环境使用</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>分组聚合</td>\n<td>Tested</td>\n<td>分组聚合返回值,用于菜单聚合等服务</td>\n<td>特殊场景使用</td>\n<td>可用于生产环境</td>\n<td></td>\n</tr>\n<tr>\n<td>参数验证</td>\n<td>Tested</td>\n<td>参数验证,JSR303验证框架集成</td>\n<td>对性能有影响</td>\n<td>试用</td>\n<td>LaiWang</td>\n</tr>\n<tr>\n<td>结果缓存</td>\n<td>Tested</td>\n<td>结果缓存,用于加速请求</td>\n<td></td>\n<td>试用</td>\n<td></td>\n</tr>\n<tr>\n<td>泛化引用</td>\n<td>Stable</td>\n<td>泛化调用,无需业务接口类进行远程调用,用于测试平台,开放网关桥接等</td>\n<td></td>\n<td>可用于生产环境</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>泛化实现</td>\n<td>Stable</td>\n<td>泛化实现,无需业务接口类实现任意接口,用于Mock平台</td>\n<td></td>\n<td>可用于生产环境</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>回声测试</td>\n<td>Tested</td>\n<td>回声测试</td>\n<td></td>\n<td>试用</td>\n<td></td>\n</tr>\n<tr>\n<td>隐式传参</td>\n<td>Stable</td>\n<td>附加参数</td>\n<td></td>\n<td>可用于生产环境</td>\n<td></td>\n</tr>\n<tr>\n<td>异步调用</td>\n<td>Tested</td>\n<td>不可靠异步调用</td>\n<td></td>\n<td>试用</td>\n<td></td>\n</tr>\n<tr>\n<td>本地调用</td>\n<td>Tested</td>\n<td>本地调用</td>\n<td></td>\n<td>试用</td>\n<td></td>\n</tr>\n<tr>\n<td>参数回调</td>\n<td>Tested</td>\n<td>参数回调</td>\n<td>特殊场景使用</td>\n<td>试用</td>\n<td>Registry</td>\n</tr>\n<tr>\n<td>事件通知</td>\n<td>Tested</td>\n<td>事件通知,在远程调用执行前后触发</td>\n<td></td>\n<td>试用</td>\n<td></td>\n</tr>\n<tr>\n<td>本地存根</td>\n<td>Stable</td>\n<td>在客户端执行部分逻辑</td>\n<td></td>\n<td>可用于生产环境</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>本地伪装</td>\n<td>Stable</td>\n<td>伪造返回结果,可在失败时执行,或直接执行,用于服务降级</td>\n<td>需注册中心支持</td>\n<td>可用于生产环境</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>延迟暴露</td>\n<td>Stable</td>\n<td>延迟暴露服务,用于等待应用加载warmup数据,或等待spring加载完成</td>\n<td></td>\n<td>可用于生产环境</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>延迟连接</td>\n<td>Tested</td>\n<td>延迟建立连接,调用时建立</td>\n<td></td>\n<td>试用</td>\n<td>Registry</td>\n</tr>\n<tr>\n<td>粘滞连接</td>\n<td>Tested</td>\n<td>粘滞连接,总是向同一个提供方发起请求,除非此提供方挂掉,再切换到另一台</td>\n<td></td>\n<td>试用</td>\n<td>Registry</td>\n</tr>\n<tr>\n<td>令牌验证</td>\n<td>Tested</td>\n<td>令牌验证,用于服务授权</td>\n<td>需注册中心支持</td>\n<td>试用</td>\n<td></td>\n</tr>\n<tr>\n<td>路由规则</td>\n<td>Tested</td>\n<td>动态决定调用关系</td>\n<td>需注册中心支持</td>\n<td>试用</td>\n<td></td>\n</tr>\n<tr>\n<td>配置规则</td>\n<td>Tested</td>\n<td>动态下发配置,实现功能的开关</td>\n<td>需注册中心支持</td>\n<td>试用</td>\n<td></td>\n</tr>\n<tr>\n<td>访问日志</td>\n<td>Tested</td>\n<td>访问日志,用于记录调用信息</td>\n<td>本地存储,影响性能,受磁盘大小限制</td>\n<td>试用</td>\n<td></td>\n</tr>\n<tr>\n<td>分布式事务</td>\n<td>Research</td>\n<td>JTA/XA三阶段提交事务</td>\n<td>不稳定</td>\n<td>不可用</td>\n<td></td>\n</tr>\n</tbody>\n</table>\n<h2>策略成熟度</h2>\n<table>\n<thead>\n<tr>\n<th>Feature</th>\n<th>Maturity</th>\n<th>Strength</th>\n<th>Problem</th>\n<th>Advise</th>\n<th>User</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Zookeeper注册中心</td>\n<td>Stable</td>\n<td>支持基于网络的集群方式,有广泛周边开源产品,建议使用dubbo-2.3.3以上版本(推荐使用)</td>\n<td>依赖于Zookeeper的稳定性</td>\n<td>可用于生产环境</td>\n<td></td>\n</tr>\n<tr>\n<td>Redis注册中心</td>\n<td>Stable</td>\n<td>支持基于客户端双写的集群方式,性能高</td>\n<td>要求服务器时间同步,用于检查心跳过期脏数据</td>\n<td>可用于生产环境</td>\n<td></td>\n</tr>\n<tr>\n<td>Multicast注册中心</td>\n<td>Tested</td>\n<td>去中心化,不需要安装注册中心</td>\n<td>依赖于网络拓普和路由,跨机房有风险</td>\n<td>小规模应用或开发测试环境</td>\n<td></td>\n</tr>\n<tr>\n<td>Simple注册中心</td>\n<td>Tested</td>\n<td>Dogfooding,注册中心本身也是一个标准的RPC服务</td>\n<td>没有集群支持,可能单点故障</td>\n<td>试用</td>\n<td></td>\n</tr>\n<tr>\n<td>Feature</td>\n<td>Maturity</td>\n<td>Strength</td>\n<td>Problem</td>\n<td>Advise</td>\n<td>User</td>\n</tr>\n<tr>\n<td>Simple监控中心</td>\n<td>Stable</td>\n<td>支持JFreeChart统计报表</td>\n<td>没有集群支持,可能单点故障,但故障后不影响RPC运行</td>\n<td>可用于生产环境</td>\n<td></td>\n</tr>\n<tr>\n<td>Feature</td>\n<td>Maturity</td>\n<td>Strength</td>\n<td>Problem</td>\n<td>Advise</td>\n<td>User</td>\n</tr>\n<tr>\n<td>Dubbo协议</td>\n<td>Stable</td>\n<td>采用NIO复用单一长连接,并使用线程池并发处理请求,减少握手和加大并发效率,性能较好(推荐使用)</td>\n<td>在大文件传输时,单一连接会成为瓶颈</td>\n<td>可用于生产环境</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>Rmi协议</td>\n<td>Stable</td>\n<td>可与原生RMI互操作,基于TCP协议</td>\n<td>偶尔会连接失败,需重建Stub</td>\n<td>可用于生产环境</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>Hessian协议</td>\n<td>Stable</td>\n<td>可与原生Hessian互操作,基于HTTP协议</td>\n<td>需hessian.jar支持,http短连接的开销大</td>\n<td>可用于生产环境</td>\n<td></td>\n</tr>\n<tr>\n<td>Feature</td>\n<td>Maturity</td>\n<td>Strength</td>\n<td>Problem</td>\n<td>Advise</td>\n<td>User</td>\n</tr>\n<tr>\n<td>Netty Transporter</td>\n<td>Stable</td>\n<td>JBoss的NIO框架,性能较好(推荐使用)</td>\n<td>一次请求派发两种事件,需屏蔽无用事件</td>\n<td>可用于生产环境</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>Mina Transporter</td>\n<td>Stable</td>\n<td>老牌NIO框架,稳定</td>\n<td>待发送消息队列派发不及时,大压力下,会出现FullGC</td>\n<td>可用于生产环境</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>Grizzly Transporter</td>\n<td>Tested</td>\n<td>Sun的NIO框架,应用于GlassFish服务器中</td>\n<td>线程池不可扩展,Filter不能拦截下一Filter</td>\n<td>试用</td>\n<td></td>\n</tr>\n<tr>\n<td>Feature</td>\n<td>Maturity</td>\n<td>Strength</td>\n<td>Problem</td>\n<td>Advise</td>\n<td>User</td>\n</tr>\n<tr>\n<td>Hessian Serialization</td>\n<td>Stable</td>\n<td>性能较好,多语言支持(推荐使用)</td>\n<td>Hessian的各版本兼容性不好,可能和应用使用的Hessian冲突,Dubbo内嵌了hessian3.2.1的源码</td>\n<td>可用于生产环境</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>Dubbo Serialization</td>\n<td>Tested</td>\n<td>通过不传送POJO的类元信息,在大量POJO传输时,性能较好</td>\n<td>当参数对象增加字段时,需外部文件声明</td>\n<td>试用</td>\n<td></td>\n</tr>\n<tr>\n<td>Json Serialization</td>\n<td>Tested</td>\n<td>纯文本,可跨语言解析,缺省采用FastJson解析</td>\n<td>性能较差</td>\n<td>试用</td>\n<td></td>\n</tr>\n<tr>\n<td>Java Serialization</td>\n<td>Stable</td>\n<td>Java原生支持</td>\n<td>性能较差</td>\n<td>可用于生产环境</td>\n<td></td>\n</tr>\n<tr>\n<td>Feature</td>\n<td>Maturity</td>\n<td>Strength</td>\n<td>Problem</td>\n<td>Advise</td>\n<td>User</td>\n</tr>\n<tr>\n<td>Javassist ProxyFactory</td>\n<td>Stable</td>\n<td>通过字节码生成代替反射,性能比较好(推荐使用)</td>\n<td>依赖于javassist.jar包,占用JVM的Perm内存,Perm可能要设大一些:java -XX:PermSize=128m</td>\n<td>可用于生产环境</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>Jdk ProxyFactory</td>\n<td>Stable</td>\n<td>JDK原生支持</td>\n<td>性能较差</td>\n<td>可用于生产环境</td>\n<td></td>\n</tr>\n<tr>\n<td>Feature</td>\n<td>Maturity</td>\n<td>Strength</td>\n<td>Problem</td>\n<td>Advise</td>\n<td>User</td>\n</tr>\n<tr>\n<td>Failover Cluster</td>\n<td>Stable</td>\n<td>失败自动切换,当出现失败,重试其它服务器,通常用于读操作(推荐使用)</td>\n<td>重试会带来更长延迟</td>\n<td>可用于生产环境</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>Failfast Cluster</td>\n<td>Stable</td>\n<td>快速失败,只发起一次调用,失败立即报错,通常用于非幂等性的写操作</td>\n<td>如果有机器正在重启,可能会出现调用失败</td>\n<td>可用于生产环境</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>Failsafe Cluster</td>\n<td>Stable</td>\n<td>失败安全,出现异常时,直接忽略,通常用于写入审计日志等操作</td>\n<td>调用信息丢失</td>\n<td>可用于生产环境</td>\n<td>Monitor</td>\n</tr>\n<tr>\n<td>Failback Cluster</td>\n<td>Tested</td>\n<td>失败自动恢复,后台记录失败请求,定时重发,通常用于消息通知操作</td>\n<td>不可靠,重启丢失</td>\n<td>可用于生产环境</td>\n<td>Registry</td>\n</tr>\n<tr>\n<td>Forking Cluster</td>\n<td>Tested</td>\n<td>并行调用多个服务器,只要一个成功即返回,通常用于实时性要求较高的读操作</td>\n<td>需要浪费更多服务资源</td>\n<td>可用于生产环境</td>\n<td></td>\n</tr>\n<tr>\n<td>Broadcast Cluster</td>\n<td>Tested</td>\n<td>广播调用所有提供者,逐个调用,任意一台报错则报错,通常用于更新提供方本地状态</td>\n<td>速度慢,任意一台报错则报错</td>\n<td>可用于生产环境</td>\n<td></td>\n</tr>\n<tr>\n<td>Feature</td>\n<td>Maturity</td>\n<td>Strength</td>\n<td>Problem</td>\n<td>Advise</td>\n<td>User</td>\n</tr>\n<tr>\n<td>Random LoadBalance</td>\n<td>Stable</td>\n<td>随机,按权重设置随机概率(推荐使用)</td>\n<td>在一个截面上碰撞的概率高,重试时,可能出现瞬间压力不均</td>\n<td>可用于生产环境</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>RoundRobin LoadBalance</td>\n<td>Stable</td>\n<td>轮循,按公约后的权重设置轮循比率</td>\n<td>存在慢的机器累积请求问题,极端情况可能产生雪崩</td>\n<td>可用于生产环境</td>\n<td></td>\n</tr>\n<tr>\n<td>LeastActive LoadBalance</td>\n<td>Stable</td>\n<td>最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差,使慢的机器收到更少请求</td>\n<td>不支持权重,在容量规划时,不能通过权重把压力导向一台机器压测容量</td>\n<td>可用于生产环境</td>\n<td></td>\n</tr>\n<tr>\n<td>ConsistentHash LoadBalance</td>\n<td>Stable</td>\n<td>一致性Hash,相同参数的请求总是发到同一提供者,当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动</td>\n<td>压力分摊不均</td>\n<td>可用于生产环境</td>\n<td></td>\n</tr>\n<tr>\n<td>Feature</td>\n<td>Maturity</td>\n<td>Strength</td>\n<td>Problem</td>\n<td>Advise</td>\n<td>User</td>\n</tr>\n<tr>\n<td>条件路由规则</td>\n<td>Stable</td>\n<td>基于条件表达式的路由规则,功能简单易用</td>\n<td>有些复杂多分支条件情况,规则很难描述</td>\n<td>可用于生产环境</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>脚本路由规则</td>\n<td>Tested</td>\n<td>基于脚本引擎的路由规则,功能强大</td>\n<td>没有运行沙箱,脚本能力过于强大,可能成为后门</td>\n<td>试用</td>\n<td></td>\n</tr>\n<tr>\n<td>Feature</td>\n<td>Maturity</td>\n<td>Strength</td>\n<td>Problem</td>\n<td>Advise</td>\n<td>User</td>\n</tr>\n<tr>\n<td>Spring Container</td>\n<td>Stable</td>\n<td>自动加载META-INF/spring目录下的所有Spring配置</td>\n<td></td>\n<td>可用于生产环境</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>Jetty Container</td>\n<td>Stable</td>\n<td>启动一个内嵌Jetty,用于汇报状态</td>\n<td>大量访问页面时,会影响服务器的线程和内存</td>\n<td>可用于生产环境</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>Log4j Container</td>\n<td>Stable</td>\n<td>自动配置log4j的配置,在多进程启动时,自动给日志文件按进程分目录</td>\n<td>用户不能控制log4j的配置,不灵活</td>\n<td>可用于生产环境</td>\n<td>Alibaba</td>\n</tr>\n</tbody>\n</table>\n"},{filename:"user/perf-test.md",__html:'<h1>性能测试报告</h1>\n<h2>测试说明</h2>\n<ol start="0">\n<li>本次性能测试,测试了 dubbo 2.0 所有支持的协议在不同大小和数据类型下的表现,并与 dubbo 1.0 进行了对比。</li>\n<li>整体性能相比 1.0 有了提升,平均提升 10%,使用 dubbo 2.0 新增的 dubbo 序列化还能获得 10%~50% 的性能提升,详见下面的性能数据。</li>\n<li>稳定性测试中由于将底层通信框架从 mina 换成 netty,old 区对象的增长大大减少,50 小时运行,增长不到 200m,无 fullgc。</li>\n<li>存在的问题:在 50k 数据的时候 2.0 性能不如 1.0,怀疑可能是缓冲区设置的问题,下版本会进一步确认。</li>\n</ol>\n<h2>测试环境</h2>\n<h3>硬件部署与参数调整</h3>\n<table>\n<thead>\n<tr>\n<th>机型</th>\n<th>CPU</th>\n<th>内存</th>\n<th>网络</th>\n<th>磁盘</th>\n<th>内核</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Tecal BH620</td>\n<td>model name : Intel(R) Xeon(R) CPU           E5520  @ 2.27GHz cache size : 8192 KB processor_count : 16</td>\n<td>Total System Memory: 6G Hardware Memory Info:  Size: 4096MB</td>\n<td>eth0: Link is up at 1000 Mbps, full duplex. peth0: Link is up at 1000 Mbps, full duplex.</td>\n<td>/dev/sda: 597.9 GB</td>\n<td>2.6.18-128.el5xen x86_64</td>\n</tr>\n</tbody>\n</table>\n<h3>软件架构</h3>\n<table>\n<thead>\n<tr>\n<th>软件名称及版本</th>\n<th>关键参数</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>java version &quot;1.6.0_18&quot; Java(TM) SE Runtime Environment (build 1.6.0_18-b07) Java HotSpot(TM) 64-Bit Server VM (build 16.0-b13, mixed mode)</td>\n<td>-server -Xmx2g -Xms2g -Xmn256m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70</td>\n</tr>\n<tr>\n<td><a href="http://jboss-4.0.5.GA">jboss-4.0.5.GA</a></td>\n<td></td>\n</tr>\n<tr>\n<td>httpd-2.0.61</td>\n<td>KeepAlive On MaxKeepAliveRequests 100000 KeepAliveTimeout 180 MaxRequestsPerChild 1000000 <IfModule worker.c>         StartServers 5         MaxClients 1024         MinSpareThreads 25         MaxSpareThreads 75         ThreadsPerChild 64         ThreadLimit 128         ServerLimit 16 </IfModule></td>\n</tr>\n</tbody>\n</table>\n<h2>测试目的</h2>\n<h3>期望性能指标(量化)</h3>\n<table>\n<thead>\n<tr>\n<th>场景名称</th>\n<th>对应指标名称</th>\n<th>期望值范围</th>\n<th>实际值</th>\n<th>是否满足期望(是/否)</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>1k数据</td>\n<td>响应时间</td>\n<td>0.9ms</td>\n<td>0.79ms</td>\n<td>是</td>\n</tr>\n<tr>\n<td>1k数据</td>\n<td>TPS</td>\n<td>10000</td>\n<td>11994</td>\n<td>是</td>\n</tr>\n</tbody>\n</table>\n<h3>期望运行状况(非量化,可选)</h3>\n<ul>\n<li>2.0 性能不低于 1.0, 2.0 和 1.0 互调用的性能无明显下降。 除了 50k string 其余皆通过</li>\n<li>JVM 内存运行稳定,无 OOM,堆内存中无不合理的大对象的占用。通过</li>\n<li>CPU、内存、网络、磁盘、文件句柄占用平稳。通过</li>\n<li>无频繁线程锁,线程数平稳。通过</li>\n<li>业务线程负载均衡。通过</li>\n</ul>\n<h2>测试脚本</h2>\n<ol start="0">\n<li>\n<p>性能测试场景(10 并发)</p>\n<ul>\n<li>传入 1k String,不做任何处理,原样返回</li>\n<li>传入 50k String,不做任何处理,原样返回</li>\n<li>传入 200k String,不做任何处理,原样返回</li>\n<li>传入 1k POJO(嵌套的复杂 person 对象),不做任何处理,原样返回</li>\n</ul>\n<p>上述场景在 dubbo 1.0, dubbo 2.0(hessian2序列化), dubbo 2.0(dubbo序列化), rmi, hessian 3.2.0, http(json序列化) 进行 10 分钟的性能测试。主要考察序列化和网络 IO 的性能,因此服务端无任何业务逻辑。取 10 并发是考虑到 http 协议在高并发下对 CPU 的使用率较高可能会先打到瓶颈。</p>\n</li>\n<li>\n<p>并发场景(20 并发)\n传入 1k String,在服务器段循环 1w 次,每次重新生成一个随机数然后进行拼装。考察业务线程是否能够分配到每个 CPU 上。</p>\n</li>\n<li>\n<p>稳定性场景(20 并发)\n同时调用 1 个参数为 String(5k)方法,1 个参数为 person 对象的方法,1 个参数为 map(值为 3 个 person)的方法,持续运行 50 小时。</p>\n</li>\n<li>\n<p>高压力场景(20 并发)\n在稳定性场景的基础上,将提供者和消费者布置成均为 2 台(一台机器 2 个实例),且 String 的参数从 20byte 到 200k,每隔 10 分钟随机变换。</p>\n</li>\n</ol>\n<h2>测试结果</h2>\n<h3>场景名称:POJO 场景</h3>\n<table>\n<thead>\n<tr>\n<th>TPS成功平均值</th>\n<th>响应时间成功平均值(ms)</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>dubbo1 (hessian2序列化+mina)</td>\n<td>10813.5</td>\n</tr>\n<tr>\n<td>dubbo2 (hessian2序列化+netty)</td>\n<td>11994</td>\n</tr>\n<tr>\n<td>dubbo2 (dubbo序列化+netty)</td>\n<td>13620</td>\n</tr>\n<tr>\n<td>rmi</td>\n<td>2461.79</td>\n</tr>\n<tr>\n<td>hessian</td>\n<td>2417.7</td>\n</tr>\n<tr>\n<td>http(json序列化)</td>\n<td>8179.08</td>\n</tr>\n<tr>\n<td>2.0和1.0默认对比百分比</td>\n<td>10.92</td>\n</tr>\n<tr>\n<td>dubbo序列化相比hessian2序列化百分比</td>\n<td>13.56</td>\n</tr>\n</tbody>\n</table>\n<p>POJO TPS</p>\n<p><img src="./sources/images/pojotps.png" alt="pojotps.png"></p>\n<p>POJO Response</p>\n<p><img src="./sources/images/pojores.png" alt="pojores.png"></p>\n<h3>场景名称:1k string 场景</h3>\n<table>\n<thead>\n<tr>\n<th>TPS成功平均值</th>\n<th>响应时间成功平均值(ms)</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>dubbo1(hessian2序列化+mina)</td>\n<td>11940</td>\n</tr>\n<tr>\n<td>dubbo2 (hessian2序列化+netty)</td>\n<td>14402</td>\n</tr>\n<tr>\n<td>dubbo2 (dubbo序列化+netty)</td>\n<td>15096</td>\n</tr>\n<tr>\n<td>rmi</td>\n<td>11136.02</td>\n</tr>\n<tr>\n<td>hessian</td>\n<td>11426.83</td>\n</tr>\n<tr>\n<td>http(json序列化)</td>\n<td>8919.27</td>\n</tr>\n<tr>\n<td>2.0和1.0默认对比百分比</td>\n<td>20.62</td>\n</tr>\n<tr>\n<td>dubbo序列化相比hessian2序列化百分比</td>\n<td>4.82</td>\n</tr>\n</tbody>\n</table>\n<p>1k TPS</p>\n<p><img src="./sources/images/1ktps.png" alt="1ktps.png"></p>\n<p>1k Response</p>\n<p><img src="./sources/images/1kres.png" alt="1kres.png"></p>\n<h3>场景名称:50k string 场景</h3>\n<table>\n<thead>\n<tr>\n<th>TPS成功平均值</th>\n<th>响应时间成功平均值(ms)</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>dubbo1(hessian2序列化+mina</td>\n<td>1962.7</td>\n</tr>\n<tr>\n<td>dubbo2 (hessian2序列化+netty)</td>\n<td>1293</td>\n</tr>\n<tr>\n<td>dubbo2 (dubbo序列化+netty)</td>\n<td>1966</td>\n</tr>\n<tr>\n<td>rmi</td>\n<td>3349.88</td>\n</tr>\n<tr>\n<td>hessian</td>\n<td>1925.33</td>\n</tr>\n<tr>\n<td>http(json序列化)</td>\n<td>3247.1</td>\n</tr>\n<tr>\n<td>2.0和1.0默认对比百分比</td>\n<td>-34.12</td>\n</tr>\n<tr>\n<td>dubbo序列化相比hessian2序列化百分比</td>\n<td>52.05</td>\n</tr>\n</tbody>\n</table>\n<p>50K TPS</p>\n<p><img src="./sources/images/50ktps.png" alt="50ktps.png"></p>\n<p>50K Response</p>\n<p><img src="./sources/images/50kres.png" alt="50kres.png"></p>\n<h3>场景名称:200k string 场景</h3>\n<table>\n<thead>\n<tr>\n<th>TPS成功平均值</th>\n<th>响应时间成功平均值(ms)</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>dubbo1(hessian2序列化+mina)</td>\n<td>324.2</td>\n</tr>\n<tr>\n<td>dubbo2 (hessian2序列化+netty)</td>\n<td>362.92</td>\n</tr>\n<tr>\n<td>dubbo2 (dubbo序列化+netty)</td>\n<td>569.5</td>\n</tr>\n<tr>\n<td>rmi</td>\n<td>1031.28</td>\n</tr>\n<tr>\n<td>hessian</td>\n<td>628.06</td>\n</tr>\n<tr>\n<td>http(json序列化)</td>\n<td>1011.97</td>\n</tr>\n<tr>\n<td>2.0和1.0默认对比百分比</td>\n<td>11.94</td>\n</tr>\n<tr>\n<td>dubbo序列化相比hessian2序列化百分比</td>\n<td>56.92</td>\n</tr>\n</tbody>\n</table>\n<p>200K TPS</p>\n<p><img src="./sources/images/200ktps.png" alt="200ktps.png"></p>\n<p><strong>200K Response</strong></p>\n<p><img src="./sources/images/200kres.png" alt="200kres.png"></p>\n<h2>测试分析</h2>\n<h3>性能分析评估</h3>\n<p>Dubbo 2.0 的性能测试结论为通过,从性能、内存占用和稳定性上都有了提高和改进。由其是内存管理由于将 mina 换成netty,大大减少了 1.0 版本在高并发大数据下的内存大锯齿。</p>\n<h3>性能对比分析(新旧环境、不同数据量级等)</h3>\n<p>Dubbo 2.0 相比较Dubbo 1.0(默认使用的都是 hessian2 序列化)性能均有提升(除了50k String),详见第五章的性能数据。</p>\n<p>出于兼容性考虑默认的序列化方式和 1.0 保持一致使用 hessian2,如对性能有更高要求可以使用 dubbo 序列化,由其是在处理复杂对象时,在大数据量下能获得 50% 的提升(但此时已不建议使用 Dubbo 协议)。</p>\n<p>Dubbo 的设计目的是为了满足高并发小数据量的 rpc 调用,在大数据量下的性能表现并不好,建议使用 rmi 或 http 协议。</p>\n<h3>测试局限性分析(可选)</h3>\n<p>本次性能测试考察的是 dubbo 本身的性能,实际使用过程中的性能有待应用来验证。</p>\n<p>由于 dubbo 本身的性能占用都在毫秒级,占的基数很小,性能提升可能对应用整体的性能变化不大。</p>\n<p>由于邮件篇幅所限没有列出所有的监控图,如需获得可在大力神平台上查询。</p>\n'},{filename:"user/preface/architecture.md",__html:'<h1>架构</h1>\n<p><img src="../sources/images/dubbo-architecture.jpg" alt="dubbo-architucture"></p>\n<h5>节点角色说明</h5>\n<table>\n<thead>\n<tr>\n<th>节点</th>\n<th>角色说明</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>Provider</code></td>\n<td>暴露服务的服务提供方</td>\n</tr>\n<tr>\n<td><code>Consumer</code></td>\n<td>调用远程服务的服务消费方</td>\n</tr>\n<tr>\n<td><code>Registry</code></td>\n<td>服务注册与发现的注册中心</td>\n</tr>\n<tr>\n<td><code>Monitor</code></td>\n<td>统计服务的调用次数和调用时间的监控中心</td>\n</tr>\n<tr>\n<td><code>Container</code></td>\n<td>服务运行容器</td>\n</tr>\n</tbody>\n</table>\n<h5>调用关系说明</h5>\n<ol start="0">\n<li>服务容器负责启动,加载,运行服务提供者。</li>\n<li>服务提供者在启动时,向注册中心注册自己提供的服务。</li>\n<li>服务消费者在启动时,向注册中心订阅自己所需的服务。</li>\n<li>注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。</li>\n<li>服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。</li>\n<li>服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。</li>\n</ol>\n<p>Dubbo 架构具有以下几个特点,分别是连通性、健壮性、伸缩性、以及向未来架构的升级性。</p>\n<h2>连通性</h2>\n<ul>\n<li>注册中心负责服务地址的注册与查找,相当于目录服务,服务提供者和消费者只在启动时与注册中心交互,注册中心不转发请求,压力较小</li>\n<li>监控中心负责统计各服务调用次数,调用时间等,统计先在内存汇总后每分钟一次发送到监控中心服务器,并以报表展示</li>\n<li>服务提供者向注册中心注册其提供的服务,并汇报调用时间到监控中心,此时间不包含网络开销</li>\n<li>服务消费者向注册中心获取服务提供者地址列表,并根据负载算法直接调用提供者,同时汇报调用时间到监控中心,此时间包含网络开销</li>\n<li>注册中心,服务提供者,服务消费者三者之间均为长连接,监控中心除外</li>\n<li>注册中心通过长连接感知服务提供者的存在,服务提供者宕机,注册中心将立即推送事件通知消费者</li>\n<li>注册中心和监控中心全部宕机,不影响已运行的提供者和消费者,消费者在本地缓存了提供者列表</li>\n<li>注册中心和监控中心都是可选的,服务消费者可以直连服务提供者</li>\n</ul>\n<h2>健壮性</h2>\n<ul>\n<li>监控中心宕掉不影响使用,只是丢失部分采样数据</li>\n<li>数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务</li>\n<li>注册中心对等集群,任意一台宕掉后,将自动切换到另一台</li>\n<li>注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯</li>\n<li>服务提供者无状态,任意一台宕掉后,不影响使用</li>\n<li>服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复</li>\n</ul>\n<h2>伸缩性</h2>\n<ul>\n<li>注册中心为对等集群,可动态增加机器部署实例,所有客户端将自动发现新的注册中心</li>\n<li>服务提供者无状态,可动态增加机器部署实例,注册中心将推送新的服务提供者信息给消费者</li>\n</ul>\n<h2>升级性</h2>\n<p>当服务集群规模进一步扩大,带动IT治理结构进一步升级,需要实现动态部署,进行流动计算,现有分布式服务架构不会带来阻力。下图是未来可能的一种架构:</p>\n<p><img src="../sources/images/dubbo-architecture-future.jpg" alt="dubbo-architucture-futures"></p>\n<h5>节点角色说明</h5>\n<table>\n<thead>\n<tr>\n<th>节点</th>\n<th>角色说明</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>Deployer</code></td>\n<td>自动部署服务的本地代理</td>\n</tr>\n<tr>\n<td><code>Repository</code></td>\n<td>仓库用于存储服务应用发布包</td>\n</tr>\n<tr>\n<td><code>Scheduler</code></td>\n<td>调度中心基于访问压力自动增减服务提供者</td>\n</tr>\n<tr>\n<td><code>Admin</code></td>\n<td>统一管理控制台</td>\n</tr>\n<tr>\n<td><code>Registry</code></td>\n<td>服务注册与发现的注册中心</td>\n</tr>\n<tr>\n<td><code>Monitor</code></td>\n<td>统计服务的调用次数和调用时间的监控中心</td>\n</tr>\n</tbody>\n</table>\n'},{filename:"user/preface/background.md",__html:'<h1>背景</h1>\n<p>随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进。</p>\n<p><img src="../sources/images/dubbo-architecture-roadmap.jpg" alt="image"></p>\n<h4>单一应用架构</h4>\n<p>当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。此时,用于简化增删改查工作量的数据访问框架(ORM)是关键。</p>\n<h4>垂直应用架构</h4>\n<p>当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率。此时,用于加速前端页面开发的Web框架(MVC)是关键。</p>\n<h4>分布式服务架构</h4>\n<p>当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。</p>\n<h4>流动计算架构</h4>\n<p>当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键。</p>\n'},{filename:"user/preface/index.md",__html:"<h1>入门</h1>\n"},{filename:"user/preface/requirements.md",__html:'<h1>需求</h1>\n<p><img src="../sources/images/dubbo-service-governance.jpg" alt="image"></p>\n<p>在大规模服务化之前,应用可能只是通过 RMI 或 Hessian 等工具,简单的暴露和引用远程服务,通过配置服务的URL地址进行调用,通过 F5 等硬件进行负载均衡。</p>\n<p><strong>当服务越来越多时,服务 URL 配置管理变得非常困难,F5 硬件负载均衡器的单点压力也越来越大。</strong> 此时需要一个服务注册中心,动态的注册和发现服务,使服务的位置透明。并通过在消费方获取服务提供方地址列表,实现软负载均衡和 Failover,降低对 F5 硬件负载均衡器的依赖,也能减少部分成本。</p>\n<p><strong>当进一步发展,服务间依赖关系变得错踪复杂,甚至分不清哪个应用要在哪个应用之前启动,架构师都不能完整的描述应用的架构关系。</strong>  这时,需要自动画出应用间的依赖关系图,以帮助架构师理清理关系。</p>\n<p><strong>接着,服务的调用量越来越大,服务的容量问题就暴露出来,这个服务需要多少机器支撑?什么时候该加机器?</strong>  为了解决这些问题,第一步,要将服务现在每天的调用量,响应时间,都统计出来,作为容量规划的参考指标。其次,要可以动态调整权重,在线上,将某台机器的权重一直加大,并在加大的过程中记录响应时间的变化,直到响应时间到达阀值,记录此时的访问量,再以此访问量乘以机器数反推总容量。</p>\n<p>以上是 Dubbo 最基本的几个需求。</p>\n'},{filename:"user/preface/usage.md",__html:'<h1>用法</h1>\n<h2>本地服务 Spring 配置</h2>\n<p>local.xml:</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">“xxxService”</span> <span class="hljs-attr">class</span>=<span class="hljs-string">“com.xxx.XxxServiceImpl”</span> /&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">bean</span> <span class="hljs-attr">id</span>=<span class="hljs-string">“xxxAction”</span> <span class="hljs-attr">class</span>=<span class="hljs-string">“com.xxx.XxxAction”</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">property</span> <span class="hljs-attr">name</span>=<span class="hljs-string">“xxxService”</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">“xxxService”</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">bean</span>&gt;</span>\n</code></pre>\n<h2>远程服务 Spring 配置</h2>\n<p>在本地服务的基础上,只需做简单配置,即可完成远程化:</p>\n<ul>\n<li>将上面的 <code>local.xml</code> 配置拆分成两份,将服务定义部分放在服务提供方 <code>remote-provider.xml</code>,将服务引用部分放在服务消费方 <code>remote-consumer.xml</code>。</li>\n<li>并在提供方增加暴露服务配置 <code>&lt;dubbo:service&gt;</code>,在消费方增加引用服务配置 <code>&lt;dubbo:reference&gt;</code>。</li>\n</ul>\n<p>remote-provider.xml:</p>\n<pre><code class="language-xml"><span class="hljs-comment">&lt;!-- 和本地服务一样实现远程服务 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">bean</span> <span class="hljs-attr">id</span>=<span class="hljs-string">“xxxService”</span> <span class="hljs-attr">class</span>=<span class="hljs-string">“com.xxx.XxxServiceImpl”</span> /&gt;</span> \n<span class="hljs-comment">&lt;!-- 增加暴露远程服务配置 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">“com.xxx.XxxService”</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">“xxxService”</span> /&gt;</span> \n</code></pre>\n<p>remote-consumer.xml:</p>\n<pre><code class="language-xml"><span class="hljs-comment">&lt;!-- 增加引用远程服务配置 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">id</span>=<span class="hljs-string">“xxxService”</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">“com.xxx.XxxService”</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- 和本地服务一样使用远程服务 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">bean</span> <span class="hljs-attr">id</span>=<span class="hljs-string">“xxxAction”</span> <span class="hljs-attr">class</span>=<span class="hljs-string">“com.xxx.XxxAction”</span>&gt;</span> \n    <span class="hljs-tag">&lt;<span class="hljs-name">property</span> <span class="hljs-attr">name</span>=<span class="hljs-string">“xxxService”</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">“xxxService”</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">bean</span>&gt;</span>\n</code></pre>\n'},{filename:"user/quick-start.md",__html:'<h1>快速启动</h1>\n<p>Dubbo 采用全 Spring 配置方式,透明化接入应用,对应用没有任何 API 侵入,只需用 Spring 加载 Dubbo 的配置即可,Dubbo 基于 Spring 的 Schema 扩展进行加载。</p>\n<p>如果不想使用 Spring 配置,可以通过 <a href="./configuration/api.md">API 的方式</a> 进行调用。</p>\n<h2>服务提供者</h2>\n<p>完整安装步骤,请参见:<a href="../admin/install/provider-demo.md">示例提供者安装</a></p>\n<h3>定义服务接口</h3>\n<p>DemoService.java <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.alibaba.dubbo.demo;\n\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">DemoService</span> </span>{\n    <span class="hljs-function">String <span class="hljs-title">sayHello</span><span class="hljs-params">(String name)</span></span>;\n}\n</code></pre>\n<h3>在服务提供方实现接口</h3>\n<p>DemoServiceImpl.java <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>:</p>\n<pre><code class="language-java">\n<span class="hljs-keyword">package</span> com.alibaba.dubbo.demo.provider;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.demo.DemoService;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DemoServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">DemoService</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">sayHello</span><span class="hljs-params">(String name)</span> </span>{\n        <span class="hljs-keyword">return</span> <span class="hljs-string">"Hello "</span> + name;\n    }\n}\n</code></pre>\n<h3>用 Spring 配置声明暴露服务</h3>\n<p>provider.xml:</p>\n<pre><code class="language-xml"><span class="php"><span class="hljs-meta">&lt;?</span>xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"UTF-8"</span><span class="hljs-meta">?&gt;</span></span>\n<span class="hljs-tag">&lt;<span class="hljs-name">beans</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans"</span>\n    <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>\n    <span class="hljs-attr">xmlns:dubbo</span>=<span class="hljs-string">"http://dubbo.apache.org/schema/dubbo"</span>\n    <span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd        http://dubbo.apache.org/schema/dubbo        http://dubbo.apache.org/schema/dubbo/dubbo.xsd"</span>&gt;</span>\n \n    <span class="hljs-comment">&lt;!-- 提供方应用信息,用于计算依赖关系 --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:application</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"hello-world-app"</span>  /&gt;</span>\n \n    <span class="hljs-comment">&lt;!-- 使用multicast广播注册中心暴露服务地址 --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"multicast://224.5.6.7:1234"</span> /&gt;</span>\n \n    <span class="hljs-comment">&lt;!-- 用dubbo协议在20880端口暴露服务 --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"20880"</span> /&gt;</span>\n \n    <span class="hljs-comment">&lt;!-- 声明需要暴露的服务接口 --&gt;</span>\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.demo.DemoService"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"demoService"</span> /&gt;</span>\n \n    <span class="hljs-comment">&lt;!-- 和本地bean一样实现服务 --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">bean</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"demoService"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"com.alibaba.dubbo.demo.provider.DemoServiceImpl"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">beans</span>&gt;</span>\n</code></pre>\n<h3>加载 Spring 配置</h3>\n<p>Provider.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">import</span> org.springframework.context.support.ClassPathXmlApplicationContext;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Provider</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> Exception </span>{\n        ClassPathXmlApplicationContext context = <span class="hljs-keyword">new</span> ClassPathXmlApplicationContext(<span class="hljs-keyword">new</span> String[] {<span class="hljs-string">"http://10.20.160.198/wiki/display/dubbo/provider.xml"</span>});\n        context.start();\n        System.in.read(); <span class="hljs-comment">// 按任意键退出</span>\n    }\n}\n</code></pre>\n<h2>服务消费者</h2>\n<p>完整安装步骤,请参见:<a href="http://dubbo.apache.org/books/dubbo-admin-book/install/consumer-demo.html">示例消费者安装</a></p>\n<h3>通过 Spring 配置引用远程服务</h3>\n<p>consumer.xml:</p>\n<pre><code class="language-xml"><span class="php"><span class="hljs-meta">&lt;?</span>xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"UTF-8"</span><span class="hljs-meta">?&gt;</span></span>\n<span class="hljs-tag">&lt;<span class="hljs-name">beans</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans"</span>\n    <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>\n    <span class="hljs-attr">xmlns:dubbo</span>=<span class="hljs-string">"http://dubbo.apache.org/schema/dubbo"</span>\n    <span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd        http://dubbo.apache.org/schema/dubbo        http://dubbo.apache.org/schema/dubbo/dubbo.xsd"</span>&gt;</span>\n \n    <span class="hljs-comment">&lt;!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:application</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"consumer-of-helloworld-app"</span>  /&gt;</span>\n \n    <span class="hljs-comment">&lt;!-- 使用multicast广播注册中心暴露发现服务地址 --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"multicast://224.5.6.7:1234"</span> /&gt;</span>\n \n    <span class="hljs-comment">&lt;!-- 生成远程服务代理,可以和本地bean一样使用demoService --&gt;</span>\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">interface</span>=<span class="hljs-string">"com.alibaba.dubbo.demo.DemoService"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">beans</span>&gt;</span>\n</code></pre>\n<h3>加载Spring配置,并调用远程服务</h3>\n<p>Consumer.java <sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup>:</p>\n<pre><code class="language-java"><span class="hljs-keyword">import</span> org.springframework.context.support.ClassPathXmlApplicationContext;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.demo.DemoService;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Consumer</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> Exception </span>{\n        ClassPathXmlApplicationContext context = <span class="hljs-keyword">new</span> ClassPathXmlApplicationContext(<span class="hljs-keyword">new</span> String[] {<span class="hljs-string">"http://10.20.160.198/wiki/display/dubbo/consumer.xml"</span>});\n        context.start();\n        DemoService demoService = (DemoService)context.getBean(<span class="hljs-string">"demoService"</span>); <span class="hljs-comment">// 获取远程服务代理</span>\n        String hello = demoService.sayHello(<span class="hljs-string">"world"</span>); <span class="hljs-comment">// 执行远程方法</span>\n        System.out.println( hello ); <span class="hljs-comment">// 显示调用结果</span>\n    }\n}\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>该接口需单独打包,在服务提供方和消费方共享 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>对服务消费方隐藏实现 <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn3" class="footnote-item"><p>也可以使用 IoC 注入 <a href="#fnref3" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/recommend.md",__html:'<h1>推荐用法</h1>\n<h2>在 Provider 上尽量多配置 Consumer 端属性</h2>\n<p>原因如下:</p>\n<ul>\n<li>作服务的提供者,比服务使用方更清楚服务性能参数,如调用的超时时间,合理的重试次数,等等</li>\n<li>在 Provider 配置后,Consumer 不配置则会使用 Provider 的配置值,即 Provider 配置可以作为 Consumer 的缺省值 [^1]。否则,Consumer 会使用 Consumer 端的全局设置,这对于 Provider 不可控的,并且往往是不合理的</li>\n</ul>\n<p>Provider 上尽量多配置 Consumer 端的属性,让 Provider 实现者一开始就思考 Provider 服务特点、服务质量的问题。</p>\n<p>示例:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.alibaba.hello.api.HelloService"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0.0"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"helloService"</span>\n    <span class="hljs-attr">timeout</span>=<span class="hljs-string">"300"</span> <span class="hljs-attr">retry</span>=<span class="hljs-string">"2"</span> <span class="hljs-attr">loadbalance</span>=<span class="hljs-string">"random"</span> <span class="hljs-attr">actives</span>=<span class="hljs-string">"0"</span>\n/&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.hello.api.WorldService"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0.0"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"helloService"</span>\n    <span class="hljs-attr">timeout</span>=<span class="hljs-string">"300"</span> <span class="hljs-attr">retry</span>=<span class="hljs-string">"2"</span> <span class="hljs-attr">loadbalance</span>=<span class="hljs-string">"random"</span> <span class="hljs-attr">actives</span>=<span class="hljs-string">"0"</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">"findAllPerson"</span> <span class="hljs-attr">timeout</span>=<span class="hljs-string">"10000"</span> <span class="hljs-attr">retries</span>=<span class="hljs-string">"9"</span> <span class="hljs-attr">loadbalance</span>=<span class="hljs-string">"leastactive"</span> <span class="hljs-attr">actives</span>=<span class="hljs-string">"5"</span> /&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span>/&gt;</span>\n</code></pre>\n<p>在 Provider 上可以配置的 Consumer 端属性有:</p>\n<ol start="0">\n<li><code>timeout</code> 方法调用超时</li>\n<li><code>retries</code> 失败重试次数,缺省是 2 [^2]</li>\n<li><code>loadbalance</code> 负载均衡算法 [^3],缺省是随机 <code>random</code>。还可以有轮询 <code>roundrobin</code>、最不活跃优先 [^4] <code>leastactive</code></li>\n<li><code>actives</code> 消费者端,最大并发调用限制,即当 Consumer 对一个服务的并发调用到上限后,新调用会 Wait 直到超时\n在方法上配置 <code>dubbo:method</code> 则并发限制针对方法,在接口上配置 <code>dubbo:service</code>,则并发限制针对服务</li>\n</ol>\n<p>详细配置说明参见:<a href="./references/xml/introduction.md">Dubbo配置参考手册</a></p>\n<h2>Provider 上配置合理的 Provider 端属性</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">threads</span>=<span class="hljs-string">"200"</span> /&gt;</span> \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.hello.api.HelloService"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0.0"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"helloService"</span>\n    <span class="hljs-attr">executes</span>=<span class="hljs-string">"200"</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">"findAllPerson"</span> <span class="hljs-attr">executes</span>=<span class="hljs-string">"50"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:service</span>&gt;</span>\n</code></pre>\n<p>Provider 上可以配置的 Provider 端属性有:</p>\n<ol start="0">\n<li><code>threads</code> 服务线程池大小</li>\n<li><code>executes</code> 一个服务提供者并行执行请求上限,即当 Provider 对一个服务的并发调用到上限后,新调用会 Wait,这个时候 Consumer可能会超时。在方法上配置 <code>dubbo:method</code> 则并发限制针对方法,在接口上配置 <code>dubbo:service</code>,则并发限制针对服务</li>\n</ol>\n<h2>配置管理信息</h2>\n<p>目前有负责人信息和组织信息用于区分站点。有问题时便于的找到服务的负责人,至少写两个人以便备份。负责人和组织的信息可以在注册中心的上看到。</p>\n<p>应用配置负责人、组织:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:application</span> <span class="hljs-attr">owner</span>=<span class="hljs-string">”ding.lid,william.liangf”</span> <span class="hljs-attr">organization</span>=<span class="hljs-string">”intl”</span> /&gt;</span>\n</code></pre>\n<p>service 配置负责人:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">owner</span>=<span class="hljs-string">”ding.lid,william.liangf”</span> /&gt;</span>\n</code></pre>\n<p>reference 配置负责人:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">owner</span>=<span class="hljs-string">”ding.lid,william.liangf”</span> /&gt;</span>\n</code></pre>\n<p><code>dubbo:service</code>、<code>dubbo:reference</code> 没有配置负责人,则使用 <code>dubbo:application</code> 设置的负责人。</p>\n<h2>配置 Dubbo 缓存文件</h2>\n<p>提供者列表缓存文件:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">file</span>=<span class="hljs-string">”${user.home}/output/dubbo.cache”</span> /&gt;</span>\n</code></pre>\n<p>注意:</p>\n<ol start="0">\n<li>文件的路径,应用可以根据需要调整,保证这个文件不会在发布过程中被清除。</li>\n<li>如果有多个应用进程注意不要使用同一个文件,避免内容被覆盖。</li>\n</ol>\n<p>这个文件会缓存注册中心的列表和服务提供者列表。有了这项配置后,当应用重启过程中,Dubbo 注册中心不可用时则应用会从这个缓存文件读取服务提供者列表的信息,进一步保证应用可靠性。</p>\n<h2>监控配置</h2>\n<ol start="0">\n<li>\n<p>使用固定端口暴露服务,而不要使用随机端口</p>\n<p>这样在注册中心推送有延迟的情况下,消费者通过缓存列表也能调用到原地址,保证调用成功。</p>\n</li>\n<li>\n<p>使用 Dragoon 的 http 监控项监控注册中心上服务提供方</p>\n<p>Dragoon 监控服务在注册中心上的状态:<a href="http://dubbo-reg1.hst.xyi.cn.alidc.net:8080/status/com.alibaba.morgan.member.MemberService:1.0.5">http://dubbo-reg1.hst.xyi.cn.alidc.net:8080/status/com.alibaba.morgan.member.MemberService:1.0.5</a> 确保注册中心上有该服务的存在。</p>\n</li>\n<li>\n<p>服务提供方,使用 Dragoon 的 telnet 或 shell 监控项</p>\n<p>监控服务提供者端口状态:<code>echo status | nc -i 1 20880 | grep OK | wc -l</code>,其中的 20880 为服务端口</p>\n</li>\n<li>\n<p>服务消费方,通过将服务强制转型为 EchoService,并调用 <code>$echo()</code> 测试该服务的提供者是可用</p>\n<p>如 <code>assertEqauls(“OK”, ((EchoService)memberService).$echo(“OK”));</code></p>\n</li>\n</ol>\n<h2>不要使用 dubbo.properties 文件配置,推荐使用对应 XML 配置</h2>\n<p>Dubbo 中所有的配置项都可以配置在 Spring 配置文件中,并且可以针对单个服务配置。</p>\n<p>如完全不配置则使用 Dubbo 缺省值,参见 <a href="./references/xml/introduction.md">Dubbo配置参考手册</a> 中的说明。</p>\n<h3>dubbo.properties 中属性名与 XML 的对应关系</h3>\n<ol start="0">\n<li>\n<p>应用名 <code>dubbo.application.name</code></p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:application</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"myalibaba"</span> &gt;</span>\n</code></pre>\n</li>\n<li>\n<p>注册中心地址 <code>dubbo.registry.address</code></p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"11.22.33.44:9090"</span> &gt;</span>\n</code></pre>\n</li>\n<li>\n<p>调用超时 <code>dubbo.service.*.timeout</code></p>\n<p>可以在多个配置项设置超时 <code>timeout</code>,由上至下覆盖(即上面的优先)[^5],其它的参数(<code>retries</code>、<code>loadbalance</code>、<code>actives</code>等)的覆盖策略也一样示例如下:</p>\n<p>提供者端特定方法的配置</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.alibaba.xxx.XxxService"</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">"findPerson"</span> <span class="hljs-attr">timeout</span>=<span class="hljs-string">"1000"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:service</span>&gt;</span>\n</code></pre>\n<p>提供者端特定接口的配置</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.alibaba.xxx.XxxService"</span> <span class="hljs-attr">timeout</span>=<span class="hljs-string">"200"</span> /&gt;</span>\n</code></pre>\n</li>\n<li>\n<p>服务提供者协议 <code>dubbo.service.protocol</code>、服务的监听端口 <code>dubbo.service.server.port</code></p>\n<pre><code class="language-xml"></code></pre>\n</li>\n</ol>\n<p>&lt;dubbo:protocol name=&quot;dubbo&quot; port=&quot;20880&quot; /&gt;</p>\n<pre><code>    \n5. 服务线程池大小 `dubbo.service.max.thread.threads.size`\n\n    ```xml\n    &lt;dubbo:protocol threads=&quot;100&quot; /&gt;\n    ```\n    \n6. 消费者启动时,没有提供者是否抛异常 Fast-Fail `alibaba.intl.commons.dubbo.service.allow.no.provider`\n\n    ```xml\n    &lt;dubbo:reference interface=&quot;com.alibaba.xxx.XxxService&quot; check=&quot;false&quot; /&gt;\n    ```\n    \n[^1]: 配置的覆盖规则:1) 方法级别配置优于接口级别,即小 Scope 优先 2) Consumer 端配置优于 Provider 配置,优于全局配置,最后是Dubbo 硬编码的配置值([Dubbo 配置参考手册](./configuration/properties.md#覆盖策略))\n[^2]: 表示加上第一次调用,会调用 3 次\n[^3]: 有多个 Provider 时,如何挑选 Provider 调用\n[^4]: 指从 Consume r端并发调用最好的 Provider,可以减少的反应慢的 Provider 的调用,因为反应更容易累积并发的调用\n[^5]: `timeout` 可以在多处设置,配置项及覆盖规则详见: [Dubbo 配置参考手册](./references/xml/introduction.md)\n</code></pre>\n'},{filename:"user/references/api.md",__html:'<h1>API 参考手册</h1>\n<p>Dubbo 的常规功能,都保持零侵入,但有些功能不得不用 API 侵入才能实现 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>。</p>\n<p>API 汇总如下:</p>\n<h2>配置 API</h2>\n<pre><code>com.alibaba.dubbo.config.ServiceConfig\ncom.alibaba.dubbo.config.ReferenceConfig\ncom.alibaba.dubbo.config.ProtocolConfig\ncom.alibaba.dubbo.config.RegistryConfig\ncom.alibaba.dubbo.config.MonitorConfig\ncom.alibaba.dubbo.config.ApplicationConfig\ncom.alibaba.dubbo.config.ModuleConfig\ncom.alibaba.dubbo.config.ProviderConfig\ncom.alibaba.dubbo.config.ConsumerConfig\ncom.alibaba.dubbo.config.MethodConfig\ncom.alibaba.dubbo.config.ArgumentConfig\n</code></pre>\n<p>详细参见:<a href="../configuration/api.md">API配置</a></p>\n<h2>注解 API</h2>\n<pre><code>com.alibaba.dubbo.config.annotation.Service\ncom.alibaba.dubbo.config.annotation.Reference\n</code></pre>\n<p>详细参见:<a href="../configuration/annotation.md">注解配置</a></p>\n<h2>模型 API</h2>\n<pre><code>com.alibaba.dubbo.common.URL\ncom.alibaba.dubbo.rpc.RpcException\n</code></pre>\n<h2>上下文 API</h2>\n<pre><code>com.alibaba.dubbo.rpc.RpcContext\n</code></pre>\n<p>详细参见:<a href="../demos/context.md">上下文信息</a> &amp; <a href="../demos/attachment.md">隐式传参</a> &amp; <a href="../demos/async-call.md">异步调用</a></p>\n<h2>服务API</h2>\n<pre><code>com.alibaba.dubbo.rpc.service.GenericService\ncom.alibaba.dubbo.rpc.service.GenericException\n</code></pre>\n<p>详细参见:<a href="../demos/generic-reference.md">泛化引用</a> &amp; <a href="../demos/generic-service.md">泛化实现</a></p>\n<pre><code>com.alibaba.dubbo.rpc.service.EchoService\n</code></pre>\n<p>详细参见:<a href="../demos/echo-service.md">回声测试</a></p>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>注意:Dubbo 中除这里声明以外的接口或类,都是内部接口或扩展接口,普通用户请不要直接依赖,否则升级版本可能出现不兼容。 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/references/maven.md",__html:'<h1>Maven 插件参考手册</h1>\n<h2>启动一个简易注册中心</h2>\n<p>以指定的9099端口启动一个简易注册中心 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>:</p>\n<pre><code class="language-sh">mvn dubbo:registry -Dport=9099 \n</code></pre>\n<h2>生成demo服务提供者应用</h2>\n<p>生成指定接口和版本的服务提供者应用:</p>\n<pre><code class="language-sh">mvn dubbo:create -Dapplication=xxx -Dpackage=com.alibaba.xxx -Dservice=XxxService,YyyService -Dversion=1.0.0 \n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>如果端口不指定,默认端口为 9090 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/references/protocol/dubbo.md",__html:'<h1>dubbo://</h1>\n<p>Dubbo 缺省协议采用单一长连接和 NIO 异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。</p>\n<p>反之,Dubbo 缺省协议不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低。</p>\n<p><img src="../../sources/images/dubbo-protocol.jpg" alt="dubbo-protocol.jpg"></p>\n<ul>\n<li>Transporter: mina, netty, grizzy</li>\n<li>Serialization: dubbo, hessian2, java, json</li>\n<li>Dispatcher: all, direct, message, execution, connection</li>\n<li>ThreadPool: fixed, cached</li>\n</ul>\n<h2>特性</h2>\n<p>缺省协议,使用基于 mina <code>1.1.7</code> 和 hessian <code>3.2.1</code> 的 tbremoting 交互。</p>\n<ul>\n<li>连接个数:单连接</li>\n<li>连接方式:长连接</li>\n<li>传输协议:TCP</li>\n<li>传输方式:NIO 异步传输</li>\n<li>序列化:Hessian 二进制序列化</li>\n<li>适用范围:传入传出参数数据包较小(建议小于100K),消费者比提供者个数多,单一消费者无法压满提供者,尽量不要用 dubbo 协议传输大文件或超大字符串。</li>\n<li>适用场景:常规远程服务方法调用</li>\n</ul>\n<h2>约束</h2>\n<ul>\n<li>参数及返回值需实现 <code>Serializable</code> 接口</li>\n<li>参数及返回值不能自定义实现 <code>List</code>, <code>Map</code>, <code>Number</code>, <code>Date</code>, <code>Calendar</code> 等接口,只能用 JDK 自带的实现,因为 hessian 会做特殊处理,自定义实现类中的属性值都会丢失。</li>\n<li>Hessian 序列化,只传成员属性值和值的类型,不传方法或静态变量,兼容情况 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup><sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>:</li>\n</ul>\n<table>\n<thead>\n<tr>\n<th>数据通讯</th>\n<th>情况</th>\n<th>结果</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>A-&gt;B</td>\n<td>类A多一种 属性(或者说类B少一种 属性)</td>\n<td>不抛异常,A多的那 个属性的值,B没有, 其他正常</td>\n</tr>\n<tr>\n<td>A-&gt;B</td>\n<td>枚举A多一种 枚举(或者说B少一种 枚举),A使用多 出来的枚举进行传输</td>\n<td>抛异常</td>\n</tr>\n<tr>\n<td>A-&gt;B</td>\n<td>枚举A多一种 枚举(或者说B少一种 枚举),A不使用 多出来的枚举进行传输</td>\n<td>不抛异常,B正常接 收数据</td>\n</tr>\n<tr>\n<td>A-&gt;B</td>\n<td>A和B的属性 名相同,但类型不相同</td>\n<td>抛异常</td>\n</tr>\n<tr>\n<td>A-&gt;B</td>\n<td>serialId 不相同</td>\n<td>正常传输</td>\n</tr>\n</tbody>\n</table>\n<p>接口增加方法,对客户端无影响,如果该方法不是客户端需要的,客户端不需要重新部署。输入参数和结果集中增加属性,对客户端无影响,如果客户端并不需要新属性,不用重新部署。</p>\n<p>输入参数和结果集属性名变化,对客户端序列化无影响,但是如果客户端不重新部署,不管输入还是输出,属性名变化的属性值是获取不到的。</p>\n<p>总结:服务器端和客户端对领域对象并不需要完全一致,而是按照最大匹配原则。</p>\n<h2>配置</h2>\n<p>配置协议:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"20880"</span> /&gt;</span>\n</code></pre>\n<p>设置默认协议:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"dubbo"</span> /&gt;</span>\n</code></pre>\n<p>设置服务协议:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"dubbo"</span> /&gt;</span>\n</code></pre>\n<p>多端口:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"dubbo1"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"20880"</span> /&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"dubbo2"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"20881"</span> /&gt;</span>\n</code></pre>\n<p>配置协议选项:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">“dubbo”</span> <span class="hljs-attr">port</span>=<span class="hljs-string">“9090”</span> <span class="hljs-attr">server</span>=<span class="hljs-string">“netty”</span> <span class="hljs-attr">client</span>=<span class="hljs-string">“netty”</span> <span class="hljs-attr">codec</span>=<span class="hljs-string">“dubbo”</span> <span class="hljs-attr">serialization</span>=<span class="hljs-string">“hessian2”</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">“UTF-8”</span> <span class="hljs-attr">threadpool</span>=<span class="hljs-string">“fixed”</span> <span class="hljs-attr">threads</span>=<span class="hljs-string">“100”</span> <span class="hljs-attr">queues</span>=<span class="hljs-string">“0”</span> <span class="hljs-attr">iothreads</span>=<span class="hljs-string">“9”</span> <span class="hljs-attr">buffer</span>=<span class="hljs-string">“8192”</span> <span class="hljs-attr">accepts</span>=<span class="hljs-string">“1000”</span> <span class="hljs-attr">payload</span>=<span class="hljs-string">“8388608”</span> /&gt;</span>\n</code></pre>\n<p>多连接配置:</p>\n<p>Dubbo 协议缺省每服务每提供者每消费者使用单一长连接,如果数据量较大,可以使用多个连接。</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">connections</span>=<span class="hljs-string">"1"</span>/&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">connections</span>=<span class="hljs-string">"1"</span>/&gt;</span>\n</code></pre>\n<ul>\n<li><code>&lt;dubbo:service connections=&quot;0&quot;&gt;</code> 或 <code>&lt;dubbo:reference connections=&quot;0&quot;&gt;</code> 表示该服务使用 JVM 共享长连接。<strong>缺省</strong></li>\n<li><code>&lt;dubbo:service connections=&quot;1&quot;&gt;</code> 或 <code>&lt;dubbo:reference connections=&quot;1&quot;&gt;</code> 表示该服务使用独立长连接。</li>\n<li><code>&lt;dubbo:service connections=&quot;2&quot;&gt;</code> 或<code>&lt;dubbo:reference connections=&quot;2&quot;&gt;</code> 表示该服务使用独立两条长连接。</li>\n</ul>\n<p>为防止被大量连接撑挂,可在服务提供方限制大接收连接数,以实现服务提供方自我保护。</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">accepts</span>=<span class="hljs-string">"1000"</span> /&gt;</span>\n</code></pre>\n<p><code>dubbo.properties</code> 配置:</p>\n<pre><code class="language-sh">dubbo.service.protocol=dubbo\n</code></pre>\n<h2>常见问题</h2>\n<h4>为什么要消费者比提供者个数多?</h4>\n<p>因 dubbo 协议采用单一长连接,假设网络为千兆网卡 <sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup>,根据测试经验数据每条连接最多只能压满 7MByte(不同的环境可能不一样,供参考),理论上 1 个服务提供者需要 20 个服务消费者才能压满网卡。</p>\n<h4>为什么不能传大包?</h4>\n<p>因 dubbo 协议采用单一长连接,如果每次请求的数据包大小为 500KByte,假设网络为千兆网卡 <sup class="footnote-ref"><a href="#fn3" id="fnref3:1">[3:1]</a></sup>,每条连接最大 7MByte(不同的环境可能不一样,供参考),单个服务提供者的 TPS(每秒处理事务数)最大为:128MByte / 500KByte = 262。单个消费者调用单个服务提供者的 TPS(每秒处理事务数)最大为:7MByte / 500KByte = 14。如果能接受,可以考虑使用,否则网络将成为瓶颈。</p>\n<h4>为什么采用异步单一长连接?</h4>\n<p>因为服务的现状大都是服务提供者少,通常只有几台机器,而服务的消费者多,可能整个网站都在访问该服务,比如 Morgan 的提供者只有 6 台提供者,却有上百台消费者,每天有 1.5 亿次调用,如果采用常规的 hessian 服务,服务提供者很容易就被压跨,通过单一连接,保证单一消费者不会压死提供者,长连接,减少连接握手验证等,并使用异步 IO,复用线程池,防止 C10K 问题。</p>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>由吴亚军提供 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>总结:会抛异常的情况:枚举值一边多一种,一边少一种,正好使用了差别的那种,或者属性名相同,类型不同 <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn3" class="footnote-item"><p>1024Mbit=128MByte <a href="#fnref3" class="footnote-backref">↩︎</a> <a href="#fnref3:1" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/references/protocol/hessian.md",__html:'<h1>hessian://</h1>\n<p>Hessian <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> 协议用于集成 Hessian 的服务,Hessian 底层采用 Http 通讯,采用 Servlet 暴露服务,Dubbo 缺省内嵌 Jetty 作为服务器实现。</p>\n<p>Dubbo 的 Hessian 协议可以和原生 Hessian 服务互操作,即:</p>\n<ul>\n<li>提供者用 Dubbo 的 Hessian 协议暴露服务,消费者直接用标准 Hessian 接口调用</li>\n<li>或者提供方用标准 Hessian 暴露服务,消费方用 Dubbo 的 Hessian 协议调用。</li>\n</ul>\n<h2>特性</h2>\n<ul>\n<li>连接个数:多连接</li>\n<li>连接方式:短连接</li>\n<li>传输协议:HTTP</li>\n<li>传输方式:同步传输</li>\n<li>序列化:Hessian二进制序列化</li>\n<li>适用范围:传入传出参数数据包较大,提供者比消费者个数多,提供者压力较大,可传文件。</li>\n<li>适用场景:页面传输,文件传输,或与原生hessian服务互操作</li>\n</ul>\n<h2>依赖</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.caucho<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>hessian<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>4.0.7<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>\n</code></pre>\n<h2>约束</h2>\n<ul>\n<li>参数及返回值需实现 <code>Serializable</code> 接口</li>\n<li>参数及返回值不能自定义实现 <code>List</code>, <code>Map</code>, <code>Number</code>, <code>Date</code>, <code>Calendar</code> 等接口,只能用 JDK 自带的实现,因为 hessian 会做特殊处理,自定义实现类中的属性值都会丢失。</li>\n</ul>\n<h2>配置</h2>\n<p>定义 hessian 协议:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"hessian"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"8080"</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"jetty"</span> /&gt;</span>\n</code></pre>\n<p>设置默认协议:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"hessian"</span> /&gt;</span>\n</code></pre>\n<p>设置 service 协议:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"hessian"</span> /&gt;</span>\n</code></pre>\n<p>多端口:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"hessian1"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"hessian"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"8080"</span> /&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"hessian2"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"hessian"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"8081"</span> /&gt;</span>\n</code></pre>\n<p>直连:</p>\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">"helloService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"HelloWorld"</span> <span class="hljs-attr">url</span>=<span class="hljs-string">"hessian://10.20.153.10:8080/helloWorld"</span> /&gt;</span>\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p><a href="http://hessian.caucho.com">Hessian</a> 是 Caucho 开源的一个 RPC 框架,其通讯效率高于 WebService 和 Java 自带的序列化。 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/references/protocol/http.md",__html:'<h1>http://</h1>\n<p>基于 HTTP 表单的远程调用协议,采用 Spring 的 HttpInvoker 实现 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup></p>\n<h2>特性</h2>\n<ul>\n<li>连接个数:多连接</li>\n<li>连接方式:短连接</li>\n<li>传输协议:HTTP</li>\n<li>传输方式:同步传输</li>\n<li>序列化:表单序列化</li>\n<li>适用范围:传入传出参数数据包大小混合,提供者比消费者个数多,可用浏览器查看,可用表单或URL传入参数,暂不支持传文件。</li>\n<li>适用场景:需同时给应用程序和浏览器 JS 使用的服务。</li>\n</ul>\n<h2>约束</h2>\n<ul>\n<li>参数及返回值需符合 Bean 规范</li>\n</ul>\n<h2>配置</h2>\n<p>配置协议:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"http"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"8080"</span> /&gt;</span>\n</code></pre>\n<p>配置 Jetty Server (默认):</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">...</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"jetty"</span> /&gt;</span>\n</code></pre>\n<p>配置 Servlet Bridge Server (推荐使用):</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">...</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"servlet"</span> /&gt;</span>\n</code></pre>\n<p>配置 DispatcherServlet:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">servlet</span>&gt;</span>\n         <span class="hljs-tag">&lt;<span class="hljs-name">servlet-name</span>&gt;</span>dubbo<span class="hljs-tag">&lt;/<span class="hljs-name">servlet-name</span>&gt;</span>\n         <span class="hljs-tag">&lt;<span class="hljs-name">servlet-class</span>&gt;</span>com.alibaba.dubbo.remoting.http.servlet.DispatcherServlet<span class="hljs-tag">&lt;/<span class="hljs-name">servlet-class</span>&gt;</span>\n         <span class="hljs-tag">&lt;<span class="hljs-name">load-on-startup</span>&gt;</span>1<span class="hljs-tag">&lt;/<span class="hljs-name">load-on-startup</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">servlet</span>&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">servlet-mapping</span>&gt;</span>\n         <span class="hljs-tag">&lt;<span class="hljs-name">servlet-name</span>&gt;</span>dubbo<span class="hljs-tag">&lt;/<span class="hljs-name">servlet-name</span>&gt;</span>\n         <span class="hljs-tag">&lt;<span class="hljs-name">url-pattern</span>&gt;</span>/*<span class="hljs-tag">&lt;/<span class="hljs-name">url-pattern</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">servlet-mapping</span>&gt;</span>\n</code></pre>\n<p>注意,如果使用 servlet 派发请求:</p>\n<ul>\n<li>协议的端口 <code>&lt;dubbo:protocol port=&quot;8080&quot; /&gt;</code> 必须与 servlet 容器的端口相同,</li>\n<li>协议的上下文路径 <code>&lt;dubbo:protocol contextpath=&quot;foo&quot; /&gt;</code> 必须与 servlet 应用的上下文路径相同。</li>\n</ul>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p><code>2.3.0</code> 以上版本支持 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/references/protocol/introduction.md",__html:'<h1>协议参考手册</h1>\n<p>推荐使用 Dubbo 协议。各协议的性能情况,请参见:<a href="../../perf-test.md">性能测试报告</a></p>\n'},{filename:"user/references/protocol/memcached.md",__html:'<h1>memcached://</h1>\n<p>基于 memcached <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> 实现的 RPC 协议 <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>。</p>\n<h2>注册 memcached 服务的地址</h2>\n<pre><code class="language-java">RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();\nRegistry registry = registryFactory.getRegistry(URL.valueOf(<span class="hljs-string">"zookeeper://10.20.153.10:2181"</span>));\nregistry.register(URL.valueOf(<span class="hljs-string">"memcached://10.20.153.11/com.foo.BarService?category=providers&amp;dynamic=false&amp;application=foo&amp;group=member&amp;loadbalance=consistenthash"</span>));\n</code></pre>\n<h2>在客户端引用</h2>\n<p>在客户端使用 <sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup>:</p>\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">"cache"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"java.util.Map"</span> <span class="hljs-attr">group</span>=<span class="hljs-string">"member"</span> /&gt;</span>\n</code></pre>\n<p>或者,点对点直连:</p>\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">"cache"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"java.util.Map"</span> <span class="hljs-attr">url</span>=<span class="hljs-string">"memcached://10.20.153.10:11211"</span> /&gt;</span>\n</code></pre>\n<p>也可以使用自定义接口:</p>\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">"cache"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.CacheService"</span> <span class="hljs-attr">url</span>=<span class="hljs-string">"memcached://10.20.153.10:11211"</span> /&gt;</span>\n</code></pre>\n<p>方法名建议和 memcached 的标准方法名相同,即:get(key), set(key, value), delete(key)。</p>\n<p>如果方法名和 memcached 的标准方法名不相同,则需要配置映射关系 <sup class="footnote-ref"><a href="#fn4" id="fnref4">[4]</a></sup>:</p>\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">"cache"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.CacheService"</span> <span class="hljs-attr">url</span>=<span class="hljs-string">"memcached://10.20.153.10:11211"</span> <span class="hljs-attr">p:set</span>=<span class="hljs-string">"putFoo"</span> <span class="hljs-attr">p:get</span>=<span class="hljs-string">"getFoo"</span> <span class="hljs-attr">p:delete</span>=<span class="hljs-string">"removeFoo"</span> /&gt;</span>\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p><a href="http://memcached.org/">Memcached</a> 是一个高效的 KV 缓存服务器 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p><code>2.3.0</code> 以上版本支持 <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn3" class="footnote-item"><p>不需要感知 Memcached 的地址 <a href="#fnref3" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn4" class="footnote-item"><p>其中 &quot;p:xxx&quot; 为 spring 的标准 p 标签 <a href="#fnref4" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/references/protocol/redis.md",__html:'<h1>redis://</h1>\n<p>基于 Redis <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> 实现的 RPC 协议 <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>。</p>\n<h2>注册 redis 服务的地址</h2>\n<pre><code class="language-java">RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();\nRegistry registry = registryFactory.getRegistry(URL.valueOf(<span class="hljs-string">"zookeeper://10.20.153.10:2181"</span>));\nregistry.register(URL.valueOf(<span class="hljs-string">"redis://10.20.153.11/com.foo.BarService?category=providers&amp;dynamic=false&amp;application=foo&amp;group=member&amp;loadbalance=consistenthash"</span>));\n</code></pre>\n<h2>在客户端引用</h2>\n<p>在客户端使用 <sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup>:</p>\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">"store"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"java.util.Map"</span> <span class="hljs-attr">group</span>=<span class="hljs-string">"member"</span> /&gt;</span>\n</code></pre>\n<p>或者,点对点直连:</p>\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">"store"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"java.util.Map"</span> <span class="hljs-attr">url</span>=<span class="hljs-string">"redis://10.20.153.10:6379"</span> /&gt;</span>\n</code></pre>\n<p>也可以使用自定义接口:</p>\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">"store"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.StoreService"</span> <span class="hljs-attr">url</span>=<span class="hljs-string">"redis://10.20.153.10:6379"</span> /&gt;</span>\n</code></pre>\n<p>方法名建议和 redis 的标准方法名相同,即:get(key), set(key, value), delet(key)。</p>\n<p>如果方法名和 redis 的标准方法名不相同,则需要配置映射关系 <sup class="footnote-ref"><a href="#fn4" id="fnref4">[4]</a></sup>:</p>\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">"cache"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.CacheService"</span> <span class="hljs-attr">url</span>=<span class="hljs-string">"redis://10.20.153.10:6379"</span> <span class="hljs-attr">p:set</span>=<span class="hljs-string">"putFoo"</span> <span class="hljs-attr">p:get</span>=<span class="hljs-string">"getFoo"</span> <span class="hljs-attr">p:delete</span>=<span class="hljs-string">"removeFoo"</span> /&gt;</span>\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p><a href="http://redis.io">Redis</a> 是一个高效的 KV 存储服务器 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p><code>2.3.0</code> 以上版本支持 <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn3" class="footnote-item"><p>不需要感知 Redis 的地址 <a href="#fnref3" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn4" class="footnote-item"><p>其中 &quot;p:xxx&quot; 为 spring 的标准 p 标签 <a href="#fnref4" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/references/protocol/rest.md",__html:'<h1>rest://</h1>\n<p>基于标准的Java REST API——JAX-RS 2.0(Java API for RESTful Web Services的简写)实现的REST调用支持</p>\n<h2>快速入门</h2>\n<p>在dubbo中开发一个REST风格的服务会比较简单,下面以一个注册用户的简单服务为例说明。</p>\n<p>这个服务要实现的功能是提供如下URL(注:这个URL不是完全符合REST的风格,但是更简单实用):</p>\n<pre><code>http://localhost:8080/users/register\n</code></pre>\n<p>而任何客户端都可以将包含用户信息的JSON字符串POST到以上URL来完成用户注册。</p>\n<p>首先,开发服务的接口:</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">UserService</span> </span>{    \n   <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">registerUser</span><span class="hljs-params">(User user)</span></span>;\n}\n</code></pre>\n<p>然后,开发服务的实现:</p>\n<pre><code class="language-java"><span class="hljs-meta">@Path</span>(<span class="hljs-string">"users"</span>)\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">UserService</span> </span>{\n       \n    <span class="hljs-meta">@POST</span>\n    <span class="hljs-meta">@Path</span>(<span class="hljs-string">"register"</span>)\n    <span class="hljs-meta">@Consumes</span>({MediaType.APPLICATION_JSON})\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">registerUser</span><span class="hljs-params">(User user)</span> </span>{\n        <span class="hljs-comment">// save the user...</span>\n    }\n}\n</code></pre>\n<p>上面的服务实现代码非常简单,但是由于REST服务是要被发布到特定HTTP URL,供任意语言客户端甚至浏览器来访问,所以这里要额外添加了几个JAX-RS的标准annotation来做相关的配置:</p>\n<p>@Path(&quot;users&quot;):指定访问UserService的URL相对路径是/users,即http://localhost:8080/users</p>\n<p>@Path(&quot;register&quot;):指定访问registerUser()方法的URL相对路径是/register,再结合上一个@Path为UserService指定的路径,则调用UserService.register()的完整路径为http://localhost:8080/users/register</p>\n<p>@POST:指定访问registerUser()用HTTP POST方法</p>\n<p>@Consumes({MediaType.APPLICATION_JSON}):指定registerUser()接收JSON格式的数据。REST框架会自动将JSON数据反序列化为User对象</p>\n<p>最后,在spring配置文件中添加此服务,即完成所有服务开发工作:</p>\n<pre><code class="language-xml"><span class="hljs-comment">&lt;!-- 用rest协议在8080端口暴露服务 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"8080"</span>/&gt;</span>\n\n<span class="hljs-comment">&lt;!-- 声明需要暴露的服务接口 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"xxx.UserService"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"userService"</span>/&gt;</span>\n\n<span class="hljs-comment">&lt;!-- 和本地bean一样实现服务 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">bean</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"userService"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"xxx.UserServiceImpl"</span> /&gt;</span>\n</code></pre>\n<h2>REST服务提供端详解</h2>\n<p>下面我们扩充“快速入门”中的UserService,进一步展示在dubbo中REST服务提供端的开发要点。</p>\n<h3>HTTP POST/GET的实现</h3>\n<p>REST服务中虽然建议使用HTTP协议中四种标准方法POST、DELETE、PUT、GET来分别实现常见的“增删改查”,但实际中,我们一般情况直接用POST来实现“增改”,GET来实现“删查”即可(DELETE和PUT甚至会被一些防火墙阻挡)。</p>\n<p>前面已经简单演示了POST的实现,在此,我们为UserService添加一个获取注册用户资料的功能,来演示GET的实现。</p>\n<p>这个功能就是要实现客户端通过访问如下不同URL来获取不同ID的用户资料:</p>\n<pre><code>http://localhost:8080/users/1001\nhttp://localhost:8080/users/1002\nhttp://localhost:8080/users/1003\n</code></pre>\n<p>当然,也可以通过其他形式的URL来访问不同ID的用户资料,例如:</p>\n<pre><code>http://localhost:8080/users/load?id=1001\n</code></pre>\n<p>JAX-RS本身可以支持所有这些形式。但是上面那种在URL路径中包含查询参数的形式(<a href="http://localhost:8080/users/1001%EF%BC%89">http://localhost:8080/users/1001)</a> 更符合REST的一般习惯,所以更推荐大家来使用。下面我们就为UserService添加一个getUser()方法来实现这种形式的URL访问:</p>\n<pre><code class="language-java"><span class="hljs-meta">@GET</span>\n<span class="hljs-meta">@Path</span>(<span class="hljs-string">"{id : \\\\d+}"</span>)\n<span class="hljs-meta">@Produces</span>({MediaType.APPLICATION_JSON})\n<span class="hljs-function"><span class="hljs-keyword">public</span> User <span class="hljs-title">getUser</span><span class="hljs-params">(@PathParam(<span class="hljs-string">"id"</span>)</span> Long id) </span>{\n    <span class="hljs-comment">// ...</span>\n}\n</code></pre>\n<p>@GET:指定用HTTP GET方法访问</p>\n<p>@Path(&quot;{id : \\d+}&quot;):根据上面的功能需求,访问getUser()的URL应当是“<a href="http://localhost:8080/users/">http://localhost:8080/users/</a> + 任意数字&quot;,并且这个数字要被做为参数传入getUser()方法。 这里的annotation配置中,@Path中间的{id: xxx}指定URL相对路径中包含了名为id参数,而它的值也将被自动传递给下面用@PathParam(&quot;id&quot;)修饰的方法参数id。{id:后面紧跟的\\d+是一个正则表达式,指定了id参数必须是数字。</p>\n<p>@Produces({MediaType.APPLICATION_JSON}):指定getUser()输出JSON格式的数据。框架会自动将User对象序列化为JSON数据。</p>\n<h3>Annotation放在接口类还是实现类</h3>\n<p>在Dubbo中开发REST服务主要都是通过JAX-RS的annotation来完成配置的,在上面的示例中,我们都是将annotation放在服务的实现类中。但其实,我们完全也可以将annotation放到服务的接口上,这两种方式是完全等价的,例如:</p>\n<pre><code class="language-java"><span class="hljs-meta">@Path</span>(<span class="hljs-string">"users"</span>)\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">UserService</span> </span>{\n    \n    <span class="hljs-meta">@GET</span>\n    <span class="hljs-meta">@Path</span>(<span class="hljs-string">"{id : \\\\d+}"</span>)\n    <span class="hljs-meta">@Produces</span>({MediaType.APPLICATION_JSON})\n    <span class="hljs-function">User <span class="hljs-title">getUser</span><span class="hljs-params">(@PathParam(<span class="hljs-string">"id"</span>)</span> Long id)</span>;\n}\n</code></pre>\n<p>在一般应用中,我们建议将annotation放到服务实现类,这样annotation和java实现代码位置更接近,更便于开发和维护。另外更重要的是,我们一般倾向于避免对接口的污染,保持接口的纯净性和广泛适用性。</p>\n<p>但是,如后文所述,如果我们要用dubbo直接开发的消费端来访问此服务,则annotation必须放到接口上。</p>\n<p>如果接口和实现类都同时添加了annotation,则实现类的annotation配置会生效,接口上的annotation被直接忽略。</p>\n<h3>JSON、XML等多数据格式的支持</h3>\n<p>在dubbo中开发的REST服务可以同时支持传输多种格式的数据,以给客户端提供最大的灵活性。其中我们目前对最常用的JSON和XML格式特别添加了额外的功能。</p>\n<p>比如,我们要让上例中的getUser()方法支持分别返回JSON和XML格式的数据,只需要在annotation中同时包含两种格式即可:</p>\n<pre><code class="language-java"><span class="hljs-meta">@Produces</span>({MediaType.APPLICATION_JSON, MediaType.TEXT_XML})\n<span class="hljs-function">User <span class="hljs-title">getUser</span><span class="hljs-params">(@PathParam(<span class="hljs-string">"id"</span>)</span> Long id)</span>;\n</code></pre>\n<p>或者也可以直接用字符串(还支持通配符)表示MediaType:</p>\n<pre><code class="language-java"><span class="hljs-meta">@Produces</span>({<span class="hljs-string">"application/json"</span>, <span class="hljs-string">"text/xml"</span>})\n<span class="hljs-function">User <span class="hljs-title">getUser</span><span class="hljs-params">(@PathParam(<span class="hljs-string">"id"</span>)</span> Long id)</span>;\n</code></pre>\n<p>如果所有方法都支持同样类型的输入输出数据格式,则我们无需在每个方法上做配置,只需要在服务类上添加annotation即可:</p>\n<pre><code class="language-java"><span class="hljs-meta">@Path</span>(<span class="hljs-string">"users"</span>)\n<span class="hljs-meta">@Consumes</span>({MediaType.APPLICATION_JSON, MediaType.TEXT_XML})\n<span class="hljs-meta">@Produces</span>({MediaType.APPLICATION_JSON, MediaType.TEXT_XML})\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">UserService</span> </span>{\n    <span class="hljs-comment">// ...</span>\n}\n\n</code></pre>\n<p>在一个REST服务同时对多种数据格式支持的情况下,根据JAX-RS标准,一般是通过HTTP中的MIME header(content-type和accept)来指定当前想用的是哪种格式的数据。</p>\n<p>但是在dubbo中,我们还自动支持目前业界普遍使用的方式,即用一个URL后缀(.json和.xml)来指定想用的数据格式。例如,在添加上述annotation后,直接访问http://localhost:8888/users/1001.json则表示用json格式,直接访问http://localhost:8888/users/1002.xml则表示用xml格式,比用HTTP Header更简单直观。Twitter、微博等的REST API都是采用这种方式。</p>\n<p>如果你既不加HTTP header,也不加后缀,则dubbo的REST会优先启用在以上annotation定义中排位最靠前的那种数据格式。</p>\n<blockquote>\n<p>注意:这里要支持XML格式数据,在annotation中既可以用MediaType.TEXT_XML,也可以用MediaType.APPLICATION_XML,但是TEXT_XML是更常用的,并且如果要利用上述的URL后缀方式来指定数据格式,只能配置为TEXT_XML才能生效。</p>\n</blockquote>\n<h3>中文字符支持</h3>\n<p>为了在dubbo REST中正常输出中文字符,和通常的Java web应用一样,我们需要将HTTP响应的contentType设置为UTF-8编码。</p>\n<p>基于JAX-RS的标准用法,我们只需要做如下annotation配置即可:</p>\n<pre><code class="language-java"><span class="hljs-meta">@Produces</span>({<span class="hljs-string">"application/json; charset=UTF-8"</span>, <span class="hljs-string">"text/xml; charset=UTF-8"</span>})\n<span class="hljs-function">User <span class="hljs-title">getUser</span><span class="hljs-params">(@PathParam(<span class="hljs-string">"id"</span>)</span> Long id)</span>;\n</code></pre>\n<p>为了方便用户,我们在dubbo REST中直接添加了一个支持类,来定义以上的常量,可以直接使用,减少出错的可能性。</p>\n<pre><code class="language-java"><span class="hljs-meta">@Produces</span>({ContentType.APPLICATION_JSON_UTF_8, ContentType.TEXT_XML_UTF_8})\n<span class="hljs-function">User <span class="hljs-title">getUser</span><span class="hljs-params">(@PathParam(<span class="hljs-string">"id"</span>)</span> Long id)</span>;\n</code></pre>\n<h3>XML数据格式的额外要求</h3>\n<p>由于JAX-RS的实现一般都用标准的JAXB(Java API for XML Binding)来序列化和反序列化XML格式数据,所以我们需要为每一个要用XML传输的对象添加一个类级别的JAXB annotation,否则序列化将报错。例如为getUser()中返回的User添加如下:</p>\n<pre><code class="language-java"><span class="hljs-meta">@XmlRootElement</span>\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Serializable</span> </span>{\n    <span class="hljs-comment">// ...</span>\n}\n</code></pre>\n<p>此外,如果service方法中的返回值是Java的 primitive类型(如int,long,float,double等),最好为它们添加一层wrapper对象,因为JAXB不能直接序列化primitive类型。</p>\n<p>例如,我们想让前述的registerUser()方法返回服务器端为用户生成的ID号:</p>\n<pre><code class="language-java"><span class="hljs-function"><span class="hljs-keyword">long</span> <span class="hljs-title">registerUser</span><span class="hljs-params">(User user)</span></span>;\n</code></pre>\n<p>由于primitive类型不被JAXB序列化支持,所以添加一个wrapper对象:</p>\n<pre><code class="language-java"><span class="hljs-meta">@XmlRootElement</span>\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RegistrationResult</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Serializable</span> </span>{\n    \n    <span class="hljs-keyword">private</span> Long id;\n    \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">RegistrationResult</span><span class="hljs-params">()</span> </span>{\n    }\n    \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">RegistrationResult</span><span class="hljs-params">(Long id)</span> </span>{\n        <span class="hljs-keyword">this</span>.id = id;\n    }\n    \n    <span class="hljs-function"><span class="hljs-keyword">public</span> Long <span class="hljs-title">getId</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-keyword">return</span> id;\n    }\n    \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setId</span><span class="hljs-params">(Long id)</span> </span>{\n        <span class="hljs-keyword">this</span>.id = id;\n    }\n}\n</code></pre>\n<p>并修改service方法:</p>\n<pre><code class="language-java"><span class="hljs-function">RegistrationResult <span class="hljs-title">registerUser</span><span class="hljs-params">(User user)</span></span>;\n</code></pre>\n<p>这样不但能够解决XML序列化的问题,而且使得返回的数据都符合XML和JSON的规范。例如,在JSON中,返回的将是如下形式:</p>\n<pre><code class="language-javascript">{<span class="hljs-string">"id"</span>: <span class="hljs-number">1001</span>}\n</code></pre>\n<p>如果不加wrapper,JSON返回值将直接是</p>\n<pre><code>1001 \t\n</code></pre>\n<p>而在XML中,加wrapper后返回值将是:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">registrationResult</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">id</span>&gt;</span>1002<span class="hljs-tag">&lt;/<span class="hljs-name">id</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">registrationResult</span>&gt;</span>\n</code></pre>\n<p>这种wrapper对象其实利用所谓Data Transfer Object(DTO)模式,采用DTO还能对传输数据做更多有用的定制。</p>\n<h3>定制序列化</h3>\n<p>如上所述,REST的底层实现会在service的对象和JSON/XML数据格式之间自动做序列化/反序列化。但有些场景下,如果觉得这种自动转换不满足要求,可以对其做定制。</p>\n<p>Dubbo中的REST实现是用JAXB做XML序列化,用Jackson做JSON序列化,所以在对象上添加JAXB或Jackson的annotation即可以定制映射。</p>\n<p>例如,定制对象属性映射到XML元素的名字:</p>\n<pre><code class="language-java"><span class="hljs-meta">@XmlRootElement</span>\n<span class="hljs-meta">@XmlAccessorType</span>(XmlAccessType.FIELD)\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Serializable</span> </span>{\n    \n    <span class="hljs-meta">@XmlElement</span>(name=<span class="hljs-string">"username"</span>) \n    <span class="hljs-keyword">private</span> String name;  \n}\n</code></pre>\n<p>定制对象属性映射到JSON字段的名字:</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">User</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Serializable</span> </span>{\n    \n    <span class="hljs-meta">@JsonProperty</span>(<span class="hljs-string">"username"</span>)\n    <span class="hljs-keyword">private</span> String name;\n}\n</code></pre>\n<p>更多资料请参考JAXB和Jackson的官方文档,或自行google。</p>\n<h3>配置REST Server的实现</h3>\n<p>目前在dubbo中,我们支持5种嵌入式rest server的实现,并同时支持采用外部应用服务器来做rest server的实现。rest server的实现是通过如下server这个XML属性来选择的:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"jetty"</span>/&gt;</span>\n</code></pre>\n<p>以上配置选用了嵌入式的jetty来做rest server,同时,如果不配置server属性,rest协议默认也是选用jetty。jetty是非常成熟的java servlet容器,并和dubbo已经有较好的集成(目前5种嵌入式server中只有jetty和后面所述的tomcat、tjws,与dubbo监控系统等完成了无缝的集成),所以,如果你的dubbo系统是单独启动的进程,你可以直接默认采用jetty即可。</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"tomcat"</span>/&gt;</span>\n</code></pre>\n<p>以上配置选用了嵌入式的tomcat来做rest server。在嵌入式tomcat上,REST的性能比jetty上要好得多(参见后面的基准测试),建议在需要高性能的场景下采用tomcat。</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"netty"</span>/&gt;</span>\n</code></pre>\n<p>以上配置选用嵌入式的netty来做rest server。(TODO more contents to add)</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"tjws"</span>/&gt;</span> (tjws is now deprecated)\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"sunhttp"</span>/&gt;</span>\n</code></pre>\n<p>以上配置选用嵌入式的tjws或Sun HTTP server来做rest server。这两个server实现非常轻量级,非常方便在集成测试中快速启动使用,当然也可以在负荷不高的生产环境中使用。\t注:tjws目前已经被deprecated掉了,因为它不能很好的和servlet 3.1 API工作。</p>\n<p>如果你的dubbo系统不是单独启动的进程,而是部署到了Java应用服务器中,则建议你采用以下配置:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"servlet"</span>/&gt;</span>\n</code></pre>\n<p>通过将server设置为servlet,dubbo将采用外部应用服务器的servlet容器来做rest server。同时,还要在dubbo系统的web.xml中添加如下配置:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">web-app</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">context-param</span>&gt;</span>\n        <span class="hljs-tag">&lt;<span class="hljs-name">param-name</span>&gt;</span>contextConfigLocation<span class="hljs-tag">&lt;/<span class="hljs-name">param-name</span>&gt;</span>\n        <span class="hljs-tag">&lt;<span class="hljs-name">param-value</span>&gt;</span>/WEB-INF/classes/META-INF/spring/dubbo-demo-provider.xml<span class="hljs-tag">&lt;/<span class="hljs-name">param-value</span>&gt;</span>\n    <span class="hljs-tag">&lt;/<span class="hljs-name">context-param</span>&gt;</span>\n    \n    <span class="hljs-tag">&lt;<span class="hljs-name">listener</span>&gt;</span>\n        <span class="hljs-tag">&lt;<span class="hljs-name">listener-class</span>&gt;</span>com.alibaba.dubbo.remoting.http.servlet.BootstrapListener<span class="hljs-tag">&lt;/<span class="hljs-name">listener-class</span>&gt;</span>\n    <span class="hljs-tag">&lt;/<span class="hljs-name">listener</span>&gt;</span>\n    \n    <span class="hljs-tag">&lt;<span class="hljs-name">listener</span>&gt;</span>\n        <span class="hljs-tag">&lt;<span class="hljs-name">listener-class</span>&gt;</span>org.springframework.web.context.ContextLoaderListener<span class="hljs-tag">&lt;/<span class="hljs-name">listener-class</span>&gt;</span>\n    <span class="hljs-tag">&lt;/<span class="hljs-name">listener</span>&gt;</span>\n    \n    <span class="hljs-tag">&lt;<span class="hljs-name">servlet</span>&gt;</span>\n        <span class="hljs-tag">&lt;<span class="hljs-name">servlet-name</span>&gt;</span>dispatcher<span class="hljs-tag">&lt;/<span class="hljs-name">servlet-name</span>&gt;</span>\n        <span class="hljs-tag">&lt;<span class="hljs-name">servlet-class</span>&gt;</span>com.alibaba.dubbo.remoting.http.servlet.DispatcherServlet<span class="hljs-tag">&lt;/<span class="hljs-name">servlet-class</span>&gt;</span>\n        <span class="hljs-tag">&lt;<span class="hljs-name">load-on-startup</span>&gt;</span>1<span class="hljs-tag">&lt;/<span class="hljs-name">load-on-startup</span>&gt;</span>\n    <span class="hljs-tag">&lt;/<span class="hljs-name">servlet</span>&gt;</span>\n    \n    <span class="hljs-tag">&lt;<span class="hljs-name">servlet-mapping</span>&gt;</span>\n        <span class="hljs-tag">&lt;<span class="hljs-name">servlet-name</span>&gt;</span>dispatcher<span class="hljs-tag">&lt;/<span class="hljs-name">servlet-name</span>&gt;</span>\n        <span class="hljs-tag">&lt;<span class="hljs-name">url-pattern</span>&gt;</span>/*<span class="hljs-tag">&lt;/<span class="hljs-name">url-pattern</span>&gt;</span>\n    <span class="hljs-tag">&lt;/<span class="hljs-name">servlet-mapping</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">web-app</span>&gt;</span>\n</code></pre>\n<p>即必须将dubbo的BootstrapListener和DispatherServlet添加到web.xml,以完成dubbo的REST功能与外部servlet容器的集成。</p>\n<blockquote>\n<p>注意:如果你是用spring的ContextLoaderListener来加载spring,则必须保证BootstrapListener配置在ContextLoaderListener之前,否则dubbo初始化会出错。</p>\n</blockquote>\n<p>其实,这种场景下你依然可以坚持用嵌入式server,但外部应用服务器的servlet容器往往比嵌入式server更加强大(特别是如果你是部署到更健壮更可伸缩的WebLogic,WebSphere等),另外有时也便于在应用服务器做统一管理、监控等等。</p>\n<h3>获取上下文(Context)信息</h3>\n<p>在远程调用中,值得获取的上下文信息可能有很多种,这里特别以获取客户端IP为例。</p>\n<p>在dubbo的REST中,我们有两种方式获取客户端IP。</p>\n<p>第一种方式,用JAX-RS标准的@Context annotation:</p>\n<pre><code class="language-java"><span class="hljs-function"><span class="hljs-keyword">public</span> User <span class="hljs-title">getUser</span><span class="hljs-params">(@PathParam(<span class="hljs-string">"id"</span>)</span> Long id, @Context HttpServletRequest request) </span>{\n    System.out.println(<span class="hljs-string">"Client address is "</span> + request.getRemoteAddr());\n} \n</code></pre>\n<p>用Context修饰getUser()的一个方法参数后,就可以将当前的HttpServletRequest注入进来,然后直接调用servlet api获取IP。</p>\n<blockquote>\n<p>注意:这种方式只能在设置server=&quot;tjws&quot;或者server=&quot;tomcat&quot;或者server=&quot;jetty&quot;或者server=&quot;servlet&quot;的时候才能工作,因为只有这几种REST server的实现才提供了servlet容器。另外,标准的JAX-RS还支持用@Context修饰service类的一个实例字段来获取HttpServletRequest,但在dubbo中我们没有对此作出支持。</p>\n</blockquote>\n<p>第二种方式,用dubbo中常用的RpcContext:</p>\n<pre><code class="language-java"><span class="hljs-function"><span class="hljs-keyword">public</span> User <span class="hljs-title">getUser</span><span class="hljs-params">(@PathParam(<span class="hljs-string">"id"</span>)</span> Long id) </span>{\n    System.out.println(<span class="hljs-string">"Client address is "</span> + RpcContext.getContext().getRemoteAddressString());\n} \n</code></pre>\n<blockquote>\n<p>注意:这种方式只能在设置server=&quot;jetty&quot;或者server=&quot;tomcat&quot;或者server=&quot;servlet&quot;或者server=&quot;tjws&quot;的时候才能工作。另外,目前dubbo的RpcContext是一种比较有侵入性的用法,未来我们很可能会做出重构。</p>\n</blockquote>\n<p>如果你想保持你的项目对JAX-RS的兼容性,未来脱离dubbo也可以运行,请选择第一种方式。如果你想要更优雅的服务接口定义,请选用第二种方式。</p>\n<p>此外,在最新的dubbo rest中,还支持通过RpcContext来获取HttpServletRequest和HttpServletResponse,以提供更大的灵活性来方便用户实现某些复杂功能,比如在dubbo标准的filter中访问HTTP Header。用法示例如下:</p>\n<pre><code class="language-java"><span class="hljs-keyword">if</span> (RpcContext.getContext().getRequest() != <span class="hljs-keyword">null</span> &amp;&amp; RpcContext.getContext().getRequest() <span class="hljs-keyword">instanceof</span> HttpServletRequest) {\n    System.out.println(<span class="hljs-string">"Client address is "</span> + ((HttpServletRequest) RpcContext.getContext().getRequest()).getRemoteAddr());\n}\n\n<span class="hljs-keyword">if</span> (RpcContext.getContext().getResponse() != <span class="hljs-keyword">null</span> &amp;&amp; RpcContext.getContext().getResponse() <span class="hljs-keyword">instanceof</span> HttpServletResponse) {\n    System.out.println(<span class="hljs-string">"Response object from RpcContext: "</span> + RpcContext.getContext().getResponse());\n}\n</code></pre>\n<blockquote>\n<p>注意:为了保持协议的中立性,RpcContext.getRequest()和RpcContext.getResponse()返回的仅仅是一个Object类,而且可能为null。所以,你必须自己做null和类型的检查。</p>\n</blockquote>\n<blockquote>\n<p>注意:只有在设置server=&quot;jetty&quot;或者server=&quot;tomcat&quot;或者server=&quot;servlet&quot;的时候,你才能通过以上方法正确的得到HttpServletRequest和HttpServletResponse,因为只有这几种server实现了servlet容器。</p>\n</blockquote>\n<p>为了简化编程,在此你也可以用泛型的方式来直接获取特定类型的request/response:</p>\n<pre><code class="language-java"><span class="hljs-keyword">if</span> (RpcContext.getContext().getRequest(HttpServletRequest.class) != <span class="hljs-keyword">null</span>) {\n    System.out.println(<span class="hljs-string">"Client address is "</span> + RpcContext.getContext().getRequest(HttpServletRequest.class).getRemoteAddr());\n}\n\n<span class="hljs-keyword">if</span> (RpcContext.getContext().getResponse(HttpServletResponse.class) != <span class="hljs-keyword">null</span>) {\n    System.out.println(<span class="hljs-string">"Response object from RpcContext: "</span> + RpcContext.getContext().getResponse(HttpServletResponse.class));\n}\n</code></pre>\n<p>如果request/response不符合指定的类型,这里也会返回null。</p>\n<h3>配置端口号和Context Path</h3>\n<p>dubbo中的rest协议默认将采用80端口,如果想修改端口,直接配置:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"8888"</span>/&gt;</span>\n</code></pre>\n<p>另外,如前所述,我们可以用@Path来配置单个rest服务的URL相对路径。但其实,我们还可以设置一个所有rest服务都适用的基础相对路径,即java web应用中常说的context path。</p>\n<p>只需要添加如下contextpath属性即可:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"8888"</span> <span class="hljs-attr">contextpath</span>=<span class="hljs-string">"services"</span>/&gt;</span>\n</code></pre>\n<p>以前面代码为例:</p>\n<pre><code class="language-java"><span class="hljs-meta">@Path</span>(<span class="hljs-string">"users"</span>)\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">UserService</span> </span>{\n       \n    <span class="hljs-meta">@POST</span>\n    <span class="hljs-meta">@Path</span>(<span class="hljs-string">"register"</span>)\n    <span class="hljs-meta">@Consumes</span>({MediaType.APPLICATION_JSON})\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">registerUser</span><span class="hljs-params">(User user)</span> </span>{\n        <span class="hljs-comment">// save the user...</span>\n    }\t\n}\n</code></pre>\n<p>现在registerUser()的完整访问路径为:</p>\n<pre><code>http://localhost:8888/services/users/register\n</code></pre>\n<p>注意:如果你是选用外部应用服务器做rest server,即配置:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"8888"</span> <span class="hljs-attr">contextpath</span>=<span class="hljs-string">"services"</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"servlet"</span>/&gt;</span>\n</code></pre>\n<p>则必须保证这里设置的port、contextpath,与外部应用服务器的端口、DispatcherServlet的上下文路径(即webapp path加上servlet url pattern)保持一致。例如,对于部署为tomcat ROOT路径的应用,这里的contextpath必须与web.xml中DispacherServlet的<code>&lt;url-pattern/&gt;</code>完全一致:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">servlet-mapping</span>&gt;</span>\n     <span class="hljs-tag">&lt;<span class="hljs-name">servlet-name</span>&gt;</span>dispatcher<span class="hljs-tag">&lt;/<span class="hljs-name">servlet-name</span>&gt;</span>\n     <span class="hljs-tag">&lt;<span class="hljs-name">url-pattern</span>&gt;</span>/services/*<span class="hljs-tag">&lt;/<span class="hljs-name">url-pattern</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">servlet-mapping</span>&gt;</span>\n</code></pre>\n<h3>配置线程数和IO线程数</h3>\n<p>可以为rest服务配置线程池大小:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">threads</span>=<span class="hljs-string">"500"</span>/&gt;</span>\n</code></pre>\n<blockquote>\n<p>注意:目前线程池的设置只有当server=&quot;netty&quot;或者server=&quot;jetty&quot;或者server=&quot;tomcat&quot;的时候才能生效。另外,如果server=&quot;servlet&quot;,由于这时候启用的是外部应用服务器做rest server,不受dubbo控制,所以这里的线程池设置也无效。</p>\n</blockquote>\n<p>如果是选用netty server,还可以配置Netty的IO worker线程数:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">iothreads</span>=<span class="hljs-string">"5"</span> <span class="hljs-attr">threads</span>=<span class="hljs-string">"100"</span>/&gt;</span>\n</code></pre>\n<h3>配置长连接</h3>\n<p>Dubbo中的rest服务默认都是采用http长连接来访问,如果想切换为短连接,直接配置:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">keepalive</span>=<span class="hljs-string">"false"</span>/&gt;</span>\n</code></pre>\n<blockquote>\n<p>注意:这个配置目前只对server=&quot;netty&quot;和server=&quot;tomcat&quot;才能生效。</p>\n</blockquote>\n<h3>配置最大的HTTP连接数</h3>\n<p>可以配置服务器提供端所能同时接收的最大HTTP连接数,防止REST server被过多连接撑爆,以作为一种最基本的自我保护机制:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">accepts</span>=<span class="hljs-string">"500"</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"tomcat/&gt;\n</span></span></code></pre>\n<blockquote>\n<p>注意:这个配置目前只对server=&quot;tomcat&quot;才能生效。</p>\n</blockquote>\n<h3>配置每个消费端的超时时间和HTTP连接数</h3>\n<p>如果rest服务的消费端也是dubbo系统,可以像其他dubbo RPC机制一样,配置消费端调用此rest服务的最大超时时间以及每个消费端所能启动的最大HTTP连接数。</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"xxx"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"xxx"</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">timeout</span>=<span class="hljs-string">"2000"</span> <span class="hljs-attr">connections</span>=<span class="hljs-string">"10"</span>/&gt;</span>\n</code></pre>\n<p>当然,由于这个配置针对消费端生效的,所以也可以在消费端配置:</p>\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">"xxx"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"xxx"</span> <span class="hljs-attr">timeout</span>=<span class="hljs-string">"2000"</span> <span class="hljs-attr">connections</span>=<span class="hljs-string">"10"</span>/&gt;</span>\n</code></pre>\n<p>但是,通常我们建议配置在服务提供端提供此类配置。按照dubbo官方文档的说法:“Provider上尽量多配置Consumer端的属性,让Provider实现者一开始就思考Provider服务特点、服务质量的问题。”</p>\n<blockquote>\n<p>注意:如果dubbo的REST服务是发布给非dubbo的客户端使用,则这里<code>&lt;dubbo:service/&gt;</code>上的配置完全无效,因为这种客户端不受dubbo控制。</p>\n</blockquote>\n<h3>用Annotation取代部分Spring XML配置</h3>\n<p>以上所有的讨论都是基于dubbo在spring中的xml配置。但是,dubbo/spring本身也支持用annotation来作配置,所以我们也可以按dubbo官方文档中的步骤,把相关annotation加到REST服务的实现中,取代一些xml配置,例如:</p>\n<pre><code class="language-java"><span class="hljs-meta">@Service</span>(protocol = <span class="hljs-string">"rest"</span>)\n<span class="hljs-meta">@Path</span>(<span class="hljs-string">"users"</span>)\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">UserService</span> </span>{\n\n    <span class="hljs-meta">@Autowired</span>\n    <span class="hljs-keyword">private</span> UserRepository userRepository;\n       \n    <span class="hljs-meta">@POST</span>\n    <span class="hljs-meta">@Path</span>(<span class="hljs-string">"register"</span>)\n    <span class="hljs-meta">@Consumes</span>({MediaType.APPLICATION_JSON})\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">registerUser</span><span class="hljs-params">(User user)</span> </span>{\n        <span class="hljs-comment">// save the user</span>\n        userRepository.save(user);\n    }\t\n}\n</code></pre>\n<p>annotation的配置更简单更精确,经常也更便于维护(当然现代IDE都可以在xml中支持比如类名重构,所以就这里的特定用例而言,xml的维护性也很好)。而xml对代码的侵入性更小一些,尤其有利于动态修改配置,特别是比如你要针对单个服务配置连接超时时间、每客户端最大连接数、集群策略、权重等等。另外,特别对复杂应用或者模块来说,xml提供了一个中心点来涵盖的所有组件和配置,更一目了然,一般更便于项目长时期的维护。</p>\n<p>当然,选择哪种配置方式没有绝对的优劣,和个人的偏好也不无关系。</p>\n<h3>添加自定义的Filter、Interceptor等</h3>\n<p>Dubbo的REST也支持JAX-RS标准的Filter和Interceptor,以方便对REST的请求与响应过程做定制化的拦截处理。</p>\n<p>其中,Filter主要用于访问和设置HTTP请求和响应的参数、URI等等。例如,设置HTTP响应的cache header:</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">CacheControlFilter</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">ContainerResponseFilter</span> </span>{\n\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">filter</span><span class="hljs-params">(ContainerRequestContext req, ContainerResponseContext res)</span> </span>{\n        <span class="hljs-keyword">if</span> (req.getMethod().equals(<span class="hljs-string">"GET"</span>)) {\n            res.getHeaders().add(<span class="hljs-string">"Cache-Control"</span>, <span class="hljs-string">"someValue"</span>);\n        }\n    }\n}\n</code></pre>\n<p>Interceptor主要用于访问和修改输入与输出字节流,例如,手动添加GZIP压缩:</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">GZIPWriterInterceptor</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">WriterInterceptor</span> </span>{\n \n    <span class="hljs-meta">@Override</span>\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">aroundWriteTo</span><span class="hljs-params">(WriterInterceptorContext context)</span>\n                    <span class="hljs-keyword">throws</span> IOException, WebApplicationException </span>{\n        OutputStream outputStream = context.getOutputStream();\n        context.setOutputStream(<span class="hljs-keyword">new</span> GZIPOutputStream(outputStream));\n        context.proceed();\n    }\n}\n</code></pre>\n<p>在标准JAX-RS应用中,我们一般是为Filter和Interceptor添加@Provider annotation,然后JAX-RS runtime会自动发现并启用它们。而在dubbo中,我们是通过添加XML配置的方式来注册Filter和Interceptor:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"8888"</span> <span class="hljs-attr">extension</span>=<span class="hljs-string">"xxx.TraceInterceptor, xxx.TraceFilter"</span>/&gt;</span>\n</code></pre>\n<p>在此,我们可以将Filter、Interceptor和DynamicFuture这三种类型的对象都添加到extension属性上,多个之间用逗号分隔。(DynamicFuture是另一个接口,可以方便我们更动态的启用Filter和Interceptor,感兴趣请自行google。)</p>\n<p>当然,dubbo自身也支持Filter的概念,但我们这里讨论的Filter和Interceptor更加接近协议实现的底层,相比dubbo的filter,可以做更底层的定制化。</p>\n<blockquote>\n<p>注:这里的XML属性叫extension,而不是叫interceptor或者filter,是因为除了Interceptor和Filter,未来我们还会添加更多的扩展类型。</p>\n</blockquote>\n<p>如果REST的消费端也是dubbo系统(参见下文的讨论),则也可以用类似方式为消费端配置Interceptor和Filter。但注意,JAX-RS中消费端的Filter和提供端的Filter是两种不同的接口。例如前面例子中服务端是ContainerResponseFilter接口,而消费端对应的是ClientResponseFilter:</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">LoggingFilter</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">ClientResponseFilter</span> </span>{\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">filter</span><span class="hljs-params">(ClientRequestContext reqCtx, ClientResponseContext resCtx)</span> <span class="hljs-keyword">throws</span> IOException </span>{\n        System.out.println(<span class="hljs-string">"status: "</span> + resCtx.getStatus());\n\t    System.out.println(<span class="hljs-string">"date: "</span> + resCtx.getDate());\n\t    System.out.println(<span class="hljs-string">"last-modified: "</span> + resCtx.getLastModified());\n\t    System.out.println(<span class="hljs-string">"location: "</span> + resCtx.getLocation());\n\t    System.out.println(<span class="hljs-string">"headers:"</span>);\n\t    <span class="hljs-keyword">for</span> (Entry&lt;String, List&lt;String&gt;&gt; header : resCtx.getHeaders().entrySet()) {\n     \t    System.out.print(<span class="hljs-string">"\\t"</span> + header.getKey() + <span class="hljs-string">" :"</span>);\n\t        <span class="hljs-keyword">for</span> (String value : header.getValue()) {\n\t            System.out.print(value + <span class="hljs-string">", "</span>);\n\t        }\n\t        System.out.print(<span class="hljs-string">"\\n"</span>);\n\t    }\n\t    System.out.println(<span class="hljs-string">"media-type: "</span> + resCtx.getMediaType().getType());\n    } \n}\n</code></pre>\n<h3>添加自定义的Exception处理</h3>\n<p>Dubbo的REST也支持JAX-RS标准的ExceptionMapper,可以用来定制特定exception发生后应该返回的HTTP响应。</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">CustomExceptionMapper</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">ExceptionMapper</span>&lt;<span class="hljs-title">NotFoundException</span>&gt; </span>{\n\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Response <span class="hljs-title">toResponse</span><span class="hljs-params">(NotFoundException e)</span> </span>{     \n        <span class="hljs-keyword">return</span> Response.status(Response.Status.NOT_FOUND).entity(<span class="hljs-string">"Oops! the requested resource is not found!"</span>).type(<span class="hljs-string">"text/plain"</span>).build();\n    }\n}\n</code></pre>\n<p>和Interceptor、Filter类似,将其添加到XML配置文件中即可启用:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"8888"</span> <span class="hljs-attr">extension</span>=<span class="hljs-string">"xxx.CustomExceptionMapper"</span>/&gt;</span>\n</code></pre>\n<h3>配置HTTP日志输出</h3>\n<p>Dubbo rest支持输出所有HTTP请求/响应中的header字段和body消息体。</p>\n<p>在XML配置中添加如下自带的REST filter:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"8888"</span> <span class="hljs-attr">extension</span>=<span class="hljs-string">"com.alibaba.dubbo.rpc.protocol.rest.support.LoggingFilter"</span>/&gt;</span>\n</code></pre>\n<p>然后配置在logging配置中至少为com.alibaba.dubbo.rpc.protocol.rest.support打开INFO级别日志输出,例如,在log4j.xml中配置:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">logger</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"com.alibaba.dubbo.rpc.protocol.rest.support"</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">level</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"INFO"</span>/&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">appender-ref</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"CONSOLE"</span>/&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">logger</span>&gt;</span>\n</code></pre>\n<p>当然,你也可以直接在ROOT logger打开INFO级别日志输出:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">root</span>&gt;</span>\n\t<span class="hljs-tag">&lt;<span class="hljs-name">level</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"INFO"</span> /&gt;</span>\n\t<span class="hljs-tag">&lt;<span class="hljs-name">appender-ref</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"CONSOLE"</span>/&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">root</span>&gt;</span>\n</code></pre>\n<p>然后在日志中会有类似如下的内容输出:</p>\n<pre><code>The HTTP headers are: \naccept: application/json;charset=UTF-8\naccept-encoding: gzip, deflate\nconnection: Keep-Alive\ncontent-length: 22\ncontent-type: application/json\nhost: 192.168.1.100:8888\nuser-agent: Apache-HttpClient/4.2.1 (java 1.5)\n</code></pre>\n<pre><code>The contents of request body is: \n{&quot;id&quot;:1,&quot;name&quot;:&quot;dang&quot;}\n</code></pre>\n<p>打开HTTP日志输出后,除了正常日志输出的性能开销外,也会在比如HTTP请求解析时产生额外的开销,因为需要建立额外的内存缓冲区来为日志的输出做数据准备。</p>\n<h3>输入参数的校验</h3>\n<p>dubbo的rest支持采用Java标准的bean validation annotation(JSR 303)来做输入校验http://beanvalidation.org/</p>\n<p>为了和其他dubbo远程调用协议保持一致,在rest中作校验的annotation必须放在服务的接口上,例如:</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">UserService</span> </span>{\n   \n    <span class="hljs-function">User <span class="hljs-title">getUser</span><span class="hljs-params">(@Min(value=<span class="hljs-number">1</span>L, message=<span class="hljs-string">"User ID must be greater than 1"</span>)</span> Long id)</span>;\n}\n\n</code></pre>\n<p>当然,在很多其他的bean validation的应用场景都是将annotation放到实现类而不是接口上。把annotation放在接口上至少有一个好处是,dubbo的客户端可以共享这个接口的信息,dubbo甚至不需要做远程调用,在本地就可以完成输入校验。</p>\n<p>然后按照dubbo的标准方式在XML配置中打开验证:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">xxx.UserService</span>" <span class="hljs-attr">ref</span>=<span class="hljs-string">"userService"</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">validation</span>=<span class="hljs-string">"true"</span>/&gt;</span>\n</code></pre>\n<p>在dubbo的其他很多远程调用协议中,如果输入验证出错,是直接将<code>RpcException</code>抛向客户端,而在rest中由于客户端经常是非dubbo,甚至非java的系统,所以不便直接抛出Java异常。因此,目前我们将校验错误以XML的格式返回:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">violationReport</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">constraintViolations</span>&gt;</span>\n        <span class="hljs-tag">&lt;<span class="hljs-name">path</span>&gt;</span>getUserArgument0<span class="hljs-tag">&lt;/<span class="hljs-name">path</span>&gt;</span>\n        <span class="hljs-tag">&lt;<span class="hljs-name">message</span>&gt;</span>User ID must be greater than 1<span class="hljs-tag">&lt;/<span class="hljs-name">message</span>&gt;</span>\n        <span class="hljs-tag">&lt;<span class="hljs-name">value</span>&gt;</span>0<span class="hljs-tag">&lt;/<span class="hljs-name">value</span>&gt;</span>\n    <span class="hljs-tag">&lt;/<span class="hljs-name">constraintViolations</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">violationReport</span>&gt;</span>\n</code></pre>\n<p>稍后也会支持其他数据格式的返回值。至于如何对验证错误消息作国际化处理,直接参考bean validation的相关文档即可。</p>\n<p>如果你认为默认的校验错误返回格式不符合你的要求,可以如上面章节所述,添加自定义的ExceptionMapper来自由的定制错误返回格式。需要注意的是,这个ExceptionMapper必须用泛型声明来捕获dubbo的RpcException,才能成功覆盖dubbo rest默认的异常处理策略。为了简化操作,其实这里最简单的方式是直接继承dubbo rest的RpcExceptionMapper,并覆盖其中处理校验异常的方法即可:</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">MyValidationExceptionMapper</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">RpcExceptionMapper</span> </span>{\n\n    <span class="hljs-function"><span class="hljs-keyword">protected</span> Response <span class="hljs-title">handleConstraintViolationException</span><span class="hljs-params">(ConstraintViolationException cve)</span> </span>{\n        ViolationReport report = <span class="hljs-keyword">new</span> ViolationReport();\n        <span class="hljs-keyword">for</span> (ConstraintViolation cv : cve.getConstraintViolations()) {\n            report.addConstraintViolation(<span class="hljs-keyword">new</span> RestConstraintViolation(\n                    cv.getPropertyPath().toString(),\n                    cv.getMessage(),\n                    cv.getInvalidValue() == <span class="hljs-keyword">null</span> ? <span class="hljs-string">"null"</span> : cv.getInvalidValue().toString()));\n        }\n        <span class="hljs-comment">// 采用json输出代替xml输出</span>\n        <span class="hljs-keyword">return</span> Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(report).type(ContentType.APPLICATION_JSON_UTF_8).build();\n    }\n}\n</code></pre>\n<p>然后将这个ExceptionMapper添加到XML配置中即可:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"8888"</span> <span class="hljs-attr">extension</span>=<span class="hljs-string">"xxx.MyValidationExceptionMapper"</span>/&gt;</span>\n</code></pre>\n'},{filename:"user/references/protocol/rmi.md",__html:'<h1>rmi://</h1>\n<p>RMI 协议采用 JDK 标准的 <code>java.rmi.*</code> 实现,采用阻塞式短连接和 JDK 标准序列化方式。</p>\n<p>注意:如果正在使用 RMI 提供服务给外部访问 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>,同时应用里依赖了老的 common-collections 包 <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup> 的情况下,存在反序列化安全风险 <sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup>。</p>\n<h2>特性</h2>\n<ul>\n<li>连接个数:多连接</li>\n<li>连接方式:短连接</li>\n<li>传输协议:TCP</li>\n<li>传输方式:同步传输</li>\n<li>序列化:Java 标准二进制序列化</li>\n<li>适用范围:传入传出参数数据包大小混合,消费者与提供者个数差不多,可传文件。</li>\n<li>适用场景:常规远程服务方法调用,与原生RMI服务互操作</li>\n</ul>\n<h2>约束</h2>\n<ul>\n<li>参数及返回值需实现 <code>Serializable</code> 接口</li>\n<li>dubbo 配置中的超时时间对 RMI 无效,需使用 java 启动参数设置:<code>-Dsun.rmi.transport.tcp.responseTimeout=3000</code>,参见下面的 RMI 配置</li>\n</ul>\n<h2>dubbo.properties 配置</h2>\n<pre><code class="language-properties">dubbo.service.protocol=rmi\n</code></pre>\n<h2>RMI配置</h2>\n<pre><code class="language-sh">java -Dsun.rmi.transport.tcp.responseTimeout=3000\n</code></pre>\n<p>更多 RMI 优化参数请查看 <a href="http://download.oracle.com/docs/cd/E17409_01/javase/6/docs/technotes/guides/rmi/sunrmiproperties.html">JDK 文档</a></p>\n<h2>接口</h2>\n<p>如果服务接口继承了 <code>java.rmi.Remote</code> 接口,可以和原生 RMI 互操作,即:</p>\n<ul>\n<li>提供者用 Dubbo 的 RMI 协议暴露服务,消费者直接用标准 RMI 接口调用,</li>\n<li>或者提供方用标准 RMI 暴露服务,消费方用 Dubbo 的 RMI 协议调用。</li>\n</ul>\n<p>如果服务接口没有继承 <code>java.rmi.Remote</code> 接口:</p>\n<ul>\n<li>缺省 Dubbo 将自动生成一个 <code>com.xxx.XxxService$Remote</code> 的接口,并继承 <code>java.rmi.Remote</code> 接口,并以此接口暴露服务,</li>\n<li>但如果设置了 <code>&lt;dubbo:protocol name=&quot;rmi&quot; codec=&quot;spring&quot; /&gt;</code>,将不生成 <code>$Remote</code> 接口,而使用 Spring 的 <code>RmiInvocationHandler</code> 接口暴露服务,和 Spring 兼容。</li>\n</ul>\n<h2>配置</h2>\n<p>定义 RMI 协议:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rmi"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"1099"</span> /&gt;</span>\n</code></pre>\n<p>设置默认协议:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"rmi"</span> /&gt;</span>\n</code></pre>\n<p>设置服务协议:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"rmi"</span> /&gt;</span>\n</code></pre>\n<p>多端口:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"rmi1"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rmi"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"1099"</span> /&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"rmi2"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rmi"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"2099"</span> /&gt;</span>\n \n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"rmi1"</span> /&gt;</span>\n</code></pre>\n<p>Spring 兼容性:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rmi"</span> <span class="hljs-attr">codec</span>=<span class="hljs-string">"spring"</span> /&gt;</span>\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>公司内网环境应该不会有攻击风险 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>dubbo 不会依赖这个包,请排查自己的应用有没有使用 <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn3" class="footnote-item"><p>请检查应用:将 commons-collections3 请升级到 <a href="https://commons.apache.org/proper/commons-collections/release_3_2_2.html">3.2.2</a>;将 commons-collections4 请升级到 <a href="https://commons.apache.org/proper/commons-collections/release_4_1.html">4.1</a>。新版本的 commons-collections 解决了该问题 <a href="#fnref3" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/references/protocol/thrift.md",__html:'<h1>thrift://</h1>\n<p>当前 dubbo 支持 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>的 thrift 协议是对 thrift 原生协议 <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup> 的扩展,在原生协议的基础上添加了一些额外的头信息,比如 service name,magic number 等。</p>\n<p>使用 dubbo thrift 协议同样需要使用 thrift 的 idl compiler 编译生成相应的 java 代码,后续版本中会在这方面做一些增强。</p>\n<h2>依赖</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.apache.thrift<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>libthrift<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>0.8.0<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>\n</code></pre>\n<h2>配置</h2>\n<p>所有服务共用一个端口 <sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup>:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"thrift"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"3030"</span> /&gt;</span>\n</code></pre>\n<h2>使用</h2>\n<p>可以参考 <a href="https://github.com/apache/incubator-dubbo/tree/master/dubbo-rpc/dubbo-rpc-thrift/src/test/java/com/alibaba/dubbo/rpc/protocol/thrift/examples">dubbo 项目中的示例代码</a></p>\n<h2>常见问题</h2>\n<ul>\n<li>Thrift 不支持 null 值,即:不能在协议中传递 null 值</li>\n</ul>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p><code>2.3.0</code> 以上版本支持 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p><a href="http://thrift.apache.org">Thrift</a> 是 Facebook 捐给 Apache 的一个 RPC 框架 <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn3" class="footnote-item"><p>与原生Thrift不兼容 <a href="#fnref3" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/references/protocol/webservice.md",__html:'<h1>webservice://</h1>\n<p>基于 WebService 的远程调用协议,基于 <a href="http://cxf.apache.org">Apache CXF</a> <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> 的 <code>frontend-simple</code> 和 <code>transports-http</code> 实现 <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>。</p>\n<p>可以和原生 WebService 服务互操作,即:</p>\n<ul>\n<li>提供者用 Dubbo 的 WebService 协议暴露服务,消费者直接用标准 WebService 接口调用,</li>\n<li>或者提供方用标准 WebService 暴露服务,消费方用 Dubbo 的 WebService 协议调用。</li>\n</ul>\n<h2>依赖</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.apache.cxf<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>cxf-rt-frontend-simple<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>2.6.1<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.apache.cxf<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>cxf-rt-transports-http<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>2.6.1<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>\n</code></pre>\n<h2>特性</h2>\n<ul>\n<li>连接个数:多连接</li>\n<li>连接方式:短连接</li>\n<li>传输协议:HTTP</li>\n<li>传输方式:同步传输</li>\n<li>序列化:SOAP 文本序列化</li>\n<li>适用场景:系统集成,跨语言调用</li>\n</ul>\n<h2>约束</h2>\n<ul>\n<li>参数及返回值需实现 <code>Serializable</code> 接口</li>\n<li>参数尽量使用基本类型和 POJO</li>\n</ul>\n<h2>配置</h2>\n<p>配置协议:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"webservice"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"8080"</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"jetty"</span> /&gt;</span>\n</code></pre>\n<p>配置默认协议:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"webservice"</span> /&gt;</span>\n</code></pre>\n<p>配置服务协议:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"webservice"</span> /&gt;</span>\n</code></pre>\n<p>多端口:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"webservice1"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"webservice"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"8080"</span> /&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"webservice2"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"webservice"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"8081"</span> /&gt;</span>\n</code></pre>\n<p>直连:</p>\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">"helloService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"HelloWorld"</span> <span class="hljs-attr">url</span>=<span class="hljs-string">"webservice://10.20.153.10:8080/com.foo.HelloWorld"</span> /&gt;</span>\n</code></pre>\n<p>WSDL:</p>\n<pre><code>http://10.20.153.10:8080/com.foo.HelloWorld?wsdl\n</code></pre>\n<p>Jetty Server (默认):</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">...</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"jetty"</span> /&gt;</span>\n</code></pre>\n<p>Servlet Bridge Server (推荐):</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">...</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"servlet"</span> /&gt;</span>\n</code></pre>\n<p>配置 DispatcherServlet:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">servlet</span>&gt;</span>\n         <span class="hljs-tag">&lt;<span class="hljs-name">servlet-name</span>&gt;</span>dubbo<span class="hljs-tag">&lt;/<span class="hljs-name">servlet-name</span>&gt;</span>\n         <span class="hljs-tag">&lt;<span class="hljs-name">servlet-class</span>&gt;</span>com.alibaba.dubbo.remoting.http.servlet.DispatcherServlet<span class="hljs-tag">&lt;/<span class="hljs-name">servlet-class</span>&gt;</span>\n         <span class="hljs-tag">&lt;<span class="hljs-name">load-on-startup</span>&gt;</span>1<span class="hljs-tag">&lt;/<span class="hljs-name">load-on-startup</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">servlet</span>&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">servlet-mapping</span>&gt;</span>\n         <span class="hljs-tag">&lt;<span class="hljs-name">servlet-name</span>&gt;</span>dubbo<span class="hljs-tag">&lt;/<span class="hljs-name">servlet-name</span>&gt;</span>\n         <span class="hljs-tag">&lt;<span class="hljs-name">url-pattern</span>&gt;</span>/*<span class="hljs-tag">&lt;/<span class="hljs-name">url-pattern</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">servlet-mapping</span>&gt;</span>\n</code></pre>\n<p>注意,如果使用 servlet 派发请求:</p>\n<ul>\n<li>协议的端口 <code>&lt;dubbo:protocol port=&quot;8080&quot; /&gt;</code> 必须与 servlet 容器的端口相同,</li>\n<li>协议的上下文路径 <code>&lt;dubbo:protocol contextpath=&quot;foo&quot; /&gt;</code> 必须与 servlet 应用的上下文路径相同。</li>\n</ul>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>CXF 是 Apache 开源的一个 RPC 框架,由 Xfire 和 Celtix 合并而来 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p><code>2.3.0</code> 以上版本支持 <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/references/qos.md",__html:"<h1>新版本 telnet 命令使用说明</h1>\n<p>dubbo 2.5.8 新版本重构了 telnet 模块,提供了新的 telnet 命令支持。</p>\n<h3>端口</h3>\n<p>新版本的 telnet 端口 与 dubbo 协议的端口是不同的端口,默认为 <code>22222</code>,可通过配置文件<code>dubbo.properties</code> 修改:</p>\n<pre><code>dubbo.application.qos.port=33333\n</code></pre>\n<p>或者通过设置 JVM 参数:</p>\n<pre><code>-Ddubbo.application.qos.port=33333\n</code></pre>\n<h3>安全</h3>\n<p>默认情况下,dubbo 接收任何主机发起的命令,可通过配置文件<code>dubbo.properties</code> 修改:</p>\n<pre><code>dubbo.application.qos.accept.foreign.ip=false\n</code></pre>\n<p>或者通过设置 JVM 参数:</p>\n<pre><code>-Ddubbo.application.qos.accept.foreign.ip=false\n</code></pre>\n<p>拒绝远端主机发出的命令,只允许服务本机执行</p>\n<h3>telnet 与 http 协议</h3>\n<p>telnet 模块现在同时支持 http 协议和 telnet 协议,方便各种情况的使用</p>\n<p>示例如下:</p>\n<pre><code>➜  ~ telnet localhost 22222\nTrying ::1...\ntelnet: connect to address ::1: Connection refused\nTrying 127.0.0.1...\nConnected to localhost.\nEscape character is '^]'.\n  ████████▄  ███    █▄  ▀█████████▄  ▀█████████▄   ▄██████▄\n  ███   ▀███ ███    ███   ███    ███   ███    ███ ███    ███\n  ███    ███ ███    ███   ███    ███   ███    ███ ███    ███\n  ███    ███ ███    ███  ▄███▄▄▄██▀   ▄███▄▄▄██▀  ███    ███\n  ███    ███ ███    ███ ▀▀███▀▀▀██▄  ▀▀███▀▀▀██▄  ███    ███\n  ███    ███ ███    ███   ███    ██▄   ███    ██▄ ███    ███\n  ███   ▄███ ███    ███   ███    ███   ███    ███ ███    ███\n  ████████▀  ████████▀  ▄█████████▀  ▄█████████▀   ▀██████▀\n\n\ndubbo&gt;ls\nAs Provider side:\n+----------------------------------+---+\n|       Provider Service Name      |PUB|\n+----------------------------------+---+\n|com.alibaba.dubbo.demo.DemoService| N |\n+----------------------------------+---+\nAs Consumer side:\n+---------------------+---+\n|Consumer Service Name|NUM|\n+---------------------+---+\n\ndubbo&gt;\n</code></pre>\n<pre><code>➜  ~ curl &quot;localhost:22222/ls?arg1=xxx&amp;arg2=xxxx&quot;\nAs Provider side:\n+----------------------------------+---+\n|       Provider Service Name      |PUB|\n+----------------------------------+---+\n|com.alibaba.dubbo.demo.DemoService| N |\n+----------------------------------+---+\nAs Consumer side:\n+---------------------+---+\n|Consumer Service Name|NUM|\n+---------------------+---+\n</code></pre>\n<h3>ls 列出消费者和提供者</h3>\n<pre><code>dubbo&gt;ls\nAs Provider side:\n+----------------------------------+---+\n|       Provider Service Name      |PUB|\n+----------------------------------+---+\n|com.alibaba.dubbo.demo.DemoService| Y |\n+----------------------------------+---+\nAs Consumer side:\n+---------------------+---+\n|Consumer Service Name|NUM|\n+---------------------+---+\n</code></pre>\n<p>列出 dubbo 的所提供的服务和消费的服务,以及消费的服务地址数</p>\n<h3>Online 上线服务命令</h3>\n<p>当使用延迟发布功能的时候(通过设置 com.alibaba.dubbo.config.AbstractServiceConfig#register 为 false),后续需要上线的时候,可通过 Online 命令</p>\n<pre><code>//上线所有服务\ndubbo&gt;online\nOK\n\n//根据正则,上线部分服务\ndubbo&gt;online com.*\nOK\n</code></pre>\n<p>常见使用场景:</p>\n<ul>\n<li>当线上的 QPS 比较高的时候,当刚重启机器的时候,由于没有进行JIT 预热或相关资源没有预热,可能会导致大量超时,这个时候,可通过分批发布服务,逐渐加大流量</li>\n<li>当由于某台机器由于某种原因,需要下线服务,然后又需要重新上线服务</li>\n</ul>\n<h3>Offline 下线服务命令</h3>\n<p>由于故障等原因,需要临时下线服务保持现场,可以使用 Offline 下线命令。</p>\n<pre><code>//下线所有服务\ndubbo&gt;offline\nOK\n\n//根据正则,下线部分服务\ndubbo&gt;offline com.*\nOK\n</code></pre>\n<h3>help 命令</h3>\n<pre><code>//列出所有命令\ndubbo&gt;help\n\n//列出单个命令的具体使用情况\ndubbo&gt;help online\n+--------------+----------------------------------------------------------------------------------+\n| COMMAND NAME | online                                                                           |\n+--------------+----------------------------------------------------------------------------------+\n|      EXAMPLE | online dubbo                                                                     |\n|              | online xx.xx.xxx.service                                                         |\n+--------------+----------------------------------------------------------------------------------+\n\ndubbo&gt;\n</code></pre>\n"},{filename:"user/references/registry/introduction.md",__html:'<h2>注册中心参考手册</h2>\n<p>推荐使用 <a href="./zookeeper.md">Zookeeper 注册中心</a></p>\n'},{filename:"user/references/registry/multicast.md",__html:'<h1>Multicast 注册中心</h1>\n<p>Multicast 注册中心不需要启动任何中心节点,只要广播地址一样,就可以互相发现。</p>\n<p><img src="../../sources/images/multicast.jpg" alt="/user-guide/images/multicast.jpg"></p>\n<ol start="0">\n<li>提供方启动时广播自己的地址</li>\n<li>消费方启动时广播订阅请求</li>\n<li>提供方收到订阅请求时,单播自己的地址给订阅者,如果设置了 <code>unicast=false</code>,则广播给订阅者</li>\n<li>消费方收到提供方地址时,连接该地址进行 RPC 调用。</li>\n</ol>\n<p>组播受网络结构限制,只适合小规模应用或开发阶段使用。组播地址段: 224.0.0.0 - 239.255.255.255</p>\n<h2>配置</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"multicast://224.5.6.7:1234"</span> /&gt;</span>\n</code></pre>\n<p>或</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"multicast"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"224.5.6.7:1234"</span> /&gt;</span>\n</code></pre>\n<p>为了减少广播量,Dubbo 缺省使用单播发送提供者地址信息给消费者,如果一个机器上同时启了多个消费者进程,消费者需声明 <code>unicast=false</code>,否则只会有一个消费者能收到消息:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"multicast://224.5.6.7:1234?unicast=false"</span> /&gt;</span>\n</code></pre>\n<p>或</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"multicast"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"224.5.6.7:1234"</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:parameter</span> <span class="hljs-attr">key</span>=<span class="hljs-string">"unicast"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"false"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:registry</span>&gt;</span>\n</code></pre>\n'},{filename:"user/references/registry/redis.md",__html:'<h1>Redis 注册中心</h1>\n<p>基于 Redis <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> 实现的注册中心 <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>。</p>\n<p><img src="../../sources/images/dubbo-redis-registry.jpg" alt="/user-guide/images/dubbo-redis-registry.jpg"></p>\n<p>使用 Redis 的 Key/Map 结构存储数据结构:</p>\n<ul>\n<li>主 Key 为服务名和类型</li>\n<li>Map 中的 Key 为 URL 地址</li>\n<li>Map 中的 Value 为过期时间,用于判断脏数据,脏数据由监控中心删除 <sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup></li>\n</ul>\n<p>使用 Redis 的 Publish/Subscribe 事件通知数据变更:</p>\n<ul>\n<li>通过事件的值区分事件类型:<code>register</code>, <code>unregister</code>, <code>subscribe</code>, <code>unsubscribe</code></li>\n<li>普通消费者直接订阅指定服务提供者的 Key,只会收到指定服务的 <code>register</code>, <code>unregister</code> 事件</li>\n<li>监控中心通过 <code>psubscribe</code> 功能订阅 <code>/dubbo/*</code>,会收到所有服务的所有变更事件</li>\n</ul>\n<p>调用过程:</p>\n<ol start="0">\n<li>服务提供方启动时,向 <code>Key:/dubbo/com.foo.BarService/providers</code> 下,添加当前提供者的地址</li>\n<li>并向 <code>Channel:/dubbo/com.foo.BarService/providers</code> 发送 <code>register</code> 事件</li>\n<li>服务消费方启动时,从 <code>Channel:/dubbo/com.foo.BarService/providers</code> 订阅 <code>register</code> 和 <code>unregister</code> 事件</li>\n<li>并向 <code>Key:/dubbo/com.foo.BarService/providers</code> 下,添加当前消费者的地址</li>\n<li>服务消费方收到 <code>register</code> 和 <code>unregister</code> 事件后,从 <code>Key:/dubbo/com.foo.BarService/providers</code> 下获取提供者地址列表</li>\n<li>服务监控中心启动时,从 <code>Channel:/dubbo/*</code> 订阅 <code>register</code> 和 <code>unregister</code>,以及 <code>subscribe</code> 和<code>unsubsribe</code>事件</li>\n<li>服务监控中心收到 <code>register</code> 和 <code>unregister</code> 事件后,从 <code>Key:/dubbo/com.foo.BarService/providers</code> 下获取提供者地址列表</li>\n<li>服务监控中心收到 <code>subscribe</code> 和 <code>unsubsribe</code> 事件后,从 <code>Key:/dubbo/com.foo.BarService/consumers</code> 下获取消费者地址列表</li>\n</ol>\n<h2>配置</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"redis://10.20.153.10:6379"</span> /&gt;</span>\n</code></pre>\n<p>或</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"redis://10.20.153.10:6379?backup=10.20.153.11:6379,10.20.153.12:6379"</span> /&gt;</span>\n</code></pre>\n<p>或</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"redis"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.153.10:6379"</span> /&gt;</span>\n</code></pre>\n<p>或</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"redis"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.153.10:6379,10.20.153.11:6379,10.20.153.12:6379"</span> /&gt;</span>\n</code></pre>\n<h2>选项</h2>\n<ul>\n<li>可通过 <code>&lt;dubbo:registry group=&quot;dubbo&quot; /&gt;</code> 设置 redis 中 key 的前缀,缺省为 <code>dubbo</code>。</li>\n<li>可通过 <code>&lt;dubbo:registry cluster=&quot;replicate&quot; /&gt;</code> 设置 redis 集群策略,缺省为 <code>failover</code>:\n<ul>\n<li><code>failover</code>: 只写入和读取任意一台,失败时重试另一台,需要服务器端自行配置数据同步</li>\n<li><code>replicate</code>: 在客户端同时写入所有服务器,只读取单台,服务器端不需要同步,注册中心集群增大,性能压力也会更大</li>\n</ul>\n</li>\n</ul>\n<h2>可靠性声明</h2>\n<p>阿里内部并没有采用 Redis 做为注册中心,而是使用自己实现的基于数据库的注册中心,即:Redis 注册中心并没有在阿里内部长时间运行的可靠性保障,此 Redis 桥接实现只为开源版本提供,其可靠性依赖于 Redis 本身的可靠性。</p>\n<h2>安装</h2>\n<p>安装方式参见: <a href="http://dubbo.apache.org/books/dubbo-admin-book/install/redis.html">Redis安装手册</a>,只需搭一个原生的 Redis 服务器,并将 <a href="../../preface/usage.md">Quick Start</a> 中 Provider 和 Consumer 里的 <code>conf/dubbo.properties</code> 中的 <code>dubbo.registry.addrss</code> 的值改为 <code>redis://127.0.0.1:6379</code> 即可使用。</p>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p><a href="http://redis.io">Redis</a> 是一个高效的 KV 存储服务器 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>从 <code>2.1.0</code> 版本开始支持 <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn3" class="footnote-item"><p>Redis 过期数据通过心跳的方式检测脏数据,服务器时间必须同步,并且对服务器有一定压力,否则过期检测会不准确 <a href="#fnref3" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/references/registry/simple.md",__html:'<h1>Simple 注册中心</h1>\n<p>Simple 注册中心本身就是一个普通的 Dubbo 服务,可以减少第三方依赖,使整体通讯方式一致。</p>\n<h2>配置</h2>\n<p>将 Simple 注册中心暴露成 Dubbo 服务:</p>\n<pre><code class="language-xml"><span class="php"><span class="hljs-meta">&lt;?</span>xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"UTF-8"</span><span class="hljs-meta">?&gt;</span></span>\n<span class="hljs-tag">&lt;<span class="hljs-name">beans</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans"</span>\n    <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>\n    <span class="hljs-attr">xmlns:dubbo</span>=<span class="hljs-string">"http://dubbo.apache.org/schema/dubbo"</span>\n    <span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"</span>&gt;</span>\n    <span class="hljs-comment">&lt;!-- 当前应用信息配置 --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:application</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"simple-registry"</span> /&gt;</span>\n    <span class="hljs-comment">&lt;!-- 暴露服务协议配置 --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"9090"</span> /&gt;</span>\n    <span class="hljs-comment">&lt;!-- 暴露服务配置 --&gt;</span>\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.registry.RegistryService"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"registryService"</span> <span class="hljs-attr">registry</span>=<span class="hljs-string">"N/A"</span> <span class="hljs-attr">ondisconnect</span>=<span class="hljs-string">"disconnect"</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">"subscribe"</span>&gt;</span><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><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:method</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"unsubscribe"</span>&gt;</span><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">"false"</span> /&gt;</span><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    <span class="hljs-comment">&lt;!-- 简单注册中心实现,可自行扩展实现集群和状态同步 --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">bean</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"registryService"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"com.alibaba.dubbo.registry.simple.SimpleRegistryService"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">beans</span>&gt;</span>\n</code></pre>\n<p>引用 Simple Registry 服务:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"127.0.0.1:9090"</span> /&gt;</span>\n</code></pre>\n<p>或者:</p>\n<pre><code class="language-xml"><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.registry.RegistryService"</span> <span class="hljs-attr">group</span>=<span class="hljs-string">"simple"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0.0"</span> <span class="hljs-attr">...</span> &gt;</span>\n</code></pre>\n<p>或者:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"127.0.0.1:9090"</span> <span class="hljs-attr">group</span>=<span class="hljs-string">"simple"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0.0"</span> /&gt;</span>\n</code></pre>\n<h2>适用性说明</h2>\n<p>此 <code>SimpleRegistryService</code> 只是简单实现,不支持集群,可作为自定义注册中心的参考,但不适合直接用于生产环境。</p>\n'},{filename:"user/references/registry/zookeeper.md",__html:'<h1>zookeeper 注册中心</h1>\n<p><a href="http://zookeeper.apache.org">Zookeeper</a> 是 Apacahe Hadoop 的子项目,是一个树型的目录服务,支持变更推送,适合作为 Dubbo 服务的注册中心,工业强度较高,可用于生产环境,并推荐使用 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>。</p>\n<p><img src="../../sources/images/zookeeper.jpg" alt="/user-guide/images/zookeeper.jpg"></p>\n<p>流程说明:</p>\n<ul>\n<li>服务提供者启动时: 向 <code>/dubbo/com.foo.BarService/providers</code> 目录下写入自己的 URL 地址</li>\n<li>服务消费者启动时: 订阅 <code>/dubbo/com.foo.BarService/providers</code> 目录下的提供者 URL 地址。并向 <code>/dubbo/com.foo.BarService/consumers</code> 目录下写入自己的 URL 地址</li>\n<li>监控中心启动时: 订阅 <code>/dubbo/com.foo.BarService</code> 目录下的所有提供者和消费者 URL 地址。</li>\n</ul>\n<p>支持以下功能:</p>\n<ul>\n<li>当提供者出现断电等异常停机时,注册中心能自动删除提供者信息</li>\n<li>当注册中心重启时,能自动恢复注册数据,以及订阅请求</li>\n<li>当会话过期时,能自动恢复注册数据,以及订阅请求</li>\n<li>当设置 <code>&lt;dubbo:registry check=&quot;false&quot; /&gt;</code> 时,记录失败注册和订阅请求,后台定时重试</li>\n<li>可通过 <code>&lt;dubbo:registry username=&quot;admin&quot; password=&quot;1234&quot; /&gt;</code> 设置 zookeeper 登录信息</li>\n<li>可通过 <code>&lt;dubbo:registry group=&quot;dubbo&quot; /&gt;</code> 设置 zookeeper 的根节点,不设置将使用无根树</li>\n<li>支持 <code>*</code> 号通配符 <code>&lt;dubbo:reference group=&quot;*&quot; version=&quot;*&quot; /&gt;</code>,可订阅服务的所有分组和所有版本的提供者</li>\n</ul>\n<h2>使用</h2>\n<p>在 provider 和 consumer 中增加 zookeeper 客户端 jar 包依赖:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.apache.zookeeper<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>zookeeper<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>3.3.3<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>\n</code></pre>\n<p>或直接<a href="http://repo1.maven.org/maven2/org/apache/zookeeper/zookeeper">下载</a>。</p>\n<p>Dubbo 支持 zkclient 和 curator 两种 Zookeeper 客户端实现:</p>\n<h3>使用 zkclient 客户端</h3>\n<p>从 <code>2.2.0</code> 版本开始缺省为 zkclient 实现,以提升 zookeeper 客户端的健状性。<a href="https://github.com/sgroschupf/zkclient">zkclient</a> 是 Datameer 开源的一个 Zookeeper 客户端实现。</p>\n<p>缺省配置:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">...</span> <span class="hljs-attr">client</span>=<span class="hljs-string">"zkclient"</span> /&gt;</span>\n</code></pre>\n<p>或:</p>\n<pre><code class="language-sh">dubbo.registry.client=zkclient\n</code></pre>\n<p>或:</p>\n<pre><code class="language-sh">zookeeper://10.20.153.10:2181?client=zkclient\n</code></pre>\n<p>需依赖或直接<a href="http://repo1.maven.org/maven2/com/github/sgroschupf/zkclient">下载</a>:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.github.sgroschupf<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>zkclient<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>0.1<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>\n</code></pre>\n<h3>使用 curator 客户端</h3>\n<p>从 <code>2.3.0</code> 版本开始支持可选 curator 实现。<a href="https://github.com/Netflix/curator">Curator</a> 是 Netflix 开源的一个 Zookeeper 客户端实现。</p>\n<p>如果需要改为 curator 实现,请配置:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">...</span> <span class="hljs-attr">client</span>=<span class="hljs-string">"curator"</span> /&gt;</span>\n</code></pre>\n<p>或:</p>\n<pre><code class="language-sh">dubbo.registry.client=curator\n</code></pre>\n<p>或:</p>\n<pre><code class="language-sh">zookeeper://10.20.153.10:2181?client=curator\n</code></pre>\n<p>需依赖或直接<a href="http://repo1.maven.org/maven2/com/netflix/curator/curator-framework">下载</a>:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.netflix.curator<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>curator-framework<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>1.1.10<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>\n</code></pre>\n<p>Zookeeper 单机配置:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"zookeeper://10.20.153.10:2181"</span> /&gt;</span>\n</code></pre>\n<p>或:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"zookeeper"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.153.10:2181"</span> /&gt;</span>\n</code></pre>\n<p>Zookeeper 集群配置:</p>\n<pre><code class="language-xml">\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"zookeeper://10.20.153.10:2181?backup=10.20.153.11:2181,10.20.153.12:2181"</span> /&gt;</span>\n</code></pre>\n<p>或:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"zookeeper"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.153.10:2181,10.20.153.11:2181,10.20.153.12:2181"</span> /&gt;</span>\n</code></pre>\n<p>同一 Zookeeper,分成多组注册中心:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"chinaRegistry"</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"zookeeper"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.153.10:2181"</span> <span class="hljs-attr">group</span>=<span class="hljs-string">"china"</span> /&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"intlRegistry"</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"zookeeper"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.153.10:2181"</span> <span class="hljs-attr">group</span>=<span class="hljs-string">"intl"</span> /&gt;</span>\n</code></pre>\n<h2>zookeeper 安装</h2>\n<p>安装方式参见: <a href="http://dubbo.apache.org/books/dubbo-admin-book/install/zookeeper.html">Zookeeper安装手册</a>,只需搭一个原生的 Zookeeper 服务器,并将 <a href="../../preface/usage.md">Quick Start</a> 中 Provider 和 Consumer 里的 <code>conf/dubbo.properties</code> 中的 <code>dubbo.registry.addrss</code> 的值改为 <code>zookeeper://127.0.0.1:2181</code> 即可使用。</p>\n<h2>可靠性声明</h2>\n<p>阿里内部并没有采用 Zookeeper 做为注册中心,而是使用自己实现的基于数据库的注册中心,即:Zookeeper 注册中心并没有在阿里内部长时间运行的可靠性保障,此 Zookeeper 桥接实现只为开源版本提供,其可靠性依赖于 Zookeeper 本身的可靠性。</p>\n<h2>兼容性声明</h2>\n<p>因 <code>2.0.8</code> 最初设计的 zookeeper 存储结构不能扩充不同类型的数据,<code>2.0.9</code> 版本做了调整,所以不兼容,需全部改用 <code>2.0.9</code> 版本才行,以后的版本会保持兼容 <code>2.0.9</code>。<code>2.2.0</code> 版本改为基于 zkclient 实现,需增加 zkclient 的依赖包,<code>2.3.0</code> 版本增加了基于 curator 的实现,作为可选实现策略。</p>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>建议使用 <code>2.3.3</code> 以上版本的 zookeeper 注册中心客户端 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/references/telnet.md",__html:'<h1>Telnet 命令参考手册</h1>\n<p>从 <code>2.0.5</code> 版本开始,dubbo 开始支持通过 telnet 命令来进行服务治理。</p>\n<h2>使用</h2>\n<pre><code class="language-sh">telnet localhost 20880\n</code></pre>\n<p>或者:</p>\n<pre><code class="language-sh"><span class="hljs-built_in">echo</span> status | nc -i 1 localhost 20880\n</code></pre>\n<p>status命令所检查的资源也可以扩展,参见:<a href="http://dubbo.apache.org/books/dubbo-dev-book/impls/status-checker.html">扩展参考手册</a>。</p>\n<h2>命令</h2>\n<p>以下展示了 dubbo 内建的 telnet 命令的说明和用法,此外,telnet 命令还支持用户自行扩展,参见:<a href="http://dubbo.apache.org/books/dubbo-dev-book/impls/telnet-handler.html">Telnet 命令扩展</a>。</p>\n<h3><code>ls</code></h3>\n<ol start="0">\n<li><code>ls</code>: 显示服务列表</li>\n<li><code>ls -l</code>: 显示服务详细信息列表</li>\n<li><code>ls XxxService</code>: 显示服务的方法列表</li>\n<li><code>ls -l XxxService</code>: 显示服务的方法详细信息列表</li>\n</ol>\n<h3><code>ps</code></h3>\n<ol start="0">\n<li><code>ps</code>: 显示服务端口列表</li>\n<li><code>ps -l</code>: 显示服务地址列表</li>\n<li><code>ps 20880</code>: 显示端口上的连接信息</li>\n<li><code>ps -l 20880</code>: 显示端口上的连接详细信息</li>\n</ol>\n<h3><code>cd</code></h3>\n<ol start="0">\n<li><code>cd XxxService</code>: 改变缺省服务,当设置了缺省服务,凡是需要输入服务名作为参数的命令,都可以省略服务参数</li>\n<li><code>cd /</code>: 取消缺省服务</li>\n</ol>\n<h3><code>pwd</code></h3>\n<p><code>pwd</code>: 显示当前缺省服务</p>\n<h3><code>trace</code></h3>\n<ol start="0">\n<li><code>trace XxxService</code>: 跟踪 1 次服务任意方法的调用情况</li>\n<li><code>trace XxxService 10</code>: 跟踪 10 次服务任意方法的调用情况</li>\n<li><code>trace XxxService xxxMethod</code>: 跟踪 1 次服务方法的调用情况</li>\n<li><code>trace XxxService xxxMethod 10</code>: 跟踪 10 次服务方法的调用情况</li>\n</ol>\n<h3><code>count</code></h3>\n<ol start="0">\n<li><code>count XxxService</code>: 统计 1 次服务任意方法的调用情况</li>\n<li><code>count XxxService 10</code>: 统计 10 次服务任意方法的调用情况</li>\n<li><code>count XxxService xxxMethod</code>: 统计 1 次服务方法的调用情况</li>\n<li><code>count XxxService xxxMethod 10</code>: 统计 10 次服务方法的调用情况</li>\n</ol>\n<h3><code>invoke</code></h3>\n<ol start="0">\n<li><code>invoke XxxService.xxxMethod({&quot;prop&quot;: &quot;value&quot;})</code>: 调用服务的方法</li>\n<li><code>invoke xxxMethod({&quot;prop&quot;: &quot;value&quot;})</code>: 调用服务的方法(自动查找包含此方法的服务)</li>\n</ol>\n<h3><code>status</code></h3>\n<ol start="0">\n<li><code>status</code>: 显示汇总状态,该状态将汇总所有资源的状态,当全部 OK 时则显示 OK,只要有一个 ERROR 则显示 ERROR,只要有一个 WARN 则显示 WARN</li>\n<li><code>status -l</code>: 显示状态列表</li>\n</ol>\n<h3><code>log</code> <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup></h3>\n<ol start="0">\n<li><code>log debug</code>: 修改 dubbo logger 的日志级别</li>\n<li><code>log 100</code>: 查看 file logger 的最后 100 字符的日志</li>\n</ol>\n<h3><code>help</code></h3>\n<ol start="0">\n<li><code>help</code>: 显示 telnet 命帮助信息</li>\n<li><code>help xxx</code>: 显示xxx命令的详细帮助信息</li>\n</ol>\n<h3><code>clear</code></h3>\n<ol start="0">\n<li><code>clear</code>: 清除屏幕上的内容</li>\n<li><code>clear 100</code>: 清除屏幕上的指定行数的内容</li>\n</ol>\n<h3><code>exit</code></h3>\n<p><code>exit</code>: 退出当前 telnet 命令行</p>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p><code>2.0.6</code> 以上版本支持 <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/references/xml/dubbo-application.md",__html:"<style>\ntable {\n  width: 100%;\n  max-width: 65em;\n  border: 1px solid #dedede;\n  margin: 15px auto;\n  border-collapse: collapse;\n  empty-cells: show;\n}\ntable th,\ntable td {\n  height: 35px;\n  border: 1px solid #dedede;\n  padding: 0 10px;\n}\ntable th {\n  font-weight: bold;\n  text-align: center !important;\n  background: rgba(158,188,226,0.2);\n  white-space: nowrap;\n}\ntable tbody tr:nth-child(2n) {\n  background: rgba(158,188,226,0.12);\n}\ntable td:nth-child(1) {\n  white-space: nowrap;\n}\ntable tr:hover {\n  background: #efefef;\n}\n.table-area {\n  overflow: auto;\n}\n</style>\n<script type=\"text/javascript\">\n[].slice.call(document.querySelectorAll('table')).forEach(function(el){\n    var wrapper = document.createElement('div');\n    wrapper.className = 'table-area';\n    el.parentNode.insertBefore(wrapper, el);\n    el.parentNode.removeChild(el);\n    wrapper.appendChild(el);\n})\n<\/script>\n<h1>dubbo:application</h1>\n<p>应用信息配置。对应的配置类:<code>com.alibaba.dubbo.config.ApplicationConfig</code></p>\n<table>\n<thead>\n<tr>\n<th>属性</th>\n<th>对应URL参数</th>\n<th>类型</th>\n<th>是否必填</th>\n<th>缺省值</th>\n<th>作用</th>\n<th>描述</th>\n<th>兼容性</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>name</td>\n<td>application</td>\n<td>string</td>\n<td><b>必填</b></td>\n<td></td>\n<td>服务治理</td>\n<td>当前应用名称,用于注册中心计算应用间依赖关系,注意:消费者和提供者应用名不要一样,此参数不是匹配条件,你当前项目叫什么名字就填什么,和提供者消费者角色无关,比如:kylin应用调用了morgan应用的服务,则kylin项目配成kylin,morgan项目配成morgan,可能kylin也提供其它服务给别人使用,但kylin项目永远配成kylin,这样注册中心将显示kylin依赖于morgan</td>\n<td>1.0.16以上版本</td>\n</tr>\n<tr>\n<td>version</td>\n<td>application.version</td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>服务治理</td>\n<td>当前应用的版本</td>\n<td>2.2.0以上版本</td>\n</tr>\n<tr>\n<td>owner</td>\n<td>owner</td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>服务治理</td>\n<td>应用负责人,用于服务治理,请填写负责人公司邮箱前缀</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>organization</td>\n<td>organization</td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>服务治理</td>\n<td>组织名称(BU或部门),用于注册中心区分服务来源,此配置项建议不要使用autoconfig,直接写死在配置中,比如china,intl,itu,crm,asc,dw,aliexpress等</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>architecture <br class=\"atl-forced-newline\" /></td>\n<td>architecture <br class=\"atl-forced-newline\" /></td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>服务治理</td>\n<td>用于服务分层对应的架构。如,intl、china。不同的架构使用不同的分层。</td>\n<td>2.0.7以上版本</td>\n</tr>\n<tr>\n<td>environment</td>\n<td>environment</td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>服务治理</td>\n<td>应用环境,如:develop/test/product,不同环境使用不同的缺省值,以及作为只用于开发测试功能的限制条件</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>compiler</td>\n<td>compiler</td>\n<td>string</td>\n<td>可选</td>\n<td>javassist</td>\n<td>性能优化</td>\n<td>Java字节码编译器,用于动态类的生成,可选:jdk或javassist</td>\n<td>2.1.0以上版本</td>\n</tr>\n<tr>\n<td>logger</td>\n<td>logger</td>\n<td>string</td>\n<td>可选</td>\n<td>slf4j</td>\n<td>性能优化</td>\n<td>日志输出方式,可选:slf4j,jcl,log4j,jdk</td>\n<td>2.2.0以上版本</td>\n</tr>\n</tbody>\n</table>\n"},{filename:"user/references/xml/dubbo-argument.md",__html:'<style>\ntable {\n  width: 100%;\n  max-width: 65em;\n  border: 1px solid #dedede;\n  margin: 15px auto;\n  border-collapse: collapse;\n  empty-cells: show;\n}\ntable th,\ntable td {\n  height: 35px;\n  border: 1px solid #dedede;\n  padding: 0 10px;\n}\ntable th {\n  font-weight: bold;\n  text-align: center !important;\n  background: rgba(158,188,226,0.2);\n  white-space: nowrap;\n}\ntable tbody tr:nth-child(2n) {\n  background: rgba(158,188,226,0.12);\n}\ntable td:nth-child(1) {\n  white-space: nowrap;\n}\ntable tr:hover {\n  background: #efefef;\n}\n.table-area {\n  overflow: auto;\n}\n</style>\n<script type="text/javascript">\n[].slice.call(document.querySelectorAll(\'table\')).forEach(function(el){\n    var wrapper = document.createElement(\'div\');\n    wrapper.className = \'table-area\';\n    el.parentNode.insertBefore(wrapper, el);\n    el.parentNode.removeChild(el);\n    wrapper.appendChild(el);\n})\n<\/script>\n<h1>dubbo:argument</h1>\n<p>方法参数配置。对应的配置类: <code>com.alibaba.dubbo.config.ArgumentConfig</code>。该标签为 <code>&lt;dubbo:method&gt;</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">"findXxx"</span> <span class="hljs-attr">timeout</span>=<span class="hljs-string">"3000"</span> <span class="hljs-attr">retries</span>=<span class="hljs-string">"2"</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">"0"</span> <span class="hljs-attr">callback</span>=<span class="hljs-string">"true"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:method</span>&gt;</span>\n</code></pre>\n<table>\n<thead>\n<tr>\n<th>属性</th>\n<th>对应URL参数</th>\n<th>类型</th>\n<th>是否必填</th>\n<th>缺省值</th>\n<th>作用</th>\n<th>描述</th>\n<th>兼容性</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>index</td>\n<td></td>\n<td>int</td>\n<td><b>必填</b></td>\n<td></td>\n<td>标识</td>\n<td>方法名</td>\n<td>2.0.6以上版本</td>\n</tr>\n<tr>\n<td>type</td>\n<td></td>\n<td>String</td>\n<td>与index二选一</td>\n<td></td>\n<td>标识</td>\n<td>通过参数类型查找参数的index</td>\n<td>2.0.6以上版本</td>\n</tr>\n<tr>\n<td>callback</td>\n<td>&lt;metodName&gt;&lt;index&gt;.retries</td>\n<td>boolean</td>\n<td>可选</td>\n<td></td>\n<td>服务治理</td>\n<td>参数是否为callback接口,如果为callback,服务提供方将生成反向代理,可以从服务提供方反向调用消费方,通常用于事件推送.</td>\n<td>2.0.6以上版本</td>\n</tr>\n</tbody>\n</table>\n'},{filename:"user/references/xml/dubbo-consumer.md",__html:"<style>\ntable {\n  width: 100%;\n  max-width: 65em;\n  border: 1px solid #dedede;\n  margin: 15px auto;\n  border-collapse: collapse;\n  empty-cells: show;\n}\ntable th,\ntable td {\n  height: 35px;\n  border: 1px solid #dedede;\n  padding: 0 10px;\n}\ntable th {\n  font-weight: bold;\n  text-align: center !important;\n  background: rgba(158,188,226,0.2);\n  white-space: nowrap;\n}\ntable tbody tr:nth-child(2n) {\n  background: rgba(158,188,226,0.12);\n}\ntable td:nth-child(1) {\n  white-space: nowrap;\n}\ntable tr:hover {\n  background: #efefef;\n}\n.table-area {\n  overflow: auto;\n}\n</style>\n<script type=\"text/javascript\">\n[].slice.call(document.querySelectorAll('table')).forEach(function(el){\n    var wrapper = document.createElement('div');\n    wrapper.className = 'table-area';\n    el.parentNode.insertBefore(wrapper, el);\n    el.parentNode.removeChild(el);\n    wrapper.appendChild(el);\n})\n<\/script>\n<h1>dubbo:consumer</h1>\n<p>服务消费者缺省值配置。配置类: <code>com.alibaba.dubbo.config.ConsumerConfig</code> 。同时该标签为 <code>&lt;dubbo:reference&gt;</code> 标签的缺省值设置。</p>\n<table>\n<thead>\n<tr>\n<th>属性</th>\n<th>对应URL参数</th>\n<th>类型</th>\n<th>是否必填</th>\n<th>缺省值</th>\n<th>作用</th>\n<th>描述</th>\n<th>兼容性</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>timeout</td>\n<td>default.timeout</td>\n<td>int</td>\n<td>可选</td>\n<td>1000</td>\n<td>性能调优</td>\n<td>远程服务调用超时时间(毫秒)</td>\n<td>1.0.16以上版本</td>\n</tr>\n<tr>\n<td>retries</td>\n<td>default.retries</td>\n<td>int</td>\n<td>可选</td>\n<td>2</td>\n<td>性能调优</td>\n<td>远程服务调用重试次数,不包括第一次调用,不需要重试请设为0</td>\n<td>1.0.16以上版本</td>\n</tr>\n<tr>\n<td>loadbalance</td>\n<td>default.loadbalance</td>\n<td>string</td>\n<td>可选</td>\n<td>random</td>\n<td>性能调优</td>\n<td>负载均衡策略,可选值:random,roundrobin,leastactive,分别表示:随机,轮循,最少活跃调用</td>\n<td>1.0.16以上版本</td>\n</tr>\n<tr>\n<td>async</td>\n<td>default.async</td>\n<td>boolean</td>\n<td>可选</td>\n<td>false</td>\n<td>性能调优</td>\n<td>是否缺省异步执行,不可靠异步,只是忽略返回值,不阻塞执行线程</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>connections</td>\n<td>default.connections</td>\n<td>int</td>\n<td>可选</td>\n<td>100</td>\n<td>性能调优</td>\n<td>每个服务对每个提供者的最大连接数,rmi、http、hessian等短连接协议支持此配置,dubbo协议长连接不支持此配置</td>\n<td>1.0.16以上版本</td>\n</tr>\n<tr>\n<td>generic</td>\n<td>generic</td>\n<td>boolean</td>\n<td>可选</td>\n<td>false</td>\n<td>服务治理</td>\n<td>是否缺省泛化接口,如果为泛化接口,将返回GenericService</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>check</td>\n<td>check</td>\n<td>boolean</td>\n<td>可选</td>\n<td>true</td>\n<td>服务治理</td>\n<td>启动时检查提供者是否存在,true报错,false忽略</td>\n<td>1.0.16以上版本</td>\n</tr>\n<tr>\n<td>proxy</td>\n<td>proxy</td>\n<td>string</td>\n<td>可选</td>\n<td>javassist</td>\n<td>性能调优</td>\n<td>生成动态代理方式,可选:jdk/javassist</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>owner</td>\n<td>owner</td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>服务治理</td>\n<td>调用服务负责人,用于服务治理,请填写负责人公司邮箱前缀</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>actives</td>\n<td>default.actives</td>\n<td>int</td>\n<td>可选</td>\n<td>0</td>\n<td>性能调优</td>\n<td>每服务消费者每服务每方法最大并发调用数</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>cluster</td>\n<td>default.cluster</td>\n<td>string</td>\n<td>可选</td>\n<td>failover</td>\n<td>性能调优</td>\n<td>集群方式,可选:failover/failfast/failsafe/failback/forking</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>filter</td>\n<td>reference.filter</td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>性能调优</td>\n<td>服务消费方远程调用过程拦截器名称,多个名称用逗号分隔</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>listener</td>\n<td>invoker.listener</td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>性能调优</td>\n<td>服务消费方引用服务监听器名称,多个名称用逗号分隔</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>registry</td>\n<td></td>\n<td>string</td>\n<td>可选</td>\n<td>缺省向所有registry注册</td>\n<td>配置关联</td>\n<td>向指定注册中心注册,在多个注册中心时使用,值为&lt;dubbo:registry&gt;的id属性,多个注册中心ID用逗号分隔,如果不想将该服务注册到任何registry,可将值设为N/A</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>layer</td>\n<td>layer</td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>服务治理</td>\n<td>服务调用者所在的分层。如:biz、dao、intl:web、china:acton。</td>\n<td>2.0.7以上版本</td>\n</tr>\n<tr>\n<td>init</td>\n<td>init</td>\n<td>boolean</td>\n<td>可选</td>\n<td>false</td>\n<td>性能调优</td>\n<td>是否在afterPropertiesSet()时饥饿初始化引用,否则等到有人注入或引用该实例时再初始化。</td>\n<td>2.0.10以上版本</td>\n</tr>\n<tr>\n<td>cache</td>\n<td>cache</td>\n<td>string/boolean</td>\n<td>可选</td>\n<td></td>\n<td>服务治理</td>\n<td>以调用参数为key,缓存返回结果,可选:lru, threadlocal, jcache等</td>\n<td>Dubbo2.1.0及其以上版本支持</td>\n</tr>\n<tr>\n<td>validation</td>\n<td>validation</td>\n<td>boolean</td>\n<td>可选</td>\n<td></td>\n<td>服务治理</td>\n<td>是否启用JSR303标准注解验证,如果启用,将对方法参数上的注解进行校验</td>\n<td>Dubbo2.1.0及其以上版本支持</td>\n</tr>\n</tbody>\n</table>\n"},{filename:"user/references/xml/dubbo-method.md",__html:'<style>\ntable {\n  width: 100%;\n  max-width: 65em;\n  border: 1px solid #dedede;\n  margin: 15px auto;\n  border-collapse: collapse;\n  empty-cells: show;\n}\ntable th,\ntable td {\n  height: 35px;\n  border: 1px solid #dedede;\n  padding: 0 10px;\n}\ntable th {\n  font-weight: bold;\n  text-align: center !important;\n  background: rgba(158,188,226,0.2);\n  white-space: nowrap;\n}\ntable tbody tr:nth-child(2n) {\n  background: rgba(158,188,226,0.12);\n}\ntable td:nth-child(1) {\n  white-space: nowrap;\n}\ntable tr:hover {\n  background: #efefef;\n}\n.table-area {\n  overflow: auto;\n}\n</style>\n<script type="text/javascript">\n[].slice.call(document.querySelectorAll(\'table\')).forEach(function(el){\n    var wrapper = document.createElement(\'div\');\n    wrapper.className = \'table-area\';\n    el.parentNode.insertBefore(wrapper, el);\n    el.parentNode.removeChild(el);\n    wrapper.appendChild(el);\n})\n<\/script>\n<h1>dubbo:method</h1>\n<p>方法级配置。对应的配置类: <code>com.alibaba.dubbo.config.MethodConfig</code>。同时该标签为 <code>&lt;dubbo:service&gt;</code> 或 <code>&lt;dubbo:reference&gt;</code> 的子标签,用于控制到方法级。</p>\n<table>\n<thead>\n<tr>\n<th>属性</th>\n<th>对应URL参数</th>\n<th>类型</th>\n<th>是否必填</th>\n<th>缺省值</th>\n<th>作用</th>\n<th>描述</th>\n<th>兼容性</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>name</td>\n<td></td>\n<td>string</td>\n<td><b>必填</b></td>\n<td></td>\n<td>标识</td>\n<td>方法名</td>\n<td>1.0.8以上版本</td>\n</tr>\n<tr>\n<td>timeout</td>\n<td>&lt;metodName&gt;.timeout</td>\n<td>int</td>\n<td>可选</td>\n<td>缺省为的timeout</td>\n<td>性能调优</td>\n<td>方法调用超时时间(毫秒)</td>\n<td>1.0.8以上版本</td>\n</tr>\n<tr>\n<td>retries</td>\n<td>&lt;metodName&gt;.retries</td>\n<td>int</td>\n<td>可选</td>\n<td>缺省为&lt;dubbo:reference&gt;的retries</td>\n<td>性能调优</td>\n<td>远程服务调用重试次数,不包括第一次调用,不需要重试请设为0</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>loadbalance</td>\n<td>&lt;metodName&gt;.loadbalance</td>\n<td>string</td>\n<td>可选</td>\n<td>缺省为的loadbalance</td>\n<td>性能调优</td>\n<td>负载均衡策略,可选值:random,roundrobin,leastactive,分别表示:随机,轮循,最少活跃调用</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>async</td>\n<td>&lt;metodName&gt;.async</td>\n<td>boolean</td>\n<td>可选</td>\n<td>缺省为&lt;dubbo:reference&gt;的async</td>\n<td>性能调优</td>\n<td>是否异步执行,不可靠异步,只是忽略返回值,不阻塞执行线程</td>\n<td>1.0.9以上版本</td>\n</tr>\n<tr>\n<td>sent</td>\n<td>&lt;methodName&gt;.sent</td>\n<td>boolean</td>\n<td>可选</td>\n<td>true</td>\n<td>性能调优</td>\n<td>异步调用时,标记sent=true时,表示网络已发出数据</td>\n<td>2.0.6以上版本</td>\n</tr>\n<tr>\n<td>actives</td>\n<td>&lt;metodName&gt;.actives</td>\n<td>int</td>\n<td>可选</td>\n<td>0</td>\n<td>性能调优</td>\n<td>每服务消费者最大并发调用限制</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>executes</td>\n<td>&lt;metodName&gt;.executes</td>\n<td>int</td>\n<td>可选</td>\n<td>0</td>\n<td>性能调优</td>\n<td>每服务每方法最大使用线程数限制- -,此属性只在&lt;dubbo:method&gt;作为&lt;dubbo:service&gt;子标签时有效</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>deprecated</td>\n<td>&lt;methodName&gt;.deprecated</td>\n<td>boolean</td>\n<td>可选</td>\n<td>false</td>\n<td>服务治理</td>\n<td>服务方法是否过时,此属性只在&lt;dubbo:method&gt;作为&lt;dubbo:service&gt;子标签时有效</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>sticky</td>\n<td>&lt;methodName&gt;.sticky</td>\n<td>boolean</td>\n<td>可选</td>\n<td>false</td>\n<td>服务治理</td>\n<td>设置true 该接口上的所有方法使用同一个provider.如果需要更复杂的规则,请使用用路由</td>\n<td>2.0.6以上版本</td>\n</tr>\n<tr>\n<td>return</td>\n<td>&lt;methodName&gt;.return</td>\n<td>boolean</td>\n<td>可选</td>\n<td>true</td>\n<td>性能调优</td>\n<td>方法调用是否需要返回值,async设置为true时才生效,如果设置为true,则返回future,或回调onreturn等方法,如果设置为false,则请求发送成功后直接返回Null</td>\n<td>2.0.6以上版本</td>\n</tr>\n<tr>\n<td>oninvoke</td>\n<td>attribute属性,不在URL中体现</td>\n<td>String</td>\n<td>可选</td>\n<td></td>\n<td>性能调优</td>\n<td>方法执行前拦截</td>\n<td>2.0.6以上版本</td>\n</tr>\n<tr>\n<td>onreturn</td>\n<td>attribute属性,不在URL中体现</td>\n<td>String</td>\n<td>可选</td>\n<td></td>\n<td>性能调优</td>\n<td>方法执行返回后拦截</td>\n<td>2.0.6以上版本</td>\n</tr>\n<tr>\n<td>onthrow</td>\n<td>attribute属性,不在URL中体现</td>\n<td>String</td>\n<td>可选</td>\n<td></td>\n<td>性能调优</td>\n<td>方法执行有异常拦截</td>\n<td>2.0.6以上版本</td>\n</tr>\n<tr>\n<td>cache</td>\n<td>&lt;methodName&gt;.cache</td>\n<td>string/boolean</td>\n<td>可选</td>\n<td></td>\n<td>服务治理</td>\n<td>以调用参数为key,缓存返回结果,可选:lru, threadlocal, jcache等</td>\n<td>Dubbo2.1.0及其以上版本支持</td>\n</tr>\n<tr>\n<td>validation</td>\n<td>&lt;methodName&gt;.validation</td>\n<td>boolean</td>\n<td>可选</td>\n<td></td>\n<td>服务治理</td>\n<td>是否启用JSR303标准注解验证,如果启用,将对方法参数上的注解进行校验</td>\n<td>Dubbo2.1.0及其以上版本支持</td>\n</tr>\n</tbody>\n</table>\n<p>比如:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.xxx.XxxService"</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">"findXxx"</span> <span class="hljs-attr">timeout</span>=<span class="hljs-string">"3000"</span> <span class="hljs-attr">retries</span>=<span class="hljs-string">"2"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:reference</span>&gt;</span>\n</code></pre>\n'},{filename:"user/references/xml/dubbo-module.md",__html:"<style>\ntable {\n  width: 100%;\n  max-width: 65em;\n  border: 1px solid #dedede;\n  margin: 15px auto;\n  border-collapse: collapse;\n  empty-cells: show;\n}\ntable th,\ntable td {\n  height: 35px;\n  border: 1px solid #dedede;\n  padding: 0 10px;\n}\ntable th {\n  font-weight: bold;\n  text-align: center !important;\n  background: rgba(158,188,226,0.2);\n  white-space: nowrap;\n}\ntable tbody tr:nth-child(2n) {\n  background: rgba(158,188,226,0.12);\n}\ntable td:nth-child(1) {\n  white-space: nowrap;\n}\ntable tr:hover {\n  background: #efefef;\n}\n.table-area {\n  overflow: auto;\n}\n</style>\n<script type=\"text/javascript\">\n[].slice.call(document.querySelectorAll('table')).forEach(function(el){\n    var wrapper = document.createElement('div');\n    wrapper.className = 'table-area';\n    el.parentNode.insertBefore(wrapper, el);\n    el.parentNode.removeChild(el);\n    wrapper.appendChild(el);\n})\n<\/script>\n<h1>dubbo:module</h1>\n<p>模块信息配置。对应的配置类 <code>com.alibaba.dubbo.config.ModuleConfig</code></p>\n<table>\n<thead>\n<tr>\n<th>属性</th>\n<th>对应URL参数</th>\n<th>类型</th>\n<th>是否必填</th>\n<th>缺省值</th>\n<th>作用</th>\n<th>描述</th>\n<th>兼容性</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>name</td>\n<td>module</td>\n<td>string</td>\n<td><b>必填</b></td>\n<td></td>\n<td>服务治理</td>\n<td>当前模块名称,用于注册中心计算模块间依赖关系</td>\n<td>2.2.0以上版本</td>\n</tr>\n<tr>\n<td>version</td>\n<td>module.version</td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>服务治理</td>\n<td>当前模块的版本</td>\n<td>2.2.0以上版本</td>\n</tr>\n<tr>\n<td>owner</td>\n<td>owner</td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>服务治理</td>\n<td>模块负责人,用于服务治理,请填写负责人公司邮箱前缀</td>\n<td>2.2.0以上版本</td>\n</tr>\n<tr>\n<td>organization</td>\n<td>organization</td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>服务治理</td>\n<td>组织名称(BU或部门),用于注册中心区分服务来源,此配置项建议不要使用autoconfig,直接写死在配置中,比如china,intl,itu,crm,asc,dw,aliexpress等</td>\n<td>2.2.0以上版本</td>\n</tr>\n</tbody>\n</table>\n"},{filename:"user/references/xml/dubbo-monitor.md",__html:"<style>\ntable {\n  width: 100%;\n  max-width: 65em;\n  border: 1px solid #dedede;\n  margin: 15px auto;\n  border-collapse: collapse;\n  empty-cells: show;\n}\ntable th,\ntable td {\n  height: 35px;\n  border: 1px solid #dedede;\n  padding: 0 10px;\n}\ntable th {\n  font-weight: bold;\n  text-align: center !important;\n  background: rgba(158,188,226,0.2);\n  white-space: nowrap;\n}\ntable tbody tr:nth-child(2n) {\n  background: rgba(158,188,226,0.12);\n}\ntable td:nth-child(1) {\n  white-space: nowrap;\n}\ntable tr:hover {\n  background: #efefef;\n}\n.table-area {\n  overflow: auto;\n}\n</style>\n<script type=\"text/javascript\">\n[].slice.call(document.querySelectorAll('table')).forEach(function(el){\n    var wrapper = document.createElement('div');\n    wrapper.className = 'table-area';\n    el.parentNode.insertBefore(wrapper, el);\n    el.parentNode.removeChild(el);\n    wrapper.appendChild(el);\n})\n<\/script>\n<h1>dubbo:monitor</h1>\n<p>监控中心配置。对应的配置类: <code>com.alibaba.dubbo.config.MonitorConfig</code></p>\n<table>\n<thead>\n<tr>\n<th>属性</th>\n<th>对应URL参数</th>\n<th>类型</th>\n<th>是否必填</th>\n<th>缺省值</th>\n<th>作用</th>\n<th>描述</th>\n<th>兼容性</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>protocol</td>\n<td>protocol</td>\n<td>string</td>\n<td>可选</td>\n<td>dubbo</td>\n<td>服务治理</td>\n<td>监控中心协议,如果为protocol=&quot;registry&quot;,表示从注册中心发现监控中心地址,否则直连监控中心。</td>\n<td>2.0.9以上版本</td>\n</tr>\n<tr>\n<td>address</td>\n<td>&lt;url&gt;</td>\n<td>string</td>\n<td>可选</td>\n<td>N/A</td>\n<td>服务治理</td>\n<td>直连监控中心服务器地址,address=&quot;10.20.130.230:12080&quot;</td>\n<td>1.0.16以上版本</td>\n</tr>\n</tbody>\n</table>\n"},{filename:"user/references/xml/dubbo-parameter.md",__html:'<style>\ntable {\n  width: 100%;\n  max-width: 65em;\n  border: 1px solid #dedede;\n  margin: 15px auto;\n  border-collapse: collapse;\n  empty-cells: show;\n}\ntable th,\ntable td {\n  height: 35px;\n  border: 1px solid #dedede;\n  padding: 0 10px;\n}\ntable th {\n  font-weight: bold;\n  text-align: center !important;\n  background: rgba(158,188,226,0.2);\n  white-space: nowrap;\n}\ntable tbody tr:nth-child(2n) {\n  background: rgba(158,188,226,0.12);\n}\ntable td:nth-child(1) {\n  white-space: nowrap;\n}\ntable tr:hover {\n  background: #efefef;\n}\n.table-area {\n  overflow: auto;\n}\n</style>\n<script type="text/javascript">\n[].slice.call(document.querySelectorAll(\'table\')).forEach(function(el){\n    var wrapper = document.createElement(\'div\');\n    wrapper.className = \'table-area\';\n    el.parentNode.insertBefore(wrapper, el);\n    el.parentNode.removeChild(el);\n    wrapper.appendChild(el);\n})\n<\/script>\n<h1>dubbo:parameter</h1>\n<p>选项参数配置。对应的配置类:<code>java.util.Map</code>。同时该标签为<code>&lt;dubbo:protocol&gt;</code>或<code>&lt;dubbo:service&gt;</code>或<code>&lt;dubbo:provider&gt;</code>或<code>&lt;dubbo:reference&gt;</code>或<code>&lt;dubbo:consumer&gt;</code>的子标签,用于配置自定义参数,该配置项将作为扩展点设置自定义参数使用。</p>\n<table>\n<thead>\n<tr>\n<th>属性</th>\n<th>对应URL参数</th>\n<th>类型</th>\n<th>是否必填</th>\n<th>缺省值</th>\n<th>作用</th>\n<th>描述</th>\n<th>兼容性</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>key</td>\n<td>key</td>\n<td>string</td>\n<td><b>必填</b></td>\n<td></td>\n<td>服务治理</td>\n<td>路由参数键</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>value</td>\n<td>value</td>\n<td>string</td>\n<td><b>必填</b></td>\n<td></td>\n<td>服务治理</td>\n<td>路由参数值</td>\n<td>2.0.0以上版本</td>\n</tr>\n</tbody>\n</table>\n<p>比如:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"napoli"</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:parameter</span> <span class="hljs-attr">key</span>=<span class="hljs-string">"http://10.20.160.198/wiki/display/dubbo/napoli.queue.name"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:protocol</span>&gt;</span>\n</code></pre>\n<p>也可以:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"jms"</span> <span class="hljs-attr">p:queue</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n</code></pre>\n'},{filename:"user/references/xml/dubbo-protocol.md",__html:"<style>\ntable {\n  width: 100%;\n  max-width: 65em;\n  border: 1px solid #dedede;\n  margin: 15px auto;\n  border-collapse: collapse;\n  empty-cells: show;\n}\ntable th,\ntable td {\n  height: 35px;\n  border: 1px solid #dedede;\n  padding: 0 10px;\n}\ntable th {\n  font-weight: bold;\n  text-align: center !important;\n  background: rgba(158,188,226,0.2);\n  white-space: nowrap;\n}\ntable tbody tr:nth-child(2n) {\n  background: rgba(158,188,226,0.12);\n}\ntable td:nth-child(1) {\n  white-space: nowrap;\n}\ntable tr:hover {\n  background: #efefef;\n}\n.table-area {\n  overflow: auto;\n}\n</style>\n<script type=\"text/javascript\">\n[].slice.call(document.querySelectorAll('table')).forEach(function(el){\n    var wrapper = document.createElement('div');\n    wrapper.className = 'table-area';\n    el.parentNode.insertBefore(wrapper, el);\n    el.parentNode.removeChild(el);\n    wrapper.appendChild(el);\n})\n<\/script>\n<h1>dubbo:protocol</h1>\n<p>服务提供者协议配置。对应的配置类: <code>com.alibaba.dubbo.config.ProtocolConfig</code>。同时,如果需要支持多协议,可以声明多个 <code>&lt;dubbo:protocol&gt;</code> 标签,并在 <code>&lt;dubbo:service&gt;</code> 中通过 <code>protocol</code> 属性指定使用的协议。</p>\n<table>\n<thead>\n<tr>\n<th>属性</th>\n<th>对应URL参数</th>\n<th>类型</th>\n<th>是否必填</th>\n<th>缺省值</th>\n<th>作用</th>\n<th>描述</th>\n<th>兼容性</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>id</td>\n<td></td>\n<td>string</td>\n<td>可选</td>\n<td>dubbo</td>\n<td>配置关联</td>\n<td>协议BeanId,可以在&lt;dubbo:service protocol=&quot;&quot;&gt;中引用此ID,如果ID不填,缺省和name属性值一样,重复则在name后加序号。</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>name</td>\n<td>&lt;protocol&gt;</td>\n<td>string</td>\n<td><b>必填</b></td>\n<td>dubbo</td>\n<td>性能调优</td>\n<td>协议名称</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>port</td>\n<td>&lt;port&gt;</td>\n<td>int</td>\n<td>可选</td>\n<td>dubbo协议缺省端口为20880,rmi协议缺省端口为1099,http和hessian协议缺省端口为80;如果配置为<b>-1</b> 或者 <b>没有</b>配置port,则会分配一个没有被占用的端口。Dubbo 2.4.0+,分配的端口在协议缺省端口的基础上增长,确保端口段可控。</td>\n<td>服务发现</td>\n<td>服务端口</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>host</td>\n<td>&lt;host&gt;</td>\n<td>string</td>\n<td>可选</td>\n<td>自动查找本机IP</td>\n<td>服务发现</td>\n<td>-服务主机名,多网卡选择或指定VIP及域名时使用,为空则自动查找本机IP,-建议不要配置,让Dubbo自动获取本机IP</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>threadpool</td>\n<td>threadpool</td>\n<td>string</td>\n<td>可选</td>\n<td>fixed</td>\n<td>性能调优</td>\n<td>线程池类型,可选:fixed/cached</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>threads</td>\n<td>threads</td>\n<td>int</td>\n<td>可选</td>\n<td>100</td>\n<td>性能调优</td>\n<td>服务线程池大小(固定大小)</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>iothreads</td>\n<td>threads</td>\n<td>int</td>\n<td>可选</td>\n<td>cpu个数+1</td>\n<td>性能调优</td>\n<td>io线程池大小(固定大小)</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>accepts</td>\n<td>accepts</td>\n<td>int</td>\n<td>可选</td>\n<td>0</td>\n<td>性能调优</td>\n<td>服务提供方最大可接受连接数</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>payload</td>\n<td>payload</td>\n<td>int</td>\n<td>可选</td>\n<td>88388608(=8M)</td>\n<td>性能调优</td>\n<td>请求及响应数据包大小限制,单位:字节</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>codec</td>\n<td>codec</td>\n<td>string</td>\n<td>可选</td>\n<td>dubbo</td>\n<td>性能调优</td>\n<td>协议编码方式</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>serialization</td>\n<td>serialization</td>\n<td>string</td>\n<td>可选</td>\n<td>dubbo协议缺省为hessian2,rmi协议缺省为java,http协议缺省为json</td>\n<td>性能调优</td>\n<td>协议序列化方式,当协议支持多种序列化方式时使用,比如:dubbo协议的dubbo,hessian2,java,compactedjava,以及http协议的json等</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>accesslog</td>\n<td>accesslog</td>\n<td>string/boolean</td>\n<td>可选</td>\n<td></td>\n<td>服务治理</td>\n<td>设为true,将向logger中输出访问日志,也可填写访问日志文件路径,直接把访问日志输出到指定文件</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>path</td>\n<td>&lt;path&gt;</td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>服务发现</td>\n<td>提供者上下文路径,为服务path的前缀</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>transporter</td>\n<td>transporter</td>\n<td>string</td>\n<td>可选</td>\n<td>dubbo协议缺省为netty</td>\n<td>性能调优</td>\n<td>协议的服务端和客户端实现类型,比如:dubbo协议的mina,netty等,可以分拆为server和client配置</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>server</td>\n<td>server</td>\n<td>string</td>\n<td>可选</td>\n<td>dubbo协议缺省为netty,http协议缺省为servlet</td>\n<td>性能调优</td>\n<td>协议的服务器端实现类型,比如:dubbo协议的mina,netty等,http协议的jetty,servlet等</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>client</td>\n<td>client</td>\n<td>string</td>\n<td>可选</td>\n<td>dubbo协议缺省为netty</td>\n<td>性能调优</td>\n<td>协议的客户端实现类型,比如:dubbo协议的mina,netty等</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>dispatcher</td>\n<td>dispatcher</td>\n<td>string</td>\n<td>可选</td>\n<td>dubbo协议缺省为all</td>\n<td>性能调优</td>\n<td>协议的消息派发方式,用于指定线程模型,比如:dubbo协议的all, direct, message, execution, connection等</td>\n<td>2.1.0以上版本</td>\n</tr>\n<tr>\n<td>queues</td>\n<td>queues</td>\n<td>int</td>\n<td>可选</td>\n<td>0</td>\n<td>性能调优</td>\n<td>线程池队列大小,当线程池满时,排队等待执行的队列大小,建议不要设置,当线程程池时应立即失败,重试其它服务提供机器,而不是排队,除非有特殊需求。</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>charset</td>\n<td>charset</td>\n<td>string</td>\n<td>可选</td>\n<td>UTF-8</td>\n<td>性能调优</td>\n<td>序列化编码</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>buffer</td>\n<td>buffer</td>\n<td>int</td>\n<td>可选</td>\n<td>8192</td>\n<td>性能调优</td>\n<td>网络读写缓冲区大小</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>heartbeat</td>\n<td>heartbeat</td>\n<td>int</td>\n<td>可选</td>\n<td>0</td>\n<td>性能调优</td>\n<td>心跳间隔,对于长连接,当物理层断开时,比如拔网线,TCP的FIN消息来不及发送,对方收不到断开事件,此时需要心跳来帮助检查连接是否已断开</td>\n<td>2.0.10以上版本</td>\n</tr>\n<tr>\n<td>telnet</td>\n<td>telnet</td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>服务治理</td>\n<td>所支持的telnet命令,多个命令用逗号分隔</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>register</td>\n<td>register</td>\n<td>boolean</td>\n<td>可选</td>\n<td>true</td>\n<td>服务治理</td>\n<td>该协议的服务是否注册到注册中心</td>\n<td>2.0.8以上版本</td>\n</tr>\n<tr>\n<td>contextpath</td>\n<td>contextpath</td>\n<td>String</td>\n<td>可选</td>\n<td>缺省为空串</td>\n<td>服务治理</td>\n<td></td>\n<td>2.0.6以上版本</td>\n</tr>\n</tbody>\n</table>\n"},{filename:"user/references/xml/dubbo-provider.md",__html:"<style>\ntable {\n  width: 100%;\n  max-width: 65em;\n  border: 1px solid #dedede;\n  margin: 15px auto;\n  border-collapse: collapse;\n  empty-cells: show;\n}\ntable th,\ntable td {\n  height: 35px;\n  border: 1px solid #dedede;\n  padding: 0 10px;\n}\ntable th {\n  font-weight: bold;\n  text-align: center !important;\n  background: rgba(158,188,226,0.2);\n  white-space: nowrap;\n}\ntable tbody tr:nth-child(2n) {\n  background: rgba(158,188,226,0.12);\n}\ntable td:nth-child(1) {\n  white-space: nowrap;\n}\ntable tr:hover {\n  background: #efefef;\n}\n.table-area {\n  overflow: auto;\n}\n</style>\n<script type=\"text/javascript\">\n[].slice.call(document.querySelectorAll('table')).forEach(function(el){\n    var wrapper = document.createElement('div');\n    wrapper.className = 'table-area';\n    el.parentNode.insertBefore(wrapper, el);\n    el.parentNode.removeChild(el);\n    wrapper.appendChild(el);\n})\n<\/script>\n<h1>dubbo:provider</h1>\n<p>服务提供者缺省值配置。对应的配置类: <code>com.alibaba.dubbo.config.ProviderConfig</code>。同时该标签为 <code>&lt;dubbo:service&gt;</code> 和 <code>&lt;dubbo:protocol&gt;</code> 标签的缺省值设置。</p>\n<table>\n<thead>\n<tr>\n<th>属性</th>\n<th>对应URL参数</th>\n<th>类型</th>\n<th>是否必填</th>\n<th>缺省值</th>\n<th>作用</th>\n<th>描述</th>\n<th>兼容性</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>id</td>\n<td></td>\n<td>string</td>\n<td>可选</td>\n<td>dubbo</td>\n<td>配置关联</td>\n<td>协议BeanId,可以在&lt;dubbo:service proivder=&quot;&quot;&gt;中引用此ID</td>\n<td>1.0.16以上版本</td>\n</tr>\n<tr>\n<td>protocol</td>\n<td>&lt;protocol&gt;</td>\n<td>string</td>\n<td>可选</td>\n<td>dubbo</td>\n<td>性能调优</td>\n<td>协议名称</td>\n<td>1.0.16以上版本</td>\n</tr>\n<tr>\n<td>host</td>\n<td>&lt;host&gt;</td>\n<td>string</td>\n<td>可选</td>\n<td>自动查找本机IP</td>\n<td>服务发现</td>\n<td>服务主机名,多网卡选择或指定VIP及域名时使用,为空则自动查找本机IP,建议不要配置,让Dubbo自动获取本机IP</td>\n<td>1.0.16以上版本</td>\n</tr>\n<tr>\n<td>threads</td>\n<td>threads</td>\n<td>int</td>\n<td>可选</td>\n<td>100</td>\n<td>性能调优</td>\n<td>服务线程池大小(固定大小)</td>\n<td>1.0.16以上版本</td>\n</tr>\n<tr>\n<td>payload</td>\n<td>payload</td>\n<td>int</td>\n<td>可选</td>\n<td>88388608(=8M)</td>\n<td>性能调优</td>\n<td>请求及响应数据包大小限制,单位:字节</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>path</td>\n<td>&lt;path&gt;</td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>服务发现</td>\n<td>提供者上下文路径,为服务path的前缀</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>server</td>\n<td>server</td>\n<td>string</td>\n<td>可选</td>\n<td>dubbo协议缺省为netty,http协议缺省为servlet</td>\n<td>性能调优</td>\n<td>协议的服务器端实现类型,比如:dubbo协议的mina,netty等,http协议的jetty,servlet等</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>client</td>\n<td>client</td>\n<td>string</td>\n<td>可选</td>\n<td>dubbo协议缺省为netty</td>\n<td>性能调优</td>\n<td>协议的客户端实现类型,比如:dubbo协议的mina,netty等</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>codec</td>\n<td>codec</td>\n<td>string</td>\n<td>可选</td>\n<td>dubbo</td>\n<td>性能调优</td>\n<td>协议编码方式</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>serialization</td>\n<td>serialization</td>\n<td>string</td>\n<td>可选</td>\n<td>dubbo协议缺省为hessian2,rmi协议缺省为java,http协议缺省为json</td>\n<td>性能调优</td>\n<td>协议序列化方式,当协议支持多种序列化方式时使用,比如:dubbo协议的dubbo,hessian2,java,compactedjava,以及http协议的json,xml等</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>default</td>\n<td></td>\n<td>boolean</td>\n<td>可选</td>\n<td>false</td>\n<td>配置关联</td>\n<td>是否为缺省协议,用于多协议</td>\n<td>1.0.16以上版本</td>\n</tr>\n<tr>\n<td>filter</td>\n<td>service.filter</td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>性能调优</td>\n<td>服务提供方远程调用过程拦截器名称,多个名称用逗号分隔</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>listener</td>\n<td>exporter.listener</td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>性能调优</td>\n<td>服务提供方导出服务监听器名称,多个名称用逗号分隔</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>threadpool</td>\n<td>threadpool</td>\n<td>string</td>\n<td>可选</td>\n<td>fixed</td>\n<td>性能调优</td>\n<td>线程池类型,可选:fixed/cached</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>accepts</td>\n<td>accepts</td>\n<td>int</td>\n<td>可选</td>\n<td>0</td>\n<td>性能调优</td>\n<td>服务提供者最大可接受连接数</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>version</td>\n<td>version</td>\n<td>string</td>\n<td>可选</td>\n<td>0.0.0</td>\n<td>服务发现</td>\n<td>服务版本,建议使用两位数字版本,如:1.0,通常在接口不兼容时版本号才需要升级</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>group</td>\n<td>group</td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>服务发现</td>\n<td>服务分组,当一个接口有多个实现,可以用分组区分</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>delay</td>\n<td>delay</td>\n<td>int</td>\n<td>可选</td>\n<td>0</td>\n<td>性能调优</td>\n<td>延迟注册服务时间(毫秒)- ,设为-1时,表示延迟到Spring容器初始化完成时暴露服务</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>timeout</td>\n<td>default.timeout</td>\n<td>int</td>\n<td>可选</td>\n<td>1000</td>\n<td>性能调优</td>\n<td>远程服务调用超时时间(毫秒)</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>retries</td>\n<td>default.retries</td>\n<td>int</td>\n<td>可选</td>\n<td>2</td>\n<td>性能调优</td>\n<td>远程服务调用重试次数,不包括第一次调用,不需要重试请设为0</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>connections</td>\n<td>default.connections</td>\n<td>int</td>\n<td>可选</td>\n<td>0</td>\n<td>性能调优</td>\n<td>对每个提供者的最大连接数,rmi、http、hessian等短连接协议表示限制连接数,dubbo等长连接协表示建立的长连接个数</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>loadbalance</td>\n<td>default.loadbalance</td>\n<td>string</td>\n<td>可选</td>\n<td>random</td>\n<td>性能调优</td>\n<td>负载均衡策略,可选值:random,roundrobin,leastactive,分别表示:随机,轮循,最少活跃调用</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>async</td>\n<td>default.async</td>\n<td>boolean</td>\n<td>可选</td>\n<td>false</td>\n<td>性能调优</td>\n<td>是否缺省异步执行,不可靠异步,只是忽略返回值,不阻塞执行线程</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>stub</td>\n<td>stub</td>\n<td>boolean</td>\n<td>可选</td>\n<td>false</td>\n<td>服务治理</td>\n<td>设为true,表示使用缺省代理类名,即:接口名 + Local后缀。</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>mock</td>\n<td>mock</td>\n<td>boolean</td>\n<td>可选</td>\n<td>false</td>\n<td>服务治理</td>\n<td>设为true,表示使用缺省Mock类名,即:接口名 + Mock后缀。</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>token</td>\n<td>token</td>\n<td>boolean</td>\n<td>可选</td>\n<td>false</td>\n<td>服务治理</td>\n<td>令牌验证,为空表示不开启,如果为true,表示随机生成动态令牌</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>registry</td>\n<td>registry</td>\n<td>string</td>\n<td>可选</td>\n<td>缺省向所有registry注册</td>\n<td>配置关联</td>\n<td>向指定注册中心注册,在多个注册中心时使用,值为&lt;dubbo:registry&gt;的id属性,多个注册中心ID用逗号分隔,如果不想将该服务注册到任何registry,可将值设为N/A</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>dynamic</td>\n<td>dynamic</td>\n<td>boolean</td>\n<td>可选</td>\n<td>true</td>\n<td>服务治理</td>\n<td>服务是否动态注册,如果设为false,注册后将显示后disable状态,需人工启用,并且服务提供者停止时,也不会自动取消册,需人工禁用。</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>accesslog</td>\n<td>accesslog</td>\n<td>string/boolean</td>\n<td>可选</td>\n<td>false</td>\n<td>服务治理</td>\n<td>设为true,将向logger中输出访问日志,也可填写访问日志文件路径,直接把访问日志输出到指定文件</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>owner</td>\n<td>owner</td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>服务治理</td>\n<td>服务负责人,用于服务治理,请填写负责人公司邮箱前缀</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>document</td>\n<td>document</td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>服务治理</td>\n<td>服务文档URL</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>weight</td>\n<td>weight</td>\n<td>int</td>\n<td>可选</td>\n<td></td>\n<td>性能调优</td>\n<td>服务权重</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>executes</td>\n<td>executes</td>\n<td>int</td>\n<td>可选</td>\n<td>0</td>\n<td>性能调优</td>\n<td>服务提供者每服务每方法最大可并行执行请求数</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>actives</td>\n<td>default.actives</td>\n<td>int</td>\n<td>可选</td>\n<td>0</td>\n<td>性能调优</td>\n<td>每服务消费者每服务每方法最大并发调用数</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>proxy</td>\n<td>proxy</td>\n<td>string</td>\n<td>可选</td>\n<td>javassist</td>\n<td>性能调优</td>\n<td>生成动态代理方式,可选:jdk/javassist</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>cluster</td>\n<td>default.cluster</td>\n<td>string</td>\n<td>可选</td>\n<td>failover</td>\n<td>性能调优</td>\n<td>集群方式,可选:failover/failfast/failsafe/failback/forking</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>deprecated</td>\n<td>deprecated</td>\n<td>boolean</td>\n<td>可选</td>\n<td>false</td>\n<td>服务治理</td>\n<td>服务是否过时,如果设为true,消费方引用时将打印服务过时警告error日志</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>queues</td>\n<td>queues</td>\n<td>int</td>\n<td>可选</td>\n<td>0</td>\n<td>性能调优</td>\n<td>线程池队列大小,当线程池满时,排队等待执行的队列大小,建议不要设置,当线程程池时应立即失败,重试其它服务提供机器,而不是排队,除非有特殊需求。</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>charset</td>\n<td>charset</td>\n<td>string</td>\n<td>可选</td>\n<td>UTF-8</td>\n<td>性能调优</td>\n<td>序列化编码</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>buffer</td>\n<td>buffer</td>\n<td>int</td>\n<td>可选</td>\n<td>8192</td>\n<td>性能调优</td>\n<td>网络读写缓冲区大小</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>iothreads</td>\n<td>iothreads</td>\n<td>int</td>\n<td>可选</td>\n<td>CPU + 1</td>\n<td>性能调优</td>\n<td>IO线程池,接收网络读写中断,以及序列化和反序列化,不处理业务,业务线程池参见threads配置,此线程池和CPU相关,不建议配置。</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>telnet</td>\n<td>telnet</td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>服务治理</td>\n<td>所支持的telnet命令,多个命令用逗号分隔</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>&lt;dubbo:service&gt;</td>\n<td>contextpath</td>\n<td>contextpath</td>\n<td>String</td>\n<td>可选</td>\n<td>缺省为空串</td>\n<td>服务治理</td>\n<td></td>\n</tr>\n<tr>\n<td>layer</td>\n<td>layer</td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>服务治理</td>\n<td>服务提供者所在的分层。如:biz、dao、intl:web、china:acton。</td>\n<td>2.0.7以上版本</td>\n</tr>\n</tbody>\n</table>\n"},{filename:"user/references/xml/dubbo-reference.md",__html:"<style>\ntable {\n  width: 100%;\n  max-width: 65em;\n  border: 1px solid #dedede;\n  margin: 15px auto;\n  border-collapse: collapse;\n  empty-cells: show;\n}\ntable th,\ntable td {\n  height: 35px;\n  border: 1px solid #dedede;\n  padding: 0 10px;\n}\ntable th {\n  font-weight: bold;\n  text-align: center !important;\n  background: rgba(158,188,226,0.2);\n  white-space: nowrap;\n}\ntable tbody tr:nth-child(2n) {\n  background: rgba(158,188,226,0.12);\n}\ntable td:nth-child(1) {\n  white-space: nowrap;\n}\ntable tr:hover {\n  background: #efefef;\n}\n.table-area {\n  overflow: auto;\n}\n</style>\n<script type=\"text/javascript\">\n[].slice.call(document.querySelectorAll('table')).forEach(function(el){\n    var wrapper = document.createElement('div');\n    wrapper.className = 'table-area';\n    el.parentNode.insertBefore(wrapper, el);\n    el.parentNode.removeChild(el);\n    wrapper.appendChild(el);\n})\n<\/script>\n<h1>dubbo:reference</h1>\n<p>服务消费者引用服务配置。对应的配置类: <code>com.alibaba.dubbo.config.ReferenceConfig</code></p>\n<table>\n<thead>\n<tr>\n<th>属性</th>\n<th>对应URL参数</th>\n<th>类型</th>\n<th>是否必填</th>\n<th>缺省值</th>\n<th>作用</th>\n<th>描述</th>\n<th>兼容性</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>id</td>\n<td></td>\n<td>string</td>\n<td><b>必填</b></td>\n<td></td>\n<td>配置关联</td>\n<td>服务引用BeanId</td>\n<td>1.0.0以上版本</td>\n</tr>\n<tr>\n<td>interface</td>\n<td></td>\n<td>class</td>\n<td><b>必填</b></td>\n<td></td>\n<td>服务发现</td>\n<td>服务接口名</td>\n<td>1.0.0以上版本</td>\n</tr>\n<tr>\n<td>version</td>\n<td>version</td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>服务发现</td>\n<td>服务版本,与服务提供者的版本一致</td>\n<td>1.0.0以上版本</td>\n</tr>\n<tr>\n<td>group</td>\n<td>group</td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>服务发现</td>\n<td>服务分组,当一个接口有多个实现,可以用分组区分,必需和服务提供方一致</td>\n<td>1.0.7以上版本</td>\n</tr>\n<tr>\n<td>timeout</td>\n<td>timeout</td>\n<td>long</td>\n<td>可选</td>\n<td>缺省使用&lt;dubbo:consumer&gt;的timeout</td>\n<td>性能调优</td>\n<td>服务方法调用超时时间(毫秒)</td>\n<td>1.0.5以上版本</td>\n</tr>\n<tr>\n<td>retries</td>\n<td>retries</td>\n<td>int</td>\n<td>可选</td>\n<td>缺省使用&lt;dubbo:consumer&gt;的retries</td>\n<td>性能调优</td>\n<td>远程服务调用重试次数,不包括第一次调用,不需要重试请设为0</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>connections</td>\n<td>connections</td>\n<td>int</td>\n<td>可选</td>\n<td>缺省使用&lt;dubbo:consumer&gt;的connections</td>\n<td>性能调优</td>\n<td>对每个提供者的最大连接数,rmi、http、hessian等短连接协议表示限制连接数,dubbo等长连接协表示建立的长连接个数</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>loadbalance</td>\n<td>loadbalance</td>\n<td>string</td>\n<td>可选</td>\n<td>缺省使用&lt;dubbo:consumer&gt;的loadbalance</td>\n<td>性能调优</td>\n<td>负载均衡策略,可选值:random,roundrobin,leastactive,分别表示:随机,轮循,最少活跃调用</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>async</td>\n<td>async</td>\n<td>boolean</td>\n<td>可选</td>\n<td>缺省使用&lt;dubbo:consumer&gt;的async</td>\n<td>性能调优</td>\n<td>是否异步执行,不可靠异步,只是忽略返回值,不阻塞执行线程</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>generic</td>\n<td>generic</td>\n<td>boolean</td>\n<td>可选</td>\n<td>缺省使用&lt;dubbo:consumer&gt;的generic</td>\n<td>服务治理</td>\n<td>是否缺省泛化接口,如果为泛化接口,将返回GenericService</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>check</td>\n<td>check</td>\n<td>boolean</td>\n<td>可选</td>\n<td>缺省使用&lt;dubbo:consumer&gt;的check</td>\n<td>服务治理</td>\n<td>启动时检查提供者是否存在,true报错,false忽略</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>url</td>\n<td>url</td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>服务治理</td>\n<td>点对点直连服务提供者地址,将绕过注册中心</td>\n<td>1.0.6以上版本</td>\n</tr>\n<tr>\n<td>stub</td>\n<td>stub</td>\n<td>class/boolean</td>\n<td>可选</td>\n<td></td>\n<td>服务治理</td>\n<td>服务接口客户端本地代理类名,用于在客户端执行本地逻辑,如本地缓存等,该本地代理类的构造函数必须允许传入远程代理对象,构造函数如:public XxxServiceLocal(XxxService xxxService)</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>mock</td>\n<td>mock</td>\n<td>class/boolean</td>\n<td>可选</td>\n<td></td>\n<td>服务治理</td>\n<td>服务接口调用失败Mock实现类名,该Mock类必须有一个无参构造函数,与Local的区别在于,Local总是被执行,而Mock只在出现非业务异常(比如超时,网络异常等)时执行,Local在远程调用之前执行,Mock在远程调用后执行。</td>\n<td>Dubbo1.0.13及其以上版本支持</td>\n</tr>\n<tr>\n<td>cache</td>\n<td>cache</td>\n<td>string/boolean</td>\n<td>可选</td>\n<td></td>\n<td>服务治理</td>\n<td>以调用参数为key,缓存返回结果,可选:lru, threadlocal, jcache等</td>\n<td>Dubbo2.1.0及其以上版本支持</td>\n</tr>\n<tr>\n<td>validation</td>\n<td>validation</td>\n<td>boolean</td>\n<td>可选</td>\n<td></td>\n<td>服务治理</td>\n<td>是否启用JSR303标准注解验证,如果启用,将对方法参数上的注解进行校验</td>\n<td>Dubbo2.1.0及其以上版本支持</td>\n</tr>\n<tr>\n<td>proxy</td>\n<td>proxy</td>\n<td>boolean</td>\n<td>可选</td>\n<td>javassist</td>\n<td>性能调优</td>\n<td>选择动态代理实现策略,可选:javassist, jdk</td>\n<td>2.0.2以上版本</td>\n</tr>\n<tr>\n<td>client</td>\n<td>client</td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>性能调优</td>\n<td>客户端传输类型设置,如Dubbo协议的netty或mina。</td>\n<td>Dubbo2.0.0以上版本支持</td>\n</tr>\n<tr>\n<td>registry</td>\n<td></td>\n<td>string</td>\n<td>可选</td>\n<td>缺省将从所有注册中心获服务列表后合并结果</td>\n<td>配置关联</td>\n<td>从指定注册中心注册获取服务列表,在多个注册中心时使用,值为&lt;dubbo:registry&gt;的id属性,多个注册中心ID用逗号分隔</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>owner</td>\n<td>owner</td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>服务治理</td>\n<td>调用服务负责人,用于服务治理,请填写负责人公司邮箱前缀</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>actives</td>\n<td>actives</td>\n<td>int</td>\n<td>可选</td>\n<td>0</td>\n<td>性能调优</td>\n<td>每服务消费者每服务每方法最大并发调用数</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>cluster</td>\n<td>cluster</td>\n<td>string</td>\n<td>可选</td>\n<td>failover</td>\n<td>性能调优</td>\n<td>集群方式,可选:failover/failfast/failsafe/failback/forking</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>filter</td>\n<td>reference.filter</td>\n<td>string</td>\n<td>可选</td>\n<td>default</td>\n<td>性能调优</td>\n<td>服务消费方远程调用过程拦截器名称,多个名称用逗号分隔</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>listener</td>\n<td>invoker.listener</td>\n<td>string</td>\n<td>可选</td>\n<td>default</td>\n<td>性能调优</td>\n<td>服务消费方引用服务监听器名称,多个名称用逗号分隔</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>layer</td>\n<td>layer</td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>服务治理</td>\n<td>服务调用者所在的分层。如:biz、dao、intl:web、china:acton。</td>\n<td>2.0.7以上版本</td>\n</tr>\n<tr>\n<td>init</td>\n<td>init</td>\n<td>boolean</td>\n<td>可选</td>\n<td>false</td>\n<td>性能调优</td>\n<td>是否在afterPropertiesSet()时饥饿初始化引用,否则等到有人注入或引用该实例时再初始化。</td>\n<td>2.0.10以上版本</td>\n</tr>\n<tr>\n<td>protocol</td>\n<td>protocol</td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>服力治理</td>\n<td>只调用指定协议的服务提供方,其它协议忽略。</td>\n<td>2.2.0以上版本</td>\n</tr>\n</tbody>\n</table>\n"},{filename:"user/references/xml/dubbo-registry.md",__html:"<style>\ntable {\n  width: 100%;\n  max-width: 65em;\n  border: 1px solid #dedede;\n  margin: 15px auto;\n  border-collapse: collapse;\n  empty-cells: show;\n}\ntable th,\ntable td {\n  height: 35px;\n  border: 1px solid #dedede;\n  padding: 0 10px;\n}\ntable th {\n  font-weight: bold;\n  text-align: center !important;\n  background: rgba(158,188,226,0.2);\n  white-space: nowrap;\n}\ntable tbody tr:nth-child(2n) {\n  background: rgba(158,188,226,0.12);\n}\ntable td:nth-child(1) {\n  white-space: nowrap;\n}\ntable tr:hover {\n  background: #efefef;\n}\n.table-area {\n  overflow: auto;\n}\n</style>\n<script type=\"text/javascript\">\n[].slice.call(document.querySelectorAll('table')).forEach(function(el){\n    var wrapper = document.createElement('div');\n    wrapper.className = 'table-area';\n    el.parentNode.insertBefore(wrapper, el);\n    el.parentNode.removeChild(el);\n    wrapper.appendChild(el);\n})\n<\/script>\n<h1>dubbo:registry</h1>\n<p>注册中心配置。对应的配置类: <code>com.alibaba.dubbo.config.RegistryConfig</code>。同时如果有多个不同的注册中心,可以声明多个 <code>&lt;dubbo:registry&gt;</code> 标签,并在 <code>&lt;dubbo:service&gt;</code> 或 <code>&lt;dubbo:reference&gt;</code> 的 <code>registry</code> 属性指定使用的注册中心。</p>\n<table>\n<thead>\n<tr>\n<th>属性</th>\n<th>对应URL参数</th>\n<th>类型</th>\n<th>是否必填</th>\n<th>缺省值</th>\n<th>作用</th>\n<th>描述</th>\n<th>兼容性</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>id</td>\n<td></td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>配置关联</td>\n<td>注册中心引用BeanId,可以在&lt;dubbo:service registry=&quot;&quot;&gt;或&lt;dubbo:reference registry=&quot;&quot;&gt;中引用此ID</td>\n<td>1.0.16以上版本</td>\n</tr>\n<tr>\n<td>address</td>\n<td>&lt;host:port&gt;</td>\n<td>string</td>\n<td><b>必填</b></td>\n<td></td>\n<td>服务发现</td>\n<td>注册中心服务器地址,如果地址没有端口缺省为9090,同一集群内的多个地址用逗号分隔,如:ip:port,ip:port,不同集群的注册中心,请配置多个&lt;dubbo:registry&gt;标签</td>\n<td>1.0.16以上版本</td>\n</tr>\n<tr>\n<td>protocol</td>\n<td>&lt;protocol&gt;</td>\n<td>string</td>\n<td>可选</td>\n<td>dubbo</td>\n<td>服务发现</td>\n<td>注同中心地址协议,支持dubbo, http, local三种协议,分别表示,dubbo地址,http地址,本地注册中心</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>port</td>\n<td>&lt;port&gt;</td>\n<td>int</td>\n<td>可选</td>\n<td>9090</td>\n<td>服务发现</td>\n<td>注册中心缺省端口,当address没有带端口时使用此端口做为缺省值</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>username</td>\n<td>&lt;username&gt;</td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>服务治理</td>\n<td>登录注册中心用户名,如果注册中心不需要验证可不填</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>password</td>\n<td>&lt;password&gt;</td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>服务治理</td>\n<td>登录注册中心密码,如果注册中心不需要验证可不填</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>transport</td>\n<td>registry.transporter</td>\n<td>string</td>\n<td>可选</td>\n<td>netty</td>\n<td>性能调优</td>\n<td>网络传输方式,可选mina,netty</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>timeout</td>\n<td>registry.timeout</td>\n<td>int</td>\n<td>可选</td>\n<td>5000</td>\n<td>性能调优</td>\n<td>注册中心请求超时时间(毫秒)</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>session</td>\n<td>registry.session</td>\n<td>int</td>\n<td>可选</td>\n<td>60000</td>\n<td>性能调优</td>\n<td>注册中心会话超时时间(毫秒),用于检测提供者非正常断线后的脏数据,比如用心跳检测的实现,此时间就是心跳间隔,不同注册中心实现不一样。</td>\n<td>2.1.0以上版本</td>\n</tr>\n<tr>\n<td>file</td>\n<td>registry.file</td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>服务治理</td>\n<td>使用文件缓存注册中心地址列表及服务提供者列表,应用重启时将基于此文件恢复,注意:两个注册中心不能使用同一文件存储</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>wait</td>\n<td>registry.wait</td>\n<td>int</td>\n<td>可选</td>\n<td>0</td>\n<td>性能调优</td>\n<td>停止时等待通知完成时间(毫秒)</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>check</td>\n<td>check</td>\n<td>boolean</td>\n<td>可选</td>\n<td>true</td>\n<td>服务治理</td>\n<td>注册中心不存在时,是否报错</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>register</td>\n<td>register</td>\n<td>boolean</td>\n<td>可选</td>\n<td>true</td>\n<td>服务治理</td>\n<td>是否向此注册中心注册服务,如果设为false,将只订阅,不注册</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>subscribe</td>\n<td>subscribe</td>\n<td>boolean</td>\n<td>可选</td>\n<td>true</td>\n<td>服务治理</td>\n<td>是否向此注册中心订阅服务,如果设为false,将只注册,不订阅</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>dynamic</td>\n<td>dynamic</td>\n<td>boolean</td>\n<td>可选</td>\n<td>true</td>\n<td>服务治理</td>\n<td>服务是否动态注册,如果设为false,注册后将显示后disable状态,需人工启用,并且服务提供者停止时,也不会自动取消册,需人工禁用。</td>\n<td>2.0.5以上版本</td>\n</tr>\n</tbody>\n</table>\n"},{filename:"user/references/xml/dubbo-service.md",__html:"<style>\ntable {\n  width: 100%;\n  max-width: 65em;\n  border: 1px solid #dedede;\n  margin: 15px auto;\n  border-collapse: collapse;\n  empty-cells: show;\n}\ntable th,\ntable td {\n  height: 35px;\n  border: 1px solid #dedede;\n  padding: 0 10px;\n}\ntable th {\n  font-weight: bold;\n  text-align: center !important;\n  background: rgba(158,188,226,0.2);\n  white-space: nowrap;\n}\ntable tbody tr:nth-child(2n) {\n  background: rgba(158,188,226,0.12);\n}\ntable td:nth-child(1) {\n  white-space: nowrap;\n}\ntable tr:hover {\n  background: #efefef;\n}\n.table-area {\n  overflow: auto;\n}\n</style>\n<script type=\"text/javascript\">\n[].slice.call(document.querySelectorAll('table')).forEach(function(el){\n    var wrapper = document.createElement('div');\n    wrapper.className = 'table-area';\n    el.parentNode.insertBefore(wrapper, el);\n    el.parentNode.removeChild(el);\n    wrapper.appendChild(el);\n})\n<\/script>\n<h1>dubbo:service</h1>\n<p>服务提供者暴露服务配置。对应的配置类:<code>com.alibaba.dubbo.config.ServiceConfig</code></p>\n<table>\n<thead>\n<tr>\n<th>属性</th>\n<th>对应URL参数</th>\n<th>类型</th>\n<th>是否必填</th>\n<th>缺省值</th>\n<th>作用</th>\n<th>描述</th>\n<th>兼容性</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>interface</td>\n<td></td>\n<td>class</td>\n<td><b>必填</b></td>\n<td></td>\n<td>服务发现</td>\n<td>服务接口名</td>\n<td>1.0.0以上版本</td>\n</tr>\n<tr>\n<td>ref</td>\n<td></td>\n<td>object</td>\n<td><b>必填</b></td>\n<td></td>\n<td>服务发现</td>\n<td>服务对象实现引用</td>\n<td>1.0.0以上版本</td>\n</tr>\n<tr>\n<td>version</td>\n<td>version</td>\n<td>string</td>\n<td>可选</td>\n<td>0.0.0</td>\n<td>服务发现</td>\n<td>服务版本,建议使用两位数字版本,如:1.0,通常在接口不兼容时版本号才需要升级</td>\n<td>1.0.0以上版本</td>\n</tr>\n<tr>\n<td>group</td>\n<td>group</td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>服务发现</td>\n<td>服务分组,当一个接口有多个实现,可以用分组区分</td>\n<td>1.0.7以上版本</td>\n</tr>\n<tr>\n<td>path</td>\n<td>&lt;path&gt;</td>\n<td>string</td>\n<td>可选</td>\n<td>缺省为接口名</td>\n<td>服务发现</td>\n<td>服务路径 (注意:1.0不支持自定义路径,总是使用接口名,如果有1.0调2.0,配置服务路径可能不兼容)</td>\n<td>1.0.12以上版本</td>\n</tr>\n<tr>\n<td>delay</td>\n<td>delay</td>\n<td>int</td>\n<td>可选</td>\n<td>0</td>\n<td>性能调优</td>\n<td>延迟注册服务时间(毫秒) ,设为-1时,表示延迟到Spring容器初始化完成时暴露服务</td>\n<td>1.0.14以上版本</td>\n</tr>\n<tr>\n<td>timeout</td>\n<td>timeout</td>\n<td>int</td>\n<td>可选</td>\n<td>1000</td>\n<td>性能调优</td>\n<td>远程服务调用超时时间(毫秒)</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>retries</td>\n<td>retries</td>\n<td>int</td>\n<td>可选</td>\n<td>2</td>\n<td>性能调优</td>\n<td>远程服务调用重试次数,不包括第一次调用,不需要重试请设为0</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>connections</td>\n<td>connections</td>\n<td>int</td>\n<td>可选</td>\n<td>100</td>\n<td>性能调优</td>\n<td>对每个提供者的最大连接数,rmi、http、hessian等短连接协议表示限制连接数,dubbo等长连接协表示建立的长连接个数</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>loadbalance</td>\n<td>loadbalance</td>\n<td>string</td>\n<td>可选</td>\n<td>random</td>\n<td>性能调优</td>\n<td>负载均衡策略,可选值:random,roundrobin,leastactive,分别表示:随机,轮循,最少活跃调用</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>async</td>\n<td>async</td>\n<td>boolean</td>\n<td>可选</td>\n<td>false</td>\n<td>性能调优</td>\n<td>是否缺省异步执行,不可靠异步,只是忽略返回值,不阻塞执行线程</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>stub</td>\n<td>stub</td>\n<td>class/boolean</td>\n<td>可选</td>\n<td>false</td>\n<td>服务治理</td>\n<td>设为true,表示使用缺省代理类名,即:接口名 + Local后缀,服务接口客户端本地代理类名,用于在客户端执行本地逻辑,如本地缓存等,该本地代理类的构造函数必须允许传入远程代理对象,构造函数如:public XxxServiceLocal(XxxService xxxService)</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>mock</td>\n<td>mock</td>\n<td>class/boolean</td>\n<td>可选</td>\n<td>false</td>\n<td>服务治理</td>\n<td>设为true,表示使用缺省Mock类名,即:接口名 + Mock后缀,服务接口调用失败Mock实现类,该Mock类必须有一个无参构造函数,与Local的区别在于,Local总是被执行,而Mock只在出现非业务异常(比如超时,网络异常等)时执行,Local在远程调用之前执行,Mock在远程调用后执行。</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>token</td>\n<td>token</td>\n<td>string/boolean</td>\n<td>可选</td>\n<td>false</td>\n<td>服务治理</td>\n<td>令牌验证,为空表示不开启,如果为true,表示随机生成动态令牌,否则使用静态令牌,令牌的作用是防止消费者绕过注册中心直接访问,保证注册中心的授权功能有效,如果使用点对点调用,需关闭令牌功能</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>registry</td>\n<td></td>\n<td>string</td>\n<td>可选</td>\n<td>缺省向所有registry注册</td>\n<td>配置关联</td>\n<td>向指定注册中心注册,在多个注册中心时使用,值为&lt;dubbo:registry&gt;的id属性,多个注册中心ID用逗号分隔,如果不想将该服务注册到任何registry,可将值设为N/A</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>provider</td>\n<td></td>\n<td>string</td>\n<td>可选</td>\n<td>缺使用第一个provider配置</td>\n<td>配置关联</td>\n<td>指定provider,值为&lt;dubbo:provider&gt;的id属性</td>\n<td>2.0.0以上版本</td>\n</tr>\n<tr>\n<td>deprecated</td>\n<td>deprecated</td>\n<td>boolean</td>\n<td>可选</td>\n<td>false</td>\n<td>服务治理</td>\n<td>服务是否过时,如果设为true,消费方引用时将打印服务过时警告error日志</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>dynamic</td>\n<td>dynamic</td>\n<td>boolean</td>\n<td>可选</td>\n<td>true</td>\n<td>服务治理</td>\n<td>服务是否动态注册,如果设为false,注册后将显示后disable状态,需人工启用,并且服务提供者停止时,也不会自动取消册,需人工禁用。</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>accesslog</td>\n<td>accesslog</td>\n<td>string/boolean</td>\n<td>可选</td>\n<td>false</td>\n<td>服务治理</td>\n<td>设为true,将向logger中输出访问日志,也可填写访问日志文件路径,直接把访问日志输出到指定文件</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>owner</td>\n<td>owner</td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>服务治理</td>\n<td>服务负责人,用于服务治理,请填写负责人公司邮箱前缀</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>document</td>\n<td>document</td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>服务治理</td>\n<td>服务文档URL</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>weight</td>\n<td>weight</td>\n<td>int</td>\n<td>可选</td>\n<td></td>\n<td>性能调优</td>\n<td>服务权重</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>executes</td>\n<td>executes</td>\n<td>int</td>\n<td>可选</td>\n<td>0</td>\n<td>性能调优</td>\n<td>服务提供者每服务每方法最大可并行执行请求数</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>proxy</td>\n<td>proxy</td>\n<td>string</td>\n<td>可选</td>\n<td>javassist</td>\n<td>性能调优</td>\n<td>生成动态代理方式,可选:jdk/javassist</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>cluster</td>\n<td>cluster</td>\n<td>string</td>\n<td>可选</td>\n<td>failover</td>\n<td>性能调优</td>\n<td>集群方式,可选:failover/failfast/failsafe/failback/forking</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>filter</td>\n<td>service.filter</td>\n<td>string</td>\n<td>可选</td>\n<td>default</td>\n<td>性能调优</td>\n<td>服务提供方远程调用过程拦截器名称,多个名称用逗号分隔</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>listener</td>\n<td>exporter.listener</td>\n<td>string</td>\n<td>可选</td>\n<td>default</td>\n<td>性能调优</td>\n<td>服务提供方导出服务监听器名称,多个名称用逗号分隔</td>\n<td></td>\n</tr>\n<tr>\n<td>protocol</td>\n<td></td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>配置关联</td>\n<td>使用指定的协议暴露服务,在多协议时使用,值为&lt;dubbo:protocol&gt;的id属性,多个协议ID用逗号分隔</td>\n<td>2.0.5以上版本</td>\n</tr>\n<tr>\n<td>layer</td>\n<td>layer</td>\n<td>string</td>\n<td>可选</td>\n<td></td>\n<td>服务治理</td>\n<td>服务提供者所在的分层。如:biz、dao、intl:web、china:acton。</td>\n<td>2.0.7以上版本</td>\n</tr>\n<tr>\n<td>register</td>\n<td>register</td>\n<td>boolean</td>\n<td>可选</td>\n<td>true</td>\n<td>服务治理</td>\n<td>该协议的服务是否注册到注册中心</td>\n<td>2.0.8以上版本</td>\n</tr>\n</tbody>\n</table>\n"},{filename:"user/references/xml/introduction.md",__html:'<h1>schema 配置参考手册</h1>\n<p>这里以 XML Config <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> 为准,列举所有配置项 <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>。其它配置方式,请参见相应转换关系:<a href="../../configuration/properties.md">属性配置</a>,<a href="../../configuration/annotation.md">注解配置</a>,<a href="../../configuration/api.md">API 配置</a>。</p>\n<p>所有配置项分为三大类,参见下表中的&quot;作用&quot; 一列。</p>\n<ul>\n<li>服务发现:表示该配置项用于服务的注册与发现,目的是让消费方找到提供方。</li>\n<li>服务治理:表示该配置项用于治理服务间的关系,或为开发测试提供便利条件。</li>\n<li>性能调优:表示该配置项用于调优性能,不同的选项对性能会产生影响。</li>\n<li>所有配置最终都将转换为 URL <sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup> 表示,并由服务提供方生成,经注册中心传递给消费方,各属性对应 URL 的参数,参见配置项一览表中的 &quot;对应URL参数&quot; 列。</li>\n</ul>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>XML Schema: <a href="http://dubbo.apache.org/schema/dubbo/dubbo.xsd">http://dubbo.apache.org/schema/dubbo/dubbo.xsd</a> <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>注意:只有 group,interface,version 是服务的匹配条件,三者决定是不是同一个服务,其它配置项均为调优和治理参数。 <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn3" class="footnote-item"><p>URL 格式:<code>protocol://username:password@host:port/path?key=value&amp;key=value</code> <a href="#fnref3" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/rest.md",__html:'<h1>在Dubbo中开发REST风格的远程调用(RESTful Remoting)</h1>\n<p><strong>作者:沈理</strong></p>\n<p><strong>文档版权:<a href="http://www.apache.org/licenses/LICENSE-2.0">Apache 2.0许可证 署名-禁止演绎</a></strong></p>\n<p>完善中……</p>\n<blockquote>\n<p>本文篇幅较长,因为REST本身涉及面较多。另外,本文参照Spring等的文档风格,不仅仅局限于框架用法的阐述,同时也努力呈现框架的设计理念和优良应用的架构思想。</p>\n</blockquote>\n<blockquote>\n<p>对于想粗略了解dubbo和REST的人,只需浏览 <code>概述</code> 至 <code>标准Java REST API:JAX-RS简介</code> 几节即可。</p>\n</blockquote>\n<p>TODO 生成可点击的目录</p>\n<h2>目录</h2>\n<ul>\n<li>概述</li>\n<li>REST的优点</li>\n<li>应用场景</li>\n<li>快速入门</li>\n<li>标准Java REST API:JAX-RS简介</li>\n<li>REST服务提供端详解\n<ul>\n<li>HTTP POST/GET的实现</li>\n<li>Annotation放在接口类还是实现类</li>\n<li>JSON、XML等多数据格式的支持</li>\n<li>中文字符支持</li>\n<li>XML数据格式的额外要求</li>\n<li>定制序列化</li>\n<li>配置REST Server的实现</li>\n<li>获取上下文(Context)信息</li>\n<li>配置端口号和Context Path</li>\n<li>配置线程数和IO线程数</li>\n<li>配置长连接</li>\n<li>配置最大的HTTP连接数</li>\n<li>配置每个消费端的超时时间和HTTP连接数</li>\n<li>GZIP数据压缩</li>\n<li>用Annotation取代部分Spring XML配置</li>\n<li>添加自定义的Filter、Interceptor等</li>\n<li>添加自定义的Exception处理</li>\n<li>配置HTTP日志输出</li>\n<li>输入参数的校验</li>\n<li>是否应该透明发布REST服务</li>\n</ul>\n</li>\n<li>REST服务消费端详解\n<ul>\n<li>场景1:非dubbo的消费端调用dubbo的REST服务</li>\n<li>场景2:dubbo消费端调用dubbo的REST服务</li>\n<li>场景3:dubbo的消费端调用非dubbo的REST服务</li>\n</ul>\n</li>\n<li>Dubbo中JAX-RS的限制</li>\n<li>REST常见问题解答(REST FAQ)\n<ul>\n<li>Dubbo REST的服务能和Dubbo注册中心、监控中心集成吗?</li>\n<li>Dubbo REST中如何实现负载均衡和容错(failover)?</li>\n<li>JAX-RS中重载的方法能够映射到同一URL地址吗?</li>\n<li>JAX-RS中作POST的方法能够接收多个参数吗?</li>\n</ul>\n</li>\n<li>Dubbo当前体系可能的不足之处(与REST相关的)\n<ul>\n<li>RpcContext的侵入性</li>\n<li>Protocol配置的局限性</li>\n<li>XML命名不符合spring规范</li>\n</ul>\n</li>\n<li>REST最佳实践</li>\n<li>性能基准测试\n<ul>\n<li>测试环境</li>\n<li>测试脚本</li>\n<li>测试结果</li>\n</ul>\n</li>\n<li>扩展讨论\n<ul>\n<li>REST与Thrift、Protobuf等的对比</li>\n<li>REST与传统WebServices的对比</li>\n<li>JAX-RS与Spring MVC的对比</li>\n</ul>\n</li>\n<li>未来</li>\n</ul>\n<h2>概述</h2>\n<p>dubbo支持多种远程调用方式,例如dubbo RPC(二进制序列化 + tcp协议)、http invoker(二进制序列化 + http协议,至少在开源版本没发现对文本序列化的支持)、hessian(二进制序列化 + http协议)、WebServices (文本序列化 + http协议)等等,但缺乏对当今特别流行的REST风格远程调用(文本序列化 + http协议)的支持。</p>\n<p>有鉴于此,我们基于标准的Java REST API——JAX-RS 2.0(Java API for RESTful Web Services的简写),为dubbo提供了接近透明的REST调用支持。由于完全兼容Java标准API,所以为dubbo开发的所有REST服务,未来脱离dubbo或者任何特定的REST底层实现一般也可以正常运行。</p>\n<p>特别值得指出的是,我们并不需要完全严格遵守REST的原始定义和架构风格。即使著名的Twitter REST API也会根据情况做适度调整,而不是机械的遵守原始的REST风格。</p>\n<blockquote>\n<p>附注:我们将这个功能称之为REST风格的远程调用,即RESTful Remoting(抽象的远程处理或者调用),而不是叫RESTful RPC(具体的远程“过程”调用),是因为REST和RPC本身可以被认为是两种不同的风格。在dubbo的REST实现中,可以说有两个面向,其一是提供或消费正常的REST服务,其二是将REST作为dubbo RPC体系中一种协议实现,而RESTful Remoting同时涵盖了这两个面向。</p>\n</blockquote>\n<h2>REST的优点</h2>\n<p>以下摘自维基百科:</p>\n<ul>\n<li>可更高效利用缓存来提高响应速度</li>\n<li>通讯本身的无状态性可以让不同的服务器的处理一系列请求中的不同请求,提高服务器的扩展性</li>\n<li>浏览器即可作为客户端,简化软件需求</li>\n<li>相对于其他叠加在HTTP协议之上的机制,REST的软件依赖性更小</li>\n<li>不需要额外的资源发现机制</li>\n<li>在软件技术演进中的长期的兼容性更好</li>\n</ul>\n<p>这里我还想特别补充REST的显著优点:基于简单的文本格式消息和通用的HTTP协议,使它具备极广的适用性,几乎所有语言和平台都对它提供支持,同时其学习和使用的门槛也较低。</p>\n<h2>应用场景</h2>\n<p>正是由于REST在适用性方面的优点,所以在dubbo中支持REST,可以为当今多数主流的远程调用场景都带来(显著)好处:</p>\n<ol>\n<li>\n<p>显著简化企业内部的异构系统之间的(跨语言)调用。此处主要针对这种场景:dubbo的系统做服务提供端,其他语言的系统(也包括某些不基于dubbo的java系统)做服务消费端,两者通过HTTP和文本消息进行通信。即使相比Thrift、ProtoBuf等二进制跨语言调用方案,REST也有自己独特的优势(详见后面讨论)</p>\n</li>\n<li>\n<p>显著简化对外Open API(开放平台)的开发。既可以用dubbo来开发专门的Open API应用,也可以将原内部使用的dubbo service直接“透明”发布为对外的Open REST API(当然dubbo本身未来最好可以较透明的提供诸如权限控制、频次控制、计费等诸多功能)</p>\n</li>\n<li>\n<p>显著简化手机(平板)APP或者PC桌面客户端开发。类似于2,既可以用dubbo来开发专门针对无线或者桌面的服务器端,也可以将原内部使用的dubbo service直接”透明“的暴露给手机APP或桌面程序。当然在有些项目中,手机或桌面程序也可以直接访问以上场景2中所述的Open API。</p>\n</li>\n<li>\n<p>显著简化浏览器AJAX应用的开发。类似于2,既可以用dubbo来开发专门的AJAX服务器端,也可以将原内部使用的dubbo service直接”透明“的暴露给浏览器中JavaScript。当然,很多AJAX应用更适合与web框架协同工作,所以直接访问dubbo service在很多web项目中未必是一种非常优雅的架构。</p>\n</li>\n<li>\n<p>为企业内部的dubbo系统之间(即服务提供端和消费端都是基于dubbo的系统)提供一种基于文本的、易读的远程调用方式。</p>\n</li>\n<li>\n<p>一定程度简化dubbo系统对其它异构系统的调用。可以用类似dubbo的简便方式“透明”的调用非dubbo系统提供的REST服务(不管服务提供端是在企业内部还是外部)</p>\n</li>\n</ol>\n<p>需要指出的是,我认为1~3是dubbo的REST调用最有价值的三种应用场景,并且我们为dubbo添加REST调用,其最主要到目的也是面向服务的提供端,即开发REST服务来提供给非dubbo的(异构)消费端。</p>\n<p>归纳起来,所有应用场景如下图所示:\n<img src="images/rest.jpg" alt="no image found"></p>\n<p>借用Java过去最流行的宣传语,为dubbo添加REST调用后,可以实现服务的”一次编写,到处访问“,理论上可以面向全世界开放,从而真正实现比较理想化的面向服务架构(SOA)。</p>\n<p>当然,传统的WebServices(WSDL/SOAP)也基本同样能满足以上场景(除了场景4)的要求(甚至还能满足那些需要企业级特性的场景),但由于其复杂性等问题,现在已经越来越少被实际采用了。</p>\n<h2>快速入门</h2>\n<p>在dubbo中开发一个REST风格的服务会比较简单,下面以一个注册用户的简单服务为例说明。</p>\n<p>这个服务要实现的功能是提供如下URL(注:这个URL不是完全符合REST的风格,但是更简单实用):</p>\n<pre><code>http://localhost:8080/users/register\n</code></pre>\n<p>而任何客户端都可以将包含用户信息的JSON字符串POST到以上URL来完成用户注册。</p>\n<p>首先,开发服务的接口:</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">UserService</span> </span>{    \n   <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">registerUser</span><span class="hljs-params">(User user)</span></span>;\n}\n</code></pre>\n<p>然后,开发服务的实现:</p>\n<pre><code class="language-java"><span class="hljs-meta">@Path</span>(<span class="hljs-string">"users"</span>)\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">UserService</span> </span>{\n       \n    <span class="hljs-meta">@POST</span>\n    <span class="hljs-meta">@Path</span>(<span class="hljs-string">"register"</span>)\n    <span class="hljs-meta">@Consumes</span>({MediaType.APPLICATION_JSON})\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">registerUser</span><span class="hljs-params">(User user)</span> </span>{\n        <span class="hljs-comment">// save the user...</span>\n    }\n}\n</code></pre>\n<p>上面的服务实现代码非常简单,但是由于REST服务是要被发布到特定HTTP URL,供任意语言客户端甚至浏览器来访问,所以这里要额外添加了几个JAX-RS的标准annotation来做相关的配置:</p>\n<p>@Path(&quot;users&quot;):指定访问UserService的URL相对路径是/users,即http://localhost:8080/users</p>\n<p>@Path(&quot;register&quot;):指定访问registerUser()方法的URL相对路径是/register,再结合上一个@Path为UserService指定的路径,则调用UserService.register()的完整路径为http://localhost:8080/users/register</p>\n<p>@POST:指定访问registerUser()用HTTP POST方法</p>\n<p>@Consumes({MediaType.APPLICATION_JSON}):指定registerUser()接收JSON格式的数据。REST框架会自动将JSON数据反序列化为User对象</p>\n<p>最后,在spring配置文件中添加此服务,即完成所有服务开发工作:</p>\n<pre><code class="language-xml"><span class="hljs-comment">&lt;!-- 用rest协议在8080端口暴露服务 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"8080"</span>/&gt;</span>\n\n<span class="hljs-comment">&lt;!-- 声明需要暴露的服务接口 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"xxx.UserService"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"userService"</span>/&gt;</span>\n\n<span class="hljs-comment">&lt;!-- 和本地bean一样实现服务 --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">bean</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"userService"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"xxx.UserServiceImpl"</span> /&gt;</span>\n</code></pre>\n<h2>标准Java REST API:JAX-RS简介</h2>\n<p>JAX-RS是标准的Java REST API,得到了业界的广泛支持和应用,其著名的开源实现就有很多,包括Oracle的Jersey,RedHat的RestEasy,Apache的CXF和Wink,以及restlet等等。另外,所有支持JavaEE 6.0以上规范的商用JavaEE应用服务器都对JAX-RS提供了支持。因此,JAX-RS是一种已经非常成熟的解决方案,并且采用它没有任何所谓vendor lock-in的问题。</p>\n<p>JAX-RS在网上的资料非常丰富,例如下面的入门教程:</p>\n<ul>\n<li>Oracle官方的tutorial:<a href="http://docs.oracle.com/javaee/7/tutorial/doc/jaxrs.htm">http://docs.oracle.com/javaee/7/tutorial/doc/jaxrs.htm</a></li>\n<li>IBM developerWorks中国站文章:<a href="http://www.ibm.com/developerworks/cn/java/j-lo-jaxrs/">http://www.ibm.com/developerworks/cn/java/j-lo-jaxrs/</a></li>\n</ul>\n<p>更多的资料请自行google或者百度一下。就学习JAX-RS来说,一般主要掌握其各种annotation的用法即可。</p>\n<blockquote>\n<p>注意:dubbo是基于JAX-RS 2.0版本的,有时候需要注意一下资料或REST实现所涉及的版本。</p>\n</blockquote>\n<h2>REST服务提供端详解</h2>\n<p>下面我们扩充“快速入门”中的UserService,进一步展示在dubbo中REST服务提供端的开发要点。</p>\n<h3>HTTP POST/GET的实现</h3>\n<p>REST服务中虽然建议使用HTTP协议中四种标准方法POST、DELETE、PUT、GET来分别实现常见的“增删改查”,但实际中,我们一般情况直接用POST来实现“增改”,GET来实现“删查”即可(DELETE和PUT甚至会被一些防火墙阻挡)。</p>\n<p>前面已经简单演示了POST的实现,在此,我们为UserService添加一个获取注册用户资料的功能,来演示GET的实现。</p>\n<p>这个功能就是要实现客户端通过访问如下不同URL来获取不同ID的用户资料:</p>\n<pre><code>http://localhost:8080/users/1001\nhttp://localhost:8080/users/1002\nhttp://localhost:8080/users/1003\n</code></pre>\n<p>当然,也可以通过其他形式的URL来访问不同ID的用户资料,例如:</p>\n<pre><code>http://localhost:8080/users/load?id=1001\n</code></pre>\n<p>JAX-RS本身可以支持所有这些形式。但是上面那种在URL路径中包含查询参数的形式(<a href="http://localhost:8080/users/1001%EF%BC%89">http://localhost:8080/users/1001)</a> 更符合REST的一般习惯,所以更推荐大家来使用。下面我们就为UserService添加一个getUser()方法来实现这种形式的URL访问:</p>\n<pre><code class="language-java"><span class="hljs-meta">@GET</span>\n<span class="hljs-meta">@Path</span>(<span class="hljs-string">"{id : \\\\d+}"</span>)\n<span class="hljs-meta">@Produces</span>({MediaType.APPLICATION_JSON})\n<span class="hljs-function"><span class="hljs-keyword">public</span> User <span class="hljs-title">getUser</span><span class="hljs-params">(@PathParam(<span class="hljs-string">"id"</span>)</span> Long id) </span>{\n    <span class="hljs-comment">// ...</span>\n}\n</code></pre>\n<p>@GET:指定用HTTP GET方法访问</p>\n<p>@Path(&quot;{id : \\d+}&quot;):根据上面的功能需求,访问getUser()的URL应当是“<a href="http://localhost:8080/users/">http://localhost:8080/users/</a> + 任意数字&quot;,并且这个数字要被做为参数传入getUser()方法。 这里的annotation配置中,@Path中间的{id: xxx}指定URL相对路径中包含了名为id参数,而它的值也将被自动传递给下面用@PathParam(&quot;id&quot;)修饰的方法参数id。{id:后面紧跟的\\d+是一个正则表达式,指定了id参数必须是数字。</p>\n<p>@Produces({MediaType.APPLICATION_JSON}):指定getUser()输出JSON格式的数据。框架会自动将User对象序列化为JSON数据。</p>\n<h3>Annotation放在接口类还是实现类</h3>\n<p>在Dubbo中开发REST服务主要都是通过JAX-RS的annotation来完成配置的,在上面的示例中,我们都是将annotation放在服务的实现类中。但其实,我们完全也可以将annotation放到服务的接口上,这两种方式是完全等价的,例如:</p>\n<pre><code class="language-java"><span class="hljs-meta">@Path</span>(<span class="hljs-string">"users"</span>)\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">UserService</span> </span>{\n    \n    <span class="hljs-meta">@GET</span>\n    <span class="hljs-meta">@Path</span>(<span class="hljs-string">"{id : \\\\d+}"</span>)\n    <span class="hljs-meta">@Produces</span>({MediaType.APPLICATION_JSON})\n    <span class="hljs-function">User <span class="hljs-title">getUser</span><span class="hljs-params">(@PathParam(<span class="hljs-string">"id"</span>)</span> Long id)</span>;\n}\n</code></pre>\n<p>在一般应用中,我们建议将annotation放到服务实现类,这样annotation和java实现代码位置更接近,更便于开发和维护。另外更重要的是,我们一般倾向于避免对接口的污染,保持接口的纯净性和广泛适用性。</p>\n<p>但是,如后文所述,如果我们要用dubbo直接开发的消费端来访问此服务,则annotation必须放到接口上。</p>\n<p>如果接口和实现类都同时添加了annotation,则实现类的annotation配置会生效,接口上的annotation被直接忽略。</p>\n<h3>JSON、XML等多数据格式的支持</h3>\n<p>在dubbo中开发的REST服务可以同时支持传输多种格式的数据,以给客户端提供最大的灵活性。其中我们目前对最常用的JSON和XML格式特别添加了额外的功能。</p>\n<p>比如,我们要让上例中的getUser()方法支持分别返回JSON和XML格式的数据,只需要在annotation中同时包含两种格式即可:</p>\n<pre><code class="language-java"><span class="hljs-meta">@Produces</span>({MediaType.APPLICATION_JSON, MediaType.TEXT_XML})\n<span class="hljs-function">User <span class="hljs-title">getUser</span><span class="hljs-params">(@PathParam(<span class="hljs-string">"id"</span>)</span> Long id)</span>;\n</code></pre>\n<p>或者也可以直接用字符串(还支持通配符)表示MediaType:</p>\n<pre><code class="language-java"><span class="hljs-meta">@Produces</span>({<span class="hljs-string">"application/json"</span>, <span class="hljs-string">"text/xml"</span>})\n<span class="hljs-function">User <span class="hljs-title">getUser</span><span class="hljs-params">(@PathParam(<span class="hljs-string">"id"</span>)</span> Long id)</span>;\n</code></pre>\n<p>如果所有方法都支持同样类型的输入输出数据格式,则我们无需在每个方法上做配置,只需要在服务类上添加annotation即可:</p>\n<pre><code class="language-java"><span class="hljs-meta">@Path</span>(<span class="hljs-string">"users"</span>)\n<span class="hljs-meta">@Consumes</span>({MediaType.APPLICATION_JSON, MediaType.TEXT_XML})\n<span class="hljs-meta">@Produces</span>({MediaType.APPLICATION_JSON, MediaType.TEXT_XML})\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">UserService</span> </span>{\n    <span class="hljs-comment">// ...</span>\n}\n\n</code></pre>\n<p>在一个REST服务同时对多种数据格式支持的情况下,根据JAX-RS标准,一般是通过HTTP中的MIME header(content-type和accept)来指定当前想用的是哪种格式的数据。</p>\n<p>但是在dubbo中,我们还自动支持目前业界普遍使用的方式,即用一个URL后缀(.json和.xml)来指定想用的数据格式。例如,在添加上述annotation后,直接访问http://localhost:8888/users/1001.json则表示用json格式,直接访问http://localhost:8888/users/1002.xml则表示用xml格式,比用HTTP Header更简单直观。Twitter、微博等的REST API都是采用这种方式。</p>\n<p>如果你既不加HTTP header,也不加后缀,则dubbo的REST会优先启用在以上annotation定义中排位最靠前的那种数据格式。</p>\n<blockquote>\n<p>注意:这里要支持XML格式数据,在annotation中既可以用MediaType.TEXT_XML,也可以用MediaType.APPLICATION_XML,但是TEXT_XML是更常用的,并且如果要利用上述的URL后缀方式来指定数据格式,只能配置为TEXT_XML才能生效。</p>\n</blockquote>\n<h3>中文字符支持</h3>\n<p>为了在dubbo REST中正常输出中文字符,和通常的Java web应用一样,我们需要将HTTP响应的contentType设置为UTF-8编码。</p>\n<p>基于JAX-RS的标准用法,我们只需要做如下annotation配置即可:</p>\n<pre><code class="language-java"><span class="hljs-meta">@Produces</span>({<span class="hljs-string">"application/json; charset=UTF-8"</span>, <span class="hljs-string">"text/xml; charset=UTF-8"</span>})\n<span class="hljs-function">User <span class="hljs-title">getUser</span><span class="hljs-params">(@PathParam(<span class="hljs-string">"id"</span>)</span> Long id)</span>;\n</code></pre>\n<p>为了方便用户,我们在dubbo REST中直接添加了一个支持类,来定义以上的常量,可以直接使用,减少出错的可能性。</p>\n<pre><code class="language-java"><span class="hljs-meta">@Produces</span>({ContentType.APPLICATION_JSON_UTF_8, ContentType.TEXT_XML_UTF_8})\n<span class="hljs-function">User <span class="hljs-title">getUser</span><span class="hljs-params">(@PathParam(<span class="hljs-string">"id"</span>)</span> Long id)</span>;\n</code></pre>\n<h3>XML数据格式的额外要求</h3>\n<p>由于JAX-RS的实现一般都用标准的JAXB(Java API for XML Binding)来序列化和反序列化XML格式数据,所以我们需要为每一个要用XML传输的对象添加一个类级别的JAXB annotation,否则序列化将报错。例如为getUser()中返回的User添加如下:</p>\n<pre><code class="language-java"><span class="hljs-meta">@XmlRootElement</span>\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Serializable</span> </span>{\n    <span class="hljs-comment">// ...</span>\n}\n</code></pre>\n<p>此外,如果service方法中的返回值是Java的 primitive类型(如int,long,float,double等),最好为它们添加一层wrapper对象,因为JAXB不能直接序列化primitive类型。</p>\n<p>例如,我们想让前述的registerUser()方法返回服务器端为用户生成的ID号:</p>\n<pre><code class="language-java"><span class="hljs-function"><span class="hljs-keyword">long</span> <span class="hljs-title">registerUser</span><span class="hljs-params">(User user)</span></span>;\n</code></pre>\n<p>由于primitive类型不被JAXB序列化支持,所以添加一个wrapper对象:</p>\n<pre><code class="language-java"><span class="hljs-meta">@XmlRootElement</span>\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RegistrationResult</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Serializable</span> </span>{\n    \n    <span class="hljs-keyword">private</span> Long id;\n    \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">RegistrationResult</span><span class="hljs-params">()</span> </span>{\n    }\n    \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">RegistrationResult</span><span class="hljs-params">(Long id)</span> </span>{\n        <span class="hljs-keyword">this</span>.id = id;\n    }\n    \n    <span class="hljs-function"><span class="hljs-keyword">public</span> Long <span class="hljs-title">getId</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-keyword">return</span> id;\n    }\n    \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setId</span><span class="hljs-params">(Long id)</span> </span>{\n        <span class="hljs-keyword">this</span>.id = id;\n    }\n}\n</code></pre>\n<p>并修改service方法:</p>\n<pre><code class="language-java"><span class="hljs-function">RegistrationResult <span class="hljs-title">registerUser</span><span class="hljs-params">(User user)</span></span>;\n</code></pre>\n<p>这样不但能够解决XML序列化的问题,而且使得返回的数据都符合XML和JSON的规范。例如,在JSON中,返回的将是如下形式:</p>\n<pre><code class="language-javascript">{<span class="hljs-string">"id"</span>: <span class="hljs-number">1001</span>}\n</code></pre>\n<p>如果不加wrapper,JSON返回值将直接是</p>\n<pre><code>1001 \t\n</code></pre>\n<p>而在XML中,加wrapper后返回值将是:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">registrationResult</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">id</span>&gt;</span>1002<span class="hljs-tag">&lt;/<span class="hljs-name">id</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">registrationResult</span>&gt;</span>\n</code></pre>\n<p>这种wrapper对象其实利用所谓Data Transfer Object(DTO)模式,采用DTO还能对传输数据做更多有用的定制。</p>\n<h3>定制序列化</h3>\n<p>如上所述,REST的底层实现会在service的对象和JSON/XML数据格式之间自动做序列化/反序列化。但有些场景下,如果觉得这种自动转换不满足要求,可以对其做定制。</p>\n<p>Dubbo中的REST实现是用JAXB做XML序列化,用Jackson做JSON序列化,所以在对象上添加JAXB或Jackson的annotation即可以定制映射。</p>\n<p>例如,定制对象属性映射到XML元素的名字:</p>\n<pre><code class="language-java"><span class="hljs-meta">@XmlRootElement</span>\n<span class="hljs-meta">@XmlAccessorType</span>(XmlAccessType.FIELD)\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Serializable</span> </span>{\n    \n    <span class="hljs-meta">@XmlElement</span>(name=<span class="hljs-string">"username"</span>) \n    <span class="hljs-keyword">private</span> String name;  \n}\n</code></pre>\n<p>定制对象属性映射到JSON字段的名字:</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">User</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Serializable</span> </span>{\n    \n    <span class="hljs-meta">@JsonProperty</span>(<span class="hljs-string">"username"</span>)\n    <span class="hljs-keyword">private</span> String name;\n}\n</code></pre>\n<p>更多资料请参考JAXB和Jackson的官方文档,或自行google。</p>\n<h3>配置REST Server的实现</h3>\n<p>目前在dubbo中,我们支持5种嵌入式rest server的实现,并同时支持采用外部应用服务器来做rest server的实现。rest server的实现是通过如下server这个XML属性来选择的:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"jetty"</span>/&gt;</span>\n</code></pre>\n<p>以上配置选用了嵌入式的jetty来做rest server,同时,如果不配置server属性,rest协议默认也是选用jetty。jetty是非常成熟的java servlet容器,并和dubbo已经有较好的集成(目前5种嵌入式server中只有jetty和后面所述的tomcat、tjws,与dubbo监控系统等完成了无缝的集成),所以,如果你的dubbo系统是单独启动的进程,你可以直接默认采用jetty即可。</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"tomcat"</span>/&gt;</span>\n</code></pre>\n<p>以上配置选用了嵌入式的tomcat来做rest server。在嵌入式tomcat上,REST的性能比jetty上要好得多(参见后面的基准测试),建议在需要高性能的场景下采用tomcat。</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"netty"</span>/&gt;</span>\n</code></pre>\n<p>以上配置选用嵌入式的netty来做rest server。(TODO more contents to add)</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"tjws"</span>/&gt;</span> (tjws is now deprecated)\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"sunhttp"</span>/&gt;</span>\n</code></pre>\n<p>以上配置选用嵌入式的tjws或Sun HTTP server来做rest server。这两个server实现非常轻量级,非常方便在集成测试中快速启动使用,当然也可以在负荷不高的生产环境中使用。\t注:tjws目前已经被deprecated掉了,因为它不能很好的和servlet 3.1 API工作。</p>\n<p>如果你的dubbo系统不是单独启动的进程,而是部署到了Java应用服务器中,则建议你采用以下配置:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"servlet"</span>/&gt;</span>\n</code></pre>\n<p>通过将server设置为servlet,dubbo将采用外部应用服务器的servlet容器来做rest server。同时,还要在dubbo系统的web.xml中添加如下配置:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">web-app</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">context-param</span>&gt;</span>\n        <span class="hljs-tag">&lt;<span class="hljs-name">param-name</span>&gt;</span>contextConfigLocation<span class="hljs-tag">&lt;/<span class="hljs-name">param-name</span>&gt;</span>\n        <span class="hljs-tag">&lt;<span class="hljs-name">param-value</span>&gt;</span>/WEB-INF/classes/META-INF/spring/dubbo-demo-provider.xml<span class="hljs-tag">&lt;/<span class="hljs-name">param-value</span>&gt;</span>\n    <span class="hljs-tag">&lt;/<span class="hljs-name">context-param</span>&gt;</span>\n    \n    <span class="hljs-tag">&lt;<span class="hljs-name">listener</span>&gt;</span>\n        <span class="hljs-tag">&lt;<span class="hljs-name">listener-class</span>&gt;</span>com.alibaba.dubbo.remoting.http.servlet.BootstrapListener<span class="hljs-tag">&lt;/<span class="hljs-name">listener-class</span>&gt;</span>\n    <span class="hljs-tag">&lt;/<span class="hljs-name">listener</span>&gt;</span>\n    \n    <span class="hljs-tag">&lt;<span class="hljs-name">listener</span>&gt;</span>\n        <span class="hljs-tag">&lt;<span class="hljs-name">listener-class</span>&gt;</span>org.springframework.web.context.ContextLoaderListener<span class="hljs-tag">&lt;/<span class="hljs-name">listener-class</span>&gt;</span>\n    <span class="hljs-tag">&lt;/<span class="hljs-name">listener</span>&gt;</span>\n    \n    <span class="hljs-tag">&lt;<span class="hljs-name">servlet</span>&gt;</span>\n        <span class="hljs-tag">&lt;<span class="hljs-name">servlet-name</span>&gt;</span>dispatcher<span class="hljs-tag">&lt;/<span class="hljs-name">servlet-name</span>&gt;</span>\n        <span class="hljs-tag">&lt;<span class="hljs-name">servlet-class</span>&gt;</span>com.alibaba.dubbo.remoting.http.servlet.DispatcherServlet<span class="hljs-tag">&lt;/<span class="hljs-name">servlet-class</span>&gt;</span>\n        <span class="hljs-tag">&lt;<span class="hljs-name">load-on-startup</span>&gt;</span>1<span class="hljs-tag">&lt;/<span class="hljs-name">load-on-startup</span>&gt;</span>\n    <span class="hljs-tag">&lt;/<span class="hljs-name">servlet</span>&gt;</span>\n    \n    <span class="hljs-tag">&lt;<span class="hljs-name">servlet-mapping</span>&gt;</span>\n        <span class="hljs-tag">&lt;<span class="hljs-name">servlet-name</span>&gt;</span>dispatcher<span class="hljs-tag">&lt;/<span class="hljs-name">servlet-name</span>&gt;</span>\n        <span class="hljs-tag">&lt;<span class="hljs-name">url-pattern</span>&gt;</span>/*<span class="hljs-tag">&lt;/<span class="hljs-name">url-pattern</span>&gt;</span>\n    <span class="hljs-tag">&lt;/<span class="hljs-name">servlet-mapping</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">web-app</span>&gt;</span>\n</code></pre>\n<p>即必须将dubbo的BootstrapListener和DispatherServlet添加到web.xml,以完成dubbo的REST功能与外部servlet容器的集成。</p>\n<blockquote>\n<p>注意:如果你是用spring的ContextLoaderListener来加载spring,则必须保证BootstrapListener配置在ContextLoaderListener之前,否则dubbo初始化会出错。</p>\n</blockquote>\n<p>其实,这种场景下你依然可以坚持用嵌入式server,但外部应用服务器的servlet容器往往比嵌入式server更加强大(特别是如果你是部署到更健壮更可伸缩的WebLogic,WebSphere等),另外有时也便于在应用服务器做统一管理、监控等等。</p>\n<h3>获取上下文(Context)信息</h3>\n<p>在远程调用中,值得获取的上下文信息可能有很多种,这里特别以获取客户端IP为例。</p>\n<p>在dubbo的REST中,我们有两种方式获取客户端IP。</p>\n<p>第一种方式,用JAX-RS标准的@Context annotation:</p>\n<pre><code class="language-java"><span class="hljs-function"><span class="hljs-keyword">public</span> User <span class="hljs-title">getUser</span><span class="hljs-params">(@PathParam(<span class="hljs-string">"id"</span>)</span> Long id, @Context HttpServletRequest request) </span>{\n    System.out.println(<span class="hljs-string">"Client address is "</span> + request.getRemoteAddr());\n} \n</code></pre>\n<p>用Context修饰getUser()的一个方法参数后,就可以将当前的HttpServletRequest注入进来,然后直接调用servlet api获取IP。</p>\n<blockquote>\n<p>注意:这种方式只能在设置server=&quot;tjws&quot;或者server=&quot;tomcat&quot;或者server=&quot;jetty&quot;或者server=&quot;servlet&quot;的时候才能工作,因为只有这几种REST server的实现才提供了servlet容器。另外,标准的JAX-RS还支持用@Context修饰service类的一个实例字段来获取HttpServletRequest,但在dubbo中我们没有对此作出支持。</p>\n</blockquote>\n<p>第二种方式,用dubbo中常用的RpcContext:</p>\n<pre><code class="language-java"><span class="hljs-function"><span class="hljs-keyword">public</span> User <span class="hljs-title">getUser</span><span class="hljs-params">(@PathParam(<span class="hljs-string">"id"</span>)</span> Long id) </span>{\n    System.out.println(<span class="hljs-string">"Client address is "</span> + RpcContext.getContext().getRemoteAddressString());\n} \n</code></pre>\n<blockquote>\n<p>注意:这种方式只能在设置server=&quot;jetty&quot;或者server=&quot;tomcat&quot;或者server=&quot;servlet&quot;或者server=&quot;tjws&quot;的时候才能工作。另外,目前dubbo的RpcContext是一种比较有侵入性的用法,未来我们很可能会做出重构。</p>\n</blockquote>\n<p>如果你想保持你的项目对JAX-RS的兼容性,未来脱离dubbo也可以运行,请选择第一种方式。如果你想要更优雅的服务接口定义,请选用第二种方式。</p>\n<p>此外,在最新的dubbo rest中,还支持通过RpcContext来获取HttpServletRequest和HttpServletResponse,以提供更大的灵活性来方便用户实现某些复杂功能,比如在dubbo标准的filter中访问HTTP Header。用法示例如下:</p>\n<pre><code class="language-java"><span class="hljs-keyword">if</span> (RpcContext.getContext().getRequest() != <span class="hljs-keyword">null</span> &amp;&amp; RpcContext.getContext().getRequest() <span class="hljs-keyword">instanceof</span> HttpServletRequest) {\n    System.out.println(<span class="hljs-string">"Client address is "</span> + ((HttpServletRequest) RpcContext.getContext().getRequest()).getRemoteAddr());\n}\n\n<span class="hljs-keyword">if</span> (RpcContext.getContext().getResponse() != <span class="hljs-keyword">null</span> &amp;&amp; RpcContext.getContext().getResponse() <span class="hljs-keyword">instanceof</span> HttpServletResponse) {\n    System.out.println(<span class="hljs-string">"Response object from RpcContext: "</span> + RpcContext.getContext().getResponse());\n}\n</code></pre>\n<blockquote>\n<p>注意:为了保持协议的中立性,RpcContext.getRequest()和RpcContext.getResponse()返回的仅仅是一个Object类,而且可能为null。所以,你必须自己做null和类型的检查。</p>\n</blockquote>\n<blockquote>\n<p>注意:只有在设置server=&quot;jetty&quot;或者server=&quot;tomcat&quot;或者server=&quot;servlet&quot;的时候,你才能通过以上方法正确的得到HttpServletRequest和HttpServletResponse,因为只有这几种server实现了servlet容器。</p>\n</blockquote>\n<p>为了简化编程,在此你也可以用泛型的方式来直接获取特定类型的request/response:</p>\n<pre><code class="language-java"><span class="hljs-keyword">if</span> (RpcContext.getContext().getRequest(HttpServletRequest.class) != <span class="hljs-keyword">null</span>) {\n    System.out.println(<span class="hljs-string">"Client address is "</span> + RpcContext.getContext().getRequest(HttpServletRequest.class).getRemoteAddr());\n}\n\n<span class="hljs-keyword">if</span> (RpcContext.getContext().getResponse(HttpServletResponse.class) != <span class="hljs-keyword">null</span>) {\n    System.out.println(<span class="hljs-string">"Response object from RpcContext: "</span> + RpcContext.getContext().getResponse(HttpServletResponse.class));\n}\n</code></pre>\n<p>如果request/response不符合指定的类型,这里也会返回null。</p>\n<h3>配置端口号和Context Path</h3>\n<p>dubbo中的rest协议默认将采用80端口,如果想修改端口,直接配置:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"8888"</span>/&gt;</span>\n</code></pre>\n<p>另外,如前所述,我们可以用@Path来配置单个rest服务的URL相对路径。但其实,我们还可以设置一个所有rest服务都适用的基础相对路径,即java web应用中常说的context path。</p>\n<p>只需要添加如下contextpath属性即可:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"8888"</span> <span class="hljs-attr">contextpath</span>=<span class="hljs-string">"services"</span>/&gt;</span>\n</code></pre>\n<p>以前面代码为例:</p>\n<pre><code class="language-java"><span class="hljs-meta">@Path</span>(<span class="hljs-string">"users"</span>)\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">UserService</span> </span>{\n       \n    <span class="hljs-meta">@POST</span>\n    <span class="hljs-meta">@Path</span>(<span class="hljs-string">"register"</span>)\n    <span class="hljs-meta">@Consumes</span>({MediaType.APPLICATION_JSON})\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">registerUser</span><span class="hljs-params">(User user)</span> </span>{\n        <span class="hljs-comment">// save the user...</span>\n    }\t\n}\n</code></pre>\n<p>现在registerUser()的完整访问路径为:</p>\n<pre><code>http://localhost:8888/services/users/register\n</code></pre>\n<p>注意:如果你是选用外部应用服务器做rest server,即配置:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"8888"</span> <span class="hljs-attr">contextpath</span>=<span class="hljs-string">"services"</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"servlet"</span>/&gt;</span>\n</code></pre>\n<p>则必须保证这里设置的port、contextpath,与外部应用服务器的端口、DispatcherServlet的上下文路径(即webapp path加上servlet url pattern)保持一致。例如,对于部署为tomcat ROOT路径的应用,这里的contextpath必须与web.xml中DispacherServlet的<code>&lt;url-pattern/&gt;</code>完全一致:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">servlet-mapping</span>&gt;</span>\n     <span class="hljs-tag">&lt;<span class="hljs-name">servlet-name</span>&gt;</span>dispatcher<span class="hljs-tag">&lt;/<span class="hljs-name">servlet-name</span>&gt;</span>\n     <span class="hljs-tag">&lt;<span class="hljs-name">url-pattern</span>&gt;</span>/services/*<span class="hljs-tag">&lt;/<span class="hljs-name">url-pattern</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">servlet-mapping</span>&gt;</span>\n</code></pre>\n<h3>配置线程数和IO线程数</h3>\n<p>可以为rest服务配置线程池大小:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">threads</span>=<span class="hljs-string">"500"</span>/&gt;</span>\n</code></pre>\n<blockquote>\n<p>注意:目前线程池的设置只有当server=&quot;netty&quot;或者server=&quot;jetty&quot;或者server=&quot;tomcat&quot;的时候才能生效。另外,如果server=&quot;servlet&quot;,由于这时候启用的是外部应用服务器做rest server,不受dubbo控制,所以这里的线程池设置也无效。</p>\n</blockquote>\n<p>如果是选用netty server,还可以配置Netty的IO worker线程数:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">iothreads</span>=<span class="hljs-string">"5"</span> <span class="hljs-attr">threads</span>=<span class="hljs-string">"100"</span>/&gt;</span>\n</code></pre>\n<h3>配置长连接</h3>\n<p>Dubbo中的rest服务默认都是采用http长连接来访问,如果想切换为短连接,直接配置:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">keepalive</span>=<span class="hljs-string">"false"</span>/&gt;</span>\n</code></pre>\n<blockquote>\n<p>注意:这个配置目前只对server=&quot;netty&quot;和server=&quot;tomcat&quot;才能生效。</p>\n</blockquote>\n<h3>配置最大的HTTP连接数</h3>\n<p>可以配置服务器提供端所能同时接收的最大HTTP连接数,防止REST server被过多连接撑爆,以作为一种最基本的自我保护机制:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">accepts</span>=<span class="hljs-string">"500"</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"tomcat/&gt;\n</span></span></code></pre>\n<blockquote>\n<p>注意:这个配置目前只对server=&quot;tomcat&quot;才能生效。</p>\n</blockquote>\n<h3>配置每个消费端的超时时间和HTTP连接数</h3>\n<p>如果rest服务的消费端也是dubbo系统,可以像其他dubbo RPC机制一样,配置消费端调用此rest服务的最大超时时间以及每个消费端所能启动的最大HTTP连接数。</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"xxx"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"xxx"</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">timeout</span>=<span class="hljs-string">"2000"</span> <span class="hljs-attr">connections</span>=<span class="hljs-string">"10"</span>/&gt;</span>\n</code></pre>\n<p>当然,由于这个配置针对消费端生效的,所以也可以在消费端配置:</p>\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">"xxx"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"xxx"</span> <span class="hljs-attr">timeout</span>=<span class="hljs-string">"2000"</span> <span class="hljs-attr">connections</span>=<span class="hljs-string">"10"</span>/&gt;</span>\n</code></pre>\n<p>但是,通常我们建议配置在服务提供端提供此类配置。按照dubbo官方文档的说法:“Provider上尽量多配置Consumer端的属性,让Provider实现者一开始就思考Provider服务特点、服务质量的问题。”</p>\n<blockquote>\n<p>注意:如果dubbo的REST服务是发布给非dubbo的客户端使用,则这里<code>&lt;dubbo:service/&gt;</code>上的配置完全无效,因为这种客户端不受dubbo控制。</p>\n</blockquote>\n<h3>GZIP数据压缩</h3>\n<p>Dubbo的REST支持用GZIP压缩请求和响应的数据,以减少网络传输时间和带宽占用,但这种方式会也增加CPU开销。</p>\n<p>TODO more contents to add</p>\n<h3>用Annotation取代部分Spring XML配置</h3>\n<p>以上所有的讨论都是基于dubbo在spring中的xml配置。但是,dubbo/spring本身也支持用annotation来作配置,所以我们也可以按dubbo官方文档中的步骤,把相关annotation加到REST服务的实现中,取代一些xml配置,例如:</p>\n<pre><code class="language-java"><span class="hljs-meta">@Service</span>(protocol = <span class="hljs-string">"rest"</span>)\n<span class="hljs-meta">@Path</span>(<span class="hljs-string">"users"</span>)\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">UserService</span> </span>{\n\n    <span class="hljs-meta">@Autowired</span>\n    <span class="hljs-keyword">private</span> UserRepository userRepository;\n       \n    <span class="hljs-meta">@POST</span>\n    <span class="hljs-meta">@Path</span>(<span class="hljs-string">"register"</span>)\n    <span class="hljs-meta">@Consumes</span>({MediaType.APPLICATION_JSON})\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">registerUser</span><span class="hljs-params">(User user)</span> </span>{\n        <span class="hljs-comment">// save the user</span>\n        userRepository.save(user);\n    }\t\n}\n</code></pre>\n<p>annotation的配置更简单更精确,经常也更便于维护(当然现代IDE都可以在xml中支持比如类名重构,所以就这里的特定用例而言,xml的维护性也很好)。而xml对代码的侵入性更小一些,尤其有利于动态修改配置,特别是比如你要针对单个服务配置连接超时时间、每客户端最大连接数、集群策略、权重等等。另外,特别对复杂应用或者模块来说,xml提供了一个中心点来涵盖的所有组件和配置,更一目了然,一般更便于项目长时期的维护。</p>\n<p>当然,选择哪种配置方式没有绝对的优劣,和个人的偏好也不无关系。</p>\n<h3>添加自定义的Filter、Interceptor等</h3>\n<p>Dubbo的REST也支持JAX-RS标准的Filter和Interceptor,以方便对REST的请求与响应过程做定制化的拦截处理。</p>\n<p>其中,Filter主要用于访问和设置HTTP请求和响应的参数、URI等等。例如,设置HTTP响应的cache header:</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">CacheControlFilter</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">ContainerResponseFilter</span> </span>{\n\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">filter</span><span class="hljs-params">(ContainerRequestContext req, ContainerResponseContext res)</span> </span>{\n        <span class="hljs-keyword">if</span> (req.getMethod().equals(<span class="hljs-string">"GET"</span>)) {\n            res.getHeaders().add(<span class="hljs-string">"Cache-Control"</span>, <span class="hljs-string">"someValue"</span>);\n        }\n    }\n}\n</code></pre>\n<p>Interceptor主要用于访问和修改输入与输出字节流,例如,手动添加GZIP压缩:</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">GZIPWriterInterceptor</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">WriterInterceptor</span> </span>{\n \n    <span class="hljs-meta">@Override</span>\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">aroundWriteTo</span><span class="hljs-params">(WriterInterceptorContext context)</span>\n                    <span class="hljs-keyword">throws</span> IOException, WebApplicationException </span>{\n        OutputStream outputStream = context.getOutputStream();\n        context.setOutputStream(<span class="hljs-keyword">new</span> GZIPOutputStream(outputStream));\n        context.proceed();\n    }\n}\n</code></pre>\n<p>在标准JAX-RS应用中,我们一般是为Filter和Interceptor添加@Provider annotation,然后JAX-RS runtime会自动发现并启用它们。而在dubbo中,我们是通过添加XML配置的方式来注册Filter和Interceptor:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"8888"</span> <span class="hljs-attr">extension</span>=<span class="hljs-string">"xxx.TraceInterceptor, xxx.TraceFilter"</span>/&gt;</span>\n</code></pre>\n<p>在此,我们可以将Filter、Interceptor和DynamicFuture这三种类型的对象都添加到extension属性上,多个之间用逗号分隔。(DynamicFuture是另一个接口,可以方便我们更动态的启用Filter和Interceptor,感兴趣请自行google。)</p>\n<p>当然,dubbo自身也支持Filter的概念,但我们这里讨论的Filter和Interceptor更加接近协议实现的底层,相比dubbo的filter,可以做更底层的定制化。</p>\n<blockquote>\n<p>注:这里的XML属性叫extension,而不是叫interceptor或者filter,是因为除了Interceptor和Filter,未来我们还会添加更多的扩展类型。</p>\n</blockquote>\n<p>如果REST的消费端也是dubbo系统(参见下文的讨论),则也可以用类似方式为消费端配置Interceptor和Filter。但注意,JAX-RS中消费端的Filter和提供端的Filter是两种不同的接口。例如前面例子中服务端是ContainerResponseFilter接口,而消费端对应的是ClientResponseFilter:</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">LoggingFilter</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">ClientResponseFilter</span> </span>{\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">filter</span><span class="hljs-params">(ClientRequestContext reqCtx, ClientResponseContext resCtx)</span> <span class="hljs-keyword">throws</span> IOException </span>{\n        System.out.println(<span class="hljs-string">"status: "</span> + resCtx.getStatus());\n\t    System.out.println(<span class="hljs-string">"date: "</span> + resCtx.getDate());\n\t    System.out.println(<span class="hljs-string">"last-modified: "</span> + resCtx.getLastModified());\n\t    System.out.println(<span class="hljs-string">"location: "</span> + resCtx.getLocation());\n\t    System.out.println(<span class="hljs-string">"headers:"</span>);\n\t    <span class="hljs-keyword">for</span> (Entry&lt;String, List&lt;String&gt;&gt; header : resCtx.getHeaders().entrySet()) {\n     \t    System.out.print(<span class="hljs-string">"\\t"</span> + header.getKey() + <span class="hljs-string">" :"</span>);\n\t        <span class="hljs-keyword">for</span> (String value : header.getValue()) {\n\t            System.out.print(value + <span class="hljs-string">", "</span>);\n\t        }\n\t        System.out.print(<span class="hljs-string">"\\n"</span>);\n\t    }\n\t    System.out.println(<span class="hljs-string">"media-type: "</span> + resCtx.getMediaType().getType());\n    } \n}\n</code></pre>\n<h3>添加自定义的Exception处理</h3>\n<p>Dubbo的REST也支持JAX-RS标准的ExceptionMapper,可以用来定制特定exception发生后应该返回的HTTP响应。</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">CustomExceptionMapper</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">ExceptionMapper</span>&lt;<span class="hljs-title">NotFoundException</span>&gt; </span>{\n\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Response <span class="hljs-title">toResponse</span><span class="hljs-params">(NotFoundException e)</span> </span>{     \n        <span class="hljs-keyword">return</span> Response.status(Response.Status.NOT_FOUND).entity(<span class="hljs-string">"Oops! the requested resource is not found!"</span>).type(<span class="hljs-string">"text/plain"</span>).build();\n    }\n}\n</code></pre>\n<p>和Interceptor、Filter类似,将其添加到XML配置文件中即可启用:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"8888"</span> <span class="hljs-attr">extension</span>=<span class="hljs-string">"xxx.CustomExceptionMapper"</span>/&gt;</span>\n</code></pre>\n<h3>配置HTTP日志输出</h3>\n<p>Dubbo rest支持输出所有HTTP请求/响应中的header字段和body消息体。</p>\n<p>在XML配置中添加如下自带的REST filter:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"8888"</span> <span class="hljs-attr">extension</span>=<span class="hljs-string">"com.alibaba.dubbo.rpc.protocol.rest.support.LoggingFilter"</span>/&gt;</span>\n</code></pre>\n<p>然后配置在logging配置中至少为com.alibaba.dubbo.rpc.protocol.rest.support打开INFO级别日志输出,例如,在log4j.xml中配置:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">logger</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"com.alibaba.dubbo.rpc.protocol.rest.support"</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">level</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"INFO"</span>/&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">appender-ref</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"CONSOLE"</span>/&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">logger</span>&gt;</span>\n</code></pre>\n<p>当然,你也可以直接在ROOT logger打开INFO级别日志输出:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">root</span>&gt;</span>\n\t<span class="hljs-tag">&lt;<span class="hljs-name">level</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"INFO"</span> /&gt;</span>\n\t<span class="hljs-tag">&lt;<span class="hljs-name">appender-ref</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"CONSOLE"</span>/&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">root</span>&gt;</span>\n</code></pre>\n<p>然后在日志中会有类似如下的内容输出:</p>\n<pre><code>The HTTP headers are: \naccept: application/json;charset=UTF-8\naccept-encoding: gzip, deflate\nconnection: Keep-Alive\ncontent-length: 22\ncontent-type: application/json\nhost: 192.168.1.100:8888\nuser-agent: Apache-HttpClient/4.2.1 (java 1.5)\n</code></pre>\n<pre><code>The contents of request body is: \n{&quot;id&quot;:1,&quot;name&quot;:&quot;dang&quot;}\n</code></pre>\n<p>打开HTTP日志输出后,除了正常日志输出的性能开销外,也会在比如HTTP请求解析时产生额外的开销,因为需要建立额外的内存缓冲区来为日志的输出做数据准备。</p>\n<h3>输入参数的校验</h3>\n<p>dubbo的rest支持采用Java标准的bean validation annotation(JSR 303)来做输入校验http://beanvalidation.org/</p>\n<p>为了和其他dubbo远程调用协议保持一致,在rest中作校验的annotation必须放在服务的接口上,例如:</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">UserService</span> </span>{\n   \n    <span class="hljs-function">User <span class="hljs-title">getUser</span><span class="hljs-params">(@Min(value=<span class="hljs-number">1</span>L, message=<span class="hljs-string">"User ID must be greater than 1"</span>)</span> Long id)</span>;\n}\n\n</code></pre>\n<p>当然,在很多其他的bean validation的应用场景都是将annotation放到实现类而不是接口上。把annotation放在接口上至少有一个好处是,dubbo的客户端可以共享这个接口的信息,dubbo甚至不需要做远程调用,在本地就可以完成输入校验。</p>\n<p>然后按照dubbo的标准方式在XML配置中打开验证:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">xxx.UserService</span>" <span class="hljs-attr">ref</span>=<span class="hljs-string">"userService"</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">validation</span>=<span class="hljs-string">"true"</span>/&gt;</span>\n</code></pre>\n<p>在dubbo的其他很多远程调用协议中,如果输入验证出错,是直接将<code>RpcException</code>抛向客户端,而在rest中由于客户端经常是非dubbo,甚至非java的系统,所以不便直接抛出Java异常。因此,目前我们将校验错误以XML的格式返回:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">violationReport</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">constraintViolations</span>&gt;</span>\n        <span class="hljs-tag">&lt;<span class="hljs-name">path</span>&gt;</span>getUserArgument0<span class="hljs-tag">&lt;/<span class="hljs-name">path</span>&gt;</span>\n        <span class="hljs-tag">&lt;<span class="hljs-name">message</span>&gt;</span>User ID must be greater than 1<span class="hljs-tag">&lt;/<span class="hljs-name">message</span>&gt;</span>\n        <span class="hljs-tag">&lt;<span class="hljs-name">value</span>&gt;</span>0<span class="hljs-tag">&lt;/<span class="hljs-name">value</span>&gt;</span>\n    <span class="hljs-tag">&lt;/<span class="hljs-name">constraintViolations</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">violationReport</span>&gt;</span>\n</code></pre>\n<p>稍后也会支持其他数据格式的返回值。至于如何对验证错误消息作国际化处理,直接参考bean validation的相关文档即可。</p>\n<p>如果你认为默认的校验错误返回格式不符合你的要求,可以如上面章节所述,添加自定义的ExceptionMapper来自由的定制错误返回格式。需要注意的是,这个ExceptionMapper必须用泛型声明来捕获dubbo的RpcException,才能成功覆盖dubbo rest默认的异常处理策略。为了简化操作,其实这里最简单的方式是直接继承dubbo rest的RpcExceptionMapper,并覆盖其中处理校验异常的方法即可:</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">MyValidationExceptionMapper</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">RpcExceptionMapper</span> </span>{\n\n    <span class="hljs-function"><span class="hljs-keyword">protected</span> Response <span class="hljs-title">handleConstraintViolationException</span><span class="hljs-params">(ConstraintViolationException cve)</span> </span>{\n        ViolationReport report = <span class="hljs-keyword">new</span> ViolationReport();\n        <span class="hljs-keyword">for</span> (ConstraintViolation cv : cve.getConstraintViolations()) {\n            report.addConstraintViolation(<span class="hljs-keyword">new</span> RestConstraintViolation(\n                    cv.getPropertyPath().toString(),\n                    cv.getMessage(),\n                    cv.getInvalidValue() == <span class="hljs-keyword">null</span> ? <span class="hljs-string">"null"</span> : cv.getInvalidValue().toString()));\n        }\n        <span class="hljs-comment">// 采用json输出代替xml输出</span>\n        <span class="hljs-keyword">return</span> Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(report).type(ContentType.APPLICATION_JSON_UTF_8).build();\n    }\n}\n</code></pre>\n<p>然后将这个ExceptionMapper添加到XML配置中即可:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rest"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"8888"</span> <span class="hljs-attr">extension</span>=<span class="hljs-string">"xxx.MyValidationExceptionMapper"</span>/&gt;</span>\n</code></pre>\n<h3>是否应该透明发布REST服务</h3>\n<p>Dubbo的REST调用和dubbo中其它某些RPC不同的是,需要在服务代码中添加JAX-RS的annotation(以及JAXB、Jackson的annotation),如果你觉得这些annotation一定程度“污染”了你的服务代码,你可以考虑编写额外的Facade和DTO类,在Facade和DTO上添加annotation,而Facade将调用转发给真正的服务实现类。当然事实上,直接在服务代码中添加annotation基本没有任何负面作用,而且这本身是Java EE的标准用法,另外JAX-RS和JAXB的annotation是属于java标准,比我们经常使用的spring、dubbo等等annotation更没有vendor lock-in的问题,所以一般没有必要因此而引入额外对象。</p>\n<p>另外,如果你想用前述的@Context annotation,通过方法参数注入HttpServletRequest(如<code>public User getUser(@PathParam(&quot;id&quot;) Long id, @Context HttpServletRequest request)</code>),这时候由于改变了服务的方法签名,并且HttpServletRequest是REST特有的参数,如果你的服务要支持多种RPC机制的话,则引入额外的Facade类是比较适当的。</p>\n<p>当然,在没有添加REST调用之前,你的服务代码可能本身已经就充当了Facade和DTO的角色(至于为什么有些场景需要这些角色,有兴趣可参考<a href="http://www.infoq.com/cn/articles/micro-soa-1">微观SOA:服务设计原则及其实践方式</a>)。这种情况下,在添加REST之后,如果你再额外添加与REST相关的Facade和DTO,就相当于对原有代码对再一次包装,即形成如下调用链:</p>\n<p><code>RestFacade/RestDTO -&gt; Facade/DTO -&gt; Service</code></p>\n<p>这种体系比较繁琐,数据转换之类的工作量也不小,所以一般应尽量避免如此。</p>\n<h2>REST服务消费端详解</h2>\n<p>这里我们用三种场景来分别讨论:</p>\n<ol>\n<li>非dubbo的消费端调用dubbo的REST服务(non-dubbo --&gt; dubbo)</li>\n<li>dubbo消费端调用dubbo的REST服务 (dubbo --&gt; dubbo)</li>\n<li>dubbo的消费端调用非dubbo的REST服务 (dubbo --&gt; non-dubbo)</li>\n</ol>\n<h3>场景1:非dubbo的消费端调用dubbo的REST服务</h3>\n<p>这种场景的客户端与dubbo本身无关,直接选用相应语言和框架中合适的方式即可。</p>\n<p>如果是还是java的客户端(但没用dubbo),可以考虑直接使用标准的JAX-RS Client API或者特定REST实现的Client API来调用REST服务。下面是用JAX-RS Client API来访问上述的UserService的registerUser():</p>\n<pre><code class="language-java">User user = <span class="hljs-keyword">new</span> User();\nuser.setName(<span class="hljs-string">"Larry"</span>);\n\nClient client = ClientBuilder.newClient();\nWebTarget target = client.target(<span class="hljs-string">"http://localhost:8080/services/users/register.json"</span>);\nResponse response = target.request().post(Entity.entity(user, MediaType.APPLICATION_JSON_TYPE));\n\n<span class="hljs-keyword">try</span> {\n    <span class="hljs-keyword">if</span> (response.getStatus() != <span class="hljs-number">200</span>) {\n        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RuntimeException(<span class="hljs-string">"Failed with HTTP error code : "</span> + response.getStatus());\n    }\n    System.out.println(<span class="hljs-string">"The generated id is "</span> + response.readEntity(RegistrationResult.class).getId());\n} <span class="hljs-keyword">finally</span> {\n    response.close();\n    client.close(); <span class="hljs-comment">// 在真正开发中不要每次关闭client,比如HTTP长连接是由client持有的</span>\n}\n</code></pre>\n<p>上面代码片段中的User和RegistrationResult类都是消费端自己编写的,JAX-RS Client API会自动对它们做序列化/反序列化。</p>\n<p>当然,在java中也可以直接用自己熟悉的比如HttpClient,FastJson,XStream等等各种不同技术来实现REST客户端,在此不再详述。</p>\n<h3>场景2:dubbo消费端调用dubbo的REST服务</h3>\n<p>这种场景下,和使用其他dubbo的远程调用方式一样,直接在服务提供端和服务消费端共享Java服务接口,并添加spring xml配置(当然也可以用spring/dubbo的annotation配置),即可透明的调用远程REST服务:</p>\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">"userService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"xxx.UserService"</span>/&gt;</span>\n</code></pre>\n<p>如前所述,这种场景下必须把JAX-RS的annotation添加到服务接口上,这样在dubbo在消费端才能共享相应的REST配置信息,并据之做远程调用:</p>\n<pre><code class="language-java"><span class="hljs-meta">@Path</span>(<span class="hljs-string">"users"</span>)\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">UserService</span> </span>{\n    \n    <span class="hljs-meta">@GET</span>\n    <span class="hljs-meta">@Path</span>(<span class="hljs-string">"{id : \\\\d+}"</span>)\n    <span class="hljs-meta">@Produces</span>({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})\n    <span class="hljs-function">User <span class="hljs-title">getUser</span><span class="hljs-params">(@PathParam(<span class="hljs-string">"id"</span>)</span> Long id)</span>;\n}\n</code></pre>\n<p>如果服务接口的annotation中配置了多种数据格式,这里由于两端都是dubbo系统,REST的大量细节被屏蔽了,所以不存在用前述URL后缀之类选择数据格式的可能。目前在这种情况下,排名最靠前的数据格式将直接被使用。</p>\n<p>因此,我们建议你在定义annotation的时候最好把最合适的数据格式放到前面,比如以上我们是把json放在xml前面,因为json的传输性能优于xml。</p>\n<h3>场景3:dubbo的消费端调用非dubbo的REST服务</h3>\n<p>这种场景下,可以直接用场景1中描述的Java的方式来调用REST服务。但其实也可以采用场景2中描述的方式,即更透明的调用REST服务,即使这个服务并不是dubbo提供的。</p>\n<p>如果用场景2的方式,由于这里REST服务并非dubbo提供,一般也就没有前述的共享的Java服务接口,所以在此我们需要根据外部REST服务的情况,自己来编写Java接口以及相应参数类,并添加JAX-RS、JAXB、Jackson等的annotation,dubbo的REST底层实现会据此去自动生成请求消息,自动解析响应消息等等,从而透明的做远程调用。或者这种方式也可以理解为,我们尝试用JAX-RS的方式去仿造实现一遍外部的REST服务提供端,然后把写成服务接口放到客户端来直接使用,dubbo的REST底层实现就能像调用dubbo的REST服务一样调用其他REST服务。</p>\n<p>例如,我们要调用如下的外部服务</p>\n<pre><code>http://api.foo.com/services/users/1001\nhttp://api.foo.com/services/users/1002\n</code></pre>\n<p>获取不同ID的用户资料,返回格式是JSON</p>\n<pre><code class="language-javascript">{\n    <span class="hljs-string">"id"</span>: <span class="hljs-number">1001</span>,\n    <span class="hljs-string">"name"</span>: <span class="hljs-string">"Larry"</span>\n}\n</code></pre>\n<p>我们可根据这些信息,编写服务接口和参数类即可:</p>\n<pre><code class="language-java"><span class="hljs-meta">@Path</span>(<span class="hljs-string">"users"</span>)\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">UserService</span> </span>{\n    \n    <span class="hljs-meta">@GET</span>\n    <span class="hljs-meta">@Path</span>(<span class="hljs-string">"{id : \\\\d+}"</span>)\n    <span class="hljs-meta">@Produces</span>({MediaType.APPLICATION_JSON})\n    <span class="hljs-function">User <span class="hljs-title">getUser</span><span class="hljs-params">(@PathParam(<span class="hljs-string">"id"</span>)</span> Long id)</span>;\n}\n</code></pre>\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">User</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Serializable</span> </span>{\n\n    <span class="hljs-keyword">private</span> Long id;\n\n    <span class="hljs-keyword">private</span> String name;\n\n    <span class="hljs-comment">// …</span>\n}\n</code></pre>\n<p>对于spring中的配置,因为这里的REST服务不是dubbo提供的,所以无法使用dubbo的注册中心,直接配置外部REST服务的url地址即可(如多个地址用逗号分隔):</p>\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">"userService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"xxx.UserService"</span> <span class="hljs-attr">url</span>=<span class="hljs-string">"rest://api.foo.com/services/"</span>/&gt;</span>\n</code></pre>\n<blockquote>\n<p>注意:这里协议必须用rest://而不是http://之类。如果外部的REST服务有context path,则在url中也必须添加上(除非你在每个服务接口的@Path annotation中都带上context path),例如上面的/services/。同时这里的services后面必须带上/,这样才能使dubbo正常工作。</p>\n</blockquote>\n<p>另外,这里依然可以配置客户端可启动的最大连接数和超时时间:</p>\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">"userService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"xxx.UserService"</span> <span class="hljs-attr">url</span>=<span class="hljs-string">"rest://api.foo.com/services/"</span> <span class="hljs-attr">timeout</span>=<span class="hljs-string">"2000"</span> <span class="hljs-attr">connections</span>=<span class="hljs-string">"10"</span>/&gt;</span>\n</code></pre>\n<h2>Dubbo中JAX-RS的限制</h2>\n<p>Dubbo中的REST开发是完全兼容标准JAX-RS的,但其支持的功能目前是完整JAX-RS的一个子集,部分因为它要受限于dubbo和spring的特定体系。</p>\n<p>在dubbo中使用的JAX-RS的局限包括但不限于:</p>\n<ol>\n<li>服务实现只能是singleton的,不能支持per-request scope和per-lookup scope</li>\n<li>不支持用@Context annotation对服务的实例字段注入 ServletConfig、ServletContext、HttpServletRequest、HttpServletResponse等等,但可以支持对服务方法参数的注入。但对某些特定REST server实现,(祥见前面的叙述),也不支持对服务方法参数的注入。</li>\n</ol>\n<h2>REST常见问题解答(REST FAQ)</h2>\n<h3>Dubbo REST的服务能和Dubbo注册中心、监控中心集成吗?</h3>\n<p>可以的,而且是自动集成的,也就是你在dubbo中开发的所有REST服务都会自动注册到服务册中心和监控中心,可以通过它们做管理。</p>\n<p>但是,只有当REST的消费端也是基于dubbo的时候,注册中心中的许多服务治理操作才能完全起作用。而如果消费端是非dubbo的,自然不受注册中心管理,所以其中很多操作是不会对消费端起作用的。</p>\n<h3>Dubbo REST中如何实现负载均衡和容错(failover)?</h3>\n<p>如果dubbo REST的消费端也是dubbo的,则Dubbo REST和其他dubbo远程调用协议基本完全一样,由dubbo框架透明的在消费端做load balance、failover等等。</p>\n<p>如果dubbo REST的消费端是非dubbo的,甚至是非java的,则最好配置服务提供端的软负载均衡机制,目前可考虑用LVS、HAProxy、 Nginx等等对HTTP请求做负载均衡。</p>\n<h3>JAX-RS中重载的方法能够映射到同一URL地址吗?</h3>\n<p><a href="http://stackoverflow.com/questions/17196766/can-resteasy-choose-method-based-on-query-params">http://stackoverflow.com/questions/17196766/can-resteasy-choose-method-based-on-query-params</a></p>\n<h3>JAX-RS中作POST的方法能够接收多个参数吗?</h3>\n<p><a href="http://stackoverflow.com/questions/5553218/jax-rs-post-multiple-objects">http://stackoverflow.com/questions/5553218/jax-rs-post-multiple-objects</a></p>\n<h2>Dubbo当前体系的不足之处(与REST相关的)</h2>\n<p>我认为dubbo当前体系中显然也有不少不足之处,这里列出几个与REST有关的、并影响用户使用的问题(不包括内部实现的问题),供参考评论,为下一步重构作准备。</p>\n<h3>RpcContext的侵入性</h3>\n<p>在前文,前面我们已经提到过RpcContext用法的侵入性,由于它是用单例的方式来访问上下文信息,这完全不符合spring应用的一般风格,不利于应用扩展和单元测试。未来我们可能用依赖注入方式注入一个接口,再用它去访问ThreadLocal中的上下文信息。</p>\n<h3>Protocol配置的局限性</h3>\n<p>dubbo支持多种远程调用方式,但所有调用方式都是用<code>&lt;dubbo:protocol/&gt;</code>来配置的,例如:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"9090"</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"netty"</span> <span class="hljs-attr">client</span>=<span class="hljs-string">"netty"</span> <span class="hljs-attr">codec</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">serialization</span>=<span class="hljs-string">"hessian2"</span> \n    <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span> <span class="hljs-attr">threadpool</span>=<span class="hljs-string">"fixed"</span> <span class="hljs-attr">threads</span>=<span class="hljs-string">"100"</span> <span class="hljs-attr">queues</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">iothreads</span>=<span class="hljs-string">"9"</span> <span class="hljs-attr">buffer</span>=<span class="hljs-string">"8192"</span> <span class="hljs-attr">accepts</span>=<span class="hljs-string">"1000"</span> <span class="hljs-attr">payload</span>=<span class="hljs-string">"8388608"</span>/&gt;</span>\n</code></pre>\n<p>其实,上面很多属性实际上dubbo RPC远程调用方式特有的,很多dubbo中的其它远程调用方式根本就不支持例如server, client, codec, iothreads, accepts, payload等等(当然,有的是条件所限不支持,有的是根本没有必要支持)。这给用户的使用徒增很多困惑,用户也并不知道有些属性(比如做性能调优)添加了实际上是不起作用的。</p>\n<p>另一方面,各种远程调用方式往往有大量自己独特的配置需要,特别是我们逐步为每种远程调用方式都添加更丰富、更高级的功能,这就不可避免的扩展<code>&lt;protocol/&gt;</code>中的属性(例如目前我们在REST中已经添加了keepalive和extension两个属性),到最后会导致<code>&lt;protocol/&gt;</code>臃肿不堪,用户的使用也更加困惑。</p>\n<p>当然,dubbo中有一种扩展<code>&lt;protocol/&gt;</code>的方式是用<code>&lt;dubbo:parameter/&gt;</code>,但这种方式显然很有局限性,而且用法复杂,缺乏schema校验。</p>\n<p>所以,最好的方式是为每种远程调用方式设置自己的protocol元素,比如<code>&lt;protocol-dubbo/&gt;</code>,<code>&lt;protocol-rest/&gt;</code>等等,每种元素用XML schema规定自己的属性(当然属性在各种远程调用方式之间能通用是最好的)。</p>\n<p>如此一来,例如前面提到过的extension配置也可以用更自由的方式,从而更清楚更可扩展(以下只是举例,当然也许有更好的方式):</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol-rest</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"8080"</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:extension</span>&gt;</span>someInterceptor<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:extension</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:extension</span>&gt;</span>someFilter<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:extension</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:extension</span>&gt;</span>someDynamicFeature<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:extension</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:extension</span>&gt;</span>someEntityProvider<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:extension</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:protocol-rest</span>&gt;</span>\n</code></pre>\n<h3>XML命名不符合spring规范</h3>\n<p>dubbo的XML配置中大量命名都不符合spring规范,比如:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"9090"</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"netty"</span> <span class="hljs-attr">client</span>=<span class="hljs-string">"netty"</span> <span class="hljs-attr">codec</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">serialization</span>=<span class="hljs-string">"hessian2"</span> \n    <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span> <span class="hljs-attr">threadpool</span>=<span class="hljs-string">"fixed"</span> <span class="hljs-attr">threads</span>=<span class="hljs-string">"100"</span> <span class="hljs-attr">queues</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">iothreads</span>=<span class="hljs-string">"9"</span> <span class="hljs-attr">buffer</span>=<span class="hljs-string">"8192"</span> <span class="hljs-attr">accepts</span>=<span class="hljs-string">"1000"</span> <span class="hljs-attr">payload</span>=<span class="hljs-string">"8388608"</span>/&gt;</span>\n</code></pre>\n<p>上面threadpool应该改为thread-pool,iothreads应该改为io-threads,单词之间应该用&quot;-&quot;分隔。这虽然看起来是个小问题,但也涉及到了可读性,特别是可扩展性,因为有时候我们不可避免要用更多单词来描述XML元素和属性。</p>\n<p>其实dubbo本身也是建议遵守spring到XML的命名规范。</p>\n<h2>REST最佳实践</h2>\n<p>TODO</p>\n<h2>性能基准测试</h2>\n<h3>测试环境</h3>\n<p>粗略如下:</p>\n<ul>\n<li>两台独立服务器</li>\n<li>4核Intel(R) Xeon(R) CPU E5-2603 0 @ 1.80GHz</li>\n<li>8G内存</li>\n<li>服务器之间网络通过百兆交换机</li>\n<li>CentOS 5</li>\n<li>JDK 7</li>\n<li>Tomcat 7</li>\n<li>JVM参数-server -Xms1g -Xmx1g -XX:PermSize=64M -XX:+UseConcMarkSweepGC</li>\n</ul>\n<h3>测试脚本</h3>\n<p>和dubbo自身的基准测试保持接近:</p>\n<p>10个并发客户端持续不断发出请求:</p>\n<ul>\n<li>传入嵌套复杂对象(但单个数据量很小),不做任何处理,原样返回</li>\n<li>传入50K字符串,不做任何处理,原样返回(TODO:结果尚未列出)</li>\n</ul>\n<p>进行5分钟性能测试。(引用dubbo自身测试的考虑:“主要考察序列化和网络IO的性能,因此服务端无任何业务逻辑。取10并发是考虑到http协议在高并发下对CPU的使用率较高可能会先打到瓶颈。”)</p>\n<h3>测试结果</h3>\n<p>下面的结果主要对比的是REST和dubbo RPC两种远程调用方式,并对它们作不同的配置,例如:</p>\n<ul>\n<li>“REST: Jetty + XML + GZIP”的意思是:测试REST,并采用jetty server,XML数据格式,启用GZIP压缩。</li>\n<li>“Dubbo: hessian2”的意思是:测试dubbo RPC,并采用hessian2序列化方式。</li>\n</ul>\n<p>针对复杂对象的结果如下(响应时间越小越好,TPS越大越好):</p>\n<table>\n<thead>\n<tr>\n<th>远程调用方式</th>\n<th>平均响应时间</th>\n<th>平均TPS(每秒事务数)</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>REST: Jetty + JSON</td>\n<td>7.806</td>\n<td>1280</td>\n</tr>\n<tr>\n<td>REST: Jetty + JSON + GZIP</td>\n<td>TODO</td>\n<td>TODO</td>\n</tr>\n<tr>\n<td>REST: Jetty + XML</td>\n<td>TODO</td>\n<td>TODO</td>\n</tr>\n<tr>\n<td>REST: Jetty + XML + GZIP</td>\n<td>TODO</td>\n<td>TODO</td>\n</tr>\n<tr>\n<td>REST: Tomcat + JSON</td>\n<td>2.082</td>\n<td>4796</td>\n</tr>\n<tr>\n<td>REST: Netty + JSON</td>\n<td>2.182</td>\n<td>4576</td>\n</tr>\n<tr>\n<td>Dubbo: FST</td>\n<td>1.211</td>\n<td>8244</td>\n</tr>\n<tr>\n<td>Dubbo: kyro</td>\n<td>1.182</td>\n<td>8444</td>\n</tr>\n<tr>\n<td>Dubbo: dubbo serialization</td>\n<td>1.43</td>\n<td>6982</td>\n</tr>\n<tr>\n<td>Dubbo: hessian2</td>\n<td>1.49</td>\n<td>6701</td>\n</tr>\n<tr>\n<td>Dubbo: fastjson</td>\n<td>1.572</td>\n<td>6352</td>\n</tr>\n</tbody>\n</table>\n<p><img src="images/rt.png" alt="no image found"></p>\n<p><img src="images/tps.png" alt="no image found"></p>\n<p>仅就目前的结果,一点简单总结:</p>\n<ul>\n<li>dubbo RPC(特别是基于高效java序列化方式如kryo,fst)比REST的响应时间和吞吐量都有较显著优势,内网的dubbo系统之间优先选择dubbo RPC。</li>\n<li>在REST的实现选择上,仅就性能而言,目前tomcat7和netty最优(当然目前使用的jetty和netty版本都较低)。tjws和sun http server在性能测试中表现极差,平均响应时间超过200ms,平均tps只有50左右(为了避免影响图片效果,没在上面列出)。</li>\n<li>在REST中JSON数据格式性能优于XML(数据暂未在以上列出)。</li>\n<li>在REST中启用GZIP对企业内网中的小数据量复杂对象帮助不大,性能反而有下降(数据暂未在以上列出)。</li>\n</ul>\n<h2>性能优化建议</h2>\n<p>如果将dubbo REST部署到外部Tomcat上,并配置server=&quot;servlet&quot;,即启用外部的tomcat来做为rest server的底层实现,则最好在tomcat上添加如下配置:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">Connector</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"8080"</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"org.apache.coyote.http11.Http11NioProtocol"</span>\n               <span class="hljs-attr">connectionTimeout</span>=<span class="hljs-string">"20000"</span>\n               <span class="hljs-attr">redirectPort</span>=<span class="hljs-string">"8443"</span>\n               <span class="hljs-attr">minSpareThreads</span>=<span class="hljs-string">"20"</span>\n               <span class="hljs-attr">enableLookups</span>=<span class="hljs-string">"false"</span>\n               <span class="hljs-attr">maxThreads</span>=<span class="hljs-string">"100"</span>\n               <span class="hljs-attr">maxKeepAliveRequests</span>=<span class="hljs-string">"-1"</span>\n               <span class="hljs-attr">keepAliveTimeout</span>=<span class="hljs-string">"60000"</span>/&gt;</span>\n</code></pre>\n<p>特别是maxKeepAliveRequests=&quot;-1&quot;,这个配置主要是保证tomcat一直启用http长连接,以提高REST调用性能。但是请注意,如果REST消费端不是持续的调用REST服务,则一直启用长连接未必是最好的做法。另外,一直启用长连接的方式一般不适合针对普通webapp,更适合这种类似rpc的场景。所以为了高性能,在tomcat中,dubbo REST应用和普通web应用最好不要混合部署,而应该用单独的实例。</p>\n<p>TODO more contents to add</p>\n<h2>扩展讨论</h2>\n<h3>REST与Thrift、Protobuf等的对比</h3>\n<p>TODO</p>\n<h3>REST与传统WebServices的对比</h3>\n<p>TODO</p>\n<h3>JAX-RS与Spring MVC的对比</h3>\n<p>初步看法,摘自http://www.infoq.com/cn/news/2014/10/dubbox-open-source?utm_source=infoq&amp;utm_medium=popular_links_homepage#theCommentsSection</p>\n<blockquote>\n<p>谢谢,对于jax-rs和spring mvc,其实我对spring mvc的rest支持还没有太深入的看过,说点初步想法,请大家指正:</p>\n</blockquote>\n<blockquote>\n<p>spring mvc也支持annotation的配置,其实和jax-rs看起来是非常非常类似的。</p>\n</blockquote>\n<blockquote>\n<p>我个人认为spring mvc相对更适合于面向web应用的restful服务,比如被AJAX调用,也可能输出HTML之类的,应用中还有页面跳转流程之类,spring mvc既可以做好正常的web页面请求也可以同时处理rest请求。但总的来说这个restful服务是在展现层或者叫web层之类实现的</p>\n</blockquote>\n<blockquote>\n<p>而jax-rs相对更适合纯粹的服务化应用,也就是传统Java EE中所说的中间层服务,比如它可以把传统的EJB发布成restful服务。在spring应用中,也就把spring中充当service之类的bean直接发布成restful服务。总的来说这个restful服务是在业务、应用层或者facade层。而MVC层次和概念在这种做比如(后台)服务化的应用中通常是没有多大价值的。</p>\n</blockquote>\n<blockquote>\n<p>当然jax-rs的有些实现比如jersey,也试图提供mvc支持,以更好的适应上面所说的web应用,但应该是不如spring mvc。</p>\n</blockquote>\n<blockquote>\n<p>在dubbo应用中,我想很多人都比较喜欢直接将一个本地的spring service bean(或者叫manager之类的)完全透明的发布成远程服务,则这里用JAX-RS是更自然更直接的,不必额外的引入MVC概念。当然,先不讨论透明发布远程服务是不是最佳实践,要不要添加facade之类。</p>\n</blockquote>\n<blockquote>\n<p>当然,我知道在dubbo不支持rest的情况下,很多朋友采用的架构是spring mvc restful调用dubbo (spring) service来发布restful服务的。这种方式我觉得也非常好,只是如果不修改spring mvc并将其与dubbo深度集成,restful服务不能像dubbo中的其他远程调用协议比如webservices、dubbo rpc、hessian等等那样,享受诸多高级的服务治理的功能,比如:注册到dubbo的服务注册中心,通过dubbo监控中心监控其调用次数、TPS、响应时间之类,通过dubbo的统一的配置方式控制其比如线程池大小、最大连接数等等,通过dubbo统一方式做服务流量控制、权限控制、频次控制。另外spring mvc仅仅负责服务端,而在消费端,通常是用spring restTemplate,如果restTemplate不和dubbo集成,有可能像dubbo服务客户端那样自动或者人工干预做服务降级。如果服务端消费端都是dubbo系统,通过spring的rest交互,如果spring rest不深度整合dubbo,则不能用dubbo统一的路由分流等功能。</p>\n</blockquote>\n<blockquote>\n<p>当然,其实我个人认为这些东西不必要非此即彼的。我听说spring创始人rod johnson总是爱说一句话,the customer is always right,其实与其非要探讨哪种方式更好,不如同时支持两种方式就是了,所以原来在文档中也写过计划支持spring rest annoation,只是不知道具体可行性有多高。</p>\n</blockquote>\n<h2>未来</h2>\n<p>稍后可能要实现的功能:</p>\n<ul>\n<li>spring mvc的rest annotation支持</li>\n<li>安全</li>\n<li>OAuth</li>\n<li>异步调用</li>\n<li>完善gzip</li>\n<li>最大payload限制</li>\n</ul>\n'},{filename:"user/serialization.md",__html:'<h1>在Dubbo中使用高效的Java序列化(Kryo和FST)</h1>\n<p><strong>作者:沈理</strong></p>\n<p><strong>文档版权:<a href="http://www.apache.org/licenses/LICENSE-2.0">Apache 2.0许可证 署名-禁止演绎</a></strong></p>\n<p>完善中……</p>\n<p>TODO 生成可点击的目录</p>\n<h2>目录</h2>\n<ul>\n<li>序列化漫谈</li>\n<li>启用Kryo和FST</li>\n<li>注册被序列化类</li>\n<li>无参构造函数和Serializable接口</li>\n<li>序列化性能分析与测试\n<ul>\n<li>测试环境</li>\n<li>测试脚本</li>\n<li>Dubbo RPC中不同序列化生成字节大小比较</li>\n<li>Dubbo RPC中不同序列化响应时间和吞吐量对比</li>\n</ul>\n</li>\n<li>未来</li>\n</ul>\n<h2>序列化漫谈</h2>\n<p>dubbo RPC是dubbo体系中最核心的一种高性能、高吞吐量的远程调用方式,我喜欢称之为多路复用的TCP长连接调用,简单的说:</p>\n<ul>\n<li>长连接:避免了每次调用新建TCP连接,提高了调用的响应速度</li>\n<li>多路复用:单个TCP连接可交替传输多个请求和响应的消息,降低了连接的等待闲置时间,从而减少了同样并发数下的网络连接数,提高了系统吞吐量。</li>\n</ul>\n<p>dubbo RPC主要用于两个dubbo系统之间作远程调用,特别适合高并发、小数据的互联网场景。</p>\n<p>而序列化对于远程调用的响应速度、吞吐量、网络带宽消耗等同样也起着至关重要的作用,是我们提升分布式系统性能的最关键因素之一。</p>\n<p>在dubbo RPC中,同时支持多种序列化方式,例如:</p>\n<ol>\n<li>dubbo序列化:阿里尚未开发成熟的高效java序列化实现,阿里不建议在生产环境使用它</li>\n<li>hessian2序列化:hessian是一种跨语言的高效二进制序列化方式。但这里实际不是原生的hessian2序列化,而是阿里修改过的hessian lite,它是dubbo RPC默认启用的序列化方式</li>\n<li>json序列化:目前有两种实现,一种是采用的阿里的fastjson库,另一种是采用dubbo中自己实现的简单json库,但其实现都不是特别成熟,而且json这种文本序列化性能一般不如上面两种二进制序列化。</li>\n<li>java序列化:主要是采用JDK自带的Java序列化实现,性能很不理想。</li>\n</ol>\n<p>在通常情况下,这四种主要序列化方式的性能从上到下依次递减。对于dubbo RPC这种追求高性能的远程调用方式来说,实际上只有1、2两种高效序列化方式比较般配,而第1个dubbo序列化由于还不成熟,所以实际只剩下2可用,所以dubbo RPC默认采用hessian2序列化。</p>\n<p>但hessian是一个比较老的序列化实现了,而且它是跨语言的,所以不是单独针对java进行优化的。而dubbo RPC实际上完全是一种Java to Java的远程调用,其实没有必要采用跨语言的序列化方式(当然肯定也不排斥跨语言的序列化)。</p>\n<p>最近几年,各种新的高效序列化方式层出不穷,不断刷新序列化性能的上限,最典型的包括:</p>\n<ul>\n<li>专门针对Java语言的:Kryo,FST等等</li>\n<li>跨语言的:Protostuff,ProtoBuf,Thrift,Avro,MsgPack等等</li>\n</ul>\n<p>这些序列化方式的性能多数都显著优于hessian2(甚至包括尚未成熟的dubbo序列化)。</p>\n<p>有鉴于此,我们为dubbo引入Kryo和FST这两种高效Java序列化实现,来逐步取代hessian2。</p>\n<p>其中,Kryo是一种非常成熟的序列化实现,已经在Twitter、Groupon、Yahoo以及多个著名开源项目(如Hive、Storm)中广泛的使用。而FST是一种较新的序列化实现,目前还缺乏足够多的成熟使用案例,但我认为它还是非常有前途的。</p>\n<p>在面向生产环境的应用中,我建议目前更优先选择Kryo。</p>\n<h2>启用Kryo和FST</h2>\n<p>使用Kryo和FST非常简单,只需要在dubbo RPC的XML配置中添加一个属性即可:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">serialization</span>=<span class="hljs-string">"kryo"</span>/&gt;</span>\n</code></pre>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">serialization</span>=<span class="hljs-string">"fst"</span>/&gt;</span>\n</code></pre>\n<h2>注册被序列化类</h2>\n<p>要让Kryo和FST完全发挥出高性能,最好将那些需要被序列化的类注册到dubbo系统中,例如,我们可以实现如下回调接口:</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">SerializationOptimizerImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">SerializationOptimizer</span> </span>{\n\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Collection&lt;Class&gt; <span class="hljs-title">getSerializableClasses</span><span class="hljs-params">()</span> </span>{\n        List&lt;Class&gt; classes = <span class="hljs-keyword">new</span> LinkedList&lt;Class&gt;();\n        classes.add(BidRequest.class);\n        classes.add(BidResponse.class);\n        classes.add(Device.class);\n        classes.add(Geo.class);\n        classes.add(Impression.class);\n        classes.add(SeatBid.class);\n        <span class="hljs-keyword">return</span> classes;\n    }\n}\n</code></pre>\n<p>然后在XML配置中添加:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">serialization</span>=<span class="hljs-string">"kryo"</span> <span class="hljs-attr">optimizer</span>=<span class="hljs-string">"com.alibaba.dubbo.demo.SerializationOptimizerImpl"</span>/&gt;</span>\n</code></pre>\n<p>在注册这些类后,序列化的性能可能被大大提升,特别针对小数量的嵌套对象的时候。</p>\n<p>当然,在对一个类做序列化的时候,可能还级联引用到很多类,比如Java集合类。针对这种情况,我们已经自动将JDK中的常用类进行了注册,所以你不需要重复注册它们(当然你重复注册了也没有任何影响),包括:</p>\n<pre><code>GregorianCalendar\nInvocationHandler\nBigDecimal\nBigInteger\nPattern\nBitSet\nURI\nUUID\nHashMap\nArrayList\nLinkedList\nHashSet\nTreeSet\nHashtable\nDate\nCalendar\nConcurrentHashMap\nSimpleDateFormat\nVector\nBitSet\nStringBuffer\nStringBuilder\nObject\nObject[]\nString[]\nbyte[]\nchar[]\nint[]\nfloat[]\ndouble[]\n</code></pre>\n<p>由于注册被序列化的类仅仅是出于性能优化的目的,所以即使你忘记注册某些类也没有关系。事实上,即使不注册任何类,Kryo和FST的性能依然普遍优于hessian和dubbo序列化。</p>\n<blockquote>\n<p>当然,有人可能会问为什么不用配置文件来注册这些类?这是因为要注册的类往往数量较多,导致配置文件冗长;而且在没有好的IDE支持的情况下,配置文件的编写和重构都比java类麻烦得多;最后,这些注册的类一般是不需要在项目编译打包后还需要做动态修改的。</p>\n</blockquote>\n<blockquote>\n<p>另外,有人也会觉得手工注册被序列化的类是一种相对繁琐的工作,是不是可以用annotation来标注,然后系统来自动发现并注册。但这里annotation的局限是,它只能用来标注你可以修改的类,而很多序列化中引用的类很可能是你没法做修改的(比如第三方库或者JDK系统类或者其他项目的类)。另外,添加annotation毕竟稍微的“污染”了一下代码,使应用代码对框架增加了一点点的依赖性。</p>\n</blockquote>\n<blockquote>\n<p>除了annotation,我们还可以考虑用其它方式来自动注册被序列化的类,例如扫描类路径,自动发现实现Serializable接口(甚至包括Externalizable)的类并将它们注册。当然,我们知道类路径上能找到Serializable类可能是非常多的,所以也可以考虑用package前缀之类来一定程度限定扫描范围。</p>\n</blockquote>\n<blockquote>\n<p>当然,在自动注册机制中,特别需要考虑如何保证服务提供端和消费端都以同样的顺序(或者ID)来注册类,避免错位,毕竟两端可被发现然后注册的类的数量可能都是不一样的。</p>\n</blockquote>\n<h2>无参构造函数和Serializable接口</h2>\n<p>如果被序列化的类中不包含无参的构造函数,则在Kryo的序列化中,性能将会大打折扣,因为此时我们在底层将用Java的序列化来透明的取代Kryo序列化。所以,尽可能为每一个被序列化的类添加无参构造函数是一种最佳实践(当然一个java类如果不自定义构造函数,默认就有无参构造函数)。</p>\n<p>另外,Kryo和FST本来都不需要被序列化都类实现Serializable接口,但我们还是建议每个被序列化类都去实现它,因为这样可以保持和Java序列化以及dubbo序列化的兼容性,另外也使我们未来采用上述某些自动注册机制带来可能。</p>\n<h2>序列化性能分析与测试</h2>\n<p>本文我们主要讨论的是序列化,但在做性能分析和测试的时候我们并不单独处理每种序列化方式,而是把它们放到dubbo RPC中加以对比,因为这样更有现实意义。</p>\n<h3>测试环境</h3>\n<p>粗略如下:</p>\n<ul>\n<li>两台独立服务器</li>\n<li>4核Intel(R) Xeon(R) CPU E5-2603 0 @ 1.80GHz</li>\n<li>8G内存</li>\n<li>虚拟机之间网络通过百兆交换机</li>\n<li>CentOS 5</li>\n<li>JDK 7</li>\n<li>Tomcat 7</li>\n<li>JVM参数-server -Xms1g -Xmx1g -XX:PermSize=64M -XX:+UseConcMarkSweepGC</li>\n</ul>\n<p>当然这个测试环境较有局限,故当前测试结果未必有非常权威的代表性。</p>\n<h3>测试脚本</h3>\n<p>和dubbo自身的基准测试保持接近:</p>\n<p>10个并发客户端持续不断发出请求:</p>\n<ul>\n<li>传入嵌套复杂对象(但单个数据量很小),不做任何处理,原样返回</li>\n<li>传入50K字符串,不做任何处理,原样返回(TODO:结果尚未列出)</li>\n</ul>\n<p>进行5分钟性能测试。(引用dubbo自身测试的考虑:“主要考察序列化和网络IO的性能,因此服务端无任何业务逻辑。取10并发是考虑到http协议在高并发下对CPU的使用率较高可能会先打到瓶颈。”)</p>\n<h3>Dubbo RPC中不同序列化生成字节大小比较</h3>\n<p>序列化生成字节码的大小是一个比较有确定性的指标,它决定了远程调用的网络传输时间和带宽占用。</p>\n<p>针对复杂对象的结果如下(数值越小越好):</p>\n<table>\n<thead>\n<tr>\n<th>序列化实现</th>\n<th>请求字节数</th>\n<th>响应字节数</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Kryo</td>\n<td>272</td>\n<td>90</td>\n</tr>\n<tr>\n<td>FST</td>\n<td>288</td>\n<td>96</td>\n</tr>\n<tr>\n<td>Dubbo Serialization</td>\n<td>430</td>\n<td>186</td>\n</tr>\n<tr>\n<td>Hessian</td>\n<td>546</td>\n<td>329</td>\n</tr>\n<tr>\n<td>FastJson</td>\n<td>461</td>\n<td>218</td>\n</tr>\n<tr>\n<td>Json</td>\n<td>657</td>\n<td>409</td>\n</tr>\n<tr>\n<td>Java Serialization</td>\n<td>963</td>\n<td>630</td>\n</tr>\n</tbody>\n</table>\n<p><img src="images/bytes.png" alt="no image found"></p>\n<h3>Dubbo RPC中不同序列化响应时间和吞吐量对比</h3>\n<table>\n<thead>\n<tr>\n<th>远程调用方式</th>\n<th>平均响应时间</th>\n<th>平均TPS(每秒事务数)</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>REST: Jetty + JSON</td>\n<td>7.806</td>\n<td>1280</td>\n</tr>\n<tr>\n<td>REST: Jetty + JSON + GZIP</td>\n<td>TODO</td>\n<td>TODO</td>\n</tr>\n<tr>\n<td>REST: Jetty + XML</td>\n<td>TODO</td>\n<td>TODO</td>\n</tr>\n<tr>\n<td>REST: Jetty + XML + GZIP</td>\n<td>TODO</td>\n<td>TODO</td>\n</tr>\n<tr>\n<td>REST: Tomcat + JSON</td>\n<td>2.082</td>\n<td>4796</td>\n</tr>\n<tr>\n<td>REST: Netty + JSON</td>\n<td>2.182</td>\n<td>4576</td>\n</tr>\n<tr>\n<td>Dubbo: FST</td>\n<td>1.211</td>\n<td>8244</td>\n</tr>\n<tr>\n<td>Dubbo: kyro</td>\n<td>1.182</td>\n<td>8444</td>\n</tr>\n<tr>\n<td>Dubbo: dubbo serialization</td>\n<td>1.43</td>\n<td>6982</td>\n</tr>\n<tr>\n<td>Dubbo: hessian2</td>\n<td>1.49</td>\n<td>6701</td>\n</tr>\n<tr>\n<td>Dubbo: fastjson</td>\n<td>1.572</td>\n<td>6352</td>\n</tr>\n</tbody>\n</table>\n<p><img src="images/rt.png" alt="no image found"></p>\n<p><img src="images/tps.png" alt="no image found"></p>\n<h3>测试总结</h3>\n<p>就目前结果而言,我们可以看到不管从生成字节的大小,还是平均响应时间和平均TPS,Kryo和FST相比Dubbo RPC中原有的序列化方式都有非常显著的改进。</p>\n<h2>未来</h2>\n<p>未来,当Kryo或者FST在dubbo中当应用足够成熟之后,我们很可能会将dubbo RPC的默认序列化从hessian2改为它们中间的某一个。</p>\n'},{filename:"user/simple-monitor.md",__html:'<blockquote>\n<p><img src="sources/images/check.gif" alt="warning">监控中心也是一个标准的Dubbo服务,可以通过注册中心发现,也可以直连。</p>\n</blockquote>\n<blockquote>\n<p><img src="sources/images/check.gif" alt="warning"><a href="admin-guide-install-manual#%E7%AE%80%E6%98%93%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83%E5%AE%89%E8%A3%85">简易注册中心安装</a></p>\n</blockquote>\n<ol start="0">\n<li>\n<p>暴露一个简单监控中心服务到注册中心: (如果是用安装包,不需要自己写这个配置,如果是自己实现监控中心,则需要)</p>\n<pre><code class="language-xml"></code></pre>\n</li>\n</ol>\n<p><beans xmlns="http://www.springframework.org/schema/beans"\n    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n    xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"\n    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"></p>\n<pre><code>&lt;!-- 当前应用信息配置 --&gt;\n&lt;dubbo:application name=&quot;simple-monitor&quot; /&gt;\n \n&lt;!-- 连接注册中心配置 --&gt;\n&lt;dubbo:registry address=&quot;127.0.0.1:9090&quot; /&gt;\n \n&lt;!-- 暴露服务协议配置 --&gt;\n&lt;dubbo:protocol port=&quot;7070&quot; /&gt;\n \n&lt;!-- 暴露服务配置 --&gt;\n&lt;dubbo:service interface=&quot;com.alibaba.dubbo.monitor.MonitorService&quot; ref=&quot;monitorService&quot; /&gt;\n \n&lt;bean id=&quot;monitorService&quot; class=&quot;com.alibaba.dubbo.monitor.simple.SimpleMonitorService&quot; /&gt;\n</code></pre>\n</beans>\n```\n<ol>\n<li>\n<p>通过注册中心发现监控中心服务:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:monitor</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"registry"</span> /&gt;</span>\n</code></pre>\n<p>或</p>\n<blockquote>\n<p>dubbo.properties</p>\n</blockquote>\n<pre><code class="language-xml">dubbo.monitor.protocol=registry\n</code></pre>\n</li>\n<li>\n<p>暴露一个简单监控中心服务,但不注册到注册中心: (如果是用安装包,不需要自己写这个配置,如果是自己实现监控中心,则需要)</p>\n<pre><code class="language-xml"></code></pre>\n</li>\n</ol>\n<p><beans xmlns="http://www.springframework.org/schema/beans"\n    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n    xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"\n    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"></p>\n<pre><code>&lt;!-- 当前应用信息配置 --&gt;\n&lt;dubbo:application name=&quot;simple-monitor&quot; /&gt;\n \n&lt;!-- 暴露服务协议配置 --&gt;\n&lt;dubbo:protocol port=&quot;7070&quot; /&gt;\n \n&lt;!-- 暴露服务配置 --&gt;\n&lt;dubbo:service interface=&quot;com.alibaba.dubbo.monitor.MonitorService&quot; ref=&quot;monitorService&quot; registry=&quot;N/A&quot; /&gt;\n \n&lt;bean id=&quot;monitorService&quot; class=&quot;com.alibaba.dubbo.monitor.simple.SimpleMonitorService&quot; /&gt;   \n</code></pre>\n</beans>\n    ```\n<ol start="3">\n<li>\n<p>直连监控中心服务</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:monitor</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"dubbo://127.0.0.1:7070/com.alibaba.dubbo.monitor.MonitorService"</span> /&gt;</span>\n</code></pre>\n<p>或:</p>\n<pre><code class="language-sh">&lt;dubbo:monitor address=<span class="hljs-string">"127.0.0.1:7070"</span> /&gt;\n</code></pre>\n<p>或:</p>\n<p><strong>dubbo.properties</strong></p>\n<pre><code class="language-sh">dubbo.monitor.address=127.0.0.1:7070\n</code></pre>\n</li>\n</ol>\n'}],"en-us":[{filename:"admin/README.md",__html:"<h1>dubbo-admin-book</h1>\n<p>The installation and maintenance guidance of registry center and dubbo-admin.</p>\n"},{filename:"admin/SUMMARY.md",__html:'<ul>\n<li><a href="install/introduction.md">1 install manual</a>\n<ul>\n<li><a href="install/provider-demo.md">1.1 install provider demo</a></li>\n<li><a href="install/consumer-demo.md">1.2 install consumer demo</a></li>\n<li><a href="install/zookeeper.md">1.3 install Zookeeper configuration center</a></li>\n<li><a href="install/redis.md">1.4 install Redis configuration center</a></li>\n<li><a href="install/simple-registry-center.md">1.5 install Simple configuration center</a></li>\n<li><a href="install/simple-monitor-center.md">1.6 install Simple monitor center</a></li>\n<li><a href="install/admin-console.md">1.7 install admin console</a></li>\n</ul>\n</li>\n<li><a href="">2 Ops manual</a>\n<ul>\n<li><a href="ops/dubbo-ops.md">2.1 admin-console Ops</a></li>\n</ul>\n</li>\n</ul>\n'},{filename:"admin/install/admin-console.md",__html:'<h1>Install admin console</h1>\n<p>Include: route rule, dynamic configuration, service downgrade, access control, weight adjustment, load balance, etc.</p>\n<p>Install:</p>\n<pre><code class="language-sh">wget http://apache.etoak.com/tomcat/tomcat-6/v6.0.35/bin/apache-tomcat-6.0.35.tar.gz\ntar zxvf apache-tomcat-6.0.35.tar.gz\n<span class="hljs-built_in">cd</span> apache-tomcat-6.0.35\nrm -rf webapps/ROOT\n\ngit <span class="hljs-built_in">clone</span> https://github.com/dubbo/dubbo-ops.git /var/tmp/dubbo-ops\n<span class="hljs-built_in">pushd</span> /var/tmp/dubbo-ops\nmvn clean package\n<span class="hljs-built_in">popd</span>\n\nunzip /var/tmp/dubbo-ops/dubbo-admin/target/dubbo-admin-2.0.0.war -d webapps/ROOT\n</code></pre>\n<p>Configuration <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>:</p>\n<pre><code class="language-sh">vi webapps/ROOT/WEB-INF/dubbo.properties\ndubbo.properties\ndubbo.registry.address=zookeeper://127.0.0.1:2181\ndubbo.admin.root.password=root\ndubbo.admin.guest.password=guest\n</code></pre>\n<p>Start:</p>\n<pre><code class="language-sh">./bin/startup.sh\n</code></pre>\n<p>Stop:</p>\n<pre><code class="language-sh">./bin/shutdown.sh\n</code></pre>\n<p>Visit <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>:</p>\n<pre><code>http://127.0.0.1:8080/\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>Or put <code>dubbo.properties</code> in current user directory <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>User: root, password: root or user: guest, password: guest <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"admin/install/consumer-demo.md",__html:'<h1>install demo consumer</h1>\n<p>install:</p>\n<pre><code class="language-sh">git <span class="hljs-built_in">clone</span> https://github.com/apache/incubator-dubbo.git\n<span class="hljs-built_in">cd</span> incubator-dubbo\nPlease start Provider first\nadd -Djava.net.preferIPv4Stack=<span class="hljs-literal">true</span> <span class="hljs-keyword">if</span> your IDE is Intellij Idea\n</code></pre>\n<p>configuration:</p>\n<pre><code class="language-sh">resource/META-INFO.spring/dubbo-demo-consumer.xml\nchange dubbo:registery to the real registery center address\n</code></pre>\n'},{filename:"admin/install/introduction.md",__html:"<h1>Install manual</h1>\n<p>You can run Demo Provider and Demo Consumer only, the default discovery strategy is Multicast by configuration center broadcast, do not run the two parts on the same machine, if you have to do so, set <code>unicast=false</code>, like <code>multicast://224.5.6.7:1234?unicast=false</code>, or the unicast send to consumer will be taken by provider, and the same for consumers. Only multicast has this issue</p>\n<p>You can run multiple Demo Provider and Demo consumer to verify load balance. Demo Consumer can run multi instance directly. Because of port conflict, you can either run multi Demo Providers on different machines or modify the value of <code>dubbo.protocol.port</code> in <code>conf/dubbo.properties</code> under the install directory of <code>conf/dubbo.properties</code></p>\n<p>You can add Simple Monitor as a monitor center, the default discovery strategy is Multicast by configuration center broadcast, display the dependency relationship, call times and cost</p>\n<p>You can use Zookeeper instead of Multicast as the configuration center, after Zookeeper Registry installation, modify <code>conf/dubbo.properties</code> under the installation directory of Demo Provider, Demo Consumer and Simple Monitor, change the value of <code>dubbo.registry.address</code> to <code>zookeeper://127.0.0.1:2181</code>(<code>redis://127.0.0.1:6379</code> for Redis Registry). the value for Simple Registry is <code>dubbo://127.0.0.1:9090</code></p>\n<p>Zookeeper configuration address is recommended</p>\n"},{filename:"admin/install/provider-demo.md",__html:'<h1>install demo provider</h1>\n<p>install:</p>\n<pre><code class="language-sh">git <span class="hljs-built_in">clone</span> https://github.com/apache/incubator-dubbo.git\n<span class="hljs-built_in">cd</span> incubator-dubbo\nrun com.alibaba.dubbo.demo.provider.Provider under dubbo-demo-provider module\nadd -Djava.net.preferIPv4Stack=<span class="hljs-literal">true</span> <span class="hljs-keyword">if</span> your IDE is Intellij Idea\n</code></pre>\n<p>configuration:</p>\n<pre><code class="language-sh">resource/META-INFO.spring/dubbo-demo-provider.xml\nchange dubbo:registery to a real registery server address, zookeeper is recommanded\n</code></pre>\n'},{filename:"admin/install/redis.md",__html:'<h1>install Redis register center</h1>\n<p>Redis <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> introductions, please refer to: <a href="http://dubbo.apache.org/books/dubbo-user-book-en/references/registry/redis.html">Redis application center manual</a>。</p>\n<p>you need an origin Redis server only, and change the value from <code>dubbo.registry.addrss</code> to <code>redis://127.0.0.1:6379</code> in <code>conf/dubbo.properties</code> of <a href="http://dubbo.apache.org/books/dubbo-user-book-en/quick-start.html">quick start</a></p>\n<p>Redis configuration center cluster <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup> write multiple server in client side and read from a single server.</p>\n<p>Install:</p>\n<pre><code class="language-sh">wget http://redis.googlecode.com/files/redis-2.4.8.tar.gz\ntar xzf redis-2.4.8.tar.gz\n<span class="hljs-built_in">cd</span> redis-2.4.8\nmake\n</code></pre>\n<p>Configuration:</p>\n<pre><code class="language-sh">vi redis.conf\n</code></pre>\n<p>Start:</p>\n<pre><code class="language-sh">nohup ./src/redis-server redis.conf &amp;\n</code></pre>\n<p>Stop:</p>\n<pre><code class="language-sh">killall redis-server\n</code></pre>\n<ul>\n<li>Command line <sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup>:</li>\n</ul>\n<pre><code class="language-sh">./src/redis-cli\nhgetall /dubbo/com.foo.BarService/providers\n</code></pre>\n<p>Or:</p>\n<pre><code class="language-sh">telnet 127.0.0.1 6379\nhgetall /dubbo/com.foo.BarService/providers\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>Redis is a high performance KV store server, please refer to: <a href="http://redis.io/topics/quickstart">http://redis.io/topics/quickstart</a> <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>Support for version <code>2.1.0</code> and higher <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn3" class="footnote-item"><p>Please refer to: <a href="http://redis.io/commands">http://redis.io/commands</a> <a href="#fnref3" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"admin/install/simple-monitor-center.md",__html:'<h1>install Simple monitor center</h1>\n<h2>Step</h2>\n<p>install:</p>\n<pre><code class="language-sh">git <span class="hljs-built_in">clone</span> https://github.com/apache/incubator-dubbo-ops\n<span class="hljs-built_in">cd</span> incubator-dubbo-ops &amp;&amp; mvn package\n<span class="hljs-built_in">cd</span> dubbo-monitor-simple/target &amp;&amp; tar xvf dubbo-monitor-simple-2.0.0-assembly.tar.gz\n<span class="hljs-built_in">cd</span> dubbo-monitor-simple-2.0.0\n</code></pre>\n<p>configuration:</p>\n<pre><code class="language-sh">vi conf/dubbo.properties\n</code></pre>\n<p>start:</p>\n<pre><code class="language-sh">./assembly.bin/start.sh\n</code></pre>\n<p>stop:</p>\n<pre><code class="language-sh">./assembly.bin/stop.sh\n</code></pre>\n<p>restart:</p>\n<pre><code class="language-sh">./assembly.bin/restart.sh\n</code></pre>\n<p>debug:</p>\n<pre><code class="language-sh">./assembly.bin/start.sh debug\n</code></pre>\n<p>system status:</p>\n<pre><code class="language-sh">./assembly.bin/dump.sh\n</code></pre>\n<p>General control entrance:</p>\n<pre><code class="language-sh">./assembly.bin/server.sh start\n./assembly.bin/server.sh stop\n./assembly.bin/server.sh restart\n./assembly.bin/server.sh debug\n./assembly.bin/server.sh dump\n</code></pre>\n<p>Stdout:</p>\n<pre><code class="language-sh">tail -f logs/stdout.log\n</code></pre>\n<p>Command line <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>:</p>\n<pre><code class="language-sh">telnet 127.0.0.1 7070\n<span class="hljs-built_in">help</span>\n</code></pre>\n<p>Or:</p>\n<pre><code class="language-sh"><span class="hljs-built_in">echo</span> status | nc -i 1 127.0.0.1 7070\n</code></pre>\n<p>Visit:</p>\n<pre><code>http://127.0.0.1:8080\n</code></pre>\n<p><img src="../sources/images/dubbo-monitor-simple.jpg" alt="/admin-guide/images/dubbo-monitor-simple.jpg"></p>\n<h2>NOTICE</h2>\n<p>The failure of Simple Monitor will not effect on consumer and provider\'s running, therefore there would be no risk in production environment\nSimple Monitor use disk to store statistics information, please focus on the limitation of your machine. Mount share disk is recommended if cluster is needed</p>\n<p>Charts directory must be in <code>jetty.directory</code>, or it can not be accessed by web page.</p>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>Please refer to <a href="http://dubbo.apache.org/books/dubbo-user-book-en/references/telnet.html">Telnet command reference manual</a> <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"admin/install/simple-registry-center.md",__html:'<h1>install Simple configuration center</h1>\n<p>Simple Registry has not been well tested, may have bug, cluster is not supported, not recommended to use in production environment</p>\n<p>Install:</p>\n<pre><code class="language-sh">git <span class="hljs-built_in">clone</span> https://github.com/apache/incubator-dubbo-ops\n<span class="hljs-built_in">cd</span> incubator-dubbo-ops &amp;&amp; mvn package\n<span class="hljs-built_in">cd</span> dubbo-registry-simple/target &amp;&amp; tar xvf dubbo-registry-simple-2.0.0-assembly.tar.gz\n<span class="hljs-built_in">cd</span> dubbo-registry-simple-2.0.0\n</code></pre>\n<p>Configuration:</p>\n<pre><code class="language-sh">vi conf/dubbo.properties\n</code></pre>\n<p>Start:</p>\n<pre><code class="language-sh">./assembly.bin/start.sh\n</code></pre>\n<p>Stop:</p>\n<pre><code class="language-sh">./assembly.bin/stop.sh\n</code></pre>\n<p>Restart:</p>\n<pre><code class="language-sh">./assembly.bin/restart.sh\n</code></pre>\n<p>Debug:</p>\n<pre><code class="language-sh">./assembly.bin/start.sh debug\n</code></pre>\n<p>System status:</p>\n<pre><code class="language-sh">./assembly.bin/dump.sh\n</code></pre>\n<p>General control entrance:</p>\n<pre><code class="language-sh">./assembly.bin/server.sh start\n./assembly.bin/server.sh stop\n./assembly.bin/server.sh restart\n./assembly.bin/server.sh debug\n./assembly.bin/server.sh dump\n</code></pre>\n<p>Stdout:</p>\n<pre><code class="language-sh">tail -f logs/stdout.log\n</code></pre>\n<p>Command line <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>:</p>\n<pre><code class="language-shell">telnet 127.0.0.1 9090\nhelp\n</code></pre>\n<p>Or:</p>\n<pre><code class="language-sh"><span class="hljs-built_in">echo</span> status | nc -i 1 127.0.0.1 9090\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>Please refer to <a href="http://dubbo.apache.org/books/dubbo-user-book-en/references/telnet.html">Telnet command manual</a> <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"admin/install/zookeeper.md",__html:'<h1>install Zookeeper configuration center</h1>\n<p>zookeeper register center client version: <code>dubbo-2.3.3</code> and above<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup></p>\n<p>Dubbo changes nothing of Zookeeper\'s server side, an original Zookeeper server is fine. All change happens while calling Zookeeper\'s client side</p>\n<p>install:</p>\n<pre><code class="language-sh">wget http://archive.apache.org/dist/zookeeper/zookeeper-3.3.3/zookeeper-3.3.3.tar.gz\ntar zxvf zookeeper-3.3.3.tar.gz\n<span class="hljs-built_in">cd</span> zookeeper-3.3.3\ncp conf/zoo_sample.cfg conf/zoo.cfg\n</code></pre>\n<p>configuration:</p>\n<pre><code class="language-sh">vi conf/zoo.cfg\n</code></pre>\n<p>If cluster is not needed, the content of <code>zoo.cfg</code> is as below <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>:</p>\n<pre><code class="language-properties">tickTime=2000\ninitLimit=10\nsyncLimit=5\ndataDir=/home/dubbo/zookeeper-3.3.3/data\nclientPort=2181\n</code></pre>\n<p>If cluster is needed, the content of <code>zoo.cfg</code> is as below <sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup>:</p>\n<pre><code class="language-properties">tickTime=2000\ninitLimit=10\nsyncLimit=5\ndataDir=/home/dubbo/zookeeper-3.3.3/data\nclientPort=2181\nserver.1=10.20.153.10:2555:3555\nserver.2=10.20.153.11:2555:3555\n</code></pre>\n<p>Put myid file in data directory <sup class="footnote-ref"><a href="#fn4" id="fnref4">[4]</a></sup>:</p>\n<pre><code class="language-sh">mkdir data\nvi myid\n</code></pre>\n<p>Myid is the number after <code>server</code> in <code>zoo.cfg</code>. The first one\'s content is 1, the second one\'s content is 2:</p>\n<pre><code>1\n</code></pre>\n<p>Start:</p>\n<pre><code class="language-sh">./bin/zkServer.sh start\n</code></pre>\n<p>Stop:</p>\n<pre><code class="language-sh">./bin/zkServer.sh stop\n</code></pre>\n<p>Command line <sup class="footnote-ref"><a href="#fn5" id="fnref5">[5]</a></sup>:</p>\n<pre><code class="language-sh">telnet 127.0.0.1 2181\ndump\n</code></pre>\n<p>Or:</p>\n<pre><code class="language-shell">echo dump | nc 127.0.0.1 2181\n</code></pre>\n<p>Usage:</p>\n<pre><code class="language-xml">dubbo.registry.address=zookeeper://10.20.153.10:2181?backup=10.20.153.11:2181\n</code></pre>\n<p>Or:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"zookeeper"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.153.10:2181,10.20.153.11:2181"</span> /&gt;</span>\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>Zookeeper is a sub project of Apache <a href="http://Hadoop.As">Hadoop.As</a> it is robust, we recommend to use in production environment. <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>Data directory should be changed into your real output directory <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn3" class="footnote-item"><p>Data directory and server address should be changed into your real machine information <a href="#fnref3" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn4" class="footnote-item"><p><code>dataDir</code> in <code>zoo.cfg</code> <a href="#fnref4" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn5" class="footnote-item"><p><a href="http://zookeeper.apache.org/doc/r3.3.3/zookeeperAdmin.html">http://zookeeper.apache.org/doc/r3.3.3/zookeeperAdmin.html</a> <a href="#fnref5" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"admin/ops/dubbo-ops.md",__html:'<h1>Ops console management</h1>\n<h2>Page search</h2>\n<p>If you need to manage a Dubbo service, you need to search it first and open it\'s management page</p>\n<p><img src="../sources/images/dubbo-search.png" alt="/admin-guide/images/dubbo-search.png"></p>\n<h2>Service provider page</h2>\n<p><img src="../sources/images/dubbo-providers.png" alt="/admin-guide/images/dubbo-providers.png"></p>\n<h2>Service consumer page</h2>\n<p><img src="../sources/images/dubbo-consumers.png" alt="/admin-guide/images/dubbo-consumers.png"></p>\n<h2>Service application page</h2>\n<p><img src="../sources/images/dubbo-applications.png" alt="/admin-guide/images/dubbo-applications.png"></p>\n<h2>Add route rule page</h2>\n<p><img src="../sources/images/dubbo-add-route.png" alt="/admin-guide/images/dubbo-add-route.png"></p>\n<h2>Add dynamic configuration page</h2>\n<p><img src="../sources/images/dubbo-add-config.png" alt="/admin-guide/images/dubbo-add-config.png"></p>\n<h5>Service register</h5>\n<h5>Service downgrade</h5>\n<h5>Route rule</h5>\n<h5>Access control</h5>\n<h5>Dynamic configuration</h5>\n<h5>Weight adjustment</h5>\n<h5>Load balance</h5>\n<h5>Service owner</h5>\n'},{filename:"admin/ops/introduction.md",__html:"<h1>Ops manual</h1>\n"},{filename:"admin/ops/pinpoint.md",__html:'<h1>Tracking with Pinpoint</h1>\n<p>After using Dubbo to serve or integrate applications, assuming that a service backstage log shows an exception and that the service is invoked by multiple applications, it is often difficult to determine which application is called, and what is the cause of the problem, so we need a set of distributed tracking systems to quickly locate the problem. Pinpoint can help us quickly locate problems (of course, there are more than one solution).</p>\n<h2>What is Pinpoint</h2>\n<p><a href="https://github.com/naver/pinpoint">Pinpoint</a> is an APM (Application Performance Management) tool for large-scale distributed systems written in Java. Inspired by Dapper, Pinpoint provides a solution to help analyze the overall structure of the system and how components within them are interconnected by tracing transactions across distributed applications.</p>\n<p>You should definitely check Pinpoint out If you want to</p>\n<p>understand your application topology at a glance\nmonitor your application in Real-Time\ngain code-level visibility to every transaction\ninstall APM Agents without changing a single line of code\nhave minimal impact on the performance (approximately 3% increase in resource usage)</p>\n<h3>ServerMap</h3>\n<p>Understand the topology of any distributed systems by visualizing how their components are interconnected. Clicking on a node reveals details about the component, such as its current status, and transaction count.</p>\n<h3>Realtime Active Thread Chart</h3>\n<p>Monitor active threads inside applications in real-time.</p>\n<h3>Request/Response Scatter Chart</h3>\n<p>Visualize request count and response patterns over time to identify potential problems. Transactions can be selected for additional detail by dragging over the chart.</p>\n<h3>CallStack</h3>\n<p>Gain code-level visibility to every transaction in a distributed environment, identifying bottlenecks and points of failure in a single view.</p>\n<h3>Inspector</h3>\n<p>View additional details on the application such as CPU usage, Memory/Garbage Collection, TPS, and JVM arguments.</p>\n<h3>Supported Modules (last updated 2018/04/01)</h3>\n<ul>\n<li>JDK 6+</li>\n<li>Tomcat 6/7/8, Jetty 8/9, JBoss EAP 6, Resin 4, Websphere 6/7/8, Vertx 3.3/3.4/3.5</li>\n<li>Spring, Spring Boot (Embedded Tomcat, Jetty)</li>\n<li>Apache HTTP Client 3.x/4.x, JDK HttpConnector, GoogleHttpClient, OkHttpClient, NingAsyncHttpClient</li>\n<li>Thrift Client, Thrift Service, DUBBO PROVIDER, DUBBO CONSUMER</li>\n<li>ActiveMQ, RabbitMQ</li>\n<li>MySQL, Oracle, MSSQL, CUBRID,POSTGRESQL, MARIA</li>\n<li>Arcus, Memcached, Redis, CASSANDRA</li>\n<li>iBATIS, MyBatis</li>\n<li>DBCP, DBCP2, HIKARICP</li>\n<li>gson, Jackson, Json Lib</li>\n<li>log4j, Logback</li>\n</ul>\n<h2>Pinpoint and Dubbo</h2>\n<h3>Quickstart Pinpoint</h3>\n<p><a href="http://naver.github.io/pinpoint/quickstart.html">Quick start</a>(No neet to start TestApp)</p>\n<h3>Dubbo demo</h3>\n<h4>Create API module</h4>\n<p>pom.xml</p>\n<pre><code class="language-xml"><span class="php"><span class="hljs-meta">&lt;?</span>xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"UTF-8"</span><span class="hljs-meta">?&gt;</span></span>\n<span class="hljs-tag">&lt;<span class="hljs-name">project</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://maven.apache.org/POM/4.0.0"</span>\n         <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>\n         <span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">modelVersion</span>&gt;</span>4.0.0<span class="hljs-tag">&lt;/<span class="hljs-name">modelVersion</span>&gt;</span>\n\n    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.example<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>demo-api<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>0.0.1-SNAPSHOT<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">project</span>&gt;</span>\n</code></pre>\n<p>Create API interface:</p>\n<pre><code>package com.example.demoapi;\n\npublic interface HelloService {\n    String sayHello(String name);\n}\n</code></pre>\n<h4>Dubbo provider</h4>\n<p>pom.xml</p>\n<pre><code class="language-xml"><span class="php"><span class="hljs-meta">&lt;?</span>xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"UTF-8"</span><span class="hljs-meta">?&gt;</span></span>\n<span class="hljs-tag">&lt;<span class="hljs-name">project</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://maven.apache.org/POM/4.0.0"</span> <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>\n\t<span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</span>&gt;</span>\n\t<span class="hljs-tag">&lt;<span class="hljs-name">modelVersion</span>&gt;</span>4.0.0<span class="hljs-tag">&lt;/<span class="hljs-name">modelVersion</span>&gt;</span>\n\n\t<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.example<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n\t<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>demo-provider<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n\t<span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>0.0.1-SNAPSHOT<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n\t<span class="hljs-tag">&lt;<span class="hljs-name">packaging</span>&gt;</span>jar<span class="hljs-tag">&lt;/<span class="hljs-name">packaging</span>&gt;</span>\n\n\t<span class="hljs-tag">&lt;<span class="hljs-name">name</span>&gt;</span>demo-provider<span class="hljs-tag">&lt;/<span class="hljs-name">name</span>&gt;</span>\n\n\t<span class="hljs-tag">&lt;<span class="hljs-name">parent</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-parent<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>2.0.3.RELEASE<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">relativePath</span>/&gt;</span> <span class="hljs-comment">&lt;!-- lookup parent from repository --&gt;</span>\n\t<span class="hljs-tag">&lt;/<span class="hljs-name">parent</span>&gt;</span>\n\n\t<span class="hljs-tag">&lt;<span class="hljs-name">properties</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">project.build.sourceEncoding</span>&gt;</span>UTF-8<span class="hljs-tag">&lt;/<span class="hljs-name">project.build.sourceEncoding</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">project.reporting.outputEncoding</span>&gt;</span>UTF-8<span class="hljs-tag">&lt;/<span class="hljs-name">project.reporting.outputEncoding</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">java.version</span>&gt;</span>1.8<span class="hljs-tag">&lt;/<span class="hljs-name">java.version</span>&gt;</span>\n\t<span class="hljs-tag">&lt;/<span class="hljs-name">properties</span>&gt;</span>\n\n\t<span class="hljs-tag">&lt;<span class="hljs-name">repositories</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">repository</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">id</span>&gt;</span>sonatype-nexus-snapshots<span class="hljs-tag">&lt;/<span class="hljs-name">id</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">url</span>&gt;</span>https://oss.sonatype.org/content/repositories/snapshots<span class="hljs-tag">&lt;/<span class="hljs-name">url</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">releases</span>&gt;</span>\n\t\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">enabled</span>&gt;</span>false<span class="hljs-tag">&lt;/<span class="hljs-name">enabled</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">releases</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">snapshots</span>&gt;</span>\n\t\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">enabled</span>&gt;</span>true<span class="hljs-tag">&lt;/<span class="hljs-name">enabled</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">snapshots</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">repository</span>&gt;</span>\n\t<span class="hljs-tag">&lt;/<span class="hljs-name">repositories</span>&gt;</span>\n\n\t<span class="hljs-tag">&lt;<span class="hljs-name">dependencies</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.alibaba.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>dubbo-spring-boot-starter<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>0.2.0<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.example<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>demo-api<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>0.0.1-SNAPSHOT<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>\n\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-test<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">scope</span>&gt;</span>test<span class="hljs-tag">&lt;/<span class="hljs-name">scope</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>\n\t<span class="hljs-tag">&lt;/<span class="hljs-name">dependencies</span>&gt;</span>\n\n\t<span class="hljs-tag">&lt;<span class="hljs-name">build</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">plugins</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">plugin</span>&gt;</span>\n\t\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n\t\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-maven-plugin<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">plugin</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">plugins</span>&gt;</span>\n\t<span class="hljs-tag">&lt;/<span class="hljs-name">build</span>&gt;</span>\n\n<span class="hljs-tag">&lt;/<span class="hljs-name">project</span>&gt;</span>\n</code></pre>\n<ol>\n<li><code>HelloService</code> interface:</li>\n</ol>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.example.demoprovider.provider;\n\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.config.annotation.Service;\n<span class="hljs-keyword">import</span> com.example.demoapi.HelloService;\n\n<span class="hljs-meta">@Service</span>(version = <span class="hljs-string">"${demo.service.version}"</span>,\n        application = <span class="hljs-string">"${dubbo.application.id}"</span>,\n        protocol = <span class="hljs-string">"${dubbo.protocol.id}"</span>,\n        registry = <span class="hljs-string">"${dubbo.registry.id}"</span>)\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HelloServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">HelloService</span> </span>{\n    <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>;\n    <span class="hljs-meta">@Override</span>\n    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">sayHello</span><span class="hljs-params">(String name)</span> </span>{\n        i++;\n        <span class="hljs-keyword">if</span> (i % <span class="hljs-number">3</span> == <span class="hljs-number">0</span>) {\n            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RuntimeException(<span class="hljs-string">"ex"</span>);\n        }\n        <span class="hljs-keyword">return</span> <span class="hljs-string">"Hello "</span> + name + <span class="hljs-string">"!"</span>;\n    }\n}\n</code></pre>\n<ol start="2">\n<li>Spring Boot bootstrap:</li>\n</ol>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.example.demoprovider;\n\n<span class="hljs-keyword">import</span> org.springframework.boot.SpringApplication;\n<span class="hljs-keyword">import</span> org.springframework.boot.autoconfigure.SpringBootApplication;\n\n<span class="hljs-meta">@SpringBootApplication</span>\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DemoProviderApplication</span> </span>{\n\n\t<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{\n\t\tSpringApplication.run(DemoProviderApplication.class, args);\n\t}\n}\n</code></pre>\n<ol start="3">\n<li><code>application.properties</code>:</li>\n</ol>\n<pre><code class="language-properties"># Spring boot application\nspring.application.name = dubbo-provider-demo\nserver.port = 9090\nmanagement.port = 9091\n\n# Service version\ndemo.service.version = 1.0.0\n\n# Base packages to scan Dubbo Components (e.g @Service , @Reference)\ndubbo.scan.basePackages  = com.example.demoprovider\n\n# Dubbo Config properties\n## ApplicationConfig Bean\ndubbo.application.id = dubbo-provider-demo\ndubbo.application.name = dubbo-provider-demo\n\n## ProtocolConfig Bean\ndubbo.protocol.id = dubbo\ndubbo.protocol.name = dubbo\ndubbo.protocol.port = 12345\n\n## RegistryConfig Bean\ndubbo.registry.id = my-registry\ndubbo.registry.address = N/A\n</code></pre>\n<h4>Dubbo consumer</h4>\n<p>pom.xml</p>\n<pre><code class="language-xml"><span class="php"><span class="hljs-meta">&lt;?</span>xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"UTF-8"</span><span class="hljs-meta">?&gt;</span></span>\n<span class="hljs-tag">&lt;<span class="hljs-name">project</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://maven.apache.org/POM/4.0.0"</span> <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>\n\t<span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</span>&gt;</span>\n\t<span class="hljs-tag">&lt;<span class="hljs-name">modelVersion</span>&gt;</span>4.0.0<span class="hljs-tag">&lt;/<span class="hljs-name">modelVersion</span>&gt;</span>\n\n\t<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.example<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n\t<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>demo-consumer<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n\t<span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>0.0.1-SNAPSHOT<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n\t<span class="hljs-tag">&lt;<span class="hljs-name">packaging</span>&gt;</span>jar<span class="hljs-tag">&lt;/<span class="hljs-name">packaging</span>&gt;</span>\n\n\t<span class="hljs-tag">&lt;<span class="hljs-name">name</span>&gt;</span>demo-consumer<span class="hljs-tag">&lt;/<span class="hljs-name">name</span>&gt;</span>\n\n\t<span class="hljs-tag">&lt;<span class="hljs-name">parent</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-parent<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>2.0.3.RELEASE<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">relativePath</span>/&gt;</span> <span class="hljs-comment">&lt;!-- lookup parent from repository --&gt;</span>\n\t<span class="hljs-tag">&lt;/<span class="hljs-name">parent</span>&gt;</span>\n\n\t<span class="hljs-tag">&lt;<span class="hljs-name">properties</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">project.build.sourceEncoding</span>&gt;</span>UTF-8<span class="hljs-tag">&lt;/<span class="hljs-name">project.build.sourceEncoding</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">project.reporting.outputEncoding</span>&gt;</span>UTF-8<span class="hljs-tag">&lt;/<span class="hljs-name">project.reporting.outputEncoding</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">java.version</span>&gt;</span>1.8<span class="hljs-tag">&lt;/<span class="hljs-name">java.version</span>&gt;</span>\n\t<span class="hljs-tag">&lt;/<span class="hljs-name">properties</span>&gt;</span>\n\n\t<span class="hljs-tag">&lt;<span class="hljs-name">repositories</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">repository</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">id</span>&gt;</span>sonatype-nexus-snapshots<span class="hljs-tag">&lt;/<span class="hljs-name">id</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">url</span>&gt;</span>https://oss.sonatype.org/content/repositories/snapshots<span class="hljs-tag">&lt;/<span class="hljs-name">url</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">releases</span>&gt;</span>\n\t\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">enabled</span>&gt;</span>false<span class="hljs-tag">&lt;/<span class="hljs-name">enabled</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">releases</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">snapshots</span>&gt;</span>\n\t\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">enabled</span>&gt;</span>true<span class="hljs-tag">&lt;/<span class="hljs-name">enabled</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">snapshots</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">repository</span>&gt;</span>\n\t<span class="hljs-tag">&lt;/<span class="hljs-name">repositories</span>&gt;</span>\n\n\t<span class="hljs-tag">&lt;<span class="hljs-name">dependencies</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-web<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.alibaba.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>dubbo-spring-boot-starter<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>0.2.0<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.example<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>demo-api<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>0.0.1-SNAPSHOT<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>\n\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-test<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">scope</span>&gt;</span>test<span class="hljs-tag">&lt;/<span class="hljs-name">scope</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>\n\t<span class="hljs-tag">&lt;/<span class="hljs-name">dependencies</span>&gt;</span>\n\n\t<span class="hljs-tag">&lt;<span class="hljs-name">build</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;<span class="hljs-name">plugins</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">plugin</span>&gt;</span>\n\t\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n\t\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-maven-plugin<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n\t\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">configuration</span>&gt;</span>\n\t\t\t\t\t<span class="hljs-tag">&lt;<span class="hljs-name">classifier</span>&gt;</span>exec<span class="hljs-tag">&lt;/<span class="hljs-name">classifier</span>&gt;</span>\n\t\t\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">configuration</span>&gt;</span>\n\t\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">plugin</span>&gt;</span>\n\t\t<span class="hljs-tag">&lt;/<span class="hljs-name">plugins</span>&gt;</span>\n\t<span class="hljs-tag">&lt;/<span class="hljs-name">build</span>&gt;</span>\n\n<span class="hljs-tag">&lt;/<span class="hljs-name">project</span>&gt;</span>\n</code></pre>\n<ol>\n<li><code>@Reference</code> injection <code>HelloService</code></li>\n</ol>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.example.democonsumer.controller;\n\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.config.annotation.Reference;\n<span class="hljs-keyword">import</span> com.example.demoapi.HelloService;\n<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.RequestMapping;\n<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.RequestParam;\n<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.RestController;\n\n<span class="hljs-meta">@RestController</span>\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DemoConsumerController</span> </span>{\n    <span class="hljs-meta">@Reference</span>(version = <span class="hljs-string">"${demo.service.version}"</span>,\n            application = <span class="hljs-string">"${dubbo.application.id}"</span>,\n            url = <span class="hljs-string">"dubbo://&lt;Real IP Address&gt;:12345"</span>)\n    <span class="hljs-keyword">private</span> HelloService helloService;\n\n    <span class="hljs-meta">@RequestMapping</span>(<span class="hljs-string">"/sayHello"</span>)\n    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">sayHello</span><span class="hljs-params">(@RequestParam String name)</span> </span>{\n        <span class="hljs-keyword">return</span> helloService.sayHello(name);\n    }\n}\n</code></pre>\n<ol start="2">\n<li>Spring Boot bootstrap:</li>\n</ol>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.example.democonsumer;\n\n<span class="hljs-keyword">import</span> org.springframework.boot.SpringApplication;\n<span class="hljs-keyword">import</span> org.springframework.boot.autoconfigure.SpringBootApplication;\n\n<span class="hljs-meta">@SpringBootApplication</span>\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DemoConsumerApplication</span> </span>{\n\n\t<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{\n\t\tSpringApplication.run(DemoConsumerApplication.class, args);\n\t}\n}\n</code></pre>\n<ol start="3">\n<li><code>application.properties</code>:</li>\n</ol>\n<pre><code class="language-properties"># Spring boot application\nspring.application.name=dubbo-consumer-demo\nserver.port=8080\nmanagement.port=8081\n\n# Service Version\ndemo.service.version=1.0.0\n\n# Dubbo Config properties\n## ApplicationConfig Bean\ndubbo.application.id=dubbo-consumer-demo\ndubbo.application.name=dubbo-consumer-demo\n\n## ProtocolConfig Bean\ndubbo.protocol.id=dubbo\ndubbo.protocol.name=dubbo\ndubbo.protocol.port=12345\n</code></pre>\n<h3>Using Pinpoint-agent to start <code>Dubbo provider</code> and <code>Dubbo consumer</code></h3>\n<ol>\n<li>Maven clean package</li>\n</ol>\n<pre><code>mvn clean package\n</code></pre>\n<ol start="2">\n<li>Start provider</li>\n</ol>\n<pre><code>java -jar -javaagent:$AGENT_PATH/pinpoint-bootstrap-$VERSION.jar -Dpinpoint.agentId=demo-provider -Dpinpoint.applicationName=DP target/demo-provider-0.0.1-SNAPSHOT.jar\n</code></pre>\n<ol start="3">\n<li>Start consumer</li>\n</ol>\n<pre><code>java -jar -javaagent:$AGENT_PATH/pinpoint-bootstrap-$VERSION.jar -Dpinpoint.agentId=demo-consumer -Dpinpoint.applicationName=DC target/demo-comsumer-0.0.1-SNAPSHOT-exec.jar\n</code></pre>\n<ol start="4">\n<li>Access the consumer address to simulate user requests</li>\n</ol>\n<p><code>http://localhost:8080/sayHello?name=ABC</code></p>\n<h2>Using Pinpoint locate problems</h2>\n<h3>Homepage</h3>\n<p><img src="../sources/images/pinpoint-home.png" alt="/admin-guide/images/pinpoint-home.png"></p>\n<blockquote>\n<p>The user request here is double the number of requests for DubboProvider, because the favicon.ico icon request is recorded.</p>\n</blockquote>\n<h3>Call tree</h3>\n<p><img src="../sources/images/pinpoint-calltree.png" alt="/admin-guide/images/pinpoint-calltree.png"></p>\n<h3>Mixed view</h3>\n<p><img src="../sources/images/pinpoint-mixedview.png" alt="/admin-guide/images/pinpoint-mixedview.png"></p>\n<h3>Other</h3>\n<p>The example simply simulates the provision and call of Dubbo, and does not carry out the application of other middleware such as database. For detailed use, please refer to the Pinpoint document.</p>\n'},{filename:"dev/README.md",__html:"<h1>dubbo-dev-book</h1>\n<p>This book dives into the design principles of dubbo, mainly covers the following topics: extension, coding styles, versio, build, etc.</p>\n"},{filename:"dev/SPI.md",__html:'<h1>SPI Loading</h1>\n<h2>SPI Config</h2>\n<h3>Source:</h3>\n<p>Dubbo SPI is inherited from standard JDK SPI(Service Provider Interface) and makes it more powerful.</p>\n<p>Dubbo fixed below issues of the standard JDK SPI:</p>\n<ul>\n<li>the standard JDK  SPI will load  and instantize all implementation at once. It will be a waste of resources if the implementation is timecosted ,but never be used.</li>\n<li>We cann\'t accquire the SPI name,if loading the SPI implementation is failed.For example:standard JDK  ScriptEngine,get script type by invoking method getName(). RubyScriptEngine class will load failed if the depenency jar jruby.jar is missing,and the real error info will be lost. When user executes ruby scripts , program throws exception , telling not support ruby, but it is not the real cause.</li>\n<li>Enhance the SPI functionality by supporting  IoC and AOP ,one SPI can be easily injected by another SPI simply using  setter.</li>\n</ul>\n<h3>Appointment:</h3>\n<p>In the jar file containing extension class <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>,places a config file  <code>META-INF/dubbo/full interface name</code>,file content pattern:<code>SPI name=the fully qualified name for the extension class</code>,use new line seperator for multiple implementation.</p>\n<h3>Example:</h3>\n<p>To extend  Dubbo Protocol,placee a text file in the extension jar file:<code>META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol</code>,content:</p>\n<pre><code class="language-properties">xxx=com.alibaba.xxx.XxxProtocol\n</code></pre>\n<p>content of the implementation <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.alibaba.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Protocol;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxProtocol</span> <span class="hljs-title">implemenets</span> <span class="hljs-title">Protocol</span> </span>{ \n    <span class="hljs-comment">// ...</span>\n}\n</code></pre>\n<h3>Configuration in config module</h3>\n<p>In Dubbo config module,all SPI points have related attributes or labels,we can choose the specific SPI implementation by using its name. Like:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n</code></pre>\n<h2>SPI Features</h2>\n<h3>SPI Auto Wrap</h3>\n<p>Auto wrap the SPI\'s Wrapper class。<code>ExtensionLoader</code>  loads the SPI implementation,if the SPI has a copy instructor ,it will be regarded as the SPI\'s Wrapper class。</p>\n<p>Wrapper class content:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.alibaba.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Protocol;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxProtocolWrapper</span> <span class="hljs-title">implemenets</span> <span class="hljs-title">Protocol</span> </span>{\n    Protocol impl;\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">XxxProtocol</span><span class="hljs-params">(Protocol protocol)</span> </span>{ impl = protocol; }\n \n    <span class="hljs-comment">//after interface method is executed,the method in extension will be executed</span>\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">refer</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-comment">//... some operation</span>\n        impl.refer();\n        <span class="hljs-comment">// ... some operation</span>\n    }\n \n    <span class="hljs-comment">// ...</span>\n}\n</code></pre>\n<p>Wrapper class also implements the same SPI interface,but Wrapper is not the real implementation。It is used for wrap the real  implementation  returned from the <code>ExtensionLoader</code> 。The real returned instance by   <code>ExtensionLoader</code>  is the Wrapper class instance,Wrapper holder the real SPI implementation class。</p>\n<p>There can be many Wrapper for one spi, simply add one if you need。</p>\n<p>By Wrapper class,you will be able move same logics into Wrapper for all SPIs。Newly added Wrapper class add external logics for all spis, looks like  AOP, Wrapper acts as a proxy for SPI.</p>\n<h3>SPI Auto Load</h3>\n<p>when loading the SPI,Dubbo will auto load the depency SPI. When one SPI implementation contains attribute which is also an SPI of another type,<code>ExtensionLoader</code> will automatically load the depency SPI.<code>ExtensionLoader</code> knows all the members of the specific SPI by scanning the setter method of all implementation class.</p>\n<p>Demo:two SPI  <code>CarMaker</code>(car maker)、<code>WheelMaker</code> (wheel maker)</p>\n<p>Intefaces look like:</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">CarMaker</span> </span>{\n    <span class="hljs-function">Car <span class="hljs-title">makeCar</span><span class="hljs-params">()</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">WheelMaker</span> </span>{\n    <span class="hljs-function">Wheel <span class="hljs-title">makeWheel</span><span class="hljs-params">()</span></span>;\n}\n</code></pre>\n<p><code>CarMaker</code>  implementation:</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">RaceCarMaker</span> <span class="hljs-title">implemenets</span> <span class="hljs-title">CarMaker</span> </span>{\n    WheelMaker wheelMaker;\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">setWheelMaker</span><span class="hljs-params">(WheelMaker wheelMaker)</span> </span>{\n        <span class="hljs-keyword">this</span>.wheelMaker = wheelMaker;\n    }\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> Car <span class="hljs-title">makeCar</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-comment">// ...</span>\n        Wheel wheel = wheelMaker.makeWheel();\n        <span class="hljs-comment">// ...</span>\n        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> RaceCar(wheel, ...);\n    }\n}\n</code></pre>\n<p>when<code>ExtensionLoader</code> loading <code>CarMaker</code>  implementation <code>RaceCar</code> ,<code>setWheelMaker</code>  needs paramType <code>WheelMaker</code>  which is also an SPI, It will be automatically loaded .</p>\n<p>This brings a new question:How <code>ExtensionLoader</code> determines which implementation to use when load the injected SPI。As for this demo, when existing multi <code>WheelMaker</code>  implementation, which one should the <code>ExtensionLoader</code>  chooses.</p>\n<p>Good question ,we will explain it in the following chapter: SPI Auto Adaptive.</p>\n<h3>SPI Auto Adaptive</h3>\n<p>The depency SPI that <code>ExtensionLoader</code>  injects is an instance of <code>Adaptive</code>,the real spi implementation is known until the adaptive instance is be executed.</p>\n<p>Dubbo  use URL (containing Key-Value) to pass the configuration.</p>\n<p>The SPI method invocation has the URL parameter(Or Entity that has URL attribute)</p>\n<p>In this way depended SPI can get configuration from URL,after config all SPI  key needed, configuration information will be passed from outer by URL.URL act as a bus when pass the config information.</p>\n<p>Demo:two SPI  <code>CarMaker</code>、<code>WheelMaker</code></p>\n<p>interface looks like:</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">CarMaker</span> </span>{\n    <span class="hljs-function">Car <span class="hljs-title">makeCar</span><span class="hljs-params">(URL url)</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">WheelMaker</span> </span>{\n    <span class="hljs-function">Wheel <span class="hljs-title">makeWheel</span><span class="hljs-params">(URL url)</span></span>;\n}\n</code></pre>\n<p><code>CarMaker</code>  implementation:</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">RaceCarMaker</span> <span class="hljs-title">implemenets</span> <span class="hljs-title">CarMaker</span> </span>{\n    WheelMaker wheelMaker;\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">setWheelMaker</span><span class="hljs-params">(WheelMaker wheelMaker)</span> </span>{\n        <span class="hljs-keyword">this</span>.wheelMaker = wheelMaker;\n    }\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> Car <span class="hljs-title">makeCar</span><span class="hljs-params">(URL url)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n        Wheel wheel = wheelMaker.makeWheel(url);\n        <span class="hljs-comment">// ...</span>\n        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> RaceCar(wheel, ...);\n    }\n}\n</code></pre>\n<p>when execute the code above</p>\n<pre><code class="language-java"><span class="hljs-comment">// ...</span>\nWheel wheel = wheelMaker.makeWheel(url);\n<span class="hljs-comment">// ...</span>\n</code></pre>\n<p>,the injected <code>Adaptive</code>  object determine which  <code>WheelMaker</code> \'s  <code>makeWheel</code>  method will be executed by predefined Key.。Such  as <code>wheel.type</code>, key  <code>url.get(&quot;wheel.type&quot;)</code>  will determine <code>WheelMake</code>  implementation。The logic of<code>Adaptive</code> instance of fixed,getting the predefined Key of the URL,dynamically creating the real implementation and execute it.</p>\n<p>For Dubbo , the SPI  <code>Adaptive</code>  implementation in  <code>ExtensionLoader</code>  is dynamically created when dubbo is loading the SPI。Get the Key from URL, the Key will be provided through <code>@Adaptive</code> annotation for the interface method definition.</p>\n<p>Below is Dubbo  Transporter SPI codes:</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">Transporter</span> </span>{\n    <span class="hljs-meta">@Adaptive</span>({<span class="hljs-string">"server"</span>, <span class="hljs-string">"transport"</span>})\n    <span class="hljs-function">Server <span class="hljs-title">bind</span><span class="hljs-params">(URL url, ChannelHandler handler)</span> <span class="hljs-keyword">throws</span> RemotingException</span>;\n \n    <span class="hljs-meta">@Adaptive</span>({<span class="hljs-string">"client"</span>, <span class="hljs-string">"transport"</span>})\n    <span class="hljs-function">Client <span class="hljs-title">connect</span><span class="hljs-params">(URL url, ChannelHandler handler)</span> <span class="hljs-keyword">throws</span> RemotingException</span>;\n}\n</code></pre>\n<p>for the method bind(),Adaptive will firstly search <code>server</code> key,if no Key  were founded then will search <code>transport</code> key ,to determine the implementation that the proxy represent for.</p>\n<h3>SPI Auto Activation</h3>\n<p>As for Collections SPI, such as:<code>Filter</code>, <code>InvokerListener</code>, <code>ExportListener</code>, <code>TelnetHandler</code>, <code>StatusChecker</code>  etc,multi implementations can be loaded at one time. User can simplify configuration by using auto activation,Like:</p>\n<pre><code class="language-java"><span class="hljs-keyword">import</span> com.alibaba.dubbo.common.extension.Activate;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Filter;\n \n<span class="hljs-meta">@Activate</span> <span class="hljs-comment">// Active for any condition</span>\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxFilter</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Filter</span> </span>{\n    <span class="hljs-comment">// ...</span>\n}\n</code></pre>\n<p>Or:</p>\n<pre><code class="language-java"><span class="hljs-keyword">import</span> com.alibaba.dubbo.common.extension.Activate;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Filter;\n \n<span class="hljs-meta">@Activate</span>(<span class="hljs-string">"xxx"</span>) <span class="hljs-comment">// when configed xxx parameter and the parameter has a valid value,the SPI is activated,for example configed cache="lru",auto acitivate CacheFilter。</span>\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxFilter</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Filter</span> </span>{\n    <span class="hljs-comment">// ...</span>\n}\n</code></pre>\n<p>Or:</p>\n<pre><code class="language-java"><span class="hljs-keyword">import</span> com.alibaba.dubbo.common.extension.Activate;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Filter;\n \n<span class="hljs-meta">@Activate</span>(group = <span class="hljs-string">"provider"</span>, value = <span class="hljs-string">"xxx"</span>) <span class="hljs-comment">// only activate for provider,group can be "provider" or "consumer"</span>\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxFilter</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Filter</span> </span>{\n    <span class="hljs-comment">// ...</span>\n}\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>Note:The config file here is in you own jar file,not in dubbo release jar file,Dubbo  will scan all jar files for the same filename in  ClassPath and merge together then <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>Note:SPI will be loaded in singleton pattern(Please ensure thread safety ),cached in  <code>ExtensionLoader</code> <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"dev/SUMMARY.md",__html:'<h1>Summary</h1>\n<ul>\n<li><a href="./build.md">1 How To Build</a></li>\n<li><a href="./design.md">2 Architecture</a></li>\n<li><a href="./SPI.md">3 How SPI Works</a></li>\n<li><a href="./implementation.md">4 Init, Process, Protocols</a></li>\n<li><a href="./impls/introduction.md">5 SPI Extensions</a>\n<ul>\n<li><a href="./impls/protocol.md">5.1 Protocol</a></li>\n<li><a href="./impls/filter.md">5.2 Filter</a></li>\n<li><a href="./impls/invoker-listener.md">5.3 InvokerListener</a></li>\n<li><a href="./impls/exporter-listener.md">5.4 ExporterListener</a></li>\n<li><a href="./impls/cluster.md">5.5 Cluster</a></li>\n<li><a href="./impls/router.md">5.6 Router</a></li>\n<li><a href="./impls/load-balance.md">5.7 LoadBalance</a></li>\n<li><a href="./impls/merger.md">5.8 Merger</a></li>\n<li><a href="./impls/registry.md">5.9 Registry</a></li>\n<li><a href="./impls/monitor.md">5.10 Monitor</a></li>\n<li><a href="./impls/extension-factory.md">5.11 ExtensionFactory</a></li>\n<li><a href="./impls/proxy-factory.md">5.12 ProxyFactory</a></li>\n<li><a href="./impls/compiler.md">5.13 Compiler</a></li>\n<li><a href="./impls/dispatcher.md">5.14 Dispatcher</a></li>\n<li><a href="./impls/threadpool.md">5.15 Threadpool</a></li>\n<li><a href="./impls/serialize.md">5.16 Serialization</a></li>\n<li><a href="./impls/remoting.md">5.17 Remoting</a></li>\n<li><a href="./impls/exchanger.md">5.18 Exchanger</a></li>\n<li><a href="./impls/networker.md">5.19 Networker</a></li>\n<li><a href="./impls/telnet-handler.md">5.20 TelnetHandler</a></li>\n<li><a href="./impls/status-checker.md">5.21 StatusChecker</a></li>\n<li><a href="./impls/container.md">5.22 Container</a></li>\n<li><a href="./impls/page.md">5.23 PageHandler</a></li>\n<li><a href="./impls/cache.md">5.24 Cache</a></li>\n<li><a href="./impls/validation.md">5.25 Validation</a></li>\n<li><a href="./impls/logger-adapter.md">5.26 LoggerAdapter</a></li>\n</ul>\n</li>\n<li><a href="./contract.md">6 Contract</a></li>\n<li><a href="./coding.md">7 Code Style</a></li>\n<li><a href="./release.md">9 Versions</a></li>\n<li><a href="./contribution.md">10 Contribution</a></li>\n<li><a href="./checklist.md">11 Checklist</a></li>\n<li><a href="./code-smell.md">12 Code Smell</a></li>\n<li><a href="./TCK.md">13 TCK</a></li>\n</ul>\n'},{filename:"dev/TCK.md",__html:'<h1>Compatibility test</h1>\n<p>Dubbo\'s protocol, communication, serialization, registry, load balancing and other SPI all offer alternative strategies for different application scenarios while our test cases are very scattered. Ours is always uncertain whether it can satisfy the complete contract of the extension point when users need to add a new implementation.</p>\n<p>Thus we need to use TCK (Technology Compatibility Kit) for the core extension points.  When users add a new implementaion, compatibility with the rest of the framework can be ensured with TCK. This can effectively improve the overall health and also facilitate the access of the third party extenders, which accelerates the maturity of the open source community.</p>\n<p>Xingzhi from the open source community is already working on this part. His preliminary idea is to build a TCK framework for Dubbo drawing on the CDI-TCK of JBoss first, then realize the TCK implementing case of Dubbo.</p>\n<p>Reference:<a href="http://docs.jboss.org/cdi/tck/reference/1.0.1-Final/html/introduction.html">http://docs.jboss.org/cdi/tck/reference/1.0.1-Final/html/introduction.html</a></p>\n<p>Anyone interested  is welcomed to work on this together.</p>\n<h4>Protocol TCK</h4>\n<blockquote>\n<p>TODO</p>\n</blockquote>\n<h4>Registry TCK</h4>\n<blockquote>\n<p>TODO</p>\n</blockquote>\n'},{filename:"dev/build.md",__html:'<h1>Source Code Build</h1>\n<h2>Checkout</h2>\n<p>checkout the lastest project source code with commands blow:</p>\n<pre><code class="language-sh">git <span class="hljs-built_in">clone</span> https://github.com/apache/incubator-dubbo.git dubbo\n</code></pre>\n<h2>Branches</h2>\n<p>We use <code>master</code> as the major branch for new feature development, and use other branches for maintenance. Tags for all versions can be checked via <a href="https://github.com/apache/incubator-dubbo/tags">https://github.com/apache/incubator-dubbo/tags</a>.</p>\n<h2>Building</h2>\n<p>Dubbo relies on <a href="http://maven.apache.org">maven</a> as the building tool.</p>\n<p>Requirements:</p>\n<ul>\n<li>Java above 1.5 version</li>\n<li>Maven version 2.2.1 or above</li>\n</ul>\n<p>The following <code>MAVEN_OPTS</code>should be configured before building:</p>\n<pre><code class="language-sh"><span class="hljs-built_in">export</span> MAVEN_OPTS=-Xmx1024m -XX:MaxPermSize=512m\n</code></pre>\n<p>build with below command:</p>\n<pre><code class="language-sh">mvn clean install\n</code></pre>\n<p>skip testing using below building command:</p>\n<pre><code class="language-sh">mvn install -Dmaven.test.skip\n</code></pre>\n<h2>Building jar package of source code</h2>\n<p>build Dubbo source code jar package with below command, which can debug Dubbo source code.</p>\n<pre><code class="language-sh">mvn clean <span class="hljs-built_in">source</span>:jar install -Dmaven.test.skip\n</code></pre>\n<h2>IDE support</h2>\n<p>use below command to generate IDE.</p>\n<h3>Intellij Idea</h3>\n<pre><code class="language-sh">mvn idea:idea\n</code></pre>\n<h3>Eclipse</h3>\n<pre><code class="language-sh">mvn eclipse:eclipse\n</code></pre>\n<p>Importing into eclipse</p>\n<p>Firstly, a maven repository needs to be configured in eclipse. Define <code>M2_REPO</code> and point it to the local maven repository by clicking <code>Preferences -&gt; Java -&gt; Build Path -&gt; Classpath</code>.</p>\n<p>Use the following maven command as well:</p>\n<pre><code class="language-sh">mvn eclipse:configure-workspace -Declipse.workspace=/path/to/the/workspace/\n</code></pre>\n<p>1: view the source code through <a href="https://github.com/apache/incubator-dubbo">https://github.com/apache/incubator-dubbo</a>\n2: path under UNIX is ${HOME}/.m2/repository, path under Windows is C:\\Documents and Settings&lt;user&gt;.m2\\repository</p>\n'},{filename:"dev/checklist.md",__html:"<h1>Checklist</h1>\n<h2>Checklist before release</h2>\n<ul>\n<li>github milestones</li>\n<li>github change lists</li>\n<li>Travis CI</li>\n<li>test code</li>\n<li>find bugs</li>\n</ul>\n<h2>Checklist for bigfix versions</h2>\n<ul>\n<li>Create a <em>github issue</em> before coding</li>\n<li>Create <em>unit test</em> before bugfix</li>\n<li>Review</li>\n<li>Test your code (Normal process / Abnormal process)</li>\n<li>Record your design on <em>github issue</em></li>\n<li>Complete javadoc and comment in code</li>\n<li>Manager for every version, responsible for scope and check</li>\n</ul>\n"},{filename:"dev/code-smell.md",__html:'<h1>Bad Smell</h1>\n<p>Ugly Dubbo design or implementation will be record here.</p>\n<h2>URL Convertion</h2>\n<h3>1. Point to Point Service export and refer</h3>\n<p>service directly export:</p>\n<pre><code>EXPORT(dubbo://provider-address/com.xxx.XxxService?version=1.0.0&quot;)\n</code></pre>\n<p>service directly refer:</p>\n<pre><code>REFER(dubbo://provider-address/com.xxx.XxxService?version=1.0.0)\n</code></pre>\n<h3>2. Export servie by registry</h3>\n<p>export service to registry:</p>\n<pre><code>EXPORT(registry://registry-address/com.alibaba.dubbo.registry.RegistrySerevice?registry=dubbo&amp;export=ENCODE(dubbo://provider-address/com.xxx.XxxService?version=1.0.0))\n</code></pre>\n<p>accquire registry:</p>\n<pre><code>url.setProtocol(url.getParameter(&quot;registry&quot;, &quot;dubbo&quot;))\nGETREGISTRY(dubbo://registry-address/com.alibaba.dubbo.registry.RegistrySerevice)\n</code></pre>\n<p>registry service address:</p>\n<pre><code>url.getParameterAndDecoded(&quot;export&quot;))\nREGISTER(dubbo://provider-address/com.xxx.XxxService?version=1.0.0)\n</code></pre>\n<h3>3. Refer service from registry</h3>\n<p>refer service from registry:</p>\n<pre><code>REFER(registry://registry-address/com.alibaba.dubbo.registry.RegistrySerevice?registry=dubbo&amp;refer=ENCODE(version=1.0.0))\n</code></pre>\n<p>accquire registry:</p>\n<pre><code>url.setProtocol(url.getParameter(&quot;registry&quot;, &quot;dubbo&quot;))\nGETREGISTRY(dubbo://registry-address/com.alibaba.dubbo.registry.RegistrySerevice)\n</code></pre>\n<p>subscribe service address:</p>\n<pre><code>url.addParameters(url.getParameterAndDecoded(&quot;refer&quot;))\nSUBSCRIBE(dubbo://registry-address/com.xxx.XxxService?version=1.0.0)\n</code></pre>\n<p>notify service address:</p>\n<pre><code>url.addParameters(url.getParameterAndDecoded(&quot;refer&quot;))\nNOTIFY(dubbo://provider-address/com.xxx.XxxService?version=1.0.0)\n</code></pre>\n<h3>4. Registry push route rule</h3>\n<p>registry push route rule:</p>\n<pre><code>NOTIFY(route://registry-address/com.xxx.XxxService?router=script&amp;type=js&amp;rule=ENCODE(function{...}))\n</code></pre>\n<p>accquire routers:</p>\n<pre><code>url.setProtocol(url.getParameter(&quot;router&quot;, &quot;script&quot;))\nGETROUTE(script://registry-address/com.xxx.XxxService?type=js&amp;rule=ENCODE(function{...}))\n</code></pre>\n<h3>5. Load route rule from file</h3>\n<p>load route rule from file:</p>\n<pre><code>GETROUTE(file://path/file.js?router=script)\n</code></pre>\n<p>accquire routers:</p>\n<pre><code>url.setProtocol(url.getParameter(&quot;router&quot;, &quot;script&quot;)).addParameter(&quot;type&quot;, SUFFIX(file)).addParameter(&quot;rule&quot;, READ(file))\nGETROUTE(script://path/file.js?type=js&amp;rule=ENCODE(function{...}))\n</code></pre>\n<h2>Invoke parameters</h2>\n<ul>\n<li>path      service path</li>\n<li>group    service group</li>\n<li>version  service version</li>\n<li>dubbo   current dubbo release version</li>\n<li>token    verify token</li>\n<li>timeout   invocation timeout</li>\n</ul>\n<h2>SPI Loading</h2>\n<h3>1. SPI Auto Adaptive</h3>\n<p>When ExtensionLoader loads SPI, It will check spi attributes(using set method) . If one attribute is SPI, ExtensionLoader  will load the SPI implementation. Auto injected object is an adaptive instance(proxy) ,because the real implementation is confirmed only in execution stage.。when adaptive spi is invoked, Dubbo will choose the real implementation and executes it. Dubbo choose the right implementation according to the parameters that the mehod defines.</p>\n<p>All the inner SPIs that  Dubbo defines have the URL  parameter defined for the method invocation. Adaptive SPI uses URL to determine which implementation is needed. One specific Key and Value in the URL confirms the usage of the specific implementation, All these is done by adding <code>@Adaptive</code>  annotation.</p>\n<pre><code class="language-java"><span class="hljs-meta">@Extension</span>\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Car</span> </span>{\n    <span class="hljs-meta">@Adaptive</span>({<span class="hljs-string">"http://10.20.160.198/wiki/display/dubbo/car.type"</span>, <span class="hljs-string">"http://10.20.160.198/wiki/display/dubbo/transport.type"</span>})\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">run</span><span class="hljs-params">(URL url, Type1 arg1, Type2 arg2)</span></span>;\n}\n</code></pre>\n<p>For the rules above,ExtensionLoader  will create a adaptive instance for each SPI injected.</p>\n<p>ExtensionLoader generated adaptive classes  look like :</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> &lt;<span class="hljs-keyword">package</span> name <span class="hljs-keyword">for</span> SPI <span class="hljs-class"><span class="hljs-keyword">interface</span>&gt;</span>;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> &lt;<span class="hljs-title">SPI</span> <span class="hljs-title">interface</span> <span class="hljs-title">name</span>&gt;$<span class="hljs-title">Adpative</span> <span class="hljs-keyword">implements</span> &lt;<span class="hljs-title">SPI</span> <span class="hljs-title">interface</span>&gt; </span>{\n    <span class="hljs-keyword">public</span> &lt;contains <span class="hljs-meta">@Adaptive</span> annotation method&gt;(&lt;parameters&gt;) {\n        <span class="hljs-keyword">if</span>(parameters containing URL Type?) <span class="hljs-function">using URL parameter\n        <span class="hljs-keyword">else</span> <span class="hljs-title">if</span><span class="hljs-params">(method returns URL)</span> using the return URL\n        # &lt;<span class="hljs-keyword">else</span> throw exception,inject SPI fail!&gt;\n         \n        <span class="hljs-title">if</span><span class="hljs-params">(URL accquired == <span class="hljs-keyword">null</span>)</span> </span>{\n            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"url == null"</span>);\n        }\n \n        According to the Key order from <span class="hljs-meta">@Adaptive</span> annotation,get the Value from the URL as the real SPI name\n        <span class="hljs-keyword">if</span> no value is found then use the <span class="hljs-keyword">default</span> SPI implementation。If no SPI point, <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalStateException(<span class="hljs-string">"Fail to get extension"</span>);\n \n        Invoke the method using the spi and <span class="hljs-keyword">return</span> the result.\n    }\n \n    <span class="hljs-keyword">public</span> &lt;method having annotation <span class="hljs-meta">@Adaptive</span>&gt;(&lt;parameters&gt;) {\n        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> UnsupportedOperationException(<span class="hljs-string">"is not adaptive method!"</span>);\n    }\n}\n</code></pre>\n<p><code>@Adaptive</code>  annotation usage:</p>\n<p>If no value is configed for those Keys in URL,default SPI implementation is used。For example ,String[] {&quot;key1&quot;, &quot;key2&quot;},firstly Dubbo will look up value for key1 and use it as SPI name;if key1 value is not founded then look up for key2,if value of key2 is also not found ,then use default spi implementation. If no default implementation is configed, then the method will throw IllegalStateException。if not configed , then default implement is lower case of the interface class full package name. For Extension interface <code>com.alibaba.dubbo.xxx.YyyInvokerWrapper</code> , default value is <code>new String[] {&quot;yyy.invoker.wrapper&quot;}</code></p>\n<h2>Callback Function</h2>\n<h3>1. Parameter Callback</h3>\n<p>main theory : in the persistent connection for one consumer-&gt;provider,export a service in  Consumer side,provider  side can reversely call the instance in consumer side.</p>\n<p>Implement details:</p>\n<ul>\n<li>For exchanging interface instance in transmition, auto export and auto refer is implemented in DubboCodec . Need to seperate business logic and codec logic.</li>\n<li>you will need to judge whether needing callback when getting exporter from invocation,if needed, get the callback instance id from the attachments. By using this method, consumer side can implement the callback interface with different implementations.</li>\n</ul>\n<h3>2. Event Notification</h3>\n<p>main theory : when Consumer  executing invoke method,judging if any configuration for  onreturn/onerror... put  the method for onreturn to the callback list of the async invocatioin.</p>\n<p>Implement details:parameters is passed using URL,but string-object is not supported for URL, so the method is stored in  staticMap,it needs to be optimized.</p>\n<h2>Lazy Connection</h2>\n<p>DubboProtocol  specific features, default disabled</p>\n<p>When client creating proxy for server, do not establish TCP persistent connection at first, only init the connecton when data is needing transmision.</p>\n<p>This feather will disable the connection retry policy , resend the data again(if connection is lost when sending data ,try to establish a new connection to send data)</p>\n<h2>Share Connection</h2>\n<p>DubboProtocol specific features, default enabled。</p>\n<p>JVM A export many services,JVM B  refer more than one services of A,Share Connection means those different services invocations between A and B uses the same TCP connection  to transmit data, reducing server connections.</p>\n<p>Implement details:when using share connection for the same address,you need pay more attention to the invoker\'s destroy action.on one hand, you should close the connection when all the invokers refering the same address is destroyed, on another hand ,you should not close the connection when not all of the invokers are destroyed. In design implementation, we uses a strategy called reference count , we create a connection called Lazy connection for exceptions not affacting business when closing the connection just in case.</p>\n<h2>sticky policy</h2>\n<p>when existing many providers and configing the sticky policy,invocation will be sent to the same provider as last invocation. Sticky Policy opens the lazy attribute of connection, for avoiding open useless connectons.</p>\n<h2>provider selecting logic</h2>\n<ol start="0">\n<li>existing multi providers,firstly select by Loadbalance 。If the selected  provider is available ,then just doing the invocation</li>\n<li>If the selected provider is not available in stage 1, then choose from the remaining ,if available then doing the inovation</li>\n<li>If all providers are not available , rescan the list(not choosen invoker first),juding if any provider is available, if existing,doing the invocatiion.</li>\n<li>If no available provider in stage 3, then the next invoker of the invoker of stage 1 will be choosen(if not the last one),avoiding collision.</li>\n</ol>\n'},{filename:"dev/coding.md",__html:'<h1>Coding convention</h1>\n<h2>Code style</h2>\n<p>The source and JavaDoc of Dubbo follow below specifications:</p>\n<ul>\n<li><a href="http://www.oracle.com/technetwork/java/codeconvtoc-136057.html">Code Conventions for the Java Programming Language</a></li>\n<li><a href="http://www.oracle.com/technetwork/java/javase/documentation/index-137868.html">How to Write Doc Comments for the Javadoc Tool</a></li>\n</ul>\n<h2>Exception and Logging</h2>\n<ul>\n<li>Log more context information as possible, such as error reason, error server address, client address, registry center address, dubbo version and so on.</li>\n<li>Try to put the main cause at the front, and display all other context information with key-value paris after it.</li>\n<li>Log is not printed where the exception is thrown, log level is determined by the final exception handler, and must print log when dicarding exception.</li>\n<li><code>ERROR</code> log means NEED TO ALARM, <code>WARN</code> log means COULD AUTO RECOVERY, <code>INFO</code> long mean NORMAL.</li>\n<li>Suggestion: config <code>ERROR</code> log in Monitor center for real-time alarm, summary and send <code>WARN</code> log weekly.</li>\n<li><code>RpcException</code> is the ONLY external exception of Dubbo,all internal exceptions mush be transfered to <code>RpcException</code> if need to throw out to user.</li>\n<li><code>RpcException</code> CAN NOT have sub-class, all types of information are identified with ErrorCode in order to keep compatible.</li>\n</ul>\n<h2>Configuration and URL</h2>\n<ul>\n<li>Use initials and camelCase for multiple words for object properties <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>.</li>\n<li>Use lowercase and split by \'-\' for multiple words for config properties <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>.</li>\n<li>Use lowercase and split by \'.\' for multiple words for URL properties <sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup>.</li>\n<li>Use URL transfer parameters as possible, Don\'t define Map or other types, config information also transfer to URL style.</li>\n<li>Minimize URL nesting to keep URL simplicity.</li>\n</ul>\n<h2>Unit testing and integration testing</h2>\n<ul>\n<li>Use JUnit and EasyMock for unit testing, use TestNG for integration testing, use DBUnit for database testing.</li>\n<li>Don\'t put large integration test case in unit testing for running speed of unit test case.</li>\n<li>Use <code>try...finally</code> or <code>tearDown</code> to release resource for all test cases of unit testing.</li>\n<li>Minimize test case that with <code>while</code> loop which need waiting repsonse, use to make the logic in timer as function for timer and net testing.</li>\n<li>For fail-safe testing, unified use <code>LogUtil</code> assertion log output.</li>\n</ul>\n<h2>Extension point base class and AOP</h2>\n<ul>\n<li>AOP class should be named as <code>XxxWrapper</code>,Base class should be named as <code>AbstractXxx</code>.</li>\n<li>Use AOP for combine relationship between extension points, <code>ExtensionLoader</code> only loads extension points, including AOP extension.</li>\n<li>Try to use Ioc inject dependency of extension points, Don\'t direct dependent on factory method of <code>ExtensionLoader</code>.</li>\n<li>Try to use AOP implement the common action of extension points, instead of using base class, such as the <code>isAvailable</code> checking before load balancing, which is independent of load balance. Close the URL paramters which no need to check.</li>\n<li>Use base class for abstaction for a variety of similar types, such as RMI, Hessian 3rd protocols which have generated interface proxy, only transfer interface proxy to <code>Invoker</code> to complete bridging, and public base class can do the logic.</li>\n<li>The base class is also part of the SPI, and each extension should have a convenient base class support.</li>\n</ul>\n<h2>Module and packaging</h2>\n<ul>\n<li>Base on reusability for packaging, dividing the interface, base class and large implementation into separate modules.</li>\n<li>Put all interfaces under the base package of module, and put base classes in support subpackage, different implementations are placed under the subpackage named by extension point.</li>\n<li>Try to keep subpackage dependent on parent package, NOT reverse.</li>\n</ul>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>Java convention <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>Spring convention <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn3" class="footnote-item"><p>Dubbo convention <a href="#fnref3" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"dev/contract.md",__html:"<h1>Public Agreement</h1>\n<p>This document is Dubbo public agreement, we expect all extension points comply with it.</p>\n<h2>URL</h2>\n<ul>\n<li>All extension points must include URL parameter, design URL as a context information which throughouts the whole extension point design system.</li>\n<li>URL standard style: <code>protocol://username:password@host:port/path?key=value&amp;key=value</code></li>\n</ul>\n<h2>Logging</h2>\n<ul>\n<li>Print <code>ERROR</code> log for unrecoverable and NEED TO ALARM situation.</li>\n<li>Print <code>WARN</code> log for recoverable exception or transient state inconsistency.</li>\n<li>Print <code>INFO</code> log for normally status.</li>\n</ul>\n"},{filename:"dev/contribution.md",__html:"<style>\ntable {\n  width: 100%;\n  max-width: 65em;\n  border: 1px solid #dedede;\n  margin: 15px auto;\n  border-collapse: collapse;\n  empty-cells: show;\n}\ntable th,\ntable td {\n  height: 35px;\n  border: 1px solid #dedede;\n  padding: 0 10px;\n}\ntable th {\n  font-weight: bold;\n  text-align: center !important;\n  background: rgba(158,188,226,0.2);\n  white-space: nowrap;\n}\ntable tbody tr:nth-child(2n) {\n  background: rgba(158,188,226,0.12);\n}\ntable td:nth-child(1) {\n  white-space: nowrap;\n}\ntable tr:hover {\n  background: #efefef;\n}\n.table-area {\n  overflow: auto;\n}\n</style>\n<script type=\"text/javascript\">\n[].slice.call(document.querySelectorAll('table')).forEach(function(el){\n    var wrapper = document.createElement('div');\n    wrapper.className = 'table-area';\n    el.parentNode.insertBefore(wrapper, el);\n    el.parentNode.removeChild(el);\n    wrapper.appendChild(el);\n})\n<\/script>\n<h1>Contribution</h1>\n<h2>Flow</h2>\n<ul>\n<li>Direct adding new project, black-box dependent on Dubbo for function extension.</li>\n<li>Fork Dubbo on Github for BUG fixing or modify the framework.</li>\n<li>Pull Request after changing.</li>\n</ul>\n<h2>Tasks</h2>\n<table>\n<thead>\n<tr>\n<th>Funcation</th>\n<th>Type</th>\n<th>Priority</th>\n<th>Status</th>\n<th>Claimer</th>\n<th>Plan complete time</th>\n<th>progress</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><Use Guideline> Translation</td>\n<td>Document</td>\n<td>High</td>\n<td>Unclaimed</td>\n<td>Pending</td>\n<td>Pending</td>\n<td>0%</td>\n</tr>\n<tr>\n<td><Developing Guideline> translation</td>\n<td>Document</td>\n<td>High</td>\n<td>Unclaimed</td>\n<td>Pending</td>\n<td>Pending</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>Extension point compatibility testing</td>\n<td>Testing</td>\n<td>High</td>\n<td>Claimed</td>\n<td>罗立树</td>\n<td>Pending</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>Performance benchmark testing</td>\n<td>Testing</td>\n<td>High</td>\n<td>Unclaimed</td>\n<td>Pending</td>\n<td>Pending</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>Functional unit testing</td>\n<td>Testing</td>\n<td>High</td>\n<td>Unclaimed</td>\n<td>Pending</td>\n<td>Pending</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>JTA/XA distributed transaction</td>\n<td>Interceptor extension</td>\n<td>High</td>\n<td>Unclaimed</td>\n<td>Pending</td>\n<td>Pending</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>Thrift</td>\n<td>Protocol extension</td>\n<td>High</td>\n<td>Developing done</td>\n<td>闾刚</td>\n<td>2012-04-27</td>\n<td>90%</td>\n</tr>\n<tr>\n<td>ICE</td>\n<td>Protocol extension</td>\n<td>High</td>\n<td>Unclaimed</td>\n<td>Pending</td>\n<td>Pending</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>ACE</td>\n<td>Protocol extension</td>\n<td>Low</td>\n<td>Unclaimed</td>\n<td>Pending</td>\n<td>Pending</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>JSON-RPC</td>\n<td>Protocol extension</td>\n<td>Low</td>\n<td>Unclaimed</td>\n<td>Pending</td>\n<td>Pending</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>XML-RPC</td>\n<td>Protocol extension</td>\n<td>Low</td>\n<td>Unclaimed</td>\n<td>Pending</td>\n<td>Pending</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>JSR181&amp;CXF(WebService)</td>\n<td>Protocol extension</td>\n<td>High</td>\n<td>Developing done</td>\n<td>白文志</td>\n<td>2012-04-27</td>\n<td>90%</td>\n</tr>\n<tr>\n<td>JSR311&amp;JSR339(RestfulWebService)</td>\n<td>Protocol extension</td>\n<td>High</td>\n<td>Unclaimed</td>\n<td>Pending</td>\n<td>Pending</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>JMS&amp;ActiveMQ</td>\n<td>Protocol extension</td>\n<td>High</td>\n<td>Unclaimed</td>\n<td>Pending</td>\n<td>Pending</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>Protobuf</td>\n<td>Serialization extension</td>\n<td>High</td>\n<td>Researching</td>\n<td>朱启恒</td>\n<td>2012-02-30</td>\n<td>20%</td>\n</tr>\n<tr>\n<td>Avro</td>\n<td>Serialization extension</td>\n<td>Low</td>\n<td>Unclaimed</td>\n<td>Pending</td>\n<td>Pending</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>XSocket</td>\n<td>Transmission extension</td>\n<td>Low</td>\n<td>Unclaimed</td>\n<td>Pending</td>\n<td>Pending</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>CGLib</td>\n<td>Dynamic proxy extension</td>\n<td>Low</td>\n<td>Unclaimed</td>\n<td>Pending</td>\n<td>Pending</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>JNDI</td>\n<td>Registry extension</td>\n<td>High</td>\n<td>Unclaimed</td>\n<td>Pending</td>\n<td>Pending</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>LDAP</td>\n<td>Registry extension</td>\n<td>Low</td>\n<td>Unclaimed</td>\n<td>Pending</td>\n<td>Pending</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>JSR140&amp;SLP</td>\n<td>Registry extension</td>\n<td>High</td>\n<td>Unclaimed</td>\n<td>Pending</td>\n<td>Pending</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>UDDI</td>\n<td>Registry extension</td>\n<td>High</td>\n<td>Unclaimed</td>\n<td>Pending</td>\n<td>Pending</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>JMX</td>\n<td>Monitoring expansion</td>\n<td>High</td>\n<td>Unclaimed</td>\n<td>Pending</td>\n<td>Pending</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>SNMP</td>\n<td>Monitoring expansion</td>\n<td>High</td>\n<td>Unclaimed</td>\n<td>Pending</td>\n<td>Pending</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>Cacti</td>\n<td>Monitoring expansion</td>\n<td>High</td>\n<td>Unclaimed</td>\n<td>Pending</td>\n<td>Pending</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>Nagios</td>\n<td>Monitoring expansion</td>\n<td>High</td>\n<td>Unclaimed</td>\n<td>Pending</td>\n<td>Pending</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>Logstash</td>\n<td>Monitoring expansion</td>\n<td>High</td>\n<td>Unclaimed</td>\n<td>Pending</td>\n<td>Pending</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>JRobin</td>\n<td>Monitoring expansion</td>\n<td>High</td>\n<td>Unclaimed</td>\n<td>Pending</td>\n<td>Pending</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>Maven</td>\n<td>Package management</td>\n<td>Low</td>\n<td>Unclaimed</td>\n<td>Pending</td>\n<td>Pending</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>Subversion</td>\n<td>Package management</td>\n<td>Low</td>\n<td>Unclaimed</td>\n<td>Pending</td>\n<td>Pending</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>JCR/JSR283</td>\n<td>Package management</td>\n<td>Low</td>\n<td>Unclaimed</td>\n<td>Pending</td>\n<td>Pending</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>SimpleDeployer</td>\n<td>Local deployment agent</td>\n<td>Low</td>\n<td>Unclaimed</td>\n<td>Pending</td>\n<td>Pending</td>\n<td>0%</td>\n</tr>\n<tr>\n<td>SimpleScheduler</td>\n<td>Scheduler</td>\n<td>Low</td>\n<td>Unclaimed</td>\n<td>Pending</td>\n<td>Pending</td>\n<td>0%</td>\n</tr>\n</tbody>\n</table>\n"},{filename:"dev/design.md",__html:'<h1>Framework Design</h1>\n<h2>Overall design</h2>\n<p><img src="sources/images/dubbo-framework.jpg" alt="/dev-guide/images/dubbo-framework.jpg"></p>\n<p>Image description:</p>\n<ul>\n<li>Left area with light blue background shows service consumer interfaces, Right area with light green background shows service provider interfaces, center area shows both side interfaces.</li>\n<li>The image is divided into 10 layers from the bottom to the top, and the layers are one-way dependence. The black arrow on the right represents the dependency between layers, and each layer can be stripped from the upper layer to be reused, the Service and Config layers are API, and the other layers are SPI.</li>\n<li>Green boxes are extension interfaces, blue boxes are implementation classes, image only shows implementation class of associated layers.</li>\n<li>The blue dashed line is the initialization process, which is assembly chain when starting, red line for the method call process, which is calling chain when running, purple triangle arrow is inherited, can treat subclass as the same node of parent class, text of lines are the method invocation.</li>\n</ul>\n<h2>Layer description</h2>\n<ul>\n<li><strong>config layer</strong>: external config interface, <code>ServiceConfig</code> and <code>ReferenceConfig</code> is the center of the layer, you can directly initialize config class, also can generate config class by spring.</li>\n<li><strong>proxy layer</strong>: transparent proxy of service interface, generate client Stub of service and server Skeletion of service, <code>ServiceProxy</code> is the center, extension interface is <code>ProxyFactory</code>.</li>\n<li><strong>registry layer</strong>: encapsulation of service registry and discovery, service URL is the center, extension interfaces are <code>RegistryFactory</code>, <code>Registry</code> and <code>RegistryService</code>.</li>\n<li><strong>cluster layer</strong>: encapsulation of cluster of muliple providers and load balance, and bridging registration center, <code>Invoker</code> is the center, extension interfaces are <code>Cluster</code>, <code>Directory</code>, <code>Router</code>, <code>LoadBalance</code>.</li>\n<li><strong>monitor layer</strong>: monitor of RPC call times and call execute time, <code>Statistics</code> is the center, extension interface are <code>MonitorFactory</code>, <code>Monitor</code>, <code>MonitorService</code></li>\n<li><strong>protocol layer</strong>: encapsulation of RPC, <code>Invocation</code> and <code>Result</code> are the center, extension interfaces are <code>Protocol</code>, <code>Invoker</code>, <code>Exporter</code></li>\n<li><strong>exchange layer</strong>: encapsulation of request and response, synchronous transfer asynchronous, <code>Request</code> and <code>Response</code> are the center, extension interfaces are <code>Exchanger</code>, <code>ExchangeChannel</code>, <code>ExchangeClient</code>, <code>ExchangeServer</code></li>\n<li><strong>transport layer</strong>: abstraction of mina and netty, <code>Message</code> is the center, extension interfaces are <code>Channel</code>, <code>Transporter</code>, <code>Client</code>, <code>Server</code>, <code>Codec</code></li>\n<li><strong>serialize layer</strong>: reusable tools, extension interfaces are <code>Serialization</code>, <code>ObjectInput</code>, <code>ObjectOutput</code>, <code>ThreadPool</code></li>\n</ul>\n<h2>Relationship description</h2>\n<ul>\n<li>In RPC, Protocol is the core layer, it means that you can complete RPC calling by Protocol + Invoker + Exporter, then filter at the main process of Invoker.</li>\n<li>Consumer and Provider are abstraction concepts, just want you have a more intuitive understanding of which classes belong to the client and server side, the reason not use Client and Server is that Dubbo uses Provider, Consumer, Registry, Monitor divide logical topology node in many scenes, keep the concept of unity.</li>\n<li>Cluster is external concept, the purpose of Cluster is that make various Invoker disguise to one Invoker, so that we just pay attention to the Invoker in Protocol layer, adding Cluster or removing Cluster will not affect other layers, because we don\'t need Cluster when only have one provider.</li>\n<li>The Proxy layer encapsulates the transparent proxy for all interfaces, and in other layers with Invoker as the center, turn Invoker into interface, or turn interface implementation into Invoker by Proxy only when exposuring to user. RPC still work even removing Proxy layer, but not so transparent, making remote service calling don\'t look like local service calling.</li>\n<li>Remoting is the implemetation of Dubbo protocols, you can remove Remoting if choosing RMI. The Remoting is divided into Transport layer and Exchange layer, Transport layer is responsible for one-way message transmission, it\'s abstraction of Mina, Netty, Grizzly, it also can extend UDP transmission. The Exchange layer encapsulates the Request-Response semantics over the transport layer.</li>\n<li>Actually Registry and Monitor are not at the same layer, they are independent nodes, draw them together by layer just for global view.</li>\n</ul>\n<h2>Modules packaging</h2>\n<p><img src="sources/images/dubbo-modules.jpg" alt="/dev-guide/images/dubbo-modules.jpg"></p>\n<p>Modules description:</p>\n<ul>\n<li><strong>dubbo-common module</strong>: includes Util classes and common modules.</li>\n<li><strong>dubbo-remoting module</strong>: is Dubbo protocol implementation, no need to use this module if using RMI for RPC.</li>\n<li><strong>dubbo-rpc module</strong>: abstraction of various protocols, and dynamic proxy, only one to one call, not concerned with the management of cluster.</li>\n<li><strong>dubbo-cluster module</strong>: disguise many service providers as one provider, including load balancing, fault tolerance, routing, etc. the address list of clusters can be either static or send by registry.</li>\n<li><strong>dubbo-registry module</strong>: Based on the cluster of registry send address and the abstraction of various registry centers.</li>\n<li><strong>dubbo-monitor module</strong>: statistics of service call times, call time, call chain tracked services.</li>\n<li><strong>dubbo-config module</strong>: is Dubbo external API, users use Dubbo by Config, hide Dubbo details.</li>\n<li><strong>dubbo-container module</strong>: is a Standlone container, just use Main method to load Spring, because usually service no need Tomcat/JBoss features.</li>\n</ul>\n<p>Package dividing according to the layer structure, and the difference with layer dividing:</p>\n<ul>\n<li>container is service container, for service running deployment, not showed in the image.</li>\n<li>protocol layer and proxy layer are placed in RPC module, they are the core of RPC module, you can use these 2 layers complete RPC call when only have 1 provider.</li>\n<li>transport layer and exchange layer are placed in remoting module, for RPC call base comminucation.</li>\n<li>serialize layer is placed in common module, for reuse.</li>\n</ul>\n<h2>Dependence relationship</h2>\n<p><img src="sources/images/dubbo-relation.jpg" alt="/dev-guide/images/dubbo-relation.jpg"></p>\n<p>Image description:</p>\n<ul>\n<li>The boxes in image, Protocol, Cluster, Proxy, Service, Container, Registry, Monitor represent layer or module, the blues mean interactive with business, the greens mean only internal interactive with Dubbo.</li>\n<li>The backgroud boxes in image, Consumer, Provider, Registry, Monitor represent deployment logical topology node.</li>\n<li>The blue dashed line in the image is called for initialization, the red dashed line is called asynchronously for runtime, and the red line is called synchronously for runtime.</li>\n<li>The image only includes RPC layer, don\'t includes Remoting layer, the whole Remoting hides in Protocol layer.</li>\n</ul>\n<h2>Call chain</h2>\n<p>Expand the red call chain of the overall design map:</p>\n<p><img src="sources/images/dubbo-extension.jpg" alt="/dev-guide/images/dubbo-extension.jpg"></p>\n<h2>Expose service sequence</h2>\n<p>Expand the initialization chain of service provider exposes service which at the left of the overall design map, the sequence diagram shows below:</p>\n<p><img src="sources/images/dubbo-export.jpg" alt="/dev-guide/images/dubbo-export.jpg"></p>\n<h2>Reference service sequence</h2>\n<p>Expand the initialization chain of service consumer references service which at the right of the overall design map, the sequence diagram shows below:</p>\n<p><img src="sources/images/dubbo-refer.jpg" alt="/dev-guide/images/dubbo-refer.jpg"></p>\n<h2>Domain model</h2>\n<p>The core domain models of Dubbo:</p>\n<ul>\n<li>Protocol is service domain, it is the main function entrance of Invoker exposure and reference, it is responsible for the life cycle management of Invoker.</li>\n<li>Invoker is entity domain, it is the core model of Dubbo, all the other models are disturbed, or converted to it, it represents an executable, you can call it by calling invoke, it can be a local implementation, a remote implementation, or a cluster implementation.</li>\n<li>Invocation is session domain, it holds variables in the calling process, such as the method name, the parameters, and so on.</li>\n</ul>\n<h2>Base design principle</h2>\n<ul>\n<li>Use Microkernel + Plugin design pattern,Microkernel only responsible for assembly Plugin, the functions of Dubbo are implemented by extension points, it means that all functions of Dubbo can be replaced by self defined extension by user.</li>\n<li>Use URL to be the startdard format of config information, all extension points transfer config information by URL.</li>\n</ul>\n<p>More design principles refer to: <a href="./principals/introduction.md">Framework design principle</a></p>\n'},{filename:"dev/implementation.md",__html:'<h1>Implementation details</h1>\n<h2>Initialization details</h2>\n<h3>Service parsing</h3>\n<p>Based on <code>META-INF/spring.handlers</code> config in dubbo.jar, Spring calls <code>DubboNamespaceHandler</code> when meeting dubbo namespace.</p>\n<p>All Dubbo tags are parsed by <code>DubboBeanDefinitionParser</code>, based on one to one attribute mapping, the XML label is parsed as a Bean object.</p>\n<p>Transfer Bean object to URL, and transfer all attributes of Bean to URL parameters when <code>ServiceConfig.export()</code> or <code>ReferenceConfig.get()</code> initialization.</p>\n<p>Then pase URL to <a href="./impls/protocol.md">Protocol extension point</a>, based on <a href="./SPI.md">Extension point adaptive mechanism</a> of extension point, processing service exposure or reference for different protocols according to URL protocol header.</p>\n<h3>Service Exposure</h3>\n<h4>1. Only expose service port:</h4>\n<p>Direct exposing to provider when have not Registry, <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>, the URL format which parsing by <code>ServiceConfig</code>:\n<code>dubbo://service-host/com.foo.FooService?version=1.0.0</code>.</p>\n<p>Based on extension point adaptive mechanism, call <code>export()</code> method of <code>DubboProtocol</code> and open server port by identifying <code>dubbo://</code> protocol header of URL.</p>\n<h4>2. Expose to Registry:</h4>\n<p>Expose provider address to Registry <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>, the URL format which parsing by <code>ServiceConfig</code>: <code>registry://registry-host/com.alibaba.dubbo.registry.RegistryService?export=URL.encode(&quot;dubbo://service-host/com.foo.FooService?version=1.0.0&quot;)</code>,</p>\n<p>Based on extension point adaptive mechanism, call <code>export()</code> method of <code>RegistryProtocol</code> by identifying  <code>registry://</code> protocol header, register the provider URL parameter of <code>export</code> to Registry.</p>\n<p>Resend to <code>Protocol</code> extension point to do exposure: <code>dubbo://service-host/com.foo.FooService?version=1.0.0</code>, then based on extension point adaptive mechanism, call <code>export()</code> method of <code>DubboProtocol</code> and open server port by identifying <code>dubbo://</code> protocol header of provider URL.</p>\n<h3>Service Reference</h3>\n<h4>1. Direct connect service</h4>\n<p>Direct connect provider when have not Registry <sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup>, the URL format which parsing by <code>ReferenceConfig</code>: <code>dubbo://service-host/com.foo.FooService?version=1.0.0</code>.</p>\n<p>Based on extension point adaptive mechanism, call <code>refer()</code> method of <code>DubboProtocol</code> by identifying <code>dubbo://</code> protocol header of URL, and return provider reference.</p>\n<h4>2. Service Registry discovery</h4>\n<p>Discover provider address by Registry <sup class="footnote-ref"><a href="#fn4" id="fnref4">[4]</a></sup>, the URL format which parsing by <code>ReferenceConfig</code>:\n<code>registry://registry-host/com.alibaba.dubbo.registry.RegistryService?refer=URL.encode(&quot;consumer://consumer-host/com.foo.FooService?version=1.0.0&quot;)</code>.</p>\n<p>Based on extension point adaptive mechanism, call <code>refer()</code> method of <code>RegistryProtocol</code> by identifying <code>registry://</code> protocol header of URL, then based on the condition of parameter of <code>refer</code> to search provider URL, for example: <code>dubbo://service-host/com.foo.FooService?version=1.0.0</code>.</p>\n<p>Then based on extension point adaptive mechanism, call <code>refer()</code> method of <code>DubboProtocol</code> to get provider reference by identifying <code>dubbo://</code> protocol header of provider URL.</p>\n<p>Then <code>RegistryProtocol</code> disguise various provider references to single provider by <code>Cluster</code> extension point and return.</p>\n<h3>Service Filter</h3>\n<p>Based on extension point adaptive mechanism, all <code>Protocol</code> extension points are auto wrapped <code>Wrapper</code> class.</p>\n<p>Based on <code>ProtocolFilterWrapper</code> class, make all <code>Filter</code> as chain, call the real reference at the end of the chain.</p>\n<p>Based on <code>ProtocolListenerWrapper</code> class, make all <code>InvokerListener</code> and <code>ExporterListener</code> as list, perform call back before and after exposure and reference.</p>\n<p>All additional functions would be implementated by <code>Filter</code>, including Monitor.</p>\n<h2>RPC details</h2>\n<h3>The detail process of exposing service by service provider</h3>\n<p><img src="sources/images/dubbo_rpc_export.jpg" alt="/dev-guide/images/dubbo_rpc_export.jpg"></p>\n<p>The above image shows the main process of exposing service by service provider:</p>\n<p>First <code>ServiceConfig</code> class get the actual class <code>ref</code> that provides service(e.g. 如:HelloWorldImpl), then generating a <code>AbstractProxyInvoker</code> instance by the <code>getInvoker</code> method of <code>ProxyFactory</code> class, to this step, complete the transformation of specific service to <code>Invoker</code>, next is the process of converting <code>Invoker</code> to <code>Exporter</code>.</p>\n<p>The key of Dubbo processing service exposure is the process of converting <code>Invoker</code> to <code>Exporter</code>, the red part in the above image. Here we introduce the implementation of the two typical protocols, Dubbo and RMI:</p>\n<h4>Dubbo implementation</h4>\n<p>The transformation from <code>Invoker</code> of Dubbo protocol to <code>Exporter</code> takes place in the <code>export</code> method of <code>DubboProtocol</code> class, it mainly opens the socket to listen service and receive all kinds of requests sent by the client, and the communication details are implementated by Dubbo itself.</p>\n<h4>RMI implementation</h4>\n<p>The transformation from <code>Invoker</code> of RMI protocol to <code>Exporter</code> takes place in the <code>export</code> method of <code>RmiProtocol</code> class, the RMI service is implementated by Spring, Dubbo or JDK, and the communication details are implementated by JDK, which saves a lot of work.</p>\n<h3>The detail process of serving service for service consumer</h3>\n<p><img src="sources/images/dubbo_rpc_refer.jpg" alt="/dev-guide/images/dubbo_rpc_refer.jpg"></p>\n<p>The above image is the main process of service consumption:</p>\n<p>First, the <code>init</code> method of <code>ReferenceConfig</code> class calls the <code>refer</code> method of <code>Protocol</code> to generate <code>Invoker</code> instance(such as the red part in the above image), which is the key of service consumption. Then the <code>Invoker</code> is converted to the interface required by the client (such as: HelloWorld).</p>\n<p>For each protocol such as RMI/Dubbo/Web service, the details they call <code>refer</code> method generate <code>Invoker</code> instance are similar to the previous section.</p>\n<h3>Invoker everywhere</h3>\n<p>Because of <code>Invoker</code> is a very important concept in the Dubbo domain model, many of the design ideas are close to it. This makes <code>Invoker</code> permeate the entire implementation code, and it\'s really easy to mix up for people who have just started Dubbo.</p>\n<p>Let\'s use a simple image below to describe the 2 important <code>Invoker</code>: service provider <code>Invoker</code> and service consumer <code>Invoker</code>:</p>\n<p><img src="sources/images/dubbo_rpc_invoke.jpg" alt="/dev-guide/images/dubbo_rpc_invoke.jpg"></p>\n<p>To better explain the above image, we provide the below code examples of service consumption and providers:</p>\n<p>Service consumer code:</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">DemoClientAction</span> </span>{\n \n    <span class="hljs-keyword">private</span> DemoService demoService;\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setDemoService</span><span class="hljs-params">(DemoService demoService)</span> </span>{\n        <span class="hljs-keyword">this</span>.demoService = demoService;\n    }\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">start</span><span class="hljs-params">()</span> </span>{\n        String hello = demoService.sayHello(<span class="hljs-string">"world"</span> + i);\n    }\n}\n</code></pre>\n<p>The <code>DemoService</code> in above code is the proxy of service consumer in above image, user can call <code>Invoker</code> <sup class="footnote-ref"><a href="#fn5" id="fnref5">[5]</a></sup> which implementate the real RPC by the proxy.</p>\n<p>Service provider code:</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">DemoServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">DemoService</span> </span>{\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">sayHello</span><span class="hljs-params">(String name)</span> <span class="hljs-keyword">throws</span> RemoteException </span>{\n        <span class="hljs-keyword">return</span> <span class="hljs-string">"Hello "</span> + name;\n    }\n}\n</code></pre>\n<p>The above class would be encapsulated to be a <code>AbstractProxyInvoker</code> instance, and create a new <code>Exporter</code> instance, then find corresponding <code>Exporter</code> instance and call its corresponding <code>AbstractProxyInvoker</code> instance when network communication layer recieve request, so that real call service provider code. There are some other <code>Invoker</code> classes, but the above 2 are the most important.</p>\n<h2>Remote communication details</h2>\n<h3>Protocol header agreement</h3>\n<p><img src="sources/images/dubbo_protocol_header.jpg" alt="/dev-guide/images/dubbo_protocol_header.jpg"></p>\n<h3>Thread dispatch model</h3>\n<p><img src="sources/images/dubbo-protocol.jpg" alt="/dev-guide/images/dubbo-protocol.jpg"></p>\n<ul>\n<li>Dispather: <code>all</code>, <code>direct</code>, <code>message</code>, <code>execution</code>, <code>connection</code></li>\n<li>ThreadPool: <code>fixed</code>, <code>cached</code></li>\n</ul>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>is <code>&lt;dubbo:service regisrty=&quot;N/A&quot; /&gt;</code> or <code>&lt;dubbo:registry address=&quot;N/A&quot; /&gt;</code> <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>is <code>&lt;dubbo:registry address=&quot;zookeeper://10.20.153.10:2181&quot; /&gt;</code> <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn3" class="footnote-item"><p>is <code>&lt;dubbo:reference url=&quot;dubbo://service-host/com.foo.FooService?version=1.0.0&quot; /&gt;</code> <a href="#fnref3" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn4" class="footnote-item"><p>is <code>&lt;dubbo:registry address=&quot;zookeeper://10.20.153.10:2181&quot; /&gt;</code> <a href="#fnref4" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn5" class="footnote-item"><p>is one of <code>DubboInvoker</code>, <code>HessianRpcInvoker</code>, <code>InjvmInvoker</code>, <code>RmiInvoker</code>, <code>WebServiceInvoker</code> <a href="#fnref5" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"dev/impls/cache.md",__html:'<h1>Cache Extension</h1>\n<h2>Summary</h2>\n<p>Cache the return value, use request parameter as the key.</p>\n<h2>Extension Interface</h2>\n<p><code>com.alibaba.dubbo.cache.CacheFactory</code></p>\n<h2>Extension Configuration</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">cache</span>=<span class="hljs-string">"lru"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- method level cache --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:method</span> <span class="hljs-attr">cache</span>=<span class="hljs-string">"lru"</span> /&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:service</span>&gt;</span> \n<span class="hljs-comment">&lt;!-- 缺省值设置,当&lt;dubbo:service&gt;没有配置cache属性时,使用此配置 --&gt;</span>\n<span class="hljs-comment">&lt;!-- default configuration, will take affect if cache attribute isn\'t configured in &lt;dubbo:service&gt; --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">cache</span>=<span class="hljs-string">"xxx,yyy"</span> /&gt;</span> \n</code></pre>\n<h2>Existing Extensions</h2>\n<ul>\n<li><code>com.alibaba.dubbo.cache.support.lru.LruCacheFactory</code></li>\n<li><code>com.alibaba.dubbo.cache.support.threadlocal.ThreadLocalCacheFactory</code></li>\n<li><code>com.alibaba.dubbo.cache.support.jcache.JCacheFactory</code></li>\n</ul>\n<h2>Extension Guide</h2>\n<p>Directory layout:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxCacheFactory.java (CacheFactory implementation)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.cache.CacheFactory (plain text file with contents: xxx=com.xxx.XxxCacheFactory)\n</code></pre>\n<p>XxxCacheFactory.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.cache.CacheFactory;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxCacheFactory</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">CacheFactory</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Cache <span class="hljs-title">getCache</span><span class="hljs-params">(URL url, String name)</span> </span>{\n        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> XxxCache(url, name);\n    }\n}\n</code></pre>\n<p>XxxCacheFactory.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.cache.Cache;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxCache</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Cache</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Cache</span><span class="hljs-params">(URL url, String name)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">put</span><span class="hljs-params">(Object key, Object value)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Object <span class="hljs-title">get</span><span class="hljs-params">(Object key)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.cache.CacheFactory:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxCacheFactory\n</code></pre>\n'},{filename:"dev/impls/cluster.md",__html:'<h1>Cluster Extension</h1>\n<h2>Summary</h2>\n<p>Group service providers in a cluster, and treat them as one single provider.</p>\n<h2>Extension Interface</h2>\n<p><code>com.alibaba.dubbo.rpc.cluster.Cluster</code></p>\n<h2>Extension Configuration</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">cluster</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- default configuration, will take affect if cluster attribute is not configured in &lt;dubbo:protocol&gt;  --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">cluster</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n</code></pre>\n<h2>Existing Extensions</h2>\n<ul>\n<li><code>com.alibaba.dubbo.rpc.cluster.support.FailoverCluster</code></li>\n<li><code>com.alibaba.dubbo.rpc.cluster.support.FailfastCluster</code></li>\n<li><code>com.alibaba.dubbo.rpc.cluster.support.FailsafeCluster</code></li>\n<li><code>com.alibaba.dubbo.rpc.cluster.support.FailbackCluster</code></li>\n<li><code>com.alibaba.dubbo.rpc.cluster.support.ForkingCluster</code></li>\n<li><code>com.alibaba.dubbo.rpc.cluster.support.AvailableCluster</code></li>\n</ul>\n<h2>Extension Guide</h2>\n<p>Directory layout:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxCluster.java (Cluster implementation)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.rpc.cluster.Cluster (plain text file with the content: xxx=com.xxx.XxxCluster)\n</code></pre>\n<p>XxxCluster.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.cluster.Cluster;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.cluster.support.AbstractClusterInvoker;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.cluster.Directory;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.cluster.LoadBalance;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Invoker;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Invocation;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Result;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.RpcException;\n \n<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>{\n    <span class="hljs-keyword">public</span> &lt;T&gt; <span class="hljs-function">Invoker&lt;T&gt; <span class="hljs-title">merge</span><span class="hljs-params">(Directory&lt;T&gt; directory)</span> <span class="hljs-keyword">throws</span> RpcException </span>{\n        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> AbstractClusterInvoker&lt;T&gt;(directory) {\n            <span class="hljs-function"><span class="hljs-keyword">public</span> Result <span class="hljs-title">doInvoke</span><span class="hljs-params">(Invocation invocation, List&lt;Invoker&lt;T&gt;&gt; invokers, LoadBalance loadbalance)</span> <span class="hljs-keyword">throws</span> RpcException </span>{\n                <span class="hljs-comment">// ...</span>\n            }\n        };\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.Cluster:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxCluster\n</code></pre>\n'},{filename:"dev/impls/compiler.md",__html:'<h1>Compiler Extension</h1>\n<h2>Summary</h2>\n<p>Java compiler, used for byte code dynamic generation for RPC invocation.</p>\n<h2>Extension Interface</h2>\n<p><code>com.alibaba.dubbo.common.compiler.Compiler</code></p>\n<h2>Extension Configuration</h2>\n<p>No configuration required, the extension will be automatically discovered and loaded.</p>\n<h2>Existing Extensions</h2>\n<ul>\n<li><code>com.alibaba.dubbo.common.compiler.support.JdkCompiler</code></li>\n<li><code>com.alibaba.dubbo.common.compiler.support.JavassistCompiler</code></li>\n</ul>\n<h2>Extension Guide</h2>\n<p>Directory layout:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxCompiler.java (Compiler implementation)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.common.compiler.Compiler (plain text file with the content: xxx=com.xxx.XxxCompiler)\n</code></pre>\n<p>XxxCompiler.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.common.compiler.Compiler;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxCompiler</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Compiler</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Object <span class="hljs-title">getExtension</span><span class="hljs-params">(Class&lt;?&gt; type, String name)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.common.compiler.Compiler:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxCompiler\n</code></pre>\n'},{filename:"dev/impls/container.md",__html:'<h1>Container Extension</h1>\n<h2>Summary</h2>\n<p>Service container extension, useful for loading custom contents.</p>\n<h2>Extension Interface</h2>\n<p><code>com.alibaba.dubbo.container.Container</code></p>\n<h2>Extension Configuration</h2>\n<pre><code class="language-sh">java com.alibaba.dubbo.container.Main spring jetty log4j\n</code></pre>\n<h2>Existing Extensions</h2>\n<ul>\n<li><code>com.alibaba.dubbo.container.spring.SpringContainer</code></li>\n<li><code>com.alibaba.dubbo.container.spring.JettyContainer</code></li>\n<li><code>com.alibaba.dubbo.container.spring.Log4jContainer</code></li>\n</ul>\n<h2>Extension Guide</h2>\n<p>Directory layout:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxContainer.java (Container implementation)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.container.Container (plain text file with the content: xxx=com.xxx.XxxContainer)\n</code></pre>\n<p>XxxContainer.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \ncom.alibaba.dubbo.container.Container;\n \n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxContainer</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Container</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Status <span class="hljs-title">start</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Status <span class="hljs-title">stop</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.container.Container:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxContainer\n</code></pre>\n'},{filename:"dev/impls/dispatcher.md",__html:'<h1>Dispatcher Extension</h1>\n<h2>Summary</h2>\n<p>Thread pool dispatch strategy.</p>\n<h2>Extension Interface</h2>\n<p><code>com.alibaba.dubbo.remoting.Dispatcher</code></p>\n<h2>Extension Configuration</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">dispatcher</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- default configuration, will take effect if dispatcher attribute is not set in &lt;dubbo:protocol&gt; --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">dispatcher</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n</code></pre>\n<h2>Existing Extensions</h2>\n<ul>\n<li><code>com.alibaba.dubbo.remoting.transport.dispatcher.all.AllDispatcher</code></li>\n<li><code>com.alibaba.dubbo.remoting.transport.dispatcher.direct.DirectDispatcher</code></li>\n<li><code>com.alibaba.dubbo.remoting.transport.dispatcher.message.MessageOnlyDispatcher</code></li>\n<li><code>com.alibaba.dubbo.remoting.transport.dispatcher.execution.ExecutionDispatcher</code></li>\n<li><code>com.alibaba.dubbo.remoting.transport.dispatcher.connection.ConnectionOrderedDispatcher</code></li>\n</ul>\n<h2>Extension Guide</h2>\n<p>Directory layout:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxDispatcher.java (Dispatcher implementation)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.remoting.Dispatcher (plain text file with the content: xxx=com.xxx.XxxDispatcher)\n</code></pre>\n<p>XxxDispatcher.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.remoting.Dispatcher;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxDispatcher</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Dispatcher</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Group <span class="hljs-title">lookup</span><span class="hljs-params">(URL url)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.remoting.Dispatcher:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxDispatcher\n</code></pre>\n'},{filename:"dev/impls/exchanger.md",__html:'<h1>Exchanger Extension</h1>\n<h2>Summary</h2>\n<p>Exchange message between request and response on network transport layer.</p>\n<h2>Extension Interface</h2>\n<ul>\n<li><code>com.alibaba.dubbo.remoting.exchange.Exchanger</code></li>\n<li><code>com.alibaba.dubbo.remoting.exchange.ExchangeServer</code></li>\n<li><code>com.alibaba.dubbo.remoting.exchange.ExchangeClient</code></li>\n</ul>\n<h2>Extension Configuration</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">exchanger</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- default configuration, will take effect if exchanger attribute is not set in &lt;dubbo:protocol&gt; --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">exchanger</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n</code></pre>\n<h2>Existing Extension</h2>\n<p><code>com.alibaba.dubbo.remoting.exchange.exchanger.HeaderExchanger</code></p>\n<h2>Extension Guide</h2>\n<p>Directory layout:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxExchanger.java (Exchanger implementation)\n                |-XxxExchangeServer.java (ExchangeServer implementation)\n                |-XxxExchangeClient.java (ExchangeClient implementation)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.remoting.exchange.Exchanger (plain text file with the content: xxx=com.xxx.XxxExchanger)\n</code></pre>\n<p>XxxExchanger.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.remoting.exchange.Exchanger;\n \n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxExchanger</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Exchanger</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> ExchangeServer <span class="hljs-title">bind</span><span class="hljs-params">(URL url, ExchangeHandler handler)</span> <span class="hljs-keyword">throws</span> RemotingException </span>{\n        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> XxxExchangeServer(url, handler);\n    }\n    <span class="hljs-function"><span class="hljs-keyword">public</span> ExchangeClient <span class="hljs-title">connect</span><span class="hljs-params">(URL url, ExchangeHandler handler)</span> <span class="hljs-keyword">throws</span> RemotingException </span>{\n        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> XxxExchangeClient(url, handler);\n    }\n}\n</code></pre>\n<p>XxxExchangeServer.java:</p>\n<pre><code class="language-java">\n<span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.remoting.exchange.ExchangeServer;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxExchangeServer</span> <span class="hljs-title">impelements</span> <span class="hljs-title">ExchangeServer</span> </span>{\n    <span class="hljs-comment">// ...</span>\n}\n</code></pre>\n<p>XxxExchangeClient.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.remoting.exchange.ExchangeClient;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxExchangeClient</span> <span class="hljs-title">impelments</span> <span class="hljs-title">ExchangeClient</span> </span>{\n    <span class="hljs-comment">// ...</span>\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.remoting.exchange.Exchanger:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxExchanger\n</code></pre>\n'},{filename:"dev/impls/exporter-listener.md",__html:'<h1>ExporterListener Extension</h1>\n<h2>Summary</h2>\n<p>Fire events when there\'s any service exported.</p>\n<h2>Extension Interface</h2>\n<p><code>com.alibaba.dubbo.rpc.ExporterListener</code></p>\n<h2>Extension Configuration</h2>\n<pre><code class="language-xml"><span class="hljs-comment">&lt;!-- service exporter listener --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">listener</span>=<span class="hljs-string">"xxx,yyy"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- default exporter listener for service provider --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">listener</span>=<span class="hljs-string">"xxx,yyy"</span> /&gt;</span>\n</code></pre>\n<h2>Existing Extension</h2>\n<p><code>com.alibaba.dubbo.registry.directory.RegistryExporterListener</code></p>\n<h2>Extension Guide</h2>\n<p>Directory layout:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxExporterListener.java (ExporterListener implementation)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.rpc.ExporterListener (plain text file with the content: xxx=com.xxx.XxxExporterListener)\n</code></pre>\n<p>XxxExporterListener.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.ExporterListener;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Exporter;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.RpcException;\n \n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxExporterListener</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">ExporterListener</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">exported</span><span class="hljs-params">(Exporter&lt;?&gt; exporter)</span> <span class="hljs-keyword">throws</span> RpcException </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">unexported</span><span class="hljs-params">(Exporter&lt;?&gt; exporter)</span> <span class="hljs-keyword">throws</span> RpcException </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.rpc.ExporterListener:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxExporterListener\n</code></pre>\n'},{filename:"dev/impls/extension-factory.md",__html:'<h1>ExtensionFactory Extension</h1>\n<h2>Summary</h2>\n<p>Factory to load dubbo extensions.</p>\n<h2>Extension Interface</h2>\n<p><code>com.alibaba.dubbo.common.extension.ExtensionFactory</code></p>\n<h2>Extension Configuration</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:application</span> <span class="hljs-attr">compiler</span>=<span class="hljs-string">"jdk"</span> /&gt;</span>\n</code></pre>\n<h2>Existing Extension</h2>\n<ul>\n<li><code>com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory</code></li>\n<li><code>com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory</code></li>\n</ul>\n<h2>Extension Guide</h2>\n<p>Directory layout:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxExtensionFactory.java (ExtensionFactory implementation)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.common.extension.ExtensionFactory (plain text file with the content: xxx=com.xxx.XxxExtensionFactory)\n</code></pre>\n<p>XxxExtensionFactory.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.common.extension.ExtensionFactory;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxExtensionFactory</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">ExtensionFactory</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Object <span class="hljs-title">getExtension</span><span class="hljs-params">(Class&lt;?&gt; type, String name)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.common.extension.ExtensionFactory:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxExtensionFactory\n</code></pre>\n'},{filename:"dev/impls/filter.md",__html:'<h1>Filter Extension</h1>\n<h2>Summary</h2>\n<p>Extension for intercepting the invocation for both service provider and consumer, furthermore, most of functions in dubbo are implemented base on the same mechanism. Since every time when remote method is invoked, the filter extensions will be executed too, the corresponding penalty should be considered before more filters are added.</p>\n<p>Contract:</p>\n<ul>\n<li>User defined filters are executed after built-in filters by default.</li>\n<li>Special value <code>default</code> is introduced to represent the default extension location. For example: for <code>filter=&quot;xxx,default,yyy&quot;</code>, <code>xxx</code> is before default filter, and <code>yyy</code> is after the default filter.</li>\n<li>Special value <code>-</code> means delete. For example: <code>filter=&quot;-foo1&quot;</code> excludes <code>foo1</code> extension. For example, <code>filter=&quot;-default&quot;</code> exclues all default filters.</li>\n<li>When provider and service have filter configured at the same moment, all filters are accumulated together instead of override, for example: for <code>&lt;dubbo:provider filter=&quot;xxx,yyy&quot;/&gt;</code> and <code>&lt;dubbo:service filter=&quot;aaa,bbb&quot; /&gt;</code>,<code>xxx</code>, <code>yyy</code>, <code>aaa</code>, <code>bbb</code> are all count as filters. In order to change to override, use: <code>&lt;dubbo:service filter=&quot;-xxx,-yyy,aaa,bbb&quot; /&gt;</code></li>\n</ul>\n<h2>Extension Interface</h2>\n<p><code>com.alibaba.dubbo.rpc.Filter</code></p>\n<h2>Extension Configuration</h2>\n<pre><code class="language-xml"><span class="hljs-comment">&lt;!-- filter for consumer --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">filter</span>=<span class="hljs-string">"xxx,yyy"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- default filter configuration for the consumer, will intercept for all references --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:consumer</span> <span class="hljs-attr">filter</span>=<span class="hljs-string">"xxx,yyy"</span>/&gt;</span>\n<span class="hljs-comment">&lt;!-- filter for provider --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">filter</span>=<span class="hljs-string">"xxx,yyy"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- default filter configuration for the provider, will intercept for all services --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">filter</span>=<span class="hljs-string">"xxx,yyy"</span>/&gt;</span>\n</code></pre>\n<h2>Existing Extension</h2>\n<ul>\n<li><code>com.alibaba.dubbo.rpc.filter.EchoFilter</code></li>\n<li><code>com.alibaba.dubbo.rpc.filter.GenericFilter</code></li>\n<li><code>com.alibaba.dubbo.rpc.filter.GenericImplFilter</code></li>\n<li><code>com.alibaba.dubbo.rpc.filter.TokenFilter</code></li>\n<li><code>com.alibaba.dubbo.rpc.filter.AccessLogFilter</code></li>\n<li><code>com.alibaba.dubbo.rpc.filter.CountFilter</code></li>\n<li><code>com.alibaba.dubbo.rpc.filter.ActiveLimitFilter</code></li>\n<li><code>com.alibaba.dubbo.rpc.filter.ClassLoaderFilter</code></li>\n<li><code>com.alibaba.dubbo.rpc.filter.ContextFilter</code></li>\n<li><code>com.alibaba.dubbo.rpc.filter.ConsumerContextFilter</code></li>\n<li><code>com.alibaba.dubbo.rpc.filter.ExceptionFilter</code></li>\n<li><code>com.alibaba.dubbo.rpc.filter.ExecuteLimitFilter</code></li>\n<li><code>com.alibaba.dubbo.rpc.filter.DeprecatedFilter</code></li>\n</ul>\n<h2>Extension Guide</h2>\n<p>Directory layout:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxFilter.java (Filter implementation)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.rpc.Filter (plain text file with the content: xxx=com.xxx.XxxFilter)\n</code></pre>\n<p>XxxFilter.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Filter;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Invoker;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Invocation;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Result;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.RpcException;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxFilter</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Filter</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Result <span class="hljs-title">invoke</span><span class="hljs-params">(Invoker&lt;?&gt; invoker, Invocation invocation)</span> <span class="hljs-keyword">throws</span> RpcException </span>{\n        <span class="hljs-comment">// before filter ...</span>\n        Result result = invoker.invoke(invocation);\n        <span class="hljs-comment">// after filter ...</span>\n        <span class="hljs-keyword">return</span> result;\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.rpc.Filter:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxFilter\n</code></pre>\n'},{filename:"dev/impls/introduction.md",__html:"<h1>SPI Extension Implementations</h1>\n<p>SPI extension interface is used for system integration, it's also useful for dubbo contributor to extend dubbo functionality.</p>\n"},{filename:"dev/impls/invoker-listener.md",__html:'<h1>InvokerListener Extension</h1>\n<h2>Summary</h2>\n<p>Fire event when there\'s any service referenced.</p>\n<h2>Extension Interface</h2>\n<p><code>com.alibaba.dubbo.rpc.InvokerListener</code></p>\n<h2>Extension Configuration</h2>\n<pre><code class="language-xml"><span class="hljs-comment">&lt;!-- 引用服务监听 --&gt;</span>\n<span class="hljs-comment">&lt;!-- service reference listener --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">listener</span>=<span class="hljs-string">"xxx,yyy"</span> /&gt;</span> \n<span class="hljs-comment">&lt;!-- default service reference listener --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:consumer</span> <span class="hljs-attr">listener</span>=<span class="hljs-string">"xxx,yyy"</span> /&gt;</span> \n</code></pre>\n<h2>Existing Extension</h2>\n<p><code>com.alibaba.dubbo.rpc.listener.DeprecatedInvokerListener</code></p>\n<h2>Extension Guide</h2>\n<p>Directory layout:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxInvokerListener.java (InvokerListener implementation)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.rpc.InvokerListener (plain text file with the content: xxx=com.xxx.XxxInvokerListener)\n</code></pre>\n<p>XxxInvokerListener.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.InvokerListener;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Invoker;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.RpcException;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxInvokerListener</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">InvokerListener</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">referred</span><span class="hljs-params">(Invoker&lt;?&gt; invoker)</span> <span class="hljs-keyword">throws</span> RpcException </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">destroyed</span><span class="hljs-params">(Invoker&lt;?&gt; invoker)</span> <span class="hljs-keyword">throws</span> RpcException </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.rpc.InvokerListener:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxInvokerListener\n</code></pre>\n'},{filename:"dev/impls/load-balance.md",__html:'<h1>LoadBalance Extension</h1>\n<h2>Summary</h2>\n<p>Pick one from service providers and fire the invocation.</p>\n<h2>Extension Interface</h2>\n<p><code>com.alibaba.dubbo.rpc.cluster.LoadBalance</code></p>\n<h2>Extension Configuration</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">loadbalance</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- default configuration, will take effect when loadbalance is not configured in &lt;dubbo:protocol&gt; --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">loadbalance</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n</code></pre>\n<h2>Existing Extension</h2>\n<ul>\n<li><code>com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance</code></li>\n<li><code>com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance</code></li>\n<li><code>com.alibaba.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance</code></li>\n</ul>\n<h2>Extension Guide</h2>\n<p>Directory layout:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxLoadBalance.java (LoadBalance implementation)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.rpc.cluster.LoadBalance (plain text file with the content: xxx=com.xxx.XxxLoadBalance)\n</code></pre>\n<p>XxxLoadBalance.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.cluster.LoadBalance;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Invoker;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Invocation;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.RpcException; \n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxLoadBalance</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">LoadBalance</span> </span>{\n    <span class="hljs-keyword">public</span> &lt;T&gt; <span class="hljs-function">Invoker&lt;T&gt; <span class="hljs-title">select</span><span class="hljs-params">(List&lt;Invoker&lt;T&gt;&gt; invokers, Invocation invocation)</span> <span class="hljs-keyword">throws</span> RpcException </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.LoadBalance:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxLoadBalance\n</code></pre>\n'},{filename:"dev/impls/logger-adapter.md",__html:'<h1>LoggerAdapter Extension</h1>\n<h2>Summary</h2>\n<p>Extension for adapting logger output</p>\n<h2>Extension Interface</h2>\n<p><code>com.alibaba.dubbo.common.logger.LoggerAdapter</code></p>\n<h2>Extension Configuration</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:application</span> <span class="hljs-attr">logger</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n</code></pre>\n<p>Or:</p>\n<pre><code class="language-sh">-Ddubbo:application.logger=xxx\n</code></pre>\n<h2>Existing Extension</h2>\n<ul>\n<li><code>com.alibaba.dubbo.common.logger.slf4j.Slf4jLoggerAdapter</code></li>\n<li><code>com.alibaba.dubbo.common.logger.jcl.JclLoggerAdapter</code></li>\n<li><code>com.alibaba.dubbo.common.logger.log4j.Log4jLoggerAdapter</code></li>\n<li><code>com.alibaba.dubbo.common.logger.jdk.JdkLoggerAdapter</code></li>\n</ul>\n<h2>Extension Guide</h2>\n<p>Directory layout:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxLoggerAdapter.java (LoggerAdapter implementation)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.common.logger.LoggerAdapter (plain text file with the content: xxx=com.xxx.XxxLoggerAdapter)\n</code></pre>\n<p>XxxLoggerAdapter.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.common.logger.LoggerAdapter;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxLoggerAdapter</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">LoggerAdapter</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Logger <span class="hljs-title">getLogger</span><span class="hljs-params">(URL url)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>XxxLogger.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.common.logger.Logger;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxLogger</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Logger</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">XxxLogger</span><span class="hljs-params">(URL url)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">info</span><span class="hljs-params">(String msg)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-comment">// ...</span>\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.common.logger.LoggerAdapter:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxLoggerAdapter\n</code></pre>\n'},{filename:"dev/impls/merger.md",__html:'<h1>Merger Extension</h1>\n<h2>Summary</h2>\n<p>Merge strategy for return result aggregation in group.</p>\n<h2>Extension Interface</h2>\n<p><code>com.alibaba.dubbo.rpc.cluster.Merger</code></p>\n<h2>Extension Configuration</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:method</span> <span class="hljs-attr">merger</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n</code></pre>\n<h2>Existing Extension</h2>\n<ul>\n<li><code>com.alibaba.dubbo.rpc.cluster.merger.ArrayMerger</code></li>\n<li><code>com.alibaba.dubbo.rpc.cluster.merger.ListMerger</code></li>\n<li><code>com.alibaba.dubbo.rpc.cluster.merger.SetMerger</code></li>\n<li><code>com.alibaba.dubbo.rpc.cluster.merger.MapMerger</code></li>\n</ul>\n<h2>Extension Guide</h2>\n<p>Directory layout:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxMerger.java (Merger implementation)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.rpc.cluster.Merger (plain text file with the content: xxx=com.xxx.XxxMerger)\n</code></pre>\n<p>XxxMerger.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.cluster.Merger;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxMerger</span>&lt;<span class="hljs-title">T</span>&gt; <span class="hljs-keyword">implements</span> <span class="hljs-title">Merger</span>&lt;<span class="hljs-title">T</span>&gt; </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> T <span class="hljs-title">merge</span><span class="hljs-params">(T... results)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.Merger:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxMerger\n</code></pre>\n'},{filename:"dev/impls/monitor.md",__html:'<h1>Monitor Extension</h1>\n<h2>Summary</h2>\n<p>Extension to monitor service invocation times and time taken for each service invocation.</p>\n<h2>Extension Interface</h2>\n<ul>\n<li><code>com.alibaba.dubbo.monitor.MonitorFactory</code></li>\n<li><code>com.alibaba.dubbo.monitor.Monitor</code></li>\n</ul>\n<h2>Extension Configuration</h2>\n<pre><code class="language-xml"><span class="hljs-comment">&lt;!-- configure monitor center --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:monitor</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"xxx://ip:port"</span> /&gt;</span>\n</code></pre>\n<h2>Existing Extension</h2>\n<p>com.alibaba.dubbo.monitor.support.dubbo.DubboMonitorFactory</p>\n<h2>Extension Guide</h2>\n<p>Directory layout:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxMonitorFactoryjava (MonitorFactory implementation)\n                |-XxxMonitor.java (Monitor implementation)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.monitor.MonitorFactory (plain text file with the format: xxx=com.xxx.XxxMonitorFactory)\n</code></pre>\n<p>XxxMonitorFactory.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.monitor.MonitorFactory;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.monitor.Monitor;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.common.URL;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxMonitorFactory</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">MonitorFactory</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Monitor <span class="hljs-title">getMonitor</span><span class="hljs-params">(URL url)</span> </span>{\n        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> XxxMonitor(url);\n    }\n}\n</code></pre>\n<p>XxxMonitor.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.monitor.Monitor;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxMonitor</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Monitor</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">count</span><span class="hljs-params">(URL statistics)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.monitor.MonitorFactory:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxMonitorFactory\n</code></pre>\n'},{filename:"dev/impls/networker.md",__html:'<h1>Networker Extension</h1>\n<h2>Summary</h2>\n<p>Extension for peer to peer network grouping.</p>\n<h2>Extension Interface</h2>\n<p><code>com.alibaba.dubbo.remoting.p2p.Networker</code></p>\n<h2>Extension Configuration</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">networker</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- default configuration, it takes effect if networker attribute is not set in &lt;dubbo:protocol&gt; --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">networker</span>=<span class="hljs-string">"xxx"</span> /&gt;</span> \n</code></pre>\n<h2>Existing Extension</h2>\n<ul>\n<li><code>com.alibaba.dubbo.remoting.p2p.support.MulticastNetworker</code></li>\n<li><code>com.alibaba.dubbo.remoting.p2p.support.FileNetworker</code></li>\n</ul>\n<h2>Extension Guide</h2>\n<p>Directory layout:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxNetworker.java (Networker implementation)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.remoting.p2p.Networker (plain text file with the content: xxx=com.xxx.XxxNetworker)\n</code></pre>\n<p>XxxNetworker.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.remoting.p2p.Networker;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxNetworker</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Networker</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Group <span class="hljs-title">lookup</span><span class="hljs-params">(URL url)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.remoting.p2p.Networker:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxNetworker\n</code></pre>\n'},{filename:"dev/impls/page.md",__html:'<h1>Page Extension</h1>\n<h2>Summary</h2>\n<p>Extension for page handler</p>\n<h2>Extension Interface</h2>\n<p><code>com.alibaba.dubbo.container.page.PageHandler</code></p>\n<h2>Extension Configuration</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">page</span>=<span class="hljs-string">"xxx,yyy"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- default configuration, will take effect if page attribute is not set in &lt;dubbo:protocol&gt; --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">page</span>=<span class="hljs-string">"xxx,yyy"</span> /&gt;</span>\n</code></pre>\n<h2>Existing Extension</h2>\n<ul>\n<li><code>com.alibaba.dubbo.container.page.pages.HomePageHandler</code></li>\n<li><code>com.alibaba.dubbo.container.page.pages.StatusPageHandler</code></li>\n<li><code>com.alibaba.dubbo.container.page.pages.LogPageHandler</code></li>\n<li><code>com.alibaba.dubbo.container.page.pages.SystemPageHandler</code></li>\n</ul>\n<h2>Extension Guide</h2>\n<p>Directory layout:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxPageHandler.java (PageHandler implementation)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.container.page.PageHandler (plain text file with the content: xxx=com.xxx.XxxPageHandler)\n</code></pre>\n<p>XxxPageHandler.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.container.page.PageHandler;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxPageHandler</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">PageHandler</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Group <span class="hljs-title">lookup</span><span class="hljs-params">(URL url)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.container.page.PageHandler:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxPageHandler\n</code></pre>\n'},{filename:"dev/impls/protocol.md",__html:'<h1>Protocol Extension</h1>\n<h2>Summary</h2>\n<p>Extension to RPC protocol, hide details of remote call.</p>\n<p>Contract:</p>\n<ul>\n<li>When user calls <code>invoke()</code> method of <code>Invoker</code> object which\'s returned from <code>refer()</code> call, the protocol needs to correspondingly execute <code>invoke()</code> method of <code>Invoker</code> object passed from remote <code>export()</code> method associated with the same URL.</li>\n<li>Moreover, it\'s protocol\'s responsibility to implement <code>Invoker</code> which\'s returned from <code>refer()</code>. Generally speaking, protocol sends remote request in the <code>Invoker</code> implementation, but needs not to care about the <code>Invoker</code> passed into <code>export()</code> since the framework will implement the logic and pass in the instance.</li>\n</ul>\n<p>Notes:</p>\n<ul>\n<li>Protocol does not need to care about the proxy of the business interface. The upper layer of the framework will convert <code>Invoker</code> into business interface.</li>\n<li>It is not a requirement that the protocol must use TCP for network communication. It could be file-sharing, IPC, or others.</li>\n</ul>\n<h2>Extension Interface</h2>\n<ul>\n<li><code>com.alibaba.dubbo.rpc.Protocol</code></li>\n<li><code>com.alibaba.dubbo.rpc.Exporter</code></li>\n<li><code>com.alibaba.dubbo.rpc.Invoker</code></li>\n</ul>\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">Protocol</span> </span>{\n    <span class="hljs-comment">/**\n     * Export remote service: &lt;br&gt;\n     * 1. Should save address info for the request when the protocol receives it: RpcContext.getContext().setRemoteAddress();&lt;br&gt;\n     * 2. export() must be implemented as idempotent, that is, it should not introduce side effect when the implementation gets called with the same Invoker for more than once.\n     * 3. Invoker is passed by the framework, and the protocol should not care about it. &lt;br&gt;\n     * \n     * <span class="hljs-doctag">@param</span> &lt;T&gt; Service type\n     * <span class="hljs-doctag">@param</span> invoker Service invoker\n     * <span class="hljs-doctag">@return</span> exporter The reference of service exporter, used for cancelling service export.\n     * <span class="hljs-doctag">@throws</span> RpcException throw when there\'s any error during service export, e.g. the port is occupied\n     */</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-comment">/**\n     * Reference remote service: &lt;br&gt;\n     * 1. When user calls `invoke()` method of `Invoker` object which\'s returned from `refer()` call, the protocol needs to correspondingly execute `invoke()` method of `Invoker` object passed from remote `export()` method associated with the same URL. &lt;br&gt;\n     * 2. It\'s protocol\'s responsibility to implement `Invoker` which\'s returned from `refer()`. Generally speaking, protocol sends remote request in the `Invoker` implementation. &lt;br&gt;\n     * 3. When there\'s check=false set in URL, the implementation must not throw exception but try to recover when connection fails.\n     * \n     * <span class="hljs-doctag">@param</span> &lt;T&gt; Service type\n     * <span class="hljs-doctag">@param</span> type Service type\n     * <span class="hljs-doctag">@param</span> url URL address for the remote service\n     * <span class="hljs-doctag">@return</span> invoker service\'s local proxy\n     * <span class="hljs-doctag">@throws</span> RpcException throw when there\'s any error while connecting to the service provider\n     */</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}\n</code></pre>\n<h2>Extension Configuration</h2>\n<pre><code class="language-xml"><span class="hljs-comment">&lt;!-- declare protocol, if id is not set, then use the value of name for id --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"xxx1"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- reference protocol, if protocol\'s attribute is not set, then protocol configuration will be scanned automatically from ApplicationContext --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"xxx1"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- default value for referenced protocol, it will be used if protocol attribute is not configured in &lt;dubbo:service&gt; --&gt;</span> \n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"xxx1"</span> /&gt;</span>\n</code></pre>\n<h2>Existing Protocol</h2>\n<ul>\n<li><code>com.alibaba.dubbo.rpc.injvm.InjvmProtocol</code></li>\n<li><code>com.alibaba.dubbo.rpc.dubbo.DubboProtocol</code></li>\n<li><code>com.alibaba.dubbo.rpc.rmi.RmiProtocol</code></li>\n<li><code>com.alibaba.dubbo.rpc.http.HttpProtocol</code></li>\n<li><code>com.alibaba.dubbo.rpc.http.hessian.HessianProtocol</code></li>\n</ul>\n<h2>Extension Guide</h2>\n<p>Directory layout:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxProtocol.java (Protocol implementation)\n                |-XxxExporter.java (Exporter implementation)\n                |-XxxInvoker.java (Invoker implementation)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.rpc.Protocol (plain text file with the content: xxx=com.xxx.XxxProtocol)\n</code></pre>\n<p>XxxProtocol.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Protocol;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxProtocol</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Protocol</span> </span>{\n    <span class="hljs-keyword">public</span> &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        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> XxxExporter(invoker);\n    }\n    <span class="hljs-keyword">public</span> &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        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> XxxInvoker(type, url);\n    }\n}\n</code></pre>\n<p>XxxExporter.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.support.AbstractExporter;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxExporter</span>&lt;<span class="hljs-title">T</span>&gt; <span class="hljs-keyword">extends</span> <span class="hljs-title">AbstractExporter</span>&lt;<span class="hljs-title">T</span>&gt; </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">XxxExporter</span><span class="hljs-params">(Invoker&lt;T&gt; invoker)</span> <span class="hljs-keyword">throws</span> RemotingException</span>{\n        <span class="hljs-keyword">super</span>(invoker);\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">unexport</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-keyword">super</span>.unexport();\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>XxxInvoker.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.support.AbstractInvoker;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxInvoker</span>&lt;<span class="hljs-title">T</span>&gt; <span class="hljs-keyword">extends</span> <span class="hljs-title">AbstractInvoker</span>&lt;<span class="hljs-title">T</span>&gt; </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">XxxInvoker</span><span class="hljs-params">(Class&lt;T&gt; type, URL url)</span> <span class="hljs-keyword">throws</span> RemotingException</span>{\n        <span class="hljs-keyword">super</span>(type, url);\n    }\n    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">abstract</span> Object <span class="hljs-title">doInvoke</span><span class="hljs-params">(Invocation invocation)</span> <span class="hljs-keyword">throws</span> Throwable </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxProtocol\n</code></pre>\n'},{filename:"dev/impls/proxy-factory.md",__html:'<h1>ProxyFactory Extension</h1>\n<h2>Summary</h2>\n<p>Convert <code>Invoker</code> into business interface.</p>\n<h2>Extension Interface</h2>\n<p><code>com.alibaba.dubbo.rpc.ProxyFactory</code></p>\n<h2>Extension Configuration</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">proxy</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- default configuration, it will take effect when proxy attribute is not configured in &lt;dubbo:protocol&gt; --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">proxy</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n</code></pre>\n<h2>Existing Extension</h2>\n<ul>\n<li><code>com.alibaba.dubbo.rpc.proxy.JdkProxyFactory</code></li>\n<li><code>com.alibaba.dubbo.rpc.proxy.JavassistProxyFactory</code></li>\n</ul>\n<h2>Extension Guide</h2>\n<p>Directory layout:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxProxyFactory.java (ProxyFactory implementation)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.rpc.ProxyFactory (plain text file with the content: xxx=com.xxx.XxxProxyFactory)\n</code></pre>\n<p>XxxProxyFactory.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.ProxyFactory;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Invoker;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.RpcException;\n \n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxProxyFactory</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">ProxyFactory</span> </span>{\n    <span class="hljs-keyword">public</span> &lt;T&gt; <span class="hljs-function">T <span class="hljs-title">getProxy</span><span class="hljs-params">(Invoker&lt;T&gt; invoker)</span> <span class="hljs-keyword">throws</span> RpcException </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-keyword">public</span> &lt;T&gt; <span class="hljs-function">Invoker&lt;T&gt; <span class="hljs-title">getInvoker</span><span class="hljs-params">(T proxy, Class&lt;T&gt; type, URL url)</span> <span class="hljs-keyword">throws</span> RpcException </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.rpc.ProxyFactory:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxProxyFactory\n</code></pre>\n'},{filename:"dev/impls/registry.md",__html:'<h1>Registry Extension</h1>\n<h2>Summary</h2>\n<p>Registry extension is used for service registration and discovery.</p>\n<h2>Extension Interface</h2>\n<ul>\n<li><code>com.alibaba.dubbo.registry.RegistryFactory</code></li>\n<li><code>com.alibaba.dubbo.registry.Registry</code></li>\n</ul>\n<h2>Extension Configuration</h2>\n<pre><code class="language-xml"><span class="hljs-comment">&lt;!-- config registry server --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"xxx1"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"xxx://ip:port"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- reference registry server, if registry attribute is not specified, then ApplicationContext will be scanned to find if there\'s any --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">registry</span>=<span class="hljs-string">"xxx1"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- default configuration for referencing registry server, it will take effect if there\'s no registry attribute specified in &lt;dubbo:service&gt; --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">registry</span>=<span class="hljs-string">"xxx1"</span> /&gt;</span>\n</code></pre>\n<h2>Extension Contract</h2>\n<p>RegistryFactory.java:</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">RegistryFactory</span> </span>{\n    <span class="hljs-comment">/**\n     * Connect to registry server\n     * \n     * The contract for connecting to registry server: &lt;br&gt;\n     * 1. Will not check connection when check=false is set, otherwise exception will be thrown if connection fails. &lt;br&gt;\n     * 2. Support authorizing against username:password in the URL &lt;br&gt;\n     * 3. Support registry server backup with backup=10.20.153.10 &lt;br&gt;\n     * 4. Support cache on local disk with file=registry.cache &lt;br&gt;\n     * 5. Support timeout setup with timeout=1000 &lt;br&gt;\n     * 6. Support session expiration setup with session=60000 &lt;br&gt;\n     * \n     * <span class="hljs-doctag">@param</span> url registry server address, null is not allowed\n     * <span class="hljs-doctag">@return</span> reference to registry server, never return null\n     */</span>\n    <span class="hljs-function">Registry <span class="hljs-title">getRegistry</span><span class="hljs-params">(URL url)</span></span>; \n}\n</code></pre>\n<p>RegistryService.java:</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">RegistryService</span> </span>{ <span class="hljs-comment">// Registry extends RegistryService </span>\n    <span class="hljs-comment">/**\n     * Register service.\n     * \n     * Contract for registering service: &lt;br&gt;\n     * 1. Registration failure will be ignored and kept retrying if check=false is set in URL, otherwise exception will be thrown &lt;br&gt;\n     * 2. Persistence is required if dynamic=false is set in URL, otherwise, the registration info will be removed automatically when register quits accidentally &lt;br&gt;\n     * 3. Persistent by category if category=overrides is set in URL, default category is providers. It is possible to notify by category. &lt;br&gt;\n     * 4. Data lost is not tolerant when registry server reboots or network jitter happens. &lt;br&gt; \n     * 5. It is not allowed to override each other when URLs have same URI part but different parameters &lt;br&gt;\n     * \n     * <span class="hljs-doctag">@param</span> url registration info,null is not allowed, e.g.: dubbo://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&amp;application=kylin\n     */</span>\n    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">register</span><span class="hljs-params">(URL url)</span></span>;\n \n    <span class="hljs-comment">/**\n     * Unregister service.\n     * \n     * Contract for unregistering service: &lt;br&gt;\n     * 1. IllegalStateException should be thrown when registration info which\'s supposed to be persistent (with dynamic=false set) cannot be found, otherwise it should be ignored. &lt;br&gt;\n     * 2. To cancel one service, extract match on its URL will be honored &lt;br&gt;\n     * \n     * <span class="hljs-doctag">@param</span> url registration info,null is not allowed, e.g.: dubbo://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&amp;application=kylin\n     */</span>\n    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">unregister</span><span class="hljs-params">(URL url)</span></span>;\n \n    <span class="hljs-comment">/**\n     * 订阅服务.\n     * Subscribe service.\n     * \n     * Contract for subscribing service: &lt;br&gt;\n     * 1. Subscription failure will be ignored and kept retrying if check=false is set in URL &lt;br&gt;\n     * 2. Only the specified category will be notified if category=overrides is set in URL. Categories are seperated with comma, and all categorized data will be subscribed when wildcard "*" is specified. &lt;br&gt;\n     * 3. Allow to query by interface, group, version, classifier, e.g.: interface=com.alibaba.foo.BarService&amp;version=1.0.0&lt;br&gt;\n     * 4. Allow to query with wildcard "*" to subscribe all versions under all categories for all interfaces, e.g.: interface=*&amp;group=*&amp;version=*&amp;classifier=*&lt;br&gt;\n     * 5. Subscription will be automatically recoverred when registry server reboots or network jitter happens. &lt;br&gt;\n     * 6. It is not allowed to override each other when URLs have same URI part but different parameters &lt;br&gt;\n     * 7. Subscription procedure will not return until the first notification happens. &lt;br&gt;\n     * \n     * <span class="hljs-doctag">@param</span> url URL for subscription, null isn\'t allowed, e.g.: consumer://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&amp;application=kylin\n     * <span class="hljs-doctag">@param</span> listener notification listener, null is not allowed\n     */</span>\n    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">subscribe</span><span class="hljs-params">(URL url, NotifyListener listener)</span></span>;\n \n    <span class="hljs-comment">/**\n     * Unsubscribe service.\n     * \n     * Contract for unsubscribing service: &lt;br&gt;\n     * 1. Simply ignore if not subscribe &lt;br&gt;\n     * 2. Unsubscribe with URL exact match &lt;br&gt;\n     * \n     * <span class="hljs-doctag">@param</span> url URL for unsubscription, null is not allowed, e.g.: consumer://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&amp;application=kylin\n     * <span class="hljs-doctag">@param</span> listener notification listener, null is not allowed\n     */</span>\n    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">unsubscribe</span><span class="hljs-params">(URL url, NotifyListener listener)</span></span>;\n \n    <span class="hljs-comment">/**\n     * 查询注册列表,与订阅的推模式相对应,这里为拉模式,只返回一次结果。\n     * Lookup subscription list. Compared to push mode for subscription, this is pull mode and returns result only once.\n     * \n     * <span class="hljs-doctag">@see</span> com.alibaba.dubbo.registry.NotifyListener#notify(List)\n     * <span class="hljs-doctag">@param</span> url URL for  query, null is not allowed, e.g.: consumer://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&amp;application=kylin\n     * <span class="hljs-doctag">@return</span> subscription list, could be null, has the same meaning as the parameters in {<span class="hljs-doctag">@link</span> com.alibaba.dubbo.registry.NotifyListener#notify(List&lt;URL&gt;)}.\n     */</span>\n    <span class="hljs-function">List&lt;URL&gt; <span class="hljs-title">lookup</span><span class="hljs-params">(URL url)</span></span>;\n \n}\n</code></pre>\n<p>NotifyListener.java:</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">NotifyListener</span> </span>{ \n    <span class="hljs-comment">/**\n     * Fire event when receive service change notification.\n     * \n     * Contract for notify: &lt;br&gt;\n     * 1. Always notify with the whole data instead of partial data from the perspective of service interface and data type. In this way, user needs not compare with the previous result. &lt;br&gt;\n     * 2. First notification for subscription must contain the full set of data for one particular service &lt;br&gt;\n     * 3. It is allowed to separate the different type of data in the upcoming notifications, e.g.: it is legal to only notify one of types among providers, consumers, routes or overrides each time, but pls. note for this particular type, the data must be a full set. &lt;br&gt;\n     * 4. If the data for one particular type is empty, need to notify with a special URL which has empty as its protocol and has category parameter for this particluar type.\n     * 5. Notifier (usually it is monitor center) needs to guarantee the notification sequence by, for say: single thread push, queuing in order,  versioning, etc. &lt;br&gt;\n     * \n     * <span class="hljs-doctag">@param</span> urls subscription list, always not empty, equivalent to the return result of {<span class="hljs-doctag">@link</span> com.alibaba.dubbo.registry.RegistryService#lookup(URL)}.\n     */</span>\n    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">notify</span><span class="hljs-params">(List&lt;URL&gt; urls)</span></span>;\n \n}\n</code></pre>\n<h2>Existing Extension</h2>\n<p><code>com.alibaba.dubbo.registry.support.dubbo.DubboRegistryFactory</code></p>\n<h2>Extension Guide</h2>\n<p>Directory structure:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxRegistryFactoryjava (RegistryFactory implementation)\n                |-XxxRegistry.java (Registry implementation)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.registry.RegistryFactory (plain text file with the content: xxx=com.xxx.XxxRegistryFactory)\n</code></pre>\n<p>XxxRegistryFactory.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.registry.RegistryFactory;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.registry.Registry;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.common.URL;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxRegistryFactory</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">RegistryFactory</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Registry <span class="hljs-title">getRegistry</span><span class="hljs-params">(URL url)</span> </span>{\n        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> XxxRegistry(url);\n    }\n}\n</code></pre>\n<p>XxxRegistry.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.registry.Registry;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.registry.NotifyListener;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.common.URL;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxRegistry</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Registry</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">register</span><span class="hljs-params">(URL url)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">unregister</span><span class="hljs-params">(URL url)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">subscribe</span><span class="hljs-params">(URL url, NotifyListener listener)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">unsubscribe</span><span class="hljs-params">(URL url, NotifyListener listener)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.registry.RegistryFactory:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxRegistryFactory\n</code></pre>\n'},{filename:"dev/impls/remoting.md",__html:'<h1>Transporter Extension</h1>\n<h2>Summary</h2>\n<p>Transportation extension for communication between server and client.</p>\n<h2>Extension Interface</h2>\n<ul>\n<li><code>com.alibaba.dubbo.remoting.Transporter</code></li>\n<li><code>com.alibaba.dubbo.remoting.Server</code></li>\n<li><code>com.alibaba.dubbo.remoting.Client</code></li>\n</ul>\n<h2>Extension Configuration</h2>\n<pre><code class="language-xml"><span class="hljs-comment">&lt;!-- server and client use the same transporter --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">transporter</span>=<span class="hljs-string">"xxx"</span> /&gt;</span> \n<span class="hljs-comment">&lt;!-- server and client use the different transporter --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"xxx"</span> <span class="hljs-attr">client</span>=<span class="hljs-string">"xxx"</span> /&gt;</span> \n<span class="hljs-comment">&lt;!-- default configuration, will take effect when transport/server/client attribute is not set in &lt;dubbo:protocol&gt; --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">transporter</span>=<span class="hljs-string">"xxx"</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"xxx"</span> <span class="hljs-attr">client</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n</code></pre>\n<h2>Existing Extension</h2>\n<ul>\n<li><code>com.alibaba.dubbo.remoting.transport.transporter.netty.NettyTransporter</code></li>\n<li><code>com.alibaba.dubbo.remoting.transport.transporter.mina.MinaTransporter</code></li>\n<li><code>com.alibaba.dubbo.remoting.transport.transporter.grizzly.GrizzlyTransporter</code></li>\n</ul>\n<h2>Extension Guide</h2>\n<p>Directory layout:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxTransporter.java (Transporter implementation)\n                |-XxxServer.java (Server implementation)\n                |-XxxClient.java (Client implementation)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.remoting.Transporter (plain text file with the content: xxx=com.xxx.XxxTransporter)\n</code></pre>\n<p>XxxTransporter.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.remoting.Transporter;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxTransporter</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Transporter</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Server <span class="hljs-title">bind</span><span class="hljs-params">(URL url, ChannelHandler handler)</span> <span class="hljs-keyword">throws</span> RemotingException </span>{\n        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> XxxServer(url, handler);\n    }\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Client <span class="hljs-title">connect</span><span class="hljs-params">(URL url, ChannelHandler handler)</span> <span class="hljs-keyword">throws</span> RemotingException </span>{\n        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> XxxClient(url, handler);\n    }\n}\n</code></pre>\n<p>XxxServer.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.remoting.transport.transporter.AbstractServer;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxServer</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">AbstractServer</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">XxxServer</span><span class="hljs-params">(URL url, ChannelHandler handler)</span> <span class="hljs-keyword">throws</span> RemotingException</span>{\n        <span class="hljs-keyword">super</span>(url, handler);\n    }\n    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">doOpen</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Throwable </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">doClose</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Throwable </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Collection&lt;Channel&gt; <span class="hljs-title">getChannels</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Channel <span class="hljs-title">getChannel</span><span class="hljs-params">(InetSocketAddress remoteAddress)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>XxxClient.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.remoting.transport.transporter.AbstractClient;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxClient</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">AbstractClient</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">XxxServer</span><span class="hljs-params">(URL url, ChannelHandler handler)</span> <span class="hljs-keyword">throws</span> RemotingException</span>{\n        <span class="hljs-keyword">super</span>(url, handler);\n    }\n    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">doOpen</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Throwable </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">doClose</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Throwable </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">doConnect</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Throwable </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Channel <span class="hljs-title">getChannel</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.remoting.Transporter:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxTransporter\n</code></pre>\n'},{filename:"dev/impls/router.md",__html:'<h1>Router Extension</h1>\n<h2>Summary</h2>\n<p>Pick one from service providers and fire the invocation.</p>\n<h2>Extension Interface</h2>\n<ul>\n<li><code>com.alibaba.dubbo.rpc.cluster.RouterFactory</code></li>\n<li><code>com.alibaba.dubbo.rpc.cluster.Router</code></li>\n</ul>\n<h2>Existing Extension</h2>\n<ul>\n<li><code>com.alibaba.dubbo.rpc.cluster.router.ScriptRouterFactory</code></li>\n<li><code>com.alibaba.dubbo.rpc.cluster.router.FileRouterFactory</code></li>\n</ul>\n<h2>Extension Guide</h2>\n<p>Directory layout:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxRouterFactory.java (LoadBalance implementation)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.rpc.cluster.RouterFactory (plain text file with the content: xxx=com.xxx.XxxRouterFactory)\n\n</code></pre>\n<p>XxxRouterFactory.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.cluster.RouterFactory;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Invoker;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.Invocation;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.RpcException;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxRouterFactory</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">RouterFactory</span> </span>{\n    <span class="hljs-keyword">public</span> &lt;T&gt; List&lt;Invoker&lt;T&gt;&gt; select(List&lt;Invoker&lt;T&gt;&gt; invokers, Invocation invocation) <span class="hljs-keyword">throws</span> RpcException {\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.RouterFactory:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxRouterFactory\n</code></pre>\n'},{filename:"dev/impls/serialize.md",__html:'<h1>Serialization Extension</h1>\n<h2>Summary</h2>\n<p>Extension to serializing java object into byte code stream for transporting on the network, and vise versa.</p>\n<h2>Extension Interface</h2>\n<ul>\n<li><code>com.alibaba.dubbo.common.serialize.Serialization</code></li>\n<li><code>com.alibaba.dubbo.common.serialize.ObjectInput</code></li>\n<li><code>com.alibaba.dubbo.common.serialize.ObjectOutput</code></li>\n</ul>\n<h2>Extension Configuration</h2>\n<pre><code class="language-xml"><span class="hljs-comment">&lt;!-- protocol serialization style --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">serialization</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- default configuration, will take effect if serialization is not configured in &lt;dubbo:protocol&gt; --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">serialization</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n</code></pre>\n<h2>Existing Extension</h2>\n<ul>\n<li><code>com.alibaba.dubbo.common.serialize.dubbo.DubboSerialization</code></li>\n<li><code>com.alibaba.dubbo.common.serialize.hessian.Hessian2Serialization</code></li>\n<li><code>com.alibaba.dubbo.common.serialize.java.JavaSerialization</code></li>\n<li><code>com.alibaba.dubbo.common.serialize.java.CompactedJavaSerialization</code></li>\n</ul>\n<h2>Extension Guide</h2>\n<p>Directory layout:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxSerialization.java (Serialization implementation)\n                |-XxxObjectInput.java (ObjectInput implementation)\n                |-XxxObjectOutput.java (ObjectOutput implementation)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.common.serialize.Serialization (plain text file with the content: xxx=com.xxx.XxxSerialization)\n</code></pre>\n<p>XxxSerialization.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.common.serialize.Serialization;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.common.serialize.ObjectInput;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.common.serialize.ObjectOutput;\n \n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxSerialization</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Serialization</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> ObjectOutput <span class="hljs-title">serialize</span><span class="hljs-params">(Parameters parameters, OutputStream output)</span> <span class="hljs-keyword">throws</span> IOException </span>{\n        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> XxxObjectOutput(output);\n    }\n    <span class="hljs-function"><span class="hljs-keyword">public</span> ObjectInput <span class="hljs-title">deserialize</span><span class="hljs-params">(Parameters parameters, InputStream input)</span> <span class="hljs-keyword">throws</span> IOException </span>{\n        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> XxxObjectInput(input);\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.common.serialize.Serialization:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxSerialization\n</code></pre>\n'},{filename:"dev/impls/status-checker.md",__html:'<h1>StatusChecker Extension</h1>\n<h2>Summary</h2>\n<p>Extension to check status of resources service depends on. This status checker can be used in both telnet status command and status page.</p>\n<h2>Extension Interface</h2>\n<p><code>com.alibaba.dubbo.common.status.StatusChecker</code></p>\n<h2>Extension Configuration</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">status</span>=<span class="hljs-string">"xxx,yyy"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- default configuration, will take effect if no status attribute is configured in &lt;dubbo:protocol&gt; --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">status</span>=<span class="hljs-string">"xxx,yyy"</span> /&gt;</span>\n</code></pre>\n<h2>Existing Extension</h2>\n<ul>\n<li><code>com.alibaba.dubbo.common.status.support.MemoryStatusChecker</code></li>\n<li><code>com.alibaba.dubbo.common.status.support.LoadStatusChecker</code></li>\n<li><code>com.alibaba.dubbo.rpc.dubbo.status.ServerStatusChecker</code></li>\n<li><code>com.alibaba.dubbo.rpc.dubbo.status.ThreadPoolStatusChecker</code></li>\n<li><code>com.alibaba.dubbo.registry.directory.RegistryStatusChecker</code></li>\n<li><code>com.alibaba.dubbo.rpc.config.spring.status.SpringStatusChecker</code></li>\n<li><code>com.alibaba.dubbo.rpc.config.spring.status.DataSourceStatusChecker</code></li>\n</ul>\n<h2>Extension Guide</h2>\n<p>Directory layout:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxStatusChecker.java (StatusChecker implementation)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.common.status.StatusChecker (plain text file with the content: xxx=com.xxx.XxxStatusChecker)\n</code></pre>\n<p>XxxStatusChecker.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.common.status.StatusChecker;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxStatusChecker</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">StatusChecker</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Status <span class="hljs-title">check</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.common.status.StatusChecker:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxStatusChecker\n</code></pre>\n'},{filename:"dev/impls/telnet-handler.md",__html:'<h1>TelnetHandler Extension</h1>\n<h2>Summary</h2>\n<p>Extension to telnet command. All server should support telnet access for operation convenience.</p>\n<h2>Extension Interface</h2>\n<p><code>com.alibaba.dubbo.remoting.telnet.TelnetHandler</code></p>\n<h2>Extension Configuration</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">telnet</span>=<span class="hljs-string">"xxx,yyy"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- default configuration, will take effect if telnet attribute is not specified in &lt;dubbo:protocol&gt; --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">telnet</span>=<span class="hljs-string">"xxx,yyy"</span> /&gt;</span>\n</code></pre>\n<h2>Existing Extension</h2>\n<ul>\n<li><code>com.alibaba.dubbo.remoting.telnet.support.ClearTelnetHandler</code></li>\n<li><code>com.alibaba.dubbo.remoting.telnet.support.ExitTelnetHandler</code></li>\n<li><code>com.alibaba.dubbo.remoting.telnet.support.HelpTelnetHandler</code></li>\n<li><code>com.alibaba.dubbo.remoting.telnet.support.StatusTelnetHandler</code></li>\n<li><code>com.alibaba.dubbo.rpc.dubbo.telnet.ListTelnetHandler</code></li>\n<li><code>com.alibaba.dubbo.rpc.dubbo.telnet.ChangeTelnetHandler</code></li>\n<li><code>com.alibaba.dubbo.rpc.dubbo.telnet.CurrentTelnetHandler</code></li>\n<li><code>com.alibaba.dubbo.rpc.dubbo.telnet.InvokeTelnetHandler</code></li>\n<li><code>com.alibaba.dubbo.rpc.dubbo.telnet.TraceTelnetHandler</code></li>\n<li><code>com.alibaba.dubbo.rpc.dubbo.telnet.CountTelnetHandler</code></li>\n<li><code>com.alibaba.dubbo.rpc.dubbo.telnet.PortTelnetHandler</code></li>\n</ul>\n<h2>Extension Guide</h2>\n<p>Directory layout:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxTelnetHandler.java (TelnetHandler implementation)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.remoting.telnet.TelnetHandler (plain text file with the content: xxx=com.xxx.XxxTelnetHandler)\n</code></pre>\n<p>XxxTelnetHandler.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.remoting.telnet.TelnetHandler;\n \n<span class="hljs-meta">@Help</span>(parameter=<span class="hljs-string">"..."</span>, summary=<span class="hljs-string">"..."</span>, detail=<span class="hljs-string">"..."</span>)\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxTelnetHandler</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">TelnetHandler</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">telnet</span><span class="hljs-params">(Channel channel, String message)</span> <span class="hljs-keyword">throws</span> RemotingException </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.remoting.telnet.TelnetHandler:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxTelnetHandler\n</code></pre>\n<h2>用法</h2>\n<pre><code class="language-sh">telnet 127.0.0.1 20880\ndubbo&gt; xxx args\n</code></pre>\n'},{filename:"dev/impls/threadpool.md",__html:'<h1>ThreadPool Extension</h1>\n<h2>Summary</h2>\n<p>Thread pool strategy extension for service provider. When server receives one request, it needs a thread from thread pool to execute business logic in service provider.</p>\n<h2>Extension Interface</h2>\n<p><code>com.alibaba.dubbo.common.threadpool.ThreadPool</code></p>\n<h2>Extension Configuration</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">threadpool</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- default configuration, it will take effect when threadpool attribute is not specified in &lt;dubbo:protocol&gt; --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">threadpool</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n</code></pre>\n<h2>Existing Extension</h2>\n<ul>\n<li><code>com.alibaba.dubbo.common.threadpool.FixedThreadPool</code></li>\n<li><code>com.alibaba.dubbo.common.threadpool.CachedThreadPool</code></li>\n</ul>\n<h2>Extension Guide</h2>\n<p>Directory layout:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxThreadPool.java (ThreadPool implementation)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.common.threadpool.ThreadPool (plain text file with the content: xxx=com.xxx.XxxThreadPool)\n</code></pre>\n<p>XxxThreadPool.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.common.threadpool.ThreadPool;\n<span class="hljs-keyword">import</span> java.util.concurrent.Executor;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxThreadPool</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">ThreadPool</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Executor <span class="hljs-title">getExecutor</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.common.threadpool.ThreadPool:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxThreadPool\n</code></pre>\n'},{filename:"dev/impls/validation.md",__html:'<h1>Validation Extension</h1>\n<h2>Summary</h2>\n<p>Extension for parameter validation.</p>\n<h2>Extension Inteface</h2>\n<p><code>com.alibaba.dubbo.validation.Validation</code></p>\n<h2>Extension Configuration</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">validation</span>=<span class="hljs-string">"xxx,yyy"</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- default configuration, it will take effect when there\'s no validation attribute specified in &lt;dubbo:service&gt; --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">validation</span>=<span class="hljs-string">"xxx,yyy"</span> /&gt;</span>\n</code></pre>\n<h2>Existing Extension</h2>\n<p><code>com.alibaba.dubbo.validation.support.jvalidation.JValidation</code></p>\n<h2>Extension Guide</h2>\n<p>Directory layout:</p>\n<pre><code>src\n |-main\n    |-java\n        |-com\n            |-xxx\n                |-XxxValidation.java (Validation implementation)\n    |-resources\n        |-META-INF\n            |-dubbo\n                |-com.alibaba.dubbo.validation.Validation (plain text file with the content: xxx=com.xxx.XxxValidation)\n</code></pre>\n<p>XxxValidation.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.validation.Validation;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxValidation</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Validation</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Object <span class="hljs-title">getValidator</span><span class="hljs-params">(URL url)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>XxxValidator.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.validation.Validator;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">XxxValidator</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Validator</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">XxxValidator</span><span class="hljs-params">(URL url)</span> </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">validate</span><span class="hljs-params">(Invocation invocation)</span> <span class="hljs-keyword">throws</span> Exception </span>{\n        <span class="hljs-comment">// ...</span>\n    }\n}\n</code></pre>\n<p>META-INF/dubbo/com.alibaba.dubbo.validation.Validation:</p>\n<pre><code class="language-properties">xxx=com.xxx.XxxValidation\n</code></pre>\n'},{filename:"dev/introduction.md",__html:""},{filename:"dev/release.md",__html:"<h1>Versions</h1>\n<p><strong>New feature development</strong> and <strong>stability improvement</strong> are equally important to product. But adding new features will affect stability, dubbo uses the following version development pattern to achieve a good balance.</p>\n<h2>Two versions evolving at the same time</h2>\n<ul>\n<li>BugFix Version:low version,e.g. <code>2.4.x</code>. This is called the GA version, which can be applied in production. We are supposed only to fix bugs in this version, and increase the third version number when release.</li>\n<li>Feature Version:high version, e.g. <code>2.5.x</code>. We add new features to this version, so applications have opportunities try new features.</li>\n</ul>\n<p>When features in <code>2.5.x</code> are proved stable enough, we will announce <code>2.5.x</code> as a beta release.</p>\n<p>When <code>2.5.x</code> proved stable after enough test on enough applications:</p>\n<ul>\n<li><code>2.5.x</code>, the GA Version, only do BugFix, the main version to be used. We can try to promote applications to upgrade to GA at the desired time.</li>\n<li><code>2.4.x</code>, no longer maintained. When bugs appear, applications have no choice but upgrade to the latest stable version- Sunset Clause</li>\n<li>We create a new branch <code>2.6.0</code> based on <code>2.5.x</code> for new features.</li>\n</ul>\n<h2>Pros</h2>\n<ul>\n<li>GA Version are promised stable:\n<ul>\n<li>only BugFix</li>\n<li>GA Version got enough tests before promotion</li>\n</ul>\n</li>\n<li>New features can respond quickly in Feature Version and allow applications to try that</li>\n<li>Significantly reduces development and maintenance costs</li>\n</ul>\n<h2>The responsibilities of users</h2>\n<p>Users should always keep in track with the GA Version, make sure all bugs were fixed.</p>\n<p>There is a fake proposition: regular upgrades bring more risks. Here's the reasons:</p>\n<ul>\n<li>GA remains stable after a trial period.</li>\n<li>Bugs find on GA will be fixed immediately.</li>\n<li>Comparing with the on-need-upgrade (only upgrade when find a serious problem, and may span multiple versions), upgrade periodically can flat risk. Experienced a long cycle of large projects, students will have such an experience, the tripartite library version does not upgrade for a long time, the result of the problem had to upgrade to the new version (across multiple versions) a huge risk.</li>\n</ul>\n"},{filename:"user/README.md",__html:"<h1>dubbo-user-book</h1>\n<p>The dubbo cookbook, covering almost all features of dubbo framework.</p>\n"},{filename:"user/SUMMARY.md",__html:'<h1>Summary</h1>\n<ul>\n<li><a href="./preface/index.md">1 Preface</a>\n<ul>\n<li><a href="./preface/background.md">1.1 Background</a></li>\n<li><a href="./preface/requirements.md">1.2 Requirements</a></li>\n<li><a href="./preface/architecture.md">1.3 Architecture</a></li>\n<li><a href="./preface/usage.md">1.4 Usage</a></li>\n</ul>\n</li>\n<li><a href="./quick-start.md">2 Quick start</a></li>\n<li><a href="./dependencies.md">3 Dependencies</a></li>\n<li><a href="./maturity.md">4 maturality</a></li>\n<li><a href="./configuration/index.md">5 Configuration</a>\n<ul>\n<li><a href="./configuration/xml.md">5.1 XML configuration</a></li>\n<li><a href="./configuration/properties.md">5.2 Properties configuration</a></li>\n<li><a href="./configuration/api.md">5.3 API configuration</a></li>\n<li><a href="./configuration/annotation.md">5.4 Annotation configuration</a></li>\n</ul>\n</li>\n<li><a href="./demos/index.md">6 Demos</a>\n<ul>\n<li><a href="./demos/preflight-check.md">6.1 Start check</a></li>\n<li><a href="./demos/fault-tolerent-strategy.md">6.2 Fault-tolerent strategy</a></li>\n<li><a href="./demos/loadbalance.md">6.3 Load balance</a></li>\n<li><a href="./demos/thread-model.md">6.4 Thread model</a></li>\n<li><a href="./demos/explicit-target.md">6.5 Connecting certain provider straightly</a></li>\n<li><a href="./demos/subscribe-only.md">6.6 Subscribe only</a></li>\n<li><a href="./demos/registry-only.md">6.7 Registry only</a></li>\n<li><a href="./demos/static-service.md">6.8 Static service</a></li>\n<li><a href="./demos/multi-protocols.md">6.9 Multi-protocols</a></li>\n<li><a href="./demos/multi-registry.md">6.10 Multi-registries</a></li>\n<li><a href="./demos/service-group.md">6.11 Service group</a></li>\n<li><a href="./demos/multi-versions.md">6.12 Multi-versions</a></li>\n<li><a href="./demos/group-merger.md">6.13 Group merger</a></li>\n<li><a href="./demos/parameter-validation.md">6.14 Parameter validation</a></li>\n<li><a href="./demos/result-cache.md">6.15 Result cache</a></li>\n<li><a href="./demos/generic-reference.md">6.16 Generic reference</a></li>\n<li><a href="./demos/generic-service.md">6.17 Generic service</a></li>\n<li><a href="./demos/echo-service.md">6.18 Echo service</a></li>\n<li><a href="./demos/context.md">6.19 Context</a></li>\n<li><a href="./demos/attachment.md">6.20 Attachment</a></li>\n<li><a href="./demos/async-call.md">6.21 Asynchronous call</a></li>\n<li><a href="./demos/local-call.md">6.22 Local call</a></li>\n<li><a href="./demos/callback-parameter.md">6.23 Callback parameter</a></li>\n<li><a href="./demos/events-notify.md">6.24 Events notify</a></li>\n<li><a href="./demos/local-stub.md">6.25 Local stub</a></li>\n<li><a href="./demos/local-mock.md">6.26 Local mock</a></li>\n<li><a href="./demos/delay-publish.md">6.27 Delay publish</a></li>\n<li><a href="./demos/concurrency-control.md">6.28 Concurrency control</a></li>\n<li><a href="./demos/config-connections.md">6.29 Connections limitation</a></li>\n<li><a href="./demos/lazy-connect.md">6.30 Lazy connect</a></li>\n<li><a href="./demos/stickiness.md">6.31 Stickness connections</a></li>\n<li><a href="./demos/token-authorization.md">6.32 Token authorization</a></li>\n<li><a href="./demos/routing-rule.md">6.33 Routing rule</a></li>\n<li><a href="./demos/config-rule.md">6.34 Configuration rule</a></li>\n<li><a href="./demos/service-donwngrade.md">6.35 Service downgrade</a></li>\n<li><a href="./demos/graceful-shutdown.md">6.36 Graceful shutdown</a></li>\n<li><a href="./demos/hostname-binding.md">6.37 Hostname binding </a></li>\n<li><a href="./demos/logger-strategy.md">6.38 Logger strategy</a></li>\n<li><a href="./demos/accesslog.md">6.39 Accesslog</a></li>\n<li><a href="./demos/service-container.md">6.40 Service container</a></li>\n<li><a href="./demos/reference-config-cache.md">6.41 Reference config cache</a></li>\n<li><a href="./demos/distributed-transaction.md">6.42 Distributed transaction</a></li>\n<li><a href="./demos/dump.md">6.43 Dumping thread stack automatically</a></li>\n<li><a href="./demos/netty4.md">6.44 Netty4</a></li>\n</ul>\n</li>\n<li><a href="./references/api.md">7 API introduction</a></li>\n<li><a href="./references/xml/introduction.md">8 Schema configuration introduction</a>\n<ul>\n<li><a href="./references/xml/dubbo-service.md">8.1 dubbo:service</a></li>\n<li><a href="./references/xml/dubbo-reference.md">8.2 dubbo:reference</a></li>\n<li><a href="./references/xml/dubbo-protocol.md">8.3 dubbo:protocol</a></li>\n<li><a href="./references/xml/dubbo-registry.md">8.4 dubbo:registry</a></li>\n<li><a href="./references/xml/dubbo-monitor.md">8.5 dubbo:monitor</a></li>\n<li><a href="./references/xml/dubbo-application.md">8.6 dubbo:application</a></li>\n<li><a href="./references/xml/dubbo-module.md">8.7 dubbo:module</a></li>\n<li><a href="./references/xml/dubbo-provider.md">8.8 dubbo:provider</a></li>\n<li><a href="./references/xml/dubbo-consumer.md">8.9 dubbo:consumer</a></li>\n<li><a href="./references/xml/dubbo-method.md">8.10 dubbo:method</a></li>\n<li><a href="./references/xml/dubbo-argument.md">8.11 dubbo:argument</a></li>\n<li><a href="./references/xml/dubbo-parameter.md">8.12 dubbo:parameter</a></li>\n</ul>\n</li>\n<li><a href="./references/protocol/introduction.md">9 Protocol introduction</a>\n<ul>\n<li><a href="./references/protocol/dubbo.md">9.1 dubbo://</a></li>\n<li><a href="./references/protocol/rmi.md">9.2 rmi//</a></li>\n<li><a href="./references/protocol/hessian.md">9.3 hessian://</a></li>\n<li><a href="./references/protocol/http.md">9.4 http://</a></li>\n<li><a href="./references/protocol/webservice.md">9.5 webservice://</a></li>\n<li><a href="./references/protocol/thrift.md">9.6 thrift://</a></li>\n<li><a href="./references/protocol/memcached.md">9.7 memcached://</a></li>\n<li><a href="./references/protocol/redis.md">9.8 redis://</a></li>\n</ul>\n</li>\n<li><a href="./references/registry/introduction.md">10 registry introduction</a>\n<ul>\n<li><a href="./references/registry/multicast.md">10.1 Multicast registry</a></li>\n<li><a href="./references/registry/zookeeper.md">10.2 Zookeeper registry</a></li>\n<li><a href="./references/registry/redis.md">10.3 Redis registry</a></li>\n<li><a href="./references/registry/simple.md">10.4 Simple registry</a></li>\n</ul>\n</li>\n<li><a href="./references/telnet.md">11 Telnet command</a></li>\n<li><a href="./references/maven.md">12 maven plugins</a></li>\n<li><a href="./best-practice.md">13 Servitization best practice</a></li>\n<li><a href="./recommend.md">14 Recommended usage</a></li>\n<li><a href="./capacity-plan.md">15 Capacity plan</a></li>\n<li><a href="./perf-test.md">16 Performance testing reports</a></li>\n<li><a href="./coveragence.md">17 Test coverage report</a></li>\n</ul>\n'},{filename:"user/benchmark-tool.md",__html:'<h1>Beanchmark testing tool installer</h1>\n<ul>\n<li>download: git clone <a href="https://github.com/apache/incubator-dubbo.git">https://github.com/apache/incubator-dubbo.git</a></li>\n<li>compile benchmark: cd incubator-dubbo/dubbo-test/dubbo-test-benchmark; mvn clean install</li>\n<li>uncompress benchmark: incubator-dubbo/dubbo-test/dubbo-test-benchmark/target/dubbo-test-benchmark-2.6.2-SNAPSHOT.tar.gz</li>\n</ul>\n<p>Read ReadMe.txt (the contents are as follows, in the compressed package.)</p>\n<ul>\n<li>\n<p>Build a new benchmark project, such as demo.benchmark</p>\n</li>\n<li>\n<p>Import the your own interface api jar and dubbo.benchmark.jar (Unzip dubbo.benchmark.tar.gz, under the lib directory )</p>\n</li>\n<li>\n<p>Create a new class to implement AbstractClientRunnable</p>\n<ul>\n<li>Implement the constructor of the parent class</li>\n<li>Implement the invoke method and create a local interface proxy by serviceFactory,and finish your own business logic, as follows:</li>\n</ul>\n<pre><code class="language-java">    <span class="hljs-function"><span class="hljs-keyword">public</span> Object <span class="hljs-title">invoke</span><span class="hljs-params">(ServiceFactory serviceFactory)</span> </span>{\n        DemoService demoService = (DemoService) serviceFactory.get(DemoService.class);\n        <span class="hljs-keyword">return</span> demoService.sendRequest(<span class="hljs-string">"hello"</span>);\n    }\n</code></pre>\n</li>\n<li>\n<p>Make your own benchmark project into a jar package, such as demo.benchmark.jar</p>\n</li>\n<li>\n<p>Put the demo.benchmark.jar and service API jar into directory dubbo.benchmark/lib</p>\n</li>\n<li>\n<p>Configuring duubo.properties</p>\n</li>\n<li>\n<p>Run run.bat(windows) or <a href="http://run.sh">run.sh</a>(linux)</p>\n</li>\n</ul>\n<p>If you want to test the different versions of Dubbo, you can replace the jar of the Dubbo.</p>\n'},{filename:"user/best-practice.md",__html:'<h1>Servitization best practice</h1>\n<h2>Modularization</h2>\n<p>It is recommended to put service interfaces, service models, service exceptions, and so on in the API package,Because the service model and exception are part of the API, it is also in conformity with the modularization principle:Reusing the publish equivalence principle (REP) and the Common Reuse Principle (CRP).</p>\n<p>If you need, you can also consider placing a spring reference configuration in the API package, so that the user can only use the configuration in the spring loading process, and the configuration suggestion is placed in the package directory of the module, so as not to conflict, eg:<code>com/alibaba/china/xxx/dubbo-reference.xml</code>。</p>\n<h2>Granularity</h2>\n<p>The service interface should have large granularity as possible.Each service method should represent a function rather than a step of a function, otherwise it will be faced with distributed transaction problem. Dubbo does not provide distributed transaction support at present.</p>\n<p>The service interface recommends the division of the business scene as a unit and abstract the similar business to prevent the explosion of the number of interfaces.</p>\n<p>It is not recommended to use an too abstract universal interface, such as Map query (Map), which has no explicit semantics, which will inconvenience later maintenance.</p>\n<h2>Version</h2>\n<p>Each interface should define a version number to provide possible subsequent incompatible upgrades,eg: <code>&lt;dubbo:service interface=&quot;com.xxx.XxxService&quot; version=&quot;1.0&quot; /&gt;</code>。</p>\n<p>It is recommended to use a two bit version number, because the third - bit version number is usually compatible with a compatible upgrade, and a change of service version is required only when incompatible.</p>\n<p>When incompatible, half of the provider is upgraded to a new version, and all the consumers are upgraded to a new version, and the remaining half providers are upgraded to a new version.</p>\n<h2>Compatibility</h2>\n<p>The service interface adds method or the service model adds fields. It can be backward compatible, delete methods or delete fields, and will not be compatible. The new fields of the enumerated type are not compatible, so we need to upgrade by changing the version number.</p>\n<p>The compatibility of each protocol is different, see: <a href="./references/protocol/introduction.md">Protocol introduction</a></p>\n<h2>Enumeration type</h2>\n<p>If it is a complete set, you can use Enum, eg:<code>ENABLE</code>, <code>DISABLE</code>。</p>\n<p>If it is the type of business, there will be an obvious type of increase in the future, and it is not recommended to use  <code>Enum</code>, and it is not recommended to use Enum and can be replaced by  <code>String</code> .</p>\n<p>If you use<code>Enum</code>in the return value,And add the  <code>Enum</code> value,suggestions to upgrade the service consumption, so that the service provider does not return a new value.</p>\n<p>If the  <code>Enum</code>  value is used in the incoming parameter,and add the <code>Enum</code> value,it is suggested that the service provider be upgraded first, so that the service consumer will not pass the new value.</p>\n<h2>Serialization</h2>\n<p>The service parameters and return values suggest that the POJO object is used, that is, the object of the attribute is represented by the <code>setter</code>, <code>getter</code> method.</p>\n<p>Service parameters and return values do not recommend the use of interfaces, because data model abstraction is of little significance, and serialization requires interfaces to implement meta information of classes, and can not play the purpose of hiding implementation.</p>\n<p>Service parameters and return values must be byValue, but not byReference. The reference or return values of consumers and providers are not the same, but the values are the same. Dubbo does not support remote objects.</p>\n<h2>Exception</h2>\n<p>It is suggested that abnormal reporting errors are used rather than return error codes, and exception information can carry more information and have more semantic friendliness.</p>\n<p>If you are worried about performance problems, you can use the override () method of fillInStackTrace () out of the exception class as an empty method to make it not a copy of the stack information when necessary.</p>\n<p>Query method is not recommended throws checked, otherwise the caller in the query will be too much <code>try...catch, and can not be processed.</code></p>\n<p>Service providers should not throw the exception of DAO or SQL to the consumer side. They should package the exception that consumers do not care about in service implementation, otherwise consumers may not be able to serialize the corresponding exception.</p>\n<h2>Call</h2>\n<p>Not just because it is a Dubbo call, wrap the call logic eith <code>try...catch</code>clause. <code>try...catch</code> should be added to the appropriate rollback boundary.</p>\n<p>The check logic for the input parameters should be available at the Provider side. For performance considerations, the service implementer may consider adding a service Stub class to the API package to complete the test.</p>\n'},{filename:"user/capacity-plan.md",__html:"<h1>Capacity plan</h1>\n<p>The following data for reference:</p>\n<h2>Use member service project of Dubbo</h2>\n<ul>\n<li>Receive 400,000,000 remote calls one day</li>\n<li>Use 12 standard servers to provide services (CPU:8 core, memory: 8G)</li>\n<li>The average load is less than 1 (For 8 core CPU, the load is very low)</li>\n<li>The average response time is 2.3 to 2.5 ms,Network cost about 1.5 to 1.6 ms(Related to the size of the packet )</li>\n</ul>\n<h2>Use product authorization service project of Dubbo</h2>\n<ul>\n<li>Receive 300,000,000 remote calls one day</li>\n<li>Use 8 standard servers to provide services (CPU:8 core, memory: 8G)</li>\n<li>The average load is less than 1 (For 8 core CPU, the load is very low)</li>\n<li>The average response time is  1.4 to 2.8 ms,Network cost about 1.0 to 1.1 ms(Related to the size of the packet )</li>\n</ul>\n"},{filename:"user/configuration/annotation.md",__html:'<h1>Annotation Configuration</h1>\n<p>Requires<code>2.5.7</code> or higher</p>\n<h2>Provider Side</h2>\n<h3><code>Service</code> annotation for exporting</h3>\n<pre><code class="language-java"><span class="hljs-keyword">import</span> com.alibaba.dubbo.config.annotation.Service;\n \n<span class="hljs-meta">@Service</span>(timeout = <span class="hljs-number">5000</span>)\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AnnotateServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">AnnotateService</span> </span>{ \n    <span class="hljs-comment">// ...</span>\n}\n</code></pre>\n<h3>Use JavaConfig for common parts</h3>\n<pre><code class="language-java"><span class="hljs-meta">@Configuration</span>\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DubboConfiguration</span> </span>{\n\n    <span class="hljs-meta">@Bean</span>\n    <span class="hljs-function"><span class="hljs-keyword">public</span> ApplicationConfig <span class="hljs-title">applicationConfig</span><span class="hljs-params">()</span> </span>{\n        ApplicationConfig applicationConfig = <span class="hljs-keyword">new</span> ApplicationConfig();\n        applicationConfig.setName(<span class="hljs-string">"provider-test"</span>);\n        <span class="hljs-keyword">return</span> applicationConfig;\n    }\n\n    <span class="hljs-meta">@Bean</span>\n    <span class="hljs-function"><span class="hljs-keyword">public</span> RegistryConfig <span class="hljs-title">registryConfig</span><span class="hljs-params">()</span> </span>{\n        RegistryConfig registryConfig = <span class="hljs-keyword">new</span> RegistryConfig();\n        registryConfig.setAddress(<span class="hljs-string">"zookeeper://127.0.0.1:2181"</span>);\n        registryConfig.setClient(<span class="hljs-string">"curator"</span>);\n        <span class="hljs-keyword">return</span> registryConfig;\n    }\n}\n</code></pre>\n<h3>Path to scan</h3>\n<pre><code class="language-java"><span class="hljs-meta">@SpringBootApplication</span>\n<span class="hljs-meta">@DubboComponentScan</span>(basePackages = <span class="hljs-string">"com.alibaba.dubbo.test.service.impl"</span>)\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ProviderTestApp</span> </span>{\n    <span class="hljs-comment">// ...</span>\n}\n</code></pre>\n<h2>Consumer Side</h2>\n<h3><code>Reference</code> annotation for reference</h3>\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">AnnotationConsumeService</span> </span>{\n\n    <span class="hljs-meta">@com</span>.alibaba.dubbo.config.annotation.Reference\n    <span class="hljs-keyword">public</span> AnnotateService annotateService;\n    \n    <span class="hljs-comment">// ...</span>\n}\n\n</code></pre>\n<h3>Use JavaConfig for common parts</h3>\n<pre><code class="language-java"><span class="hljs-meta">@Configuration</span>\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DubboConfiguration</span> </span>{\n\n    <span class="hljs-meta">@Bean</span>\n    <span class="hljs-function"><span class="hljs-keyword">public</span> ApplicationConfig <span class="hljs-title">applicationConfig</span><span class="hljs-params">()</span> </span>{\n        ApplicationConfig applicationConfig = <span class="hljs-keyword">new</span> ApplicationConfig();\n        applicationConfig.setName(<span class="hljs-string">"consumer-test"</span>);\n        <span class="hljs-keyword">return</span> applicationConfig;\n    }\n\n    <span class="hljs-meta">@Bean</span>\n    <span class="hljs-function"><span class="hljs-keyword">public</span> ConsumerConfig <span class="hljs-title">consumerConfig</span><span class="hljs-params">()</span> </span>{\n        ConsumerConfig consumerConfig = <span class="hljs-keyword">new</span> ConsumerConfig();\n        consumerConfig.setTimeout(<span class="hljs-number">3000</span>);\n        <span class="hljs-keyword">return</span> consumerConfig;\n    }\n\n    <span class="hljs-meta">@Bean</span>\n    <span class="hljs-function"><span class="hljs-keyword">public</span> RegistryConfig <span class="hljs-title">registryConfig</span><span class="hljs-params">()</span> </span>{\n        RegistryConfig registryConfig = <span class="hljs-keyword">new</span> RegistryConfig();\n        registryConfig.setAddress(<span class="hljs-string">"zookeeper://127.0.0.1:2181"</span>);\n        registryConfig.setClient(<span class="hljs-string">"curator"</span>);\n        <span class="hljs-keyword">return</span> registryConfig;\n    }\n}\n</code></pre>\n<h3>Path to scan</h3>\n<pre><code class="language-java"><span class="hljs-meta">@SpringBootApplication</span>\n<span class="hljs-meta">@DubboComponentScan</span>(basePackages = <span class="hljs-string">"com.alibaba.dubbo.test.service"</span>)\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ConsumerTestApp</span> </span>{\n    <span class="hljs-comment">// ...</span>\n}\n</code></pre>\n<h2>NOTES</h2>\n<p>All annotations in 2.5.7 will be removed later, if you have used these annotations in your project, please upgrade to the latest version.</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:annotation</span> <span class="hljs-attr">package</span>=<span class="hljs-string">"com.alibaba.dubbo.test.service"</span> /&gt;</span> \n</code></pre>\n'},{filename:"user/configuration/api.md",__html:'<h1>API Configuration</h1>\n<p>All API properties have counterparts in XML, see <a href="../references/xml/introduction.md">XML References</a> for details. For example <code>ApplicationConfig.setName(&quot;xxx&quot;)</code> equals to  <code>&lt;dubbo:application name=&quot;xxx&quot; /&gt;</code> <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup></p>\n<h2>Provider Side</h2>\n<pre><code class="language-java"><span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.config.ApplicationConfig;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.config.RegistryConfig;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.config.ProviderConfig;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.config.ServiceConfig;\n<span class="hljs-keyword">import</span> com.xxx.XxxService;\n<span class="hljs-keyword">import</span> com.xxx.XxxServiceImpl;\n \n<span class="hljs-comment">// Implementation</span>\nXxxService xxxService = <span class="hljs-keyword">new</span> XxxServiceImpl();\n \n<span class="hljs-comment">// Application Info</span>\nApplicationConfig application = <span class="hljs-keyword">new</span> ApplicationConfig();\napplication.setName(<span class="hljs-string">"xxx"</span>);\n \n<span class="hljs-comment">// Registry Info</span>\nRegistryConfig registry = <span class="hljs-keyword">new</span> RegistryConfig();\nregistry.setAddress(<span class="hljs-string">"10.20.130.230:9090"</span>);\nregistry.setUsername(<span class="hljs-string">"aaa"</span>);\nregistry.setPassword(<span class="hljs-string">"bbb"</span>);\n \n<span class="hljs-comment">// Protocol</span>\nProtocolConfig protocol = <span class="hljs-keyword">new</span> ProtocolConfig();\nprotocol.setName(<span class="hljs-string">"dubbo"</span>);\nprotocol.setPort(<span class="hljs-number">12345</span>);\nprotocol.setThreads(<span class="hljs-number">200</span>);\n \n<span class="hljs-comment">// NOTES: ServiceConfig holds the serversocket instance and keeps connections to registry, please cache it for performance.</span>\n \n<span class="hljs-comment">// Exporting</span>\nServiceConfig&lt;XxxService&gt; service = <span class="hljs-keyword">new</span> ServiceConfig&lt;XxxService&gt;(); <span class="hljs-comment">// In case of memory leak, please cache.</span>\nservice.setApplication(application);\nservice.setRegistry(registry); <span class="hljs-comment">// Use setRegistries() for multi-registry case</span>\nservice.setProtocol(protocol); <span class="hljs-comment">// Use setProtocols() for multi-protocol case</span>\nservice.setInterface(XxxService.class);\nservice.setRef(xxxService);\nservice.setVersion(<span class="hljs-string">"1.0.0"</span>);\n \n<span class="hljs-comment">// Local export and register</span>\nservice.export();\n</code></pre>\n<h2>Consumer Side</h2>\n<pre><code class="language-java"><span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.config.ApplicationConfig;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.config.RegistryConfig;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.config.ConsumerConfig;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.config.ReferenceConfig;\n<span class="hljs-keyword">import</span> com.xxx.XxxService;\n \n<span class="hljs-comment">// Application Info</span>\nApplicationConfig application = <span class="hljs-keyword">new</span> ApplicationConfig();\napplication.setName(<span class="hljs-string">"yyy"</span>);\n \n<span class="hljs-comment">// Registry Info</span>\nRegistryConfig registry = <span class="hljs-keyword">new</span> RegistryConfig();\nregistry.setAddress(<span class="hljs-string">"10.20.130.230:9090"</span>);\nregistry.setUsername(<span class="hljs-string">"aaa"</span>);\nregistry.setPassword(<span class="hljs-string">"bbb"</span>);\n \n<span class="hljs-comment">// NOTES: ReferenceConfig holds the connections to registry and providers, please cache it for performance.</span>\n \n<span class="hljs-comment">// Refer remote service</span>\nReferenceConfig&lt;XxxService&gt; reference = <span class="hljs-keyword">new</span> ReferenceConfig&lt;XxxService&gt;(); <span class="hljs-comment">// In case of memory leak, please cache.</span>\nreference.setApplication(application);\nreference.setRegistry(registry); \nreference.setInterface(XxxService.class);\nreference.setVersion(<span class="hljs-string">"1.0.0"</span>);\n \n<span class="hljs-comment">// Use xxxService just like a local bean</span>\nXxxService xxxService = reference.get(); <span class="hljs-comment">// NOTES: Please cache this proxy instance.</span>\n</code></pre>\n<h2>Specials</h2>\n<p>Only care about the differences:</p>\n<h3>Configuration of Method level</h3>\n<pre><code class="language-java">...\n \n<span class="hljs-comment">// Method level config</span>\nList&lt;MethodConfig&gt; methods = <span class="hljs-keyword">new</span> ArrayList&lt;MethodConfig&gt;();\nMethodConfig method = <span class="hljs-keyword">new</span> MethodConfig();\nmethod.setName(<span class="hljs-string">"createXxx"</span>);\nmethod.setTimeout(<span class="hljs-number">10000</span>);\nmethod.setRetries(<span class="hljs-number">0</span>);\nmethods.add(method);\n \n<span class="hljs-comment">// Referring</span>\nReferenceConfig&lt;XxxService&gt; reference = <span class="hljs-keyword">new</span> ReferenceConfig&lt;XxxService&gt;();\n...\nreference.setMethods(methods); \n \n...\n</code></pre>\n<h3>Peer to Peer</h3>\n<pre><code class="language-java">\n...\n \nReferenceConfig&lt;XxxService&gt; reference = <span class="hljs-keyword">new</span> ReferenceConfig&lt;XxxService&gt;(); \n<span class="hljs-comment">// If you know the address of the provider and want to bypass the registry, use `reference.setUrl()` to specify the provider directly. Refer [How to Invoke a specific provider](../demos/explicit-target.md) for details.</span>\nreference.setUrl(<span class="hljs-string">"dubbo://10.20.130.230:20880/com.xxx.XxxService"</span>); \n \n...\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>When should we usd API: API is very useful for integrating with systems like OpenAPI, ESB, Test, Mock, etc. General Providers and Consumers, we still recommend use <a href="../configuration/xml.md">XML Configuration</a>. <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/configuration/index.md",__html:"<h1>Configuration</h1>\n"},{filename:"user/configuration/properties.md",__html:'<h1>Properties Configuration</h1>\n<p>If your application is simple enough, say, you do not need multi-registries or multi-protocols, and you want to share configuration among Spring containers. You can use <code>dubbo.properties</code> as default configuration.</p>\n<p>Dubbo will load dubbo.properties under the root of classpath automatically, you can also specify the path for loading this file by using JVM parameter: <code>-Ddubbo.properties.file=xxx.properties</code>.</p>\n<h2>Mapping Rules</h2>\n<p>Combine the tag name and attribute name of the XML tag, use <code>.</code> to split. One property per line.</p>\n<ul>\n<li><code>dubbo.application.name=foo</code> equals to <code>&lt;dubbo:application name=&quot;foo&quot; /&gt;</code></li>\n<li><code>dubbo.registry.address=10.20.153.10:9090</code> equals to <code>&lt;dubbo:registry address=&quot;10.20.153.10:9090&quot; /&gt;</code></li>\n</ul>\n<p>If you have more than one tags in a XML configuration, you can use the <code>id</code> value to distinguish. If you don\'t specify a id, ti will applied to all tags.</p>\n<ul>\n<li><code>dubbo.protocol.rmi.port=1234</code> equals to <code>&lt;dubbo:protocol id=&quot;rmi&quot; name=&quot;rmi&quot; port=&quot;1099&quot; /&gt;</code></li>\n<li><code>dubbo.registry.china.address=10.20.153.10:9090</code> equals to <code>&lt;dubbo:registry id=&quot;china&quot; address=&quot;10.20.153.10:9090&quot; /&gt;</code></li>\n</ul>\n<p>Here is a typical dubbo.properties demo configuration:</p>\n<pre><code class="language-properties">dubbo.application.name=foo\ndubbo.application.owner=bar\ndubbo.registry.address=10.20.153.10:9090\n</code></pre>\n<h2>Overrides and Priorities</h2>\n<p><img src="../sources/images/dubbo-properties-override.jpg" alt="properties-override"></p>\n<p>Priorities from high to low:</p>\n<ul>\n<li>\n<p>JVM -D parameters, you can easily override configuration when deploying or starting applications, e.g., change the port of dubbo protocol.</p>\n</li>\n<li>\n<p>XML, the properties present in XML will override that in dubbo.properties.</p>\n</li>\n<li>\n<p>Properties, the default value, only works when it is not configured with XML or JVM.</p>\n</li>\n</ul>\n<p>1: If more than one dubbo.properties under classpath, say, two jars contains dubbo.properties separately, Dubbo will arbitarily choose one to to load, and log Error info.<br>\n2: If <code>id</code> not configured on <code>protocol</code>, will use <code>name</code> property as default</p>\n'},{filename:"user/configuration/xml.md",__html:'<h1>XML Configuration</h1>\n<p>About the XML configuration items, see:<a href="../references/xml/introduction.md">XML References</a>. If you prefer use API directly instead of using Spring, see <a href="./api.md">API Configuration</a>. Want a example of how to use configuration, see <a href="../quick-start.md">Quick Start</a>。</p>\n<h2>provider.xml demo</h2>\n<pre><code class="language-xml"><span class="php"><span class="hljs-meta">&lt;?</span>xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"UTF-8"</span><span class="hljs-meta">?&gt;</span></span>\n<span class="hljs-tag">&lt;<span class="hljs-name">beans</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans"</span>\n    <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>\n    <span class="hljs-attr">xmlns:dubbo</span>=<span class="hljs-string">"http://dubbo.apache.org/schema/dubbo"</span>\n    <span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"</span>&gt;</span>  \n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:application</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"hello-world-app"</span>  /&gt;</span>  \n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"multicast://224.5.6.7:1234"</span> /&gt;</span>  \n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"20880"</span> /&gt;</span>  \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.demo.DemoService"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"demoServiceLocal"</span> /&gt;</span>  \n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"demoServiceRemote"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.alibaba.dubbo.demo.DemoService"</span> /&gt;</span>  \n<span class="hljs-tag">&lt;/<span class="hljs-name">beans</span>&gt;</span>\n</code></pre>\n<p>All tags support custom parameters, so we can meet the special config requirements at different extension points, such as:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"jms"</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:parameter</span> <span class="hljs-attr">key</span>=<span class="hljs-string">"queue"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"your_queue"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:protocol</span>&gt;</span>\n</code></pre>\n<p>Or:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">beans</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans"</span>\n    <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>\n    <span class="hljs-attr">xmlns:dubbo</span>=<span class="hljs-string">"http://dubbo.apache.org/schema/dubbo"</span>\n    <span class="hljs-attr">xmlns:p</span>=<span class="hljs-string">"http://www.springframework.org/schema/p"</span>\n    <span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"</span>&gt;</span>  \n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"jms"</span> <span class="hljs-attr">p:queue</span>=<span class="hljs-string">"your_queue"</span> /&gt;</span>  \n<span class="hljs-tag">&lt;/<span class="hljs-name">beans</span>&gt;</span>\n</code></pre>\n<h2>The relations between configuration tags</h2>\n<p><img src="../sources/images/dubbo-config.jpg" alt="dubbo-config"></p>\n<table>\n<thead>\n<tr>\n<th>tag</th>\n<th>purpose</th>\n<th>introduction</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>&lt;dubbo:service/&gt;</code></td>\n<td>Service Export</td>\n<td>Used to export service, define service metadata, export service with mutiple protocols, register service to multiple registries</td>\n</tr>\n<tr>\n<td><code>&lt;dubbo:reference/&gt;</code></td>\n<td>Service Reference</td>\n<td>Used to create a remote proxy, subscribe to multiple registries</td>\n</tr>\n<tr>\n<td><code>&lt;dubbo:protocol/&gt;</code></td>\n<td>Protocol Config</td>\n<td>Configure the protocol for services on provider side, the consumer side follows.</td>\n</tr>\n<tr>\n<td><code>&lt;dubbo:application/&gt;</code></td>\n<td>Application Config</td>\n<td>Applies to both provider and consumer.</td>\n</tr>\n<tr>\n<td><code>&lt;dubbo:module/&gt;</code></td>\n<td>Module Config</td>\n<td>Optional.</td>\n</tr>\n<tr>\n<td><code>&lt;dubbo:registry/&gt;</code></td>\n<td>Registry Center</td>\n<td>Registry info: address, protocol, etc.</td>\n</tr>\n<tr>\n<td><code>&lt;dubbo:monitor/&gt;</code></td>\n<td>Monitor Center</td>\n<td>Monitor info: address, address, etc. Optional.</td>\n</tr>\n<tr>\n<td><code>&lt;dubbo:provider/&gt;</code></td>\n<td>Default Config for Providers</td>\n<td>Default Config for ServiceConfigs. Optional.</td>\n</tr>\n<tr>\n<td><code>&lt;dubbo:consumer/&gt;</code></td>\n<td>Default Config for Consumers</td>\n<td>Default Config for ReferenceConfigs. Optional.</td>\n</tr>\n<tr>\n<td><code>&lt;dubbo:method/&gt;</code></td>\n<td>Method level Config</td>\n<td>Method level Config for ServiceConfig and ReferenceConfig.</td>\n</tr>\n<tr>\n<td><code>&lt;dubbo:argument/&gt;</code></td>\n<td>Argument Config</td>\n<td>Used to specify the method parameter configuration.</td>\n</tr>\n</tbody>\n</table>\n<h2>Overrides and Priorities</h2>\n<p>Take timeout as an example, here is the priorities, from high to low (retries, loadbalance, actives also applies  the same rule):</p>\n<ul>\n<li>method level,interface level,default/global level。</li>\n<li>at the same leveel, consumer has higher priority than provider</li>\n</ul>\n<p>Configurations on the provider side are passed to the consumer side through registry in the form of URL.</p>\n<p><img src="../sources/images/dubbo-config-override.jpg" alt="dubbo-config-override"></p>\n<p>It is recommended that the provider set a timeout for every service, because the provider knows exactly how long a method needs to be executed. If a consumer cites multiple services at the same time, it doesn\'t need to care about the timeout settings of each service.</p>\n<p>Theoretically, almost all configuration items supported in ReferenceConfig can be configured with a default value using ConsumerConfig, ServiceConfig, ProviderConfig.</p>\n<p>1: Requires spring <code>3.2.16+</code>, see announcement for details:<code>xmlns:p=&quot;http://www.springframework.org/schema/p&quot;</code>  \n2: The reference bean obeys lazy init by default, only if it is refered by other beans or other instance try to get its instance using <code>getBean()</code> method  will the reference be initialized. If you need eager init, config this way: <code>&lt;dubbo:reference ... init=&quot;true&quot; /&gt;</code></p>\n'},{filename:"user/coveragence.md",__html:'<h1>Test coverage report</h1>\n<p>Based on version <code>2.0.12</code>,Statistics on 2012-02-03</p>\n<p><img src="sources/images/code-quality1.jpg" alt="/sources/images/code-quality1.jpg"></p>\n<p><img src="sources/images/code-quality2.jpg" alt="/sources/images/code-quality2.jpg"></p>\n<p><img src="sources/images/code-quality3.jpg" alt="/sources/images/code-quality3.jpg"></p>\n<p><img src="sources/images/code-quality4.jpg" alt="/sources/images/code-quality4.jpg"></p>\n<p><img src="sources/images/code-quality5.jpg" alt="/sources/images/code-quality5.jpg"></p>\n<p><img src="sources/images/code-quality6.jpg" alt="/sources/images/code-quality6.jpg"></p>\n<p><img src="sources/images/code-quality7.jpg" alt="/sources/images/code-quality7.jpg"></p>\n<p><img src="sources/images/code-coverage.jpg" alt="/sources/images/code-coverage.jpg"></p>\n<p><img src="sources/images/code-tendency.jpg" alt="/sources/images/code-tendency.jpg"></p>\n<p><img src="sources/images/code-dependency.jpg" alt="/sources/images/code-dependency.jpg"></p>\n'},{filename:"user/demos/accesslog.md",__html:'<h1>Access Log</h1>\n<p>If you want to logging the access information for each provide service,you can turn on the <code>accesslog</code> switch,which like the access log of <code>Apache</code>.</p>\n<p><strong>Note:</strong>\nThe size of the access log maybe too much,please check the disk capacity.\nNow I will show you how to config the access log.</p>\n<h2>Logging by logging framework</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">accesslog</span>=<span class="hljs-string">"true"</span> <span class="hljs-attr">...</span>/&gt;</span>\n</code></pre>\n<p>The above configuration will turn on <code>accesslog</code> switch for all provide services,and logging the access log with logging framework(log4j/logback/slf4j...).You can config the logging framework of <code>logger</code> and <code>appender</code> for logging the access log.The simplest way is config logger name with <code>dubbo.accesslog</code>. The Example:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">appender</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"accesslogAppender"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"ch.qos.logback.core.rolling.RollingFileAppender"</span>&gt;</span>\n        <span class="hljs-tag">&lt;<span class="hljs-name">file</span>&gt;</span>${loggingRoot}/accesslog/logging.log<span class="hljs-tag">&lt;/<span class="hljs-name">file</span>&gt;</span>\n        <span class="hljs-tag">&lt;<span class="hljs-name">encoding</span>&gt;</span>${loggingCharset}<span class="hljs-tag">&lt;/<span class="hljs-name">encoding</span>&gt;</span>\n        <span class="hljs-tag">&lt;<span class="hljs-name">append</span>&gt;</span>true<span class="hljs-tag">&lt;/<span class="hljs-name">append</span>&gt;</span>\n        <span class="hljs-tag">&lt;<span class="hljs-name">rollingPolicy</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"ch.qos.logback.core.rolling.TimeBasedRollingPolicy"</span>&gt;</span>\n            <span class="hljs-tag">&lt;<span class="hljs-name">FileNamePattern</span>&gt;</span>${loggingRoot}/accesslog/%d{yyyyMMdd}/logging.log.%d{yyyyMMdd}%i.gz\n            <span class="hljs-tag">&lt;/<span class="hljs-name">FileNamePattern</span>&gt;</span>\n            <span class="hljs-tag">&lt;<span class="hljs-name">MaxHistory</span>&gt;</span>15<span class="hljs-tag">&lt;/<span class="hljs-name">MaxHistory</span>&gt;</span>\n            <span class="hljs-tag">&lt;<span class="hljs-name">TimeBasedFileNamingAndTriggeringPolicy</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"</span>&gt;</span>\n                <span class="hljs-tag">&lt;<span class="hljs-name">MaxFileSize</span>&gt;</span>1024MB<span class="hljs-tag">&lt;/<span class="hljs-name">MaxFileSize</span>&gt;</span>\n            <span class="hljs-tag">&lt;/<span class="hljs-name">TimeBasedFileNamingAndTriggeringPolicy</span>&gt;</span>\n        <span class="hljs-tag">&lt;/<span class="hljs-name">rollingPolicy</span>&gt;</span>\n        <span class="hljs-tag">&lt;<span class="hljs-name">layout</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"ch.qos.logback.classic.PatternLayout"</span>&gt;</span>\n            <span class="hljs-tag">&lt;<span class="hljs-name">pattern</span>&gt;</span>&lt;![CDATA[%level|%d{yyyy-MM-dd HH:mm:ss}|%m%n}]]&gt;<span class="hljs-tag">&lt;/<span class="hljs-name">pattern</span>&gt;</span>\n        <span class="hljs-tag">&lt;/<span class="hljs-name">layout</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">appender</span>&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">logger</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo.accesslog"</span> <span class="hljs-attr">level</span>=<span class="hljs-string">"INFO"</span> <span class="hljs-attr">additivity</span>=<span class="hljs-string">"false"</span>&gt;</span>\n        <span class="hljs-tag">&lt;<span class="hljs-name">appender-ref</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"accesslogAppender"</span>/&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">logger</span>&gt;</span>\n</code></pre>\n<p>The above is the demonstration of logback framework.Other logging framework is same <a href="http://too.It">too.It</a> will logging the access log of all provide services into single file(<code>accesslog/logging.log</code>). And you can also config the access log of each provide service to logging separately,Only change <code>name</code> attribute of the <code>logger</code> tag,set the <code>name</code> attribute to <code>dubbo.accesslog.serviceInterfaceClassFullName</code>.The Example:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">logger</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo.accesslog.com.dubbo.FooServiceInterface"</span> <span class="hljs-attr">level</span>=<span class="hljs-string">"INFO"</span> <span class="hljs-attr">additivity</span>=<span class="hljs-string">"false"</span>&gt;</span>\n        <span class="hljs-tag">&lt;<span class="hljs-name">appender-ref</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"fooServiceAccesslogAppender"</span>/&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">logger</span>&gt;</span>\n</code></pre>\n<p>If you only want logging the access log of specified provide service,but not all <a href="http://services.It">services.It</a>\'s supported too.The Example:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">accesslog</span>=<span class="hljs-string">"true"</span> <span class="hljs-attr">...</span>/&gt;</span>\n</code></pre>\n<h2>Logging by specified file path</h2>\n<p>You can specify the file path with the <code>accesslog</code> attribute.The Example:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">accesslog</span>=<span class="hljs-string">"/home/admin/logs/service/accesslog.log"</span> <span class="hljs-attr">...</span>/&gt;</span>\n</code></pre>\n<p>OR</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">accesslog</span>=<span class="hljs-string">"/home/admin/logs/service/accesslog.log"</span> <span class="hljs-attr">...</span>/&gt;</span>\n</code></pre>\n'},{filename:"user/demos/async-call.md",__html:'<h1>Asynchronous Call</h1>\n<p>As dubbo is based on a non-blocking NIO network layer, the client can start parallel call to multiple remote services without explicitly starting mulithreads, which costs relatively fewer resources.</p>\n<p><img src="../sources/images/future.jpg" alt="/user-guide/images/future.jpg"></p>\n<p>You can config at <code>consumer.xml</code> for setup asynchronous call some remote service.</p>\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">"fooService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.alibaba.foo.FooService"</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">"findFoo"</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<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"barService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.alibaba.bar.BarService"</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">"findBar"</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>Configure the above configuration information,you can invoke the remote service in your code.</p>\n<pre><code class="language-java"><span class="hljs-comment">// the invoke will return null immediately</span>\nfooService.findFoo(fooId);\n<span class="hljs-comment">// get current invoke Future instance,when the remote service has return result,will notify this Future instance.</span>\nFuture&lt;Foo&gt; fooFuture = RpcContext.getContext().getFuture();\n\n<span class="hljs-comment">// the invoke will return null immediately</span>\nbarService.findBar(barId);\n<span class="hljs-comment">// get current invoke Future instance,when the remote service has return result,will notify this Future instance.</span>\nFuture&lt;Bar&gt; barFuture = RpcContext.getContext().getFuture();\n\n<span class="hljs-comment">// now the request of findFoo and findBar was executed at same time,The client not need setup multithreading for parallel call, which is NIO-based non-blocking implementation of parallel calls</span>\n\n<span class="hljs-comment">// Current thread will be blocking,and wait findFoo has return. when remote service has return findFoo result,the current thread will be wake up.</span>\nFoo foo = fooFuture.get();\n<span class="hljs-comment">// same to findFoo</span>\nBar bar = barFuture.get();\n\n<span class="hljs-comment">// if findFoo expend five second for wait remote service  return result,and findBar expend six second. Actually,only expend six second will get findFoo and findBar result,and proceed to the next step.</span>\n</code></pre>\n<p>You can also set whether to wait for the message to be sent:</p>\n<ul>\n<li><code>sent=&quot;true&quot;</code> wait for the message to be send,if send failure,will throw exception.</li>\n<li><code>sent=&quot;false&quot;</code> do not wait for the message to be send,when the message will push into io queue,will return immediately.</li>\n</ul>\n<p>The Example:</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">"findFoo"</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>if you only want to asynchronous call,and don\'t care the return.you can config <code>return=&quot;false&quot;</code>,To reduce the cost of creating and managing Future objects.</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">"findFoo"</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><strong>Note</strong>\n<code>2.0.6+</code> version supported.</p>\n'},{filename:"user/demos/attachment.md",__html:'<h1>Implicit parameters</h1>\n<p>You can implicitly pass parameters between service consumers and providers via <code>setAttachment</code> and<code>getAttachment</code> on <code>RpcContext</code>.\n<img src="../sources/images/context.png" alt="/user-guide/images/context.png"></p>\n<h2>Set the implicit parameters at service consumer side</h2>\n<p>Via <code>setAttachment</code> on <code>RpcContext</code> set key/value pair for implicitly pass parameters.When finished once remote invoke,will be clear,so multi-invoke must set multi-times.</p>\n<pre><code class="language-java">RpcContext.getContext().setAttachment(<span class="hljs-string">"index"</span>, <span class="hljs-string">"1"</span>); <span class="hljs-comment">// implicitly pass parameters,behind the remote call will implicitly send these parameters to the server side, similar to the cookie, for the framework of integration, not recommended for regular business use</span>\nxxxService.xxx(); <span class="hljs-comment">// remote call</span>\n<span class="hljs-comment">// ...</span>\n</code></pre>\n<h2>Fetch the implicit parameters at service provider side</h2>\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">XxxServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">XxxService</span> </span>{\n\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">xxx</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-comment">// get parameters which passed by the consumer side,for the framework of integration, not recommended for regular business use</span>\n        String index = RpcContext.getContext().getAttachment(<span class="hljs-string">"index"</span>);\n    }\n}\n</code></pre>\n'},{filename:"user/demos/callback-parameter.md",__html:'<h1>Callback parameter</h1>\n<p>The parameter callback is the same as calling a local callback or listener, just declare which parameter is a callback type in Spring\'s configuration file, and Dubbo will generate a reverse proxy based on the long connection so that client logic can be called from the server.Can ref to <a href="https://github.com/apache/incubator-dubbo/tree/master/dubbo-test/dubbo-test-examples/src/main/java/com/alibaba/dubbo/examples/callback">Sample code in the dubbo project</a>.</p>\n<h2>Example of service interface</h2>\n<h3>CallbackService.java</h3>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.callback;\n\n<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</code></pre>\n<h3>CallbackListener.java</h3>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.callback;\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<h2>Example of service provider interface implementation</h2>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.callback.impl;\n\n<span class="hljs-keyword">import</span> java.text.SimpleDateFormat;\n<span class="hljs-keyword">import</span> java.util.Date;\n<span class="hljs-keyword">import</span> java.util.Map;\n<span class="hljs-keyword">import</span> java.util.concurrent.ConcurrentHashMap;\n\n<span class="hljs-keyword">import</span> com.callback.CallbackListener;\n<span class="hljs-keyword">import</span> com.callback.CallbackService;\n\n<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">// Timed trigger change notification</span>\n                    } <span class="hljs-keyword">catch</span> (Throwable t) { <span class="hljs-comment">// Defense fault tolerance</span>\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 change notification</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<h2>Example of service provider configuration</h2>\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.callback.impl.CallbackServiceImpl"</span> /&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.callback.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;!--also can via specified argument type--&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<h2>Example of service consumer configuration</h2>\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">"callbackService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.callback.CallbackService"</span> /&gt;</span>\n</code></pre>\n<h2>Example of service consumer call</h2>\n<pre><code class="language-java">ClassPathXmlApplicationContext context = <span class="hljs-keyword">new</span> ClassPathXmlApplicationContext(<span class="hljs-string">"classpath:consumer.xml"</span>);\ncontext.start();\n\nCallbackService callbackService = (CallbackService) context.getBean(<span class="hljs-string">"callbackService"</span>);\n\ncallbackService.addListener(<span class="hljs-string">"http://10.20.160.198/wiki/display/dubbo/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><strong>NOTE</strong> <code>2.0.6+</code> version supported.</p>\n'},{filename:"user/demos/concurrency-control.md",__html:'<h1>Parallel control</h1>\n<h2>Example of configuration</h2>\n<ul>\n<li>Example 1: Control the concurrency of all method for a specified service interface at server-side</li>\n</ul>\n<p>Limit each method of <code>com.foo.BarService</code> to no more than 10 concurrent server-side executions (or take up thread pool threads):</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">executes</span>=<span class="hljs-string">"10"</span> /&gt;</span>\n</code></pre>\n<ul>\n<li>Example 2: Control the concurrency of specified method for a specified service interface at server-side</li>\n</ul>\n<p>Limit the <code>sayHello</code> method of <code>com.foo.BarService</code> to no more than 10 concurrent server-side executions(or take up thread pool threads):</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</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">executes</span>=<span class="hljs-string">"10"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:service</span>&gt;</span>\n</code></pre>\n<ul>\n<li>Example 3: Control the concurrency of all method for a specified service interface at client-side\nLimit each method of <code>com.foo.BarService</code> to no more than 10 concurrent client-side executions (or take up thread pool threads):</li>\n</ul>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">actives</span>=<span class="hljs-string">"10"</span> /&gt;</span>\n</code></pre>\n<p>OR</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">actives</span>=<span class="hljs-string">"10"</span> /&gt;</span>\n</code></pre>\n<ul>\n<li>Example 4: Control the concurrency of specified method for a specified service interface at client-side\nLimit the <code>sayHello</code> method of <code>com.foo.BarService</code> to no more than 10 concurrent client-side executions(or take up thread pool threads):</li>\n</ul>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</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">actives</span>=<span class="hljs-string">"10"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:service</span>&gt;</span>\n</code></pre>\n<p>OR</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</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">actives</span>=<span class="hljs-string">"10"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:service</span>&gt;</span>\n</code></pre>\n<p>If <code>&lt;dubbo:service&gt;</code> and <code>&lt;dubbo:reference&gt;</code> are both configured with <code>actives</code>,<code>&lt;dubbo:reference&gt;</code> is preferred.Ref to:<a href="./config-rule.md">Configuration coverage strategy</a>.</p>\n<h2>Load Balance</h2>\n<p>You can config the <code>loadbalance</code> attribute with <code>leastactive</code> at server-side or client-side,then the framework will make consumer call the minimum number of concurrent one.</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">loadbalance</span>=<span class="hljs-string">"leastactive"</span> /&gt;</span>\n</code></pre>\n<p>OR</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">loadbalance</span>=<span class="hljs-string">"leastactive"</span> /&gt;</span>\n</code></pre>\n'},{filename:"user/demos/config-connections.md",__html:'<h1>Config connections</h1>\n<h2>Control connections at server-side</h2>\n<p>Limit server-side accept to no more than 10 connections</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">accepts</span>=<span class="hljs-string">"10"</span> /&gt;</span>\n</code></pre>\n<p>OR</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">accepts</span>=<span class="hljs-string">"10"</span> /&gt;</span>\n</code></pre>\n<h2>Control connections at client-side</h2>\n<p>Limit client-side creating connection to no more than 10 connections for interface <code>com.foo.BarService</code>.</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">connections</span>=<span class="hljs-string">"10"</span> /&gt;</span>\n</code></pre>\n<p>OR</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">connections</span>=<span class="hljs-string">"10"</span> /&gt;</span>\n</code></pre>\n<p><strong>NOTE:</strong> If used default protocol(<code>dubbo</code> protocol), and the value of  <code>connections</code> attribute is great than 0,then each service reference will has itself connection,else all service which belong to same remote server will share only one connection. In this framework,we called <code>private</code> connection or <code>share</code> connection.</p>\n<p>If <code>&lt;dubbo:service&gt;</code> and <code>&lt;dubbo:reference&gt;</code> are both configured accepts/connections,<code>&lt;dubbo:reference&gt;</code> is preferred,Ref to <a href="http://dubbo.apache.org/books/dubbo-user-book-en/demos/config-rule.html">Configuration coverage strategy</a>.</p>\n<ul>\n<li>: Because connection is connect on Server,so configure at Provider.</li>\n</ul>\n'},{filename:"user/demos/config-rule.md",__html:'<h1>Configure rule</h1>\n<p>Write then dynamic configuration to the registry center,This feature is usually done by the monitoring center or the center\'s page.</p>\n<pre><code class="language-java">RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();\nRegistry registry = registryFactory.getRegistry(URL.valueOf(<span class="hljs-string">"zookeeper://10.20.153.10:2181"</span>));\nregistry.register(URL.valueOf(<span class="hljs-string">"override://0.0.0.0/com.foo.BarService?category=configurators&amp;dynamic=false&amp;application=foo&amp;timeout=1000"</span>));\n</code></pre>\n<p>In the config override url:</p>\n<ul>\n<li><code>override://</code> Indicates that the data is overwritten,support <code>override</code> and  <code>absent</code>,can extends,<strong>Required</strong>.</li>\n<li><code>0.0.0.0</code> Indicates that the configurations is valid for all IP addresses,If only want to overwritten specified ip data,you can replace that specified ip address.<strong>Required</strong>.</li>\n<li><code>com.foo.BarService</code> Indicates that is valid for specified service,<strong>Required</strong>.</li>\n<li><code>category=configurators</code> Indicates that the data is dynamic configuration,<strong>Required</strong>。</li>\n<li><code>dynamic=false</code> Indicates that the data is persistent,When the registered party withdraws,the data is still stored in the registry <strong>Required</strong>。</li>\n<li><code>enabled=true</code> override strategy is enable,can absent,if absent,then enable.</li>\n<li><code>application=foo</code> Indicates that is valid for specified application,can absent,if absent,then valid for all application.</li>\n<li><code>timeout=1000</code> Indicates that the value of the <code>timeout</code> parameter that satisfies the above conditions is overwritten by 1000,if want to override another parameters, add directly to the <code>override</code> URL parameter.</li>\n</ul>\n<p>Example:</p>\n<ol>\n<li>\n<p>Disable service provider.(Usually used to temporarily kick off a provider machine, similar to the prohibition of consumer access, please use the routing rules)</p>\n<pre><code>override://10.20.153.10/com.foo.BarService?category=configurators&amp;dynamic=false&amp;disbaled=true\n</code></pre>\n</li>\n<li>\n<p>Adjustment weight:(Usually used to capacity assessment,default is 100)</p>\n<pre><code>override://10.20.153.10/com.foo.BarService?category=configurators&amp;dynamic=false&amp;weight=200\n</code></pre>\n</li>\n<li>\n<p>Adjustment load balance strategy.(default random)</p>\n<pre><code>override://10.20.153.10/com.foo.BarService?category=configurators&amp;dynamic=false&amp;loadbalance=leastactive\n</code></pre>\n</li>\n<li>\n<p>Service downgrade:(Usually used to temporarily mask an error of non-critical services)</p>\n<pre><code>override://0.0.0.0/com.foo.BarService?category=configurators&amp;dynamic=false&amp;application=foo&amp;mock=force:return+null\n</code></pre>\n</li>\n</ol>\n<p><strong>NOTE</strong>: <code>2.2.0+</code> version supported.</p>\n'},{filename:"user/demos/context.md",__html:'<h1>Context Information</h1>\n<p>All environment information of during the current call will put into the context,and all configuration information will convert the parameters of <code>URL</code> instance,Ref to the column of <strong>URL parameters</strong> at the <a href="../references/xml/introduction.md">schema configuration reference book</a></p>\n<p><code>RpcContext</code> is a temporary status recorder of <code>ThreadLocal</code>,when accept <code>RPC</code> request or send <code>RPC</code> request,The <code>RpcContext</code> will be  changed.Such as: <code>A</code> call <code>B</code> and <code>B</code> call <code>C</code>. On <code>B</code> machine,before <code>B</code> call <code>C</code>,the <code>RpcContext</code> will record the information of <code>A</code> call <code>B</code>.After <code>B</code> call <code>C</code>,the <code>RpcContext</code> record the information of <code>B</code> call <code>C</code>.</p>\n<h2>At service consumer</h2>\n<pre><code class="language-java"><span class="hljs-comment">// remote invoke</span>\nxxxService.xxx();\n<span class="hljs-comment">// if return true,then the current side is consumer.</span>\n<span class="hljs-keyword">boolean</span> isConsumerSide = RpcContext.getContext().isConsumerSide();\n<span class="hljs-comment">// get the provider ip address of the last invoke.</span>\nString serverIP = RpcContext.getContext().getRemoteHost();\n<span class="hljs-comment">// because all configuration information has convert the URL\'s  parameters,so at this place can get the application parameter value.</span>\nString application = RpcContext.getContext().getUrl().getParameter(<span class="hljs-string">"application"</span>);\n<span class="hljs-comment">// Note:every rpc invoke,then context will be changed.</span>\nyyyService.yyy();\n</code></pre>\n<h2>At service provider</h2>\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">XxxServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">XxxService</span> </span>{\n\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">xxx</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-comment">// if return true,then the current side is provider.</span>\n        <span class="hljs-keyword">boolean</span> isProviderSide = RpcContext.getContext().isProviderSide();\n        <span class="hljs-comment">// get the invoker ip</span>\n        String clientIP = RpcContext.getContext().getRemoteHost();\n        <span class="hljs-comment">// because all configuration information has convert the URL\'s  parameters,so at this place can get the application parameter value.</span>\n        String application = RpcContext.getContext().getUrl().getParameter(<span class="hljs-string">"application"</span>);\n        <span class="hljs-comment">// Note:every rpc invoke,then context will be changed.</span>\n        yyyService.yyy();;\n    }\n}\n</code></pre>\n'},{filename:"user/demos/delay-publish.md",__html:'<h1>Delay publish service</h1>\n<p>If your service need time to warm up.such as:initialization cache,or another reference resources has to be <a href="http://ready.so">ready.so</a> you can use the delay feature for delay publish service.</p>\n<h2>Delay five second publish</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">delay</span>=<span class="hljs-string">"5000"</span> /&gt;</span>\n</code></pre>\n<h2>Delay until Spring initialization is complete before exposing the service</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">delay</span>=<span class="hljs-string">"-1"</span> /&gt;</span>\n</code></pre>\n<h3>The initialization deadlock problem of Spring 2.x</h3>\n<h4>Trigger condition</h4>\n<p>The service has already published when <code>Spring</code> parse the <code>&lt;dubbo:service /&gt;</code> element,but the <code>Spring</code> is still initializing other beans.If there is a request coming in, and the service implementation class has a call to <code>applicationContext.getBean ()</code> usage.</p>\n<ol>\n<li>\n<p>Request thread applicationContext.getBean() call, the first synchronization <code>singletonObjects</code> determine whether the existence of the bean, the synchronization does not exist to initialize the <code>beanDefinitionMap</code>, and re-synchronize <code>singletonObjects</code> write Bean instance cache.</p>\n<p><img src="../sources/images/lock-get-bean.jpg" alt="deadlock"></p>\n</li>\n<li>\n<p>But the <code>Spring</code> initialization thread,because need to determine the <code>Bean</code> is exist,Directly synchronize beanDefinitionMap to initialize, and synchronize singletonObjects write Bean instance cache.</p>\n<p><img src="../sources/images/lock-init-context.jpg" alt="/user-guide/images/lock-init-context.jpg"></p>\n</li>\n</ol>\n<p>This will cause the getBean thread to lock the singletonObjects first, then lock the beanDefinitionMap, and lock the singletonObjects again.The Spring initialization thread, the first lock beanDefinitionMap, then lock singletonObjects. Reverse lock thread deadlock, can not provide services, can not start.</p>\n<h4>Avoid ways</h4>\n<ol>\n<li>It is highly recommended not to call applicationContext.getBean() in the service implementation class, all using Spring\'s beans using IoC injection.</li>\n<li>If you really want to tune getBean(), you can put the configuration of Dubbo Spring final loading.</li>\n<li>If you do not want to rely on the configuration order, you can use <code>&lt;dubbo:provider delay =&quot;-1&quot;/&gt;</code> to make Dubbo expose the service after the Spring container has been initialized.</li>\n<li>If you use getBean() extensively, the equivalent of degenerating Spring to factory mode is to isolate Dubbo\'s service from a separate Spring container.</li>\n</ol>\n'},{filename:"user/demos/distributed-transaction.md",__html:'<h1>Distributed transaction</h1>\n<p>Distributed transactions are based on the JTA / XA specification(this feature has not yet been implemented)</p>\n<p>Two-phase commit:</p>\n<p><img src="../sources/images/jta-xa.jpg" alt="/user-guide/images/jta-xa.jpg"></p>\n'},{filename:"user/demos/dump.md",__html:'<h1>Dump</h1>\n<p>When the business thread pool is full, we need to know what resources/conditions are waiting for the thread , to find the bottleneck point of the system or abnormal point. <code>dubbo</code> automatically export thread stack through <code>Jstack</code> to keep the scene for easy to troubleshoot the problem.</p>\n<p>Default policy:</p>\n<ul>\n<li>Export file path,user.home directory</li>\n<li>Export interval,The shortest interval allows you to export every 10 minutes</li>\n</ul>\n<p>Specified export file path:</p>\n<pre><code class="language-properties"># dubbo.properties\ndubbo.application.dump.directory=/tmp\n</code></pre>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:application</span> <span class="hljs-attr">...</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:parameter</span> <span class="hljs-attr">key</span>=<span class="hljs-string">"dump.directory"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"/tmp"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:application</span>&gt;</span>\n</code></pre>\n'},{filename:"user/demos/echo-service.md",__html:'<h1>Echo Testing</h1>\n<p>Echo testing is used for check the service is available,Echo testing is performed according to the normal request flow and is able to test whether the entire call is unobstructed and can be used for monitoring.</p>\n<p>All the services will be automatically implemented <code>EchoService</code> interface,just cast any service reference to <code>EchoService</code> to use it.</p>\n<p>Spring configuration:</p>\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">"memberService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.xxx.MemberService"</span> /&gt;</span>\n</code></pre>\n<p>The java code:</p>\n<pre><code class="language-java"><span class="hljs-comment">// reference the remote service</span>\nMemberService memberService = ctx.getBean(<span class="hljs-string">"memberService"</span>);\n<span class="hljs-comment">// case the service reference to EchoService</span>\nEchoService echoService = (EchoService) memberService;\n\n<span class="hljs-comment">// Echo test usability</span>\nString status = echoService.$echo(<span class="hljs-string">"OK"</span>);\n\n<span class="hljs-keyword">assert</span>(status.equals(<span class="hljs-string">"OK"</span>));\n</code></pre>\n'},{filename:"user/demos/events-notify.md",__html:'<h1>Event Notify</h1>\n<p>Before calling, after calling, when an exception occurs,will trigger <code>oninvoke</code>, <code>onreturn</code>, <code>onthrow</code> events.You can configure which method to notify when an event occurs.</p>\n<h2>Service Interface</h2>\n<pre><code class="language-java"><span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">IDemoService</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Person <span class="hljs-title">get</span><span class="hljs-params">(<span class="hljs-keyword">int</span> id)</span></span>;\n}\n</code></pre>\n<h2>Service provider implement the service.</h2>\n<pre><code class="language-java"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">NormalDemoService</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">IDemoService</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> Person <span class="hljs-title">get</span><span class="hljs-params">(<span class="hljs-keyword">int</span> id)</span> </span>{\n        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Person(id, <span class="hljs-string">"charles`son"</span>, <span class="hljs-number">4</span>);\n    }\n}\n</code></pre>\n<h2>Service provider configure the service which it provided.</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:application</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rpc-callback-demo"</span> /&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"http://10.20.160.198/wiki/display/dubbo/10.20.153.186"</span> /&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">bean</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"demoService"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"com.alibaba.dubbo.callback.implicit.NormalDemoService"</span> /&gt;</span>\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.callback.implicit.IDemoService"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"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</code></pre>\n<h2>Declare the Callback interface at service consumer-side.</h2>\n<pre><code class="language-java"><span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Notify</span> </span>{\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">(Person msg, Integer id)</span></span>;\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, Integer id)</span></span>;\n}\n</code></pre>\n<h2>Implement the Callback at service consumer-side.</h2>\n<pre><code class="language-java"><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    <span class="hljs-keyword">public</span> Map&lt;Integer, Person&gt;    ret    = <span class="hljs-keyword">new</span> HashMap&lt;Integer, Person&gt;();\n    <span class="hljs-keyword">public</span> Map&lt;Integer, Throwable&gt; errors = <span class="hljs-keyword">new</span> HashMap&lt;Integer, Throwable&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">(Person msg, Integer id)</span> </span>{\n        System.out.println(<span class="hljs-string">"onreturn:"</span> + msg);\n        ret.put(id, msg);\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, Integer id)</span> </span>{\n        errors.put(id, ex);\n    }\n}\n</code></pre>\n<h2>Configure the Callback at service consumer-side.</h2>\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.callback.implicit.NofifyImpl"</span> /&gt;</span>\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">interface</span>=<span class="hljs-string">"com.alibaba.dubbo.callback.implicit.IDemoService"</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">"get"</span> <span class="hljs-attr">async</span>=<span class="hljs-string">"true"</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><code>callback</code> and<code>async</code> functions are orthogonally decomposed. <code>async = true</code> means that the result is returned immediately.<code>onreturn</code> means that a callback is required.</p>\n<p>There are several situations with the tow attributes[^2].</p>\n<ul>\n<li>Asynchronous callback mode:<code>async=true onreturn=&quot;xxx&quot;</code></li>\n<li>Synchronous callback mode:<code>async=false onreturn=&quot;xxx&quot;</code></li>\n<li>Asynchronous no callback:<code>async=true</code></li>\n<li>Synchronous no callback:<code>async=true</code></li>\n</ul>\n<h2>Testing code</h2>\n<pre><code class="language-java">IDemoService demoService = (IDemoService) context.getBean(<span class="hljs-string">"demoService"</span>);\nNofifyImpl notify = (NofifyImpl) context.getBean(<span class="hljs-string">"demoCallback"</span>);\n<span class="hljs-keyword">int</span> requestId = <span class="hljs-number">2</span>;\nPerson ret = demoService.get(requestId);\nAssert.assertEquals(<span class="hljs-keyword">null</span>, ret);\n<span class="hljs-comment">//for Test:Just used to illustrate the normal callback callback, the specific business decisions.</span>\n<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">10</span>; i++) {\n    <span class="hljs-keyword">if</span> (!notify.ret.containsKey(requestId)) {\n        Thread.sleep(<span class="hljs-number">200</span>);\n    } <span class="hljs-keyword">else</span> {\n        <span class="hljs-keyword">break</span>;\n    }\n}\nAssert.assertEquals(requestId, notify.ret.get(requestId).getId());\n</code></pre>\n<p><strong>NOTE</strong><code>2.0.7+</code> version,<code>async=false</code> is default.</p>\n'},{filename:"user/demos/explicit-target.md",__html:'<h1>Explicit target</h1>\n<p>In the development and testing environment, it is often necessary to bypass the registry and test only designated service providers. In this case, point-to-point direct connection may be required, and the service provider will ignore the list of provider registration providers. The interface A configure Point-to-point, does not affect the B interface to obtain a list from the registry.</p>\n<p><img src="../sources/images/dubbo-directly.jpg" alt="/user-guide/images/dubbo-directly.jpg"></p>\n<h2>Configure with XML</h2>\n<p>If it is online demand needs the point-to-point feature,You can configure the specified provider url at <code>&lt;dubbo:reference&gt;</code>.it will bypass the registry, multiple addresses separated by semicolons, the following configuration:</p>\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">"xxxService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.alibaba.xxx.XxxService"</span> <span class="hljs-attr">url</span>=<span class="hljs-string">"dubbo://localhost:20890"</span> /&gt;</span>\n</code></pre>\n<h2>Configure with the <code>-D</code> argument</h2>\n<p>Add the -D parameter mapping service address to the JVM startup parameters:</p>\n<pre><code class="language-sh">java -Dcom.alibaba.xxx.XxxService=dubbo://localhost:20890\n</code></pre>\n<h2>Configure with the <code>.properties</code> file</h2>\n<p>If you have more services, you can also use file mapping to specify the mapping file path with <code>-Ddubbo.resolve.file</code>. This configuration takes precedence over the configuration in<code>&lt;dubbo: reference&gt;</code>, for example:</p>\n<pre><code class="language-sh">java -Ddubbo.resolve.file=xxx.properties\n</code></pre>\n<p>Then add the configuration in the mapping file <code>xxx.properties</code>, where key is the service name and value is the service provider URL:</p>\n<pre><code class="language-properties">com.alibaba.xxx.XxxService=dubbo://localhost:20890\n</code></pre>\n<p><strong>NOTE</strong> To avoid complicating the online environment, do not use this feature online and should only be used during the testing phase</p>\n'},{filename:"user/demos/fault-tolerent-strategy.md",__html:'<h1>Fault Tolerance Strategy</h1>\n<p>Dubbo offers a variety of fault-tolerant scenarios when a cluster call fails, with a default failover retry.</p>\n<p><img src="../sources/images/cluster.jpg" alt="cluster"></p>\n<p>The relationship between nodes:</p>\n<ul>\n<li>This <code>Invoker</code> is the callable Service\'s abstract of the<code>Provider</code>, and the <code>Invoker</code> packaging the<code>Provider</code>\'s address and <code>Service</code>\'s interface.</li>\n<li>The <code>Directory</code> represent multiple <code>Invoker</code>,You can think of it as <code>List&lt;Invoker&gt;</code>,But unlike <code>List</code>,its value  can be dynamically changing.such as registry push changes</li>\n<li>The <code>Cluster</code> disguises multiple<code>Invoker</code> in <code>Directory</code> as a<code>Invoker</code>,The upper transparent, masquerade process contains fault-tolerant logic, call failed, try another</li>\n<li>The <code>Router</code> is responsible for selecting subsets according to routing rules from multiple <code>Invoker</code>s, such as read-write separation, application isolation, etc.</li>\n<li><code>LoadBalance</code> is responsible for selecting a specific one from multiple<code>Invoker</code> for this call. The selection process includes the load balancing algorithm. If the call fails, it needs to be re-selected</li>\n</ul>\n<h2>Cluster fault-tolerant mode</h2>\n<p>You can expand the cluster fault tolerance strategy, see:<a href="http://dubbo.apache.org/books/dubbo-dev-book-en/impls/cluster.html">Cluster expansion</a></p>\n<h2>Failover Cluster</h2>\n<p>Failure automatically switch, when there is failure, retry the other server (default). Usually used for read operations, but retries can result in longer delays. The times of retries can be set via <code>retries =2</code> (excluding the first time).</p>\n<p>The times of retries is configured as follows:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">retries</span>=<span class="hljs-string">"2"</span> /&gt;</span>\n</code></pre>\n<p>OR</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">retries</span>=<span class="hljs-string">"2"</span> /&gt;</span>\n</code></pre>\n<p>OR</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</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">"findFoo"</span> <span class="hljs-attr">retries</span>=<span class="hljs-string">"2"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:reference</span>&gt;</span>\n</code></pre>\n<h3>Failfast Cluster</h3>\n<p>Fast failure, only made a call, failure immediately error. Usually used for non-idempotent write operations, such as adding records</p>\n<h3>Failsafe Cluster</h3>\n<p>Failure of security, anomalies, directly ignored. Usually used to write audit logs and other operations.</p>\n<h3>Failback Cluster</h3>\n<p>Failure automatically restored, failed to record the background request, regular retransmission. Usually used for message notification operations.</p>\n<h3>Forking Cluster</h3>\n<p>Multiple servers are invoked in parallel, returning as soon as one succeeds. Usually used for real-time demanding read operations, but need to waste more service resources. The maximum number of parallelism can be set with <code>forks=2</code>.</p>\n<h3>Broadcast Cluster</h3>\n<p>Calling all providers broadcast, one by one call, any error is reported (<code>2.1.0+</code>). It is usually used to notify all providers to update local resource information such as caches or logs.</p>\n<h2>Cluster mode configuration</h2>\n<p>Follow the example below to configure cluster mode on service providers and consumers</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">cluster</span>=<span class="hljs-string">"failsafe"</span> /&gt;</span>\n</code></pre>\n<p>OR</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">cluster</span>=<span class="hljs-string">"failsafe"</span> /&gt;</span>\n</code></pre>\n'},{filename:"user/demos/generic-reference.md",__html:'<h1>Generic Reference</h1>\n<p>Generic invocation is mainly used when the client does not have API interface or model class,  all POJOs in parameters and return values are represented by <code>Map</code>.Commonly used for framework integration such as: implementing a common service testing framework, all service implementations can be invoked via <code>GenericService</code>.</p>\n<h2>Use generic invocation via Spring</h2>\n<p>Declared in the Spring configuration file <code>generic =&quot; true &quot;</code>:</p>\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">"barService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">generic</span>=<span class="hljs-string">"true"</span> /&gt;</span>\n</code></pre>\n<p>In Java code, get <code>barService</code> and start generic invocation:</p>\n<pre><code class="language-java">GenericService barService = (GenericService) applicationContext.getBean(<span class="hljs-string">"barService"</span>);\nObject result = barService.$invoke(<span class="hljs-string">"sayHello"</span>, <span class="hljs-keyword">new</span> String[] { <span class="hljs-string">"java.lang.String"</span> }, <span class="hljs-keyword">new</span> Object[] { <span class="hljs-string">"World"</span> });\n</code></pre>\n<h2>Use generic invocation via API</h2>\n<pre><code class="language-java"><span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.service.GenericService;\n...\n\n<span class="hljs-comment">// reference remote service</span>\n<span class="hljs-comment">// The instance is very heavy, which encapsulates all the registration center and service provider connection, please cache</span>\nReferenceConfig&lt;GenericService&gt; reference = <span class="hljs-keyword">new</span> ReferenceConfig&lt;GenericService&gt;();\n<span class="hljs-comment">// weak type service interface name</span>\nreference.setInterface(<span class="hljs-string">"com.xxx.XxxService"</span>);  \nreference.setVersion(<span class="hljs-string">"1.0.0"</span>);\n<span class="hljs-comment">// declared as generic service</span>\nreference.setGeneric(<span class="hljs-keyword">true</span>);  \n\n<span class="hljs-comment">// service stub type is also the com.alibaba.dubbo.rpc.service.GenericService</span>\nGenericService genericService = reference.get();\n\n<span class="hljs-comment">// basic types and Date, List, Map, etc. do not need conversion, direct use them</span>\nObject result = genericService.$invoke(<span class="hljs-string">"sayHello"</span>, <span class="hljs-keyword">new</span> String[] {<span class="hljs-string">"java.lang.String"</span>}, <span class="hljs-keyword">new</span> Object[] {<span class="hljs-string">"world"</span>});\n\n<span class="hljs-comment">// map POJO parameters, if the return value is POJO will automatically turn into map</span>\nMap&lt;String, Object&gt; person = <span class="hljs-keyword">new</span> HashMap&lt;String, Object&gt;();\nperson.put(<span class="hljs-string">"name"</span>, <span class="hljs-string">"xxx"</span>);\nperson.put(<span class="hljs-string">"password"</span>, <span class="hljs-string">"yyy"</span>);\n<span class="hljs-comment">// if the return value is POJO will automatically turn into map</span>\nObject result = genericService.$invoke(<span class="hljs-string">"findPerson"</span>, <span class="hljs-keyword">new</span> String[]\n{<span class="hljs-string">"com.xxx.Person"</span>}, <span class="hljs-keyword">new</span> Object[]{person});\n\n...\n</code></pre>\n<h2>Further explanation of generalized types</h2>\n<p>Consider POJO like this:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.xxx;\n\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PersonImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Person</span> </span>{\n    <span class="hljs-keyword">private</span> String name;\n    <span class="hljs-keyword">private</span> String password;\n\n    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getName</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-keyword">return</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">setName</span><span class="hljs-params">(String name)</span> </span>{\n        <span class="hljs-keyword">this</span>.name = name;\n    }\n\n    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getPassword</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-keyword">return</span> password;\n    }\n\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setPassword</span><span class="hljs-params">(String password)</span> </span>{\n        <span class="hljs-keyword">this</span>.password = password;\n    }\n}\n</code></pre>\n<p>The POJO data:</p>\n<pre><code class="language-java">Person person = <span class="hljs-keyword">new</span> PersonImpl();\nperson.setName(<span class="hljs-string">"xxx"</span>);\nperson.setPassword(<span class="hljs-string">"yyy"</span>);\n</code></pre>\n<p>Data represented by <code>Map</code>:</p>\n<pre><code class="language-java">Map&lt;String, Object&gt; map = <span class="hljs-keyword">new</span> HashMap&lt;String, Object&gt;();\n<span class="hljs-comment">// Note: If the parameter type is an interface, or List lost the generic class, you can specify the type of the class attribute</span>\nmap.put(<span class="hljs-string">"class"</span>, <span class="hljs-string">"com.xxx.PersonImpl"</span>);\nmap.put(<span class="hljs-string">"name"</span>, <span class="hljs-string">"xxx"</span>);\nmap.put(<span class="hljs-string">"password"</span>, <span class="hljs-string">"yyy"</span>);\n</code></pre>\n'},{filename:"user/demos/generic-service.md",__html:'<h1>Generic Service</h1>\n<p>The implementation of the generic interface is mainly used when there is no API interface and model class on the server side. All POJOs in the parameters and return values are represented by the Map and are usually used for framework integration. For example, to implement a universal remote service Mock framework, handle all service requests by implementing the GenericService interface.</p>\n<p>In Java code, implement <code>GenericService</code> interface:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.foo;\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyGenericService</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">GenericService</span> </span>{\n\n    <span class="hljs-keyword">public</span> Object $invoke(String methodName, String[] parameterTypes, Object[] args) <span class="hljs-keyword">throws</span> GenericException {\n        <span class="hljs-keyword">if</span> (<span class="hljs-string">"sayHello"</span>.equals(methodName)) {\n            <span class="hljs-keyword">return</span> <span class="hljs-string">"Welcome "</span> + args[<span class="hljs-number">0</span>];\n        }\n    }\n}\n</code></pre>\n<h2>Export generic implements via Spring</h2>\n<p>Declared in the Spring configuration file:</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">"genericService"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"com.foo.MyGenericService"</span> /&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"genericService"</span> /&gt;</span>\n</code></pre>\n<h2>Export generic implements via API</h2>\n<pre><code class="language-java">...\n<span class="hljs-comment">// use com.alibaba.dubbo.rpc.service.GenericService can replace all implements</span>\nGenericService xxxService = <span class="hljs-keyword">new</span> XxxGenericService();\n\n<span class="hljs-comment">// The instance is very heavy, which encapsulates all the registration center and service provider connection, please cache</span>\nServiceConfig&lt;GenericService&gt; service = <span class="hljs-keyword">new</span> ServiceConfig&lt;GenericService&gt;();\n<span class="hljs-comment">// weak type service interface name</span>\nservice.setInterface(<span class="hljs-string">"com.xxx.XxxService"</span>);  \nservice.setVersion(<span class="hljs-string">"1.0.0"</span>);\n<span class="hljs-comment">// point to a generic serivce instance</span>\nservice.setRef(xxxService);\n\n<span class="hljs-comment">// export service to registration center</span>\nservice.export();\n</code></pre>\n'},{filename:"user/demos/graceful-shutdown.md",__html:'<h1>Graceful Shutdown</h1>\n<p>Dubbo is graceful shutdown through the <code>ShutdownHook</code> of the JDK, so graceful shutdowns are not performed if you force shutdown the command, such as <code>kill -9 PID</code>, and will only be executed if <code>kill PID</code> is passed.</p>\n<h2>Howto</h2>\n<h3>Service provider</h3>\n<ul>\n<li>When stop, first marked as not receiving new requests, the new request directly return the error, so that the client retries other machines.</li>\n<li>Then check thread pool thread is running, if any, waiting for all threads to complete execution, unless overtime, then forced to close.</li>\n</ul>\n<h3>Service consumer</h3>\n<ul>\n<li>When stop, No longer initiate a new request, all request on the client that got an error.</li>\n<li>Then check the request has not returned the response, waiting for the response to return, unless overtime, then forced to close.</li>\n</ul>\n<h2>Configuration shutdown wait time</h2>\n<p>Set graceful shutdown timeout, the default timeout is 10 seconds, if the overtime is forced to close.</p>\n<pre><code class="language-properties"># dubbo.properties\ndubbo.service.shutdown.wait=15000\n</code></pre>\n<p>If ShutdownHook does not take effect, you can call it yourself, <strong>in tomcat, it is recommended by extending the ContextListener and call the following code for graceful shutdown</strong>:</p>\n<pre><code class="language-java">ProtocolConfig.destroyAll();\n</code></pre>\n'},{filename:"user/demos/group-merger.md",__html:'<h1>Group Merger</h1>\n<p>According to the group to invoke server and return the merge result <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>, such as the menu service, the same interface, but there are a variety of implementations, using group distinction, consumers call each group and get the results, the merger can merge the resules, so that you can achieve aggregation Menu Item.</p>\n<p>Related code can refer to <a href="https://github.com/apache/incubator-dubbo/tree/master/dubbo-test/dubbo-test-examples/src/main/java/com/alibaba/dubbo/examples/merge">dubbo project example</a></p>\n<h2>Configuration</h2>\n<p>Merge all groups</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.xxx.MenuService"</span> <span class="hljs-attr">group</span>=<span class="hljs-string">"*"</span> <span class="hljs-attr">merger</span>=<span class="hljs-string">"true"</span> /&gt;</span>\n</code></pre>\n<p>Merge the specified group</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.xxx.MenuService"</span> <span class="hljs-attr">group</span>=<span class="hljs-string">"aaa,bbb"</span> <span class="hljs-attr">merger</span>=<span class="hljs-string">"true"</span> /&gt;</span>\n</code></pre>\n<p>The specified method to merge the results, other unspecified methods, will only call one group</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.xxx.MenuService"</span> <span class="hljs-attr">group</span>=<span class="hljs-string">"*"</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">"getMenuItems"</span> <span class="hljs-attr">merger</span>=<span class="hljs-string">"true"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:service</span>&gt;</span>\n</code></pre>\n<p>The Specified a method does not merge the results, others merge the results</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.xxx.MenuService"</span> <span class="hljs-attr">group</span>=<span class="hljs-string">"*"</span> <span class="hljs-attr">merger</span>=<span class="hljs-string">"true"</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">"getMenuItems"</span> <span class="hljs-attr">merger</span>=<span class="hljs-string">"false"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:service</span>&gt;</span>\n</code></pre>\n<p>Specify the merge strategy, the default according to the type of return value automatically match, if the same type has two mergers, you need to specify the name of the merger<sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup></p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.xxx.MenuService"</span> <span class="hljs-attr">group</span>=<span class="hljs-string">"*"</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">"getMenuItems"</span> <span class="hljs-attr">merger</span>=<span class="hljs-string">"mymerge"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:service</span>&gt;</span>\n</code></pre>\n<p>Specify the merge method, it will call the return type\'s method for merging, the merging method parameter type must be the return type</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.xxx.MenuService"</span> <span class="hljs-attr">group</span>=<span class="hljs-string">"*"</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">"getMenuItems"</span> <span class="hljs-attr">merger</span>=<span class="hljs-string">".addAll"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:service</span>&gt;</span>\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>since <code>2.1.0</code> began to support <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>See also:<a href="http://dubbo.apache.org/books/dubbo-user-book-en/demos/group-merger.html">merger extensions</a> <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/demos/hostname-binding.md",__html:'<h1>Hostname Binding</h1>\n<h2>Lookup order</h2>\n<p>Default host IP lookup order:</p>\n<ul>\n<li>Get local address via <code>LocalHost.getLocalHost()</code>.</li>\n<li>If it is <code>127. *</code> loopback address, then scan the network for host IP</li>\n</ul>\n<h2>Host configuration</h2>\n<p>Registered address if it is not correct, such as the need to register public address, you can do this:</p>\n<ol>\n<li>\n<p>edit <code>/etc/hosts</code> : add machinename and public ip, such as:</p>\n<pre><code>test1 205.182.23.201\n</code></pre>\n</li>\n<li>\n<p>in <code>dubbo.xml</code> add host address configuration:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">host</span>=<span class="hljs-string">"205.182.23.201"</span>&gt;</span>\n</code></pre>\n</li>\n<li>\n<p>or config that in <code>dubbo.properties</code>:</p>\n<pre><code class="language-properties">dubbo.protocol.host=205.182.23.201\n</code></pre>\n</li>\n</ol>\n<h2>Port configuration</h2>\n<p>The default port and protocol:</p>\n<table>\n<thead>\n<tr>\n<th>Protocol</th>\n<th>Port</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>dubbo</td>\n<td>20880</td>\n</tr>\n<tr>\n<td>rmi</td>\n<td>1099</td>\n</tr>\n<tr>\n<td>http</td>\n<td>80</td>\n</tr>\n<tr>\n<td>hessian</td>\n<td>80</td>\n</tr>\n<tr>\n<td>webservice</td>\n<td>80</td>\n</tr>\n<tr>\n<td>memcached</td>\n<td>11211</td>\n</tr>\n<tr>\n<td>redis</td>\n<td>6379</td>\n</tr>\n</tbody>\n</table>\n<p>You can configure the port as follows:</p>\n<ol>\n<li>\n<p>in <code>dubbo.xml</code> add port configuration:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"20880"</span>&gt;</span>\n</code></pre>\n</li>\n<li>\n<p>or config that in <code>dubbo.properties</code>:</p>\n<pre><code class="language-properties">dubbo.protocol.dubbo.port=20880\n</code></pre>\n</li>\n</ol>\n'},{filename:"user/demos/index.md",__html:"<h1>Example</h1>\n"},{filename:"user/demos/introduction.md",__html:'<blockquote>\n<p><img src="../sources/images/check.gif" alt="warning">To complete run, please see:<a href="quickstart.md">Quickstart</a>, here just lists the configuration of various scenarios\n<img src="../sources/images/check.gif" alt="warning">The following examples are all based on Spring configuration:<a href="../configuration/xml.md">Xml configuration</a> for reference, if you do not want to use Spring, but want to be use it via the directly API, please see:<a href="../configuration/api.md">API configuration</a></p>\n</blockquote>\n'},{filename:"user/demos/lazy-connect.md",__html:'<h1>Lazy Connect</h1>\n<p>Lazy connect can reduce the number of keep-alive connections. When a call is initiated, create a keep-alive connection.<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup></p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">lazy</span>=<span class="hljs-string">"true"</span> /&gt;</span>\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>Note: This configuration takes effect only for dubbo protocols that use keep-alive connections. <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/demos/loadbalance.md",__html:'<h1>LoadBalance</h1>\n<p>Dubbo offers a number of balancing strategies for cluster load balancing, which defaults to <code>random</code>.</p>\n<p>You can extend the load balancing strategy by yourself, see:<a href="http://dubbo.apache.org/books/dubbo-dev-book-en/impls/load-balance.html">LoadBalance extension</a></p>\n<h2>LoadBalance strategy</h2>\n<h3>Random LoadBalance</h3>\n<ul>\n<li><strong>Ramdom</strong>, set random probabilities by weight.</li>\n<li>The probability of collisions on one section is high, but the larger the amount of calls, the more uniform the distribution. And when use weight based on probability the distribution turns out to be uniform, which also helps to dynamically adjust the provider weights.</li>\n</ul>\n<h3>RoundRobin LoadBalance</h3>\n<ul>\n<li><strong>RoundRobin</strong>, use the weight\'s common advisor to determine round robin ratio.</li>\n<li>Traffic flow to slower providers may cause requests piled up, e.g., if there\'s a provider processing requests in a very slow speed, but it\'s still alive, which means it can receive request as normal. According to RoundRobin policy, consumers will continuously send requests to this provider on predetermined pace, have no aware of the provider\'s bad status. Finally, we will get many requests stuck on this unhealthy provider.</li>\n</ul>\n<h3>LeastActive LoadBalance</h3>\n<ul>\n<li><strong>LeastActive</strong>, a random mechanism based on actives, <code>actives</code> means the num of requests a consumer have sent but not return yet。</li>\n<li>Slower providers will receive fewer requests, cause slower provider have higher <code>actives</code>.</li>\n</ul>\n<h3>ConsistentHash LoadBalance</h3>\n<ul>\n<li><strong>ConsistentHash</strong>, the same parameters of the request is always sent to the same provider.</li>\n<li>When a provider fails, the original request to the provider, based on the virtual node algorithm, averages to other providers, does not cause drastic changes.</li>\n<li>Algorithm reference:<a href="http://en.wikipedia.org/wiki/Consistent_hashing">http://en.wikipedia.org/wiki/Consistent_hashing</a></li>\n<li>By default only the first parameter Hash, if you want to modify, please configure <code>&lt;dubbo:parameter key=&quot;hash.arguments&quot; value=&quot;0,1&quot; /&gt;</code></li>\n<li>By default 160 virtual nodes, if you want to modify, please configure <code>&lt;dubbo:parameter key=&quot;hash.nodes&quot; value=&quot;320&quot; /&gt;</code></li>\n</ul>\n<p>See the algorithm at <a href="http://en.wikipedia.org/wiki/Consistent_hashing">http://en.wikipedia.org/wiki/Consistent_hashing</a></p>\n<h2>Configuration</h2>\n<h3>Server service level</h3>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"..."</span> <span class="hljs-attr">loadbalance</span>=<span class="hljs-string">"roundrobin"</span> /&gt;</span>\n</code></pre>\n<h3>Client service level</h3>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"..."</span> <span class="hljs-attr">loadbalance</span>=<span class="hljs-string">"roundrobin"</span> /&gt;</span>\n</code></pre>\n<h3>Server method level</h3>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"..."</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">"..."</span> <span class="hljs-attr">loadbalance</span>=<span class="hljs-string">"roundrobin"</span>/&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:service</span>&gt;</span>\n</code></pre>\n<h3>Client method level</h3>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"..."</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">"..."</span> <span class="hljs-attr">loadbalance</span>=<span class="hljs-string">"roundrobin"</span>/&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:reference</span>&gt;</span>\n</code></pre>\n'},{filename:"user/demos/local-call.md",__html:'<h1>Local call</h1>\n<p>The local call uses the <code>injvm</code> protocol, a pseudo-protocol that does not turn on the port, does not initiate remote calls, is directly associated within the JVM, but executes the Dubbo Filter chain.</p>\n<h2>Configuration</h2>\n<p>Configure <code>injvm</code> protocol</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"injvm"</span> /&gt;</span>\n</code></pre>\n<p>Configure default provider</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"injvm"</span> /&gt;</span>\n</code></pre>\n<p>Configure default service</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"injvm"</span> /&gt;</span>\n</code></pre>\n<p>Use injvm first</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:consumer</span> <span class="hljs-attr">injvm</span>=<span class="hljs-string">"true"</span> <span class="hljs-attr">...</span>/&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">injvm</span>=<span class="hljs-string">"true"</span> <span class="hljs-attr">...</span>/&gt;</span>\n</code></pre>\n<p>or</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">injvm</span>=<span class="hljs-string">"true"</span> <span class="hljs-attr">...</span>/&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">injvm</span>=<span class="hljs-string">"true"</span> <span class="hljs-attr">...</span>/&gt;</span>\n</code></pre>\n<p>Note: Both service provider and service references need to declare <code>injvm=&quot;true&quot;</code></p>\n<h2>Automatically exposed, local service references</h2>\n<p><code>2.2.0</code> or later, each service is exposed locally by default. When referring to the service, the local service is referenced by default. If you want to reference a remote service, you can use the following configuration to force a reference to a remote service.</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">...</span> <span class="hljs-attr">scope</span>=<span class="hljs-string">"remote"</span> /&gt;</span>\n</code></pre>\n'},{filename:"user/demos/local-mock.md",__html:'<h1>Local mock</h1>\n<p>Local mock <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> is usually used for service downgrade, such as a verification service, the client does not throw an exception when the service provider hangs up all the time, but returns the authorization failed through the Mock data.</p>\n<p>Configured in the spring configuration file as follows:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">mock</span>=<span class="hljs-string">"true"</span> /&gt;</span>\n</code></pre>\n<p>or</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">mock</span>=<span class="hljs-string">"com.foo.BarServiceMock"</span> /&gt;</span>\n</code></pre>\n<p>Mock implementation in the project <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.foo;\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BarServiceMock</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">BarService</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">sayHello</span><span class="hljs-params">(String name)</span> </span>{\n        <span class="hljs-comment">// You can return mock data, this method is only executed when an RpcException is thrown.</span>\n        <span class="hljs-keyword">return</span> <span class="hljs-string">"mock data"</span>;\n    }\n}\n</code></pre>\n<p>If the service consumer often needs <code>try-catch</code> to catch exceptions, such as:</p>\n<pre><code class="language-java">Offer offer = <span class="hljs-keyword">null</span>;\n<span class="hljs-keyword">try</span> {\n    offer = offerService.findOffer(offerId);\n} <span class="hljs-keyword">catch</span> (RpcException e) {\n   logger.error(e);\n}\n</code></pre>\n<p>Consider changing to Mock implementation and return null in Mock implementation. If you just want to simply ignore the exception, <code>2.0.11</code> version or later version is available:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">mock</span>=<span class="hljs-string">"return null"</span> /&gt;</span>\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>Mock is a subset of the Stub. If you use Stub, you may need to rely on the RpcException class. If you use Mock, you do not need to rely on RpcException, when throwing RpcException, it will callback Mock implementation class. <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>BarServiceMock implements BarService and has a no-argument constructor. <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/demos/local-stub.md",__html:'<h1>Local stub</h1>\n<p>When using rpc, the client usually only the interface, but sometimes the client also want to perform part of the logic in the client. For example: do ThreadLocal cache, verify parameters, return mock data when call fails., etc.</p>\n<p>To solve this problem, you can configure the stub in the API, so that when the client generates the proxy instance, it passes the proxy to the <code>Stub</code> via the constructor <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>, and then you can implement your logic in the stub implementation code.</p>\n<p><img src="../sources/images/stub.jpg" alt="/user-guide/images/stub.jpg"></p>\n<p>Configured in the spring configuration file as follows:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">stub</span>=<span class="hljs-string">"true"</span> /&gt;</span>\n</code></pre>\n<p>or</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">stub</span>=<span class="hljs-string">"com.foo.BarServiceStub"</span> /&gt;</span>\n</code></pre>\n<p>Provide Stub implementation <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.foo;\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BarServiceStub</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">BarService</span> </span>{\n    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> BarService barService;\n\n    <span class="hljs-comment">// The real remote proxy object is passed in through the constructor</span>\n    <span class="hljs-keyword">public</span> (BarService barService) {\n        <span class="hljs-keyword">this</span>.barService = barService;\n    }\n\n    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">sayHello</span><span class="hljs-params">(String name)</span> </span>{\n        <span class="hljs-comment">// The following code is executed on the client. You can do local ThreadLocal caching on the client side, or verify parameters, etc.</span>\n        <span class="hljs-keyword">try</span> {\n            <span class="hljs-keyword">return</span> barService.sayHello(name);\n        } <span class="hljs-keyword">catch</span> (Exception e) {\n            <span class="hljs-comment">// You can return the mock data.</span>\n            <span class="hljs-keyword">return</span> <span class="hljs-string">"MockData"</span>;\n        }\n    }\n}\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>The Stub must have a constructor that can pass in the proxy. <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>BarServiceStub implements BarService ,it has a constructor passed in the remote BarService instance <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/demos/logger-strategy.md",__html:'<h1>Logger adapter</h1>\n<p><code>2.2.1</code> or later, dubbo support log4j、slf4j、jcl、jdk adapters <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>, you can also explicitly configure the log output policy in the following ways:</p>\n<ol start="0">\n<li>\n<p>Command</p>\n<pre><code class="language-sh">java -Ddubbo.application.logger=log4j\n</code></pre>\n</li>\n<li>\n<p>Configure in <code>dubbo.properties</code></p>\n<pre><code class="language-properties">dubbo.application.logger=log4j\n</code></pre>\n</li>\n<li>\n<p>Configure in <code>dubbo.xml</code></p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:application</span> <span class="hljs-attr">logger</span>=<span class="hljs-string">"log4j"</span> /&gt;</span>\n</code></pre>\n</li>\n</ol>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>Custom Extensions<a href="http://dubbo.apache.org/books/dubbo-dev-book-en/impls/logger-adapter.html">logger-adapter</a> <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/demos/multi-protocols.md",__html:'<h1>Multiple protocols</h1>\n<p>Dubbo allows you to configure multiple protocols, support different protocols on different services, or support multiple protocols on the same service.</p>\n<h2>Every service export to one specific protocol separately</h2>\n<p>Different protocol performance is not the same. Such as big data should use short connection protocol, small data and concurrent should use long connection protocol.</p>\n<pre><code class="language-xml"><span class="php"><span class="hljs-meta">&lt;?</span>xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"UTF-8"</span><span class="hljs-meta">?&gt;</span></span>\n<span class="hljs-tag">&lt;<span class="hljs-name">beans</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans"</span>\n    <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>\n    <span class="hljs-attr">xmlns:dubbo</span>=<span class="hljs-string">"http://dubbo.apache.org/schema/dubbo"</span>\n    <span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:application</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"world"</span>  /&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"registry"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.141.150:9090"</span> <span class="hljs-attr">username</span>=<span class="hljs-string">"admin"</span> <span class="hljs-attr">password</span>=<span class="hljs-string">"hello1234"</span> /&gt;</span>\n    <span class="hljs-comment">&lt;!-- multiple protocols --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"20880"</span> /&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rmi"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"1099"</span> /&gt;</span>\n    <span class="hljs-comment">&lt;!-- Use dubbo protocol to expose the service --&gt;</span>\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.hello.api.HelloService"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0.0"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"helloService"</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"dubbo"</span> /&gt;</span>\n    <span class="hljs-comment">&lt;!-- Use rmi protocol to expose services --&gt;</span>\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.hello.api.DemoService"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0.0"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"demoService"</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"rmi"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">beans</span>&gt;</span>\n</code></pre>\n<h2>One service export to several protocols</h2>\n<pre><code class="language-xml"><span class="php"><span class="hljs-meta">&lt;?</span>xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"UTF-8"</span><span class="hljs-meta">?&gt;</span></span>\n<span class="hljs-tag">&lt;<span class="hljs-name">beans</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans"</span>\n    <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>\n    <span class="hljs-attr">xmlns:dubbo</span>=<span class="hljs-string">"http://dubbo.apache.org/schema/dubbo"</span>\n    <span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:application</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"world"</span>  /&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"registry"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.141.150:9090"</span> <span class="hljs-attr">username</span>=<span class="hljs-string">"admin"</span> <span class="hljs-attr">password</span>=<span class="hljs-string">"hello1234"</span> /&gt;</span>\n    <span class="hljs-comment">&lt;!-- multiple protocols--&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"20880"</span> /&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"hessian"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"8080"</span> /&gt;</span>\n    <span class="hljs-comment">&lt;!-- Service exposes multiple protocols --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"helloService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.alibaba.hello.api.HelloService"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0.0"</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"dubbo,hessian"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">beans</span>&gt;</span>\n</code></pre>\n'},{filename:"user/demos/multi-registry.md",__html:'<h1>Multiple registries</h1>\n<p>Dubbo supports the same service to register multiple registries, or different services were registered to different registries, or even reference the same name service from different registries. In addition, the registry supports custom extensions <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>。</p>\n<h2>One service register to multiple registries</h2>\n<p>For example: Alibaba some services are not deployed in Qingdao, only deployed in Hangzhou. While other applications in Qingdao need to reference this service, you can register your services to both registries at the same time.</p>\n<pre><code class="language-xml"><span class="php"><span class="hljs-meta">&lt;?</span>xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"UTF-8"</span><span class="hljs-meta">?&gt;</span></span>\n<span class="hljs-tag">&lt;<span class="hljs-name">beans</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans"</span>\n    <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>\n    <span class="hljs-attr">xmlns:dubbo</span>=<span class="hljs-string">"http://dubbo.apache.org/schema/dubbo"</span>\n    <span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:application</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"world"</span>  /&gt;</span>\n    <span class="hljs-comment">&lt;!-- Multi registries --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"hangzhouRegistry"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.141.150:9090"</span> /&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"qingdaoRegistry"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.141.151:9010"</span> <span class="hljs-attr">default</span>=<span class="hljs-string">"false"</span> /&gt;</span>\n    <span class="hljs-comment">&lt;!-- Service register to multiple registries --&gt;</span>\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.hello.api.HelloService"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0.0"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"helloService"</span> <span class="hljs-attr">registry</span>=<span class="hljs-string">"hangzhouRegistry,qingdaoRegistry"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">beans</span>&gt;</span>\n</code></pre>\n<h2>Different services register to different registries</h2>\n<p>For example: Some CRM services are specifically designed for international stations, and some services are specifically designed for Chinese stations.</p>\n<pre><code class="language-xml"><span class="php"><span class="hljs-meta">&lt;?</span>xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"UTF-8"</span><span class="hljs-meta">?&gt;</span></span>\n<span class="hljs-tag">&lt;<span class="hljs-name">beans</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans"</span>\n    <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>\n    <span class="hljs-attr">xmlns:dubbo</span>=<span class="hljs-string">"http://dubbo.apache.org/schema/dubbo"</span>\n    <span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:application</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"world"</span>  /&gt;</span>\n    <span class="hljs-comment">&lt;!-- Multi registries --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"chinaRegistry"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.141.150:9090"</span> /&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"intlRegistry"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.154.177:9010"</span> <span class="hljs-attr">default</span>=<span class="hljs-string">"false"</span> /&gt;</span>\n    <span class="hljs-comment">&lt;!-- Service register to Chinese station registry --&gt;</span>\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.hello.api.HelloService"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0.0"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"helloService"</span> <span class="hljs-attr">registry</span>=<span class="hljs-string">"chinaRegistry"</span> /&gt;</span>\n    <span class="hljs-comment">&lt;!-- Service register to international station registry --&gt;</span>\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.hello.api.DemoService"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0.0"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"demoService"</span> <span class="hljs-attr">registry</span>=<span class="hljs-string">"intlRegistry"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">beans</span>&gt;</span>\n</code></pre>\n<h2>Reference services from multiple registries</h2>\n<p>For example: CRM needs to call the PC2 service of Chinese station and international station at the same time. PC2 is deployed in both Chinese station and international station. The interfaces and version numbers are the same, but the database used is different.</p>\n<pre><code class="language-xml"><span class="php"><span class="hljs-meta">&lt;?</span>xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"UTF-8"</span><span class="hljs-meta">?&gt;</span></span>\n<span class="hljs-tag">&lt;<span class="hljs-name">beans</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans"</span>\n    <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>\n    <span class="hljs-attr">xmlns:dubbo</span>=<span class="hljs-string">"http://dubbo.apache.org/schema/dubbo"</span>\n    <span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:application</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"world"</span>  /&gt;</span>\n    <span class="hljs-comment">&lt;!-- Multi registries --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"chinaRegistry"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.141.150:9090"</span> /&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"intlRegistry"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.154.177:9010"</span> <span class="hljs-attr">default</span>=<span class="hljs-string">"false"</span> /&gt;</span>\n    <span class="hljs-comment">&lt;!-- Reference Chinese station service --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"chinaHelloService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.alibaba.hello.api.HelloService"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0.0"</span> <span class="hljs-attr">registry</span>=<span class="hljs-string">"chinaRegistry"</span> /&gt;</span>\n    <span class="hljs-comment">&lt;!-- Reference international station service --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"intlHelloService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.alibaba.hello.api.HelloService"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0.0"</span> <span class="hljs-attr">registry</span>=<span class="hljs-string">"intlRegistry"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">beans</span>&gt;</span>\n</code></pre>\n<p>When testing, the service needs to be temporarily register to two registries, which can use vertical signs to separate multiple different registry addresses:</p>\n<pre><code class="language-xml"><span class="php"><span class="hljs-meta">&lt;?</span>xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"UTF-8"</span><span class="hljs-meta">?&gt;</span></span>\n<span class="hljs-tag">&lt;<span class="hljs-name">beans</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans"</span>\n    <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>\n    <span class="hljs-attr">xmlns:dubbo</span>=<span class="hljs-string">"http://dubbo.apache.org/schema/dubbo"</span>\n    <span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:application</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"world"</span>  /&gt;</span>\n    <span class="hljs-comment">&lt;!-- The vertical separation means that multiple registries are connected at the same time. Multiple cluster addresses of the same registry are separated by commas --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.141.150:9090|10.20.154.177:9010"</span> /&gt;</span>\n    <span class="hljs-comment">&lt;!-- service reference --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"helloService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.alibaba.hello.api.HelloService"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0.0"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">beans</span>&gt;</span>\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>custom registry, see:<a href="http://dubbo.apache.org/books/dubbo-dev-book-en/impls/registry.html">registry extension</a> <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/demos/multi-versions.md",__html:'<h1>Multi versions</h1>\n<p>When an interface to achieve an incompatible upgrade, you can use the version number transition. Different versions of the services do not reference each other.</p>\n<p>You can follow the steps below for version migration:</p>\n<ol start="0">\n<li>In the low pressure period, upgrade to half of the provider to the new version</li>\n<li>Then upgrade all consumers to the new version</li>\n<li>Then upgrade the remaining half providers to the new version</li>\n</ol>\n<p>Old version of the service provider configuration:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0.0"</span> /&gt;</span>\n</code></pre>\n<p>New version of the service provider configuration:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"2.0.0"</span> /&gt;</span>\n</code></pre>\n<p>Old version of the service consumer configuration:</p>\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">"barService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0.0"</span> /&gt;</span>\n</code></pre>\n<p>New version of the service consumer configuration:</p>\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">"barService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"2.0.0"</span> /&gt;</span>\n</code></pre>\n<p>If you do not need to distinguish between versions, can be configured as follows <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>:</p>\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">"barService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"*"</span> /&gt;</span>\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p><code>2.2.0</code> or later support <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/demos/netty4.md",__html:'<p>Add support for netty4 communication module in 2.5.6 version of dubbo, enabled as follows:</p>\n<p>provider:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"netty4"</span> /&gt;</span>\n</code></pre>\n<p>or</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"netty4"</span> /&gt;</span>\n</code></pre>\n<p>consumer:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:consumer</span> <span class="hljs-attr">client</span>=<span class="hljs-string">"netty4"</span> /&gt;</span>\n\n</code></pre>\n<blockquote>\n<p><strong>NOTES</strong></p>\n<ol>\n<li>If provider need to use different communication layer framework for different protocols , please configure multiple protocols separately.</li>\n<li>consumer configuration as follow:</li>\n</ol>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:consumer</span> <span class="hljs-attr">client</span>=<span class="hljs-string">"netty"</span>&gt;</span>\n  <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:consumer</span>&gt;</span>\n</code></pre>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:consumer</span> <span class="hljs-attr">client</span>=<span class="hljs-string">"netty4"</span>&gt;</span>\n  <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:consumer</span>&gt;</span>\n</code></pre>\n</blockquote>\n<blockquote>\n<p>Next we will continue to do something:</p>\n<ol>\n<li>We will provide a reference data on the performance test indicators and performance test comparison with the version of netty 3.</li>\n</ol>\n</blockquote>\n'},{filename:"user/demos/parameter-validation.md",__html:'<h1>Parameter Validation</h1>\n<p>The parameter validation <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> is based on [JSR303] (<a href="https://jcp.org/en/jsr/detail?id=303">https://jcp.org/en/jsr/detail?id=303</a>). The user simply add the validation annotation of the JSR303 and declares the filter for validation <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>.</p>\n<h2>Maven Dependency</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>javax.validation<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>validation-api<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>1.0.0.GA<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.hibernate<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>hibernate-validator<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>4.2.0.Final<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>\n</code></pre>\n<h2>Sample</h2>\n<h3>Example of Parameter Annotation</h3>\n<pre><code class="language-java"><span class="hljs-keyword">import</span> java.io.Serializable;\n<span class="hljs-keyword">import</span> java.util.Date;\n \n<span class="hljs-keyword">import</span> javax.validation.constraints.Future;\n<span class="hljs-keyword">import</span> javax.validation.constraints.Max;\n<span class="hljs-keyword">import</span> javax.validation.constraints.Min;\n<span class="hljs-keyword">import</span> javax.validation.constraints.NotNull;\n<span class="hljs-keyword">import</span> javax.validation.constraints.Past;\n<span class="hljs-keyword">import</span> javax.validation.constraints.Pattern;\n<span class="hljs-keyword">import</span> javax.validation.constraints.Size;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ValidationParameter</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Serializable</span> </span>{\n    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">long</span> serialVersionUID = <span class="hljs-number">7158911668568000392L</span>;\n \n    <span class="hljs-meta">@NotNull</span> <span class="hljs-comment">// Required </span>\n    <span class="hljs-meta">@Size</span>(min = <span class="hljs-number">1</span>, max = <span class="hljs-number">20</span>) <span class="hljs-comment">// range</span>\n    <span class="hljs-keyword">private</span> String name;\n \n    <span class="hljs-meta">@NotNull</span>(groups = ValidationService.Save.class) <span class="hljs-comment">// It is not allowed to be blank when saving. When it is updated, it is allowed to be blank, indicating that the field is not updated </span>\n    <span class="hljs-meta">@Pattern</span>(regexp = <span class="hljs-string">"^\\\\s*\\\\w+(?:\\\\.{0,1}[\\\\w-]+)*@[a-zA-Z0-9]+(?:[-.][a-zA-Z0-9]+)*\\\\.[a-zA-Z]+\\\\s*$"</span>)\n    <span class="hljs-keyword">private</span> String email;\n \n    <span class="hljs-meta">@Min</span>(<span class="hljs-number">18</span>) <span class="hljs-comment">// min value</span>\n    <span class="hljs-meta">@Max</span>(<span class="hljs-number">100</span>) <span class="hljs-comment">// max value</span>\n    <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> age;\n \n    <span class="hljs-meta">@Past</span> <span class="hljs-comment">// Must be a past time</span>\n    <span class="hljs-keyword">private</span> Date loginDate;\n \n    <span class="hljs-meta">@Future</span> <span class="hljs-comment">// Must be a future time</span>\n    <span class="hljs-keyword">private</span> Date expiryDate;\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getName</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-keyword">return</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">setName</span><span class="hljs-params">(String name)</span> </span>{\n        <span class="hljs-keyword">this</span>.name = name;\n    }\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getEmail</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-keyword">return</span> email;\n    }\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setEmail</span><span class="hljs-params">(String email)</span> </span>{\n        <span class="hljs-keyword">this</span>.email = email;\n    }\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getAge</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-keyword">return</span> age;\n    }\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setAge</span><span class="hljs-params">(<span class="hljs-keyword">int</span> age)</span> </span>{\n        <span class="hljs-keyword">this</span>.age = age;\n    }\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> Date <span class="hljs-title">getLoginDate</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-keyword">return</span> loginDate;\n    }\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setLoginDate</span><span class="hljs-params">(Date loginDate)</span> </span>{\n        <span class="hljs-keyword">this</span>.loginDate = loginDate;\n    }\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> Date <span class="hljs-title">getExpiryDate</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-keyword">return</span> expiryDate;\n    }\n \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setExpiryDate</span><span class="hljs-params">(Date expiryDate)</span> </span>{\n        <span class="hljs-keyword">this</span>.expiryDate = expiryDate;\n    }\n}\n</code></pre>\n<h3>Example of group validation</h3>\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">ValidationService</span> </span>{ <span class="hljs-comment">// By default, service interfaces are used to differentiate authentication scenarios. For example:@NotNull(groups = ValidationService.class)   </span>\n    <span class="hljs-meta">@interface</span> Save{} <span class="hljs-comment">// The same name as the method interface, the first letter capitalized, used to distinguish between authentication scene. For example:@NotNull(groups = ValidationService.Save.class),option</span>\n    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">save</span><span class="hljs-params">(ValidationParameter parameter)</span></span>;\n    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">update</span><span class="hljs-params">(ValidationParameter parameter)</span></span>;\n}\n</code></pre>\n<h3>Example of Cascading Validation</h3>\n<pre><code class="language-java"><span class="hljs-keyword">import</span> javax.validation.GroupSequence;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">ValidationService</span> </span>{   \n    <span class="hljs-meta">@GroupSequence</span>(Update.class) <span class="hljs-comment">// validate the Update group rules at the same time</span>\n    <span class="hljs-meta">@interface</span> Save{}\n    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">save</span><span class="hljs-params">(ValidationParameter parameter)</span></span>;\n \n    <span class="hljs-meta">@interface</span> Update{} \n    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">update</span><span class="hljs-params">(ValidationParameter parameter)</span></span>;\n}\n</code></pre>\n<h3>Example of parameter validation</h3>\n<pre><code class="language-java"><span class="hljs-keyword">import</span> javax.validation.constraints.Min;\n<span class="hljs-keyword">import</span> javax.validation.constraints.NotNull;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">ValidationService</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">save</span><span class="hljs-params">(@NotNull ValidationParameter parameter)</span></span>; <span class="hljs-comment">// Param must not be null</span>\n    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">delete</span><span class="hljs-params">(@Min(<span class="hljs-number">1</span>)</span> <span class="hljs-keyword">int</span> id)</span>; <span class="hljs-comment">// validate the range</span>\n}\n</code></pre>\n<h2>Configuration</h2>\n<h3>Validate Parameter on the client</h3>\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">"validationService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.alibaba.dubbo.examples.validation.api.ValidationService"</span> <span class="hljs-attr">validation</span>=<span class="hljs-string">"true"</span> /&gt;</span>\n</code></pre>\n<h3>Validate Parameter on the server</h3>\n<pre><code class="language-xml"><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.examples.validation.api.ValidationService"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"validationService"</span> <span class="hljs-attr">validation</span>=<span class="hljs-string">"true"</span> /&gt;</span>\n</code></pre>\n<h2>Validate Exception</h2>\n<pre><code class="language-java"><span class="hljs-keyword">import</span> javax.validation.ConstraintViolationException;\n<span class="hljs-keyword">import</span> javax.validation.ConstraintViolationException;\n \n<span class="hljs-keyword">import</span> org.springframework.context.support.ClassPathXmlApplicationContext;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.examples.validation.api.ValidationParameter;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.examples.validation.api.ValidationService;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.rpc.RpcException;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ValidationConsumer</span> </span>{   \n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> Exception </span>{\n        String config = ValidationConsumer.class.getPackage().getName().replace(<span class="hljs-string">\'.\'</span>, <span class="hljs-string">\'/\'</span>) + <span class="hljs-string">"/validation-consumer.xml"</span>;\n        ClassPathXmlApplicationContext context = <span class="hljs-keyword">new</span> ClassPathXmlApplicationContext(config);\n        context.start();\n        ValidationService validationService = (ValidationService)context.getBean(<span class="hljs-string">"validationService"</span>);\n        <span class="hljs-comment">// Error</span>\n        <span class="hljs-keyword">try</span> {\n            parameter = <span class="hljs-keyword">new</span> ValidationParameter();\n            validationService.save(parameter);\n            System.out.println(<span class="hljs-string">"Validation ERROR"</span>);\n        } <span class="hljs-keyword">catch</span> (RpcException e) { <span class="hljs-comment">// throw RpcException</span>\n            ConstraintViolationException ve = (ConstraintViolationException) e.getCause(); <span class="hljs-comment">// Inside a ConstraintViolationException</span>\n            Set&lt;ConstraintViolation&lt;?&gt;&gt; violations = ve.getConstraintViolations(); <span class="hljs-comment">// You can get the collection of validation error details</span>\n            System.out.println(violations);\n        }\n    } \n}\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>Support since <code>2.1.0</code> version. If you want to know how to use it, refer to  [Sample code in dubbo project] (<a href="https://github.com/apache/incubator-dubbo/tree/master/dubbo-test/dubbo-test-examples/src/main/java/com/alibaba/dubbo/examples/validation">https://github.com/apache/incubator-dubbo/tree/master/dubbo-test/dubbo-test-examples/src/main/java/com/alibaba/dubbo/examples/validation</a>) <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>The validation method is extensible, refer to <a href="https://github.com/apache/incubator-dubbo/tree/master/dubbo-test/dubbo-test-examples/src/main/java/com/alibaba/dubbo/examples/validation">Developer Extension</a> in the developer\'s manual. <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/demos/preflight-check.md",__html:'<h1>Check on start up</h1>\n<p>By default dubbo will check if the dependent service is available at startup . It will throw an exception to prevent Spring complete initialization when it is not available, so that you can find the problems early before publishing you application, the default setting: <code>check=true</code>.</p>\n<p>You can turn off checking by <code>check=false</code>. For example, some services do not care it when you run testing, or you must have one started firstly because of circular dependency.</p>\n<p>In addition, if your Spring bean is lazy-loaded or you delay reference service with API programming, turn off the check,\notherwise the service will throw an exception when the service is temporarily unavailable ,then get a null reference.  If you configure <code>check=false</code> ,you can get a reference . When the service is restored, the service can automatically reconnect.</p>\n<h2>Example</h2>\n<h3>Use the spring configuration file</h3>\n<p>Disable the startup check of a service (throw some exception/error when no provider is provided):</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span> = <span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">check</span> = <span class="hljs-string">"false"</span> /&gt;</span>\n</code></pre>\n<p>Disable startup checking for all services (throw some exception/error when not provided):</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:consumer</span> <span class="hljs-attr">check</span> = <span class="hljs-string">"false"</span> /&gt;</span>\n</code></pre>\n<p>Disable the registration center startup check (registration subscription failed error):</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">check</span>=<span class="hljs-string">"false"</span> /&gt;</span>\n</code></pre>\n<h3>Use dubbo.properties</h3>\n<pre><code class="language-properties">dubbo.reference.com.foo.BarService.check = false\ndubbo.reference.check = false\ndubbo.consumer.check = false\ndubbo.registry.check = false\n</code></pre>\n<h3>Use the -D parameter</h3>\n<pre><code class="language-sh">java -Ddubbo.reference.com.foo.BarService.check = <span class="hljs-literal">false</span>\njava -Ddubbo.reference.check = <span class="hljs-literal">false</span>\njava -Ddubbo.consumer.check = <span class="hljs-literal">false</span>\njava -Ddubbo.registry.check = <span class="hljs-literal">false</span>\n</code></pre>\n<h2>Configuration Meaning</h2>\n<p><code>dubbo.reference.check=false</code>,  Change the check value of all references forcibly, even if the configuration has a declaration, it also will be overwritten.</p>\n<p><code>dubbo.consumer.check=false</code>  The default value of <code>check</code>.  It will not be affected if there is an explicit declaration in the configuration such as<code></code>&lt;dubbo: reference check =&quot; true &quot;/&gt;`.</p>\n<p><code>dubbo.registry.check=false</code>, The two configuration above is to express success of the subscription. If the subscription is also allowed to start when the registration fails for the provider list is empty, you need to use this configuration. The system will try again in the background regularly.</p>\n'},{filename:"user/demos/reference-config-cache.md",__html:'<h1>ReferenceConfig Cache</h1>\n<p>The instance of <code>ReferenceConfig</code> is heavy. It encapsulates the connection to the registry and the connection to the provider, so it need to be cached. Otherwise, repeatedly generating <code>ReferenceConfig</code> may cause performance problems , memory and connection leaks. This problem is easy to ignored when programming in API mode.</p>\n<p>Therefore, since <code>2.4.0</code>, dubbo provides a simple utility ReferenceConfigCache for caching instances of <code>ReferenceConfig</code>.</p>\n<p>Use as follows:</p>\n<pre><code class="language-java">ReferenceConfig&lt;XxxService&gt; reference = <span class="hljs-keyword">new</span> ReferenceConfig&lt;XxxService&gt;();\nreference.setInterface(XxxService.class);\nreference.setVersion(<span class="hljs-string">"1.0.0"</span>);\n......\nReferenceConfigCache cache = ReferenceConfigCache.getCache();\n<span class="hljs-comment">// cache.get will cache the instance of Reference ,and call ReferenceConfig.get method to start ReferenceConfig</span>\nXxxService xxxService = cache.get(reference);\n<span class="hljs-comment">// Note: Cache will hold ReferenceConfig, do not call destroy method of ReferenceConfig outside. If you do this, it will invalidate ReferenceConfig in Cache!</span>\n<span class="hljs-comment">// Use xxxService instance</span>\nxxxService.sayHello();\n</code></pre>\n<p>Destroy <code>ReferenceConfig</code> in the Cache, it also remove <code>ReferenceConfig</code> and release the corresponding resources。</p>\n<pre><code class="language-java">ReferenceConfigCache cache = ReferenceConfigCache.getCache();\ncache.destroy(reference);\n</code></pre>\n<p>By default ,<code>ReferenceConfigCache</code> caches one <code>ReferenceConfig</code> for the same service Group, interface, version. The key of <code>ReferenceConfigCache</code> is from the group of service Group, interface, and the version.</p>\n<p>You can modify the strategy. Define an instance of KeyGenerator, pass it as parameter of getCache method. Refer to <code>ReferenceConfigCache</code> for information。</p>\n<pre><code class="language-java">KeyGenerator keyGenerator = <span class="hljs-keyword">new</span> ...\nReferenceConfigCache cache = ReferenceConfigCache.getCache(keyGenerator );\n</code></pre>\n'},{filename:"user/demos/registry-only.md",__html:'<h1>Register only</h1>\n<p>You have two mirroring environments, two registries.\nYou have deployed one service at only one of the registries, another registries have not had time to deploy, and other applications at both registries need to rely on the service.\nAt this time, the service provider registers service to another registrar, but the service consumers do not consume the service from another registrar.</p>\n<p>Disable subscription configuration</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"hzRegistry"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.153.10:9090"</span> /&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"qdRegistry"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.141.150:9090"</span> <span class="hljs-attr">subscribe</span>=<span class="hljs-string">"false"</span> /&gt;</span>\n</code></pre>\n<p>or</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"hzRegistry"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.153.10:9090"</span> /&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"qdRegistry"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.141.150:9090?subscribe=false"</span> /&gt;</span>\n</code></pre>\n'},{filename:"user/demos/result-cache.md",__html:'<h1>Cache Result</h1>\n<p>Cache Result <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> is used to speed up access to popular data. Dubbo provides declarative caching to reduce the user work of adding cache <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>。</p>\n<h2>Cache Type</h2>\n<ul>\n<li><code>lru</code> Delete excess cache Based on the principle of least recently used.  The hottest data is cached.</li>\n<li><code>threadlocal</code> The current thread cache. For example, a page have a lot of portal and each portal need to check user information,  you can reduce this redundant visit with this cache.</li>\n<li><code>jcache</code> integrate with <a href="http://jcp.org/en/jsr/detail?id=107%27">JSR107</a> , you can bridge a variety of cache implementation。</li>\n</ul>\n<p>Caching type can be extended,refer to:<a href="http://dubbo.apache.org/books/dubbo-dev-book-en/impls/cache.html">Cache extension</a></p>\n<h2>Configuration</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">cache</span>=<span class="hljs-string">"lru"</span> /&gt;</span>\n</code></pre>\n<p>or:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</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">"findBar"</span> <span class="hljs-attr">cache</span>=<span class="hljs-string">"lru"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:reference</span>&gt;</span>\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>Support since <code>2.1.0</code> <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p><a href="https://github.com/apache/incubator-dubbo/tree/master/dubbo-test/dubbo-test-examples/src/main/java/com/alibaba/dubbo/examples/cache">Sample</a> <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/demos/routing-rule.md",__html:'<h1>Routing Rules</h1>\n<p>The routing rules <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> determine the target server of one service call. It has two kinds of routing rules: conditional routing rules and script routing rules. It also support extension<sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>.</p>\n<h2>Write Routing Rules</h2>\n<p>Writing routing rules to the registry is usually done by the monitoring center or the console page.</p>\n<pre><code class="language-java">RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();\nRegistry registry = registryFactory.getRegistry(URL.valueOf("zookeeper://10.20.153.10:2181"));\nregistry.register(URL.valueOf("condition://0.0.0.0/com.foo.BarService?category=routers&amp;dynamic=false&amp;rule=" + URL.encode("host = 10.20.153.10 =&gt; host = 10.20.153.11") + "));\n</code></pre>\n<p>其中:</p>\n<ul>\n<li><code>condition://</code> It indicates the type of routing rules, supports routing rules and script routing rules, and can be extended. <strong>Required</strong>。</li>\n<li><code>0.0.0.0</code> It indicates that all IP addresses are valid. If you want to take effect for only one IP address, fill in the IP address. <strong>Required</strong>。</li>\n<li><code>com.foo.BarService</code> It indicates that the specified service is effective. <strong>Required</strong>。</li>\n<li><code>group=foo</code> It indicates that the specified service in specified group is effective. When absent, the specified service which dosen\'t configure group is effective.</li>\n<li><code>version=1.0</code>It indicates that the specified service in specified version is effective. When absent, the specified service which dosen\'t configure version is effective.</li>\n<li><code>category=routers</code> It indicates that the data is a dynamic configuration type. <strong>Required</strong>。</li>\n<li><code>dynamic=false</code> It indicates that it is persistent data. When the registrant exits, the data is still stored in the registry. <strong>Required</strong>。</li>\n<li><code>enabled=true</code> It indicates whether this routing rules is effective. Option, and default effective.</li>\n<li><code>force=false</code> It indicates whether it is forced to be executed when the routing result is null. If it is not enforced, the route will be automatically invalidated. Option, and default <code>false</code>.</li>\n<li><code>runtime=false</code> It indicates whether to execute routing rules at every call. If not, the result is only pre-executed and cached when the provider\'s address list changes.  It will get routing result from cache when the service is invoked. If you use parameter routing, you must to configure it as <code>true</code>. Be careful that the configuration will affect the performance. Option, and default <code>false</code>.</li>\n<li><code>priority=1</code> The priority of the routing rules. it is used for sorting, the greater the priority, the more front execution. Option, and default <code>0</code>。</li>\n<li><code>rule=URL.encode(&quot;host = 10.20.153.10 =&gt; host = 10.20.153.11&quot;)</code> It indicates the content of routing rule,<strong>Required</strong>。</li>\n</ul>\n<h2>Conditional routing rules</h2>\n<p>Routing rules based on conditional expressions, such as:<code>host = 10.20.153.10 =&gt; host = 10.20.153.11</code></p>\n<h3>Rules:</h3>\n<ul>\n<li>The previous of <code>=&gt;</code> is matched condition for consumer. All parameters compare with URL of consumers. When the consumer meet the condition, it will continue to execute the behind filter rules for consumer.</li>\n<li>After the <code>=&gt;</code> aims to filter the provider address list.  All the parameters are compared against the provider\'s URL, and consumer get only the filtered address list at finally.</li>\n<li>If the previous condition for consumer is empty, it means all consumers can matched. such as : <code>=&gt; host != 10.20.153.11</code></li>\n<li>If the filter condition for provider is empty, it means it is forbidden to visit. such as :<code>host = 10.20.153.10 =&gt;</code></li>\n</ul>\n<h3>Expressions:</h3>\n<p>Parameter Support:</p>\n<ul>\n<li>Service call information, such as: method, argument etc. Parameter routing is currently not supported</li>\n<li>URL field (On URL own), such as: protocol, host, port etc.</li>\n<li>All parameters on the URL. such as: application, organization etc.</li>\n</ul>\n<p>Condition Support:</p>\n<ul>\n<li>Equal sign <code>=</code> indicates match. such as: <code>host = 10.20.153.10</code></li>\n<li>Not equal sign <code>!=</code> indicates &quot;does not match&quot;. such as: <code>host != 10.20.153.10</code>.</li>\n</ul>\n<p>Value Support:</p>\n<ul>\n<li>Separate multiple values with a comma <code>,</code> .  Such as:<code>host != 10.20.153.10,10.20.153.11</code></li>\n<li>End with  <code>*</code> to indicate wildcard.  Such as: <code>host != 10.20.*</code></li>\n<li>Start with <code>$</code> to indicate reference to consumer parameters. Such as: <code>host = $host</code></li>\n</ul>\n<h3>Samples</h3>\n<ol start="0">\n<li>\n<p>Exclude pre-release machine:</p>\n<pre><code>=&gt; host != 172.22.3.91\n</code></pre>\n</li>\n<li>\n<p>Whitelist <sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup>:</p>\n<pre><code>host != 10.20.153.10,10.20.153.11 =&gt;\n</code></pre>\n</li>\n<li>\n<p>Blacklist:</p>\n<pre><code>host = 10.20.153.10,10.20.153.11 =&gt;\n</code></pre>\n</li>\n<li>\n<p>Service boarding application only expose part of the machine to prevent the entire cluster hanging up:</p>\n<pre><code>=&gt; host = 172.22.3.1*,172.22.3.2*\n</code></pre>\n</li>\n<li>\n<p>Additional machines for important applications:</p>\n<pre><code>application != kylin =&gt; host != 172.22.3.95,172.22.3.96\n</code></pre>\n</li>\n<li>\n<p>Read and write separation:</p>\n<pre><code>method = find*,list*,get*,is* =&gt; host = 172.22.3.94,172.22.3.95,172.22.3.96\nmethod != find*,list*,get*,is* =&gt; host = 172.22.3.97,172.22.3.98\n</code></pre>\n</li>\n<li>\n<p>Separation of Front and Background Application:</p>\n<pre><code>application = bops =&gt; host = 172.22.3.91,172.22.3.92,172.22.3.93\napplication != bops =&gt; host = 172.22.3.94,172.22.3.95,172.22.3.96\n</code></pre>\n</li>\n<li>\n<p>Isolate different network segments:</p>\n<pre><code>host != 172.22.3.* =&gt; host != 172.22.3.*\n</code></pre>\n</li>\n<li>\n<p>Providers and consumers deployed in the same cluster, the machine only visit the local service:</p>\n<pre><code>=&gt; host = $host\n</code></pre>\n</li>\n</ol>\n<h2>Script routing rules</h2>\n<p>Script routing rules <sup class="footnote-ref"><a href="#fn4" id="fnref4">[4]</a></sup> support all scripts of JDK script engine. such as: javascript, jruby, groovy, etc. Configure the script type by <code>type=javascript</code>, the default is javascript.</p>\n<pre><code>&quot;script://0.0.0.0/com.foo.BarService?category=routers&amp;dynamic=false&amp;rule=&quot; + URL.encode(&quot;(function route(invokers) { ... } (invokers))&quot;)\n</code></pre>\n<p>Routing rules that base on script engine is as follow:</p>\n<pre><code class="language-javascript">(<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">route</span>(<span class="hljs-params">invokers</span>) </span>{\n    <span class="hljs-keyword">var</span> result = <span class="hljs-keyword">new</span> java.util.ArrayList(invokers.size());\n    <span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i &lt; invokers.size(); i ++) {\n        <span class="hljs-keyword">if</span> (<span class="hljs-string">"10.20.153.10"</span>.equals(invokers.get(i).getUrl().getHost())) {\n            result.add(invokers.get(i));\n        }\n    }\n    <span class="hljs-keyword">return</span> result;\n} (invokers)); <span class="hljs-comment">// Indicates that the method is executed immediately</span>\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>Support since <code>2.2.0</code> <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>Routing Rules Extension Point: <a href="http://dubbo.apache.org/books/dubbo-dev-book-en/impls/router.html">Route Extension</a> <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn3" class="footnote-item"><p>Note: A service can only have one whitelist rule, otherwise the two rules will be filtered out. <a href="#fnref3" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn4" class="footnote-item"><p>Note: Scripts have no sandbox constraints, can execute arbitrary code, and poses a backdoor risk. <a href="#fnref4" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/demos/service-container.md",__html:'<h1>Service container</h1>\n<p>The service container is a standalone launcher because the backend service does not require the functionality of a Web container ,such as Tomcat or JBoss. If you insist on using web containers to load service providers, that increase complexity and is waste of resources.</p>\n<p>The service container is just a simple Main method and loads a simple Spring container to expose the service.</p>\n<p>The content of Service container can be extended, built-in spring, jetty, log4j etc..  This can be expanded with <a href="http://dubbo.apache.org/books/dubbo-dev-book-en/impls/container.html">Container Extension Points</a>. Configure it with the -D parameter in the java command or <code>dubbo.properties</code>.</p>\n<h2>Container type</h2>\n<h3>Spring Container</h3>\n<ul>\n<li>\n<p>Automatically load all spring configurations in the <code>META-INF/spring</code>.</p>\n<pre><code class="language-properties"></code></pre>\n</li>\n</ul>\n<p>dubbo.spring.config=classpath*:META-INF/spring/*.xml</p>\n<pre><code>\n### Jetty Container\n\n* Start an embedded Jetty for reporting status.\n* Configure:\n    * `dubbo.jetty.port=8080`: configure jetty start up port\n    * `dubbo.jetty.directory=/foo/bar`: static file that can be visited by jetty directly.\n    * `dubbo.jetty.page=log,status,system`: configure the displayed page, loading all pages by default\n\n\n### Log4j Container\n\n* Automatic configuration log4j configuration. At the start of the multi-process, log files automatically by process sub-directory.\n* Configure:\n    * `dubbo.log4j.file=/foo/bar.log`: configure log file path\n    * `dubbo.log4j.level=WARN`: configure log level\n    * `dubbo.log4j.subdirectory=20880`: configure log sub directory for multi-process startup and avoiding conflict\n\n## Container startup\n\nload spring by default.\n\n```sh\njava com.alibaba.dubbo.container.Main\n</code></pre>\n<p>Load the container that passed in by the main method</p>\n<pre><code class="language-sh">java com.alibaba.dubbo.container.Main spring jetty log4j\n</code></pre>\n<p>Load the container that passed in by the JVM option.</p>\n<pre><code class="language-sh">java com.alibaba.dubbo.container.Main -Ddubbo.container=spring,jetty,log4j\n</code></pre>\n<p>Load the container that passed in by <code>dubbo.properties</code> in the classpath.</p>\n<pre><code>dubbo.container=spring,jetty,log4j\n</code></pre>\n'},{filename:"user/demos/service-downgrade.md",__html:'<h1>Service-Downgrade</h1>\n<p>You can temporarilly shield a non-critical service through the service downgrade and define the return policy for it.</p>\n<p>Publish dynamic configuration rule to the registry:</p>\n<pre><code class="language-java">RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();\nRegistry registry = registryFactory.getRegistry(URL.valueOf(<span class="hljs-string">"zookeeper://10.20.153.10:2181"</span>));\nregistry.register(URL.valueOf(<span class="hljs-string">"override://0.0.0.0/com.foo.BarService?category=configurators&amp;dynamic=false&amp;application=foo&amp;mock=force:return+null"</span>));\n</code></pre>\n<ul>\n<li>\n<p>The configuration <code>mock=force:return+null</code> means that all calls of this service will return null value directly,without making remote calls.Usually used to reduce the effect of some slow non-critical services.</p>\n</li>\n<li>\n<p>Also you can change that configuration to <code>mock=fail:return+null</code>.Then you will get null value after a failed call.Consumer will try to make a remote call to get the truely result if succeed,and if the call failed you will get null value.Usually used to tolerate some non-critical services.</p>\n</li>\n</ul>\n'},{filename:"user/demos/service-group.md",__html:'<h1>Service Group</h1>\n<p>When you have multi-impls of a interface,you can distinguish them with the group.</p>\n<h2>Service</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">group</span>=<span class="hljs-string">"feedback"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.xxx.IndexService"</span> /&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">group</span>=<span class="hljs-string">"member"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.xxx.IndexService"</span> /&gt;</span>\n</code></pre>\n<h2>Reference</h2>\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">"feedbackIndexService"</span> <span class="hljs-attr">group</span>=<span class="hljs-string">"feedback"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.xxx.IndexService"</span> /&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"memberIndexService"</span> <span class="hljs-attr">group</span>=<span class="hljs-string">"member"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.xxx.IndewxService"</span> /&gt;</span>\n</code></pre>\n<p>Any group <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>:</p>\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">"barService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">group</span>=<span class="hljs-string">"*"</span> /&gt;</span>\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>supported after version <code>2.2.0</code> ,always select only one available group of implementations to invoke. <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/demos/static-service.md",__html:'<h1>Static Service</h1>\n<ul>\n<li>Sometimes we want to manually manage the registration and deregistration for service provider, we need to set registry to non-dynamoic mode.</li>\n</ul>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.141.150:9090"</span> <span class="hljs-attr">dynamic</span>=<span class="hljs-string">"false"</span> /&gt;</span>\n</code></pre>\n<p>Or</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.141.150:9090?dynamic=false"</span> /&gt;</span>\n</code></pre>\n<p>dynamic mode is disabled when service provider initially registers, then we need to enable it manually. When disconnects, the setting will not be deleted automatically, need to disable it manually.</p>\n<p>For a third party service provider like “memcachd”, it can directly write the address information of service provider to registry, which can be used by consumer.</p>\n<pre><code class="language-java">RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();\nRegistry registry = registryFactory.getRegistry(URL.valueOf(<span class="hljs-string">"zookeeper://10.20.153.10:2181"</span>));\nregistry.register(URL.valueOf(<span class="hljs-string">"memcached://10.20.153.11/com.foo.BarService?category=providers&amp;dynamic=false&amp;application=foo"</span>));\n</code></pre>\n'},{filename:"user/demos/stickiness.md",__html:'<h1>stickiness</h1>\n<p>Sticky connections are used for stateful services, as much as possible so that clients always make calls to the same provider, unless the provider hangs up and connects to the other one.</p>\n<p>Sticky connections will automatically open <a href="./lazy-connect.md">Delayed Connections</a> to reduce the number of long connections.</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">sticky</span>=<span class="hljs-string">"true"</span> /&gt;</span>\n</code></pre>\n'},{filename:"user/demos/subscribe-only.md",__html:'<h1>Subscribe only</h1>\n<p>To facilitate the development of tests, it is common to have a registry of all services available in develop environment.And the registration of a service provider under development may affect consumers\' inability to run.</p>\n<p>You can let service provider developers only subscribe to services only (services developed may rely on other services) ,don\'t register services under development and testing services under development with directly connection.</p>\n<p><img src="../sources/images/subscribe-only.jpg" alt="/user-guide/images/subscribe-only.jpg"></p>\n<p>User configuration:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.153.10:9090"</span> <span class="hljs-attr">register</span>=<span class="hljs-string">"false"</span> /&gt;</span>\n</code></pre>\n<p>or</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.153.10:9090?register=false"</span> /&gt;</span>\n</code></pre>\n'},{filename:"user/demos/thread-model.md",__html:'<h1>Thread Model</h1>\n<h2>Thread Model</h2>\n<ul>\n<li>If events handing can be executed quickly without sending new request like marking in memory. Events should be handled by I/O thread since it reduces thread dispatching.</li>\n<li>If event handling will be executed slowly or needs to send new I/O request like querying from database, events should be handled in thread pool. Otherwise, I/O thread will be blocked and then will be not able to receive requests.</li>\n<li>If events are handled by I/O thread, and send new I/O requests during the handling like sending a l login request during connect event, it will alert with “Potentially leading to deadlock”, but deadlock will not happen actually.</li>\n</ul>\n<p><img src="../sources/images/dubbo-protocol.jpg" alt="dubbo-protocol"></p>\n<p>Thus, we need different dispatch strategies and different thread pool configurations to face difference scenarios.</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">dispatcher</span>=<span class="hljs-string">"all"</span> <span class="hljs-attr">threadpool</span>=<span class="hljs-string">"fixed"</span> <span class="hljs-attr">threads</span>=<span class="hljs-string">"100"</span> /&gt;</span>\n</code></pre>\n<h2>Dispatcher</h2>\n<ul>\n<li>all: All messages will be dispatched to thread pool, including request, response, connect event, disconnect event and heartbeat.</li>\n<li>direct: All messages will not be dispatched to thread pool and will be executed directly by I/O thread.</li>\n<li>message: Only request, response messages will be dispatched to I/O thread. Other messages like disconnect, connect, heartbeat messages will be executed by I/O thread.</li>\n<li>execution: Only request message will be dispatched to thread pool. Other messages like response, connect, disconnect, heartbeat will be directly executed by I/O thread.</li>\n<li>connection: I/O thread will put disconnect and connect events in the queue and execute them sequentially, other messages will be dispatched to the thread pool.</li>\n</ul>\n<h2>Thread pool</h2>\n<ul>\n<li>fixed: A fixed size of thread pool. It creates threads when starts, never shut down.(default).</li>\n<li>cached: A cached thread pool. Automatically delete the thread when it’s in idle for one minute. Recreate when needed.</li>\n<li>limit: elastic thread pool. But it can only increase the size of the thread pool. The reason is to avoid performance issue caused by traffic spike when decrease the size of the thread pool.</li>\n</ul>\n'},{filename:"user/demos/token-authorization.md",__html:'<h1>Token Authorization</h1>\n<p>Through the token authorization control center at the registry to decide whether to issue tokens to consumers, you can prevent consumers from bypassing the registry access provider, another through the registry can flexibly change the authorization without modification or upgrade provider</p>\n<p><img src="../sources/images/dubbo-token.jpg" alt="/user-guide/images/dubbo-token.jpg"></p>\n<p>You can turn on token authentication globally:</p>\n<pre><code class="language-xml"><span class="hljs-comment">&lt;!--Random token , generated using a UUID--&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">token</span>=<span class="hljs-string">"true"</span> /&gt;</span>\n</code></pre>\n<p>or</p>\n<pre><code class="language-xml"><span class="hljs-comment">&lt;!--Fixed token, equivalent to the password--&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">token</span>=<span class="hljs-string">"123456"</span> /&gt;</span>\n</code></pre>\n<p>Of course can turn on token authentication at service level:</p>\n<pre><code class="language-xml"><span class="hljs-comment">&lt;!--Random token , generated using a UUID--&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">token</span>=<span class="hljs-string">"true"</span> /&gt;</span>\n</code></pre>\n<p>or</p>\n<pre><code class="language-xml"><span class="hljs-comment">&lt;!--Fixed token, equivalent to the password--&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.BarService"</span> <span class="hljs-attr">token</span>=<span class="hljs-string">"123456"</span> /&gt;</span>\n</code></pre>\n<p>Also can turn on token authentication at protocol level:</p>\n<pre><code class="language-xml"><span class="hljs-comment">&lt;!--Random token , generated using a UUID--&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">token</span>=<span class="hljs-string">"true"</span> /&gt;</span>\n</code></pre>\n<p>or</p>\n<pre><code class="language-xml"><span class="hljs-comment">&lt;!--Fixed token, equivalent to the password--&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">token</span>=<span class="hljs-string">"123456"</span> /&gt;</span>\n</code></pre>\n'},{filename:"user/dependencies.md",__html:'<h1>Dependencies</h1>\n<h2>Necessary dependencies</h2>\n<p>JDK 1.6+ <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup></p>\n<h2>Default dependencies</h2>\n<p>use <code>mvn dependency:tree &gt; dep.log</code>  command to analysis,Dubbo default depends on the following 3rd party libraries:</p>\n<pre><code>[INFO] +- com.alibaba:dubbo:jar:2.5.9-SNAPSHOT:compile\n[INFO] |  +- org.springframework:spring-context:jar:4.3.10.RELEASE:compile\n[INFO] |  +- org.javassist:javassist:jar:3.21.0-GA:compile\n[INFO] |  \\- org.jboss.netty:netty:jar:3.2.5.Final:compile\n</code></pre>\n<p>All dependencies here are selected for the default configuration of the Dubbo, which are based on stability and performance considerations.</p>\n<ul>\n<li>javassist.jar <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>: if <code>&lt;dubbo:provider proxy=&quot;jdk&quot; /&gt;</code> or <code>&lt;dubbo:consumer proxy=&quot;jdk&quot; /&gt;</code>,or <code>&lt;dubbo:application compiler=&quot;jdk&quot; /&gt;</code>, is not required.</li>\n<li>spring-context.jar <sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup>: If you are using <code>ServiceConfig</code> and <code>ReferenceConfig</code> API calls, is not required.</li>\n<li>netty.jar <sup class="footnote-ref"><a href="#fn4" id="fnref4">[4]</a></sup>: if <code>&lt;dubbo:protocol server=&quot;mina&quot;/&gt;</code> or <code>&lt;dubbo:protocol server=&quot;grizzly&quot;/&gt;</code>,Then change to mina.jar or grizzly.jar. If <code>&lt;protocol name=&quot;rmi&quot;/&gt;</code>, is not required.</li>\n</ul>\n<h2>Optinal dependencies</h2>\n<p>These dependencies  needs to be added to project manually,when you need them.</p>\n<ul>\n<li>netty-all 4.0.35.Final</li>\n<li>mina: 1.1.7</li>\n<li>grizzly: 2.1.4</li>\n<li>httpclient: 4.5.3</li>\n<li>hessian_lite: 3.2.1-fixed</li>\n<li>fastjson: 1.2.31</li>\n<li>zookeeper: 3.4.9</li>\n<li>jedis: 2.9.0</li>\n<li>xmemcached: 1.3.6</li>\n<li>hessian: 4.0.38</li>\n<li>jetty: 6.1.26</li>\n<li>hibernate-validator: 5.4.1.Final</li>\n<li>zkclient: 0.2</li>\n<li>curator: 2.12.0</li>\n<li>cxf: 3.0.14</li>\n<li>thrift: 0.8.0</li>\n<li>servlet: 3.0 <sup class="footnote-ref"><a href="#fn5" id="fnref5">[5]</a></sup></li>\n<li>validation-api: <a href="http://1.1.0.GA">1.1.0.GA</a> <sup class="footnote-ref"><a href="#fn5" id="fnref5:1">[5:1]</a></sup></li>\n<li>jcache: 1.0.0 <sup class="footnote-ref"><a href="#fn5" id="fnref5:2">[5:2]</a></sup></li>\n<li>javax.el: 3.0.1-b08 <sup class="footnote-ref"><a href="#fn5" id="fnref5:3">[5:3]</a></sup></li>\n<li>kryo: 4.0.1</li>\n<li>kryo-serializers: 0.42</li>\n<li>fst: 2.48-jdk-6</li>\n<li>resteasy: 3.0.19.Final</li>\n<li>tomcat-embed-core: 8.0.11</li>\n<li>slf4j: 1.7.25</li>\n<li>log4j: 1.2.16</li>\n</ul>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>In theory, Dubbo only depend on JDK, not depend on any 3rd party libs, you can finish logic by useing  JDK. <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>Bytecode generation <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn3" class="footnote-item"><p>Configuration parsing <a href="#fnref3" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn4" class="footnote-item"><p>Network transmission <a href="#fnref4" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn5" class="footnote-item"><p>JAVAEE <a href="#fnref5" class="footnote-backref">↩︎</a> <a href="#fnref5:1" class="footnote-backref">↩︎</a> <a href="#fnref5:2" class="footnote-backref">↩︎</a> <a href="#fnref5:3" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/maturity.md",__html:"<h1>Maturity</h1>\n<h2>Function maturity</h2>\n<table>\n<thead>\n<tr>\n<th>Feature</th>\n<th>Maturity</th>\n<th>Strength</th>\n<th>Problem</th>\n<th>Advise</th>\n<th>User</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Concurrency control</td>\n<td>Tested</td>\n<td>concurrency control</td>\n<td></td>\n<td>On trial</td>\n<td></td>\n</tr>\n<tr>\n<td>Connection control</td>\n<td>Tested</td>\n<td>connection number control</td>\n<td></td>\n<td>On trial</td>\n<td></td>\n</tr>\n<tr>\n<td>Connecting certain provider straightly</td>\n<td>Tested</td>\n<td>Provider service for point-to-point connecting straightly,for test</td>\n<td></td>\n<td>Can be used in the test environment</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>Grouping polymerization</td>\n<td>Tested</td>\n<td>Return vlue of grouping polymerization, service for menu aggregation and other services</td>\n<td>Used in special scenes</td>\n<td>Can be used in the production environment</td>\n<td></td>\n</tr>\n<tr>\n<td>Parameters validator</td>\n<td>Tested</td>\n<td>parameters validator,JSR303  validation framework integration</td>\n<td>Have effect on Performance</td>\n<td>On trial</td>\n<td>LaiWang</td>\n</tr>\n<tr>\n<td>Result cache</td>\n<td>Tested</td>\n<td>result cache,for accelerating requests</td>\n<td></td>\n<td>On trial</td>\n<td></td>\n</tr>\n<tr>\n<td>Generic reference</td>\n<td>Stable</td>\n<td>Generic reference,remote call without a business interface class,for test platforms, open api proxy service, and so on</td>\n<td></td>\n<td>Can be used in the production environment</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>Generic service</td>\n<td>Stable</td>\n<td>Generic service,no interface class is required to implement any interface,for mock paltform</td>\n<td></td>\n<td>Can be used in the production environment</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>Echo test</td>\n<td>Tested</td>\n<td>echo test</td>\n<td></td>\n<td>On trial</td>\n<td></td>\n</tr>\n<tr>\n<td>Attachment</td>\n<td>Stable</td>\n<td>Attachment</td>\n<td></td>\n<td>Can be used in the production environment</td>\n<td></td>\n</tr>\n<tr>\n<td>Asynchronous call</td>\n<td>Tested</td>\n<td>Unavailable asynchronous call</td>\n<td></td>\n<td>On trial</td>\n<td></td>\n</tr>\n<tr>\n<td>Local call</td>\n<td>Tested</td>\n<td>Local call</td>\n<td></td>\n<td>On trial</td>\n<td></td>\n</tr>\n<tr>\n<td>Callback parameter</td>\n<td>Tested</td>\n<td>Callback parameter</td>\n<td>Used in special scenes</td>\n<td>On trial</td>\n<td>Registry</td>\n</tr>\n<tr>\n<td>Events notify</td>\n<td>Tested</td>\n<td>Events notify, triggering before and after the remote call is executed</td>\n<td></td>\n<td>On trial</td>\n<td></td>\n</tr>\n<tr>\n<td>Local stub</td>\n<td>Stable</td>\n<td>Performing part of the logic on the client side</td>\n<td></td>\n<td>Can be used in the production environment</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>Local mock</td>\n<td>Stable</td>\n<td>Forged return results, which can be executed when failed, or directly executed, for service degradation</td>\n<td>Need support of registry</td>\n<td>Can be used in the production environment</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>Delay publish</td>\n<td>Stable</td>\n<td>Delay publish,used to wait for the application to load warmup data,or wait for spring context to load completly</td>\n<td></td>\n<td>Can be used in the production environment</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>Lazy connect</td>\n<td>Tested</td>\n<td>Delay setting up connections,when invocation is set up</td>\n<td></td>\n<td>On trial</td>\n<td>Registry</td>\n</tr>\n<tr>\n<td>Stickness connections</td>\n<td>Tested</td>\n<td>Stickness connections,always make a request to the same provider service unless the service is down, and then switch to another</td>\n<td></td>\n<td>On trial</td>\n<td>Registry</td>\n</tr>\n<tr>\n<td>Token authorization</td>\n<td>Tested</td>\n<td>Token authorization,  is used for service authorization</td>\n<td>Need support of registry</td>\n<td>On trial</td>\n<td></td>\n</tr>\n<tr>\n<td>Routing rule</td>\n<td>Tested</td>\n<td>Dynamically determining the call relationship</td>\n<td>Need support of registry</td>\n<td>On trial</td>\n<td></td>\n</tr>\n<tr>\n<td>Configuration rule</td>\n<td>Tested</td>\n<td>Distribute the configuration dynamically ,is the switch of business logic</td>\n<td>Need support of registry</td>\n<td>On trial</td>\n<td></td>\n</tr>\n<tr>\n<td>Accesslog</td>\n<td>Tested</td>\n<td>Accesslog,used to record call information</td>\n<td>Local storage, impact performance, limited by disk size</td>\n<td>On trial</td>\n<td></td>\n</tr>\n<tr>\n<td>Distributed transaction</td>\n<td>Research</td>\n<td>JTA/XA three phase submission transaction(TCC)</td>\n<td>Unstable</td>\n<td>Unavailable</td>\n<td></td>\n</tr>\n</tbody>\n</table>\n<h2>Strategy maturity</h2>\n<table>\n<thead>\n<tr>\n<th>Feature</th>\n<th>Maturity</th>\n<th>Strength</th>\n<th>Problem</th>\n<th>Advise</th>\n<th>User</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Zookeeper registry</td>\n<td>Stable</td>\n<td>Support the cluster,have various of related open source products, dubbo-2.3.3  and above versions are recommended</td>\n<td>Depended on the stability of zookeeper</td>\n<td>Can be used in the production environment</td>\n<td></td>\n</tr>\n<tr>\n<td>Redis registry</td>\n<td>Stable</td>\n<td>Support the client - based double - write clustering method with high performance</td>\n<td>Please ensure server time synchronization,be used to check the expired dirty data of heartbeat</td>\n<td>Can be used in the production environment</td>\n<td></td>\n</tr>\n<tr>\n<td>Multicast registry</td>\n<td>Tested</td>\n<td>Decentration,no registry needs to be installed</td>\n<td>Depending on the network topology  and routing, there is a risk across the server rooms</td>\n<td>Can be used in a small range , in developement/test  environment</td>\n<td></td>\n</tr>\n<tr>\n<td>Simple registry</td>\n<td>Tested</td>\n<td>Dogfooding,the registry itself is also a standard RPC service</td>\n<td>No cluster support, may occur single-point failure</td>\n<td>On trial</td>\n<td></td>\n</tr>\n<tr>\n<td>Feature</td>\n<td>Maturity</td>\n<td>Strength</td>\n<td>Problem</td>\n<td>Advise</td>\n<td>User</td>\n</tr>\n<tr>\n<td>Simple monitor system</td>\n<td>Stable</td>\n<td>Support JFreeChart statistics report</td>\n<td>No cluster support, may occur single-point failure,but the failure does not affect the RPC call</td>\n<td>Can be used in the production environment</td>\n<td></td>\n</tr>\n<tr>\n<td>Feature</td>\n<td>Maturity</td>\n<td>Strength</td>\n<td>Problem</td>\n<td>Advise</td>\n<td>User</td>\n</tr>\n<tr>\n<td>Dubbo protocol</td>\n<td>Stable</td>\n<td>Use NIO to reuse a single long connection and use a thread pool to process requests concurrently, Reduce handshake and increase concurrency efficiency, good performance</td>\n<td>A single connection will become a bottleneck in the transmission of large files</td>\n<td>Can be used in the production environment</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>Rmi protocol</td>\n<td>Stable</td>\n<td>Interoperable with native RMI, based on the TCP protocol</td>\n<td>Occasionally the connection fails, and the stub needs to be rebuilt</td>\n<td>Can be used in the production environment</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>Hessian protocol</td>\n<td>Stable</td>\n<td>Interoperable with native Hessian, based on the HTTP protocol</td>\n<td>Hessian.jar support is required, and the overhead of HTTP short connections is large</td>\n<td>Can be used in the production environment</td>\n<td></td>\n</tr>\n<tr>\n<td>Feature</td>\n<td>Maturity</td>\n<td>Strength</td>\n<td>Problem</td>\n<td>Advise</td>\n<td>User</td>\n</tr>\n<tr>\n<td>Netty Transporter</td>\n<td>Stable</td>\n<td>The NIO framework of JBoss, has good performance</td>\n<td>A request sends two events and needs to shield useless events</td>\n<td>Can be used in the production environment</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>Mina Transporter</td>\n<td>Stable</td>\n<td>Classic NIO framework,stable</td>\n<td>The dispatch of the message queue is not timely, under great pressure, there will be FullGC</td>\n<td>Can be used in the production environment</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>Grizzly Transporter</td>\n<td>Tested</td>\n<td>The NIO framework of Sun,applied in the GlassFish container</td>\n<td>The thread pool is not extensible, and Filter can't intercept the next filter</td>\n<td>On trial</td>\n<td></td>\n</tr>\n<tr>\n<td>Feature</td>\n<td>Maturity</td>\n<td>Strength</td>\n<td>Problem</td>\n<td>Advise</td>\n<td>User</td>\n</tr>\n<tr>\n<td>Hessian Serialization</td>\n<td>Stable</td>\n<td>Good performance,multilingual support (recommended)</td>\n<td>The compatibility of various versions of Hessian is not good, it may be in conflict with the Hessian used in the application, and the Dubbo is embedded with the source code of the hessian3.2.1</td>\n<td>Can be used in the production environment</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>Dubbo Serialization</td>\n<td>Tested</td>\n<td>The performance is better in a large number of POJO transmission by not transmitting the class information of POJO.</td>\n<td>When a field is added to the parameter object, an external file declaration is required</td>\n<td>On trial</td>\n<td></td>\n</tr>\n<tr>\n<td>Json Serialization</td>\n<td>Tested</td>\n<td>pure text,can be cross-language parsed,default using FastJson</td>\n<td>Poor performance</td>\n<td>On trial</td>\n<td></td>\n</tr>\n<tr>\n<td>Java Serialization</td>\n<td>Stable</td>\n<td>Java native support</td>\n<td>Poor performance</td>\n<td>Can be used in the production environment</td>\n<td></td>\n</tr>\n<tr>\n<td>Feature</td>\n<td>Maturity</td>\n<td>Strength</td>\n<td>Problem</td>\n<td>Advise</td>\n<td>User</td>\n</tr>\n<tr>\n<td>Javassist ProxyFactory</td>\n<td>Stable</td>\n<td>Bytecode generation instead of reflection,good performance(recommended)</td>\n<td>Depending on the javassist.jar and taking up the JVM's Perm memory, the Perm may have to be larger:java -XX:PermSize=128m</td>\n<td>Can be used in the production environment</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>Jdk ProxyFactory</td>\n<td>Stable</td>\n<td>JDK native support</td>\n<td>Poor performance</td>\n<td>Can be used in the production environment</td>\n<td></td>\n</tr>\n<tr>\n<td>Feature</td>\n<td>Maturity</td>\n<td>Strength</td>\n<td>Problem</td>\n<td>Advise</td>\n<td>User</td>\n</tr>\n<tr>\n<td>Failover Cluster</td>\n<td>Stable</td>\n<td>Failure automatically switches, when failure occurs, retries other servers, usually used for read operations.(recommended)</td>\n<td>Retry will lead to longer delays</td>\n<td>Can be used in the production environment</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>Failfast Cluster</td>\n<td>Stable</td>\n<td>Fast failure, only one call, failure to be reported immediately, usually used for non idempotent writing.</td>\n<td>If a server is being restarted, a call failure may occur</td>\n<td>Can be used in the production environment</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>Failsafe Cluster</td>\n<td>Stable</td>\n<td>Failsafe, when abnormal, directly ignored, usually used to write to the audit log and other operations</td>\n<td>Call information loss</td>\n<td>Can be used in the production environment</td>\n<td>Monitor</td>\n</tr>\n<tr>\n<td>Failback Cluster</td>\n<td>Tested</td>\n<td>Failure auto recovery, backstage record failure request, regular retransmission, usually used for message notification operations</td>\n<td>Unreliable, lost when restart the server</td>\n<td>Can be used in the production environment</td>\n<td>Registry</td>\n</tr>\n<tr>\n<td>Forking Cluster</td>\n<td>Tested</td>\n<td>Multiple servers are invoked in parallel, as long as one success is returned, often used for high real-time reading operations.</td>\n<td>Need to waste more service resources</td>\n<td>Can be used in the production environment</td>\n<td></td>\n</tr>\n<tr>\n<td>Broadcast Cluster</td>\n<td>Tested</td>\n<td>A broadcast calls all providers, one by one, and any error is wrongly reported, usually used to update the provider's local state</td>\n<td>The speed is slow, and any false report is wrong.</td>\n<td>Can be used in the production environment</td>\n<td></td>\n</tr>\n<tr>\n<td>Feature</td>\n<td>Maturity</td>\n<td>Strength</td>\n<td>Problem</td>\n<td>Advise</td>\n<td>User</td>\n</tr>\n<tr>\n<td>Random LoadBalance</td>\n<td>Stable</td>\n<td>Random probability, set random probability according to weight(recommended)</td>\n<td>The probability of a collision on a cross section is high. When retrying, there may be an unequal instantaneous pressure.</td>\n<td>Can be used in the production environment</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>RoundRobin LoadBalance</td>\n<td>Stable</td>\n<td>Round Robin,setting wheel based ratio according to the weight after the Convention</td>\n<td>There is a slow machine accumulation request problem, and extreme circumstances may cause an avalanche</td>\n<td>Can be used in the production environment</td>\n<td></td>\n</tr>\n<tr>\n<td>LeastActive LoadBalance</td>\n<td>Stable</td>\n<td>The least active call number, the random number of the same active number, the active number is the count difference before and after the call, making the slow machine receive less request.</td>\n<td>Do not support the weight, in the capacity planning, not to pressure a machine oriented pressure measurement by weight capacity</td>\n<td>Can be used in the production environment</td>\n<td></td>\n</tr>\n<tr>\n<td>ConsistentHash LoadBalance</td>\n<td>Stable</td>\n<td>The consistency hash, the same parameters always request to the same provider, when one provider hung, originally sent to the provider's request, based on virtual nodes, spread to other providers, will not cause dramatic changes</td>\n<td>Uneven distribution of pressure</td>\n<td>Can be used in the production environment</td>\n<td></td>\n</tr>\n<tr>\n<td>Feature</td>\n<td>Maturity</td>\n<td>Strength</td>\n<td>Problem</td>\n<td>Advise</td>\n<td>User</td>\n</tr>\n<tr>\n<td>Condition routing rule</td>\n<td>Stable</td>\n<td>Routing rules based on conditional expressions, simple and easy to use</td>\n<td>There are some complex multi branch conditions, and the rules are difficult to describe</td>\n<td>Can be used in the production environment</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>Script routing rules</td>\n<td>Tested</td>\n<td>Routing rules based on the script engine, powerful</td>\n<td>No sandbox is running, scripting ability is too powerful and may be the back door</td>\n<td>On trial</td>\n<td></td>\n</tr>\n<tr>\n<td>Feature</td>\n<td>Maturity</td>\n<td>Strength</td>\n<td>Problem</td>\n<td>Advise</td>\n<td>User</td>\n</tr>\n<tr>\n<td>Spring Container</td>\n<td>Stable</td>\n<td>Automatically load all Spring configurations under the META-INF/spring directory</td>\n<td></td>\n<td>Can be used in the production environment</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>Jetty Container</td>\n<td>Stable</td>\n<td>Start an embedded Jetty for reporting state</td>\n<td>When a large number of pages are accessed, the threads and memory of the server are affected</td>\n<td>Can be used in the production environment</td>\n<td>Alibaba</td>\n</tr>\n<tr>\n<td>Log4j Container</td>\n<td>Stable</td>\n<td>Configuring the configuration of the log4j automatically, automatically subdirecting the log files by process at the startup of multiple processes</td>\n<td>The user can't control the configuration of log4j, inflexible</td>\n<td>Can be used in the production environment</td>\n<td>Alibaba</td>\n</tr>\n</tbody>\n</table>\n"},{filename:"user/perf-test.md",__html:'<h1>Performance test report</h1>\n<h2>Test instructions</h2>\n<ol start="0">\n<li>In this performance test, the performance of all Dubbo 2.0 supported protocols in different sizes and data types is tested and compared with the Dubbo 1.0.</li>\n<li>The overall performance is increased by 1.0 compared with 10%, and the average increase is 10%. The performance improvement of 10%~50% can also be achieved by using the new Dubbo serialization of Dubbo 2.0 .</li>\n<li>In the stability test, because the underlying communication framework is changed from Mina to netty, the growth of objects in old area is greatly reduced, and the 50 hour operation increases less than 200m and no fullgc.</li>\n<li>There is a problem: performance of 2.0 is less than 1.0 in 50K data, and it is doubted that it may be a buffer setting problem, and the next version will be further confirmed.</li>\n</ol>\n<h2>Test environment</h2>\n<h3>Hardware deployment and parameter adjustment</h3>\n<table>\n<thead>\n<tr>\n<th>Model</th>\n<th>CPU</th>\n<th>Memory</th>\n<th>Network</th>\n<th>Disk</th>\n<th>Kernel</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Tecal BH620</td>\n<td>model name : Intel(R) Xeon(R) CPU           E5520  @ 2.27GHz cache size : 8192 KB processor_count : 16</td>\n<td>Total System Memory: 6G Hardware Memory Info:  Size: 4096MB</td>\n<td>eth0: Link is up at 1000 Mbps, full duplex. peth0: Link is up at 1000 Mbps, full duplex.</td>\n<td>/dev/sda: 597.9 GB</td>\n<td>2.6.18-128.el5xen x86_64</td>\n</tr>\n</tbody>\n</table>\n<h3>Software architecture</h3>\n<table>\n<thead>\n<tr>\n<th>Software name and version</th>\n<th>key parameter</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>java version &quot;1.6.0_18&quot; Java(TM) SE Runtime Environment (build 1.6.0_18-b07) Java HotSpot(TM) 64-Bit Server VM (build 16.0-b13, mixed mode)</td>\n<td>-server -Xmx2g -Xms2g -Xmn256m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70</td>\n</tr>\n<tr>\n<td><a href="http://jboss-4.0.5.GA">jboss-4.0.5.GA</a></td>\n<td></td>\n</tr>\n<tr>\n<td>httpd-2.0.61</td>\n<td>KeepAlive On MaxKeepAliveRequests 100000 KeepAliveTimeout 180 MaxRequestsPerChild 1000000 <IfModule worker.c>         StartServers 5         MaxClients 1024         MinSpareThreads 25         MaxSpareThreads 75         ThreadsPerChild 64         ThreadLimit 128         ServerLimit 16 </IfModule></td>\n</tr>\n</tbody>\n</table>\n<h2>Test purpose</h2>\n<h3>Expected performance indicators (quantized)</h3>\n<table>\n<thead>\n<tr>\n<th>Scene name</th>\n<th>Corresponding index name</th>\n<th>Range of expected values</th>\n<th>Actual value</th>\n<th>Whether or not to meet expectations (yes / no)</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>1k data</td>\n<td>Response time</td>\n<td>0.9ms</td>\n<td>0.79ms</td>\n<td>Yes</td>\n</tr>\n<tr>\n<td>1k data</td>\n<td>TPS</td>\n<td>10000</td>\n<td>11994</td>\n<td>Yes</td>\n</tr>\n</tbody>\n</table>\n<h3>Expected operating conditions (non quantified, optional)</h3>\n<ul>\n<li>The performance of 2.0 is not less than 1, and the performance of the intermodulation of 2.0 and 1.0 is not significantly reduced. In addition to 50K string, the rest are passed</li>\n<li>JVM memory is running stable, no OOM, and there is no reasonable large object in the heap memory. Passed</li>\n<li>CPU, memory, network, disk, file handle are occupied smoothly. Passed</li>\n<li>There is no frequent thread lock, and the number of threads is stable. Passed</li>\n<li>Business thread load balance. Passed</li>\n</ul>\n<h2>Test script</h2>\n<ol start="0">\n<li>\n<p>Performance test scence (10 concurrency)</p>\n<ul>\n<li>Pass in 1K String, do not do anything, return the original</li>\n<li>Pass in 50K String, do not do anything, return the original</li>\n<li>Pass in 200K String, do not do anything, return the original</li>\n<li>Incoming 1K POJO (nested complex person objects) without any processing, return to the original</li>\n</ul>\n<p>The above scenario is tested for 10 minutes in Dubbo 1.0, Dubbo 2.0 (hessian2 serialization), Dubbo 2.0 (Dubbo serialization), RMI, Hessian 3.2.0, HTTP (JSON serialization). It mainly examines the performance of serialization and network IO, so the server has no business logic. 10 is to consider the concurrent HTTP protocol in high with the use of CPU high rate may hit the bottleneck.</p>\n</li>\n<li>\n<p>Concurrent scene (20 concurrency)\n1K String is introduced into the server segment for 1W times, and a random number is regenerated each time and then assembled. Examine whether business threads can be assigned to each CPU.</p>\n</li>\n<li>\n<p>Stability scence (20 concurrency)\nAt the same time, we call the 1 parameter String (5K) method, the 1 parameter is the person object method, the 1 parameter is map (the value is 3 person), and it runs for 50 hours continuously.</p>\n</li>\n<li>\n<p>High pressure scene (20 concurrency)\nOn the basis of the stability scenario, the providers and consumers are arranged into 2 sets (one machine and 2 instances), and the parameters of String are 20byte to 200K, and are randomly transformed every 10 minutes.</p>\n</li>\n</ol>\n<h2>Test result</h2>\n<h3>Scene name: scence POJO</h3>\n<table>\n<thead>\n<tr>\n<th></th>\n<th>TPS success avg value</th>\n<th>Response time avg value(ms)</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>dubbo1 (hessian2 serialization+mina)</td>\n<td>10813.5</td>\n<td>0.9</td>\n</tr>\n<tr>\n<td>dubbo2 (hessian2 serialization+netty)</td>\n<td>11994</td>\n<td>0.79</td>\n</tr>\n<tr>\n<td>dubbo2 (dubbo serialization+netty)</td>\n<td>13620</td>\n<td>0.67</td>\n</tr>\n<tr>\n<td>rmi</td>\n<td>2461.79</td>\n<td>4</td>\n</tr>\n<tr>\n<td>hessian</td>\n<td>2417.7</td>\n<td>4.1</td>\n</tr>\n<tr>\n<td>http(json serialization)</td>\n<td>8179.08</td>\n<td>1.15</td>\n</tr>\n<tr>\n<td>The default percentage of 2.0 and 1.0</td>\n<td>10.92</td>\n<td>-12.22</td>\n</tr>\n<tr>\n<td>Dubbo serialization compared to the percentage of hessian2 serialization</td>\n<td>13.56</td>\n<td>-15.19</td>\n</tr>\n</tbody>\n</table>\n<p>POJO TPS</p>\n<p><img src="./sources/images/pojotps.png" alt="pojotps.png"></p>\n<p>POJO Response</p>\n<p><img src="./sources/images/pojores.png" alt="pojores.png"></p>\n<h3>Scene name: scence 1k string</h3>\n<table>\n<thead>\n<tr>\n<th></th>\n<th>TPS success avg value</th>\n<th>Response time avg value(ms)</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>dubbo1(hessian2 serialization+mina)</td>\n<td>11940</td>\n<td>0.8</td>\n</tr>\n<tr>\n<td>dubbo2 (hessian2 serialization+netty)</td>\n<td>14402</td>\n<td>0.64</td>\n</tr>\n<tr>\n<td>dubbo2 (dubbo serialization+netty)</td>\n<td>15096</td>\n<td>0.6</td>\n</tr>\n<tr>\n<td>rmi</td>\n<td>11136.02</td>\n<td>0.81</td>\n</tr>\n<tr>\n<td>hessian</td>\n<td>11426.83</td>\n<td>0.79</td>\n</tr>\n<tr>\n<td>http(json serialization)</td>\n<td>8919.27</td>\n<td>1.04</td>\n</tr>\n<tr>\n<td>The default percentage of 2.0 and 1.0</td>\n<td>20.62</td>\n<td>-20.00</td>\n</tr>\n<tr>\n<td>Dubbo serialization compared to the percentage of hessian2 serialization</td>\n<td>4.82</td>\n<td>-6.25</td>\n</tr>\n</tbody>\n</table>\n<p>1k TPS</p>\n<p><img src="./sources/images/1ktps.png" alt="1ktps.png"></p>\n<p>1k Response</p>\n<p><img src="./sources/images/1kres.png" alt="1kres.png"></p>\n<h3>Scene name: scence 50k string</h3>\n<table>\n<thead>\n<tr>\n<th>TPS success avg value</th>\n<th>Response time avg value(ms)</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>dubbo1(hessian2 serialization+mina</td>\n<td>1962.7</td>\n</tr>\n<tr>\n<td>dubbo2 (hessian2 serialization+netty)</td>\n<td>1293</td>\n</tr>\n<tr>\n<td>dubbo2 (dubbo serialization+netty)</td>\n<td>1966</td>\n</tr>\n<tr>\n<td>rmi</td>\n<td>3349.88</td>\n</tr>\n<tr>\n<td>hessian</td>\n<td>1925.33</td>\n</tr>\n<tr>\n<td>http(json serialization)</td>\n<td>3247.1</td>\n</tr>\n<tr>\n<td>The default percentage of 2.0 and 1.0</td>\n<td>-34.12</td>\n</tr>\n<tr>\n<td>Dubbo serialization compared to the percentage of hessian2 serialization</td>\n<td>52.05</td>\n</tr>\n</tbody>\n</table>\n<p>50K TPS</p>\n<p><img src="./sources/images/50ktps.png" alt="50ktps.png"></p>\n<p>50K Response</p>\n<p><img src="./sources/images/50kres.png" alt="50kres.png"></p>\n<h3>Scene name: scence 200k string</h3>\n<table>\n<thead>\n<tr>\n<th>TPS success avg value</th>\n<th>Response time avg value(ms)</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>dubbo1(hessian2 serialization+mina)</td>\n<td>324.2</td>\n</tr>\n<tr>\n<td>dubbo2 (hessian2 serialization+netty)</td>\n<td>362.92</td>\n</tr>\n<tr>\n<td>dubbo2 (dubbo serialization+netty)</td>\n<td>569.5</td>\n</tr>\n<tr>\n<td>rmi</td>\n<td>1031.28</td>\n</tr>\n<tr>\n<td>hessian</td>\n<td>628.06</td>\n</tr>\n<tr>\n<td>http(json serialization)</td>\n<td>1011.97</td>\n</tr>\n<tr>\n<td>The default percentage of 2.0 and 1.0</td>\n<td>11.94</td>\n</tr>\n<tr>\n<td>Dubbo serialization compared to the percentage of hessian2 serialization</td>\n<td>56.92</td>\n</tr>\n</tbody>\n</table>\n<p>200K TPS</p>\n<p><img src="./sources/images/200ktps.png" alt="200ktps.png"></p>\n<p><strong>200K Response</strong></p>\n<p><img src="./sources/images/200kres.png" alt="200kres.png"></p>\n<h2>Test analysis</h2>\n<h3>Performance analysis and evaluation</h3>\n<p>The performance test conclusion of Dubbo 2 has been improved and improved from performance, memory footprint and stability. Because of its memory management, the change of Mina into netty greatly reduces the 1 version of the large memory sawtooth in high concurrency and large data.</p>\n<h3>Performance comparison analysis (new and old environment, different data magnitude, etc.)</h3>\n<p>The performance of Dubbo 2 is compared with that of Dubbo 1, which is all hessian2 serialization. The performance is improved (except for 50K String). See the performance data of the fifth chapter in detail.</p>\n<p>For compatibility default serialization and 1 consistent with hessian2, such as have higher requirements on the performance of Dubbo serialization can be used, which is in the process of complicated object, can be obtained in 50% large data upgrade (but it is not recommended for use Dubbo protocol).</p>\n<p>The purpose of Dubbo is to meet the RPC calls with high concurrent and small data volume. The performance is not good under large data volume. It is recommended to use RMI or HTTP protocol.</p>\n<h3>Test limitation analysis (optional)</h3>\n<p>This performance test examines the performance of the Dubbo itself, and the performance of the actual use needs to be verified.</p>\n<p>Because the performance of Dubbo itself is in millisecond and the base number is small, performance improvement may not change the performance of the application as a whole.</p>\n<p>All the monitoring charts are not listed because of the limit of length.</p>\n'},{filename:"user/preface/architecture.md",__html:"<h1>Architecture</h1>\n<p><img src=\"../sources/images/dubbo-architecture.jpg\" alt=\"dubbo-architucture\"></p>\n<h5>Specification of Node's Role</h5>\n<table>\n<thead>\n<tr>\n<th>Node</th>\n<th>Role Spec</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>Provider</code></td>\n<td>The provider exposes remote services</td>\n</tr>\n<tr>\n<td><code>Consumer</code></td>\n<td>The consumer calls the remote services</td>\n</tr>\n<tr>\n<td><code>Registry</code></td>\n<td>The registry is responsible for service discovery and configuration</td>\n</tr>\n<tr>\n<td><code>Monitor</code></td>\n<td>The monitor counts the number of service invocations and time-consuming</td>\n</tr>\n<tr>\n<td><code>Container</code></td>\n<td>The container manages the services's lifetime</td>\n</tr>\n</tbody>\n</table>\n<h5>Service relationship</h5>\n<ol start=\"0\">\n<li><code>Container</code> is responsible for launching, loading, and running the service <code>Provider</code>.</li>\n<li><code>Provider</code> registers its services to <code>Register</code> at the time it starts.</li>\n<li><code>Consumer</code> subscribes the services it needs from the <code>Register</code> when it starts.</li>\n<li><code>Register</code> returns the <code>Provider</code>s list to <code>Consumer</code>, when it changes, the <code>Register</code> will push the changed data to <code>Consumer</code> through long connection.</li>\n<li><code>Consumer</code> selects one of the <code>Provider</code>s based on soft load balancing algorithm and executes the invocation, if fails, it will choose another <code>Provider</code>.</li>\n<li>Both <code>Consumer</code> and <code>Provider</code> will count the number service invocations and time-consuming in memory, and send the statistics to <code>Monitor</code> every minute.</li>\n</ol>\n<p>Dubbo has the following features: Connectivity, Robustness, Scalability and Upgradeability.</p>\n<h2>Connectivity</h2>\n<ul>\n<li><code>Register</code> is responsible for the registration and search of service addresses, like directory services, <code>Provider</code> and <code>Consumer</code> only interact with the registry during startup, and the registry does not forward requests, so it is less stressed</li>\n<li>'Monitor' is responsible for counting the number of service invocations and time-consuming, the statistics will assembles in <code>Provider</code>'s and <code>Consumer</code>'s memory first and  then sent to <code>Monitor</code></li>\n<li>'Provider' registers services to 'Register' and report time-consuming statistic(not include network overhead) to 'Monitor'</li>\n<li>'Consumer' gets a list of service provider addresses from <code>Registry</code>, call the provider directly according to the LB algorithm, report the time-consuming statistic to <code>Monitor</code>, which includes network overhead</li>\n<li>The connections between <code>Register</code>, <code>Provider</code> and <code>Consumer</code> are long connections, <code>Moniter</code> is an exception</li>\n<li><code>Register</code> is aware of the existence of <code>Provider</code> through the long connection, when <code>Provider</code> gets down, <code>Provider</code> will push the event to <code>Consumer</code></li>\n<li>It doesn't affect the already running instances of <code>Provider</code> and <code>Consumer</code> even all of the <code>Register</code> and <code>Monitor</code> get down, since <code>Consumer</code> got a cache of <code>Provider</code>s list</li>\n<li><code>Register</code> and <code>Monitor</code> are optional, <code>Consumer</code> can connect <code>Provider</code> directly</li>\n</ul>\n<h2>Robustness</h2>\n<ul>\n<li><code>Monitor</code>'s downtime doesn't affect the usage, only lose some sampling data</li>\n<li>When the DB server goes down, <code>Register</code> can return service <code>Provider</code>s list to <code>Consumer</code> by checking its cache, but new <code>Provider</code> cannot regiter any services</li>\n<li><code>Register</code> is a peer cluster, it will automatically switch to another when any instance goes down</li>\n<li>Even all <code>Register</code>'s instances go down, <code>Provider</code> and <code>Consumer</code> can still conmunicate by checking their local cache</li>\n<li>Service <code>Provider</code>s are stateless, one instance's downtime doesn't affect the usage</li>\n<li>After all the <code>Provider</code>s of one service go down, <code>Consumer</code> can not use the that service, and infinitely reconnect to wait for service <code>Provider</code> to recover</li>\n</ul>\n<h2>Scalability</h2>\n<ul>\n<li><code>Register</code> is a peer cluster that can dynamically increases its instances,  all clients will automatically discover the new instances.</li>\n<li><code>Provider</code> is stateless, it can dynamically increases the deployment instances, and the registry will push the new service provider information to the <code>Consumer</code>.</li>\n</ul>\n<h2>Upgradeablity</h2>\n<p>When the service cluster is further expanded and the IT governance structure is further upgraded, dynamic deployment is needed, and the current distributed service architecture will not bring resistance. Here is a possible future architecture:</p>\n<p><img src=\"../sources/images/dubbo-architecture-future.jpg\" alt=\"dubbo-architucture-futures\"></p>\n<h5>Specification of Node's Role</h5>\n<table>\n<thead>\n<tr>\n<th>Node</th>\n<th>Role Spec</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>Deployer</code></td>\n<td>Local proxy for automatic services deployment</td>\n</tr>\n<tr>\n<td><code>Repository</code></td>\n<td>The repository is used to store application packages</td>\n</tr>\n<tr>\n<td><code>Scheduler</code></td>\n<td>The scheduler automatically increases or decreases service providers based on the access pressure</td>\n</tr>\n<tr>\n<td><code>Admin</code></td>\n<td>Unified management console</td>\n</tr>\n<tr>\n<td><code>Registry</code></td>\n<td>the registry is responsible for service discovery and configuration</td>\n</tr>\n<tr>\n<td><code>Monitor</code></td>\n<td>The monitor counts the service call times and time-consuming</td>\n</tr>\n</tbody>\n</table>\n"},{filename:"user/preface/background.md",__html:'<h1>Background</h1>\n<p>With the fast development of Internet, the scale of web applications expands unceasingly, and finally we find that the traditional vertical architecture(monolithic) can not handle this any more. Distributed service architecture and the flow computing architecture are imperative, and a governance system is urgently needed to ensure an orderly evolution of the architecture.</p>\n<p><img src="../sources/images/dubbo-architecture-roadmap.jpg" alt="image"></p>\n<h4>Monolithic architecture</h4>\n<p>When the traffic is very low, there is only one application, all the features are deployed together to reduce the deployment node and cost. At this point, the data access framework (ORM) is the key to simplifying the workload of the CRUD.</p>\n<h4>Vertical architecture</h4>\n<p>When the traffic gets heavier, add monolithic application instances can not accelerate the access very well, one way to improve efficiency is to split the monolithic into discrete applications. At this point, the Web framework (MVC) used to accelerate front-end page development is the key.</p>\n<h4>Distributed service architecture</h4>\n<p>When there are more and more vertical applications, the interaction between applications is inevitable, some core businesses are extracted and served as independent services, which gradually forms a stable service center,this way the front-end application can respond to the changeable market demand more quickly. At this point, the distributed service framework (RPC) for business reuse and integration is the key.</p>\n<h4>Flow computing architecture</h4>\n<p>When there are more and more services, capacity evaluation becomes difficult, and also services with small scales often causes waste of resources. To solve these problems, a scheduling center should be added to manage the cluster capacity based on traffics and to improve the utilization of the cluster. At this time, the resource scheduling and governance centers (SOA), which are used to improve machine utilization, are the keys.</p>\n'},{filename:"user/preface/index.md",__html:"<h1>Introduction</h1>\n"},{filename:"user/preface/requirements.md",__html:'<h1>Requirements</h1>\n<p><img src="../sources/images/dubbo-service-governance.jpg" alt="image"></p>\n<p>Before the advent of large-scare services, an application might just exposes or references remote service by using RMI or Hessian, the call is done by configuring serive URL, and load balance is done through hardwares, like F5.</p>\n<p><strong>When there are more and more services, it becomes very difficult to configure the service URL, the single point pressure of F5 hardware load balancer is also increasing.</strong> At this point, a service registry is needed to dynamically register and discover services to make the service\'s location transparent. By obtaining the list of service provider addresses in the consumer side, the soft load balancing and Failover can be realized, this reduces the dependence on the F5 hardware load balacer and some of the costs.</p>\n<p><strong>When things go further, the service dependencies become so complex that it can\'t even tell which applications to start before, even the architect can\'t fully describe the application architecture relationships</strong>. At this time, automatically draw the dependency diagram of the applications is needed to help the architect to be clear of the relationship.</p>\n<p><strong>Then, the traffic becomes even heavier, the capacity problem of the service is exposed, how many machines are needed to support this service? When should the machine be added?</strong> To solve these problems, first, the daily service calls and the amount of response time should be counted as a reference for capacity planning. Second, dynamically adjust the weight, increase the weight of an online machine, and recorded the response time changes until it reaches the threshold, record the visits times at this time, then multiply this number of visits by the total number of machines to calculate the capacity in turn.</p>\n<p>Above are the most basic requirements of Dubbo.</p>\n'},{filename:"user/preface/usage.md",__html:'<h1>Usage</h1>\n<h2>Spring configuration of local service</h2>\n<p>local.xml:</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">“xxxService”</span> <span class="hljs-attr">class</span>=<span class="hljs-string">“com.xxx.XxxServiceImpl”</span> /&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">bean</span> <span class="hljs-attr">id</span>=<span class="hljs-string">“xxxAction”</span> <span class="hljs-attr">class</span>=<span class="hljs-string">“com.xxx.XxxAction”</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">property</span> <span class="hljs-attr">name</span>=<span class="hljs-string">“xxxService”</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">“xxxService”</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">bean</span>&gt;</span>\n</code></pre>\n<h2>Spring configuration of remote service</h2>\n<p>The remote configuration can be done by very little change based on the local configuration:</p>\n<ul>\n<li>split the <code>local.xml</code> into two part, put the service define part into <code>remote-privider.xml</code>(exists in the provider node), meanwhile the refrence part into <code>remote-consumer.xml</code>(exists in the consumer node).</li>\n<li>add <code>&lt;dubbo:service&gt;</code> to the provider\'s configuration, and <code>&lt;dubbo:reference&gt;</code> to the consumer\'s configuration.</li>\n</ul>\n<p>remote-provider.xml:</p>\n<pre><code class="language-xml"><span class="hljs-comment">&lt;!-- define remote service bean the same way as local service bean --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">bean</span> <span class="hljs-attr">id</span>=<span class="hljs-string">“xxxService”</span> <span class="hljs-attr">class</span>=<span class="hljs-string">“com.xxx.XxxServiceImpl”</span> /&gt;</span> \n<span class="hljs-comment">&lt;!-- expose the remote service --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">“com.xxx.XxxService”</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">“xxxService”</span> /&gt;</span> \n</code></pre>\n<p>remote-consumer.xml:</p>\n<pre><code class="language-xml"><span class="hljs-comment">&lt;!-- reference the remote service --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">id</span>=<span class="hljs-string">“xxxService”</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">“com.xxx.XxxService”</span> /&gt;</span>\n<span class="hljs-comment">&lt;!-- use remote service the same say as local service --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">bean</span> <span class="hljs-attr">id</span>=<span class="hljs-string">“xxxAction”</span> <span class="hljs-attr">class</span>=<span class="hljs-string">“com.xxx.XxxAction”</span>&gt;</span> \n    <span class="hljs-tag">&lt;<span class="hljs-name">property</span> <span class="hljs-attr">name</span>=<span class="hljs-string">“xxxService”</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">“xxxService”</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">bean</span>&gt;</span>\n</code></pre>\n'},{filename:"user/quick-start.md",__html:'<h1>Quick start</h1>\n<p>Dubbo uses a full Spring configuration, transparent access application,No API intrusion to your application,Just load the Dubbo configuration with Spring,Dubbo is loaded on the spring based schema extension.</p>\n<p>If you don\'t want to use the Spring configuration, you can call it by <a href="./configuration/api.md">the way of API</a>.</p>\n<h2>Service provider</h2>\n<p>Complete installation steps, see:<a href="../admin/install/provider-demo.md">Provider demo installation</a></p>\n<h3>Defining service interfaces</h3>\n<p>DemoService.java <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>:</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.alibaba.dubbo.demo;\n\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">DemoService</span> </span>{\n    <span class="hljs-function">String <span class="hljs-title">sayHello</span><span class="hljs-params">(String name)</span></span>;\n}\n</code></pre>\n<h3>Implement interface in service provider</h3>\n<p>DemoServiceImpl.java <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>:</p>\n<pre><code class="language-java">\n<span class="hljs-keyword">package</span> com.alibaba.dubbo.demo.provider;\n \n<span class="hljs-keyword">import</span> com.alibaba.dubbo.demo.DemoService;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DemoServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">DemoService</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">sayHello</span><span class="hljs-params">(String name)</span> </span>{\n        <span class="hljs-keyword">return</span> <span class="hljs-string">"Hello "</span> + name;\n    }\n}\n</code></pre>\n<h3>Declaring exposure services with Spring configuration</h3>\n<p>provider.xml:</p>\n<pre><code class="language-xml"><span class="php"><span class="hljs-meta">&lt;?</span>xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"UTF-8"</span><span class="hljs-meta">?&gt;</span></span>\n<span class="hljs-tag">&lt;<span class="hljs-name">beans</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans"</span>\n    <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>\n    <span class="hljs-attr">xmlns:dubbo</span>=<span class="hljs-string">"http://dubbo.apache.org/schema/dubbo"</span>\n    <span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd        http://dubbo.apache.org/schema/dubbo        http://dubbo.apache.org/schema/dubbo/dubbo.xsd"</span>&gt;</span>\n \n    <span class="hljs-comment">&lt;!-- Provider application information for computing dependencies --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:application</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"hello-world-app"</span>  /&gt;</span>\n \n    <span class="hljs-comment">&lt;!-- Using the multicast broadcast registry to expose service addresses --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"multicast://224.5.6.7:1234"</span> /&gt;</span>\n \n    <span class="hljs-comment">&lt;!-- Exposing service at port 20880 with Dubbo protocol --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"20880"</span> /&gt;</span>\n \n    <span class="hljs-comment">&lt;!-- Declaration of service interfaces that need to be exposed  --&gt;</span>\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.demo.DemoService"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"demoService"</span> /&gt;</span>\n \n    <span class="hljs-comment">&lt;!-- Implement services like local bean --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">bean</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"demoService"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"com.alibaba.dubbo.demo.provider.DemoServiceImpl"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">beans</span>&gt;</span>\n</code></pre>\n<h3>Loading  Spring Configuration</h3>\n<p>Provider.java:</p>\n<pre><code class="language-java"><span class="hljs-keyword">import</span> org.springframework.context.support.ClassPathXmlApplicationContext;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Provider</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> Exception </span>{\n        ClassPathXmlApplicationContext context = <span class="hljs-keyword">new</span> ClassPathXmlApplicationContext(<span class="hljs-keyword">new</span> String[] {<span class="hljs-string">"http://10.20.160.198/wiki/display/dubbo/provider.xml"</span>});\n        context.start();\n        System.in.read(); <span class="hljs-comment">// Press any key to exit</span>\n    }\n}\n</code></pre>\n<h2>Service consumer</h2>\n<p>Complete installation steps, see : <a href="http://dubbo.apache.org/books/dubbo-admin-book-en/install/consumer-demo.html">Consumer demo installation</a></p>\n<h3>Using the Spring configuration to reference a remote service</h3>\n<p>consumer.xml:</p>\n<pre><code class="language-xml"><span class="php"><span class="hljs-meta">&lt;?</span>xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"UTF-8"</span><span class="hljs-meta">?&gt;</span></span>\n<span class="hljs-tag">&lt;<span class="hljs-name">beans</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans"</span>\n    <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>\n    <span class="hljs-attr">xmlns:dubbo</span>=<span class="hljs-string">"http://dubbo.apache.org/schema/dubbo"</span>\n    <span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd        http://dubbo.apache.org/schema/dubbo        http://dubbo.apache.org/schema/dubbo/dubbo.xsd"</span>&gt;</span>\n \n    <span class="hljs-comment">&lt;!-- Consumer application names, used to calculate dependencies,not matching conditions, do not be the same as the provider --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:application</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"consumer-of-helloworld-app"</span>  /&gt;</span>\n \n    <span class="hljs-comment">&lt;!-- Using the multicast broadcast registry to discovery the exposed  services --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"multicast://224.5.6.7:1234"</span> /&gt;</span>\n \n    <span class="hljs-comment">&lt;!-- Generate a remote service proxy that can be used as demoService as local bean --&gt;</span>\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">interface</span>=<span class="hljs-string">"com.alibaba.dubbo.demo.DemoService"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">beans</span>&gt;</span>\n</code></pre>\n<h3>Load the Spring configuration and call a remote service</h3>\n<p>Consumer.java <sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup>:</p>\n<pre><code class="language-java"><span class="hljs-keyword">import</span> org.springframework.context.support.ClassPathXmlApplicationContext;\n<span class="hljs-keyword">import</span> com.alibaba.dubbo.demo.DemoService;\n \n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Consumer</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> Exception </span>{\n        ClassPathXmlApplicationContext context = <span class="hljs-keyword">new</span> ClassPathXmlApplicationContext(<span class="hljs-keyword">new</span> String[] {<span class="hljs-string">"http://10.20.160.198/wiki/display/dubbo/consumer.xml"</span>});\n        context.start();\n        DemoService demoService = (DemoService)context.getBean(<span class="hljs-string">"demoService"</span>); <span class="hljs-comment">//Obtaining a remote service proxy</span>\n        String hello = demoService.sayHello(<span class="hljs-string">"world"</span>); <span class="hljs-comment">// Executing remote methods </span>\n        System.out.println( hello ); <span class="hljs-comment">// Display the call result </span>\n    }\n}\n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>The interface needs to be packaged separately, shared by the service provider and the consumer <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>Hidden realization of service consumer <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn3" class="footnote-item"><p>IoC injection can also be used <a href="#fnref3" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/recommend.md",__html:'<h1>Recommended usage</h1>\n<h2>Configuring the attributes of the consumer side as much as possible on the provider side</h2>\n<p>the reason is:</p>\n<ul>\n<li>Service providers are more aware of service performance parameters than service users,Such as the timeout time of the call, the reasonable retry times, and so on.</li>\n<li>If  a attribute is configurated in provider side,  not configurated in consumer side,  consumer service will use the attribute in provider side. That is to say, the provider side\'s attribute can be used as consumer\'s default value <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>. Otherwise, consumer service will use consumer-side\'s attribute,but can\'t cnotrol the provider service,it\'s usually unreasonable.</li>\n</ul>\n<p>Configuring the attributes of the consumer side as much as possible on the provider side,Make the provider service developer think more about the characteristics and quality of the provider side service.</p>\n<p>Examples:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.alibaba.hello.api.HelloService"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0.0"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"helloService"</span>\n    <span class="hljs-attr">timeout</span>=<span class="hljs-string">"300"</span> <span class="hljs-attr">retry</span>=<span class="hljs-string">"2"</span> <span class="hljs-attr">loadbalance</span>=<span class="hljs-string">"random"</span> <span class="hljs-attr">actives</span>=<span class="hljs-string">"0"</span>\n/&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.hello.api.WorldService"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0.0"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"helloService"</span>\n    <span class="hljs-attr">timeout</span>=<span class="hljs-string">"300"</span> <span class="hljs-attr">retry</span>=<span class="hljs-string">"2"</span> <span class="hljs-attr">loadbalance</span>=<span class="hljs-string">"random"</span> <span class="hljs-attr">actives</span>=<span class="hljs-string">"0"</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">"findAllPerson"</span> <span class="hljs-attr">timeout</span>=<span class="hljs-string">"10000"</span> <span class="hljs-attr">retries</span>=<span class="hljs-string">"9"</span> <span class="hljs-attr">loadbalance</span>=<span class="hljs-string">"leastactive"</span> <span class="hljs-attr">actives</span>=<span class="hljs-string">"5"</span> /&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span>/&gt;</span>\n</code></pre>\n<p>The consumer side properties that can be configured on provider are:</p>\n<ol start="0">\n<li><code>timeout</code> Method call timeout</li>\n<li><code>retries</code> The number of failed retries, default value is 1 <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup></li>\n<li><code>loadbalance</code> Load balance algorithm <sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup>,default algorithm is random <code>random</code>,and polling <code>roundrobin</code>、least active <sup class="footnote-ref"><a href="#fn4" id="fnref4">[4]</a></sup> <code>leastactive</code></li>\n<li><code>actives</code> Consumer side, maximum concurrent call limitation. That is , when the concurrent requests of consumer service reach maximum  configuration,the new call will wait until to catch a timeout error.\nConfigurated in  <code>dubbo:method</code>(method level configuration) , then the concurrent limitation point at method.Configurated in <code>dubbo:service</code>(service level configuration),then the concurrent limitation point at service.</li>\n</ol>\n<p>Detailed configuration instructions see:<a href="./references/xml/introduction.md">Dubbo configuration introduction</a></p>\n<h2>Configuring reasonable provider end properties on provider</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">threads</span>=<span class="hljs-string">"200"</span> /&gt;</span> \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.hello.api.HelloService"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0.0"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"helloService"</span>\n    <span class="hljs-attr">executes</span>=<span class="hljs-string">"200"</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">"findAllPerson"</span> <span class="hljs-attr">executes</span>=<span class="hljs-string">"50"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:service</span>&gt;</span>\n</code></pre>\n<p>The provider side properties that can be configured on provider service are:</p>\n<ol start="0">\n<li><code>threads</code> service thread pool size</li>\n<li><code>executes</code> If concurrent requests number that a provider service handled reach the maximum thead pool count , the new call will wait,then the consumer call may catch a timeout error. Configurated in  <code>dubbo:method</code>(method level configuration) , then the concurrent limitation point at method.Configurated in <code>dubbo:service</code>(service level configuration),then the concurrent limitation point at service.</li>\n</ol>\n<h2>Configuration management information</h2>\n<p>Now we have the owner information and organization infomation to differentiate the sites。It\'s easy to contact with the service owners when there is a problem, please write at least two persons for backup. The information of owners and organizations can be seen in the registry.</p>\n<p>application configuration owners,organizations:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:application</span> <span class="hljs-attr">owner</span>=<span class="hljs-string">”ding.lid,william.liangf”</span> <span class="hljs-attr">organization</span>=<span class="hljs-string">”intl”</span> /&gt;</span>\n</code></pre>\n<p>service configuration owners:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">owner</span>=<span class="hljs-string">”ding.lid,william.liangf”</span> /&gt;</span>\n</code></pre>\n<p>reference configuration owners:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">owner</span>=<span class="hljs-string">”ding.lid,william.liangf”</span> /&gt;</span>\n</code></pre>\n<p><code>dubbo:service</code>、<code>dubbo:reference</code> have no configuration owner, then use the owner configured in <code>dubbo:application</code>.</p>\n<h2>Set up the Dubbo cache file</h2>\n<p>Provider service list caching file:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">file</span>=<span class="hljs-string">”${user.home}/output/dubbo.cache”</span> /&gt;</span>\n</code></pre>\n<p>Notations:</p>\n<ol start="0">\n<li>You can modify  the cahe file path of the application according to the needs. Ensure that the file will not be cleared during the release process.</li>\n<li>If there are more than one application process, do not use the same file path to avoid the content being overwritten.</li>\n</ol>\n<p>This file caches the list of the registry and the list of service providers. With this configuration, when the application is restarted , if  the Dubbo registry is not available, the application will read the information from the service provider list from the cache file. That can ensure the availability of the application.</p>\n<h2>Monitor configuration</h2>\n<ol start="0">\n<li>\n<p>Expose service with a fixed port, instead of using a random port</p>\n<p>In this way, when there is a delay in the registry push, the consumer can also call the  original provider service address hrough the cache list and succeed。</p>\n</li>\n<li>\n<p>Use Dragoon\'s HTTP monitoring item to monitor the service provider on the registry</p>\n<p>The state of Dragoon monitoring service in the registry : <a href="http://dubbo-reg1.hst.xyi.cn.alidc.net:8080/status/com.alibaba.morgan.member.MemberService:1.0.5">http://dubbo-reg1.hst.xyi.cn.alidc.net:8080/status/com.alibaba.morgan.member.MemberService:1.0.5</a> Ensure that the service exists on the registry .</p>\n</li>\n<li>\n<p>Service provider,use Dragoon\'s telnet mommand or shell monitor  command</p>\n<p>Monitoring service provider port status :<code>echo status | nc -i 1 20880 | grep OK | wc -l</code>, 20880 is the service port</p>\n</li>\n<li>\n<p>Service consumer side, cast the service to EchoService,and call <code>$echo()</code>  to test whether the provider of the service is available</p>\n<p>eg: <code>assertEqauls(“OK”, ((EchoService)memberService).$echo(“OK”));</code></p>\n</li>\n</ol>\n<h2>Don\'t use the configuration of dubbo.properties file, suggeset to use  the configuration of XML</h2>\n<p>All of the configuration items in the dubbo can be configured in the spring configuration file,and can be configured for a single service.</p>\n<p>The Dubbo default value is used if completely not set up , please see the instructions in the article  <a href="./references/xml/introduction.md">Dubbo configuration introduction</a> .</p>\n<h3>The relation between attribute name of dubbo.properties and XML</h3>\n<ol start="0">\n<li>\n<p>application name <code>dubbo.application.name</code></p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:application</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"myalibaba"</span> &gt;</span>\n</code></pre>\n</li>\n<li>\n<p>registry address <code>dubbo.registry.address</code></p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"11.22.33.44:9090"</span> &gt;</span>\n</code></pre>\n</li>\n<li>\n<p>call timeout <code>dubbo.service.*.timeout</code></p>\n<p>Timeout can be set in multiple configuration items <code>timeout</code>,cover from top to bottom (The top one have a higher priority )<sup class="footnote-ref"><a href="#fn5" id="fnref5">[5]</a></sup>,The coverage strategy of other parameters(<code>retries</code>、<code>loadbalance</code>、<code>actives</code> and so on)is:</p>\n<p>Certain method  Configuration of a provider service</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.alibaba.xxx.XxxService"</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">"findPerson"</span> <span class="hljs-attr">timeout</span>=<span class="hljs-string">"1000"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:service</span>&gt;</span>\n</code></pre>\n<p>Configuration of a provider specific interface</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.alibaba.xxx.XxxService"</span> <span class="hljs-attr">timeout</span>=<span class="hljs-string">"200"</span> /&gt;</span>\n</code></pre>\n</li>\n<li>\n<p>Service provider protocol <code>dubbo.service.protocol</code>、Service monitor port <code>dubbo.service.server.port</code></p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"20880"</span> /&gt;</span>\n</code></pre>\n</li>\n<li>\n<p>Service thread pool size <code>dubbo.service.max.thread.threads.size</code></p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">threads</span>=<span class="hljs-string">"100"</span> /&gt;</span>\n</code></pre>\n</li>\n<li>\n<p>No provider throws exceptions (Fast-Fail) when the consumer is started ()\n<code>alibaba.intl.commons.dubbo.service.allow.no.provider</code></p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.alibaba.xxx.XxxService"</span> <span class="hljs-attr">check</span>=<span class="hljs-string">"false"</span> /&gt;</span>\n</code></pre>\n</li>\n</ol>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>Overlay rules for configuration: 1) The method level configuration has a higher priority than the interface level, that is to say,small scope have a high priority 2) Consumer side configuration has a higher priority than provider side, better than global configuration, the last one is the Dubbo hard coded configuration value(<a href="./configuration/properties.md">Dubbo configuration introduction</a>) <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>With the first call, the call will be called 3 times <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn3" class="footnote-item"><p>How to select a service to call when there are multiple Provider services <a href="#fnref3" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn4" class="footnote-item"><p>It means that consumer service can call the best provider service, and reduce to call the the slow provider service. <a href="#fnref4" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn5" class="footnote-item"><p><code>timeout</code> Can be set in multiple places, configuration items and overlay rules: <a href="./references/xml/introduction.md">Dubbo Schema configuration introduction</a> <a href="#fnref5" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/references/api.md",__html:'<h1>API Reference</h1>\n<p>Generally speaking, dubbo keeps its functionality no intrusive as much as possible, but for some particular features, there\'s no other way not only API can achieve. <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup></p>\n<p>These APIs are summarized here below:</p>\n<h2>Configuration API</h2>\n<pre><code>com.alibaba.dubbo.config.ServiceConfig\ncom.alibaba.dubbo.config.ReferenceConfig\ncom.alibaba.dubbo.config.ProtocolConfig\ncom.alibaba.dubbo.config.RegistryConfig\ncom.alibaba.dubbo.config.MonitorConfig\ncom.alibaba.dubbo.config.ApplicationConfig\ncom.alibaba.dubbo.config.ModuleConfig\ncom.alibaba.dubbo.config.ProviderConfig\ncom.alibaba.dubbo.config.ConsumerConfig\ncom.alibaba.dubbo.config.MethodConfig\ncom.alibaba.dubbo.config.ArgumentConfig\n</code></pre>\n<p>Pls. refer to <a href="../configuration/api.md">API Configuration</a> for further information.</p>\n<h2>Annotation API</h2>\n<pre><code>com.alibaba.dubbo.config.annotation.Service\ncom.alibaba.dubbo.config.annotation.Reference\n</code></pre>\n<p>Pls. refer to <a href="../configuration/annotation.md">Annotation Configuration</a> for further information.</p>\n<h2>Model API</h2>\n<pre><code>com.alibaba.dubbo.common.URL\ncom.alibaba.dubbo.rpc.RpcException\n</code></pre>\n<h2>Context API</h2>\n<pre><code>com.alibaba.dubbo.rpc.RpcContext\n</code></pre>\n<p>Pls. refer to <a href="../demos/context.md">context</a> &amp; <a href="../demos/attachment.md">pass parameter in attachment</a> &amp; <a href="../demos/async-call.md">asynchronous call</a> for further information.</p>\n<h2>Service API</h2>\n<pre><code>com.alibaba.dubbo.rpc.service.GenericService\ncom.alibaba.dubbo.rpc.service.GenericException\n</code></pre>\n<p>Pls. refer to <a href="../demos/generic-reference.md">generic reference</a> &amp; <a href="../demos/generic-service.md">generic service</a> for further information.</p>\n<pre><code>com.alibaba.dubbo.rpc.service.EchoService\n</code></pre>\n<p>Pls. refer to <a href="../demos/echo-service.md">test via echo service</a> for further details.</p>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>Attention: do not rely on APIs other than what\'re mentioned here, otherwise your application may face the risk of incompatibility after upgrade dubbo. <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/references/maven.md",__html:'<h1>Maven Plugin Reference</h1>\n<h2>Start a simple registry server</h2>\n<p>Start a simple registry server listening on port 9099 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>:</p>\n<pre><code class="language-sh">mvn dubbo:registry -Dport=9099 \n</code></pre>\n<h2>Generate a service provider demo application</h2>\n<p>Generate a service provider with the specified interface and version:</p>\n<pre><code class="language-sh">mvn dubbo:create -Dapplication=xxx -Dpackage=com.alibaba.xxx -Dservice=XxxService,YyyService -Dversion=1.0.0 \n</code></pre>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>Default port is 9090 if the port is not specified <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/references/protocol/dubbo.md",__html:'<h1>dubbo://</h1>\n<p>Dubbo protocol which is the default protocol of Dubbo RPC Framework uses a single long connection and NIO asynchronous communication,it is suitable for small data but with high concurrency RPC call and the number of consumer machine is much greater than provider</p>\n<p>On the other hand, the Dubbo protocol is not suitable for transmitting large amounts of data, such as file transmission, video transmission, etc., unless the request is very low.</p>\n<p><img src="../../sources/images/dubbo-protocol.jpg" alt="dubbo-protocol.jpg"></p>\n<ul>\n<li>Transporter: mina, netty, grizzy</li>\n<li>Serialization: dubbo, hessian2, java, json</li>\n<li>Dispatcher: all, direct, message, execution, connection</li>\n<li>ThreadPool: fixed, cached</li>\n</ul>\n<h2>Features</h2>\n<p>The default protocol is Dubbo protocol ,based on netty  <code>3.2.5.Final</code> and Hessian2 <code>3.2.1-fixed-2(Alibaba embed version)</code>.</p>\n<ul>\n<li>Default connection number: single connection</li>\n<li>Default connection mode: long connection</li>\n<li>Transmission protocol: TCP</li>\n<li>Transmission mode: NIO asynchronous transmission</li>\n<li>Serialization: Hessian2 serialization</li>\n<li>Scope of application: incoming and outgoing data packets are small (recommended less than 100K),try not to transfer large files or large strings with Dubbo protocol.</li>\n<li>Applicable scenarios:: most RPC scenarios</li>\n</ul>\n<h2>Constraint</h2>\n<ul>\n<li>Parameters and return values must implement <code>Serializable</code> interface</li>\n<li>Parameters and return values can not be customized to implement <code>List</code>,<code>Map</code>, <code>Number</code>,<code>Date</code>, <code>Calendar</code> interface, can only be implemented with the JDK, because Hessian2 will do some special treatment, Attribute values in the class will be lost.</li>\n<li>Hessian serialization:to solve compatibility issues, only serialize class name,all the fields declared by the class,not included static fields,method information</li>\n</ul>\n<table>\n<thead>\n<tr>\n<th>Data transformation</th>\n<th>Cases</th>\n<th>Result</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>A-&gt;B</td>\n<td>Class A has one more property than Class B</td>\n<td>It doesn\'t throw exception ,Class B doesn\'t have Class A new property,other is normal</td>\n</tr>\n<tr>\n<td>A-&gt;B</td>\n<td>enum Class A has one more new enum than enum Class B,when use Class A new enum to transfor to B</td>\n<td>throw exception</td>\n</tr>\n<tr>\n<td>A-&gt;B</td>\n<td>enum Class A has one more new enum than enum Class B,when don\'t use Class A new enum to transfor to B</td>\n<td>It doesn\'t throw exception</td>\n</tr>\n<tr>\n<td>A-&gt;B</td>\n<td>Class A and Class B have same property name,but the property type is different</td>\n<td>throw exception</td>\n</tr>\n<tr>\n<td>A-&gt;B</td>\n<td>serialId is not same</td>\n<td>normal</td>\n</tr>\n</tbody>\n</table>\n<p>The interface new addition method has no effect on the client. If the method is not required by the client, the client does not need to redeploy it. The input parameter and result class add new properties, and if the client does not need new properties, it does not need to be redeployed too.</p>\n<p>The change of input parameter and result property name has no effect on client serialization, but if the client is not redeployed, no matter the input or output, the value of which property name had change is not available.</p>\n<p>Summary: the server side and the client do not need to be fully consistent with the domain objects,but you still should know about what would happen.</p>\n<h2>Configuration</h2>\n<p>configure protocol</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"20880"</span> /&gt;</span>\n</code></pre>\n<p>configure provider level default protocol:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"dubbo"</span> /&gt;</span>\n</code></pre>\n<p>configure service level default protocol:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"dubbo"</span> /&gt;</span>\n</code></pre>\n<p>configure multiple port:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"dubbo1"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"20880"</span> /&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"dubbo2"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"20881"</span> /&gt;</span>\n</code></pre>\n<p>configure protocol options:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">“dubbo”</span> <span class="hljs-attr">port</span>=<span class="hljs-string">“9090”</span> <span class="hljs-attr">server</span>=<span class="hljs-string">“netty”</span> <span class="hljs-attr">client</span>=<span class="hljs-string">“netty”</span> <span class="hljs-attr">codec</span>=<span class="hljs-string">“dubbo”</span> <span class="hljs-attr">serialization</span>=<span class="hljs-string">“hessian2”</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">“UTF-8”</span> <span class="hljs-attr">threadpool</span>=<span class="hljs-string">“fixed”</span> <span class="hljs-attr">threads</span>=<span class="hljs-string">“100”</span> <span class="hljs-attr">queues</span>=<span class="hljs-string">“0”</span> <span class="hljs-attr">iothreads</span>=<span class="hljs-string">“9”</span> <span class="hljs-attr">buffer</span>=<span class="hljs-string">“8192”</span> <span class="hljs-attr">accepts</span>=<span class="hljs-string">“1000”</span> <span class="hljs-attr">payload</span>=<span class="hljs-string">“8388608”</span> /&gt;</span>\n</code></pre>\n<p>configure multiple connectios:</p>\n<p>Dubbo protocol default uses a single long connection per service per consumer for each service provider,and multiple connections can be used if the amount of data is large</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">connections</span>=<span class="hljs-string">"2"</span> /&gt;</span>\n</code></pre>\n<ul>\n<li><code>&lt;dubbo:service connections=&quot;0&quot;&gt;</code>  OR <code>&lt;dubbo:reference connections=&quot;0&quot;&gt;</code> It means that the service uses a share long connection per provider. <code>default</code></li>\n<li><code>&lt;dubbo:service connections=&quot;1&quot;&gt;</code> OR <code>&lt;dubbo:reference connections=&quot;1&quot;&gt;</code> It means that the service uses a separate long connection.</li>\n<li><code>&lt;dubbo:service connections=&quot;2&quot;&gt;</code> OR<code>&lt;dubbo:reference connections=&quot;2&quot;&gt;</code> It means that the service uses two separate long connection.</li>\n</ul>\n<p>To prevent being hung up by a large number of connections, you can limit the number of connections at the service provider side.</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"dubbo"</span> <span class="hljs-attr">accepts</span>=<span class="hljs-string">"1000"</span> /&gt;</span>\n</code></pre>\n<p>or configure in <code>dubbo.properties</code>:</p>\n<pre><code>dubbo.service.protocol=dubbo\n</code></pre>\n'},{filename:"user/references/protocol/hessian.md",__html:'<h1>hessian://</h1>\n<p>Hessian protocol is used for integrate Hessian services, and it use http protocol to  communicate and expose services by servlet.Dubbo use Jetty server as default servlet container.</p>\n<p>Dubbo\'s Hessian protocol interoperates with native Hessian services:</p>\n<ul>\n<li>Providers use Dubbo\'s Hessian protocol to expose services that consumers call directly using standard Hessian interfaces</li>\n<li>Alternatively, the provider exposes the service using standard Hessian and the consumer calls it using Dubbo\'s Hessian protocol.</li>\n</ul>\n<h2>Features</h2>\n<ul>\n<li>Number of connections: multiple connections</li>\n<li>Connection: short connection</li>\n<li>Transmission protocol: HTTP</li>\n<li>Transmission: synchronous transmission</li>\n<li>Serialization: Hessian binary serialization</li>\n<li>Scope of application: Incoming and outgoing parameter packets are large, the number of providers is more than that of consumers and can transfer files.</li>\n<li>Applicable scenarios: page transfer, file transfer, or interoperability with native hessian services</li>\n</ul>\n<h2>dependency</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.caucho<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>hessian<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>4.0.7<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>\n</code></pre>\n<h2>Constraint</h2>\n<ul>\n<li>Parameters and return class must implement <code>Serializable</code> interface</li>\n<li>Parameters and return values can not be customized to implement <code>List</code>,<code>Map</code>, <code>Number</code>,<code>Date</code>, <code>Calendar</code> interface, can only be implemented with the JDK, because Hessian2 will do some special treatment, Attribute values in the class will be lost.</li>\n</ul>\n<h2>Configuration</h2>\n<p>configure hessian protocol:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"hessian"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"8080"</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"jetty"</span> /&gt;</span>\n</code></pre>\n<p>configure provider level default protocol:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"hessian"</span> /&gt;</span>\n</code></pre>\n<p>configure service level default protocol:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"hessian"</span> /&gt;</span>\n</code></pre>\n<p>configure multiple port:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"hessian1"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"hessian"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"8080"</span> /&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"hessian2"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"hessian"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"8081"</span> /&gt;</span>\n</code></pre>\n<p>configure direct connect mode:</p>\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">"helloService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"HelloWorld"</span> <span class="hljs-attr">url</span>=<span class="hljs-string">"hessian://10.20.153.10:8080/helloWorld"</span> /&gt;</span>\n</code></pre>\n'},{filename:"user/references/protocol/http.md",__html:'<h1>http://</h1>\n<p>Dubbo http protocol is base on HTTP form and Spring\'s HttpInvoker</p>\n<h2>Features</h2>\n<ul>\n<li>Number of connections: multiple connections</li>\n<li>Connection: short connection</li>\n<li>Transmission protocol: HTTP</li>\n<li>Transmission: synchronous transmission</li>\n<li>Serialization: form serialization</li>\n<li>Scope of application: Available browser view, the form or URL can be passed parameters, Temporary files are not supported.</li>\n<li>Applicable scenarios: Services that need to be available to both application and browser</li>\n</ul>\n<h2>Constraint</h2>\n<ul>\n<li>Parameters and return values must be consistent with Bean specifications</li>\n</ul>\n<h2>Configuration</h2>\n<p>configure http protocol:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"http"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"8080"</span> /&gt;</span>\n</code></pre>\n<p>configure Jetty Server (default):</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">...</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"jetty"</span> /&gt;</span>\n</code></pre>\n<p>configure Servlet Bridge Server (recommend):</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">...</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"servlet"</span> /&gt;</span>\n</code></pre>\n<p>configure DispatcherServlet:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">servlet</span>&gt;</span>\n         <span class="hljs-tag">&lt;<span class="hljs-name">servlet-name</span>&gt;</span>dubbo<span class="hljs-tag">&lt;/<span class="hljs-name">servlet-name</span>&gt;</span>\n         <span class="hljs-tag">&lt;<span class="hljs-name">servlet-class</span>&gt;</span>com.alibaba.dubbo.remoting.http.servlet.DispatcherServlet<span class="hljs-tag">&lt;/<span class="hljs-name">servlet-class</span>&gt;</span>\n         <span class="hljs-tag">&lt;<span class="hljs-name">load-on-startup</span>&gt;</span>1<span class="hljs-tag">&lt;/<span class="hljs-name">load-on-startup</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">servlet</span>&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">servlet-mapping</span>&gt;</span>\n         <span class="hljs-tag">&lt;<span class="hljs-name">servlet-name</span>&gt;</span>dubbo<span class="hljs-tag">&lt;/<span class="hljs-name">servlet-name</span>&gt;</span>\n         <span class="hljs-tag">&lt;<span class="hljs-name">url-pattern</span>&gt;</span>/*<span class="hljs-tag">&lt;/<span class="hljs-name">url-pattern</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">servlet-mapping</span>&gt;</span>\n</code></pre>\n<p>Note that if you use servlets to dispatch requests:</p>\n<ul>\n<li>the port of protocol <code>&lt;dubbo:protocol port=&quot;8080&quot; /&gt;</code> must same as  servlet container\'s.</li>\n<li>the context path of protocol <code>&lt;dubbo:protocol contextpath=&quot;foo&quot; /&gt;</code> must same as servlet application\'s.</li>\n</ul>\n'},{filename:"user/references/protocol/introduction.md",__html:'<h1>Protocol reference Book</h1>\n<p>Dubbo protocol is recommended. The performance of each protocol, see:<a href="../../perf-test.md">Performance</a></p>\n'},{filename:"user/references/protocol/memcached.md",__html:'<h1>memcached://</h1>\n<p>RPC protocol based on memcached implementation.</p>\n<h2>Register memcached service address</h2>\n<pre><code class="language-java">RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();\nRegistry registry = registryFactory.getRegistry(URL.valueOf(<span class="hljs-string">"zookeeper://10.20.153.10:2181"</span>));\nregistry.register(URL.valueOf(<span class="hljs-string">"memcached://10.20.153.11/com.foo.BarService?category=providers&amp;dynamic=false&amp;application=foo&amp;group=member&amp;loadbalance=consistenthash"</span>));\n</code></pre>\n<h2>Use in client</h2>\n<p>get service reference:</p>\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">"cache"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"java.util.Map"</span> <span class="hljs-attr">group</span>=<span class="hljs-string">"member"</span> /&gt;</span>\n</code></pre>\n<p>or direct access by IP:</p>\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">"cache"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"java.util.Map"</span> <span class="hljs-attr">url</span>=<span class="hljs-string">"memcached://10.20.153.10:11211"</span> /&gt;</span>\n</code></pre>\n<p>you can also use a custom interface:</p>\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">"cache"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.CacheService"</span> <span class="hljs-attr">url</span>=<span class="hljs-string">"memcached://10.20.153.10:11211"</span> /&gt;</span>\n</code></pre>\n<p>The method name is the same as the standard method name of memcached, just like get(key), set(key, value), delete(key)。</p>\n<p>If the method name and the memcached standard method name are not the same, you need to configure the mapping</p>\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">"cache"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.CacheService"</span> <span class="hljs-attr">url</span>=<span class="hljs-string">"memcached://10.20.153.10:11211"</span> <span class="hljs-attr">p:set</span>=<span class="hljs-string">"putFoo"</span> <span class="hljs-attr">p:get</span>=<span class="hljs-string">"getFoo"</span> <span class="hljs-attr">p:delete</span>=<span class="hljs-string">"removeFoo"</span> /&gt;</span>\n</code></pre>\n'},{filename:"user/references/protocol/redis.md",__html:'<h1>redis://</h1>\n<p>RPC protocol based on memcached implementation.</p>\n<h2>Register redis service address</h2>\n<pre><code class="language-java">RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();\nRegistry registry = registryFactory.getRegistry(URL.valueOf(<span class="hljs-string">"zookeeper://10.20.153.10:2181"</span>));\nregistry.register(URL.valueOf(<span class="hljs-string">"redis://10.20.153.11/com.foo.BarService?category=providers&amp;dynamic=false&amp;application=foo&amp;group=member&amp;loadbalance=consistenthash"</span>));\n</code></pre>\n<h2>Use in client</h2>\n<p>get service reference:</p>\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">"store"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"java.util.Map"</span> <span class="hljs-attr">group</span>=<span class="hljs-string">"member"</span> /&gt;</span>\n</code></pre>\n<p>or direct access by IP:</p>\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">"store"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"java.util.Map"</span> <span class="hljs-attr">url</span>=<span class="hljs-string">"redis://10.20.153.10:6379"</span> /&gt;</span>\n</code></pre>\n<p>you can also use a custom interface:</p>\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">"store"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.StoreService"</span> <span class="hljs-attr">url</span>=<span class="hljs-string">"redis://10.20.153.10:6379"</span> /&gt;</span>\n</code></pre>\n<p>The method name is the same as the standard method name of memcached, just like get(key), set(key, value), delete(key)。</p>\n<p>If the method name and the memcached standard method name are not the same, you need to configure the mapping</p>\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">"cache"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.foo.CacheService"</span> <span class="hljs-attr">url</span>=<span class="hljs-string">"memcached://10.20.153.10:11211"</span> <span class="hljs-attr">p:set</span>=<span class="hljs-string">"putFoo"</span> <span class="hljs-attr">p:get</span>=<span class="hljs-string">"getFoo"</span> <span class="hljs-attr">p:delete</span>=<span class="hljs-string">"removeFoo"</span> /&gt;</span>\n</code></pre>\n'},{filename:"user/references/protocol/rmi.md",__html:'<h1>rmi://</h1>\n<p>The RMI protocol uses the JDK standard <code>java.rmi.*</code> Implementation, using a block short connection and JDK standard serialization.</p>\n<h2>Features</h2>\n<ul>\n<li>Number of connections: multiple connections</li>\n<li>Connection: short connection</li>\n<li>Transmission protocol: HTTP</li>\n<li>Transmission: synchronous transmission</li>\n<li>Serialization: Java standard Object Serialization</li>\n<li>Scope of application:the number of providers is more than that of consumers and can transfer files.</li>\n<li>Applicable scenarios: Conventional remote service method calls, interoperating with native RMI services</li>\n</ul>\n<h2>Constraint</h2>\n<ul>\n<li>Parameters and return values must implement <code>Serializable</code> interface</li>\n<li>The timeout configuration for RMI  is invalid, you need to use java startup parameter settings:<code>-Dsun.rmi.transport.tcp.responseTimeout=3000</code>,see the RMI configuration below</li>\n</ul>\n<h2>Configuration in dubbo.properties</h2>\n<pre><code class="language-properties">dubbo.service.protocol=rmi\n</code></pre>\n<h2>RMI Configuration</h2>\n<pre><code class="language-sh">java -Dsun.rmi.transport.tcp.responseTimeout=3000\n</code></pre>\n<p>more RMI options please check <a href="http://download.oracle.com/docs/cd/E17409_01/javase/6/docs/technotes/guides/rmi/sunrmiproperties.html">JDK Document</a></p>\n<h2>Interface</h2>\n<p>If the service interface implement the <code>java.rmi.Remote</code> interface, it can interoperate with the native RMI, ie:</p>\n<ul>\n<li>Providers expose services using Dubbo\'s RMI protocol, consumers call directly with the standard RMI interface,</li>\n<li>Or the provider exposes services using standard RMI, and consumers invoke with Dubbo\'s RMI protocol.</li>\n</ul>\n<p>If the service interface doesn\'t implement the <code>java.rmi.Remote</code> interface:</p>\n<ul>\n<li>Default Dubbo will automatically generate a <code>com.xxx.XxxService$Remote</code> interface and implement the<code>java.rmi.Remote</code> interface, expose the service as this interface,</li>\n<li>But if <code>&lt;dubbo: protocol name = \'rmi\' codec = \'spring\' /&gt;</code>is set, the<code>$Remote</code> interface will not be generated, but Spring\'s <code>RmiInvocationHandler</code> interface will be used to expose services.</li>\n</ul>\n<h2>Configuration</h2>\n<p>configure RMI protocol:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rmi"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"1099"</span> /&gt;</span>\n</code></pre>\n<p>configure provider level default protocol:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"rmi"</span> /&gt;</span>\n</code></pre>\n<p>configure service level default protocol:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"rmi"</span> /&gt;</span>\n</code></pre>\n<p>configure multiple port:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"rmi1"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rmi"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"1099"</span> /&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"rmi2"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rmi"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"2099"</span> /&gt;</span>\n \n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"rmi1"</span> /&gt;</span>\n</code></pre>\n<p>Compatible with Spring:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"rmi"</span> <span class="hljs-attr">codec</span>=<span class="hljs-string">"spring"</span> /&gt;</span>\n</code></pre>\n'},{filename:"user/references/protocol/thrift.md",__html:'<h1>thrift://</h1>\n<p>The current dubbo support thrift protocol is an extension of the thrift native protocol, adding some additional header information to the native protocol, such as service name, magic number, and so on.</p>\n<p>The use of dubbo thrift protocol also need to use thrift idl compiler to generate the corresponding java code, follow-up version will do some enhancement in this aspect.</p>\n<h2>dependency</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.apache.thrift<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>libthrift<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>0.8.0<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>\n</code></pre>\n<h2>Configuration</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"thrift"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"3030"</span> /&gt;</span>\n</code></pre>\n<h2>Example</h2>\n<p>you can check <a href="https://github.com/apache/incubator-dubbo/tree/master/dubbo-rpc/dubbo-rpc-thrift/src/test/java/com/alibaba/dubbo/rpc/protocol/thrift/examples">dubbo thrift example</a></p>\n<h2>Common problem</h2>\n<ul>\n<li>Thrift does not support null values, that is, you can not pass null values</li>\n</ul>\n'},{filename:"user/references/protocol/webservice.md",__html:'<h1>webservice://</h1>\n<p>WebService-based remote calling protocol,base on <a href="http://cxf.apache.org">Apache CXF</a>  <code>frontend-simple</code> and  <code>transports-http</code> implements。</p>\n<p>Interoperable with native WebService services:</p>\n<ul>\n<li>Providers expose services using Dubbo\'s WebService protocol, which consumers invoke directly using the standard WebService interface,</li>\n<li>Or the provider exposes the service using the standard WebService, which consumers invoke using the Dubbo WebService protocol.</li>\n</ul>\n<h2>dependency</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.apache.cxf<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>cxf-rt-frontend-simple<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>2.6.1<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.apache.cxf<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>cxf-rt-transports-http<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>2.6.1<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>\n</code></pre>\n<h2>Features</h2>\n<ul>\n<li>Number of connections: multiple connections</li>\n<li>Connection: short connection</li>\n<li>Transmission protocol: HTTP</li>\n<li>Transmission: synchronous transmission</li>\n<li>Serialization: SOAP text serialization</li>\n<li>Applicable scenarios: System integration, cross-language calls</li>\n</ul>\n<h2>Constraint</h2>\n<ul>\n<li>Parameters and return class should implement <code>Serializable</code> interface</li>\n<li>Parameters should try to use the basic types and POJO</li>\n</ul>\n<h2>Configuration</h2>\n<p>configure webservice protocol:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"webservice"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"8080"</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"jetty"</span> /&gt;</span>\n</code></pre>\n<p>configure provider level default protocol:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:provider</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"webservice"</span> /&gt;</span>\n</code></pre>\n<p>configure service level default protocol:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:service</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"webservice"</span> /&gt;</span>\n</code></pre>\n<p>configure multiple port:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"webservice1"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"webservice"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"8080"</span> /&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"webservice2"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"webservice"</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"8081"</span> /&gt;</span>\n</code></pre>\n<p>configure direct connect mode:</p>\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">"helloService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"HelloWorld"</span> <span class="hljs-attr">url</span>=<span class="hljs-string">"webservice://10.20.153.10:8080/com.foo.HelloWorld"</span> /&gt;</span>\n</code></pre>\n<p>WSDL:</p>\n<pre><code>http://10.20.153.10:8080/com.foo.HelloWorld?wsdl\n</code></pre>\n<p>Jetty Server (Default):</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">...</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"jetty"</span> /&gt;</span>\n</code></pre>\n<p>Servlet Bridge Server (recommend):</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">...</span> <span class="hljs-attr">server</span>=<span class="hljs-string">"servlet"</span> /&gt;</span>\n</code></pre>\n<p>configure DispatcherServlet:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">servlet</span>&gt;</span>\n         <span class="hljs-tag">&lt;<span class="hljs-name">servlet-name</span>&gt;</span>dubbo<span class="hljs-tag">&lt;/<span class="hljs-name">servlet-name</span>&gt;</span>\n         <span class="hljs-tag">&lt;<span class="hljs-name">servlet-class</span>&gt;</span>com.alibaba.dubbo.remoting.http.servlet.DispatcherServlet<span class="hljs-tag">&lt;/<span class="hljs-name">servlet-class</span>&gt;</span>\n         <span class="hljs-tag">&lt;<span class="hljs-name">load-on-startup</span>&gt;</span>1<span class="hljs-tag">&lt;/<span class="hljs-name">load-on-startup</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">servlet</span>&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">servlet-mapping</span>&gt;</span>\n         <span class="hljs-tag">&lt;<span class="hljs-name">servlet-name</span>&gt;</span>dubbo<span class="hljs-tag">&lt;/<span class="hljs-name">servlet-name</span>&gt;</span>\n         <span class="hljs-tag">&lt;<span class="hljs-name">url-pattern</span>&gt;</span>/*<span class="hljs-tag">&lt;/<span class="hljs-name">url-pattern</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">servlet-mapping</span>&gt;</span>\n</code></pre>\n<p>Note that if you use servlets to dispatch requests:</p>\n<ul>\n<li>the port of protocol <code>&lt;dubbo:protocol port=&quot;8080&quot; /&gt;</code> must same as  servlet container\'s.</li>\n<li>the context path of protocol <code>&lt;dubbo:protocol contextpath=&quot;foo&quot; /&gt;</code> must same as servlet application\'s.</li>\n</ul>\n'},{filename:"user/references/registry/introduction.md",__html:'<h2>Registry Server References</h2>\n<p>It is recommended to use <a href="./zookeeper.md">zookeeper registry server</a></p>\n'},{filename:"user/references/registry/multicast.md",__html:'<h1>Multicast Registry</h1>\n<p>Multicast registry doesn\'t require to setup any central node. Just like IP address broadcast, dubbo service providers and consumers can discover each other through this mechanism.</p>\n<p><img src="../../sources/images/multicast.jpg" alt="/user-guide/images/multicast.jpg"></p>\n<ol start="0">\n<li>Service provider broadcasts its address when it boots up.</li>\n<li>Service consumer broadcasts its subscription request when it boots up.</li>\n<li>Once service provider receives subscription request, it unicasts its own address to the corresponding consumer, if <code>unicast=false</code> is set, then broadcast will be used instead.</li>\n<li>When service consumer receives provider\'s address, it can start RPC invocation on the received address.</li>\n</ol>\n<p>Multicast is limited to network topology, and is only suitable for development purpose or small deployment. The valid multcast addresses scope is: 224.0.0.0 - 239.255.255.255.</p>\n<h2>Configuration</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"multicast://224.5.6.7:1234"</span> /&gt;</span>\n</code></pre>\n<p>Or</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"multicast"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"224.5.6.7:1234"</span> /&gt;</span>\n</code></pre>\n<p>In order to avoid multicast as much as possible, dubbo uses unicast for address information from service provider to service consumer, if there are multiple consumer processes on one single machine, consumers need to set <code>unicast=false</code>, otherwise only one consumer can be able to receive the address info:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"multicast://224.5.6.7:1234?unicast=false"</span> /&gt;</span>\n</code></pre>\n<p>Or</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"multicast"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"224.5.6.7:1234"</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:parameter</span> <span class="hljs-attr">key</span>=<span class="hljs-string">"unicast"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"false"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:registry</span>&gt;</span>\n</code></pre>\n'},{filename:"user/references/registry/redis.md",__html:'<h1>Redis Registry Server</h1>\n<p>It is a registry server implementation <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> based on redis <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>.</p>\n<p><img src="../../sources/images/dubbo-redis-registry.jpg" alt="/user-guide/images/dubbo-redis-registry.jpg"></p>\n<p>Use key/map structure in redis to save the registration info:</p>\n<ul>\n<li>Main key for service name and type</li>\n<li>Key in the map is URL address</li>\n<li>Value in the map is the expiration time. Monitor center uses it to track and remove dirty data <sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup></li>\n</ul>\n<p>Publish/Subscribe events in redis is leveraged for data change notification:</p>\n<ul>\n<li>Distinguish event type with event\'s value: <code>register</code>, <code>unregister</code>, <code>subscribe</code>, <code>unsubscribe</code>.</li>\n<li>Regular subscriber subscribes the particular key presenting service provider, then will receive <code>register</code> and <code>unregister</code> events fired from the specified service.</li>\n<li>Monitor center subscribes <code>/dubbo/*</code> via <code>psubscribe</code>, then will receive all change notifications from all services.</li>\n</ul>\n<p>Procedure:</p>\n<ol start="0">\n<li>When service provider boots up, it adds its address under <code>Key:/dubbo/com.foo.BarService/providers</code>.</li>\n<li>Then service provider sends <code>register</code> event to <code>Channel:/dubbo/com.foo.BarService/providers</code></li>\n<li>When service consumer boots up, it subscribe events <code>register</code> and <code>unregister</code> from <code>Channel:/dubbo/com.foo.BarService/providers</code></li>\n<li>Then service consumer add its address under <code>Key:/dubbo/com.foo.BarService/providers</code></li>\n<li>When service consumer receives events <code>register</code> and <code>unregister</code>, it will fetch provider\'s addresses from <code>Key:/dubbo/com.foo.BarService/providers</code></li>\n<li>When monitor center boots up, it subscribes events <code>register</code>, <code>unregister</code>, <code>subscribe</code>, and <code>unsubsribe</code>.</li>\n<li>After monitor center receives <code>register</code> and <code>unregister</code>, it fetches provider\'s addresses from <code>Key:/dubbo/com.foo.BarService/providers</code></li>\n<li>After monitor center receives <code>subscribe</code> and <code>unsubscribe</code>, it fetches consumer\'s addresses from <code>Key:/dubbo/com.foo.BarService/consumers</code></li>\n</ol>\n<h2>Configuration</h2>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"redis://10.20.153.10:6379"</span> /&gt;</span>\n</code></pre>\n<p>Or</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"redis://10.20.153.10:6379?backup=10.20.153.11:6379,10.20.153.12:6379"</span> /&gt;</span>\n</code></pre>\n<p>Or</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"redis"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.153.10:6379"</span> /&gt;</span>\n</code></pre>\n<p>Or</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"redis"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.153.10:6379,10.20.153.11:6379,10.20.153.12:6379"</span> /&gt;</span>\n</code></pre>\n<h2>Options</h2>\n<ul>\n<li>Config key\'s prefix in redis via <code>&lt;dubbo:registry group=&quot;dubbo&quot; /&gt;</code>, the default value is <code>dubbo</code>.</li>\n<li>Config redis cluster strategy via <code>&lt;dubbo:registry cluster=&quot;replicate&quot; /&gt;</code>, the default value is <code>failover</code>:\n<ul>\n<li><code>failover</code>: when read/write error happens, try another instance, require the cluster to support data replication.</li>\n<li><code>replicate</code>: client writes to all nodes of the cluster, but only peeks a random node for read. The cluster doesn\'t need to take care of data replication, but may require more nodes and higher performance for each node, compared to option 1.</li>\n</ul>\n</li>\n</ul>\n<h2>Declaration of Reliability</h2>\n<p>A home-brewed service registry server is used in Alibaba instead of redis server. Redis based registry center does not have long-run practice within Alibaba, therefore we cannot guarantee its reliability. This registry server implementation is provided for dubbo community, and its reliability relies on redis itself largely.</p>\n<h2>Installation</h2>\n<p>Pls. refer to <a href="http://dubbo.apache.org/books/dubbo-admin-book/install/redis.html">redis install manual</a> for how to install a redis based registry server. To set it up, specify <code>dubbo.registry.addrss</code> to <code>redis://127.0.0.1:6379</code> in <code>conf/dubbo.properties</code> for both provider and consumer (you can refer to <a href="../../preface/usage.md">quick start</a>) after install a redis server.</p>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>Support since <code>2.1.0</code> <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p><a href="http://redis.io">Redis</a> is a high performance KV cache server <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn3" class="footnote-item"><p>Heartbeat mechanism is used to detect the dirty data in redis. It requires time among servers must be sync in advanced, otherwise expiration check may inaccurate, plus, heartbeats may add extra pressure on servers. <a href="#fnref3" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/references/registry/simple.md",__html:'<h1>Simple Registry Server</h1>\n<p>Simple registry server itself is a regular dubbo service. In this way, third-party dependency is unnecessary, and communication keeps consistent at the same moment.</p>\n<h2>Configuration</h2>\n<p>Register simple registry server as dubbo service:</p>\n<pre><code class="language-xml"><span class="php"><span class="hljs-meta">&lt;?</span>xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"UTF-8"</span><span class="hljs-meta">?&gt;</span></span>\n<span class="hljs-tag">&lt;<span class="hljs-name">beans</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans"</span>\n    <span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>\n    <span class="hljs-attr">xmlns:dubbo</span>=<span class="hljs-string">"http://dubbo.apache.org/schema/dubbo"</span>\n    <span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"</span>&gt;</span>\n    <span class="hljs-comment">&lt;!-- application info configuration --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:application</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"simple-registry"</span> /&gt;</span>\n    <span class="hljs-comment">&lt;!-- service protocol configuration --&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"9090"</span> /&gt;</span>\n    <span class="hljs-comment">&lt;!-- service configuration --&gt;</span>\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.registry.RegistryService"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"registryService"</span> <span class="hljs-attr">registry</span>=<span class="hljs-string">"N/A"</span> <span class="hljs-attr">ondisconnect</span>=<span class="hljs-string">"disconnect"</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">"subscribe"</span>&gt;</span><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><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:method</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"unsubscribe"</span>&gt;</span><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">"false"</span> /&gt;</span><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    <span class="hljs-comment">&lt;!-- simple registry server implementation, register other implementation if cluster ability is a requirement--&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">bean</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"registryService"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"com.alibaba.dubbo.registry.simple.SimpleRegistryService"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">beans</span>&gt;</span>\n</code></pre>\n<p>Reference simple registry server service:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"127.0.0.1:9090"</span> /&gt;</span>\n</code></pre>\n<p>Or:</p>\n<pre><code class="language-xml"><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.registry.RegistryService"</span> <span class="hljs-attr">group</span>=<span class="hljs-string">"simple"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0.0"</span> <span class="hljs-attr">...</span> &gt;</span>\n</code></pre>\n<p>Or:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"127.0.0.1:9090"</span> <span class="hljs-attr">group</span>=<span class="hljs-string">"simple"</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0.0"</span> /&gt;</span>\n</code></pre>\n<h2>Applicability</h2>\n<p>This <code>SimpleRegistryService</code> is just a simple implementation for register server, and it doesn\'t have cluster support. It is useful for the implementation reference for the custom registery server, but not suitable for use in production environment directly.</p>\n'},{filename:"user/references/registry/zookeeper.md",__html:'<h1>Zookeeper Registry Server</h1>\n<p><a href="http://zookeeper.apache.org">Zookeeper</a> is the child project of apache hadoop. Since it offers tree-like directory service and supports change notification, it\'s suitable to use it as dubbo\'s registry server. It\'s a field-proven product, therefore it\'s recommended to use it in the production environment. <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup></p>\n<p><img src="../../sources/images/zookeeper.jpg" alt="/user-guide/images/zookeeper.jpg"></p>\n<p>Description on registration procedure:</p>\n<ul>\n<li>When service provider boots up: write service URL address under directory <code>/dubbo/com.foo.BarService/providers</code></li>\n<li>When service consumer boots up: subscribe to <code>/dubbo/com.foo.BarService/providers</code> for provider\'s URL addresses. At the same time, write consumer\'s URL address under <code>/dubbo/com.foo.BarService/providers</code>.</li>\n<li>When monitor center boots up: subscribe to <code>/dubbo/com.foo.BarService</code> for the URL addresses from all providers and consumers.</li>\n</ul>\n<p>The following abilities are supported:</p>\n<ul>\n<li>When provider stops by accident, registry server can remove its info automatically.</li>\n<li>When registry server reboots, all registration data and subscription requests can be recovered automatically.</li>\n<li>When session is expired, all registration data and subscription requests can be recovered automatically.</li>\n<li>When <code>&lt;dubbo:registry check=&quot;false&quot; /&gt;</code> is configured, failed requests for subscription and registration will be recorded and kept retrying in the background.</li>\n<li>Configure <code>&lt;dubbo:registry username=&quot;admin&quot; password=&quot;1234&quot; /&gt;</code> for zookeeper login.</li>\n<li>Configure <code>&lt;dubbo:registry group=&quot;dubbo&quot; /&gt;</code> for dubbo\'s root node on zookeeper. Default root node will be used if it\'s not specified.</li>\n<li>Support to use wildcard <code>*</code> in <code>&lt;dubbo:reference group=&quot;*&quot; version=&quot;*&quot; /&gt;</code> in order to subscribe all groups and all versions for the services to be referenced.</li>\n</ul>\n<h2>How to Use</h2>\n<p>Add zookeeper client dependency in both provider and consumer:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.apache.zookeeper<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>zookeeper<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>3.3.3<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>\n</code></pre>\n<p>Or <a href="http://repo1.maven.org/maven2/org/apache/zookeeper/zookeeper">download</a> directly from apache.</p>\n<p>Dubbo supports two zookeeper clients: zkclient and curator:</p>\n<h3>Use zkclient</h3>\n<p>Since <code>2.2.0</code> dubbo uses zkclient by default, in order to improve the robustness. <a href="https://github.com/sgroschupf/zkclient">zkclient</a> is a zookeeper client implementation open-sourced by Datameer.</p>\n<p>Default configuration:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">...</span> <span class="hljs-attr">client</span>=<span class="hljs-string">"zkclient"</span> /&gt;</span>\n</code></pre>\n<p>Or:</p>\n<pre><code class="language-sh">dubbo.registry.client=zkclient\n</code></pre>\n<p>Or:</p>\n<pre><code class="language-sh">zookeeper://10.20.153.10:2181?client=zkclient\n</code></pre>\n<p>In order to use it, need to explicitly declare the following maven dependency or <a href="http://repo1.maven.org/maven2/com/github/sgroschupf/zkclient">download its client</a>.</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.github.sgroschupf<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>zkclient<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>0.1<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>\n</code></pre>\n<h3>Use curator</h3>\n<p>Since <code>2.3.0</code> dubbo also supports curator but explicit configuration is required. <a href="https://github.com/Netflix/curator">Curator</a> is the zookeeper client open-sourced by Netflix.</p>\n<p>In order to switch to curator, use the configuration below:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">...</span> <span class="hljs-attr">client</span>=<span class="hljs-string">"curator"</span> /&gt;</span>\n</code></pre>\n<p>Or:</p>\n<pre><code class="language-sh">dubbo.registry.client=curator\n</code></pre>\n<p>Or:</p>\n<pre><code class="language-sh">zookeeper://10.20.153.10:2181?client=curator\n</code></pre>\n<p>Also need to explicitly add maven dependency or directly <a href="http://repo1.maven.org/maven2/com/netflix/curator/curator-framework">download</a> the jar:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.netflix.curator<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>curator-framework<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>1.1.10<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>\n</code></pre>\n<p>Zookeeper single node configuration:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"zookeeper://10.20.153.10:2181"</span> /&gt;</span>\n</code></pre>\n<p>Or:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"zookeeper"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.153.10:2181"</span> /&gt;</span>\n</code></pre>\n<p>Zookeeper cluster configuration:</p>\n<pre><code class="language-xml">\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"zookeeper://10.20.153.10:2181?backup=10.20.153.11:2181,10.20.153.12:2181"</span> /&gt;</span>\n</code></pre>\n<p>Or:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"zookeeper"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.153.10:2181,10.20.153.11:2181,10.20.153.12:2181"</span> /&gt;</span>\n</code></pre>\n<p>Configure single zookeeper to serve as multiple registry servers:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"chinaRegistry"</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"zookeeper"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.153.10:2181"</span> <span class="hljs-attr">group</span>=<span class="hljs-string">"china"</span> /&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"intlRegistry"</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"zookeeper"</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"10.20.153.10:2181"</span> <span class="hljs-attr">group</span>=<span class="hljs-string">"intl"</span> /&gt;</span>\n</code></pre>\n<h2>Zookeeper Installation</h2>\n<p>Pls. refer to <a href="http://dubbo.apache.org/books/dubbo-admin-book/install/zookeeper.html">zookeeper install manual</a> for how to install zookeeper based registry server. To set it up, specify <code>dubbo.registry.address</code> to <code>zookeeper://127.0.0.1:2181</code> in <code>conf/dubbo.properties</code> for both provider and consumer (you can refer to <a href="../../preface/usage.md">quick start</a>) after install a zookeeper server.</p>\n<h2>Declaration of Reliability</h2>\n<p>A home-brewed service registry server is used in Alibaba instead of zookeeper server. Zookeeper based registry center does not have long-run practice within Alibaba, therefore we cannot guarantee its reliability. Zookeeper registry server is provided for dubbo community, and its reliability relies on zookeeper itself largely.</p>\n<h2>Declaration of Compatibility</h2>\n<p>The original designed data structure for zookeeper in <code>2.0.8</code> has the limitation that data type cannot extended, it\'s redesigned in <code>2.0.9</code>. But at the same time incompatibility is introduced, thereby <code>2.0.9</code> is required for all service providers and service consumers.</p>\n<p>Since <code>2.2.0</code> zkclient is used by default, therefore its dependency is needed.</p>\n<p>Since <code>2.3.0</code> curator is supported as alternative option.</p>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>Suggest to use <code>2.3.3</code> or above for zookeeper registry client <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/references/telnet.md",__html:'<h1>Telnet Command Reference</h1>\n<p>Since <code>2.0.5</code> dubbo starts supporting to use telnet command to govern services.</p>\n<h2>How To Use</h2>\n<pre><code class="language-sh">telnet localhost 20880\n</code></pre>\n<p>Or:</p>\n<pre><code class="language-sh"><span class="hljs-built_in">echo</span> status | nc -i 1 localhost 20880\n</code></pre>\n<p>It is possible to extend command <code>status</code> to check more resources, pls. refer to <a href="http://dubbo.apache.org/books/dubbo-dev-book-en/impls/status-checker.html">extension references</a> for more details.</p>\n<h2>Supported Commands</h2>\n<p>The built-in telnet commands are listed below. Furthermore, it is possible to extend telnet commands, pls. refer to\n<a href="http://dubbo.apache.org/books/dubbo-dev-book/impls/telnet-handler.html">extend telnet command</a> for more details.</p>\n<h3><code>ls</code></h3>\n<ol start="0">\n<li><code>ls</code>: list services</li>\n<li><code>ls -l</code>: list services in more details</li>\n<li><code>ls XxxService</code>: list methods for the particular service</li>\n<li><code>ls -l XxxService</code>: list methods for the particular service in more dtails</li>\n</ol>\n<h3><code>ps</code></h3>\n<ol start="0">\n<li><code>ps</code>: list service ports</li>\n<li><code>ps -l</code>: list service addresses</li>\n<li><code>ps 20880</code>: show connection info for the particular port</li>\n<li><code>ps -l 20880</code>: show connection info for the particular port in more details</li>\n</ol>\n<h3><code>cd</code></h3>\n<ol start="0">\n<li><code>cd XxxService</code>: switch default service. When default service is set, service parameter can be ignored in all commands when it\'s needed</li>\n<li><code>cd /</code>: reset default service</li>\n</ol>\n<h3><code>pwd</code></h3>\n<p><code>pwd</code>: show what current default service is</p>\n<h3><code>trace</code></h3>\n<ol start="0">\n<li><code>trace XxxService</code>: trace method invocation once for the given service</li>\n<li><code>trace XxxService 10</code>: trace method invocations 10 times for the given service</li>\n<li><code>trace XxxService xxxMethod</code>: trace particular method invocation once for the given service</li>\n<li><code>trace XxxService xxxMethod 10</code>: trace particular method invocations 10 times for the given service</li>\n</ol>\n<h3><code>count</code></h3>\n<ol start="0">\n<li><code>count XxxService</code>: count method invocation once for the given service</li>\n<li><code>count XxxService 10</code>: count method invocations 10 times for the given service</li>\n<li><code>count XxxService xxxMethod</code>: count particular method invocation once for the given service</li>\n<li><code>count XxxService xxxMethod 10</code>: count particular method invocation 10 times for the given service</li>\n</ol>\n<h3><code>invoke</code></h3>\n<ol start="0">\n<li><code>invoke XxxService.xxxMethod({&quot;prop&quot;: &quot;value&quot;})</code>: invoke particular method for the given service</li>\n<li><code>invoke xxxMethod({&quot;prop&quot;: &quot;value&quot;})</code>: invoke particular method for the default service</li>\n</ol>\n<h3><code>status</code></h3>\n<ol start="0">\n<li><code>status</code>: show summarized status. This status summarizes statuses from all resources, and it shows OK when all resources are OK, shows ERROR when any resource has ERROR, and WARN when any has WARN.</li>\n<li><code>status -l</code>: show status list</li>\n</ol>\n<h3><code>log</code> <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup></h3>\n<ol start="0">\n<li><code>log debug</code>: modify logger level to debug</li>\n<li><code>log 100</code>: examine the last 100 characters from the file logger</li>\n</ol>\n<h3><code>help</code></h3>\n<ol start="0">\n<li><code>help</code>: show help for telnet commands</li>\n<li><code>help xxx</code>: 显示xxx命令的详细帮助信息</li>\n<li><code>help xxx</code>: show help for particular telnet command</li>\n</ol>\n<h3><code>clear</code></h3>\n<ol start="0">\n<li><code>clear</code>: clear screen</li>\n<li><code>clear 100</code>: only clear particular lines on the screen</li>\n</ol>\n<h3><code>exit</code></h3>\n<p><code>exit</code>: exit current telnet session</p>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>support since <code>2.0.6</code> <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/references/xml/dubbo-application.md",__html:'<style>\ntable {\n  width: 100%;\n  max-width: 65em;\n  border: 1px solid #dedede;\n  margin: 15px auto;\n  border-collapse: collapse;\n  empty-cells: show;\n}\ntable th,\ntable td {\n  height: 35px;\n  border: 1px solid #dedede;\n  padding: 0 10px;\n}\ntable th {\n  font-weight: bold;\n  text-align: center !important;\n  background: rgba(158,188,226,0.2);\n  white-space: nowrap;\n}\ntable tbody tr:nth-child(2n) {\n  background: rgba(158,188,226,0.12);\n}\ntable td:nth-child(1) {\n  white-space: nowrap;\n}\ntable tr:hover {\n  background: #efefef;\n}\n.table-area {\n  overflow: auto;\n}\n</style>\n<script type="text/javascript">\n[].slice.call(document.querySelectorAll(\'table\')).forEach(function(el){\n    var wrapper = document.createElement(\'div\');\n    wrapper.className = \'table-area\';\n    el.parentNode.insertBefore(wrapper, el);\n    el.parentNode.removeChild(el);\n    wrapper.appendChild(el);\n})\n<\/script>\n<h1>dubbo:application</h1>\n<p>Application configuration. The corresponding class: <code>com.alibaba.dubbo.config.ApplicationConfig</code></p>\n<table>\n<thead>\n<tr>\n<th>Property</th>\n<th>Corresponding URL parameter</th>\n<th>Type</th>\n<th>Requisite</th>\n<th>Default</th>\n<th>Effect</th>\n<th>Description</th>\n<th>Compatibility</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>name</td>\n<td>application</td>\n<td>string</td>\n<td><b>Y</b></td>\n<td></td>\n<td>service governance</td>\n<td>Application name is the unique identifier of an application. It is for registry combing the dependencies of applications. Note: Consumer and provider application name should not be the same, and this parameter is not a match condition. As a suggestion, you can name it as your project name. For example, kylin application invokes the service of morgan application, then you can name kylin application as &quot;kylin&quot;, and morgan application as &quot;morgan&quot;. Maybe kylin also works as a provider, but kylin should still called &quot;kylin&quot;. In this way, registry can understand the dependence of applications</td>\n<td>above 1.0.16</td>\n</tr>\n<tr>\n<td>version</td>\n<td>application.version</td>\n<td>string</td>\n<td>N</td>\n<td></td>\n<td>service governance</td>\n<td>The version of current application</td>\n<td>above 2.2.0</td>\n</tr>\n<tr>\n<td>owner</td>\n<td>owner</td>\n<td>string</td>\n<td>N</td>\n<td></td>\n<td>service governance</td>\n<td>Application manager. Pls. fill in the mailbox prefix of the person in charge</td>\n<td>above 2.0.5</td>\n</tr>\n<tr>\n<td>organization</td>\n<td>organization</td>\n<td>string</td>\n<td>N</td>\n<td></td>\n<td>service governance</td>\n<td>Organization name is for registry distinguishing between the source of service. As a suggestion, this property should be written in config file directly. Such as china,intl,itu,crm,asc,dw,aliexpress etc.</td>\n<td>above 2.0.0</td>\n</tr>\n<tr>\n<td>architecture <br class="atl-forced-newline" /></td>\n<td>architecture <br class="atl-forced-newline" /></td>\n<td>string</td>\n<td>N</td>\n<td></td>\n<td>service governance</td>\n<td>The architecture of service layering. Like intl,china and so on. Different architecture use different layer</td>\n<td>above 2.0.7</td>\n</tr>\n<tr>\n<td>environment</td>\n<td>environment</td>\n<td>string</td>\n<td>N</td>\n<td></td>\n<td>service governance</td>\n<td>Application environment. Like develop,test,product. Work as the limit condition of developing new function</td>\n<td>above 2.0.0</td>\n</tr>\n<tr>\n<td>compiler</td>\n<td>compiler</td>\n<td>string</td>\n<td>N</td>\n<td>javassist</td>\n<td>performance optimization</td>\n<td>Java class <a href="http://compile.It">compile.It</a> is used for the generating of dynamic class. The options are JDK and javassist</td>\n<td>above 2.1.0</td>\n</tr>\n<tr>\n<td>logger</td>\n<td>logger</td>\n<td>string</td>\n<td>N</td>\n<td>slf4j</td>\n<td>performance optimization</td>\n<td>The format of log output,The options are slf4j,jcl,log4j and jdk</td>\n<td>above 2.2.0</td>\n</tr>\n</tbody>\n</table>\n'},{filename:"user/references/xml/dubbo-argument.md",__html:'<style>\ntable {\n  width: 100%;\n  max-width: 65em;\n  border: 1px solid #dedede;\n  margin: 15px auto;\n  border-collapse: collapse;\n  empty-cells: show;\n}\ntable th,\ntable td {\n  height: 35px;\n  border: 1px solid #dedede;\n  padding: 0 10px;\n}\ntable th {\n  font-weight: bold;\n  text-align: center !important;\n  background: rgba(158,188,226,0.2);\n  white-space: nowrap;\n}\ntable tbody tr:nth-child(2n) {\n  background: rgba(158,188,226,0.12);\n}\ntable td:nth-child(1) {\n  white-space: nowrap;\n}\ntable tr:hover {\n  background: #efefef;\n}\n.table-area {\n  overflow: auto;\n}\n</style>\n<script type="text/javascript">\n[].slice.call(document.querySelectorAll(\'table\')).forEach(function(el){\n    var wrapper = document.createElement(\'div\');\n    wrapper.className = \'table-area\';\n    el.parentNode.insertBefore(wrapper, el);\n    el.parentNode.removeChild(el);\n    wrapper.appendChild(el);\n})\n<\/script>\n<h1>dubbo:argument</h1>\n<p>Method argument configuration. The corresponding class:<code>com.alibaba.dubbo.config.ArgumentConfig</code>. This tag is child of <code>&lt;dubbo:method&gt;</code>, which is for feature description of method argument, such as:</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">"findXxx"</span> <span class="hljs-attr">timeout</span>=<span class="hljs-string">"3000"</span> <span class="hljs-attr">retries</span>=<span class="hljs-string">"2"</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">"0"</span> <span class="hljs-attr">callback</span>=<span class="hljs-string">"true"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:method</span>&gt;</span>\n</code></pre>\n<table>\n<thead>\n<tr>\n<th>Property</th>\n<th>Corresponding URL parameter</th>\n<th>Type</th>\n<th>Requisite</th>\n<th>Default</th>\n<th>Effect</th>\n<th>Description</th>\n<th>Compatibility</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>index</td>\n<td></td>\n<td>int</td>\n<td><b>Y</b></td>\n<td></td>\n<td>identification</td>\n<td>method name</td>\n<td>above 2.0.6</td>\n</tr>\n<tr>\n<td>type</td>\n<td></td>\n<td>String</td>\n<td>Index and type choose one</td>\n<td></td>\n<td>identification</td>\n<td>Find index of argument by it</td>\n<td>above 2.0.6</td>\n</tr>\n<tr>\n<td>callback</td>\n<td>&lt;metodName&gt;&lt;index&gt;.retries</td>\n<td>boolean</td>\n<td>N</td>\n<td></td>\n<td>service governance</td>\n<td>Mark whether this argument is a callback service. If true, provider will generate reverse proxy,which can invoke consumer in turn. Generally for event pushing</td>\n<td>above 2.0.6</td>\n</tr>\n</tbody>\n</table>\n'},{filename:"user/references/xml/dubbo-consumer.md",__html:"<style>\ntable {\n  width: 100%;\n  max-width: 65em;\n  border: 1px solid #dedede;\n  margin: 15px auto;\n  border-collapse: collapse;\n  empty-cells: show;\n}\ntable th,\ntable td {\n  height: 35px;\n  border: 1px solid #dedede;\n  padding: 0 10px;\n}\ntable th {\n  font-weight: bold;\n  text-align: center !important;\n  background: rgba(158,188,226,0.2);\n  white-space: nowrap;\n}\ntable tbody tr:nth-child(2n) {\n  background: rgba(158,188,226,0.12);\n}\ntable td:nth-child(1) {\n  white-space: nowrap;\n}\ntable tr:hover {\n  background: #efefef;\n}\n.table-area {\n  overflow: auto;\n}\n</style>\n<script type=\"text/javascript\">\n[].slice.call(document.querySelectorAll('table')).forEach(function(el){\n    var wrapper = document.createElement('div');\n    wrapper.className = 'table-area';\n    el.parentNode.insertBefore(wrapper, el);\n    el.parentNode.removeChild(el);\n    wrapper.appendChild(el);\n})\n<\/script>\n<h1>dubbo:consumer</h1>\n<p>Consumer default configuration. The corresponding clas: <code>com.alibaba.dubbo.config.ConsumerConfig</code>. It is also default configuration of <code>&lt;dubbo:reference&gt;</code>.</p>\n<table>\n<thead>\n<tr>\n<th>Property</th>\n<th>Corresponding URL parameter</th>\n<th>Type</th>\n<th>Requisite</th>\n<th>Default</th>\n<th>Effect</th>\n<th>Description</th>\n<th>Compatibility</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>timeout</td>\n<td>default.timeout</td>\n<td>int</td>\n<td>N</td>\n<td>1000</td>\n<td>performance optimization</td>\n<td>invoking timeout(ms)</td>\n<td>above 1.0.16</td>\n</tr>\n<tr>\n<td>retries</td>\n<td>default.retries</td>\n<td>int</td>\n<td>N</td>\n<td>2</td>\n<td>performance optimization</td>\n<td>Invoking retry times, exclude the first invoking. Set 0 to disable it</td>\n<td>above 1.0.16</td>\n</tr>\n<tr>\n<td>loadbalance</td>\n<td>default.loadbalance</td>\n<td>string</td>\n<td>N</td>\n<td>random</td>\n<td>performance optimization</td>\n<td>Load balancing strategy. Choices:random, roundrobin(polling), leastactive(invoking least active service)</td>\n<td>above 1.0.16</td>\n</tr>\n<tr>\n<td>async</td>\n<td>default.async</td>\n<td>boolean</td>\n<td>N</td>\n<td>false</td>\n<td>performance optimization</td>\n<td>Whether invoke asynchronously</td>\n<td>above 2.0.0</td>\n</tr>\n<tr>\n<td>connections</td>\n<td>default.connections</td>\n<td>int</td>\n<td>N</td>\n<td>100</td>\n<td>performance optimization</td>\n<td>The maximum number of connections of per service provider. Only short link protocol such as rmi,http,hessian etc. supports. Long link protocol such as dubbo doesn't support</td>\n<td>above 1.0.16</td>\n</tr>\n<tr>\n<td>generic</td>\n<td>generic</td>\n<td>boolean</td>\n<td>N</td>\n<td>false</td>\n<td>service governance</td>\n<td>Whether default generic interface. A instance of GenericService will be got if true.</td>\n<td>above 2.0.0</td>\n</tr>\n<tr>\n<td>check</td>\n<td>check</td>\n<td>boolean</td>\n<td>N</td>\n<td>true</td>\n<td>service governance</td>\n<td>Whether check the survival of provider. If true, throw exception when no provider of some services is alive. Otherwise, just ignore it</td>\n<td>above 1.0.16</td>\n</tr>\n<tr>\n<td>proxy</td>\n<td>proxy</td>\n<td>string</td>\n<td>N</td>\n<td>javassist</td>\n<td>performance optimization</td>\n<td>Java class <a href=\"http://compile.It\">compile.It</a> is used for the generating of dynamic class. The options are JDK and javassist</td>\n<td>above 2.0.5</td>\n</tr>\n<tr>\n<td>owner</td>\n<td>owner</td>\n<td>string</td>\n<td>N</td>\n<td></td>\n<td>service governance</td>\n<td>Application manager. Pls. fill in the mailbox prefix of the person in charge</td>\n<td>above 2.0.5</td>\n</tr>\n<tr>\n<td>actives</td>\n<td>default.actives</td>\n<td>int</td>\n<td>N</td>\n<td>0</td>\n<td>performance optimization</td>\n<td>The max concurrency of per service method for each corresponding consumer</td>\n<td>above 2.0.5</td>\n</tr>\n<tr>\n<td>cluster</td>\n<td>default.cluster</td>\n<td>string</td>\n<td>N</td>\n<td>failover</td>\n<td>performance optimization</td>\n<td>Cluster tolerance. Choices:failover/failfast/failsafe/failback/forking</td>\n<td>above 2.0.5</td>\n</tr>\n<tr>\n<td>filter</td>\n<td>reference.filter</td>\n<td>string</td>\n<td>N</td>\n<td></td>\n<td>performance optimization</td>\n<td>The name of filter which intercepts consumer remote invoke. Multiple names are separated by commas</td>\n<td>above 2.0.5</td>\n</tr>\n<tr>\n<td>listener</td>\n<td>invoker.listener</td>\n<td>string</td>\n<td>N</td>\n<td></td>\n<td>performance optimization</td>\n<td>The consumer referenced service listener name. Multiple names are separated by commas</td>\n<td>above 2.0.5</td>\n</tr>\n<tr>\n<td>registry</td>\n<td></td>\n<td>string</td>\n<td>N</td>\n<td>register with the registry</td>\n<td>configuration relevant</td>\n<td>Register with the designated registry. Generally,for multiple registries, and value is the &quot;id&quot; of &lt;dubbo:registry&gt;. Multiple registries are separated by commas.If you do not want to register the service to any registry,pls set &quot;N/A&quot;</td>\n<td>above 2.0.5</td>\n</tr>\n<tr>\n<td>layer</td>\n<td>layer</td>\n<td>string</td>\n<td>N</td>\n<td></td>\n<td>service governance</td>\n<td>The layer of consumer. Such as: biz, dao, intl:web, china:acton</td>\n<td>above 2.0.7</td>\n</tr>\n<tr>\n<td>init</td>\n<td>init</td>\n<td>boolean</td>\n<td>N</td>\n<td>false</td>\n<td>performance optimization</td>\n<td>If true, initialize when &quot;afterPropertiesSet()&quot; is invoked. Otherwise wait until the instance is referenced to initialize</td>\n<td>above 2.0.10</td>\n</tr>\n<tr>\n<td>cache</td>\n<td>cache</td>\n<td>string/boolean</td>\n<td>N</td>\n<td></td>\n<td>service governance</td>\n<td>Cache return result, and key is call parameters. Choices: lru, threadlocal, jcache and so on</td>\n<td>at least 2.1.0</td>\n</tr>\n<tr>\n<td>validation</td>\n<td>validation</td>\n<td>boolean</td>\n<td>N</td>\n<td></td>\n<td>service governance</td>\n<td>Whether enable JSR303 standard annotation validation</td>\n<td>at least 2.1.0</td>\n</tr>\n</tbody>\n</table>\n"},{filename:"user/references/xml/dubbo-method.md",__html:'<style>\ntable {\n  width: 100%;\n  max-width: 65em;\n  border: 1px solid #dedede;\n  margin: 15px auto;\n  border-collapse: collapse;\n  empty-cells: show;\n}\ntable th,\ntable td {\n  height: 35px;\n  border: 1px solid #dedede;\n  padding: 0 10px;\n}\ntable th {\n  font-weight: bold;\n  text-align: center !important;\n  background: rgba(158,188,226,0.2);\n  white-space: nowrap;\n}\ntable tbody tr:nth-child(2n) {\n  background: rgba(158,188,226,0.12);\n}\ntable td:nth-child(1) {\n  white-space: nowrap;\n}\ntable tr:hover {\n  background: #efefef;\n}\n.table-area {\n  overflow: auto;\n}\n</style>\n<script type="text/javascript">\n[].slice.call(document.querySelectorAll(\'table\')).forEach(function(el){\n    var wrapper = document.createElement(\'div\');\n    wrapper.className = \'table-area\';\n    el.parentNode.insertBefore(wrapper, el);\n    el.parentNode.removeChild(el);\n    wrapper.appendChild(el);\n})\n<\/script>\n<h1>dubbo:method</h1>\n<p>Method level configuration. The corresponding class: <code>com.alibaba.dubbo.config.MethodConfig</code>. This tag is a child tag of <code>&lt;dubbo:service&gt;</code> or <code>&lt;dubbo:reference&gt;</code>, for accuracy to method level.</p>\n<table>\n<thead>\n<tr>\n<th>Property</th>\n<th>Corresponding URL parameter</th>\n<th>Type</th>\n<th>Requisite</th>\n<th>Default</th>\n<th>Effect</th>\n<th>Description</th>\n<th>Compatibility</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>name</td>\n<td></td>\n<td>string</td>\n<td><b>Y</b></td>\n<td></td>\n<td>identifier</td>\n<td>Method name</td>\n<td>above 1.0.8</td>\n</tr>\n<tr>\n<td>timeout</td>\n<td>&lt;metodName&gt;.timeout</td>\n<td>int</td>\n<td>N</td>\n<td>&lt;dubbo:reference&gt; timeout</td>\n<td>performance optimization</td>\n<td>Method call timeout(ms)</td>\n<td>above 1.0.8</td>\n</tr>\n<tr>\n<td>retries</td>\n<td>&lt;metodName&gt;.retries</td>\n<td>int</td>\n<td>N</td>\n<td>&lt;dubbo:reference&gt; retries</td>\n<td>performance optimization</td>\n<td>Invoking retry times, exclude the first invoking. Set 0 to disable it</td>\n<td>above 2.0.0</td>\n</tr>\n<tr>\n<td>loadbalance</td>\n<td>&lt;metodName&gt;.loadbalance</td>\n<td>string</td>\n<td>N</td>\n<td>&lt;dubbo:reference&gt; loadbalance</td>\n<td>performance optimization</td>\n<td>Load balancing strategy. Choices:random, roundrobin(polling), leastactive(invoking least active service)</td>\n<td>above 2.0.0</td>\n</tr>\n<tr>\n<td>async</td>\n<td>&lt;metodName&gt;.async</td>\n<td>boolean</td>\n<td>N</td>\n<td>&lt;dubbo:reference&gt; async</td>\n<td>performance optimization</td>\n<td>Whether invoke asynchronously</td>\n<td>above 1.0.9</td>\n</tr>\n<tr>\n<td>sent</td>\n<td>&lt;methodName&gt;.sent</td>\n<td>boolean</td>\n<td>N</td>\n<td>true</td>\n<td>performance optimization</td>\n<td>Generally used when async is true, and If true, indicate that the network has sent out data</td>\n<td>above 2.0.6</td>\n</tr>\n<tr>\n<td>actives</td>\n<td>&lt;metodName&gt;.actives</td>\n<td>int</td>\n<td>N</td>\n<td>0</td>\n<td>performance optimization</td>\n<td>The max concurrency of per service method for each corresponding consumer</td>\n<td>above 2.0.5</td>\n</tr>\n<tr>\n<td>executes</td>\n<td>&lt;metodName&gt;.executes</td>\n<td>int</td>\n<td>N</td>\n<td>0</td>\n<td>performance optimization</td>\n<td>The maximum number of threads of per service method is limited- -. Only take effect when &lt;dubbo:method&gt; is &lt;dubbo:service&gt; child tag</td>\n<td>above 2.0.5</td>\n</tr>\n<tr>\n<td>deprecated</td>\n<td>&lt;methodName&gt;.deprecated</td>\n<td>boolean</td>\n<td>N</td>\n<td>false</td>\n<td>service governance</td>\n<td>Whether is deprecated method. Only take effect when &lt;dubbo:method&gt; is &lt;dubbo:service&gt; child tag</td>\n<td>above 2.0.5</td>\n</tr>\n<tr>\n<td>sticky</td>\n<td>&lt;methodName&gt;.sticky</td>\n<td>boolean</td>\n<td>N</td>\n<td>false</td>\n<td>service governance</td>\n<td>If true, all methods on this interface use the same provider. If more complex rules are required, use routing</td>\n<td>above 2.0.6</td>\n</tr>\n<tr>\n<td>return</td>\n<td>&lt;methodName&gt;.return</td>\n<td>boolean</td>\n<td>N</td>\n<td>true</td>\n<td>performance optimization</td>\n<td>Whether need return value. Only take effect when async is true. If true, return future, or callback such as &quot;onreturn&quot; method. Otherwise, return null.</td>\n<td>above 2.0.6</td>\n</tr>\n<tr>\n<td>oninvoke</td>\n<td></td>\n<td>String</td>\n<td>N</td>\n<td></td>\n<td>performance optimization</td>\n<td>Intercept before invoke</td>\n<td>above 2.0.6</td>\n</tr>\n<tr>\n<td>onreturn</td>\n<td></td>\n<td>String</td>\n<td>N</td>\n<td></td>\n<td>performance optimization</td>\n<td>Intercept after invoke</td>\n<td>above 2.0.6</td>\n</tr>\n<tr>\n<td>onthrow</td>\n<td></td>\n<td>String</td>\n<td>N</td>\n<td></td>\n<td>performance optimization</td>\n<td>Intercept when catch exception</td>\n<td>above 2.0.6</td>\n</tr>\n<tr>\n<td>cache</td>\n<td>&lt;methodName&gt;.cache</td>\n<td>string/boolean</td>\n<td>N</td>\n<td></td>\n<td>service governance</td>\n<td>Cache return result, and key is call parameters. Choices: lru, threadlocal, jcache and so on</td>\n<td>at least 2.1.0</td>\n</tr>\n<tr>\n<td>validation</td>\n<td>&lt;methodName&gt;.validation</td>\n<td>boolean</td>\n<td>N</td>\n<td></td>\n<td>service governance</td>\n<td>Whether enable JSR303 standard annotation validation</td>\n<td>at least 2.1.0</td>\n</tr>\n</tbody>\n</table>\n<p>For example:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:reference</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.xxx.XxxService"</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">"findXxx"</span> <span class="hljs-attr">timeout</span>=<span class="hljs-string">"3000"</span> <span class="hljs-attr">retries</span>=<span class="hljs-string">"2"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:reference</span>&gt;</span>\n</code></pre>\n'},{filename:"user/references/xml/dubbo-module.md",__html:"<style>\ntable {\n  width: 100%;\n  max-width: 65em;\n  border: 1px solid #dedede;\n  margin: 15px auto;\n  border-collapse: collapse;\n  empty-cells: show;\n}\ntable th,\ntable td {\n  height: 35px;\n  border: 1px solid #dedede;\n  padding: 0 10px;\n}\ntable th {\n  font-weight: bold;\n  text-align: center !important;\n  background: rgba(158,188,226,0.2);\n  white-space: nowrap;\n}\ntable tbody tr:nth-child(2n) {\n  background: rgba(158,188,226,0.12);\n}\ntable td:nth-child(1) {\n  white-space: nowrap;\n}\ntable tr:hover {\n  background: #efefef;\n}\n.table-area {\n  overflow: auto;\n}\n</style>\n<script type=\"text/javascript\">\n[].slice.call(document.querySelectorAll('table')).forEach(function(el){\n    var wrapper = document.createElement('div');\n    wrapper.className = 'table-area';\n    el.parentNode.insertBefore(wrapper, el);\n    el.parentNode.removeChild(el);\n    wrapper.appendChild(el);\n})\n<\/script>\n<h1>dubbo:module</h1>\n<p>Module configuration. The corresponding class <code>com.alibaba.dubbo.config.ModuleConfig</code></p>\n<table>\n<thead>\n<tr>\n<th>Property</th>\n<th>The corresponding class</th>\n<th>Type</th>\n<th>Requisite</th>\n<th>Default</th>\n<th>Effect</th>\n<th>Description</th>\n<th>Compatibility</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>name</td>\n<td>module</td>\n<td>string</td>\n<td><b>Y</b></td>\n<td></td>\n<td>service governance</td>\n<td>Module name is for registry combing the dependencies of modules.</td>\n<td>above 2.2.0</td>\n</tr>\n<tr>\n<td>version</td>\n<td>module.version</td>\n<td>string</td>\n<td>N</td>\n<td></td>\n<td>service governance</td>\n<td>module version</td>\n<td>above 2.2.0</td>\n</tr>\n<tr>\n<td>owner</td>\n<td>owner</td>\n<td>string</td>\n<td>N</td>\n<td></td>\n<td>service governance</td>\n<td>Module manager, Pls. fill in the mailbox prefix of the person in charge</td>\n<td>above 2.2.0</td>\n</tr>\n<tr>\n<td>organization</td>\n<td>organization</td>\n<td>string</td>\n<td>N</td>\n<td></td>\n<td>service governance</td>\n<td>Organization name is for registry distinguishing between the source of service. As a suggestion, this property should be written in config file directly. Such as china,intl,itu,crm,asc,dw,aliexpress etc.</td>\n<td>above 2.2.0</td>\n</tr>\n</tbody>\n</table>\n"},{filename:"user/references/xml/dubbo-monitor.md",__html:"<style>\ntable {\n  width: 100%;\n  max-width: 65em;\n  border: 1px solid #dedede;\n  margin: 15px auto;\n  border-collapse: collapse;\n  empty-cells: show;\n}\ntable th,\ntable td {\n  height: 35px;\n  border: 1px solid #dedede;\n  padding: 0 10px;\n}\ntable th {\n  font-weight: bold;\n  text-align: center !important;\n  background: rgba(158,188,226,0.2);\n  white-space: nowrap;\n}\ntable tbody tr:nth-child(2n) {\n  background: rgba(158,188,226,0.12);\n}\ntable td:nth-child(1) {\n  white-space: nowrap;\n}\ntable tr:hover {\n  background: #efefef;\n}\n.table-area {\n  overflow: auto;\n}\n</style>\n<script type=\"text/javascript\">\n[].slice.call(document.querySelectorAll('table')).forEach(function(el){\n    var wrapper = document.createElement('div');\n    wrapper.className = 'table-area';\n    el.parentNode.insertBefore(wrapper, el);\n    el.parentNode.removeChild(el);\n    wrapper.appendChild(el);\n})\n<\/script>\n<h1>dubbo:monitor</h1>\n<p>Monitor center configuration. The corresponding class: <code>com.alibaba.dubbo.config.MonitorConfig</code></p>\n<table>\n<thead>\n<tr>\n<th>Property</th>\n<th>The corresponding class</th>\n<th>Type</th>\n<th>Requisite</th>\n<th>Default</th>\n<th>Effect</th>\n<th>Description</th>\n<th>Compatibility</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>protocol</td>\n<td>protocol</td>\n<td>string</td>\n<td>N</td>\n<td>dubbo</td>\n<td>service governance</td>\n<td>Monitor center protocol. &quot;registry&quot; means looking up monitor center from registry. Others mean communicating to monitor center directly</td>\n<td>above 2.0.9</td>\n</tr>\n<tr>\n<td>address</td>\n<td>&lt;url&gt;</td>\n<td>string</td>\n<td>N</td>\n<td>N/A</td>\n<td>service governance</td>\n<td>Communicating to monitor center directly. address=&quot;10.20.130.230:12080&quot;</td>\n<td>above 1.0.16</td>\n</tr>\n</tbody>\n</table>\n"},{filename:"user/references/xml/dubbo-parameter.md",__html:'<style>\ntable {\n  width: 100%;\n  max-width: 65em;\n  border: 1px solid #dedede;\n  margin: 15px auto;\n  border-collapse: collapse;\n  empty-cells: show;\n}\ntable th,\ntable td {\n  height: 35px;\n  border: 1px solid #dedede;\n  padding: 0 10px;\n}\ntable th {\n  font-weight: bold;\n  text-align: center !important;\n  background: rgba(158,188,226,0.2);\n  white-space: nowrap;\n}\ntable tbody tr:nth-child(2n) {\n  background: rgba(158,188,226,0.12);\n}\ntable td:nth-child(1) {\n  white-space: nowrap;\n}\ntable tr:hover {\n  background: #efefef;\n}\n.table-area {\n  overflow: auto;\n}\n</style>\n<script type="text/javascript">\n[].slice.call(document.querySelectorAll(\'table\')).forEach(function(el){\n    var wrapper = document.createElement(\'div\');\n    wrapper.className = \'table-area\';\n    el.parentNode.insertBefore(wrapper, el);\n    el.parentNode.removeChild(el);\n    wrapper.appendChild(el);\n})\n<\/script>\n<h1>dubbo:parameter</h1>\n<p>Optional parameter configuration. The corresponding class is <code>java.util.Map</code>. This tag is used as a sub tag to configure custom parameters for extending <code>&lt;dubbo:protocol&gt;</code>, <code>&lt;dubbo:service&gt;</code>, <code>&lt;dubbo:provider&gt;</code>, <code>&lt;dubbo:reference&gt;</code> or <code>&lt;dubbo:consumer&gt;</code>.</p>\n<table>\n<thead>\n<tr>\n<th>Attribute</th>\n<th>Corresponding URL parameter</th>\n<th>Type</th>\n<th>Required</th>\n<th>Default Value</th>\n<th>Function</th>\n<th>Description</th>\n<th>Compatibility</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>key</td>\n<td>key</td>\n<td>string</td>\n<td><b>True</b></td>\n<td></td>\n<td>Service governance</td>\n<td>routing parameter key</td>\n<td>Above 2.0.0</td>\n</tr>\n<tr>\n<td>value</td>\n<td>value</td>\n<td>string</td>\n<td><b>True</b></td>\n<td></td>\n<td>Service governance</td>\n<td>routing parameter value</td>\n<td>Above 2.0.0</td>\n</tr>\n</tbody>\n</table>\n<p>For example:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"napoli"</span>&gt;</span>\n    <span class="hljs-tag">&lt;<span class="hljs-name">dubbo:parameter</span> <span class="hljs-attr">key</span>=<span class="hljs-string">"http://10.20.160.198/wiki/display/dubbo/napoli.queue.name"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">dubbo:protocol</span>&gt;</span>\n</code></pre>\n<p>you can also use it like this:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"jms"</span> <span class="hljs-attr">p:queue</span>=<span class="hljs-string">"xxx"</span> /&gt;</span>\n</code></pre>\n'},{filename:"user/references/xml/dubbo-protocol.md",__html:"<style>\ntable {\n  width: 100%;\n  max-width: 65em;\n  border: 1px solid #dedede;\n  margin: 15px auto;\n  border-collapse: collapse;\n  empty-cells: show;\n}\ntable th,\ntable td {\n  height: 35px;\n  border: 1px solid #dedede;\n  padding: 0 10px;\n}\ntable th {\n  font-weight: bold;\n  text-align: center !important;\n  background: rgba(158,188,226,0.2);\n  white-space: nowrap;\n}\ntable tbody tr:nth-child(2n) {\n  background: rgba(158,188,226,0.12);\n}\ntable td:nth-child(1) {\n  white-space: nowrap;\n}\ntable tr:hover {\n  background: #efefef;\n}\n.table-area {\n  overflow: auto;\n}\n</style>\n<script type=\"text/javascript\">\n[].slice.call(document.querySelectorAll('table')).forEach(function(el){\n    var wrapper = document.createElement('div');\n    wrapper.className = 'table-area';\n    el.parentNode.insertBefore(wrapper, el);\n    el.parentNode.removeChild(el);\n    wrapper.appendChild(el);\n})\n<\/script>\n<h1>dubbo:protocol</h1>\n<p>Service provider protocol configuration. The corresponding class is <code>com.alibaba.dubbo.config.ProtocolConfig</code>. If you need to support multiple protocols, you could declare multiple <code>&lt;dubbo:protocol&gt;</code> tags, and specify the protocol via <code>protocol</code> property.</p>\n<table>\n<thead>\n<tr>\n<th>Attribute</th>\n<th>Corresponding URL parameter</th>\n<th>Type</th>\n<th>Required</th>\n<th>Default Value</th>\n<th>Function</th>\n<th>Description</th>\n<th>Compatibility</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>id</td>\n<td></td>\n<td>string</td>\n<td>False</td>\n<td>dubbo</td>\n<td>Configuration association</td>\n<td>Bean Id of the protocol, can be referenced in &lt;dubbo:service protocol=&quot;&quot;&gt; The default value is equal to the value of <code>name</code> attribute while <code>id</code> is not filled. If <code>name</code> value has already existed, it will add index to it's suffix.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>name</td>\n<td>&lt;protocol&gt;</td>\n<td>string</td>\n<td><b>True</b></td>\n<td>dubbo</td>\n<td>Performance optimize</td>\n<td>Protocol name</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>port</td>\n<td>&lt;port&gt;</td>\n<td>int</td>\n<td>False</td>\n<td>The default port of dubbo protocol is 20880, rmi protocol is 1099, http and hessian protocol are 80;It will allocate an unused port if <code>port</code> is not filled or equals <code>-1</code>. To ensure the ports scope is controllable, the port will increase based on the corresponding protocol default port after Dubbo 2.4.0+</td>\n<td>Service discovery</td>\n<td>Service port</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>host</td>\n<td>&lt;host&gt;</td>\n<td>string</td>\n<td>False</td>\n<td>Find local IP automatically</td>\n<td>Service discovery</td>\n<td>-The host name of services, to specify VIP and domain, or having multiple network cards. If null, it will find local IP automatically- It's recommended to let Dubbo find local IP automatically</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>threadpool</td>\n<td>threadpool</td>\n<td>string</td>\n<td>False</td>\n<td>fixed</td>\n<td>Performance optimize</td>\n<td>The type of Thread Pool, fixed/cached are available</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>threads</td>\n<td>threads</td>\n<td>int</td>\n<td>False</td>\n<td>100</td>\n<td>Performance optimize</td>\n<td>The size of the services' Thread Pool(Fixed)</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>iothreads</td>\n<td>threads</td>\n<td>int</td>\n<td>False</td>\n<td>The count of CPU + 1</td>\n<td>Performance optimize</td>\n<td>The size of io Thread Pool(Fixed)</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>accepts</td>\n<td>accepts</td>\n<td>int</td>\n<td>False</td>\n<td>0</td>\n<td>Performance optimize</td>\n<td>The maximum connection count of the service provider</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>payload</td>\n<td>payload</td>\n<td>int</td>\n<td>False</td>\n<td>88388608(=8M)</td>\n<td>Performance optimize</td>\n<td>The length limit of request and response, unit is byte</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>codec</td>\n<td>codec</td>\n<td>string</td>\n<td>False</td>\n<td>dubbo</td>\n<td>Performance optimize</td>\n<td>Protocol encoding</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>serialization</td>\n<td>serialization</td>\n<td>string</td>\n<td>False</td>\n<td>The default serialization of dubbo protocol is hessian2, rmi protocol is java, http protocol is json</td>\n<td>Performance optimize</td>\n<td>Protocol serialization, It's used when a protocol has multiple serializations. For example, <code>dubbo</code> protocol has 4 serializations, they are <code>dubbo</code>, <code>hessian2</code>, <code>java</code> and <code>compactedjava</code>.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>accesslog</td>\n<td>accesslog</td>\n<td>string/boolean</td>\n<td>False</td>\n<td></td>\n<td>Service discovery</td>\n<td><code>true</code> will write access log to logger. Specifying it to a log path, you can write access logs to special log file.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>path</td>\n<td>&lt;path&gt;</td>\n<td>string</td>\n<td>False</td>\n<td></td>\n<td>Service discovery</td>\n<td>Context path, the prefix of the service path</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>transporter</td>\n<td>transporter</td>\n<td>string</td>\n<td>False</td>\n<td>The default value of dubbo protocol is netty</td>\n<td>Performance optimize</td>\n<td>The server and client implements of the protocol. For example, mina and netty for dubbo protocol. You can configure server or client side separately.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>server</td>\n<td>server</td>\n<td>string</td>\n<td>False</td>\n<td>The default value of dubbo protocol is netty, http protocol is servlet</td>\n<td>Performance optimize</td>\n<td>The server implement of the protocol. For example, mina and netty for dubbo ptotocol, jetty and servlet for http protocol.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>client</td>\n<td>client</td>\n<td>string</td>\n<td>False</td>\n<td>The default value of dubbo protocol is netty</td>\n<td>Performance optimize</td>\n<td>The client implement of the protocol. For example, mina and netty for dubbo protocol.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>dispatcher</td>\n<td>dispatcher</td>\n<td>string</td>\n<td>False</td>\n<td>The default value of dubbo protocol is all</td>\n<td>Performance optimize</td>\n<td>specify the thread model of the way to dispatching. Such as <code>all</code>, <code>direct</code>, <code>message</code>, <code>execution</code>, and <code>connection</code> for dubbo protocol.</td>\n<td>Above 2.1.0</td>\n</tr>\n<tr>\n<td>queues</td>\n<td>queues</td>\n<td>int</td>\n<td>False</td>\n<td>0</td>\n<td>Performance optimize</td>\n<td>The queue size of the Thread Pool. It's recommended not to specify it in order to invoke other provides rather than queueing unless you have special requirement.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>charset</td>\n<td>charset</td>\n<td>string</td>\n<td>False</td>\n<td>UTF-8</td>\n<td>Performance optimize</td>\n<td>Serialization encoding</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>buffer</td>\n<td>buffer</td>\n<td>int</td>\n<td>False</td>\n<td>8192</td>\n<td>Performance optimize</td>\n<td>The buffer size of networking IO</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>heartbeat</td>\n<td>heartbeat</td>\n<td>int</td>\n<td>False</td>\n<td>0</td>\n<td>Performance optimize</td>\n<td>Heartbeat interval. For long connection, it's difficult to receive closing event while the physical layer is disconnected. So heartbeat is necessary to help checking the connection quality</td>\n<td>Above 2.0.10</td>\n</tr>\n<tr>\n<td>telnet</td>\n<td>telnet</td>\n<td>string</td>\n<td>False</td>\n<td></td>\n<td>Service discovery</td>\n<td>Supported telnet commands, use <code>,</code> to separate commands.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>register</td>\n<td>register</td>\n<td>boolean</td>\n<td>False</td>\n<td>true</td>\n<td>Service discovery</td>\n<td>Whether registering the corresponding services to registry center</td>\n<td>Above 2.0.8</td>\n</tr>\n<tr>\n<td>contextpath</td>\n<td>contextpath</td>\n<td>String</td>\n<td>False</td>\n<td>Default value is an empty string</td>\n<td>Service discovery</td>\n<td></td>\n<td>Above 2.0.6</td>\n</tr>\n</tbody>\n</table>\n"},{filename:"user/references/xml/dubbo-provider.md",__html:"<style>\ntable {\n  width: 100%;\n  max-width: 65em;\n  border: 1px solid #dedede;\n  margin: 15px auto;\n  border-collapse: collapse;\n  empty-cells: show;\n}\ntable th,\ntable td {\n  height: 35px;\n  border: 1px solid #dedede;\n  padding: 0 10px;\n}\ntable th {\n  font-weight: bold;\n  text-align: center !important;\n  background: rgba(158,188,226,0.2);\n  white-space: nowrap;\n}\ntable tbody tr:nth-child(2n) {\n  background: rgba(158,188,226,0.12);\n}\ntable td:nth-child(1) {\n  white-space: nowrap;\n}\ntable tr:hover {\n  background: #efefef;\n}\n.table-area {\n  overflow: auto;\n}\n</style>\n<script type=\"text/javascript\">\n[].slice.call(document.querySelectorAll('table')).forEach(function(el){\n    var wrapper = document.createElement('div');\n    wrapper.className = 'table-area';\n    el.parentNode.insertBefore(wrapper, el);\n    el.parentNode.removeChild(el);\n    wrapper.appendChild(el);\n})\n<\/script>\n<h1>dubbo:provider</h1>\n<p>The default configuration of service provider. The corresponding class is <code>com.alibaba.dubbo.config.ProviderConfig</code>. This tag provider default values for <code>&lt;dubbo:service&gt;</code> and <code>&lt;dubbo:protocol&gt;</code>.</p>\n<table>\n<thead>\n<tr>\n<th>Attribute</th>\n<th>Corresponding URL parameter</th>\n<th>Type</th>\n<th>Required</th>\n<th>Default Value</th>\n<th>Function</th>\n<th>Description</th>\n<th>Compatibility</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>id</td>\n<td></td>\n<td>string</td>\n<td>False</td>\n<td>dubbo</td>\n<td>Configuration association</td>\n<td>Bean Id of the protocol, can be referenced in &lt;dubbo:service proivder=&quot;&quot;&gt;</td>\n<td>Above 1.0.16</td>\n</tr>\n<tr>\n<td>protocol</td>\n<td>&lt;protocol&gt;</td>\n<td>string</td>\n<td>False</td>\n<td>dubbo</td>\n<td>Performance optimize</td>\n<td>Protocol name</td>\n<td>Above 1.0.16</td>\n</tr>\n<tr>\n<td>host</td>\n<td>&lt;host&gt;</td>\n<td>string</td>\n<td>False</td>\n<td>Find local IP automatically</td>\n<td>Service discovery</td>\n<td>The host name of services, to specify VIP and domain, or having multiple network cards. If null, it will find local IP automatically. It's recommended to let Dubbo find local IP automatically</td>\n<td>Above 1.0.16</td>\n</tr>\n<tr>\n<td>threads</td>\n<td>threads</td>\n<td>int</td>\n<td>False</td>\n<td>100</td>\n<td>Performance optimize</td>\n<td>The size of the services' Thread Pool(Fixed)</td>\n<td>Above 1.0.16</td>\n</tr>\n<tr>\n<td>payload</td>\n<td>payload</td>\n<td>int</td>\n<td>False</td>\n<td>88388608(=8M)</td>\n<td>Performance optimize</td>\n<td>The length limit of request and response, unit is byte</td>\n<td>Above 2.0.0</td>\n</tr>\n<tr>\n<td>path</td>\n<td>&lt;path&gt;</td>\n<td>string</td>\n<td>False</td>\n<td></td>\n<td>Service discovery</td>\n<td>Context path of the service provider, the prefix of the service path</td>\n<td>Above 2.0.0</td>\n</tr>\n<tr>\n<td>server</td>\n<td>server</td>\n<td>string</td>\n<td>False</td>\n<td>Default is netty for dubbo protocol, servlet for http protocol</td>\n<td>Performance optimize</td>\n<td>The server implement of the protocol. For example, mina and netty for dubbo ptotocol, jetty and servlet for http protocol.</td>\n<td>Above 2.0.0</td>\n</tr>\n<tr>\n<td>client</td>\n<td>client</td>\n<td>string</td>\n<td>False</td>\n<td>Default is netty for dubbo protocol</td>\n<td>Performance optimize</td>\n<td>The client implement of the protocol. For example, mina and netty for dubbo protocol.</td>\n<td>Above 2.0.0</td>\n</tr>\n<tr>\n<td>codec</td>\n<td>codec</td>\n<td>string</td>\n<td>False</td>\n<td>dubbo</td>\n<td>Performance optimize</td>\n<td>Protocol encoding</td>\n<td>Above 2.0.0</td>\n</tr>\n<tr>\n<td>serialization</td>\n<td>serialization</td>\n<td>string</td>\n<td>False</td>\n<td>Default is hessian2 for dubbo protocol, json for http protocol</td>\n<td>Performance optimize</td>\n<td>Protocol serialization, It's used when a protocol has multiple serializations. For example, <code>dubbo</code> protocol has 4 serializations, they are <code>dubbo</code>, <code>hessian2</code>, <code>java</code> and <code>compactedjava</code>, <code>http</code> protocol has <code>json</code> and <code>xml</code>.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>default</td>\n<td></td>\n<td>boolean</td>\n<td>False</td>\n<td>false</td>\n<td>Configuration association</td>\n<td>To specify the default protocol for support multiple protocols.</td>\n<td>Above 1.0.16</td>\n</tr>\n<tr>\n<td>filter</td>\n<td>service.filter</td>\n<td>string</td>\n<td>False</td>\n<td></td>\n<td>Performance optimize</td>\n<td>The filter name of the RPC process of the provider, use <code>,</code> to separate multiple filter names.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>listener</td>\n<td>exporter.listener</td>\n<td>string</td>\n<td>False</td>\n<td></td>\n<td>Performance optimize</td>\n<td>The listener name of the provider, use <code>,</code> to separate multiple listener names.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>threadpool</td>\n<td>threadpool</td>\n<td>string</td>\n<td>False</td>\n<td>fixed</td>\n<td>Performance optimize</td>\n<td>The type of Thread Pool, fixed/cached are available</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>accepts</td>\n<td>accepts</td>\n<td>int</td>\n<td>False</td>\n<td>0</td>\n<td>Performance optimize</td>\n<td>The maximum connection count of the service provider</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>version</td>\n<td>version</td>\n<td>string</td>\n<td>False</td>\n<td>0.0.0</td>\n<td>Service discovery</td>\n<td>Service version. It's recommended to use 2 digitals such as <code>1.0</code>. It's necessary to upgrade version only when the service is not compatible.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>group</td>\n<td>group</td>\n<td>string</td>\n<td>False</td>\n<td></td>\n<td>Service discovery</td>\n<td>The group of the service providers. It can distinguish services when it has multiple implements.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>delay</td>\n<td>delay</td>\n<td>int</td>\n<td>False</td>\n<td>0</td>\n<td>Performance optimize</td>\n<td>The delay time(ms) for registering services.  When set to -1, it indicates that the services will expose to registry after the Spring context is initialized</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>timeout</td>\n<td>default.timeout</td>\n<td>int</td>\n<td>False</td>\n<td>1000</td>\n<td>Performance optimize</td>\n<td>The RPC timeout(ms)</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>retries</td>\n<td>default.retries</td>\n<td>int</td>\n<td>False</td>\n<td>2</td>\n<td>Performance optimize</td>\n<td>The retry count for RPC, not including the first invoke. Please set it to 0 if don't need to retry.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>connections</td>\n<td>default.connections</td>\n<td>int</td>\n<td>False</td>\n<td>0</td>\n<td>Performance optimize</td>\n<td>The maximum connections of every provider. For short connection such as rmi, http and hessian, it's connection limit, but for long connection such as dubbo, it's connection count.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>loadbalance</td>\n<td>default.loadbalance</td>\n<td>string</td>\n<td>False</td>\n<td>random</td>\n<td>Performance optimize</td>\n<td>Strategy of load balance, <code>random</code>, <code>roundrobin</code> and <code>leastactive</code> are available.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>async</td>\n<td>default.async</td>\n<td>boolean</td>\n<td>False</td>\n<td>false</td>\n<td>Performance optimize</td>\n<td>Asynchronous execution, not reliable. It does not block the execution thread just only ignores the return value.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>stub</td>\n<td>stub</td>\n<td>boolean</td>\n<td>False</td>\n<td>false</td>\n<td>Service governance</td>\n<td><code>true</code> means use the default proxy class name, which is the interface name with <code>Local</code> as the suffix.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>mock</td>\n<td>mock</td>\n<td>boolean</td>\n<td>False</td>\n<td>false</td>\n<td>Service governance</td>\n<td><code>true</code> means use the default mock class name, which is the interface name with <code>Mock</code> suffix.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>token</td>\n<td>token</td>\n<td>boolean</td>\n<td>False</td>\n<td>false</td>\n<td>Service governance</td>\n<td>Enable token validation. Disable token if it's null. It will generate token randomly when it is enable.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>registry</td>\n<td>registry</td>\n<td>string</td>\n<td>False</td>\n<td>By default, register to all registries</td>\n<td>Configuration association</td>\n<td>Register services to specified registry while having multiple registries. It is the <code>id</code> value of the &lt;dubbo:registry&gt;. If don't want to register to any registry, set it as <code>N/A</code></td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>dynamic</td>\n<td>dynamic</td>\n<td>boolean</td>\n<td>False</td>\n<td>true</td>\n<td>Service governance</td>\n<td>Whether the service is registered dynamically. If false, services will be showed as <code>disable</code>, you need to enable it manually. And you also need to disable it when provider shut down.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>accesslog</td>\n<td>accesslog</td>\n<td>string/boolean</td>\n<td>False</td>\n<td>false</td>\n<td>Service governance</td>\n<td><code>true</code> will write access log to logger. Specifying it to a log path, you can write access logs to special log file.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>owner</td>\n<td>owner</td>\n<td>string</td>\n<td>False</td>\n<td></td>\n<td>Service governance</td>\n<td>The owner of the service. It's used for service governance.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>document</td>\n<td>document</td>\n<td>string</td>\n<td>False</td>\n<td></td>\n<td>Service governance</td>\n<td>Service document URL</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>weight</td>\n<td>weight</td>\n<td>int</td>\n<td>False</td>\n<td></td>\n<td>Performance optimize</td>\n<td>The weight of the service</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>executes</td>\n<td>executes</td>\n<td>int</td>\n<td>False</td>\n<td>0</td>\n<td>Performance optimize</td>\n<td>The maximum parallel execution request count per method per service for the provider.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>actives</td>\n<td>default.actives</td>\n<td>int</td>\n<td>False</td>\n<td>0</td>\n<td>Performance optimize</td>\n<td>The maximum concurrent calls per method per service of the consumer.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>proxy</td>\n<td>proxy</td>\n<td>string</td>\n<td>False</td>\n<td>javassist</td>\n<td>Performance optimize</td>\n<td>The proxy implement, jdk/javassist are available.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>cluster</td>\n<td>default.cluster</td>\n<td>string</td>\n<td>False</td>\n<td>failover</td>\n<td>Performance optimize</td>\n<td>failover/failfast/failsafe/failback/forking are available.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>deprecated</td>\n<td>deprecated</td>\n<td>boolean</td>\n<td>False</td>\n<td>false</td>\n<td>Service governance</td>\n<td>Mark the service is deprecated. If true, there will log an error log on the client side.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>queues</td>\n<td>queues</td>\n<td>int</td>\n<td>False</td>\n<td>0</td>\n<td>Performance optimize</td>\n<td>The queue size of the Thread Pool. It's recommended not to specify it in order to invoke other provides rather than queueing unless you have special requirement.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>charset</td>\n<td>charset</td>\n<td>string</td>\n<td>False</td>\n<td>UTF-8</td>\n<td>Performance optimize</td>\n<td>Serialization encoding</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>buffer</td>\n<td>buffer</td>\n<td>int</td>\n<td>False</td>\n<td>8192</td>\n<td>Performance optimize</td>\n<td>The buffer size of networking IO</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>iothreads</td>\n<td>iothreads</td>\n<td>int</td>\n<td>False</td>\n<td>CPU + 1</td>\n<td>Performance optimize</td>\n<td>The size of io Thread Pool(Fixed). These threads are used to receive, serialize and deserialize IO data. See <code>threads</code> for configuring business thread pool. It's not recommended to configure this.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>telnet</td>\n<td>telnet</td>\n<td>string</td>\n<td>False</td>\n<td></td>\n<td>Service governance</td>\n<td>Supported telnet commands, use <code>,</code> to separate commands.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>contextpath</td>\n<td>contextpath</td>\n<td>String</td>\n<td>False</td>\n<td>Empty string</td>\n<td>Service governance</td>\n<td></td>\n<td></td>\n</tr>\n<tr>\n<td>layer</td>\n<td>layer</td>\n<td>string</td>\n<td>False</td>\n<td></td>\n<td>Service governance</td>\n<td>The biz layer of the service provider, such as biz, dao, intl:web and china:acton.</td>\n<td>Above 2.0.7</td>\n</tr>\n</tbody>\n</table>\n"},{filename:"user/references/xml/dubbo-reference.md",__html:"<style>\ntable {\n  width: 100%;\n  max-width: 65em;\n  border: 1px solid #dedede;\n  margin: 15px auto;\n  border-collapse: collapse;\n  empty-cells: show;\n}\ntable th,\ntable td {\n  height: 35px;\n  border: 1px solid #dedede;\n  padding: 0 10px;\n}\ntable th {\n  font-weight: bold;\n  text-align: center !important;\n  background: rgba(158,188,226,0.2);\n  white-space: nowrap;\n}\ntable tbody tr:nth-child(2n) {\n  background: rgba(158,188,226,0.12);\n}\ntable td:nth-child(1) {\n  white-space: nowrap;\n}\ntable tr:hover {\n  background: #efefef;\n}\n.table-area {\n  overflow: auto;\n}\n</style>\n<script type=\"text/javascript\">\n[].slice.call(document.querySelectorAll('table')).forEach(function(el){\n    var wrapper = document.createElement('div');\n    wrapper.className = 'table-area';\n    el.parentNode.insertBefore(wrapper, el);\n    el.parentNode.removeChild(el);\n    wrapper.appendChild(el);\n})\n<\/script>\n<h1>dubbo:reference</h1>\n<p>The configuration of service consumer. The corresponding class is<code>com.alibaba.dubbo.config.ReferenceConfig</code></p>\n<table>\n<thead>\n<tr>\n<th>Attribute</th>\n<th>Corresponding URL parameter</th>\n<th>Type</th>\n<th>Required</th>\n<th>Default Value</th>\n<th>Function</th>\n<th>Description</th>\n<th>Compatibility</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>id</td>\n<td></td>\n<td>string</td>\n<td><b>True</b></td>\n<td></td>\n<td>Configuration association</td>\n<td>Bean Id of the service reference</td>\n<td>Above 1.0.0</td>\n</tr>\n<tr>\n<td>interface</td>\n<td></td>\n<td>class</td>\n<td><b>True</b></td>\n<td></td>\n<td>Service discovery</td>\n<td>Interface name of the service</td>\n<td>Above 1.0.0</td>\n</tr>\n<tr>\n<td>version</td>\n<td>version</td>\n<td>string</td>\n<td>False</td>\n<td></td>\n<td>Service discovery</td>\n<td>Service version, must be equal to the provider's version</td>\n<td>Above 1.0.0</td>\n</tr>\n<tr>\n<td>group</td>\n<td>group</td>\n<td>string</td>\n<td>False</td>\n<td></td>\n<td>Service discovery</td>\n<td>The group of the service reference, must be equal to the provider's group. It can distinguish services when it has multiple implements.</td>\n<td>Above 1.0.7</td>\n</tr>\n<tr>\n<td>timeout</td>\n<td>timeout</td>\n<td>long</td>\n<td>False</td>\n<td>By default, use &lt;dubbo:consumer&gt; timeout</td>\n<td>Performance optimize</td>\n<td>The RPC timeout(ms)</td>\n<td>Above 1.0.5</td>\n</tr>\n<tr>\n<td>retries</td>\n<td>retries</td>\n<td>int</td>\n<td>False</td>\n<td>By default, use &lt;dubbo:consumer&gt; retries</td>\n<td>Performance optimize</td>\n<td>The retry count for RPC, not including the first invoke. Please set it to 0 if don't need to retry.</td>\n<td>Above 2.0.0</td>\n</tr>\n<tr>\n<td>connections</td>\n<td>connections</td>\n<td>int</td>\n<td>False</td>\n<td>By default, use &lt;dubbo:consumer&gt; connections</td>\n<td>Performance optimize</td>\n<td>The maximum connections of every provider. For short connection such as rmi, http and hessian, it's connection limit, but for long connection such as dubbo, it's connection count.</td>\n<td>Above 2.0.0</td>\n</tr>\n<tr>\n<td>loadbalance</td>\n<td>loadbalance</td>\n<td>string</td>\n<td>False</td>\n<td>By default, use &lt;dubbo:consumer&gt; loadbalance</td>\n<td>Performance optimize</td>\n<td>Strategy of load balance, <code>random</code>, <code>roundrobin</code> and <code>leastactive</code> are available.</td>\n<td>Above 2.0.0</td>\n</tr>\n<tr>\n<td>async</td>\n<td>async</td>\n<td>boolean</td>\n<td>False</td>\n<td>By default, use &lt;dubbo:consumer&gt; async</td>\n<td>Performance optimize</td>\n<td>Asynchronous execution, not reliable. It does not block the execution thread just only ignores the return value.</td>\n<td>Above 2.0.0</td>\n</tr>\n<tr>\n<td>generic</td>\n<td>generic</td>\n<td>boolean</td>\n<td>False</td>\n<td>By default, use &lt;dubbo:consumer&gt; generic</td>\n<td>Service governance</td>\n<td>Enable generic interface. If true, the reference will return <code>GenericService</code></td>\n<td>Above 2.0.0</td>\n</tr>\n<tr>\n<td>check</td>\n<td>check</td>\n<td>boolean</td>\n<td>False</td>\n<td>By default, use &lt;dubbo:consumer&gt; check</td>\n<td>Service governance</td>\n<td>Check the exist of the provider. If true, it will throw exception when provider is not exist. If false, will ignore.</td>\n<td>Above 2.0.0</td>\n</tr>\n<tr>\n<td>url</td>\n<td>url</td>\n<td>string</td>\n<td>False</td>\n<td></td>\n<td>Service governance</td>\n<td>Connect to the provider directly via this url. It will bypass the registry.</td>\n<td>Above 1.0.6</td>\n</tr>\n<tr>\n<td>stub</td>\n<td>stub</td>\n<td>class/boolean</td>\n<td>False</td>\n<td></td>\n<td>Service governance</td>\n<td>The local proxy class name of the client, it's used to execute local logic such as caching. The proxy class must have a constructor with the remote proxy object as a parameter, such as <code>public XxxServiceLocal(XxxService xxxService)</code></td>\n<td>Above 2.0.0</td>\n</tr>\n<tr>\n<td>mock</td>\n<td>mock</td>\n<td>class/boolean</td>\n<td>False</td>\n<td></td>\n<td>Service governance</td>\n<td>The mock class name. It's called when the RPC is failed, such as timeout or IO exception. The mock class must carry a  none parameter constructor. The difference between mock and local proxy is that local proxy is always invoked before RPC but mock is invoked only when exception after RPC.</td>\n<td>Above 1.0.13</td>\n</tr>\n<tr>\n<td>cache</td>\n<td>cache</td>\n<td>string/boolean</td>\n<td>False</td>\n<td></td>\n<td>Service governance</td>\n<td>lru, threadlocal, jcache等Using RPC parameters as the key to cache the result. <code>lru</code>, <code>threadlocal</code> and <code>jcache</code> are available.</td>\n<td>Above 2.1.0</td>\n</tr>\n<tr>\n<td>validation</td>\n<td>validation</td>\n<td>boolean</td>\n<td>False</td>\n<td></td>\n<td>Service governance</td>\n<td>Enable JSR303 annotation validation. If true, it will validate the method parameters' annotations.</td>\n<td>Above 2.1.0</td>\n</tr>\n<tr>\n<td>proxy</td>\n<td>proxy</td>\n<td>boolean</td>\n<td>False</td>\n<td>javassist</td>\n<td>Performance optimize</td>\n<td>The proxy implement, jdk/javassist are available.</td>\n<td>Above 2.0.2</td>\n</tr>\n<tr>\n<td>client</td>\n<td>client</td>\n<td>string</td>\n<td>False</td>\n<td></td>\n<td>Performance optimize</td>\n<td>The transport type of the client, such as netty and mina for dubbo protocol.</td>\n<td>Above 2.0.0</td>\n</tr>\n<tr>\n<td>registry</td>\n<td></td>\n<td>string</td>\n<td>False</td>\n<td>By default, it will merge all the service providers that getting from all registries</td>\n<td>Configuration association</td>\n<td>Get provider lists from the specified registry. It is the <code>id</code> value of the &lt;dubbo:registry&gt;, use <code>,</code> to separate multiple regsitries id.</td>\n<td>Above 2.0.0</td>\n</tr>\n<tr>\n<td>owner</td>\n<td>owner</td>\n<td>string</td>\n<td>False</td>\n<td></td>\n<td>Service governance</td>\n<td>The owner of the service. It's used for service governance.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>actives</td>\n<td>actives</td>\n<td>int</td>\n<td>False</td>\n<td>0</td>\n<td>Performance optimize</td>\n<td>The maximum concurrent calls per method per service of the consumer.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>cluster</td>\n<td>cluster</td>\n<td>string</td>\n<td>False</td>\n<td>failover</td>\n<td>Performance optimize</td>\n<td>failover/failfast/failsafe/failback/forking are available.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>filter</td>\n<td>reference.filter</td>\n<td>string</td>\n<td>False</td>\n<td>default</td>\n<td>Performance optimize</td>\n<td>The filter name of the RPC process of the reference, use <code>,</code> to separate multiple filter names.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>listener</td>\n<td>invoker.listener</td>\n<td>string</td>\n<td>False</td>\n<td>default</td>\n<td>Performance optimize</td>\n<td>The listener name of the reference, use <code>,</code> to separate multiple listener names.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>layer</td>\n<td>layer</td>\n<td>string</td>\n<td>False</td>\n<td></td>\n<td>Service governance</td>\n<td>The biz layer of the service provider, such as biz, dao, intl:web and china:acton.</td>\n<td>Above 2.0.7</td>\n</tr>\n<tr>\n<td>init</td>\n<td>init</td>\n<td>boolean</td>\n<td>False</td>\n<td>false</td>\n<td>Performance optimize</td>\n<td>If true, init the service reference when <code>afterPropertiesSet()</code>is invoked, or it will init later only when it is referenced and autowired.</td>\n<td>Above 2.0.10</td>\n</tr>\n<tr>\n<td>protocol</td>\n<td>protocol</td>\n<td>string</td>\n<td>False</td>\n<td></td>\n<td>Service governance</td>\n<td>Only invoke the  providers with specified protocol, and ignore other protocol providers.</td>\n<td>Above 2.2.0</td>\n</tr>\n</tbody>\n</table>\n"},{filename:"user/references/xml/dubbo-registry.md",__html:"<style>\ntable {\n  width: 100%;\n  max-width: 65em;\n  border: 1px solid #dedede;\n  margin: 15px auto;\n  border-collapse: collapse;\n  empty-cells: show;\n}\ntable th,\ntable td {\n  height: 35px;\n  border: 1px solid #dedede;\n  padding: 0 10px;\n}\ntable th {\n  font-weight: bold;\n  text-align: center !important;\n  background: rgba(158,188,226,0.2);\n  white-space: nowrap;\n}\ntable tbody tr:nth-child(2n) {\n  background: rgba(158,188,226,0.12);\n}\ntable td:nth-child(1) {\n  white-space: nowrap;\n}\ntable tr:hover {\n  background: #efefef;\n}\n.table-area {\n  overflow: auto;\n}\n</style>\n<script type=\"text/javascript\">\n[].slice.call(document.querySelectorAll('table')).forEach(function(el){\n    var wrapper = document.createElement('div');\n    wrapper.className = 'table-area';\n    el.parentNode.insertBefore(wrapper, el);\n    el.parentNode.removeChild(el);\n    wrapper.appendChild(el);\n})\n<\/script>\n<h1>dubbo:registry</h1>\n<p>The configuration of the registry center. The corresponding class is <code>com.alibaba.dubbo.config.RegistryConfig</code>. If you have multiple different registries, you can declare multiple <code>&lt;dubbo:registry&gt;</code> tags, and then reference specified registry with <code>registry</code> property in <code>&lt;dubbo:service&gt;</code> or <code>&lt;dubbo:reference&gt;</code> tag.</p>\n<table>\n<thead>\n<tr>\n<th>Attribute</th>\n<th>Corresponding URL parameter</th>\n<th>Type</th>\n<th>Required</th>\n<th>Default Value</th>\n<th>Function</th>\n<th>Description</th>\n<th>Compatibility</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>id</td>\n<td></td>\n<td>string</td>\n<td>False</td>\n<td></td>\n<td>Configuration association</td>\n<td>Bean Id of the registry center, can be referenced in &lt;dubbo:service registry=&quot;&quot;&gt;or  &lt;dubbo:reference registry=&quot;&quot;&gt;</td>\n<td>Above 1.0.16</td>\n</tr>\n<tr>\n<td>address</td>\n<td>&lt;host:port&gt;</td>\n<td>string</td>\n<td><b>True</b></td>\n<td></td>\n<td>Service discovery</td>\n<td>The address of the registry center. If the address has no port, default port 9999 will be adopted. Multiple addresses within the same cluster use <code>,</code> to seperate, such as <code>ip:port,ip:port</code>. Multiple registries within different cluster, please configure different <code>dubbo:registry</code> tag.</td>\n<td>Above 1.0.16</td>\n</tr>\n<tr>\n<td>protocol</td>\n<td>&lt;protocol&gt;</td>\n<td>string</td>\n<td>False</td>\n<td>dubbo</td>\n<td>Service discovery</td>\n<td>The protocol of the registry center. <code>dubbo</code>, <code>http</code>, <code>local</code> are available.</td>\n<td>Above 2.0.0</td>\n</tr>\n<tr>\n<td>port</td>\n<td>&lt;port&gt;</td>\n<td>int</td>\n<td>False</td>\n<td>9090</td>\n<td>Service discovery</td>\n<td>The default port of the registry. When the <code>address</code> has no port, this default port will be adopted.</td>\n<td>Above 2.0.0</td>\n</tr>\n<tr>\n<td>username</td>\n<td>&lt;username&gt;</td>\n<td>string</td>\n<td>False</td>\n<td></td>\n<td>Service governance</td>\n<td>The usename of the registry. Do not set it if the registry doesn't need validation.</td>\n<td>Above 2.0.0</td>\n</tr>\n<tr>\n<td>password</td>\n<td>&lt;password&gt;</td>\n<td>string</td>\n<td>False</td>\n<td></td>\n<td>Service governance</td>\n<td>The password of the registry. Do not set it if the registry doesn't need validation.</td>\n<td>Above 2.0.0</td>\n</tr>\n<tr>\n<td>transport</td>\n<td>registry.transporter</td>\n<td>string</td>\n<td>False</td>\n<td>netty</td>\n<td>Performance optimize</td>\n<td>mina, netty are available.</td>\n<td>Above 2.0.0</td>\n</tr>\n<tr>\n<td>timeout</td>\n<td>registry.timeout</td>\n<td>int</td>\n<td>False</td>\n<td>5000</td>\n<td>Performance optimize</td>\n<td>The timeout(ms) of the request to registry.</td>\n<td>Above 2.0.0</td>\n</tr>\n<tr>\n<td>session</td>\n<td>registry.session</td>\n<td>int</td>\n<td>False</td>\n<td>60000</td>\n<td>Performance optimize</td>\n<td>The session timeout(ms) of the registry. It's used to check whether the providers are alive. It depends on the implement of the registry. For example, for HeartBeat implement, the timeout is the interval of two heart beats.</td>\n<td>Above 2.1.0</td>\n</tr>\n<tr>\n<td>file</td>\n<td>registry.file</td>\n<td>string</td>\n<td>False</td>\n<td></td>\n<td>Service governance</td>\n<td>The local file to cache the address list of registries and providers. When application restarts, it will restore the registries and providers. Please use different file for different registy.</td>\n<td>Above 2.0.0</td>\n</tr>\n<tr>\n<td>check</td>\n<td>check</td>\n<td>boolean</td>\n<td>False</td>\n<td>true</td>\n<td>Service governance</td>\n<td>Whether throwing exception while the registry isn't existed.</td>\n<td>Above 2.0.0</td>\n</tr>\n<tr>\n<td>register</td>\n<td>register</td>\n<td>boolean</td>\n<td>False</td>\n<td>true</td>\n<td>Service governance</td>\n<td>whether registering to the registry center. If false, just subscribing, not registering.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>subscribe</td>\n<td>subscribe</td>\n<td>boolean</td>\n<td>False</td>\n<td>true</td>\n<td>Service governance</td>\n<td>whether subscribing from the registry center. If false, just registering, not subscribing.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>dynamic</td>\n<td>dynamic</td>\n<td>boolean</td>\n<td>False</td>\n<td>true</td>\n<td>Service governance</td>\n<td>Whether the service is registered dynamically. If false, services will be showed as <code>disable</code>, you need to enable it manually. And you also need to disable it when provider shut down.</td>\n<td>Above 2.0.5</td>\n</tr>\n</tbody>\n</table>\n"},{filename:"user/references/xml/dubbo-service.md",__html:"<style>\ntable {\n  width: 100%;\n  max-width: 65em;\n  border: 1px solid #dedede;\n  margin: 15px auto;\n  border-collapse: collapse;\n  empty-cells: show;\n}\ntable th,\ntable td {\n  height: 35px;\n  border: 1px solid #dedede;\n  padding: 0 10px;\n}\ntable th {\n  font-weight: bold;\n  text-align: center !important;\n  background: rgba(158,188,226,0.2);\n  white-space: nowrap;\n}\ntable tbody tr:nth-child(2n) {\n  background: rgba(158,188,226,0.12);\n}\ntable td:nth-child(1) {\n  white-space: nowrap;\n}\ntable tr:hover {\n  background: #efefef;\n}\n.table-area {\n  overflow: auto;\n}\n</style>\n<script type=\"text/javascript\">\n[].slice.call(document.querySelectorAll('table')).forEach(function(el){\n    var wrapper = document.createElement('div');\n    wrapper.className = 'table-area';\n    el.parentNode.insertBefore(wrapper, el);\n    el.parentNode.removeChild(el);\n    wrapper.appendChild(el);\n})\n<\/script>\n<h1>dubbo:service</h1>\n<p>The configuration of the service provider. The corresponding class is <code>com.alibaba.dubbo.config.ServiceConfig</code>.</p>\n<table>\n<thead>\n<tr>\n<th>Attribute</th>\n<th>Corresponding URL parameter</th>\n<th>Type</th>\n<th>Required</th>\n<th>Default Value</th>\n<th>Function</th>\n<th>Description</th>\n<th>Compatibility</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>interface</td>\n<td></td>\n<td>class</td>\n<td><b>True</b></td>\n<td></td>\n<td>Service discovery</td>\n<td>The service interface name</td>\n<td>Above 1.0.0</td>\n</tr>\n<tr>\n<td>ref</td>\n<td></td>\n<td>object</td>\n<td><b>True</b></td>\n<td></td>\n<td>Service discovery</td>\n<td>The reference to the service implementation</td>\n<td>Above 1.0.0</td>\n</tr>\n<tr>\n<td>version</td>\n<td>version</td>\n<td>string</td>\n<td>False</td>\n<td>0.0.0</td>\n<td>Service discovery</td>\n<td>Service version. It's recommended to use 2 digitals such as <code>1.0</code>. It's necessary to upgrade version only when the service is not compatible.</td>\n<td>Above 1.0.0</td>\n</tr>\n<tr>\n<td>group</td>\n<td>group</td>\n<td>string</td>\n<td>False</td>\n<td></td>\n<td>Service discovery</td>\n<td>The group of the service providers. It can distinguish services when it has multiple implements.</td>\n<td>Above 1.0.7</td>\n</tr>\n<tr>\n<td>path</td>\n<td>&lt;path&gt;</td>\n<td>string</td>\n<td>False</td>\n<td>default value is the interface name</td>\n<td>Service discovery</td>\n<td>In 1.0, service path is not supported, it's always equals to the interface name. So it may not compitable when a service reference in 1.0 calls a service provider in 2.0 that specified path.</td>\n<td>Above 1.0.12</td>\n</tr>\n<tr>\n<td>delay</td>\n<td>delay</td>\n<td>int</td>\n<td>False</td>\n<td>0</td>\n<td>Performance optimize</td>\n<td>The delay time(ms) for registering services.  When set to -1, it indicates that the services will expose to registry after the Spring context is initialized</td>\n<td>Above 1.0.14</td>\n</tr>\n<tr>\n<td>timeout</td>\n<td>timeout</td>\n<td>int</td>\n<td>False</td>\n<td>1000</td>\n<td>Performance optimize</td>\n<td>The RPC timeout(ms)</td>\n<td>Above 2.0.0</td>\n</tr>\n<tr>\n<td>retries</td>\n<td>retries</td>\n<td>int</td>\n<td>False</td>\n<td>2</td>\n<td>Performance optimize</td>\n<td>The retry count for RPC, not including the first invoke. Please set it to 0 if don't need to retry.</td>\n<td>Above 2.0.0</td>\n</tr>\n<tr>\n<td>connections</td>\n<td>connections</td>\n<td>int</td>\n<td>False</td>\n<td>100</td>\n<td>Performance optimize</td>\n<td>The maximum connections of every provider. For short connection such as rmi, http and hessian, it's connection limit, but for long connection such as dubbo, it's connection count.</td>\n<td>Above 2.0.0</td>\n</tr>\n<tr>\n<td>loadbalance</td>\n<td>loadbalance</td>\n<td>string</td>\n<td>False</td>\n<td>random</td>\n<td>Performance optimize</td>\n<td>Strategy of load balance, <code>random</code>, <code>roundrobin</code> and <code>leastactive</code> are available.</td>\n<td>Above 2.0.0</td>\n</tr>\n<tr>\n<td>async</td>\n<td>async</td>\n<td>boolean</td>\n<td>False</td>\n<td>false</td>\n<td>Performance optimize</td>\n<td>Asynchronous execution, not reliable. It does not block the execution thread just only ignores the return value.</td>\n<td>Above 2.0.0</td>\n</tr>\n<tr>\n<td>stub</td>\n<td>stub</td>\n<td>class/boolean</td>\n<td>False</td>\n<td>false</td>\n<td>Service governance</td>\n<td><code>true</code> means use the default proxy class name, which is the interface name with <code>Local</code> as the suffix. It's used to execute local logic such as caching. The proxy class must have a constructor with the remote proxy object as a parameter, such as <code>public XxxServiceLocal(XxxService xxxService)</code></td>\n<td>Above 2.0.0</td>\n</tr>\n<tr>\n<td>mock</td>\n<td>mock</td>\n<td>class/boolean</td>\n<td>False</td>\n<td>false</td>\n<td>Service governance</td>\n<td><code>true</code> means use the default mock class name, which is the interface name with <code>Mock</code> suffix. It's called when the RPC is failed, such as timeout or IO exception. The mock class must carry a  none parameter constructor. The difference between mock and local proxy is that local proxy is always invoked before RPC but mock is invoked only when exception after RPC.</td>\n<td>Above 2.0.0</td>\n</tr>\n<tr>\n<td>token</td>\n<td>token</td>\n<td>string/boolean</td>\n<td>False</td>\n<td>false</td>\n<td>Service governance</td>\n<td>Enable token validation. Disable token if it's null. It will generate token randomly when enable, or will use static token.  The token is designed to prevent consumers from bypassing the registry direct access to provider. If you want peer to peer, token validation must disbable.</td>\n<td>Above 2.0.0</td>\n</tr>\n<tr>\n<td>registry</td>\n<td></td>\n<td>string</td>\n<td>False</td>\n<td>register to all registries by default</td>\n<td>Configuration association</td>\n<td>Register services to specified registry while having multiple registries. It is the <code>id</code> value of the &lt;dubbo:registry&gt;. If don't want to register to any registry, set it as <code>N/A</code></td>\n<td>Above 2.0.0</td>\n</tr>\n<tr>\n<td>provider</td>\n<td></td>\n<td>string</td>\n<td>False</td>\n<td>use the first configured provider</td>\n<td>Configuration association</td>\n<td>The reference to <code>&lt;dubbo:provider&gt;</code></td>\n<td>Above 2.0.0</td>\n</tr>\n<tr>\n<td>deprecated</td>\n<td>deprecated</td>\n<td>boolean</td>\n<td>False</td>\n<td>false</td>\n<td>Service governance</td>\n<td>Mark the service is deprecated. If true, there will log an error log on the client side.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>dynamic</td>\n<td>dynamic</td>\n<td>boolean</td>\n<td>False</td>\n<td>true</td>\n<td>Service governance</td>\n<td>Whether the service is registered dynamically. If false, services will be showed as <code>disable</code>, you need to enable it manually. And you also need to disable it when provider shut down.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>accesslog</td>\n<td>accesslog</td>\n<td>string/boolean</td>\n<td>False</td>\n<td>false</td>\n<td>Service governance</td>\n<td><code>true</code> will write access log to logger. Specifying it to a log path, you can write access logs to special log file.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>owner</td>\n<td>owner</td>\n<td>string</td>\n<td>False</td>\n<td></td>\n<td>Service governance</td>\n<td>The owner of the service. It's used for service governance.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>document</td>\n<td>document</td>\n<td>string</td>\n<td>False</td>\n<td></td>\n<td>Service governance</td>\n<td>Service document URL</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>weight</td>\n<td>weight</td>\n<td>int</td>\n<td>False</td>\n<td></td>\n<td>Performance optimize</td>\n<td>The weight of the service</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>executes</td>\n<td>executes</td>\n<td>int</td>\n<td>False</td>\n<td>0</td>\n<td>Performance optimize</td>\n<td>The maximum parallel execution request count per method per service for the provider.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>proxy</td>\n<td>proxy</td>\n<td>string</td>\n<td>False</td>\n<td>javassist</td>\n<td>Performance optimize</td>\n<td>The proxy implement, jdk/javassist are available.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>cluster</td>\n<td>cluster</td>\n<td>string</td>\n<td>False</td>\n<td>failover</td>\n<td>Performance optimize</td>\n<td>failover/failfast/failsafe/failback/forking are available.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>filter</td>\n<td>service.filter</td>\n<td>string</td>\n<td>False</td>\n<td>default</td>\n<td>Performance optimize</td>\n<td>The filter name of the RPC process of the provider, use <code>,</code> to separate multiple filter names.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>listener</td>\n<td>exporter.listener</td>\n<td>string</td>\n<td>False</td>\n<td>default</td>\n<td>Performance optimize</td>\n<td>The listener name of the provider, use <code>,</code> to separate multiple listener names.</td>\n<td></td>\n</tr>\n<tr>\n<td>protocol</td>\n<td></td>\n<td>string</td>\n<td>False</td>\n<td></td>\n<td>Configuration association</td>\n<td>Specify the protocol for service provider. It references the <code>id</code> of <code>&lt;dubbo:protocol&gt;</code> tag. Use <code>,</code> to separate multiple protocols.</td>\n<td>Above 2.0.5</td>\n</tr>\n<tr>\n<td>layer</td>\n<td>layer</td>\n<td>string</td>\n<td>False</td>\n<td></td>\n<td>Service governance</td>\n<td>The biz layer of the service provider, such as biz, dao, intl:web and china:acton.</td>\n<td>Above 2.0.7</td>\n</tr>\n<tr>\n<td>register</td>\n<td>register</td>\n<td>boolean</td>\n<td>False</td>\n<td>true</td>\n<td>Service governance</td>\n<td>Whether registering service providers to registry.</td>\n<td>Above 2.0.8</td>\n</tr>\n</tbody>\n</table>\n"},{filename:"user/references/xml/introduction.md",__html:'<h1>schema configuration reference</h1>\n<p>The following pages show all the configuration properties <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> with XML Config <sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup> as an example.  For other configurations, please reference: <a href="../../configuration/properties.md">Properties Configuration</a>, <a href="../../configuration/annotation.md">Annotation Configuration</a>, <a href="../../configuration/api.md">API Configuration</a>.</p>\n<p>All configuration properties fall into three categories, see the &quot;Function&quot; in the table below.</p>\n<ul>\n<li>Service discovery: used for service registration and discovery in order to find providers for consumers.</li>\n<li>Service governance: used for service management and governance, such as to provide conveninence for dev or test.</li>\n<li>Performance optinize: used for optimizing performance. Diffenent properties may has different performance impact.</li>\n<li>All properties will transform into URL <sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup>  which is generated by provider. The url will be subscribed by consumers through registry. Please see the <code>Corresponding URL parameter</code> in the table below for each property.</li>\n</ul>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>Notice: These three properties, group, interface, and version determine a service. All other properties are used for service governance or performance optimize. <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p>XML Schema: <a href="http://dubbo.apache.org/schema/dubbo/dubbo.xsd">http://dubbo.apache.org/schema/dubbo/dubbo.xsd</a> <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn3" class="footnote-item"><p>URL format:<code>protocol://username:password@host:port/path?key=value&amp;key=value</code> <a href="#fnref3" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"user/simple-monitor.md",__html:'<blockquote>\n<p><img src="sources/images/check.gif" alt="warning">Monitor service is a standard Dubbo service,can be exported to the registry,also can be connected straightly。</p>\n</blockquote>\n<blockquote>\n<p><img src="sources/images/check.gif" alt="warning">[Install the simple registry](admin-guide-install-manual#Install the simple registry)</p>\n</blockquote>\n<ol start="0">\n<li>\n<p>export a simple monitor service to the registry: (If you use the installer, you don\'t need to write this configuration yourself. if you implement the monitor service yourself,need it)</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">beans</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans"</span>\n<span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>\n<span class="hljs-attr">xmlns:dubbo</span>=<span class="hljs-string">"http://dubbo.apache.org/schema/dubbo"</span>\n<span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"</span>&gt;</span>\n \n<span class="hljs-comment">&lt;!-- configuration of current application --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:application</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"simple-monitor"</span> /&gt;</span>\n \n<span class="hljs-comment">&lt;!-- connection address of the registry --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:registry</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"127.0.0.1:9090"</span> /&gt;</span>\n \n<span class="hljs-comment">&lt;!-- protool configuration of exposed services --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"7070"</span> /&gt;</span>\n \n<span class="hljs-comment">&lt;!-- configuration of certain exposed service --&gt;</span>\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.monitor.MonitorService"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"monitorService"</span> /&gt;</span>\n \n<span class="hljs-tag">&lt;<span class="hljs-name">bean</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"monitorService"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"com.alibaba.dubbo.monitor.simple.SimpleMonitorService"</span> /&gt;</span>\n<span class="hljs-tag">&lt;/<span class="hljs-name">beans</span>&gt;</span>\n</code></pre>\n</li>\n<li>\n<p>Discovery the monitor service int the registry:</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:monitor</span> <span class="hljs-attr">protocol</span>=<span class="hljs-string">"registry"</span> /&gt;</span>\n</code></pre>\n<p>or</p>\n<blockquote>\n<p>dubbo.properties</p>\n</blockquote>\n<pre><code class="language-xml">dubbo.monitor.protocol=registry\n</code></pre>\n</li>\n<li>\n<p>Export a simple monitor service ,but don\'t register it to th registry: (If you use the installer, you don\'t need to write this configuration yourself. if you implement the monitor service yourself,need it)</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">beans</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans"</span>\n<span class="hljs-attr">xmlns:xsi</span>=<span class="hljs-string">"http://www.w3.org/2001/XMLSchema-instance"</span>\n<span class="hljs-attr">xmlns:dubbo</span>=<span class="hljs-string">"http://dubbo.apache.org/schema/dubbo"</span>\n<span class="hljs-attr">xsi:schemaLocation</span>=<span class="hljs-string">"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"</span>&gt;</span>\n \n<span class="hljs-comment">&lt;!-- configuration of current application --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:application</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"simple-monitor"</span> /&gt;</span>\n \n<span class="hljs-comment">&lt;!-- protool configuration of exposed service --&gt;</span>\n<span class="hljs-tag">&lt;<span class="hljs-name">dubbo:protocol</span> <span class="hljs-attr">port</span>=<span class="hljs-string">"7070"</span> /&gt;</span>\n \n<span class="hljs-comment">&lt;!-- configuration of exposed service --&gt;</span>\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.monitor.MonitorService"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"monitorService"</span> <span class="hljs-attr">registry</span>=<span class="hljs-string">"N/A"</span> /&gt;</span>\n \n<span class="hljs-tag">&lt;<span class="hljs-name">bean</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"monitorService"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"com.alibaba.dubbo.monitor.simple.SimpleMonitorService"</span> /&gt;</span>   \n<span class="hljs-tag">&lt;/<span class="hljs-name">beans</span>&gt;</span>\n</code></pre>\n</li>\n<li>\n<p>connected to the monitor service straightly</p>\n<pre><code class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">dubbo:monitor</span> <span class="hljs-attr">address</span>=<span class="hljs-string">"dubbo://127.0.0.1:7070/com.alibaba.dubbo.monitor.MonitorService"</span> /&gt;</span>\n</code></pre>\n<p>or:</p>\n<pre><code class="language-sh">&lt;dubbo:monitor address=<span class="hljs-string">"127.0.0.1:7070"</span> /&gt;\n</code></pre>\n<p>or:</p>\n<p><strong>dubbo.properties</strong></p>\n<pre><code class="language-sh">dubbo.monitor.address=127.0.0.1:7070\n</code></pre>\n</li>\n</ol>\n'}]}},,,function(s,n,a){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default={"en-us":{sidemenu:[{title:"User doc",children:[{title:"Preface",children:[{title:"Background",link:"/docs/user/preface/background.md"},{title:"Requirements",link:"/docs/user/preface/requirements.md"},{title:"Architecture",link:"/docs/user/preface/architecture.md"},{title:"Usage",link:"/docs/user/preface/usage.md"}]},{title:"Quick start",link:"/docs/user/quick-start.md"},{title:"Dependencies",link:"/docs/user/dependencies.md"},{title:"Maturality",link:"/docs/user/maturity.md"},{title:"Configuration",children:[{title:"XML configuration",link:"/docs/user/configuration/xml.md"},{title:"Properties configuration",link:"/docs/user/configuration/properties.md"},{title:"API configuration",link:"/docs/user/configuration/api.md"},{title:"Annotation configuration",link:"/docs/user/configuration/annotation.md"}]},{title:"Demos",children:[{title:"Start check",link:"/docs/user/demos/preflight-check.md"},{title:"Fault-tolerent strategy",link:"/docs/user/demos/fault-tolerent-strategy.md"},{title:"Load balance",link:"/docs/user/demos/loadbalance.md"},{title:"Thread model",link:"/docs/user/demos/thread-model.md"},{title:"Connecting certain provider straightly",link:"/docs/user/demos/explicit-target.md"},{title:"Subscribe only",link:"/docs/user/demos/subscribe-only.md"},{title:"Registry only",link:"/docs/user/demos/registry-only.md"},{title:"Static service",link:"/docs/user/demos/static-service.md"},{title:"Multi-protocols",link:"/docs/user/demos/multi-protocols.md"},{title:"Multi-registries",link:"/docs/user/demos/multi-registry.md"},{title:"Service group",link:"/docs/user/demos/service-group.md"},{title:"Multi-versions",link:"/docs/user/demos/multi-versions.md"},{title:"Group merger",link:"/docs/user/demos/group-merger.md"},{title:"Parameter validation",link:"/docs/user/demos/parameter-validation.md"},{title:"Result cache",link:"/docs/user/demos/result-cache.md"},{title:"Generic reference",link:"/docs/user/demos/generic-reference.md"},{title:"Generic service",link:"/docs/user/demos/generic-service.md"},{title:"Echo service",link:"/docs/user/demos/echo-service.md"},{title:"Context",link:"/docs/user/demos/context.md"},{title:"Attachment",link:"/docs/user/demos/attachment.md"},{title:"Asynchronous call",link:"/docs/user/demos/async-call.md"},{title:"Local call",link:"/docs/user/demos/local-call.md"},{title:"Callback parameter",link:"/docs/user/demos/callback-parameter.md"},{title:"Events notify",link:"/docs/user/demos/events-notify.md"},{title:"Local stub",link:"/docs/user/demos/local-stub.md"},{title:"Local mock",link:"/docs/user/demos/local-mock.md"},{title:"Delay publish",link:"/docs/user/demos/delay-publish.md"},{title:"Concurrency control",link:"/docs/user/demos/concurrency-control.md"},{title:"Connections limitation",link:"/docs/user/demos/config-connections.md"},{title:"Lazy connect",link:"/docs/user/demos/lazy-connect.md"},{title:"Stickness connections",link:"/docs/user/demos/stickiness.md"},{title:"Token authorization",link:"/docs/user/demos/token-authorization.md"},{title:"Routing rule",link:"/docs/user/demos/routing-rule.md"},{title:"Configuration rule",link:"/docs/user/demos/config-rule.md"},{title:"Service downgrade",link:"/docs/user/demos/service-downgrade.md"},{title:"Graceful shutdown",link:"/docs/user/demos/graceful-shutdown.md"},{title:"Hostname binding",link:"/docs/user/demos/hostname-binding.md"},{title:"Logger strategy",link:"/docs/user/demos/logger-strategy.md"},{title:"Accesslog",link:"/docs/user/demos/accesslog.md"},{title:"Service container",link:"/docs/user/demos/service-container.md"},{title:"Reference config cache",link:"/docs/user/demos/reference-config-cache.md"},{title:"Distributed transaction",link:"/docs/user/demos/distributed-transaction.md"},{title:"Automatic thread dump",link:"/docs/user/demos/dump.md"},{title:"Netty4",link:"/docs/user/demos/netty4.md"}]},{title:"API configuration reference",link:"/docs/user/references/api.md"},{title:"Schema configuration reference",children:[{title:"Introduction",link:"/docs/user/references/xml/introduction.md"},{title:"dubbo:service",link:"/docs/user/references/xml/dubbo-service.md"},{title:"dubbo:reference",link:"/docs/user/references/xml/dubbo-reference.md"},{title:"dubbo:protocol",link:"/docs/user/references/xml/dubbo-protocol.md"},{title:"dubbo:registry",link:"/docs/user/references/xml/dubbo-registry.md"},{title:"dubbo:monitor",link:"/docs/user/references/xml/dubbo-monitor.md"},{title:"dubbo:application",link:"/docs/user/references/xml/dubbo-application.md"},{title:"dubbo:module",link:"/docs/user/references/xml/dubbo-module.md"},{title:"dubbo:provider",link:"/docs/user/references/xml/dubbo-provider.md"},{title:"dubbo:consumer",link:"/docs/user/references/xml/dubbo-consumer.md"},{title:"dubbo:method",link:"/docs/user/references/xml/dubbo-method.md"},{title:"dubbo:argument",link:"/docs/user/references/xml/dubbo-argument.md"},{title:"dubbo:parameter",link:"/docs/user/references/xml/dubbo-parameter.md"}]},{title:"Protocol configuration reference",children:[{title:"Introduction",link:"/docs/user/references/protocol/introduction.md"},{title:"dubbo://",link:"/docs/user/references/protocol/dubbo.md"},{title:"rmi://",link:"/docs/user/references/protocol/rmi.md"},{title:"hessian://",link:"/docs/user/references/protocol/hessian.md"},{title:"http://",link:"/docs/user/references/protocol/http.md"},{title:"webservice://",link:"/docs/user/references/protocol/webservice.md"},{title:"thrift://",link:"/docs/user/references/protocol/thrift.md"},{title:"memcached://",link:"/docs/user/references/protocol/memcached.md"},{title:"redis://",link:"/docs/user/references/protocol/redis.md"},{title:"rest://",link:"/docs/user/references/protocol/rest.md"}]},{title:"Registry configuration reference",children:[{title:"Introduction",link:"/docs/user/references/registry/introduction.md"},{title:"Multicast registry",link:"/docs/user/references/registry/multicast.md"},{title:"Zookeeper registry",link:"/docs/user/references/registry/zookeeper.md"},{title:"Redis registry",link:"/docs/user/references/registry/redis.md"},{title:"Simple registry",link:"/docs/user/references/registry/simple.md"}]},{title:"Telnet command",link:"/docs/user/references/telnet.md"},{title:"Maven plugin",link:"/docs/user/references/maven.md"},{title:"Best practice",link:"/docs/user/best-practice.md"},{title:"Recommended usage",link:"/docs/user/recommend.md"},{title:"Capacity plan",link:"/docs/user/capacity-plan.md"},{title:"Performance testing reports",link:"/docs/user/perf-test.md"},{title:"Test coverage report",link:"/docs/user/coveragence.md"}]},{title:"Developer guide",children:[{title:"How To Build",link:"/docs/dev/build.md"},{title:"Architecture",link:"/docs/dev/design.md"},{title:"How SPI Works",link:"/docs/dev/SPI.md"},{title:"Init, Process, Protocols",link:"/docs/dev/implementation.md"},{title:"SPI Extensions",children:[{title:"Protocol",link:"/docs/dev/impls/protocol.md"},{title:"Filter",link:"/docs/dev/impls/filter.md"},{title:"InvokerListener",link:"/docs/dev/impls/invoker-listener.md"},{title:"ExporterListener",link:"/docs/dev/impls/exporter-listener.md"},{title:"Cluster",link:"/docs/dev/impls/cluster.md"},{title:"Router",link:"/docs/dev/impls/router.md"},{title:"LoadBalance",link:"/docs/dev/impls/load-balance.md"},{title:"Merger",link:"/docs/dev/impls/merger.md"},{title:"Registry",link:"/docs/dev/impls/registry.md"},{title:"Monitor",link:"/docs/dev/impls/monitor.md"},{title:"ExtensionFactory",link:"/docs/dev/impls/extension-factory.md"},{title:"ProxyFactory",link:"/docs/dev/impls/proxy-factory.md"},{title:"Compiler",link:"/docs/dev/impls/compiler.md"},{title:"Dispatcher",link:"/docs/dev/impls/dispatcher.md"},{title:"Threadpool",link:"/docs/dev/impls/threadpool.md"},{title:"Serialization",link:"/docs/dev/impls/serialize.md"},{title:"Remoting",link:"/docs/dev/impls/remoting.md"},{title:"Exchanger",link:"/docs/dev/impls/exchanger.md"},{title:"Networker",link:"/docs/dev/impls/networker.md"},{title:"TelnetHandler",link:"/docs/dev/impls/telnet-handler.md"},{title:"StatusChecker",link:"/docs/dev/impls/status-checker.md"},{title:"Container",link:"/docs/dev/impls/container.md"},{title:"PageHandler",link:"/docs/dev/impls/page.md"},{title:"Cache",link:"/docs/dev/impls/cache.md"},{title:"Validation",link:"/docs/dev/impls/validation.md"},{title:"LoggerAdapter",link:"/docs/dev/impls/logger-adapter.md"}]},{title:"Contract",link:"/docs/dev/contract.md"},{title:"Code Style",link:"/docs/dev/coding.md"},{title:"Versions",link:"/docs/dev/release.md"},{title:"Contribution",link:"/docs/dev/contribution.md"},{title:"Checklist",link:"/docs/dev/checklist.md"},{title:"Code Smell",link:"/docs/dev/code-smell.md"},{title:"TCK",link:"/docs/dev/TCK.md"}]},{title:"Admin guide",children:[{title:"Installation",children:[{title:"Install provider demo",link:"/docs/admin/install/provider-demo.md"},{title:"Install consumer demo",link:"/docs/admin/install/consumer-demo.md"},{title:"Install Zookeeper configuration center",link:"/docs/admin/install/zookeeper.md"},{title:"Install Redis configuration center",link:"/docs/admin/install/redis.md"},{title:"Install Simple configuration center",link:"/docs/admin/install/simple-registry-center.md"},{title:"Install Simple monitor center",link:"/docs/admin/install/simple-monitor-center.md"},{title:"Install admin console",link:"/docs/admin/install/admin-console.md"}]},{title:"Operation manual",children:[{title:"Admin console operation guide",link:"/docs/admin/ops/dubbo-ops.md"},{title:"Tracking with Pinpoint",link:"/docs/admin/ops/pinpoint.md"}]}]}],barText:"Documentation"},"zh-cn":{sidemenu:[{title:"用户文档",children:[{title:"入门",children:[{title:"背景",link:"/docs/user/preface/background.md"},{title:"需求",link:"/docs/user/preface/requirements.md"},{title:"架构",link:"/docs/user/preface/architecture.md"},{title:"用法",link:"/docs/user/preface/usage.md"}]},{title:"快速启动",link:"/docs/user/quick-start.md"},{title:"依赖",link:"/docs/user/dependencies.md"},{title:"成熟度",link:"/docs/user/maturity.md"},{title:"配置",children:[{title:"XML配置",link:"/docs/user/configuration/xml.md"},{title:"属性配置",link:"/docs/user/configuration/properties.md"},{title:"API配置",link:"/docs/user/configuration/api.md"},{title:"注解配置",link:"/docs/user/configuration/annotation.md"}]},{title:"示例",children:[{title:"启动时检查",link:"/docs/user/demos/preflight-check.md"},{title:"集群容错",link:"/docs/user/demos/fault-tolerent-strategy.md"},{title:"负载均衡",link:"/docs/user/demos/loadbalance.md"},{title:"线程模型",link:"/docs/user/demos/thread-model.md"},{title:"直连提供者",link:"/docs/user/demos/explicit-target.md"},{title:"只订阅",link:"/docs/user/demos/subscribe-only.md"},{title:"只注册",link:"/docs/user/demos/registry-only.md"},{title:"静态服务",link:"/docs/user/demos/static-service.md"},{title:"多协议",link:"/docs/user/demos/multi-protocols.md"},{title:"多注册中心",link:"/docs/user/demos/multi-registry.md"},{title:"服务分组",link:"/docs/user/demos/service-group.md"},{title:"多版本",link:"/docs/user/demos/multi-versions.md"},{title:"分组聚合",link:"/docs/user/demos/group-merger.md"},{title:"参数验证",link:"/docs/user/demos/parameter-validation.md"},{title:"结果缓存",link:"/docs/user/demos/result-cache.md"},{title:"泛化引用",link:"/docs/user/demos/generic-reference.md"},{title:"泛化实现",link:"/docs/user/demos/generic-service.md"},{title:"回声测试",link:"/docs/user/demos/echo-service.md"},{title:"上下文信息",link:"/docs/user/demos/context.md"},{title:"隐式参数",link:"/docs/user/demos/attachment.md"},{title:"异步调用",link:"/docs/user/demos/async-call.md"},{title:"本地调用",link:"/docs/user/demos/local-call.md"},{title:"参数回调",link:"/docs/user/demos/callback-parameter.md"},{title:"事件通知",link:"/docs/user/demos/events-notify.md"},{title:"本地存根",link:"/docs/user/demos/local-stub.md"},{title:"本地伪装",link:"/docs/user/demos/local-mock.md"},{title:"延迟暴露",link:"/docs/user/demos/delay-publish.md"},{title:"并发控制",link:"/docs/user/demos/concurrency-control.md"},{title:"连接控制",link:"/docs/user/demos/config-connections.md"},{title:"延迟连接",link:"/docs/user/demos/lazy-connect.md"},{title:"粘滞连接",link:"/docs/user/demos/stickiness.md"},{title:"令牌验证",link:"/docs/user/demos/token-authorization.md"},{title:"路由规则",link:"/docs/user/demos/routing-rule.md"},{title:"配置规则",link:"/docs/user/demos/config-rule.md"},{title:"服务降级",link:"/docs/user/demos/service-downgrade.md"},{title:"优雅停机",link:"/docs/user/demos/graceful-shutdown.md"},{title:"主机绑定",link:"/docs/user/demos/hostname-binding.md"},{title:"日志适配",link:"/docs/user/demos/logger-strategy.md"},{title:"访问日志",link:"/docs/user/demos/accesslog.md"},{title:"服务容器",link:"/docs/user/demos/service-container.md"},{title:"Reference Config 缓存",link:"/docs/user/demos/reference-config-cache.md"},{title:"分布式事务",link:"/docs/user/demos/distributed-transaction.md"},{title:"线程栈自动dump",link:"/docs/user/demos/dump.md"},{title:"Netty4",link:"/docs/user/demos/netty4.md"},{title:"Kryo和FST序列化",link:"/docs/user/demos/serialization.md"}]},{title:"API配置参考手册",link:"/docs/user/references/api.md"},{title:"schema配置参考手册",children:[{title:"介绍",link:"/docs/user/references/xml/introduction.md"},{title:"dubbo:service",link:"/docs/user/references/xml/dubbo-service.md"},{title:"dubbo:reference",link:"/docs/user/references/xml/dubbo-reference.md"},{title:"dubbo:protocol",link:"/docs/user/references/xml/dubbo-protocol.md"},{title:"dubbo:registry",link:"/docs/user/references/xml/dubbo-registry.md"},{title:"dubbo:monitor",link:"/docs/user/references/xml/dubbo-monitor.md"},{title:"dubbo:application",link:"/docs/user/references/xml/dubbo-application.md"},{title:"dubbo:module",link:"/docs/user/references/xml/dubbo-module.md"},{title:"dubbo:provider",link:"/docs/user/references/xml/dubbo-provider.md"},{title:"dubbo:consumer",link:"/docs/user/references/xml/dubbo-consumer.md"},{title:"dubbo:method",link:"/docs/user/references/xml/dubbo-method.md"},{title:"dubbo:argument",link:"/docs/user/references/xml/dubbo-argument.md"},{title:"dubbo:parameter",link:"/docs/user/references/xml/dubbo-parameter.md"}]},{title:"协议参考手册",children:[{title:"介绍",link:"/docs/user/references/protocol/introduction.md"},{title:"dubbo://",link:"/docs/user/references/protocol/dubbo.md"},{title:"rmi://",link:"/docs/user/references/protocol/rmi.md"},{title:"hessian://",link:"/docs/user/references/protocol/hessian.md"},{title:"http://",link:"/docs/user/references/protocol/http.md"},{title:"webservice://",link:"/docs/user/references/protocol/webservice.md"},{title:"thrift://",link:"/docs/user/references/protocol/thrift.md"},{title:"memcached://",link:"/docs/user/references/protocol/memcached.md"},{title:"redis://",link:"/docs/user/references/protocol/redis.md"},{title:"rest://",link:"/docs/user/references/protocol/rest.md"}]},{title:"注册中心参考手册",children:[{title:"介绍",link:"/docs/user/references/registry/introduction.md"},{title:"Multicast 注册中心",link:"/docs/user/references/registry/multicast.md"},{title:"Zookeeper 注册中心",link:"/docs/user/references/registry/zookeeper.md"},{title:"Redis 注册中心",link:"/docs/user/references/registry/redis.md"},{title:"Simple 注册中心",link:"/docs/user/references/registry/simple.md"}]},{title:"telnet命令参考手册",link:"/docs/user/references/telnet.md"},{title:"在线运维命令-QOS",link:"/docs/user/references/qos.md"},{title:"maven插件参考手册",link:"/docs/user/references/maven.md"},{title:"服务化最佳实践",link:"/docs/user/best-practice.md"},{title:"推荐用法",link:"/docs/user/recommend.md"},{title:"容量规划",link:"/docs/user/capacity-plan.md"},{title:"性能测试报告",link:"/docs/user/perf-test.md"},{title:"测试覆盖率报告",link:"/docs/user/coveragence.md"}]},{title:"开发者指南",children:[{title:"源码构建",link:"/docs/dev/build.md"},{title:"框架设计",link:"/docs/dev/design.md"},{title:"扩展点加载",link:"/docs/dev/SPI.md"},{title:"实现细节",link:"/docs/dev/implementation.md"},{title:"SPI 扩展实现",children:[{title:"协议扩展",link:"/docs/dev/impls/protocol.md"},{title:"调用拦截扩展",link:"/docs/dev/impls/filter.md"},{title:"引用监听扩展",link:"/docs/dev/impls/invoker-listener.md"},{title:"暴露监听扩展",link:"/docs/dev/impls/exporter-listener.md"},{title:"集群扩展",link:"/docs/dev/impls/cluster.md"},{title:"路由扩展",link:"/docs/dev/impls/router.md"},{title:"负载均衡扩展",link:"/docs/dev/impls/load-balance.md"},{title:"合并结果扩展",link:"/docs/dev/impls/merger.md"},{title:"注册中心扩展",link:"/docs/dev/impls/registry.md"},{title:"监控中心扩展",link:"/docs/dev/impls/monitor.md"},{title:"扩展点加载扩展",link:"/docs/dev/impls/extension-factory.md"},{title:"动态代理扩展",link:"/docs/dev/impls/proxy-factory.md"},{title:"编译器扩展",link:"/docs/dev/impls/compiler.md"},{title:"消息派发扩展",link:"/docs/dev/impls/dispatcher.md"},{title:"线程池扩展",link:"/docs/dev/impls/threadpool.md"},{title:"序列化扩展",link:"/docs/dev/impls/serialize.md"},{title:"网络传输扩展",link:"/docs/dev/impls/remoting.md"},{title:"信息交换扩展",link:"/docs/dev/impls/exchanger.md"},{title:"组网扩展",link:"/docs/dev/impls/networker.md"},{title:"Telnet 命令扩展",link:"/docs/dev/impls/telnet-handler.md"},{title:"状态检查扩展",link:"/docs/dev/impls/status-checker.md"},{title:"容器扩展",link:"/docs/dev/impls/container.md"},{title:"页面扩展",link:"/docs/dev/impls/page.md"},{title:"缓存扩展",link:"/docs/dev/impls/cache.md"},{title:"验证扩展",link:"/docs/dev/impls/validation.md"},{title:"日志适配扩展",link:"/docs/dev/impls/logger-adapter.md"}]},{title:"公共契约",link:"/docs/dev/contract.md"},{title:"编码约定",link:"/docs/dev/coding.md"},{title:"设计原则",children:[{title:"魔鬼在细节",link:"/docs/dev/principals/code-detail.md"},{title:"一些设计上的基本常识",link:"/docs/dev/principals/general-knowledge.md"},{title:"谈谈扩充式扩展与增量式扩展",link:"/docs/dev/principals/expansibility.md"},{title:"配置设计",link:"/docs/dev/principals/configuration.md"},{title:"设计实现的健壮性",link:"/docs/dev/principals/robustness.md"},{title:"防痴呆设计",link:"/docs/dev/principals/dummy.md"},{title:"扩展点重构",link:"/docs/dev/principals/extension.md"}]},{title:"版本管理",link:"/docs/dev/release.md"},{title:"贡献",link:"/docs/dev/contribution.md"},{title:"检查列表",link:"/docs/dev/checklist.md"},{title:"坏味道",link:"/docs/dev/code-smell.md"},{title:"技术兼容性测试",link:"/docs/dev/TCK.md"}]},{title:"运维管理",children:[{title:"安装手册",children:[{title:"示例提供者安装",link:"/docs/admin/install/provider-demo.md"},{title:"示例消费者安装",link:"/docs/admin/install/consumer-demo.md"},{title:"Zookeeper 注册中心安装",link:"/docs/admin/install/zookeeper.md"},{title:"Redis 注册中心安装",link:"/docs/admin/install/redis.md"},{title:"Simple 注册中心安装",link:"/docs/admin/install/simple-registry-center.md"},{title:"Simple 监控中心安装",link:"/docs/admin/install/simple-monitor-center.md"},{title:"管理控制台安装",link:"/docs/admin/install/admin-console.md"}]},{title:"运维手册",children:[{title:"管理控制台运维",link:"/docs/admin/ops/dubbo-ops.md"},{title:"使用Pinpoint做分布式跟踪",link:"/docs/admin/ops/pinpoint.md"}]}]}],barText:"文档"}}},,,function(s,n,a){"use strict";function e(s){return s&&s.__esModule?s:{default:s}}function t(s,n){if(!(s instanceof n))throw new TypeError("Cannot call a class as a function")}function l(s,n){if(!s)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!n||"object"!=typeof n&&"function"!=typeof n?s:n}function o(s,n){if("function"!=typeof n&&null!==n)throw new TypeError("Super expression must either be null or a function, not "+typeof n);s.prototype=Object.create(n&&n.prototype,{constructor:{value:s,enumerable:!1,writable:!0,configurable:!0}}),n&&(Object.setPrototypeOf?Object.setPrototypeOf(s,n):s.__proto__=n)}Object.defineProperty(n,"__esModule",{value:!0}),n.default=void 0;var r,p=function(){function s(s,n){for(var a=0;a<n.length;a++){var e=n[a];e.enumerable=e.enumerable||!1,e.configurable=!0,"value"in e&&(e.writable=!0),Object.defineProperty(s,e.key,e)}}return function(n,a,e){return a&&s(n.prototype,a),e&&s(n,e),n}}(),c=a(0),i=e(c),d=a(19),h=e(d),u=a(16),g=a(83),m=e(g);a(92);var b=(r=function(s){function n(s){t(this,n);var a=l(this,(n.__proto__||Object.getPrototypeOf(n)).call(this,s));return a.state={menuBodyVisible:!1},a}return o(n,s),p(n,[{key:"toggleMenuBody",value:function(){this.setState({menuBodyVisible:!this.state.menuBodyVisible})}},{key:"render",value:function(){var s=this,n=this.props.dataSource,a=this.state.menuBodyVisible,e=(0,h.default)({sidemenu:!0,"sidemenu-open":a}),t=(0,h.default)({"menu-item":!0,"menu-item-level-1":!0});return i.default.createElement("div",{className:e},i.default.createElement("div",{onClick:this.toggleMenuBody,className:"sidemenu-toggle"},i.default.createElement("img",{src:a?"https://img.alicdn.com/tfs/TB1I5itXQyWBuNjy0FpXXassXXa-200-200.png":"https://img.alicdn.com/tfs/TB1E6apXHGYBuNjy0FoXXciBFXa-200-200.png"})),i.default.createElement("ul",null,n.map(function(n,a){return i.default.createElement("li",{className:t,key:a},i.default.createElement("span",null,n.title),i.default.createElement("ul",null,n.children.map(function(n,a){return i.default.createElement(m.default,{item:n,key:a,toggleMenuBody:s.toggleMenuBody})})))})))}}]),n}(i.default.Component),function(s,n,a,e,t){var l={};return Object.keys(e).forEach(function(s){l[s]=e[s]}),l.enumerable=!!l.enumerable,l.configurable=!!l.configurable,("value"in l||l.initializer)&&(l.writable=!0),l=a.slice().reverse().reduce(function(a,e){return e(s,n,a)||a},l),t&&void 0!==l.initializer&&(l.value=l.initializer?l.initializer.call(t):void 0,l.initializer=void 0),void 0===l.initializer&&(Object.defineProperty(s,n,l),l=null),l}(r.prototype,"toggleMenuBody",[u.autobind],Object.getOwnPropertyDescriptor(r.prototype,"toggleMenuBody"),r.prototype),r);n.default=b},function(s,n,a){"use strict";function e(s){return s&&s.__esModule?s:{default:s}}function t(s,n){if(!(s instanceof n))throw new TypeError("Cannot call a class as a function")}function l(s,n){if(!s)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!n||"object"!=typeof n&&"function"!=typeof n?s:n}function o(s,n){if("function"!=typeof n&&null!==n)throw new TypeError("Super expression must either be null or a function, not "+typeof n);s.prototype=Object.create(n&&n.prototype,{constructor:{value:s,enumerable:!1,writable:!0,configurable:!0}}),n&&(Object.setPrototypeOf?Object.setPrototypeOf(s,n):s.__proto__=n)}function r(s,n,a,e,t){var l={};return Object.keys(e).forEach(function(s){l[s]=e[s]}),l.enumerable=!!l.enumerable,l.configurable=!!l.configurable,("value"in l||l.initializer)&&(l.writable=!0),l=a.slice().reverse().reduce(function(a,e){return e(s,n,a)||a},l),t&&void 0!==l.initializer&&(l.value=l.initializer?l.initializer.call(t):void 0,l.initializer=void 0),void 0===l.initializer&&(Object.defineProperty(s,n,l),l=null),l}Object.defineProperty(n,"__esModule",{value:!0}),n.default=void 0;var p,c=function(){function s(s,n){for(var a=0;a<n.length;a++){var e=n[a];e.enumerable=e.enumerable||!1,e.configurable=!0,"value"in e&&(e.writable=!0),Object.defineProperty(s,e.key,e)}}return function(n,a,e){return a&&s(n.prototype,a),e&&s(n,e),n}}(),i=a(0),d=e(i),h=a(1),u=a(16),g=a(19),m=e(g),b=(p=function(s){function n(s){t(this,n);var a=l(this,(n.__proto__||Object.getPrototypeOf(n)).call(this,s)),e=s.item,o=e.children&&e.children.length,r=s.item.opened;return o?void 0===r&&(r=e.children.find(function(s){return s.link===window.location.hash.split("?")[0].slice(1)})):r=!1,a.state={opened:r},a}return o(n,s),c(n,[{key:"onItemClick",value:function(s){this.props.toggleMenuBody(),s.stopPropagation()}},{key:"toggle",value:function(){this.setState({opened:!this.state.opened})}},{key:"renderSubMenu",value:function(s){var n=this;return d.default.createElement("ul",null,s.map(function(s,a){return d.default.createElement("li",{className:(0,m.default)({"menu-item":!0,"menu-item-level-3":!0,"menu-item-selected":s.link===window.location.hash.split("?")[0].slice(1)}),key:a,onClick:n.onItemClick},d.default.createElement(h.Link,{to:s.link},s.title))}))}},{key:"render",value:function(){var s=this.props.item,n=s.children&&s.children.length,a=this.state.opened,e=(0,m.default)({"menu-item":!0,"menu-item-level-2":!0,"menu-item-selected":s.link===window.location.hash.split("?")[0].slice(1)}),t={height:a?36*(s.children.length+1):36,overflow:"hidden"};return n?d.default.createElement("li",{style:t,className:e,onClick:this.toggle},d.default.createElement("span",null,s.title,d.default.createElement("img",{style:{transform:"rotate("+(a?0:-90)+"deg)"},className:"menu-toggle",src:"./img/arrow_down.png"})),this.renderSubMenu(s.children)):d.default.createElement("li",{style:t,className:e,onClick:this.onItemClick},d.default.createElement(h.Link,{to:s.link},s.title))}}]),n}(d.default.Component),r(p.prototype,"onItemClick",[u.autobind],Object.getOwnPropertyDescriptor(p.prototype,"onItemClick"),p.prototype),r(p.prototype,"toggle",[u.autobind],Object.getOwnPropertyDescriptor(p.prototype,"toggle"),p.prototype),r(p.prototype,"renderSubMenu",[u.autobind],Object.getOwnPropertyDescriptor(p.prototype,"renderSubMenu"),p.prototype),p);n.default=b},,,,,,,,,function(s,n,a){a(14)(a(101))},,,,,,function(s,n,a){a(14)(a(107))},,,function(s,n){s.exports=".sidemenu {\n  background: #f8f8f8;\n  width: 295px;\n  position: relative;\n  display: inline-block;\n  padding: 22px 0; }\n  .sidemenu .sidemenu-toggle {\n    text-align: center;\n    cursor: pointer;\n    position: absolute;\n    top: 0;\n    width: 40px;\n    right: -40px;\n    height: 30px;\n    line-height: 30px;\n    background: #f8f8f8;\n    border-radius: 0 4px 4px 0;\n    display: none; }\n    .sidemenu .sidemenu-toggle img {\n      width: 16px;\n      text-align: center;\n      vertical-align: middle; }\n  .sidemenu ul {\n    list-style: none;\n    padding: 0;\n    margin: 0; }\n  .sidemenu li {\n    line-height: 0; }\n  .sidemenu span, .sidemenu a {\n    box-sizing: border-box;\n    display: inline-block;\n    position: relative;\n    width: 100%;\n    overflow-x: hidden;\n    text-overflow: ellipsis;\n    white-space: nowrap; }\n  .sidemenu .menu-item-selected a {\n    background: white; }\n  .sidemenu .menu-item-selected a::before {\n    content: '';\n    position: absolute;\n    left: 0;\n    top: 0;\n    width: 4px;\n    height: 100%;\n    background-image: linear-gradient(0deg, #03DDE4 0%, #30AFED 51%, #8755FF 100%); }\n  .sidemenu .menu-item-level-1 > span {\n    font-family: Avenir-Heavy;\n    font-size: 18px;\n    color: #333;\n    padding-left: 20px;\n    height: 40px;\n    line-height: 40px; }\n  .sidemenu .menu-item-level-2, .sidemenu .menu-item-level-3 {\n    cursor: pointer; }\n    .sidemenu .menu-item-level-2 > span, .sidemenu .menu-item-level-2 > a, .sidemenu .menu-item-level-3 > span, .sidemenu .menu-item-level-3 > a {\n      font-family: Avenir-Medium;\n      font-size: 14px;\n      color: #666; }\n      .sidemenu .menu-item-level-2 > span:hover, .sidemenu .menu-item-level-2 > a:hover, .sidemenu .menu-item-level-3 > span:hover, .sidemenu .menu-item-level-3 > a:hover {\n        background: white; }\n        .sidemenu .menu-item-level-2 > span:hover::before, .sidemenu .menu-item-level-2 > a:hover::before, .sidemenu .menu-item-level-3 > span:hover::before, .sidemenu .menu-item-level-3 > a:hover::before {\n          content: '';\n          position: absolute;\n          left: 0;\n          top: 0;\n          width: 4px;\n          height: 100%;\n          background-image: linear-gradient(0deg, #03DDE4 0%, #30AFED 51%, #8755FF 100%); }\n  .sidemenu .menu-item-level-2 > span, .sidemenu .menu-item-level-2 > a {\n    padding-left: 40px;\n    height: 36px;\n    line-height: 36px; }\n    .sidemenu .menu-item-level-2 > span img.menu-toggle, .sidemenu .menu-item-level-2 > a img.menu-toggle {\n      float: right;\n      width: 13px;\n      height: 8px;\n      margin: 14px 20px 14px 0; }\n  .sidemenu .menu-item-level-3 > a {\n    padding-left: 60px;\n    height: 36px;\n    line-height: 36px; }\n\n@media screen and (max-width: 640px) {\n  .sidemenu {\n    width: 0; }\n    .sidemenu .sidemenu-toggle {\n      display: inline-block; }\n    .sidemenu.sidemenu-open {\n      width: 295px; } }\n"},,,,,,function(s,n){s.exports=".documentation-page .content-section {\n  max-width: 1280px;\n  margin: 0 auto;\n  box-sizing: border-box;\n  padding: 75px 40px 80px;\n  position: relative;\n  min-height: 1100px; }\n  .documentation-page .content-section .doc-content {\n    display: inline-block;\n    vertical-align: top;\n    box-sizing: border-box;\n    padding: 0 6% 0;\n    width: calc(100% - 295px); }\n\n@media screen and (max-width: 640px) {\n  .documentation-page .content-section {\n    padding-left: 20px;\n    padding-right: 20px; }\n    .documentation-page .content-section .doc-content {\n      width: 100%; }\n  .documentation-page .sidemenu {\n    position: absolute;\n    z-index: 100;\n    left: 0;\n    top: 40px; } }\n"}]);
\ No newline at end of file
diff --git a/build/2b4e297caa93bedd5375.js b/build/2b4e297caa93bedd5375.js
new file mode 100644
index 0000000..ce25858
--- /dev/null
+++ b/build/2b4e297caa93bedd5375.js
@@ -0,0 +1,6 @@
+webpackJsonp([2],[,,,,,,,,function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function a(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(t,"__esModule",{value:!0});var l=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),c=n(0),u=r(c),s=n(18),d=r(s),f=n(21),p=r(f),m=n(1),b=n(25),g=r(b),h=n(24),y=r(h),v=n(55),x=r(v),w=n(84),k=r(w),D=n(89),C=r(D),O=n(86),E=r(O),j=n(87),A=r(j),_=n(88),S=r(_),P=n(23),z=r(P),F=n(17),M=r(F),T=n(78),N=r(T);n(97);var R=function(e){function t(){return o(this,t),i(this,(t.__proto__||Object.getPrototypeOf(t)).apply(this,arguments))}return a(t,e),l(t,[{key:"render",value:function(){var e=window.location.hash.split("?"),t=p.default.parse(e[1]||""),n=t.lang||d.default.get("docsite_language")||M.default.defaultLanguage;if("en-us"!==n&&"zh-cn"!==n&&(n=M.default.defaultLanguage),n!==d.default.get("docsite_language")&&d.default.set("docsite_language",n,{expires:365,path:""}),!t.lang)return u.default.createElement(m.Redirect,{to:this.props.match.url+"?lang="+n});var r=N.default[n];return u.default.createElement("div",{className:"community-page"},u.default.createElement(y.default,{type:"normal",logo:"./img/dubbo_colorful.png",language:n,onLanguageChange:this.onLanguageChange}),u.default.createElement(x.default,{img:"./img/community.png",text:r.barText}),u.default.createElement("section",{className:"events-section"},u.default.createElement("h3",null,r.events.title),u.default.createElement(k.default,null,r.events.list.map(function(e,t){return u.default.createElement(C.default,{event:e,key:t})}))),u.default.createElement("section",{className:"eco-section"},u.default.createElement("h3",null,r.ecos.title),u.default.createElement("div",{className:"eco-lists"},r.ecos.list.map(function(e,t){return u.default.createElement(S.default,{eco:e,key:t})}))),u.default.createElement("section",{className:"contact-section"},u.default.createElement("h3",null,r.contacts.title),u.default.createElement("p",null,r.contacts.desc),u.default.createElement("div",{className:"contact-list"},r.contacts.list.map(function(e,t){return u.default.createElement(E.default,{contact:e,key:t})}))),u.default.createElement("section",{className:"contributor-section"},u.default.createElement("h3",null,r.contributorGuide.title),u.default.createElement("p",null,r.contributorGuide.desc),u.default.createElement("div",{className:"contributor-list"},r.contributorGuide.list.map(function(e,t){return u.default.createElement(A.default,{contributor:e,key:t})}))),u.default.createElement(z.default,{logo:"./img/dubbo_gray.png"}))}}]),t}(g.default);t.default=R},,,,,,,function(e,t,n){"use strict";function r(e,t,n,r){n&&Object.defineProperty(e,t,{enumerable:n.enumerable,configurable:n.configurable,writable:n.writable,value:n.initializer?n.initializer.call(r):void 0})}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t,n,r,o){var i={};return Object.keys(r).forEach(function(e){i[e]=r[e]}),i.enumerable=!!i.enumerable,i.configurable=!!i.configurable,("value"in i||i.initializer)&&(i.writable=!0),i=n.slice().reverse().reduce(function(n,r){return r(e,t,n)||n},i),o&&void 0!==i.initializer&&(i.value=i.initializer?i.initializer.call(o):void 0,i.initializer=void 0),void 0===i.initializer&&(Object.defineProperty(e,t,i),i=null),i}function a(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t<e.length;t++)n[t]=e[t];return n}return Array.from(e)}function l(e){if(!e||!e.hasOwnProperty)return!1;for(var t=["value","initializer","get","set"],n=0,r=t.length;n<r;n++)if(e.hasOwnProperty(t[n]))return!0;return!1}function c(e,t){return l(t[t.length-1])?e.apply(void 0,a(t).concat([[]])):function(){return e.apply(void 0,a(Array.prototype.slice.call(arguments)).concat([t]))}}function u(e){return!1===e.hasOwnProperty(j)&&k(e,j,{value:new E}),e[j]}function s(e){var t={};return A(e).forEach(function(n){return t[n]=D(e,n)}),t}function d(e){return function(t){return Object.defineProperty(this,e,{configurable:!0,writable:!0,enumerable:!0,value:t}),t}}function f(e,t){return e.bind?e.bind(t):function(){return e.apply(t,arguments)}}function p(e){!0!==S[e]&&(S[e]=!0,_("DEPRECATION: "+e))}t.d=c,t.c=u,n.d(t,"g",function(){return A}),t.f=s,t.e=d,t.a=f,n.d(t,"b",function(){return _}),t.h=p;var m,b,g,h,y,v,x=n(20),w="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},k=Object.defineProperty,D=Object.getOwnPropertyDescriptor,C=Object.getOwnPropertyNames,O=Object.getOwnPropertySymbols,E=(m=function e(){o(this,e),r(this,"debounceTimeoutIds",b,this),r(this,"throttleTimeoutIds",g,this),r(this,"throttlePreviousTimestamps",h,this),r(this,"throttleTrailingArgs",y,this),r(this,"profileLastRan",v,this)},b=i(m.prototype,"debounceTimeoutIds",[x.a],{enumerable:!0,initializer:function(){return{}}}),g=i(m.prototype,"throttleTimeoutIds",[x.a],{enumerable:!0,initializer:function(){return{}}}),h=i(m.prototype,"throttlePreviousTimestamps",[x.a],{enumerable:!0,initializer:function(){return{}}}),y=i(m.prototype,"throttleTrailingArgs",[x.a],{enumerable:!0,initializer:function(){return null}}),v=i(m.prototype,"profileLastRan",[x.a],{enumerable:!0,initializer:function(){return null}}),m),j="function"==typeof Symbol?Symbol("__core_decorators__"):"__core_decorators__",A=O?function(e){return C(e).concat(O(e))}:C,_=function(){return"object"===("undefined"==typeof console?"undefined":w(console))&&console&&"function"==typeof console.warn?f(console.warn,console):function(){}}(),S={}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(37);n.d(t,"override",function(){return r.a});var o=n(30);n.d(t,"deprecate",function(){return o.a}),n.d(t,"deprecated",function(){return o.a});var i=n(40);n.d(t,"suppressWarnings",function(){return i.a});var a=n(33);n.d(t,"memoize",function(){return a.a});var l=n(27);n.d(t,"autobind",function(){return l.a});var c=n(39);n.d(t,"readonly",function(){return c.a});var u=n(31);n.d(t,"enumerable",function(){return u.a});var s=n(36);n.d(t,"nonenumerable",function(){return s.a});var d=n(35);n.d(t,"nonconfigurable",function(){return d.a});var f=n(28);n.d(t,"debounce",function(){return f.a});var p=n(41);n.d(t,"throttle",function(){return p.a});var m=n(29);n.d(t,"decorate",function(){return m.a});var b=n(34);n.d(t,"mixin",function(){return b.a}),n.d(t,"mixins",function(){return b.a});var g=n(20);n.d(t,"lazyInitialize",function(){return g.a});var h=n(42);n.d(t,"time",function(){return h.a});var y=n(32);n.d(t,"extendDescriptor",function(){return y.a});var v=n(38);n.d(t,"profile",function(){return v.a});var x=n(26);n.d(t,"applyDecorators",function(){return x.a})},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={defaultLanguage:"en-us","en-us":{pageMenu:[{text:"HOME",link:"/"},{text:"DOCS",link:"/docs/user/quick-start.md"},{text:"BLOG",link:"/blog"},{text:"COMMUNITY",link:"/community"}],disclaimer:{title:"Disclaimer",content:"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 process have stabilized in a manner consistent with other successful ASF projects. While incubation status is not necessarily a reflection of the completeness or stability of the code, it does indicate that the project has yet to be fully endorsed by the ASF."},documentation:{title:"Documentation",list:[{text:"Quick start",link:"/docs/user/quick-start.md"},{text:"Developer guide",link:"/docs/dev/build.md"},{text:"Admin manual",link:"/docs/admin/ops/dubbo-ops.md"}]},resources:{title:"Resources",list:[{text:"Blog",link:"/blog"},{text:"Community",link:"/community"}]},copyright:"Copyright © 2018 The Apache Software Foundation. Apache and the Apache feather logo are trademarks of The Apache Software Foundation."},"zh-cn":{pageMenu:[{text:"首页",link:"/"},{text:"文档",link:"/docs/user/quick-start.md"},{text:"博客",link:"/blog"},{text:"社区",link:"/community"}],disclaimer:{title:"Disclaimer",content:"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 process have stabilized in a manner consistent with other successful ASF projects. While incubation status is not necessarily a reflection of the completeness or stability of the code, it does indicate that the project has yet to be fully endorsed by the ASF."},documentation:{title:"文档",list:[{text:"快速开始",link:"/docs/user/quick-start.md"},{text:"开发者指南",link:"/docs/dev/build.md"},{text:"运维管理",link:"/docs/admin/ops/dubbo-ops.md"}]},resources:{title:"资源",list:[{text:"博客",link:"/blog"},{text:"社区",link:"/community"}]},copyright:"Copyright © 2018 The Apache Software Foundation. Apache and the Apache feather logo are trademarks of The Apache Software Foundation."}}},function(e,t,n){var r,o;!function(i){var a=!1;if(r=i,void 0!==(o="function"==typeof r?r.call(t,n,t,e):r)&&(e.exports=o),a=!0,e.exports=i(),a=!0,!a){var l=window.Cookies,c=window.Cookies=i();c.noConflict=function(){return window.Cookies=l,c}}}(function(){function e(){for(var e=0,t={};e<arguments.length;e++){var n=arguments[e];for(var r in n)t[r]=n[r]}return t}function t(n){function r(t,o,i){var a;if("undefined"!=typeof document){if(arguments.length>1){if(i=e({path:"/"},r.defaults,i),"number"==typeof i.expires){var l=new Date;l.setMilliseconds(l.getMilliseconds()+864e5*i.expires),i.expires=l}i.expires=i.expires?i.expires.toUTCString():"";try{a=JSON.stringify(o),/^[\{\[]/.test(a)&&(o=a)}catch(e){}o=n.write?n.write(o,t):encodeURIComponent(String(o)).replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g,decodeURIComponent),t=encodeURIComponent(String(t)),t=t.replace(/%(23|24|26|2B|5E|60|7C)/g,decodeURIComponent),t=t.replace(/[\(\)]/g,escape);var c="";for(var u in i)i[u]&&(c+="; "+u,!0!==i[u]&&(c+="="+i[u]));return document.cookie=t+"="+o+c}t||(a={});for(var s=document.cookie?document.cookie.split("; "):[],d=/(%[0-9A-Z]{2})+/g,f=0;f<s.length;f++){var p=s[f].split("="),m=p.slice(1).join("=");this.json||'"'!==m.charAt(0)||(m=m.slice(1,-1));try{var b=p[0].replace(d,decodeURIComponent);if(m=n.read?n.read(m,b):n(m,b)||m.replace(d,decodeURIComponent),this.json)try{m=JSON.parse(m)}catch(e){}if(t===b){a=m;break}t||(a[b]=m)}catch(e){}}return a}}return r.set=r,r.get=function(e){return r.call(r,e)},r.getJSON=function(){return r.apply({json:!0},[].slice.call(arguments))},r.defaults={},r.remove=function(t,n){r(t,"",e(n,{expires:-1}))},r.withConverter=t,r}return t(function(){})})},function(e,t,n){var r,o;/*!
+  Copyright (c) 2017 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";function r(e,t,r){var o=r.configurable,l=r.enumerable,c=r.initializer,u=r.value;return{configurable:o,enumerable:l,get:function(){if(this!==e){var n=c?c.call(this):u;return a(this,t,{configurable:o,enumerable:l,writable:!0,value:n}),n}},set:n.i(i.e)(t)}}function o(){for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];return n.i(i.d)(r,t)}t.a=o;var i=n(15),a=Object.defineProperty},function(e,t,n){"use strict";t.decode=t.parse=n(46),t.encode=t.stringify=n(47)},,function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var o=n(0),i=r(o),a=n(18),l=r(a),c=n(1),u=n(17),s=r(u);n(43);var d=function(e){var t=l.default.get("docsite_language")||s.default.defaultLanguage,n=s.default[t];return i.default.createElement("footer",{className:"footer-container"},i.default.createElement("div",{className:"footer-body"},i.default.createElement("img",{src:e.logo}),i.default.createElement("img",{className:"apache",src:"./img/apache_logo.png"}),i.default.createElement("div",{className:"cols-container"},i.default.createElement("div",{className:"col col-12"},i.default.createElement("h3",null,n.disclaimer.title),i.default.createElement("p",null,n.disclaimer.content)),i.default.createElement("div",{className:"col col-6"},i.default.createElement("dl",null,i.default.createElement("dt",null,n.documentation.title),n.documentation.list.map(function(e,t){return i.default.createElement("dd",{key:t},i.default.createElement(c.Link,{to:e.link},e.text))}))),i.default.createElement("div",{className:"col col-6"},i.default.createElement("dl",null,i.default.createElement("dt",null,n.resources.title),n.resources.list.map(function(e,t){return i.default.createElement("dd",{key:t},i.default.createElement(c.Link,{to:e.link},e.text))})))),i.default.createElement("div",{className:"copyright"},i.default.createElement("span",null,n.copyright))))};t.default=d},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function l(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function c(e,t,n,r,o){var i={};return Object.keys(r).forEach(function(e){i[e]=r[e]}),i.enumerable=!!i.enumerable,i.configurable=!!i.configurable,("value"in i||i.initializer)&&(i.writable=!0),i=n.slice().reverse().reduce(function(n,r){return r(e,t,n)||n},i),o&&void 0!==i.initializer&&(i.value=i.initializer?i.initializer.call(o):void 0,i.initializer=void 0),void 0===i.initializer&&(Object.defineProperty(e,t,i),i=null),i}Object.defineProperty(t,"__esModule",{value:!0});var u,s=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),d=n(0),f=r(d),p=n(1),m=n(19),b=r(m),g=n(16),h=n(17),y=r(h);n(44);var v=[{text:"中",value:"en-us"},{text:"En",value:"zh-cn"}],x=function(){},w={type:"primary",language:"en-us",onLanguageChange:x},k=(u=function(e){function t(e){i(this,t);var n=a(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e));return n.state={menuBodyVisible:!1,language:e.language},n}return l(t,e),s(t,[{key:"toggleMenu",value:function(){this.setState({menuBodyVisible:!this.state.menuBodyVisible})}},{key:"switchLang",value:function(){var e=void 0;e="zh-cn"===this.state.language?"en-us":"zh-cn",this.setState({language:e}),this.props.onLanguageChange(e)}},{key:"componentWillReceiveProps",value:function(e){this.setState({language:e.language})}},{key:"render",value:function(){var e=this.props,t=e.type,n=e.logo,r=e.onLanguageChange,i=this.state,a=i.menuBodyVisible,l=i.language;return f.default.createElement("header",{className:(0,b.default)(o({"header-container":!0},"header-container-"+t,!0))},f.default.createElement("div",{className:"header-body"},f.default.createElement(p.Link,{to:"/"},f.default.createElement("img",{className:"logo",alt:y.default.name,title:y.default.name,src:n})),r!==x?f.default.createElement("span",{className:(0,b.default)(o({"language-switch":!0},"language-switch-"+t,!0)),onClick:this.switchLang},v.find(function(e){return e.value===l}).text):null,f.default.createElement("div",{className:(0,b.default)({"header-menu":!0,"header-menu-open":a})},f.default.createElement("img",{className:"header-menu-toggle",onClick:this.toggleMenu,src:"primary"===t?"./img/menu_white.png":"./img/menu_gray.png"}),f.default.createElement("ul",null,y.default[l].pageMenu.map(function(e){var n;return f.default.createElement("li",{className:(0,b.default)((n={"menu-item":!0},o(n,"menu-item-"+t,!0),o(n,"menu-item-"+t+"-active",window.location.hash.split("?")[0].slice(1).split("/")[1]===e.link.split("/")[1]),n))},f.default.createElement(p.Link,{to:e.link},e.text))})))))}}]),t}(f.default.Component),c(u.prototype,"toggleMenu",[g.autobind],Object.getOwnPropertyDescriptor(u.prototype,"toggleMenu"),u.prototype),c(u.prototype,"switchLang",[g.autobind],Object.getOwnPropertyDescriptor(u.prototype,"switchLang"),u.prototype),u);k.defaultProps=w,t.default=k},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function a(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var l,c=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),u=n(0),s=r(u),d=n(16),f=n(18),p=r(f),m=n(21),b=r(m),g=(l=function(e){function t(){return o(this,t),i(this,(t.__proto__||Object.getPrototypeOf(t)).apply(this,arguments))}return a(t,e),c(t,[{key:"onLanguageChange",value:function(e){this.props.location;p.default.set("docsite_language",e,{expires:365,path:""});var t=window.location.hash.split("?");if(t&&t.length){var n=b.default.parse(t[1]||"");n.lang=e,window.location.hash=(t[0]||"")+"?"+b.default.stringify(n)}this.forceUpdate()}}]),t}(s.default.Component),function(e,t,n,r,o){var i={};return Object.keys(r).forEach(function(e){i[e]=r[e]}),i.enumerable=!!i.enumerable,i.configurable=!!i.configurable,("value"in i||i.initializer)&&(i.writable=!0),i=n.slice().reverse().reduce(function(n,r){return r(e,t,n)||n},i),o&&void 0!==i.initializer&&(i.value=i.initializer?i.initializer.call(o):void 0,i.initializer=void 0),void 0===i.initializer&&(Object.defineProperty(e,t,i),i=null),i}(l.prototype,"onLanguageChange",[d.autobind],Object.getOwnPropertyDescriptor(l.prototype,"onLanguageChange"),l.prototype),l);t.default=g},function(e,t,n){"use strict";function r(e,t){var n=e.prototype;for(var r in t)for(var a=t[r],l=0,c=a.length;l<c;l++){var u=a[l];o(n,r,u(n,r,i(n,r)))}return e}t.a=r;var o=Object.defineProperty,i=Object.getOwnPropertyDescriptor},function(e,t,n){"use strict";function r(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t<e.length;t++)n[t]=e[t];return n}return Array.from(e)}function o(e,t){if("undefined"==typeof WeakMap)throw new Error("Using @autobind on "+t.name+"() requires WeakMap support due to its use of super."+t.name+"()\n      See https://github.com/jayphelps/core-decorators.js/issues/20");f||(f=new WeakMap),!1===f.has(e)&&f.set(e,new WeakMap);var r=f.get(e);return!1===r.has(t)&&r.set(t,n.i(u.a)(t,e)),r.get(t)}function i(e){for(var t=n.i(u.f)(e.prototype),r=n.i(u.g)(t),o=0,i=r.length;o<i;o++){var l=r[o],c=t[l];"function"==typeof c.value&&"constructor"!==l&&s(e.prototype,l,a(e.prototype,l,c))}}function a(e,t,r){var i=r.value,a=r.configurable,l=r.enumerable;if("function"!=typeof i)throw new SyntaxError("@autobind can only be used on functions, not: "+i);var c=e.constructor;return{configurable:a,enumerable:l,get:function(){if(this===e)return i;if(this.constructor!==c&&d(this).constructor===c)return i;if(this.constructor!==c&&t in this.constructor.prototype)return o(this,i);var r=n.i(u.a)(i,this);return s(this,t,{configurable:!0,writable:!0,enumerable:!1,value:r}),r},set:n.i(u.e)(t)}}function l(e){return 1===e.length?i.apply(void 0,r(e)):a.apply(void 0,r(e))}function c(){for(var e=arguments.length,t=Array(e),n=0;n<e;n++)t[n]=arguments[n];return 0===t.length?function(){return l(arguments)}:l(t)}t.a=c;var u=n(15),s=Object.defineProperty,d=Object.getPrototypeOf,f=void 0},function(e,t,n){"use strict";function r(e,t,r,o){var u=l(o,2),s=u[0],d=void 0===s?c:s,f=u[1],p=void 0!==f&&f,m=r.value;if("function"!=typeof m)throw new SyntaxError("Only functions can be debounced");return a({},r,{value:function(){var e=this,r=n.i(i.c)(this),o=r.debounceTimeoutIds,a=o[t],l=p&&!a,c=arguments;clearTimeout(a),o[t]=setTimeout(function(){delete o[t],p||m.apply(e,c)},d),l&&m.apply(this,c)}})}function o(){n.i(i.h)("@debounce is deprecated and will be removed shortly. Use @debounce from lodash-decorators.\n\n  https://www.npmjs.com/package/lodash-decorators");for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];return n.i(i.d)(r,t)}t.a=o;var i=n(15),a=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},l=function(){function e(e,t){var n=[],r=!0,o=!1,i=void 0;try{for(var a,l=e[Symbol.iterator]();!(r=(a=l.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(e){o=!0,i=e}finally{try{!r&&l.return&&l.return()}finally{if(o)throw i}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),c=300},function(e,t,n){"use strict";function r(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t<e.length;t++)n[t]=e[t];return n}return Array.from(e)}function o(e){return Array.isArray(e)?e:Array.from(e)}function i(e,t,i,a){var u=o(a),s=u[0],d=u.slice(1),f=i.configurable,p=i.enumerable,m=i.writable,b=i.get,g=i.set,h=i.value,y=!!b;return{configurable:f,enumerable:p,get:function(){var e=y?b.call(this):h,n=s.call.apply(s,[this,e].concat(r(d)));if(y)return n;var o={configurable:f,enumerable:p};return o.value=n,o.writable=m,c(this,t,o),n},set:y?g:n.i(l.e)()}}function a(){for(var e=arguments.length,t=Array(e),r=0;r<e;r++)t[r]=arguments[r];return n.i(l.d)(i,t)}t.a=a;var l=n(15),c=Object.defineProperty},function(e,t,n){"use strict";function r(e,t,r,o){var u=l(o,2),s=u[0],d=void 0===s?c:s,f=u[1],p=void 0===f?{}:f;if("function"!=typeof r.value)throw new SyntaxError("Only functions can be marked as deprecated");var m=e.constructor.name+"#"+t;return p.url&&(d+="\n\n    See "+p.url+" for more details.\n\n"),a({},r,{value:function(){return n.i(i.b)("DEPRECATION "+m+": "+d),r.value.apply(this,arguments)}})}function o(){for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];return n.i(i.d)(r,t)}t.a=o;var i=n(15),a=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},l=function(){function e(e,t){var n=[],r=!0,o=!1,i=void 0;try{for(var a,l=e[Symbol.iterator]();!(r=(a=l.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(e){o=!0,i=e}finally{try{!r&&l.return&&l.return()}finally{if(o)throw i}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),c="This function will be removed in future versions."},function(e,t,n){"use strict";function r(e,t,n){return n.enumerable=!0,n}function o(){for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];return n.i(i.d)(r,t)}t.a=o;var i=n(15)},function(e,t,n){"use strict";function r(e,t,n){var r=l(e),o=c(r,t);return a({},o,{value:n.value,initializer:n.initializer,get:n.get||o.get,set:n.set||o.set})}function o(){for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];return n.i(i.d)(r,t)}t.a=o;var i=n(15),a=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},l=Object.getPrototypeOf,c=Object.getOwnPropertyDescriptor},function(e,t,n){"use strict";function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){return t===Object(t)?t:e[t]||(e[t]={})}function i(e,t,n,r,o){var i=t.apply(e,n);return r[o]=i,i}function a(e){var t=void 0,n=void 0;return e.value?(t=e.value,n="value"):e.get?(t=e.get,n="get"):e.set&&(t=e.set,n="set"),{fn:t,wrapKey:n}}function l(e,t,n){var l=a(n),c=l.fn,u=l.wrapKey,d=new WeakMap,f=Object.create(null),p=Object.create(null),m=0;return s({},n,r({},u,function(){for(var e=arguments.length,t=Array(e),n=0;n<e;n++)t[n]=arguments[n];for(var r="0",a=0,l=t.length;a<l;a++){var u=t[a],s=o(p,u),b=d.get(s);void 0===b&&(b=++m,d.set(s,b)),r+=b}return f[r]||i(this,c,arguments,f,r)}))}function c(){n.i(u.h)("@memoize is deprecated and will be removed shortly. Use @memoize from lodash-decorators.\n\n  https://www.npmjs.com/package/lodash-decorators");for(var e=arguments.length,t=Array(e),r=0;r<e;r++)t[r]=arguments[r];return n.i(u.d)(l,t)}t.a=c;var u=n(15),s=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e}},function(e,t,n){"use strict";function r(e){return"[object Symbol]"===Object.prototype.toString.call(e)&&"object"===(void 0===e?"undefined":c(e))}function o(e,t){if(r(e)){do{if(t===Object.prototype)return void 0!==t[e];if(t.hasOwnProperty(e))return!0}while(t=s(t));return!1}return e in t}function i(e,t){if(!t.length)throw new SyntaxError("@mixin() class "+e.name+" requires at least one mixin as an argument");for(var r=0,i=t.length;r<i;r++)for(var a=n.i(l.f)(t[r]),c=n.i(l.g)(a),s=0,d=c.length;s<d;s++){var f=c[s];o(f,e.prototype)||u(e.prototype,f,a[f])}}function a(){for(var e=arguments.length,t=Array(e),r=0;r<e;r++)t[r]=arguments[r];return n.i(l.h)("@mixin is deprecated and will be removed shortly. Use @mixin from lodash-decorators.\n\n  https://www.npmjs.com/package/lodash-decorators"),"function"==typeof t[0]?i(t[0],[]):function(e){return i(e,t)}}t.a=a;var l=n(15),c="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},u=Object.defineProperty,s=Object.getPrototypeOf},function(e,t,n){"use strict";function r(e,t,n){return n.configurable=!1,n}function o(){for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];return n.i(i.d)(r,t)}t.a=o;var i=n(15)},function(e,t,n){"use strict";function r(e,t,n){return n.enumerable=!1,n}function o(){for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];return n.i(i.d)(r,t)}t.a=o;var i=n(15)},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(e){return e.hasOwnProperty("value")?"data":e.hasOwnProperty("get")||e.hasOwnProperty("set")?"accessor":"data"}function i(e,t,n){n.assert(e.length===t.length)}function a(e,t,n){var r=p(e.value),o=p(t.value);if("undefined"===r&&"undefined"===o&&n.error("descriptor values are both undefined. (class properties are are not currently supported)'"),r!==o){("function"===o&&void 0===r||void 0!==r)&&n.error('value types do not match. {parent} is "'+r+'", {child} is "'+o+'"')}switch(o){case"function":i(e.value,t.value,n);break;default:n.error('Unexpected error. Please file a bug with: {parent} is "'+r+'", {child} is "'+o+'"')}}function l(e,t,n){var r="function"==typeof e.get,o="function"==typeof t.get,a="function"==typeof e.set,l="function"==typeof t.set;(r||o)&&(!r&&a&&n.error("{parent} is setter but {child} is getter"),!o&&l&&n.error("{parent} is getter but {child} is setter"),i(e.get,t.get,n)),(a||l)&&(!a&&r&&n.error("{parent} is getter but {child} is setter"),!l&&o&&n.error("{parent} is setter but {child} is getter"),i(e.set,t.set,n))}function c(e,t,n){var r=o(e),i=o(t);switch(r!==i&&n.error('descriptor types do not match. {parent} is "'+r+'", {child} is "'+i+'"'),i){case"data":a(e,t,n);break;case"accessor":l(e,t,n)}}function u(e,t){for(var n=0,r=h.length;n<r;n++){var o=h[n],i=o(t);if(i in e)return i}return null}function s(e,t,n){n.key=t;var r=Object.getPrototypeOf(e),o=Object.getOwnPropertyDescriptor(r,t),i=new g(r,e,o,n);if(void 0===o){var a=u(r,t),l=a?'\n\n  Did you mean "'+a+'"?':"";i.error("No descriptor matching {child} was found on the prototype chain."+l)}return c(o,n,i),n}function d(){for(var e=arguments.length,t=Array(e),r=0;r<e;r++)t[r]=arguments[r];return n.i(f.d)(s,t)}t.a=d;var f=n(15),p="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},m=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),b=/^function ([_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*)?(\([^\)]*\))[\s\S]+$/,g=function(){function e(t,n,o,i){r(this,e),this.parentKlass=t,this.childKlass=n,this.parentDescriptor=o,this.childDescriptor=i}return m(e,[{key:"_getTopic",value:function(e){return void 0===e?null:"value"in e?e.value:"get"in e?e.get:"set"in e?e.set:void 0}},{key:"_extractTopicSignature",value:function(e){switch(void 0===e?"undefined":p(e)){case"function":return this._extractFunctionSignature(e);default:return this.key}}},{key:"_extractFunctionSignature",value:function(e){var t=this;return e.toString().replace(b,function(e){return(arguments.length>1&&void 0!==arguments[1]?arguments[1]:t.key)+arguments[2]})}},{key:"key",get:function(){return this.childDescriptor.key}},{key:"parentNotation",get:function(){return this.parentKlass.constructor.name+"#"+this.parentPropertySignature}},{key:"childNotation",get:function(){return this.childKlass.constructor.name+"#"+this.childPropertySignature}},{key:"parentTopic",get:function(){return this._getTopic(this.parentDescriptor)}},{key:"childTopic",get:function(){return this._getTopic(this.childDescriptor)}},{key:"parentPropertySignature",get:function(){return this._extractTopicSignature(this.parentTopic)}},{key:"childPropertySignature",get:function(){return this._extractTopicSignature(this.childTopic)}}]),m(e,[{key:"assert",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";!0!==e&&this.error("{child} does not properly override {parent}"+t)}},{key:"error",value:function(e){var t=this;throw e=e.replace("{parent}",function(e){return t.parentNotation}).replace("{child}",function(e){return t.childNotation}),new SyntaxError(e)}}]),e}(),h=[function(e){return e.toLowerCase()},function(e){return e.toUpperCase()},function(e){return e+"s"},function(e){return e.slice(0,-1)},function(e){return e.slice(1,e.length)}]},function(e,t,n){"use strict";function r(e,t,r,u){var s=l(u,3),d=s[0],f=void 0===d?null:d,p=s[1],m=void 0!==p&&p,b=s[2],g=void 0===b?c:b;if(!o.__enabled)return o.__warned||(g.warn("console.profile is not supported. All @profile decorators are disabled."),o.__warned=!0),r;var h=r.value;if(null===f&&(f=e.constructor.name+"."+t),"function"!=typeof h)throw new SyntaxError("@profile can only be used on functions, not: "+h);return a({},r,{value:function(){var e=Date.now(),t=n.i(i.c)(this);(!0===m&&!t.profileLastRan||!1===m||"number"==typeof m&&e-t.profileLastRan>m||"function"==typeof m&&m.apply(this,arguments))&&(g.profile(f),t.profileLastRan=e);try{return h.apply(this,arguments)}finally{g.profileEnd(f)}}})}function o(){for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];return n.i(i.d)(r,t)}t.a=o;var i=n(15),a=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},l=function(){function e(e,t){var n=[],r=!0,o=!1,i=void 0;try{for(var a,l=e[Symbol.iterator]();!(r=(a=l.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(e){o=!0,i=e}finally{try{!r&&l.return&&l.return()}finally{if(o)throw i}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),c=(console,{profile:console.profile?n.i(i.a)(console.profile,console):function(){},profileEnd:console.profileEnd?n.i(i.a)(console.profileEnd,console):function(){},warn:i.b});o.__enabled=!!console.profile,o.__warned=!1},function(e,t,n){"use strict";function r(e,t,n){return n.writable=!1,n}function o(){for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];return n.i(i.d)(r,t)}t.a=o;var i=n(15)},function(e,t,n){"use strict";function r(){}function o(e,t,n){if("object"===("undefined"==typeof console?"undefined":u(console))){var o=console.warn;console.warn=r;var i=t.apply(e,n);return console.warn=o,i}return t.apply(e,n)}function i(e,t,n){return c({},n,{value:function(){return o(this,n.value,arguments)}})}function a(){for(var e=arguments.length,t=Array(e),r=0;r<e;r++)t[r]=arguments[r];return n.i(l.d)(i,t)}t.a=a;var l=n(15),c=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},u="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e}},function(e,t,n){"use strict";function r(e,t,r,o){var u=l(o,2),s=u[0],d=void 0===s?c:s,f=u[1],p=void 0===f?{}:f,m=r.value;if("function"!=typeof m)throw new SyntaxError("Only functions can be throttled");return!1!==p.leading&&(p.leading=!0),!1!==p.trailing&&(p.trailing=!0),a({},r,{value:function(){var e=this,r=n.i(i.c)(this),o=r.throttleTimeoutIds,a=r.throttlePreviousTimestamps,l=o[t],c=a[t]||0,u=Date.now();p.trailing&&(r.throttleTrailingArgs=arguments),c||!1!==p.leading||(c=u);var s=d-(u-c);s<=0?(clearTimeout(l),delete o[t],a[t]=u,m.apply(this,arguments)):!l&&p.trailing&&(o[t]=setTimeout(function(){a[t]=!1===p.leading?0:Date.now(),delete o[t],m.apply(e,r.throttleTrailingArgs),r.throttleTrailingArgs=null},s))}})}function o(){n.i(i.h)("@throttle is deprecated and will be removed shortly. Use @throttle from lodash-decorators.\n\n  https://www.npmjs.com/package/lodash-decorators");for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];return n.i(i.d)(r,t)}t.a=o;var i=n(15),a=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},l=function(){function e(e,t){var n=[],r=!0,o=!1,i=void 0;try{for(var a,l=e[Symbol.iterator]();!(r=(a=l.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(e){o=!0,i=e}finally{try{!r&&l.return&&l.return()}finally{if(o)throw i}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),c=300},function(e,t,n){"use strict";function r(e,t,n,r){var o=l(r,2),i=o[0],c=void 0===i?null:i,d=o[1],f=void 0===d?u:d,p=n.value;if(null===c&&(c=e.constructor.name+"."+t),"function"!=typeof p)throw new SyntaxError("@time can only be used on functions, not: "+p);return a({},n,{value:function(){var e=c+"-"+s;s++,f.time(e);try{return p.apply(this,arguments)}finally{f.timeEnd(e)}}})}function o(){for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];return n.i(i.d)(r,t)}t.a=o;var i=n(15),a=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},l=function(){function e(e,t){var n=[],r=!0,o=!1,i=void 0;try{for(var a,l=e[Symbol.iterator]();!(r=(a=l.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(e){o=!0,i=e}finally{try{!r&&l.return&&l.return()}finally{if(o)throw i}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),c={},u={time:console.time?console.time.bind(console):function(e){c[e]=new Date},timeEnd:console.timeEnd?console.timeEnd.bind(console):function(e){var t=new Date,n=t-c[e];delete c[e],console.log(e+": "+n+"ms")}},s=0},function(e,t,n){n(14)(n(48))},function(e,t,n){n(14)(n(49))},,function(e,t,n){"use strict";function r(e,t){return Object.prototype.hasOwnProperty.call(e,t)}e.exports=function(e,t,n,i){t=t||"&",n=n||"=";var a={};if("string"!=typeof e||0===e.length)return a;var l=/\+/g;e=e.split(t);var c=1e3;i&&"number"==typeof i.maxKeys&&(c=i.maxKeys);var u=e.length;c>0&&u>c&&(u=c);for(var s=0;s<u;++s){var d,f,p,m,b=e[s].replace(l,"%20"),g=b.indexOf(n);g>=0?(d=b.substr(0,g),f=b.substr(g+1)):(d=b,f=""),p=decodeURIComponent(d),m=decodeURIComponent(f),r(a,p)?o(a[p])?a[p].push(m):a[p]=[a[p],m]:a[p]=m}return a};var o=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)}},function(e,t,n){"use strict";function r(e,t){if(e.map)return e.map(t);for(var n=[],r=0;r<e.length;r++)n.push(t(e[r],r));return n}var o=function(e){switch(typeof e){case"string":return e;case"boolean":return e?"true":"false";case"number":return isFinite(e)?e:"";default:return""}};e.exports=function(e,t,n,l){return t=t||"&",n=n||"=",null===e&&(e=void 0),"object"==typeof e?r(a(e),function(a){var l=encodeURIComponent(o(a))+n;return i(e[a])?r(e[a],function(e){return l+encodeURIComponent(o(e))}).join(t):l+encodeURIComponent(o(e[a]))}).join(t):l?encodeURIComponent(o(l))+n+encodeURIComponent(o(e)):""};var i=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)},a=Object.keys||function(e){var t=[];for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&t.push(n);return t}},function(e,t){e.exports=".footer-container {\n  background: #F8F8F8; }\n  .footer-container .footer-body {\n    max-width: 1280px;\n    margin: 0 auto;\n    box-sizing: border-box;\n    padding: 40px 40px 0; }\n    @media screen and (max-width: 640px) {\n      .footer-container .footer-body {\n        padding-left: 20px;\n        padding-right: 20px; } }\n    .footer-container .footer-body img {\n      width: 125px;\n      height: 26px;\n      margin-bottom: 28px;\n      margin-right: 20px;\n      vertical-align: middle; }\n    .footer-container .footer-body .apache {\n      width: 50px;\n      height: 50px; }\n    .footer-container .footer-body .cols-container .col {\n      display: inline-block;\n      box-sizing: border-box;\n      vertical-align: top; }\n    .footer-container .footer-body .cols-container .col-12 {\n      width: 50%;\n      padding-right: 125px; }\n    .footer-container .footer-body .cols-container .col-6 {\n      width: 25%; }\n    .footer-container .footer-body .cols-container h3 {\n      font-family: Avenir-Heavy;\n      font-size: 18px;\n      color: #333;\n      line-height: 18px; }\n    .footer-container .footer-body .cols-container p {\n      font-family: Avenir-Medium;\n      font-size: 12px;\n      color: #999;\n      line-height: 18px; }\n    .footer-container .footer-body .cols-container dl {\n      font-family: Avenir-Heavy;\n      line-height: 18px; }\n    .footer-container .footer-body .cols-container dt {\n      font-weight: bold;\n      font-size: 18px;\n      color: #333;\n      margin-bottom: 20px; }\n    .footer-container .footer-body .cols-container dd {\n      padding: 0;\n      margin: 0; }\n      .footer-container .footer-body .cols-container dd a {\n        text-decoration: none;\n        display: block;\n        font-size: 14px;\n        color: #999;\n        margin: 10px 0; }\n      .footer-container .footer-body .cols-container dd a:hover {\n        color: #2DACEC; }\n    .footer-container .footer-body .copyright {\n      border-top: 1px solid #ccc;\n      min-height: 60px;\n      line-height: 20px;\n      text-align: center;\n      font-family: Avenir-Medium;\n      font-size: 12px;\n      color: #999;\n      display: flex;\n      align-items: center; }\n      .footer-container .footer-body .copyright span {\n        display: inline-block;\n        margin: 0 auto; }\n\n@media screen and (max-width: 640px) {\n  .footer-container .footer-body .cols-container .col {\n    width: 100%;\n    text-align: center;\n    padding: 0; } }\n"},function(e,t){e.exports=".header-container {\n  position: fixed;\n  left: 0;\n  top: 0;\n  width: 100%;\n  z-index: 1000;\n  background-color: #fff; }\n  .header-container-primary {\n    background-color: transparent; }\n  .header-container-normal {\n    background-color: #fff;\n    box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.08); }\n  .header-container .header-body {\n    max-width: 1280px;\n    margin: 0 auto;\n    height: 66px;\n    line-height: 66px; }\n    .header-container .header-body .logo {\n      margin-left: 40px;\n      width: 96px;\n      vertical-align: sub; }\n    .header-container .header-body .header-menu {\n      float: right; }\n      .header-container .header-body .header-menu .header-menu-toggle {\n        display: none;\n        width: 19px;\n        margin-right: 40px;\n        margin-top: 18px;\n        cursor: pointer; }\n    .header-container .header-body ul {\n      padding: 0;\n      margin: 0; }\n    .header-container .header-body li {\n      display: inline-block;\n      margin-right: 40px; }\n    .header-container .header-body .menu-item {\n      font-family: Avenir-Heavy;\n      font-size: 14px; }\n    .header-container .header-body .menu-item-primary a {\n      color: #fff;\n      opacity: 0.6;\n      font-family: Avenir-Medium; }\n    .header-container .header-body .menu-item-primary:hover a {\n      opacity: 1; }\n    .header-container .header-body .menu-item-primary-active a {\n      opacity: 1; }\n    .header-container .header-body .menu-item-normal a {\n      color: #333;\n      opacity: 0.6;\n      font-family: Avenir-Medium; }\n    .header-container .header-body .menu-item-normal:hover a {\n      opacity: 1; }\n    .header-container .header-body .menu-item-normal-active a {\n      opacity: 1; }\n    .header-container .header-body .language-switch {\n      float: right;\n      display: inline-block;\n      box-sizing: border-box;\n      width: 24px;\n      height: 24px;\n      line-height: 20px;\n      margin-top: 21px;\n      margin-right: 40px;\n      text-align: center;\n      border-radius: 2px;\n      cursor: pointer;\n      font-family: PingFangSC-Medium;\n      font-size: 14px;\n      opacity: 0.6; }\n      .header-container .header-body .language-switch:hover {\n        opacity: 1; }\n    .header-container .header-body .language-switch-primary {\n      border: 1px solid #FFF;\n      color: #FFF; }\n    .header-container .header-body .language-switch-normal {\n      border: 1px solid #333;\n      color: #333; }\n\n@media screen and (max-width: 640px) {\n  .header-container .header-body .logo {\n    margin-left: 20px; }\n  .header-container .header-body .language-switch {\n    margin-right: 20px; }\n  .header-container .header-body .header-menu ul {\n    display: none; }\n  .header-container .header-body .header-menu .header-menu-toggle {\n    display: inline-block;\n    margin-right: 20px; }\n  .header-container .header-body .header-menu-open ul {\n    background-color: #f8f8f8;\n    display: inline-block;\n    position: absolute;\n    right: 0;\n    top: 66px;\n    z-index: 100; }\n  .header-container .header-body .header-menu-open li {\n    width: 200px;\n    display: list-item;\n    padding-left: 30px;\n    list-style: none;\n    line-height: 40px;\n    margin-right: 0; }\n    .header-container .header-body .header-menu-open li a {\n      color: #333;\n      display: inline-block;\n      width: 100%; }\n    .header-container .header-body .header-menu-open li:hover {\n      background: #8755FF; }\n      .header-container .header-body .header-menu-open li:hover a {\n        color: #fff;\n        opactiy: 1; }\n  .header-container .header-body .header-menu-open .menu-item-primary-active, .header-container .header-body .header-menu-open .menu-item-normal-active {\n    background: #8755FF; }\n    .header-container .header-body .header-menu-open .menu-item-primary-active a, .header-container .header-body .header-menu-open .menu-item-normal-active a {\n      color: #fff;\n      opactiy: 1; } }\n"},,,,,,function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e){var t=e.text,n=e.img,r=(0,c.default)({bar:!0});return a.default.createElement("div",{className:r},a.default.createElement("div",{className:"bar-body"},a.default.createElement("img",{src:n,className:"front-img"}),a.default.createElement("span",null,t),a.default.createElement("img",{src:n,className:"back-img"})))}Object.defineProperty(t,"__esModule",{value:!0}),t.default=o;var i=n(0),a=r(i),l=n(19),c=r(l);n(57)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});t.throttle=function(e,t){var n=null;return function(){for(var r=arguments.length,o=Array(r),i=0;i<r;i++)o[i]=arguments[i];var a=this;clearTimeout(n),n=setTimeout(function(){e.apply(a,o)},t)}},t.getScrollTop=function(){var e=0;return document.documentElement&&document.documentElement.scrollTop?e=document.documentElement.scrollTop:document.body&&(e=document.body.scrollTop),e}},function(e,t,n){n(14)(n(58))},function(e,t){e.exports=".bar {\n  margin-top: 66px;\n  background-image: linear-gradient(-90deg, #03DDE4 0%, #30AFED 51%, #8755FF 100%); }\n  .bar .bar-body {\n    max-width: 1280px;\n    margin: 0 auto;\n    height: 200px;\n    line-height: 200px;\n    font-family: Avenir-Heavy;\n    font-size: 36px;\n    color: #FFF;\n    position: relative; }\n    .bar .bar-body::before {\n      content: '';\n      height: 100%;\n      position: absolute;\n      left: 42px;\n      top: 0;\n      opacity: 0.3;\n      border-left: 1px solid #FFFFFF; }\n    .bar .bar-body::after {\n      content: '';\n      height: 16px;\n      position: absolute;\n      left: 40px;\n      top: 50%;\n      margin: auto 0;\n      border-left: 4px solid #FFFFFF; }\n    .bar .bar-body .front-img {\n      width: 80px;\n      height: 80px;\n      vertical-align: middle;\n      margin: 0 28px 0 70px; }\n    .bar .bar-body .back-img {\n      width: 160px;\n      height: 160px;\n      position: absolute;\n      right: 168px;\n      bottom: 0;\n      opacity: 0.15; }\n    @media screen and (max-width: 640px) {\n      .bar .bar-body::before {\n        left: 22px; }\n      .bar .bar-body::after {\n        left: 20px; }\n      .bar .bar-body .front-img {\n        margin-left: 50px; } }\n"},,,,,,,,,,,,,,,,,,,,function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(0),o=function(e){return e&&e.__esModule?e:{default:e}}(r);t.default={"en-us":{barText:"Community",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",content:"The first Dubbo meetup has successfully been held in Beijing, over 400+ people were present. What a great event!",dateStr:"May 12nd,2018",link:"/blog/dubbo-meetup-beijing-may-12th-2018.md"},{img:"./img/blog/apachecon-na-2018.png",title:"The ApacheCon NA schedule has been announced",content:'Ian Luo/Jun Liu will talk about "Introducing Apache Dubbo(Incubating): What is Dubbo and How it Works" at ApacheCon NA this year in Montréal!',dateStr:"May 2nd,2018",link:"/blog/apachecon-na-2018.md"},{img:"./img/blog/qcon-beijing-2018.jpeg",title:"Dubbo roadmap is announced in QCon Beijing 2018",content:"Ian Luo has delivered a great talk at QCon Beijing 2018, where the roadmap of Dubbo has also be announced.",dateStr:"April 21st,2018",link:"/blog/qcon-beijing-2018.md"}]},contacts:{title:"Talk To Us",desc:"Feel free to contact us via the following channel.",list:[{img:"./img/mailinglist.png",imgHover:"./img/mailinglist_hover.png",title:"Mailing List",link:"https://github.com/apache/incubator-dubbo/wiki/New-contributor-guide"},{img:"./img/alibaba.png",imgHover:"./img/alibaba_hover.png",title:"#alibaba/dubbo",link:"https://gitter.im/alibaba/dubbo"},{img:"./img/segmentfault.png",imgHover:"./img/segmentfault_hover.png",title:"Segment Fault",link:"https://segmentfault.com/t/dubbo"},{img:"./img/twitter.png",imgHover:"./img/twitter_hover.png",title:"@ApacheDubbo",link:"https://twitter.com/ApacheDubbo"}]},contributorGuide:{title:"Contributor Guide",desc:"Want to contribute to Dubbo?",list:[{img:"./img/mailinglist.png",title:"Mailing List",content:o.default.createElement("span",null,"Join the ",o.default.createElement("a",{href:"https://github.com/apache/incubator-dubbo/wiki/New-contributor-guide"},"mailing list "),"and discussion your ideas with us.")},{img:"./img/issue.png",title:"Issue",content:o.default.createElement("span",null,"Reporting issues via ",o.default.createElement("a",{href:"https://github.com/apache/incubator-dubbo/issues"},"Github issues"),".")},{img:"./img/documents.png",title:"Documents",content:o.default.createElement("span",null,"Improve the ",o.default.createElement("a",{href:"https://github.com/apache/incubator-dubbo-docs"},"documentation"),".")},{img:"./img/pullrequest.png",title:"Pull Request",content:o.default.createElement("span",null,"Send your awesome enhancement via ",o.default.createElement("a",{href:"https://github.com/apache/incubator-dubbo/pulls"},"Pull requests."))}]},ecos:{title:"Eco System",list:[{title:"Language",content:o.default.createElement("span",null,"Dubbo supports the following languages:"),tags:[{text:"Java",link:"https://github.com/apache/incubator-dubbo",bgColor:"#7A63FC"},{text:"Node.js",link:"https://github.com/dubbo/dubbo2.js",bgColor:"#00D0D9"},{text:"Python",link:"https://github.com/dubbo/dubbo-client-py",bgColor:"#00D0D9"},{text:"php",link:"https://github.com/dubbo/dubbo-php-framework",bgColor:"#00D0D9"}]},{title:"API",content:o.default.createElement("span",null,"Dubbo supports the following API:"),tags:[{text:"Spring XML",link:"#/docs/user/configuration/xml.md",bgColor:"#7A63FC"},{text:"Spring Annotation",link:"#/docs/user/configuration/annotation.md",bgColor:"#00D0D9"},{text:"Plain Java",link:"#/docs/user/configuration/properties.md",bgColor:"#00D0D9"},{text:"Spring-boot",link:"https://github.com/apache/incubator-dubbo-spring-boot-project",bgColor:"#00D0D9"}]},{title:"Registry",content:o.default.createElement("span",null,"Dubbo supports the following registries:"),tags:[{text:"Zookeeper",link:"#/docs/user/references/registry/zookeeper.md",bgColor:"#7A63FC"},{text:"Redis",link:"#/docs/user/references/registry/redis.md",bgColor:"#00D0D9"},{text:"Simple",link:"#/docs/user/references/registry/simple.md",bgColor:"#00D0D9"},{text:"Multicast",link:"#/docs/user/references/registry/multicast.md",bgColor:"#00D0D9"}]},{title:"Cluster",content:o.default.createElement("span",null,"Dubbo supports the following clusters:"),tags:[{text:"Fail over",link:"#/docs/user/demos/fault-tolerent-strategy.md",bgColor:"#7A63FC"},{text:"Fail safe",link:"#/docs/user/demos/fault-tolerent-strategy.md",bgColor:"#00D0D9"},{text:"Fail fast",link:"#/docs/user/demos/fault-tolerent-strategy.md",bgColor:"#00D0D9"},{text:"Fail back",link:"#/docs/user/demos/fault-tolerent-strategy.md",bgColor:"#00D0D9"},{text:"Forking",link:"#/docs/user/demos/fault-tolerent-strategy.md",bgColor:"#00D0D9"},{text:"Broadcast",link:"#/docs/user/demos/fault-tolerent-strategy.md",bgColor:"#00D0D9"}]},{title:"Load balance",content:o.default.createElement("span",null,"Dubbo supports the following load balance:"),tags:[{text:"Random",link:"#/docs/user/demos/loadbalance.md",bgColor:"#7A63FC"},{text:"Least Active",link:"#/docs/user/demos/loadbalance.md",bgColor:"#00D0D9"},{text:"Round Robin",link:"#/docs/user/demos/loadbalance.md",bgColor:"#00D0D9"},{text:"Consistent hash",link:"#/docs/user/demos/loadbalance.md",bgColor:"#00D0D9"}]},{title:"Protocol",content:o.default.createElement("span",null,"Dubbo supports the following protocols:"),tags:[{text:"Dubbo",link:"#/docs/user/references/protocol/dubbo.md",bgColor:"#7A63FC"},{text:"RMI",link:"#/docs/user/references/protocol/rmi.md",bgColor:"#00D0D9"},{text:"Hessian",link:"#/docs/user/references/protocol/hessian.md",bgColor:"#00D0D9"},{text:"HTTP",link:"#/docs/user/references/protocol/http.md",bgColor:"#00D0D9"},{text:"WebService",link:"#/docs/user/references/protocol/webservice.md",bgColor:"#00D0D9"},{text:"Thrift",link:"#/docs/user/references/protocol/thrift.md",bgColor:"#00D0D9"},{text:"Memcached",link:"#/docs/user/references/protocol/memcached.md",bgColor:"#00D0D9"},{text:"Redis",link:"#/docs/user/references/protocol/redis.md",bgColor:"#00D0D9"},{text:"Rest",link:"#/docs/user/references/protocol/rest.md",bgColor:"#00D0D9"},{text:"JsonRPC",link:"https://github.com/apache/incubator-dubbo-rpc-jsonrpc",bgColor:"#00D0D9"},{text:"XmlRPC",link:"https://github.com/dubbo/incubator-dubbo-rpc-xmlrpc",bgColor:"#00D0D9"}]},{title:"Transport",content:o.default.createElement("span",null,"Dubbo supports the following transporters:"),tags:[{text:"Netty3",link:"",bgColor:"#7A63FC"},{text:"Netty4",link:"#/docs/user/demos/netty4.md",bgColor:"#00D0D9"},{text:"Grizzly",link:"",bgColor:"#00D0D9"},{text:"Jetty",link:"",bgColor:"#00D0D9"},{text:"Mina",link:"",bgColor:"#00D0D9"},{text:"p2p",link:"",bgColor:"#00D0D9"},{text:"Zookeeper",link:"",bgColor:"#00D0D9"}]},{title:"Serialization",content:o.default.createElement("span",null,"Dubbo supports the following serialization:"),tags:[{text:"Hessian2",link:"",bgColor:"#7A63FC"},{text:"Java",link:"",bgColor:"#00D0D9"},{text:"JSON",link:"",bgColor:"#00D0D9"},{text:"Fst",link:"",bgColor:"#00D0D9"},{text:"Kryo",link:"",bgColor:"#00D0D9"}]}]}},"zh-cn":{barText:"社区",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开发者沙龙在北京成功举办",content:"首届Dubbo开发者沙龙在北京成功举办,超过400位开发者参加!",dateStr:"May 12nd,2018",link:"/blog/dubbo-meetup-beijing-may-12th-2018.md"},{img:"./img/blog/apachecon-na-2018.png",title:"ApacheCon大会议程公布",content:'罗毅/刘军 将进行题为"Introducing Apache Dubbo(Incubating): What is Dubbo and How it Works"的演讲。',dateStr:"May 2nd,2018",link:"/blog/apachecon-na-2018.md"},{img:"./img/blog/qcon-beijing-2018.jpeg",title:"Dubbo路线图在QCon Beijing 2018上公布",content:"罗毅在Qcon Beijing 2018上进行了Dubbo开源现状及未来规划的主题演讲。",dateStr:"April 21st,2018",link:"/blog/qcon-beijing-2018.md"}]},contacts:{title:"联系我们",desc:"有问题需要反馈?请通过一下方式联系我们。",list:[{img:"./img/mailinglist.png",imgHover:"./img/mailinglist_hover.png",title:"邮件列表",link:"https://github.com/apache/incubator-dubbo/wiki/New-contributor-guide"},{img:"./img/alibaba.png",imgHover:"./img/alibaba_hover.png",title:"Gitter",link:"https://gitter.im/alibaba/dubbo"},{img:"./img/segmentfault.png",imgHover:"./img/segmentfault_hover.png",title:"Segment Fault",link:"https://segmentfault.com/t/dubbo"},{img:"./img/twitter.png",imgHover:"./img/twitter_hover.png",title:"@ApacheDubbo",link:"https://twitter.com/ApacheDubbo"}]},contributorGuide:{title:"贡献指南",desc:"Dubbo社区欢迎任何形式的贡献。",list:[{img:"./img/mailinglist.png",title:"邮件列表",content:o.default.createElement("span",null,"加入 ",o.default.createElement("a",{href:"https://github.com/apache/incubator-dubbo/wiki/New-contributor-guide"},"邮件列表 "),"参与讨论。")},{img:"./img/issue.png",title:"报告缺陷",content:o.default.createElement("span",null,"通过",o.default.createElement("a",{href:"https://github.com/apache/incubator-dubbo/issues"}," Github issues "),"报告缺陷。")},{img:"./img/documents.png",title:"文档",content:o.default.createElement("span",null,"优化Dubbo ",o.default.createElement("a",{href:"http://dubbo.apache.org/#/docs/"}," 文档"),"。")},{img:"./img/pullrequest.png",title:"Pull Request",content:o.default.createElement("span",null,"提交 ",o.default.createElement("a",{href:"https://github.com/apache/incubator-dubbo/pulls"}," Pull requests "),"来修复问题。")}]},ecos:{title:"生态系统",list:[{title:"多语言",content:o.default.createElement("span",null,"Dubbo支持以下语言:"),tags:[{text:"Java",link:"https://github.com/apache/incubator-dubbo",bgColor:"#7A63FC"},{text:"Node.js",link:"https://github.com/dubbo/dubbo2.js",bgColor:"#00D0D9"},{text:"Python",link:"https://github.com/dubbo/dubbo-client-py",bgColor:"#00D0D9"},{text:"php",link:"https://github.com/dubbo/dubbo-php-framework",bgColor:"#00D0D9"}]},{title:"API",content:o.default.createElement("span",null,"Dubbo支持通过多种API方式启动:"),tags:[{text:"Spring XML",link:"#/docs/user/configuration/xml.md",bgColor:"#7A63FC"},{text:"Spring Annotation",link:"#/docs/user/configuration/annotation.md",bgColor:"#00D0D9"},{text:"Plain Java",link:"#/docs/user/configuration/properties.md",bgColor:"#00D0D9"},{text:"Spring-boot",link:"https://github.com/apache/incubator-dubbo-spring-boot-project",bgColor:"#00D0D9"}]},{title:"Registry",content:o.default.createElement("span",null,"Dubbo支持以下注册中心:"),tags:[{text:"Zookeeper",link:"#/docs/user/references/registry/zookeeper.md",bgColor:"#7A63FC"},{text:"Redis",link:"#/docs/user/references/registry/redis.md",bgColor:"#00D0D9"},{text:"Simple",link:"#/docs/user/references/registry/simple.md",bgColor:"#00D0D9"},{text:"Multicast",link:"#/docs/user/references/registry/multicast.md",bgColor:"#00D0D9"}]},{title:"Cluster",content:o.default.createElement("span",null,"Dubbo支持一下容错机制:"),tags:[{text:"Fail over",link:"#/docs/user/demos/fault-tolerent-strategy.md",bgColor:"#7A63FC"},{text:"Fail safe",link:"#/docs/user/demos/fault-tolerent-strategy.md",bgColor:"#00D0D9"},{text:"Fail fast",link:"#/docs/user/demos/fault-tolerent-strategy.md",bgColor:"#00D0D9"},{text:"Fail back",link:"#/docs/user/demos/fault-tolerent-strategy.md",bgColor:"#00D0D9"},{text:"Forking",link:"#/docs/user/demos/fault-tolerent-strategy.md",bgColor:"#00D0D9"},{text:"Broadcast",link:"#/docs/user/demos/fault-tolerent-strategy.md",bgColor:"#00D0D9"}]},{title:"Load balance",content:o.default.createElement("span",null,"Dubbo支持以下负载均衡策略:"),tags:[{text:"Random",link:"#/docs/user/demos/loadbalance.md",bgColor:"#7A63FC"},{text:"Least Active",link:"#/docs/user/demos/loadbalance.md",bgColor:"#00D0D9"},{text:"Round Robin",link:"#/docs/user/demos/loadbalance.md",bgColor:"#00D0D9"},{text:"Consistent hash",link:"#/docs/user/demos/loadbalance.md",bgColor:"#00D0D9"}]},{title:"Protocol",content:o.default.createElement("span",null,"Dubbo支持以下协议:"),tags:[{text:"Dubbo",link:"#/docs/user/references/protocol/dubbo.md",bgColor:"#7A63FC"},{text:"RMI",link:"#/docs/user/references/protocol/rmi.md",bgColor:"#00D0D9"},{text:"Hessian",link:"#/docs/user/references/protocol/hessian.md",bgColor:"#00D0D9"},{text:"HTTP",link:"#/docs/user/references/protocol/http.md",bgColor:"#00D0D9"},{text:"WebService",link:"#/docs/user/references/protocol/webservice.md",bgColor:"#00D0D9"},{text:"Thrift",link:"#/docs/user/references/protocol/thrift.md",bgColor:"#00D0D9"},{text:"Memcached",link:"#/docs/user/references/protocol/memcached.md",bgColor:"#00D0D9"},{text:"Redis",link:"#/docs/user/references/protocol/redis.md",bgColor:"#00D0D9"},{text:"Rest",link:"#/docs/user/references/protocol/rest.md",bgColor:"#00D0D9"},{text:"JsonRPC",link:"https://github.com/apache/incubator-dubbo-rpc-jsonrpc",bgColor:"#00D0D9"},{text:"XmlRPC",link:"https://github.com/dubbo/incubator-dubbo-rpc-xmlrpc",bgColor:"#00D0D9"}]},{title:"Transport",content:o.default.createElement("span",null,"Dubbo支持以下网络传输扩展:"),tags:[{text:"Netty3",link:"",bgColor:"#7A63FC"},{text:"Netty4",link:"#/docs/user/demos/netty4.md",bgColor:"#00D0D9"},{text:"Grizzly",link:"",bgColor:"#00D0D9"},{text:"Jetty",link:"",bgColor:"#00D0D9"},{text:"Mina",link:"",bgColor:"#00D0D9"},{text:"p2p",link:"",bgColor:"#00D0D9"},{text:"Zookeeper",link:"",bgColor:"#00D0D9"}]},{title:"Serialization",content:o.default.createElement("span",null,"Dubbo支持以下序列化机制:"),tags:[{text:"Hessian2",link:"",bgColor:"#7A63FC"},{text:"Java",link:"",bgColor:"#00D0D9"},{text:"JSON",link:"",bgColor:"#00D0D9"},{text:"Fst",link:"",bgColor:"#00D0D9"},{text:"Kryo",link:"",bgColor:"#00D0D9"}]}]}}}},,,,,,function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function a(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function l(e,t,n,r,o){var i={};return Object.keys(r).forEach(function(e){i[e]=r[e]}),i.enumerable=!!i.enumerable,i.configurable=!!i.configurable,("value"in i||i.initializer)&&(i.writable=!0),i=n.slice().reverse().reduce(function(n,r){return r(e,t,n)||n},i),o&&void 0!==i.initializer&&(i.value=i.initializer?i.initializer.call(o):void 0,i.initializer=void 0),void 0===i.initializer&&(Object.defineProperty(e,t,i),i=null),i}Object.defineProperty(t,"__esModule",{value:!0});var c,u=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),s=n(0),d=r(s),f=n(2),p=r(f),m=n(16),b=n(19),g=r(b),h=n(56);n(93);var y=(c=function(e){function t(e){o(this,t);var n=i(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e));return n.container=null,n.state={screenIndex:0,visibleNum:1},n}return a(t,e),u(t,[{key:"componentDidMount",value:function(){var e=this;this.throttleAdjust=(0,h.throttle)(function(){e.setState({visibleNum:e.getVisibleNum()})},200),window.addEventListener("resize",this.throttleAdjust),this.setState({visibleNum:this.getVisibleNum()})}},{key:"componentWillUnmount",value:function(){window.removeEventListener("resize",this.throttleAdjust)}},{key:"getVisibleNum",value:function(){var e=1,t=this.container.getBoundingClientRect().width,n=this.sliderItemChild0.getBoundingClientRect?this.sliderItemChild0.getBoundingClientRect().width:p.default.findDOMNode(this.sliderItemChild0).getBoundingClientRect().width;return t&&n&&(e=Math.floor(t/n)),e||1}},{key:"getListWidth",value:function(){var e=0,t=this.props.children,n=this.state.visibleNum,r=d.default.Children.count(t),o=Math.ceil(r/n);if(this.container){e=this.container.getBoundingClientRect().width*o}return e}},{key:"changeScreen",value:function(e){e!==this.state.screenIndex&&this.setState({screenIndex:e})}},{key:"renderSliderList",value:function(){for(var e=this,t=this.props.children,n=this.state,r=n.screenIndex,o=n.visibleNum,i=[],a=d.default.Children.count(t),l=Math.ceil(a/o),c=0;c<l;c++)i.push(Array.from(t).slice(c*o,(c+1)*o));return d.default.createElement("div",{className:"slider-list",style:{transform:"translateX(-"+r*(this.container&&this.container.getBoundingClientRect().width||0)+"px)",transition:"transform 500ms ease",width:this.getListWidth()}},i.map(function(t,n){return d.default.createElement("div",{className:"slider-screen",style:{width:e.container&&e.container.getBoundingClientRect().width||0},key:n,ref:function(t){e["sliderScreen"+n]=t}},t.map(function(t,r){return d.default.createElement("div",{className:"slider-item",key:r},d.default.cloneElement(t,{ref:function(t){e["sliderItemChild"+(n*o+r)]=t}}))}))}))}},{key:"renderControl",value:function(){for(var e=this.props.children,t=this.state,n=t.screenIndex,r=t.visibleNum,o=d.default.Children.count(e),i=Math.ceil(o/r),a=[],l=0;l<i;l++)a.push(d.default.createElement("span",{key:l,className:(0,g.default)({"slider-control-item":!0,"slider-control-item-active":l===n}),onClick:this.changeScreen.bind(this,l)}));return d.default.createElement("div",{className:"slider-control"},a)}},{key:"render",value:function(){var e=this;return d.default.createElement("div",{className:"slider",ref:function(t){e.container=t}},this.renderSliderList(),this.renderControl())}}]),t}(d.default.Component),l(c.prototype,"getVisibleNum",[m.autobind],Object.getOwnPropertyDescriptor(c.prototype,"getVisibleNum"),c.prototype),l(c.prototype,"getListWidth",[m.autobind],Object.getOwnPropertyDescriptor(c.prototype,"getListWidth"),c.prototype),l(c.prototype,"renderSliderList",[m.autobind],Object.getOwnPropertyDescriptor(c.prototype,"renderSliderList"),c.prototype),l(c.prototype,"renderControl",[m.autobind],Object.getOwnPropertyDescriptor(c.prototype,"renderControl"),c.prototype),c);t.default=y},,function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function i(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function a(e,t,n,r,o){var i={};return Object.keys(r).forEach(function(e){i[e]=r[e]}),i.enumerable=!!i.enumerable,i.configurable=!!i.configurable,("value"in i||i.initializer)&&(i.writable=!0),i=n.slice().reverse().reduce(function(n,r){return r(e,t,n)||n},i),o&&void 0!==i.initializer&&(i.value=i.initializer?i.initializer.call(o):void 0,i.initializer=void 0),void 0===i.initializer&&(Object.defineProperty(e,t,i),i=null),i}Object.defineProperty(t,"__esModule",{value:!0});var l,c=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),u=n(0),s=function(e){return e&&e.__esModule?e:{default:e}}(u),d=n(16),f=(l=function(e){function t(e){r(this,t);var n=o(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e));return n.state={img:e.contact.img},n}return i(t,e),c(t,[{key:"onMouseOver",value:function(){this.setState({img:this.props.contact.imgHover})}},{key:"onMouseOut",value:function(){this.setState({img:this.props.contact.img})}},{key:"render",value:function(){var e=this.props.contact,t=this.state.img;return s.default.createElement("a",{className:"contact-item",href:e.link,target:"__blank",onMouseOver:this.onMouseOver,onMouseOut:this.onMouseOut},s.default.createElement("img",{src:t}),s.default.createElement("div",null,e.title))}}]),t}(s.default.Component),a(l.prototype,"onMouseOver",[d.autobind],Object.getOwnPropertyDescriptor(l.prototype,"onMouseOver"),l.prototype),a(l.prototype,"onMouseOut",[d.autobind],Object.getOwnPropertyDescriptor(l.prototype,"onMouseOut"),l.prototype),l);t.default=f},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(0),o=function(e){return e&&e.__esModule?e:{default:e}}(r),i=function(e){var t=e.contributor,n=t.img,r=t.title,i=t.content;return o.default.createElement("div",{className:"contributor-item"},o.default.createElement("img",{src:n}),o.default.createElement("div",null,r),o.default.createElement("p",null,i))};t.default=i},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(0),o=function(e){return e&&e.__esModule?e:{default:e}}(r),i=function(e){var t=e.eco;return o.default.createElement("div",{className:"eco-item"},o.default.createElement("h4",null,t.title),o.default.createElement("p",null,t.content),o.default.createElement("div",{className:"tags"},t.tags.map(function(e,t){return o.default.createElement("a",{key:t,href:e.link,target:"__blank",style:{background:e.bgColor}},e.text)})))};t.default=i},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function i(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(t,"__esModule",{value:!0});var a=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),l=n(0),c=function(e){return e&&e.__esModule?e:{default:e}}(l),u=n(1),s=function(e){function t(){return r(this,t),o(this,(t.__proto__||Object.getPrototypeOf(t)).apply(this,arguments))}return i(t,e),a(t,[{key:"render",value:function(){var e=this.props.event;return c.default.createElement("div",{className:"event-card"},c.default.createElement("img",{src:e.img}),c.default.createElement("div",{className:"event-introduction"},c.default.createElement("h4",null,e.title),c.default.createElement("p",null,e.content),c.default.createElement(u.Link,{to:e.link},e.dateStr,c.default.createElement("img",{className:"arrow",src:"./img/arrow_right.png"}))))}}]),t}(c.default.Component);t.default=s},,,,function(e,t,n){n(14)(n(102))},,,,function(e,t,n){n(14)(n(106))},,,,,function(e,t){e.exports=".slider {\n  overflow: hidden; }\n  .slider .slider-list {\n    overflow: visible; }\n    .slider .slider-list .slider-screen {\n      display: inline-flex;\n      justify-content: space-around;\n      overflow: hidden; }\n  .slider .slider-control {\n    text-align: center;\n    margin-top: 30px; }\n    .slider .slider-control .slider-control-item {\n      cursor: pointer;\n      display: inline-block;\n      width: 20px;\n      height: 4px;\n      margin-right: 4px;\n      background: #ccc; }\n      .slider .slider-control .slider-control-item-active {\n        width: 40px;\n        height: 6px;\n        background-image: linear-gradient(-90deg, #03DDE4 0%, #30AFED 51%, #8755FF 100%); }\n"},,,,function(e,t){e.exports=".community-page .events-section {\n  max-width: 1280px;\n  margin: 0 auto;\n  box-sizing: border-box;\n  padding: 0 40px; }\n  @media screen and (max-width: 640px) {\n    .community-page .events-section {\n      padding: 0; } }\n  .community-page .events-section h3 {\n    font-family: Avenir-Heavy;\n    font-size: 36px;\n    color: #333;\n    text-align: center;\n    margin: 82px 0 40px; }\n  .community-page .events-section .event-card {\n    width: 373px;\n    font-size: 0; }\n    .community-page .events-section .event-card img {\n      width: 373px;\n      height: 209px; }\n    @media screen and (max-width: 320px) {\n      .community-page .events-section .event-card {\n        width: 320px; }\n        .community-page .events-section .event-card img {\n          width: 320px;\n          height: 179px; } }\n    .community-page .events-section .event-card .event-introduction {\n      padding: 20px;\n      background: #F8F8F8; }\n      .community-page .events-section .event-card .event-introduction h4 {\n        font-family: Avenir-Heavy;\n        font-size: 20px;\n        color: #333;\n        margin: 0 0 10px; }\n      .community-page .events-section .event-card .event-introduction p {\n        font-family: Avenir-Medium;\n        font-size: 14px;\n        color: #666;\n        margin: 0; }\n      .community-page .events-section .event-card .event-introduction a {\n        display: inline-block;\n        width: 100%;\n        font-family: Avenir-Medium;\n        font-size: 12px;\n        color: #999;\n        margin-top: 10px; }\n        .community-page .events-section .event-card .event-introduction a .arrow {\n          width: 8px;\n          height: 13px;\n          float: right; }\n\n.community-page .contact-section {\n  max-width: 1280px;\n  margin: 0 auto;\n  box-sizing: border-box;\n  padding: 120px 40px 0; }\n  @media screen and (max-width: 640px) {\n    .community-page .contact-section {\n      padding-left: 20px;\n      padding-right: 20px; } }\n  .community-page .contact-section h3 {\n    font-family: Avenir-Heavy;\n    font-size: 36px;\n    color: #333;\n    text-align: center;\n    margin: 0 0 20px; }\n  .community-page .contact-section p {\n    font-family: Avenir-Medium;\n    font-size: 14px;\n    color: #666;\n    text-align: center;\n    margin: 0 0 50px; }\n  .community-page .contact-section .contact-list {\n    display: flex;\n    justify-content: space-around;\n    flex-wrap: wrap; }\n    .community-page .contact-section .contact-list .contact-item {\n      display: inline-block;\n      text-align: center;\n      padding: 0 20px;\n      font-family: Avenir-Heavy;\n      font-size: 18px;\n      color: #999; }\n      .community-page .contact-section .contact-list .contact-item:hover {\n        color: #2DACEC; }\n      .community-page .contact-section .contact-list .contact-item img {\n        width: 82px;\n        height: 86px; }\n\n.community-page .contributor-section {\n  max-width: 1280px;\n  margin: 0 auto;\n  box-sizing: border-box;\n  padding: 120px 40px 0; }\n  .community-page .contributor-section h3 {\n    font-family: Avenir-Heavy;\n    font-size: 36px;\n    color: #333;\n    text-align: center;\n    margin: 0 0 20px; }\n  .community-page .contributor-section p {\n    font-family: Avenir-Medium;\n    font-size: 14px;\n    color: #666;\n    text-align: center;\n    margin: 0 0 50px; }\n  .community-page .contributor-section .contributor-list {\n    display: flex;\n    justify-content: space-around;\n    flex-wrap: wrap; }\n    .community-page .contributor-section .contributor-list .contributor-item {\n      display: inline-block;\n      width: 240px;\n      text-align: center;\n      font-family: Avenir-Heavy;\n      font-size: 18px;\n      color: #999; }\n      .community-page .contributor-section .contributor-list .contributor-item img {\n        width: 80px;\n        height: 80px; }\n\n.community-page .eco-section {\n  padding: 120px 20% 0;\n  max-width: 735px;\n  margin: 0 auto; }\n  .community-page .eco-section h3 {\n    font-family: Avenir-Heavy;\n    font-size: 36px;\n    color: #333;\n    text-align: center; }\n  .community-page .eco-section .eco-item {\n    margin-bottom: 30px; }\n    .community-page .eco-section .eco-item h4 {\n      font-family: Avenir-Heavy;\n      font-size: 18px;\n      color: #333;\n      margin: 0 0 10px; }\n    .community-page .eco-section .eco-item p {\n      font-family: Avenir-Medium;\n      font-size: 14px;\n      color: #666;\n      text-align: justify; }\n    .community-page .eco-section .eco-item .tags a {\n      font-family: Avenir-Medium;\n      font-size: 12px;\n      color: #fff;\n      display: inline-block;\n      height: 32px;\n      line-height: 32px;\n      padding: 0 16px;\n      border-radius: 2px;\n      text-align: center;\n      margin: 0 10px 10px 0; }\n"}]);
\ No newline at end of file
diff --git a/build/2c33718a291dda18fd19.js b/build/2c33718a291dda18fd19.js
new file mode 100644
index 0000000..8f6aa8c
--- /dev/null
+++ b/build/2c33718a291dda18fd19.js
@@ -0,0 +1,6 @@
+webpackJsonp([4],[,,,,,,,,,,function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function a(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(t,"__esModule",{value:!0});var c=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),u=n(0),l=r(u),s=n(1),d=n(18),f=r(d),p=n(21),h=r(p),g=n(56),m=n(24),b=r(m),y=n(23),v=r(y),x=n(25),w=r(x),k=n(90),E=r(k),_=n(17),O=r(_),A=n(80),j=r(A);n(99);var S=function(e){function t(e){o(this,t);var n=i(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e));return n.state={headerType:"primary"},n}return a(t,e),c(t,[{key:"componentDidMount",value:function(){var e=this;window.addEventListener("scroll",function(){(0,g.getScrollTop)()>66?e.setState({headerType:"normal"}):e.setState({headerType:"primary"})})}},{key:"render",value:function(){var e=window.location.hash.split("?"),t=h.default.parse(e[1]||""),n=t.lang||f.default.get("docsite_language")||O.default.defaultLanguage;if("en-us"!==n&&"zh-cn"!==n&&(n=O.default.defaultLanguage),n!==f.default.get("docsite_language")&&f.default.set("docsite_language",n,{expires:365,path:""}),!t.lang)return l.default.createElement(s.Redirect,{to:this.props.match.url+"?lang="+n});var r=j.default[n],o=this.state.headerType,i="primary"===o?"./img/dubbo_white.png":"./img/dubbo_colorful.png";return l.default.createElement("div",{className:"home-page"},l.default.createElement("section",{className:"top-section"},l.default.createElement(b.default,{type:o,logo:i,language:n,onLanguageChange:this.onLanguageChange}),l.default.createElement("div",{className:"vertical-middle"},l.default.createElement("img",{src:"./img/dubbo.png"}),l.default.createElement("div",{className:"product-name"},l.default.createElement("h2",null,r.brand.brandName),l.default.createElement("img",{src:"./img/incubating.svg"})),l.default.createElement("p",{className:"product-desc"},r.brand.briefIntroduction),l.default.createElement("div",{className:"button-area"},l.default.createElement(s.Link,{className:"button",to:r.brand.button.link},r.brand.button.text))),l.default.createElement("div",{className:"animation animation1"}),l.default.createElement("div",{className:"animation animation2"}),l.default.createElement("div",{className:"animation animation3"}),l.default.createElement("div",{className:"animation animation4"}),l.default.createElement("div",{className:"animation animation5"})),l.default.createElement("section",{className:"introduction-section"},l.default.createElement("div",{className:"introduction-body"},l.default.createElement("div",{className:"introduction"},l.default.createElement("h3",null,r.introduction.title),l.default.createElement("p",null,r.introduction.desc)),l.default.createElement("img",{src:r.introduction.img}))),l.default.createElement("section",{className:"feature-section"},l.default.createElement("h3",null,r.features.title),l.default.createElement("ul",null,r.features.list.map(function(e,t){return l.default.createElement(E.default,{feature:e,key:t})}))),l.default.createElement("section",{className:"start-section"},l.default.createElement("div",{className:"start-body"},l.default.createElement("div",{className:"left-part"},l.default.createElement("h3",null,r.start.title),l.default.createElement("p",null,r.start.desc),l.default.createElement(s.Link,{to:r.start.button.link},r.start.button.text)),l.default.createElement("div",{className:"right-part"},l.default.createElement("img",{src:"./img/quick_start.png"})))),l.default.createElement("section",{className:"users-section"},l.default.createElement("h3",null,r.users.title),l.default.createElement("p",null,r.users.desc),l.default.createElement("div",{className:"users"},r.users.list.map(function(e,t){return l.default.createElement("img",{src:e,key:t})}))),l.default.createElement(v.default,{logo:"./img/dubbo_gray.png"}))}}]),t}(w.default);t.default=S},,,,,function(e,t,n){"use strict";function r(e,t,n,r){n&&Object.defineProperty(e,t,{enumerable:n.enumerable,configurable:n.configurable,writable:n.writable,value:n.initializer?n.initializer.call(r):void 0})}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t,n,r,o){var i={};return Object.keys(r).forEach(function(e){i[e]=r[e]}),i.enumerable=!!i.enumerable,i.configurable=!!i.configurable,("value"in i||i.initializer)&&(i.writable=!0),i=n.slice().reverse().reduce(function(n,r){return r(e,t,n)||n},i),o&&void 0!==i.initializer&&(i.value=i.initializer?i.initializer.call(o):void 0,i.initializer=void 0),void 0===i.initializer&&(Object.defineProperty(e,t,i),i=null),i}function a(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t<e.length;t++)n[t]=e[t];return n}return Array.from(e)}function c(e){if(!e||!e.hasOwnProperty)return!1;for(var t=["value","initializer","get","set"],n=0,r=t.length;n<r;n++)if(e.hasOwnProperty(t[n]))return!0;return!1}function u(e,t){return c(t[t.length-1])?e.apply(void 0,a(t).concat([[]])):function(){return e.apply(void 0,a(Array.prototype.slice.call(arguments)).concat([t]))}}function l(e){return!1===e.hasOwnProperty(j)&&k(e,j,{value:new A}),e[j]}function s(e){var t={};return S(e).forEach(function(n){return t[n]=E(e,n)}),t}function d(e){return function(t){return Object.defineProperty(this,e,{configurable:!0,writable:!0,enumerable:!0,value:t}),t}}function f(e,t){return e.bind?e.bind(t):function(){return e.apply(t,arguments)}}function p(e){!0!==z[e]&&(z[e]=!0,P("DEPRECATION: "+e))}t.d=u,t.c=l,n.d(t,"g",function(){return S}),t.f=s,t.e=d,t.a=f,n.d(t,"b",function(){return P}),t.h=p;var h,g,m,b,y,v,x=n(20),w="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},k=Object.defineProperty,E=Object.getOwnPropertyDescriptor,_=Object.getOwnPropertyNames,O=Object.getOwnPropertySymbols,A=(h=function e(){o(this,e),r(this,"debounceTimeoutIds",g,this),r(this,"throttleTimeoutIds",m,this),r(this,"throttlePreviousTimestamps",b,this),r(this,"throttleTrailingArgs",y,this),r(this,"profileLastRan",v,this)},g=i(h.prototype,"debounceTimeoutIds",[x.a],{enumerable:!0,initializer:function(){return{}}}),m=i(h.prototype,"throttleTimeoutIds",[x.a],{enumerable:!0,initializer:function(){return{}}}),b=i(h.prototype,"throttlePreviousTimestamps",[x.a],{enumerable:!0,initializer:function(){return{}}}),y=i(h.prototype,"throttleTrailingArgs",[x.a],{enumerable:!0,initializer:function(){return null}}),v=i(h.prototype,"profileLastRan",[x.a],{enumerable:!0,initializer:function(){return null}}),h),j="function"==typeof Symbol?Symbol("__core_decorators__"):"__core_decorators__",S=O?function(e){return _(e).concat(O(e))}:_,P=function(){return"object"===("undefined"==typeof console?"undefined":w(console))&&console&&"function"==typeof console.warn?f(console.warn,console):function(){}}(),z={}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(37);n.d(t,"override",function(){return r.a});var o=n(30);n.d(t,"deprecate",function(){return o.a}),n.d(t,"deprecated",function(){return o.a});var i=n(40);n.d(t,"suppressWarnings",function(){return i.a});var a=n(33);n.d(t,"memoize",function(){return a.a});var c=n(27);n.d(t,"autobind",function(){return c.a});var u=n(39);n.d(t,"readonly",function(){return u.a});var l=n(31);n.d(t,"enumerable",function(){return l.a});var s=n(36);n.d(t,"nonenumerable",function(){return s.a});var d=n(35);n.d(t,"nonconfigurable",function(){return d.a});var f=n(28);n.d(t,"debounce",function(){return f.a});var p=n(41);n.d(t,"throttle",function(){return p.a});var h=n(29);n.d(t,"decorate",function(){return h.a});var g=n(34);n.d(t,"mixin",function(){return g.a}),n.d(t,"mixins",function(){return g.a});var m=n(20);n.d(t,"lazyInitialize",function(){return m.a});var b=n(42);n.d(t,"time",function(){return b.a});var y=n(32);n.d(t,"extendDescriptor",function(){return y.a});var v=n(38);n.d(t,"profile",function(){return v.a});var x=n(26);n.d(t,"applyDecorators",function(){return x.a})},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={defaultLanguage:"en-us","en-us":{pageMenu:[{text:"HOME",link:"/"},{text:"DOCS",link:"/docs/user/quick-start.md"},{text:"BLOG",link:"/blog"},{text:"COMMUNITY",link:"/community"}],disclaimer:{title:"Disclaimer",content:"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 process have stabilized in a manner consistent with other successful ASF projects. While incubation status is not necessarily a reflection of the completeness or stability of the code, it does indicate that the project has yet to be fully endorsed by the ASF."},documentation:{title:"Documentation",list:[{text:"Quick start",link:"/docs/user/quick-start.md"},{text:"Developer guide",link:"/docs/dev/build.md"},{text:"Admin manual",link:"/docs/admin/ops/dubbo-ops.md"}]},resources:{title:"Resources",list:[{text:"Blog",link:"/blog"},{text:"Community",link:"/community"}]},copyright:"Copyright © 2018 The Apache Software Foundation. Apache and the Apache feather logo are trademarks of The Apache Software Foundation."},"zh-cn":{pageMenu:[{text:"首页",link:"/"},{text:"文档",link:"/docs/user/quick-start.md"},{text:"博客",link:"/blog"},{text:"社区",link:"/community"}],disclaimer:{title:"Disclaimer",content:"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 process have stabilized in a manner consistent with other successful ASF projects. While incubation status is not necessarily a reflection of the completeness or stability of the code, it does indicate that the project has yet to be fully endorsed by the ASF."},documentation:{title:"文档",list:[{text:"快速开始",link:"/docs/user/quick-start.md"},{text:"开发者指南",link:"/docs/dev/build.md"},{text:"运维管理",link:"/docs/admin/ops/dubbo-ops.md"}]},resources:{title:"资源",list:[{text:"博客",link:"/blog"},{text:"社区",link:"/community"}]},copyright:"Copyright © 2018 The Apache Software Foundation. Apache and the Apache feather logo are trademarks of The Apache Software Foundation."}}},function(e,t,n){var r,o;!function(i){var a=!1;if(r=i,void 0!==(o="function"==typeof r?r.call(t,n,t,e):r)&&(e.exports=o),a=!0,e.exports=i(),a=!0,!a){var c=window.Cookies,u=window.Cookies=i();u.noConflict=function(){return window.Cookies=c,u}}}(function(){function e(){for(var e=0,t={};e<arguments.length;e++){var n=arguments[e];for(var r in n)t[r]=n[r]}return t}function t(n){function r(t,o,i){var a;if("undefined"!=typeof document){if(arguments.length>1){if(i=e({path:"/"},r.defaults,i),"number"==typeof i.expires){var c=new Date;c.setMilliseconds(c.getMilliseconds()+864e5*i.expires),i.expires=c}i.expires=i.expires?i.expires.toUTCString():"";try{a=JSON.stringify(o),/^[\{\[]/.test(a)&&(o=a)}catch(e){}o=n.write?n.write(o,t):encodeURIComponent(String(o)).replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g,decodeURIComponent),t=encodeURIComponent(String(t)),t=t.replace(/%(23|24|26|2B|5E|60|7C)/g,decodeURIComponent),t=t.replace(/[\(\)]/g,escape);var u="";for(var l in i)i[l]&&(u+="; "+l,!0!==i[l]&&(u+="="+i[l]));return document.cookie=t+"="+o+u}t||(a={});for(var s=document.cookie?document.cookie.split("; "):[],d=/(%[0-9A-Z]{2})+/g,f=0;f<s.length;f++){var p=s[f].split("="),h=p.slice(1).join("=");this.json||'"'!==h.charAt(0)||(h=h.slice(1,-1));try{var g=p[0].replace(d,decodeURIComponent);if(h=n.read?n.read(h,g):n(h,g)||h.replace(d,decodeURIComponent),this.json)try{h=JSON.parse(h)}catch(e){}if(t===g){a=h;break}t||(a[g]=h)}catch(e){}}return a}}return r.set=r,r.get=function(e){return r.call(r,e)},r.getJSON=function(){return r.apply({json:!0},[].slice.call(arguments))},r.defaults={},r.remove=function(t,n){r(t,"",e(n,{expires:-1}))},r.withConverter=t,r}return t(function(){})})},function(e,t,n){var r,o;/*!
+  Copyright (c) 2017 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 c in r)i.call(r,c)&&r[c]&&e.push(c)}}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";function r(e,t,r){var o=r.configurable,c=r.enumerable,u=r.initializer,l=r.value;return{configurable:o,enumerable:c,get:function(){if(this!==e){var n=u?u.call(this):l;return a(this,t,{configurable:o,enumerable:c,writable:!0,value:n}),n}},set:n.i(i.e)(t)}}function o(){for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];return n.i(i.d)(r,t)}t.a=o;var i=n(15),a=Object.defineProperty},function(e,t,n){"use strict";t.decode=t.parse=n(46),t.encode=t.stringify=n(47)},,function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var o=n(0),i=r(o),a=n(18),c=r(a),u=n(1),l=n(17),s=r(l);n(43);var d=function(e){var t=c.default.get("docsite_language")||s.default.defaultLanguage,n=s.default[t];return i.default.createElement("footer",{className:"footer-container"},i.default.createElement("div",{className:"footer-body"},i.default.createElement("img",{src:e.logo}),i.default.createElement("img",{className:"apache",src:"./img/apache_logo.png"}),i.default.createElement("div",{className:"cols-container"},i.default.createElement("div",{className:"col col-12"},i.default.createElement("h3",null,n.disclaimer.title),i.default.createElement("p",null,n.disclaimer.content)),i.default.createElement("div",{className:"col col-6"},i.default.createElement("dl",null,i.default.createElement("dt",null,n.documentation.title),n.documentation.list.map(function(e,t){return i.default.createElement("dd",{key:t},i.default.createElement(u.Link,{to:e.link},e.text))}))),i.default.createElement("div",{className:"col col-6"},i.default.createElement("dl",null,i.default.createElement("dt",null,n.resources.title),n.resources.list.map(function(e,t){return i.default.createElement("dd",{key:t},i.default.createElement(u.Link,{to:e.link},e.text))})))),i.default.createElement("div",{className:"copyright"},i.default.createElement("span",null,n.copyright))))};t.default=d},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function c(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function u(e,t,n,r,o){var i={};return Object.keys(r).forEach(function(e){i[e]=r[e]}),i.enumerable=!!i.enumerable,i.configurable=!!i.configurable,("value"in i||i.initializer)&&(i.writable=!0),i=n.slice().reverse().reduce(function(n,r){return r(e,t,n)||n},i),o&&void 0!==i.initializer&&(i.value=i.initializer?i.initializer.call(o):void 0,i.initializer=void 0),void 0===i.initializer&&(Object.defineProperty(e,t,i),i=null),i}Object.defineProperty(t,"__esModule",{value:!0});var l,s=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),d=n(0),f=r(d),p=n(1),h=n(19),g=r(h),m=n(16),b=n(17),y=r(b);n(44);var v=[{text:"中",value:"en-us"},{text:"En",value:"zh-cn"}],x=function(){},w={type:"primary",language:"en-us",onLanguageChange:x},k=(l=function(e){function t(e){i(this,t);var n=a(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e));return n.state={menuBodyVisible:!1,language:e.language},n}return c(t,e),s(t,[{key:"toggleMenu",value:function(){this.setState({menuBodyVisible:!this.state.menuBodyVisible})}},{key:"switchLang",value:function(){var e=void 0;e="zh-cn"===this.state.language?"en-us":"zh-cn",this.setState({language:e}),this.props.onLanguageChange(e)}},{key:"componentWillReceiveProps",value:function(e){this.setState({language:e.language})}},{key:"render",value:function(){var e=this.props,t=e.type,n=e.logo,r=e.onLanguageChange,i=this.state,a=i.menuBodyVisible,c=i.language;return f.default.createElement("header",{className:(0,g.default)(o({"header-container":!0},"header-container-"+t,!0))},f.default.createElement("div",{className:"header-body"},f.default.createElement(p.Link,{to:"/"},f.default.createElement("img",{className:"logo",alt:y.default.name,title:y.default.name,src:n})),r!==x?f.default.createElement("span",{className:(0,g.default)(o({"language-switch":!0},"language-switch-"+t,!0)),onClick:this.switchLang},v.find(function(e){return e.value===c}).text):null,f.default.createElement("div",{className:(0,g.default)({"header-menu":!0,"header-menu-open":a})},f.default.createElement("img",{className:"header-menu-toggle",onClick:this.toggleMenu,src:"primary"===t?"./img/menu_white.png":"./img/menu_gray.png"}),f.default.createElement("ul",null,y.default[c].pageMenu.map(function(e){var n;return f.default.createElement("li",{className:(0,g.default)((n={"menu-item":!0},o(n,"menu-item-"+t,!0),o(n,"menu-item-"+t+"-active",window.location.hash.split("?")[0].slice(1).split("/")[1]===e.link.split("/")[1]),n))},f.default.createElement(p.Link,{to:e.link},e.text))})))))}}]),t}(f.default.Component),u(l.prototype,"toggleMenu",[m.autobind],Object.getOwnPropertyDescriptor(l.prototype,"toggleMenu"),l.prototype),u(l.prototype,"switchLang",[m.autobind],Object.getOwnPropertyDescriptor(l.prototype,"switchLang"),l.prototype),l);k.defaultProps=w,t.default=k},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function a(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var c,u=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),l=n(0),s=r(l),d=n(16),f=n(18),p=r(f),h=n(21),g=r(h),m=(c=function(e){function t(){return o(this,t),i(this,(t.__proto__||Object.getPrototypeOf(t)).apply(this,arguments))}return a(t,e),u(t,[{key:"onLanguageChange",value:function(e){this.props.location;p.default.set("docsite_language",e,{expires:365,path:""});var t=window.location.hash.split("?");if(t&&t.length){var n=g.default.parse(t[1]||"");n.lang=e,window.location.hash=(t[0]||"")+"?"+g.default.stringify(n)}this.forceUpdate()}}]),t}(s.default.Component),function(e,t,n,r,o){var i={};return Object.keys(r).forEach(function(e){i[e]=r[e]}),i.enumerable=!!i.enumerable,i.configurable=!!i.configurable,("value"in i||i.initializer)&&(i.writable=!0),i=n.slice().reverse().reduce(function(n,r){return r(e,t,n)||n},i),o&&void 0!==i.initializer&&(i.value=i.initializer?i.initializer.call(o):void 0,i.initializer=void 0),void 0===i.initializer&&(Object.defineProperty(e,t,i),i=null),i}(c.prototype,"onLanguageChange",[d.autobind],Object.getOwnPropertyDescriptor(c.prototype,"onLanguageChange"),c.prototype),c);t.default=m},function(e,t,n){"use strict";function r(e,t){var n=e.prototype;for(var r in t)for(var a=t[r],c=0,u=a.length;c<u;c++){var l=a[c];o(n,r,l(n,r,i(n,r)))}return e}t.a=r;var o=Object.defineProperty,i=Object.getOwnPropertyDescriptor},function(e,t,n){"use strict";function r(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t<e.length;t++)n[t]=e[t];return n}return Array.from(e)}function o(e,t){if("undefined"==typeof WeakMap)throw new Error("Using @autobind on "+t.name+"() requires WeakMap support due to its use of super."+t.name+"()\n      See https://github.com/jayphelps/core-decorators.js/issues/20");f||(f=new WeakMap),!1===f.has(e)&&f.set(e,new WeakMap);var r=f.get(e);return!1===r.has(t)&&r.set(t,n.i(l.a)(t,e)),r.get(t)}function i(e){for(var t=n.i(l.f)(e.prototype),r=n.i(l.g)(t),o=0,i=r.length;o<i;o++){var c=r[o],u=t[c];"function"==typeof u.value&&"constructor"!==c&&s(e.prototype,c,a(e.prototype,c,u))}}function a(e,t,r){var i=r.value,a=r.configurable,c=r.enumerable;if("function"!=typeof i)throw new SyntaxError("@autobind can only be used on functions, not: "+i);var u=e.constructor;return{configurable:a,enumerable:c,get:function(){if(this===e)return i;if(this.constructor!==u&&d(this).constructor===u)return i;if(this.constructor!==u&&t in this.constructor.prototype)return o(this,i);var r=n.i(l.a)(i,this);return s(this,t,{configurable:!0,writable:!0,enumerable:!1,value:r}),r},set:n.i(l.e)(t)}}function c(e){return 1===e.length?i.apply(void 0,r(e)):a.apply(void 0,r(e))}function u(){for(var e=arguments.length,t=Array(e),n=0;n<e;n++)t[n]=arguments[n];return 0===t.length?function(){return c(arguments)}:c(t)}t.a=u;var l=n(15),s=Object.defineProperty,d=Object.getPrototypeOf,f=void 0},function(e,t,n){"use strict";function r(e,t,r,o){var l=c(o,2),s=l[0],d=void 0===s?u:s,f=l[1],p=void 0!==f&&f,h=r.value;if("function"!=typeof h)throw new SyntaxError("Only functions can be debounced");return a({},r,{value:function(){var e=this,r=n.i(i.c)(this),o=r.debounceTimeoutIds,a=o[t],c=p&&!a,u=arguments;clearTimeout(a),o[t]=setTimeout(function(){delete o[t],p||h.apply(e,u)},d),c&&h.apply(this,u)}})}function o(){n.i(i.h)("@debounce is deprecated and will be removed shortly. Use @debounce from lodash-decorators.\n\n  https://www.npmjs.com/package/lodash-decorators");for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];return n.i(i.d)(r,t)}t.a=o;var i=n(15),a=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},c=function(){function e(e,t){var n=[],r=!0,o=!1,i=void 0;try{for(var a,c=e[Symbol.iterator]();!(r=(a=c.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(e){o=!0,i=e}finally{try{!r&&c.return&&c.return()}finally{if(o)throw i}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),u=300},function(e,t,n){"use strict";function r(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t<e.length;t++)n[t]=e[t];return n}return Array.from(e)}function o(e){return Array.isArray(e)?e:Array.from(e)}function i(e,t,i,a){var l=o(a),s=l[0],d=l.slice(1),f=i.configurable,p=i.enumerable,h=i.writable,g=i.get,m=i.set,b=i.value,y=!!g;return{configurable:f,enumerable:p,get:function(){var e=y?g.call(this):b,n=s.call.apply(s,[this,e].concat(r(d)));if(y)return n;var o={configurable:f,enumerable:p};return o.value=n,o.writable=h,u(this,t,o),n},set:y?m:n.i(c.e)()}}function a(){for(var e=arguments.length,t=Array(e),r=0;r<e;r++)t[r]=arguments[r];return n.i(c.d)(i,t)}t.a=a;var c=n(15),u=Object.defineProperty},function(e,t,n){"use strict";function r(e,t,r,o){var l=c(o,2),s=l[0],d=void 0===s?u:s,f=l[1],p=void 0===f?{}:f;if("function"!=typeof r.value)throw new SyntaxError("Only functions can be marked as deprecated");var h=e.constructor.name+"#"+t;return p.url&&(d+="\n\n    See "+p.url+" for more details.\n\n"),a({},r,{value:function(){return n.i(i.b)("DEPRECATION "+h+": "+d),r.value.apply(this,arguments)}})}function o(){for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];return n.i(i.d)(r,t)}t.a=o;var i=n(15),a=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},c=function(){function e(e,t){var n=[],r=!0,o=!1,i=void 0;try{for(var a,c=e[Symbol.iterator]();!(r=(a=c.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(e){o=!0,i=e}finally{try{!r&&c.return&&c.return()}finally{if(o)throw i}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),u="This function will be removed in future versions."},function(e,t,n){"use strict";function r(e,t,n){return n.enumerable=!0,n}function o(){for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];return n.i(i.d)(r,t)}t.a=o;var i=n(15)},function(e,t,n){"use strict";function r(e,t,n){var r=c(e),o=u(r,t);return a({},o,{value:n.value,initializer:n.initializer,get:n.get||o.get,set:n.set||o.set})}function o(){for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];return n.i(i.d)(r,t)}t.a=o;var i=n(15),a=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},c=Object.getPrototypeOf,u=Object.getOwnPropertyDescriptor},function(e,t,n){"use strict";function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){return t===Object(t)?t:e[t]||(e[t]={})}function i(e,t,n,r,o){var i=t.apply(e,n);return r[o]=i,i}function a(e){var t=void 0,n=void 0;return e.value?(t=e.value,n="value"):e.get?(t=e.get,n="get"):e.set&&(t=e.set,n="set"),{fn:t,wrapKey:n}}function c(e,t,n){var c=a(n),u=c.fn,l=c.wrapKey,d=new WeakMap,f=Object.create(null),p=Object.create(null),h=0;return s({},n,r({},l,function(){for(var e=arguments.length,t=Array(e),n=0;n<e;n++)t[n]=arguments[n];for(var r="0",a=0,c=t.length;a<c;a++){var l=t[a],s=o(p,l),g=d.get(s);void 0===g&&(g=++h,d.set(s,g)),r+=g}return f[r]||i(this,u,arguments,f,r)}))}function u(){n.i(l.h)("@memoize is deprecated and will be removed shortly. Use @memoize from lodash-decorators.\n\n  https://www.npmjs.com/package/lodash-decorators");for(var e=arguments.length,t=Array(e),r=0;r<e;r++)t[r]=arguments[r];return n.i(l.d)(c,t)}t.a=u;var l=n(15),s=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e}},function(e,t,n){"use strict";function r(e){return"[object Symbol]"===Object.prototype.toString.call(e)&&"object"===(void 0===e?"undefined":u(e))}function o(e,t){if(r(e)){do{if(t===Object.prototype)return void 0!==t[e];if(t.hasOwnProperty(e))return!0}while(t=s(t));return!1}return e in t}function i(e,t){if(!t.length)throw new SyntaxError("@mixin() class "+e.name+" requires at least one mixin as an argument");for(var r=0,i=t.length;r<i;r++)for(var a=n.i(c.f)(t[r]),u=n.i(c.g)(a),s=0,d=u.length;s<d;s++){var f=u[s];o(f,e.prototype)||l(e.prototype,f,a[f])}}function a(){for(var e=arguments.length,t=Array(e),r=0;r<e;r++)t[r]=arguments[r];return n.i(c.h)("@mixin is deprecated and will be removed shortly. Use @mixin from lodash-decorators.\n\n  https://www.npmjs.com/package/lodash-decorators"),"function"==typeof t[0]?i(t[0],[]):function(e){return i(e,t)}}t.a=a;var c=n(15),u="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},l=Object.defineProperty,s=Object.getPrototypeOf},function(e,t,n){"use strict";function r(e,t,n){return n.configurable=!1,n}function o(){for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];return n.i(i.d)(r,t)}t.a=o;var i=n(15)},function(e,t,n){"use strict";function r(e,t,n){return n.enumerable=!1,n}function o(){for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];return n.i(i.d)(r,t)}t.a=o;var i=n(15)},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(e){return e.hasOwnProperty("value")?"data":e.hasOwnProperty("get")||e.hasOwnProperty("set")?"accessor":"data"}function i(e,t,n){n.assert(e.length===t.length)}function a(e,t,n){var r=p(e.value),o=p(t.value);if("undefined"===r&&"undefined"===o&&n.error("descriptor values are both undefined. (class properties are are not currently supported)'"),r!==o){("function"===o&&void 0===r||void 0!==r)&&n.error('value types do not match. {parent} is "'+r+'", {child} is "'+o+'"')}switch(o){case"function":i(e.value,t.value,n);break;default:n.error('Unexpected error. Please file a bug with: {parent} is "'+r+'", {child} is "'+o+'"')}}function c(e,t,n){var r="function"==typeof e.get,o="function"==typeof t.get,a="function"==typeof e.set,c="function"==typeof t.set;(r||o)&&(!r&&a&&n.error("{parent} is setter but {child} is getter"),!o&&c&&n.error("{parent} is getter but {child} is setter"),i(e.get,t.get,n)),(a||c)&&(!a&&r&&n.error("{parent} is getter but {child} is setter"),!c&&o&&n.error("{parent} is setter but {child} is getter"),i(e.set,t.set,n))}function u(e,t,n){var r=o(e),i=o(t);switch(r!==i&&n.error('descriptor types do not match. {parent} is "'+r+'", {child} is "'+i+'"'),i){case"data":a(e,t,n);break;case"accessor":c(e,t,n)}}function l(e,t){for(var n=0,r=b.length;n<r;n++){var o=b[n],i=o(t);if(i in e)return i}return null}function s(e,t,n){n.key=t;var r=Object.getPrototypeOf(e),o=Object.getOwnPropertyDescriptor(r,t),i=new m(r,e,o,n);if(void 0===o){var a=l(r,t),c=a?'\n\n  Did you mean "'+a+'"?':"";i.error("No descriptor matching {child} was found on the prototype chain."+c)}return u(o,n,i),n}function d(){for(var e=arguments.length,t=Array(e),r=0;r<e;r++)t[r]=arguments[r];return n.i(f.d)(s,t)}t.a=d;var f=n(15),p="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},h=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),g=/^function ([_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*)?(\([^\)]*\))[\s\S]+$/,m=function(){function e(t,n,o,i){r(this,e),this.parentKlass=t,this.childKlass=n,this.parentDescriptor=o,this.childDescriptor=i}return h(e,[{key:"_getTopic",value:function(e){return void 0===e?null:"value"in e?e.value:"get"in e?e.get:"set"in e?e.set:void 0}},{key:"_extractTopicSignature",value:function(e){switch(void 0===e?"undefined":p(e)){case"function":return this._extractFunctionSignature(e);default:return this.key}}},{key:"_extractFunctionSignature",value:function(e){var t=this;return e.toString().replace(g,function(e){return(arguments.length>1&&void 0!==arguments[1]?arguments[1]:t.key)+arguments[2]})}},{key:"key",get:function(){return this.childDescriptor.key}},{key:"parentNotation",get:function(){return this.parentKlass.constructor.name+"#"+this.parentPropertySignature}},{key:"childNotation",get:function(){return this.childKlass.constructor.name+"#"+this.childPropertySignature}},{key:"parentTopic",get:function(){return this._getTopic(this.parentDescriptor)}},{key:"childTopic",get:function(){return this._getTopic(this.childDescriptor)}},{key:"parentPropertySignature",get:function(){return this._extractTopicSignature(this.parentTopic)}},{key:"childPropertySignature",get:function(){return this._extractTopicSignature(this.childTopic)}}]),h(e,[{key:"assert",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";!0!==e&&this.error("{child} does not properly override {parent}"+t)}},{key:"error",value:function(e){var t=this;throw e=e.replace("{parent}",function(e){return t.parentNotation}).replace("{child}",function(e){return t.childNotation}),new SyntaxError(e)}}]),e}(),b=[function(e){return e.toLowerCase()},function(e){return e.toUpperCase()},function(e){return e+"s"},function(e){return e.slice(0,-1)},function(e){return e.slice(1,e.length)}]},function(e,t,n){"use strict";function r(e,t,r,l){var s=c(l,3),d=s[0],f=void 0===d?null:d,p=s[1],h=void 0!==p&&p,g=s[2],m=void 0===g?u:g;if(!o.__enabled)return o.__warned||(m.warn("console.profile is not supported. All @profile decorators are disabled."),o.__warned=!0),r;var b=r.value;if(null===f&&(f=e.constructor.name+"."+t),"function"!=typeof b)throw new SyntaxError("@profile can only be used on functions, not: "+b);return a({},r,{value:function(){var e=Date.now(),t=n.i(i.c)(this);(!0===h&&!t.profileLastRan||!1===h||"number"==typeof h&&e-t.profileLastRan>h||"function"==typeof h&&h.apply(this,arguments))&&(m.profile(f),t.profileLastRan=e);try{return b.apply(this,arguments)}finally{m.profileEnd(f)}}})}function o(){for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];return n.i(i.d)(r,t)}t.a=o;var i=n(15),a=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},c=function(){function e(e,t){var n=[],r=!0,o=!1,i=void 0;try{for(var a,c=e[Symbol.iterator]();!(r=(a=c.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(e){o=!0,i=e}finally{try{!r&&c.return&&c.return()}finally{if(o)throw i}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),u=(console,{profile:console.profile?n.i(i.a)(console.profile,console):function(){},profileEnd:console.profileEnd?n.i(i.a)(console.profileEnd,console):function(){},warn:i.b});o.__enabled=!!console.profile,o.__warned=!1},function(e,t,n){"use strict";function r(e,t,n){return n.writable=!1,n}function o(){for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];return n.i(i.d)(r,t)}t.a=o;var i=n(15)},function(e,t,n){"use strict";function r(){}function o(e,t,n){if("object"===("undefined"==typeof console?"undefined":l(console))){var o=console.warn;console.warn=r;var i=t.apply(e,n);return console.warn=o,i}return t.apply(e,n)}function i(e,t,n){return u({},n,{value:function(){return o(this,n.value,arguments)}})}function a(){for(var e=arguments.length,t=Array(e),r=0;r<e;r++)t[r]=arguments[r];return n.i(c.d)(i,t)}t.a=a;var c=n(15),u=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},l="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e}},function(e,t,n){"use strict";function r(e,t,r,o){var l=c(o,2),s=l[0],d=void 0===s?u:s,f=l[1],p=void 0===f?{}:f,h=r.value;if("function"!=typeof h)throw new SyntaxError("Only functions can be throttled");return!1!==p.leading&&(p.leading=!0),!1!==p.trailing&&(p.trailing=!0),a({},r,{value:function(){var e=this,r=n.i(i.c)(this),o=r.throttleTimeoutIds,a=r.throttlePreviousTimestamps,c=o[t],u=a[t]||0,l=Date.now();p.trailing&&(r.throttleTrailingArgs=arguments),u||!1!==p.leading||(u=l);var s=d-(l-u);s<=0?(clearTimeout(c),delete o[t],a[t]=l,h.apply(this,arguments)):!c&&p.trailing&&(o[t]=setTimeout(function(){a[t]=!1===p.leading?0:Date.now(),delete o[t],h.apply(e,r.throttleTrailingArgs),r.throttleTrailingArgs=null},s))}})}function o(){n.i(i.h)("@throttle is deprecated and will be removed shortly. Use @throttle from lodash-decorators.\n\n  https://www.npmjs.com/package/lodash-decorators");for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];return n.i(i.d)(r,t)}t.a=o;var i=n(15),a=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},c=function(){function e(e,t){var n=[],r=!0,o=!1,i=void 0;try{for(var a,c=e[Symbol.iterator]();!(r=(a=c.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(e){o=!0,i=e}finally{try{!r&&c.return&&c.return()}finally{if(o)throw i}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),u=300},function(e,t,n){"use strict";function r(e,t,n,r){var o=c(r,2),i=o[0],u=void 0===i?null:i,d=o[1],f=void 0===d?l:d,p=n.value;if(null===u&&(u=e.constructor.name+"."+t),"function"!=typeof p)throw new SyntaxError("@time can only be used on functions, not: "+p);return a({},n,{value:function(){var e=u+"-"+s;s++,f.time(e);try{return p.apply(this,arguments)}finally{f.timeEnd(e)}}})}function o(){for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];return n.i(i.d)(r,t)}t.a=o;var i=n(15),a=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},c=function(){function e(e,t){var n=[],r=!0,o=!1,i=void 0;try{for(var a,c=e[Symbol.iterator]();!(r=(a=c.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(e){o=!0,i=e}finally{try{!r&&c.return&&c.return()}finally{if(o)throw i}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),u={},l={time:console.time?console.time.bind(console):function(e){u[e]=new Date},timeEnd:console.timeEnd?console.timeEnd.bind(console):function(e){var t=new Date,n=t-u[e];delete u[e],console.log(e+": "+n+"ms")}},s=0},function(e,t,n){n(14)(n(48))},function(e,t,n){n(14)(n(49))},,function(e,t,n){"use strict";function r(e,t){return Object.prototype.hasOwnProperty.call(e,t)}e.exports=function(e,t,n,i){t=t||"&",n=n||"=";var a={};if("string"!=typeof e||0===e.length)return a;var c=/\+/g;e=e.split(t);var u=1e3;i&&"number"==typeof i.maxKeys&&(u=i.maxKeys);var l=e.length;u>0&&l>u&&(l=u);for(var s=0;s<l;++s){var d,f,p,h,g=e[s].replace(c,"%20"),m=g.indexOf(n);m>=0?(d=g.substr(0,m),f=g.substr(m+1)):(d=g,f=""),p=decodeURIComponent(d),h=decodeURIComponent(f),r(a,p)?o(a[p])?a[p].push(h):a[p]=[a[p],h]:a[p]=h}return a};var o=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)}},function(e,t,n){"use strict";function r(e,t){if(e.map)return e.map(t);for(var n=[],r=0;r<e.length;r++)n.push(t(e[r],r));return n}var o=function(e){switch(typeof e){case"string":return e;case"boolean":return e?"true":"false";case"number":return isFinite(e)?e:"";default:return""}};e.exports=function(e,t,n,c){return t=t||"&",n=n||"=",null===e&&(e=void 0),"object"==typeof e?r(a(e),function(a){var c=encodeURIComponent(o(a))+n;return i(e[a])?r(e[a],function(e){return c+encodeURIComponent(o(e))}).join(t):c+encodeURIComponent(o(e[a]))}).join(t):c?encodeURIComponent(o(c))+n+encodeURIComponent(o(e)):""};var i=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)},a=Object.keys||function(e){var t=[];for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&t.push(n);return t}},function(e,t){e.exports=".footer-container {\n  background: #F8F8F8; }\n  .footer-container .footer-body {\n    max-width: 1280px;\n    margin: 0 auto;\n    box-sizing: border-box;\n    padding: 40px 40px 0; }\n    @media screen and (max-width: 640px) {\n      .footer-container .footer-body {\n        padding-left: 20px;\n        padding-right: 20px; } }\n    .footer-container .footer-body img {\n      width: 125px;\n      height: 26px;\n      margin-bottom: 28px;\n      margin-right: 20px;\n      vertical-align: middle; }\n    .footer-container .footer-body .apache {\n      width: 50px;\n      height: 50px; }\n    .footer-container .footer-body .cols-container .col {\n      display: inline-block;\n      box-sizing: border-box;\n      vertical-align: top; }\n    .footer-container .footer-body .cols-container .col-12 {\n      width: 50%;\n      padding-right: 125px; }\n    .footer-container .footer-body .cols-container .col-6 {\n      width: 25%; }\n    .footer-container .footer-body .cols-container h3 {\n      font-family: Avenir-Heavy;\n      font-size: 18px;\n      color: #333;\n      line-height: 18px; }\n    .footer-container .footer-body .cols-container p {\n      font-family: Avenir-Medium;\n      font-size: 12px;\n      color: #999;\n      line-height: 18px; }\n    .footer-container .footer-body .cols-container dl {\n      font-family: Avenir-Heavy;\n      line-height: 18px; }\n    .footer-container .footer-body .cols-container dt {\n      font-weight: bold;\n      font-size: 18px;\n      color: #333;\n      margin-bottom: 20px; }\n    .footer-container .footer-body .cols-container dd {\n      padding: 0;\n      margin: 0; }\n      .footer-container .footer-body .cols-container dd a {\n        text-decoration: none;\n        display: block;\n        font-size: 14px;\n        color: #999;\n        margin: 10px 0; }\n      .footer-container .footer-body .cols-container dd a:hover {\n        color: #2DACEC; }\n    .footer-container .footer-body .copyright {\n      border-top: 1px solid #ccc;\n      min-height: 60px;\n      line-height: 20px;\n      text-align: center;\n      font-family: Avenir-Medium;\n      font-size: 12px;\n      color: #999;\n      display: flex;\n      align-items: center; }\n      .footer-container .footer-body .copyright span {\n        display: inline-block;\n        margin: 0 auto; }\n\n@media screen and (max-width: 640px) {\n  .footer-container .footer-body .cols-container .col {\n    width: 100%;\n    text-align: center;\n    padding: 0; } }\n"},function(e,t){e.exports=".header-container {\n  position: fixed;\n  left: 0;\n  top: 0;\n  width: 100%;\n  z-index: 1000;\n  background-color: #fff; }\n  .header-container-primary {\n    background-color: transparent; }\n  .header-container-normal {\n    background-color: #fff;\n    box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.08); }\n  .header-container .header-body {\n    max-width: 1280px;\n    margin: 0 auto;\n    height: 66px;\n    line-height: 66px; }\n    .header-container .header-body .logo {\n      margin-left: 40px;\n      width: 96px;\n      vertical-align: sub; }\n    .header-container .header-body .header-menu {\n      float: right; }\n      .header-container .header-body .header-menu .header-menu-toggle {\n        display: none;\n        width: 19px;\n        margin-right: 40px;\n        margin-top: 18px;\n        cursor: pointer; }\n    .header-container .header-body ul {\n      padding: 0;\n      margin: 0; }\n    .header-container .header-body li {\n      display: inline-block;\n      margin-right: 40px; }\n    .header-container .header-body .menu-item {\n      font-family: Avenir-Heavy;\n      font-size: 14px; }\n    .header-container .header-body .menu-item-primary a {\n      color: #fff;\n      opacity: 0.6;\n      font-family: Avenir-Medium; }\n    .header-container .header-body .menu-item-primary:hover a {\n      opacity: 1; }\n    .header-container .header-body .menu-item-primary-active a {\n      opacity: 1; }\n    .header-container .header-body .menu-item-normal a {\n      color: #333;\n      opacity: 0.6;\n      font-family: Avenir-Medium; }\n    .header-container .header-body .menu-item-normal:hover a {\n      opacity: 1; }\n    .header-container .header-body .menu-item-normal-active a {\n      opacity: 1; }\n    .header-container .header-body .language-switch {\n      float: right;\n      display: inline-block;\n      box-sizing: border-box;\n      width: 24px;\n      height: 24px;\n      line-height: 20px;\n      margin-top: 21px;\n      margin-right: 40px;\n      text-align: center;\n      border-radius: 2px;\n      cursor: pointer;\n      font-family: PingFangSC-Medium;\n      font-size: 14px;\n      opacity: 0.6; }\n      .header-container .header-body .language-switch:hover {\n        opacity: 1; }\n    .header-container .header-body .language-switch-primary {\n      border: 1px solid #FFF;\n      color: #FFF; }\n    .header-container .header-body .language-switch-normal {\n      border: 1px solid #333;\n      color: #333; }\n\n@media screen and (max-width: 640px) {\n  .header-container .header-body .logo {\n    margin-left: 20px; }\n  .header-container .header-body .language-switch {\n    margin-right: 20px; }\n  .header-container .header-body .header-menu ul {\n    display: none; }\n  .header-container .header-body .header-menu .header-menu-toggle {\n    display: inline-block;\n    margin-right: 20px; }\n  .header-container .header-body .header-menu-open ul {\n    background-color: #f8f8f8;\n    display: inline-block;\n    position: absolute;\n    right: 0;\n    top: 66px;\n    z-index: 100; }\n  .header-container .header-body .header-menu-open li {\n    width: 200px;\n    display: list-item;\n    padding-left: 30px;\n    list-style: none;\n    line-height: 40px;\n    margin-right: 0; }\n    .header-container .header-body .header-menu-open li a {\n      color: #333;\n      display: inline-block;\n      width: 100%; }\n    .header-container .header-body .header-menu-open li:hover {\n      background: #8755FF; }\n      .header-container .header-body .header-menu-open li:hover a {\n        color: #fff;\n        opactiy: 1; }\n  .header-container .header-body .header-menu-open .menu-item-primary-active, .header-container .header-body .header-menu-open .menu-item-normal-active {\n    background: #8755FF; }\n    .header-container .header-body .header-menu-open .menu-item-primary-active a, .header-container .header-body .header-menu-open .menu-item-normal-active a {\n      color: #fff;\n      opactiy: 1; } }\n"},,,,,,,function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});t.throttle=function(e,t){var n=null;return function(){for(var r=arguments.length,o=Array(r),i=0;i<r;i++)o[i]=arguments[i];var a=this;clearTimeout(n),n=setTimeout(function(){e.apply(a,o)},t)}},t.getScrollTop=function(){var e=0;return document.documentElement&&document.documentElement.scrollTop?e=document.documentElement.scrollTop:document.body&&(e=document.body.scrollTop),e}},,,,,,,,,,,,,,,,,,,,,,,,function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(0),o=function(e){return e&&e.__esModule?e:{default:e}}(r);t.default={"zh-cn":{brand:{brandName:"Apache Dubbo",briefIntroduction:"Apache Dubbo™ (incubating)是一款高性能Java RPC框架。",button:{text:"立即开始",link:"/docs/user/quick-start.md"}},introduction:{title:"高性能Java RPC框架",desc:"Apache Dubbo (incubating) |ˈdʌbəʊ| 是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。",img:"./img/architecture.png"},features:{title:"特性一览",list:[{img:"./img/feature_transpart.png",title:"面向接口代理的高性能RPC调用",content:"提供高性能的基于代理的远程调用能力,服务以接口为粒度,为开发者屏蔽远程调用底层细节。"},{img:"./img/feature_loadbalances.png",title:"智能负载均衡",content:"内置多种负载均衡策略,智能感知下游节点健康状况,显著减少调用延迟,提高系统吞吐量。"},{img:"./img/feature_service.png",title:"服务自动注册与发现",content:"支持多种注册中心服务,服务实例上下线实时感知。"},{img:"./img/feature_hogh.png",title:"高度可扩展能力",content:"遵循微内核+插件的设计原则,所有核心能力如Protocol、Transport、Serialization被设计为扩展点,平等对待内置实现和第三方实现。"},{img:"./img/feature_runtime.png",title:"运行期流量调度",content:"内置条件、脚本等路由策略,通过配置不同的路由规则,轻松实现灰度发布,同机房优先等功能。"},{img:"./img/feature_maintenance.png",title:"可视化的服务治理与运维",content:"提供丰富服务治理、运维工具:随时查询服务元数据、服务健康状态及调用统计,实时下发路由策略、调整配置参数。"}]},start:{title:"快速开始",desc:"只需通过几行代码即可快速构建一个Dubbo应用。",img:"./img/quick_start.png",button:{text:"阅读更多",link:"/docs/user/quick-start.md"}},users:{title:"谁在使用Dubbo",desc:o.default.createElement("span",null,"请在 ",o.default.createElement("a",{target:"__blank",href:"https://github.com/apache/incubator-dubbo/issues/1012"},"Wanted: who's using dubbo")," 上提供信息来帮助Dubbo做的更好。"),list:["./img/users_alibaba.png","./img/users_kingdee.png","./img/users_dangdang.png","./img/users_didi.png","./img/users_qunar.png","./img/users_wanglian.png","./img/users_kaola.png","./img/users_zhengcaiyun.png","./img/users_chinalife.png","./img/users_haier.png","./img/users_yinlian.png","./img/users_telecom.png","./img/users_weidian.png","./img/users_icbc.png","./img/users_other1.png","./img/users_other2.png"]}},"en-us":{brand:{brandName:"Apache Dubbo",briefIntroduction:"Apache Dubbo™ (incubating) is a high-performance, java based open source RPC framework.",button:{text:"GET STARTED",link:"/docs/user/quick-start.md"}},introduction:{title:"A high performance Java RPC framework",desc:"Apache Dubbo (incubating) |ˈdʌbəʊ| is a high-performance, light weight, java based RPC framework. Dubbo offers three key functionalities, which include interface based remote call, fault tolerance & load balancing, and automatic service registration & discovery.",img:"./img/architecture.png"},features:{title:"Feature List",list:[{img:"./img/feature_transpart.png",title:"Transparent interface based RPC",content:"Dubbo provides high performance interface based RPC, which is transparent to users."},{img:"./img/feature_loadbalances.png",title:"Intelligent load balancing",content:"Dubbo supports multiple load balancing strategies out of the box, which perceives downstream service status to reduce overall latency and improve system throughput."},{img:"./img/feature_service.png",title:"Automatic service registration and discovery",content:"Dubbo supports multiple service registries, which can detect service online/offline instantly."},{img:"./img/feature_hogh.png",title:"High extensibility",content:"Dubbo's micro-kernel and plugin design ensures that it can easily be extended by third party implementation across core features like Protocol, Transport, and Serialization."},{img:"./img/feature_runtime.png",title:"Runtime traffic routing",content:"Dubbo can be configured at runtime so that traffic can be routed according to different rules, which makes it easy to support features like blue-green deployment, data center aware routing, etc."},{img:"./img/feature_maintenance.png",title:"Visualized service governance",content:"Dubbo provides rich tools for service governance and maintenance such as querying service metadata, health status and statistics."}]},start:{title:"Quick start",desc:"This guide gets you started with dubbo in Java with a simple working example.",img:"./img/quick_start.png",button:{text:"READ MORE",link:"/docs/user/quick-start.md"}},users:{title:"Who is using Dubbo",desc:o.default.createElement("span",null,"Providing your info on ",o.default.createElement("a",{target:"__blank",href:"https://github.com/apache/incubator-dubbo/issues/1012"},"Wanted: who's using dubbo")," to help improving dubbo better"),list:["./img/users_alibaba.png","./img/users_kingdee.png","./img/users_dangdang.png","./img/users_didi.png","./img/users_qunar.png","./img/users_wanglian.png","./img/users_kaola.png","./img/users_zhengcaiyun.png","./img/users_chinalife.png","./img/users_haier.png","./img/users_yinlian.png","./img/users_telecom.png","./img/users_weidian.png","./img/users_icbc.png","./img/users_other1.png","./img/users_other2.png"]}}}},,,,,,,,,,function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(0),o=function(e){return e&&e.__esModule?e:{default:e}}(r),i=function(e){var t=e.feature;return o.default.createElement("li",null,o.default.createElement("img",{src:t.img}),o.default.createElement("div",null,o.default.createElement("h4",null,t.title),o.default.createElement("p",null,t.content)))};t.default=i},,,,,,,,,function(e,t,n){n(14)(n(108))},,,,,,,,,function(e,t){e.exports="@keyframes fallingStar {\n  0% {\n    transform: translate3d(0, -100%, 0); }\n  100% {\n    transform: translate3d(0, 720px, 0); } }\n\n.home-page .top-section {\n  position: relative;\n  height: 720px;\n  background-image: linear-gradient(0deg, #03DDE4 0%, #30AFED 51%, #8755FF 100%); }\n  .home-page .top-section .animation {\n    height: 100%;\n    position: absolute;\n    top: 0;\n    border-right: 1px solid rgba(255, 255, 255, 0.3);\n    transform: scaleX(0.5); }\n    .home-page .top-section .animation::before {\n      position: absolute;\n      background: #fff;\n      content: '';\n      transform: translate3d(0, -100%, 0); }\n    .home-page .top-section .animation1 {\n      left: 15%; }\n      .home-page .top-section .animation1::before {\n        width: 4px;\n        height: 18px;\n        opacity: 0.8;\n        animation: fallingStar 1.3s infinite ease-in-out; }\n    .home-page .top-section .animation2 {\n      left: 30%; }\n      .home-page .top-section .animation2::before {\n        left: -3px;\n        width: 6px;\n        height: 24px;\n        animation: fallingStar 1.5s infinite ease-in-out; }\n    .home-page .top-section .animation3 {\n      left: 52%; }\n      .home-page .top-section .animation3::before {\n        width: 4px;\n        height: 14px;\n        opacity: 0.6;\n        animation: fallingStar 2s infinite ease-in-out; }\n    .home-page .top-section .animation4 {\n      left: 65%; }\n      .home-page .top-section .animation4::before {\n        left: -3px;\n        width: 6px;\n        height: 24px;\n        animation: fallingStar 1.5s infinite ease-in-out; }\n    .home-page .top-section .animation5 {\n      left: 85%; }\n      .home-page .top-section .animation5::before {\n        width: 4px;\n        height: 18px;\n        opacity: 0.8;\n        animation: fallingStar 1.3s infinite ease-in-out; }\n  .home-page .top-section .vertical-middle {\n    position: absolute;\n    left: 0;\n    top: 50%;\n    transform: translateY(-50%);\n    box-sizing: border-box;\n    width: 100%;\n    text-align: center;\n    padding: 0 20px; }\n    .home-page .top-section .vertical-middle > img {\n      position: absolute;\n      z-index: -1;\n      left: 0;\n      top: -36px;\n      width: 100%; }\n  .home-page .top-section .product-name {\n    position: relative;\n    display: inline-block; }\n    .home-page .top-section .product-name h2 {\n      font-family: Avenir-Heavy;\n      font-size: 46px;\n      color: #FFF;\n      text-align: center;\n      word-break: break-word;\n      margin: 0; }\n    .home-page .top-section .product-name img {\n      width: 99px;\n      position: absolute;\n      right: -84px;\n      top: -30px; }\n      @media screen and (max-width: 500px) {\n        .home-page .top-section .product-name img {\n          right: -15px; } }\n  .home-page .top-section .product-desc {\n    opacity: 0.6;\n    font-family: Avenir-Medium;\n    font-size: 24px;\n    color: #FFF;\n    text-align: center;\n    margin: 22px auto 0;\n    max-width: 730px; }\n  .home-page .top-section .button-area {\n    text-align: center;\n    margin-top: 48px; }\n    .home-page .top-section .button-area .button {\n      display: inline-block;\n      background-color: #fff;\n      border-radius: 4px;\n      width: 140px;\n      height: 54px;\n      line-height: 54px;\n      color: #2DACEC;\n      font-family: Avenir-Heavy;\n      font-size: 14px;\n      color: #2DACEC;\n      text-align: center; }\n\n.home-page .introduction-section {\n  background: #F9FAFA; }\n  .home-page .introduction-section .introduction-body {\n    max-width: 1280px;\n    box-sizing: border-box;\n    margin: 0 auto;\n    min-height: 640px;\n    padding: 0 40px;\n    position: relative;\n    display: flex;\n    flex-wrap: wrap;\n    align-items: center;\n    justify-content: space-between; }\n    .home-page .introduction-section .introduction-body::before {\n      content: '';\n      position: absolute;\n      top: 0;\n      left: 40px;\n      height: 155px;\n      opacity: 0.3;\n      border-right: 1px solid #666; }\n    .home-page .introduction-section .introduction-body::after {\n      content: '';\n      position: absolute;\n      left: 39px;\n      top: 138px;\n      width: 3px;\n      height: 17px;\n      background-image: linear-gradient(0deg, #03DDE4 0%, #30AFED 51%, #8755FF 100%); }\n    .home-page .introduction-section .introduction-body .introduction {\n      display: inline-block;\n      width: calc(100% - 726px);\n      min-width: 300px;\n      max-width: 590px; }\n      .home-page .introduction-section .introduction-body .introduction h3 {\n        font-family: Avenir-Heavy;\n        font-size: 36px;\n        color: #333;\n        margin-bottom: 40px;\n        word-break: break-word; }\n      .home-page .introduction-section .introduction-body .introduction p {\n        opacity: 0.56;\n        font-family: Avenir-Medium;\n        font-size: 18px;\n        color: #999; }\n    .home-page .introduction-section .introduction-body img {\n      width: 530px;\n      height: 412px;\n      margin: 0 82px;\n      max-width: 100%;\n      box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.05); }\n    @media screen and (max-width: 1106px) {\n      .home-page .introduction-section .introduction-body .introduction {\n        display: inline-block;\n        width: 100%;\n        max-width: 100%; }\n      .home-page .introduction-section .introduction-body img {\n        margin: 0; } }\n\n.home-page .feature-section {\n  box-sizing: border-box;\n  max-width: 1280px;\n  margin: 0 auto;\n  position: relative;\n  padding: 80px 40px; }\n  .home-page .feature-section::before {\n    content: '';\n    position: absolute;\n    top: 0;\n    left: 50%;\n    transform: translateX(-50%);\n    margin: 0 auto;\n    height: 83px;\n    opacity: 0.3;\n    border-right: 1px solid #666; }\n  .home-page .feature-section::after {\n    content: '';\n    position: absolute;\n    top: 66px;\n    left: 50%;\n    transform: translateX(-50%);\n    width: 3px;\n    height: 17px;\n    background-image: linear-gradient(0deg, #03DDE4 0%, #30AFED 51%, #8755FF 100%); }\n  .home-page .feature-section h3 {\n    font-family: Avenir-Heavy;\n    font-size: 36px;\n    color: #333;\n    text-align: center;\n    margin: 0 0 45px; }\n  .home-page .feature-section ul {\n    list-style: none;\n    padding: 0;\n    margin: 0; }\n    .home-page .feature-section ul li {\n      vertical-align: top;\n      display: inline-block;\n      width: 50%; }\n      .home-page .feature-section ul li img {\n        vertical-align: top;\n        width: 48px;\n        height: 48px;\n        margin-right: 20px; }\n      .home-page .feature-section ul li div {\n        display: inline-block;\n        width: 80%; }\n        .home-page .feature-section ul li div h4 {\n          font-family: Avenir-Heavy;\n          font-size: 20px;\n          color: #333;\n          margin-top: 10px; }\n        .home-page .feature-section ul li div p {\n          font-family: Avenir-Medium;\n          font-size: 14px;\n          line-height: 20px;\n          color: #999; }\n    @media screen and (max-width: 768px) {\n      .home-page .feature-section ul li {\n        width: 100%; } }\n\n.home-page .start-section {\n  background-image: linear-gradient(0deg, #03DDE4 0%, #30AFED 51%, #8755FF 100%); }\n  .home-page .start-section .start-body {\n    max-width: 1280px;\n    margin: 0 auto;\n    box-sizing: border-box;\n    height: 260px;\n    padding: 35px 40px;\n    position: relative; }\n    .home-page .start-section .start-body::before {\n      content: '';\n      position: absolute;\n      top: 0;\n      left: 20px;\n      height: 100%;\n      opacity: 0.3;\n      border-right: 1px solid #fff; }\n    .home-page .start-section .start-body::after {\n      content: '';\n      position: absolute;\n      left: 19px;\n      top: 48px;\n      width: 3px;\n      height: 17px;\n      background: #fff; }\n    .home-page .start-section .start-body .left-part {\n      display: inline-block;\n      width: 50%;\n      vertical-align: top; }\n      .home-page .start-section .start-body .left-part h3 {\n        font-family: Avenir-Heavy;\n        font-size: 36px;\n        color: #FFF;\n        margin: 0; }\n      .home-page .start-section .start-body .left-part p {\n        opacity: 0.8;\n        font-family: Avenir-Medium;\n        font-size: 18px;\n        color: #FFF;\n        line-height: 24px;\n        margin: 6px 0 12px; }\n      .home-page .start-section .start-body .left-part a {\n        font-family: Avenir-Heavy;\n        font-size: 14px;\n        color: #2DACEC;\n        text-align: center;\n        display: inline-block;\n        width: 140px;\n        height: 48px;\n        line-height: 48px;\n        background: #FFF;\n        border-radius: 4px; }\n    .home-page .start-section .start-body .right-part {\n      display: inline-block;\n      width: 50%;\n      font-size: 0;\n      margin-top: 15px; }\n      .home-page .start-section .start-body .right-part img {\n        margin-left: 5%;\n        width: 500px; }\n    @media screen and (max-width: 1050px) {\n      .home-page .start-section .start-body {\n        height: 474px; }\n      .home-page .start-section .start-body .left-part {\n        width: 100%; }\n      .home-page .start-section .start-body .right-part {\n        width: 100%;\n        margin-top: 38px; }\n        .home-page .start-section .start-body .right-part img {\n          max-width: 100%;\n          margin-left: 0; } }\n\n.home-page .users-section {\n  box-sizing: border-box;\n  max-width: 1280px;\n  margin: 0 auto;\n  padding: 80px 40px 0;\n  position: relative; }\n  .home-page .users-section::before {\n    content: '';\n    position: absolute;\n    top: 0;\n    left: 50%;\n    transform: translateX(-50%);\n    margin: 0 auto;\n    height: 83px;\n    opacity: 0.3;\n    border-right: 1px solid #666; }\n  .home-page .users-section::after {\n    content: '';\n    position: absolute;\n    top: 66px;\n    left: 50%;\n    transform: translateX(-50%);\n    width: 3px;\n    height: 17px;\n    background-image: linear-gradient(0deg, #03DDE4 0%, #30AFED 51%, #8755FF 100%); }\n  .home-page .users-section h3 {\n    font-family: Avenir-Heavy;\n    font-size: 36px;\n    color: #333;\n    text-align: center; }\n  .home-page .users-section p {\n    font-family: Avenir-Medium;\n    font-size: 14px;\n    color: #666;\n    text-align: center;\n    margin: 0 0 100px; }\n    .home-page .users-section p a {\n      color: #1e6bb8;\n      text-decoration: none; }\n  .home-page .users-section .users {\n    display: inline-block;\n    text-align: center; }\n    .home-page .users-section .users img {\n      margin-right: 10px;\n      width: 140px;\n      height: 80px;\n      margin-bottom: 80px; }\n\n@media screen and (max-width: 640px) {\n  .home-page .introduction-section {\n    padding: 0 20px; }\n    .home-page .introduction-section::before {\n      left: 20px; }\n    .home-page .introduction-section::after {\n      left: 19px; }\n  .home-page .feature-section, .home-page .users-section {\n    padding-left: 20px;\n    padding-right: 20px; } }\n"}]);
\ No newline at end of file
diff --git a/build/5a6160d1904d4b715bba.js b/build/5a6160d1904d4b715bba.js
new file mode 100644
index 0000000..123e9a5
--- /dev/null
+++ b/build/5a6160d1904d4b715bba.js
@@ -0,0 +1,6 @@
+webpackJsonp([1],[,,,,,,,function(n,e,t){"use strict";function a(n){return n&&n.__esModule?n:{default:n}}function s(n,e){if(!(n instanceof e))throw new TypeError("Cannot call a class as a function")}function o(n,e){if(!n)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?n:e}function r(n,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);n.prototype=Object.create(e&&e.prototype,{constructor:{value:n,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(n,e):n.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var l=function(){function n(n,e){for(var t=0;t<e.length;t++){var a=e[t];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(n,a.key,a)}}return function(e,t,a){return t&&n(e.prototype,t),a&&n(e,a),e}}(),i=t(0),c=a(i),p=t(18),u=a(p),d=t(70),h=t(63),b=a(h),f=t(21),m=a(f),g=t(1),y=t(25),v=a(y),j=t(24),w=a(j),k=t(23),S=a(k),x=t(17),C=a(x),E=t(75),P=a(E);t(96);var A=/^#[^\/]/,I=/^((\.{1,2}\/)|([\w-]+[\/.]))/,O=function(n){function e(){return s(this,e),o(this,(e.__proto__||Object.getPrototypeOf(e)).apply(this,arguments))}return r(e,n),l(e,[{key:"componentDidMount",value:function(){if(this.markdownContainer){window.scrollTo&&window.scrollTo(0,0);var n=this.props.match.url.split("?")[0].split("/").slice(2).join("/"),e=n.split("/").slice(0,-1).join("/"),t=window.location.hash.split("?"),a=m.default.parse(t[1]||""),s=a.lang||u.default.get("docsite_language")||C.default.defaultLanguage,o=Array.from(this.markdownContainer.querySelectorAll("img")),r=Array.from(this.markdownContainer.querySelectorAll("a"));o.forEach(function(n){var t=n.getAttribute("src");I.test(t)&&(n.src=window.location.protocol+"//"+window.location.host+b.default.join(window.location.pathname,"./docs",s,e,t))}),r.forEach(function(n){var t=n.getAttribute("href");I.test(t)&&(n.href=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search+"#/"+b.default.join("./blog",e,t))}),this.markdownContainer.addEventListener("click",function(n){if("a"===n.target.nodeName.toLowerCase()&&n.target.getAttribute("href")&&A.test(n.target.getAttribute("href"))){n.preventDefault();var e=n.target.getAttribute("href").slice(1);d.scroller.scrollTo(e,{duration:1e3,smooth:"easeInOutQuint"})}})}}},{key:"componentDidUpdate",value:function(){this.componentDidMount()}},{key:"render",value:function(){var n=this,e=window.location.hash.split("?"),t=m.default.parse(e[1]||""),a=t.lang||u.default.get("docsite_language")||C.default.defaultLanguage;if("en-us"!==a&&"zh-cn"!==a&&(a=C.default.defaultLanguage),a!==u.default.get("docsite_language")&&u.default.set("docsite_language",a,{expires:365,path:""}),!t.lang)return c.default.createElement(g.Redirect,{to:this.props.match.url+"?lang="+a});var s=this.props.match.url.split("/").slice(2).join("/"),o=P.default[a].find(function(n){return n.filename===s}),r=o&&o.__html?o.__html:"";return c.default.createElement("div",{className:"blog-detail-page"},c.default.createElement(w.default,{type:"normal",logo:"./img/dubbo_colorful.png",language:a,onLanguageChange:this.onLanguageChange}),c.default.createElement("section",{className:"blog-content markdown-body",ref:function(e){n.markdownContainer=e},dangerouslySetInnerHTML:{__html:r}}),c.default.createElement(S.default,{logo:"./img/dubbo_gray.png"}))}}]),e}(v.default);e.default=O},,,,,,,,function(n,e,t){"use strict";function a(n,e,t,a){t&&Object.defineProperty(n,e,{enumerable:t.enumerable,configurable:t.configurable,writable:t.writable,value:t.initializer?t.initializer.call(a):void 0})}function s(n,e){if(!(n instanceof e))throw new TypeError("Cannot call a class as a function")}function o(n,e,t,a,s){var o={};return Object.keys(a).forEach(function(n){o[n]=a[n]}),o.enumerable=!!o.enumerable,o.configurable=!!o.configurable,("value"in o||o.initializer)&&(o.writable=!0),o=t.slice().reverse().reduce(function(t,a){return a(n,e,t)||t},o),s&&void 0!==o.initializer&&(o.value=o.initializer?o.initializer.call(s):void 0,o.initializer=void 0),void 0===o.initializer&&(Object.defineProperty(n,e,o),o=null),o}function r(n){if(Array.isArray(n)){for(var e=0,t=Array(n.length);e<n.length;e++)t[e]=n[e];return t}return Array.from(n)}function l(n){if(!n||!n.hasOwnProperty)return!1;for(var e=["value","initializer","get","set"],t=0,a=e.length;t<a;t++)if(n.hasOwnProperty(e[t]))return!0;return!1}function i(n,e){return l(e[e.length-1])?n.apply(void 0,r(e).concat([[]])):function(){return n.apply(void 0,r(Array.prototype.slice.call(arguments)).concat([e]))}}function c(n){return!1===n.hasOwnProperty(P)&&k(n,P,{value:new E}),n[P]}function p(n){var e={};return A(n).forEach(function(t){return e[t]=S(n,t)}),e}function u(n){return function(e){return Object.defineProperty(this,n,{configurable:!0,writable:!0,enumerable:!0,value:e}),e}}function d(n,e){return n.bind?n.bind(e):function(){return n.apply(e,arguments)}}function h(n){!0!==O[n]&&(O[n]=!0,I("DEPRECATION: "+n))}e.d=i,e.c=c,t.d(e,"g",function(){return A}),e.f=p,e.e=u,e.a=d,t.d(e,"b",function(){return I}),e.h=h;var b,f,m,g,y,v,j=t(20),w="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(n){return typeof n}:function(n){return n&&"function"==typeof Symbol&&n.constructor===Symbol&&n!==Symbol.prototype?"symbol":typeof n},k=Object.defineProperty,S=Object.getOwnPropertyDescriptor,x=Object.getOwnPropertyNames,C=Object.getOwnPropertySymbols,E=(b=function n(){s(this,n),a(this,"debounceTimeoutIds",f,this),a(this,"throttleTimeoutIds",m,this),a(this,"throttlePreviousTimestamps",g,this),a(this,"throttleTrailingArgs",y,this),a(this,"profileLastRan",v,this)},f=o(b.prototype,"debounceTimeoutIds",[j.a],{enumerable:!0,initializer:function(){return{}}}),m=o(b.prototype,"throttleTimeoutIds",[j.a],{enumerable:!0,initializer:function(){return{}}}),g=o(b.prototype,"throttlePreviousTimestamps",[j.a],{enumerable:!0,initializer:function(){return{}}}),y=o(b.prototype,"throttleTrailingArgs",[j.a],{enumerable:!0,initializer:function(){return null}}),v=o(b.prototype,"profileLastRan",[j.a],{enumerable:!0,initializer:function(){return null}}),b),P="function"==typeof Symbol?Symbol("__core_decorators__"):"__core_decorators__",A=C?function(n){return x(n).concat(C(n))}:x,I=function(){return"object"===("undefined"==typeof console?"undefined":w(console))&&console&&"function"==typeof console.warn?d(console.warn,console):function(){}}(),O={}},function(n,e,t){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var a=t(37);t.d(e,"override",function(){return a.a});var s=t(30);t.d(e,"deprecate",function(){return s.a}),t.d(e,"deprecated",function(){return s.a});var o=t(40);t.d(e,"suppressWarnings",function(){return o.a});var r=t(33);t.d(e,"memoize",function(){return r.a});var l=t(27);t.d(e,"autobind",function(){return l.a});var i=t(39);t.d(e,"readonly",function(){return i.a});var c=t(31);t.d(e,"enumerable",function(){return c.a});var p=t(36);t.d(e,"nonenumerable",function(){return p.a});var u=t(35);t.d(e,"nonconfigurable",function(){return u.a});var d=t(28);t.d(e,"debounce",function(){return d.a});var h=t(41);t.d(e,"throttle",function(){return h.a});var b=t(29);t.d(e,"decorate",function(){return b.a});var f=t(34);t.d(e,"mixin",function(){return f.a}),t.d(e,"mixins",function(){return f.a});var m=t(20);t.d(e,"lazyInitialize",function(){return m.a});var g=t(42);t.d(e,"time",function(){return g.a});var y=t(32);t.d(e,"extendDescriptor",function(){return y.a});var v=t(38);t.d(e,"profile",function(){return v.a});var j=t(26);t.d(e,"applyDecorators",function(){return j.a})},function(n,e,t){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.default={defaultLanguage:"en-us","en-us":{pageMenu:[{text:"HOME",link:"/"},{text:"DOCS",link:"/docs/user/quick-start.md"},{text:"BLOG",link:"/blog"},{text:"COMMUNITY",link:"/community"}],disclaimer:{title:"Disclaimer",content:"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 process have stabilized in a manner consistent with other successful ASF projects. While incubation status is not necessarily a reflection of the completeness or stability of the code, it does indicate that the project has yet to be fully endorsed by the ASF."},documentation:{title:"Documentation",list:[{text:"Quick start",link:"/docs/user/quick-start.md"},{text:"Developer guide",link:"/docs/dev/build.md"},{text:"Admin manual",link:"/docs/admin/ops/dubbo-ops.md"}]},resources:{title:"Resources",list:[{text:"Blog",link:"/blog"},{text:"Community",link:"/community"}]},copyright:"Copyright © 2018 The Apache Software Foundation. Apache and the Apache feather logo are trademarks of The Apache Software Foundation."},"zh-cn":{pageMenu:[{text:"首页",link:"/"},{text:"文档",link:"/docs/user/quick-start.md"},{text:"博客",link:"/blog"},{text:"社区",link:"/community"}],disclaimer:{title:"Disclaimer",content:"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 process have stabilized in a manner consistent with other successful ASF projects. While incubation status is not necessarily a reflection of the completeness or stability of the code, it does indicate that the project has yet to be fully endorsed by the ASF."},documentation:{title:"文档",list:[{text:"快速开始",link:"/docs/user/quick-start.md"},{text:"开发者指南",link:"/docs/dev/build.md"},{text:"运维管理",link:"/docs/admin/ops/dubbo-ops.md"}]},resources:{title:"资源",list:[{text:"博客",link:"/blog"},{text:"社区",link:"/community"}]},copyright:"Copyright © 2018 The Apache Software Foundation. Apache and the Apache feather logo are trademarks of The Apache Software Foundation."}}},function(n,e,t){var a,s;!function(o){var r=!1;if(a=o,void 0!==(s="function"==typeof a?a.call(e,t,e,n):a)&&(n.exports=s),r=!0,n.exports=o(),r=!0,!r){var l=window.Cookies,i=window.Cookies=o();i.noConflict=function(){return window.Cookies=l,i}}}(function(){function n(){for(var n=0,e={};n<arguments.length;n++){var t=arguments[n];for(var a in t)e[a]=t[a]}return e}function e(t){function a(e,s,o){var r;if("undefined"!=typeof document){if(arguments.length>1){if(o=n({path:"/"},a.defaults,o),"number"==typeof o.expires){var l=new Date;l.setMilliseconds(l.getMilliseconds()+864e5*o.expires),o.expires=l}o.expires=o.expires?o.expires.toUTCString():"";try{r=JSON.stringify(s),/^[\{\[]/.test(r)&&(s=r)}catch(n){}s=t.write?t.write(s,e):encodeURIComponent(String(s)).replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g,decodeURIComponent),e=encodeURIComponent(String(e)),e=e.replace(/%(23|24|26|2B|5E|60|7C)/g,decodeURIComponent),e=e.replace(/[\(\)]/g,escape);var i="";for(var c in o)o[c]&&(i+="; "+c,!0!==o[c]&&(i+="="+o[c]));return document.cookie=e+"="+s+i}e||(r={});for(var p=document.cookie?document.cookie.split("; "):[],u=/(%[0-9A-Z]{2})+/g,d=0;d<p.length;d++){var h=p[d].split("="),b=h.slice(1).join("=");this.json||'"'!==b.charAt(0)||(b=b.slice(1,-1));try{var f=h[0].replace(u,decodeURIComponent);if(b=t.read?t.read(b,f):t(b,f)||b.replace(u,decodeURIComponent),this.json)try{b=JSON.parse(b)}catch(n){}if(e===f){r=b;break}e||(r[f]=b)}catch(n){}}return r}}return a.set=a,a.get=function(n){return a.call(a,n)},a.getJSON=function(){return a.apply({json:!0},[].slice.call(arguments))},a.defaults={},a.remove=function(e,t){a(e,"",n(t,{expires:-1}))},a.withConverter=e,a}return e(function(){})})},function(n,e,t){var a,s;/*!
+  Copyright (c) 2017 Jed Watson.
+  Licensed under the MIT License (MIT), see
+  http://jedwatson.github.io/classnames
+*/
+!function(){"use strict";function t(){for(var n=[],e=0;e<arguments.length;e++){var a=arguments[e];if(a){var s=typeof a;if("string"===s||"number"===s)n.push(a);else if(Array.isArray(a)&&a.length){var r=t.apply(null,a);r&&n.push(r)}else if("object"===s)for(var l in a)o.call(a,l)&&a[l]&&n.push(l)}}return n.join(" ")}var o={}.hasOwnProperty;void 0!==n&&n.exports?(t.default=t,n.exports=t):(a=[],void 0!==(s=function(){return t}.apply(e,a))&&(n.exports=s))}()},function(n,e,t){"use strict";function a(n,e,a){var s=a.configurable,l=a.enumerable,i=a.initializer,c=a.value;return{configurable:s,enumerable:l,get:function(){if(this!==n){var t=i?i.call(this):c;return r(this,e,{configurable:s,enumerable:l,writable:!0,value:t}),t}},set:t.i(o.e)(e)}}function s(){for(var n=arguments.length,e=Array(n),s=0;s<n;s++)e[s]=arguments[s];return t.i(o.d)(a,e)}e.a=s;var o=t(15),r=Object.defineProperty},function(n,e,t){"use strict";e.decode=e.parse=t(46),e.encode=e.stringify=t(47)},function(n,e,t){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var a=function(n){if(n=n?0===n.indexOf("#")?n:"#"+n:"",history.pushState){var e=window.location;history.pushState(null,null,n?e.pathname+e.search+n:e.pathname+e.search)}else location.hash=n},s=function(){return window.location.hash.replace(/^#/,"")},o=function(n){return function(e){return n.contains?n!=e&&n.contains(e):!!(16&n.compareDocumentPosition(e))}},r=function(n,e){return n===document?e.getBoundingClientRect().top+(window.scrollY||window.pageYOffset):"relative"===getComputedStyle(n).position?e.offsetTop:e.getBoundingClientRect().top+n.scrollTop};e.default={pushHash:a,getHash:s,filterElementInContainer:o,scrollOffset:r}},function(n,e,t){"use strict";function a(n){return n&&n.__esModule?n:{default:n}}Object.defineProperty(e,"__esModule",{value:!0});var s=t(0),o=a(s),r=t(18),l=a(r),i=t(1),c=t(17),p=a(c);t(43);var u=function(n){var e=l.default.get("docsite_language")||p.default.defaultLanguage,t=p.default[e];return o.default.createElement("footer",{className:"footer-container"},o.default.createElement("div",{className:"footer-body"},o.default.createElement("img",{src:n.logo}),o.default.createElement("img",{className:"apache",src:"./img/apache_logo.png"}),o.default.createElement("div",{className:"cols-container"},o.default.createElement("div",{className:"col col-12"},o.default.createElement("h3",null,t.disclaimer.title),o.default.createElement("p",null,t.disclaimer.content)),o.default.createElement("div",{className:"col col-6"},o.default.createElement("dl",null,o.default.createElement("dt",null,t.documentation.title),t.documentation.list.map(function(n,e){return o.default.createElement("dd",{key:e},o.default.createElement(i.Link,{to:n.link},n.text))}))),o.default.createElement("div",{className:"col col-6"},o.default.createElement("dl",null,o.default.createElement("dt",null,t.resources.title),t.resources.list.map(function(n,e){return o.default.createElement("dd",{key:e},o.default.createElement(i.Link,{to:n.link},n.text))})))),o.default.createElement("div",{className:"copyright"},o.default.createElement("span",null,t.copyright))))};e.default=u},function(n,e,t){"use strict";function a(n){return n&&n.__esModule?n:{default:n}}function s(n,e,t){return e in n?Object.defineProperty(n,e,{value:t,enumerable:!0,configurable:!0,writable:!0}):n[e]=t,n}function o(n,e){if(!(n instanceof e))throw new TypeError("Cannot call a class as a function")}function r(n,e){if(!n)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?n:e}function l(n,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);n.prototype=Object.create(e&&e.prototype,{constructor:{value:n,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(n,e):n.__proto__=e)}function i(n,e,t,a,s){var o={};return Object.keys(a).forEach(function(n){o[n]=a[n]}),o.enumerable=!!o.enumerable,o.configurable=!!o.configurable,("value"in o||o.initializer)&&(o.writable=!0),o=t.slice().reverse().reduce(function(t,a){return a(n,e,t)||t},o),s&&void 0!==o.initializer&&(o.value=o.initializer?o.initializer.call(s):void 0,o.initializer=void 0),void 0===o.initializer&&(Object.defineProperty(n,e,o),o=null),o}Object.defineProperty(e,"__esModule",{value:!0});var c,p=function(){function n(n,e){for(var t=0;t<e.length;t++){var a=e[t];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(n,a.key,a)}}return function(e,t,a){return t&&n(e.prototype,t),a&&n(e,a),e}}(),u=t(0),d=a(u),h=t(1),b=t(19),f=a(b),m=t(16),g=t(17),y=a(g);t(44);var v=[{text:"中",value:"en-us"},{text:"En",value:"zh-cn"}],j=function(){},w={type:"primary",language:"en-us",onLanguageChange:j},k=(c=function(n){function e(n){o(this,e);var t=r(this,(e.__proto__||Object.getPrototypeOf(e)).call(this,n));return t.state={menuBodyVisible:!1,language:n.language},t}return l(e,n),p(e,[{key:"toggleMenu",value:function(){this.setState({menuBodyVisible:!this.state.menuBodyVisible})}},{key:"switchLang",value:function(){var n=void 0;n="zh-cn"===this.state.language?"en-us":"zh-cn",this.setState({language:n}),this.props.onLanguageChange(n)}},{key:"componentWillReceiveProps",value:function(n){this.setState({language:n.language})}},{key:"render",value:function(){var n=this.props,e=n.type,t=n.logo,a=n.onLanguageChange,o=this.state,r=o.menuBodyVisible,l=o.language;return d.default.createElement("header",{className:(0,f.default)(s({"header-container":!0},"header-container-"+e,!0))},d.default.createElement("div",{className:"header-body"},d.default.createElement(h.Link,{to:"/"},d.default.createElement("img",{className:"logo",alt:y.default.name,title:y.default.name,src:t})),a!==j?d.default.createElement("span",{className:(0,f.default)(s({"language-switch":!0},"language-switch-"+e,!0)),onClick:this.switchLang},v.find(function(n){return n.value===l}).text):null,d.default.createElement("div",{className:(0,f.default)({"header-menu":!0,"header-menu-open":r})},d.default.createElement("img",{className:"header-menu-toggle",onClick:this.toggleMenu,src:"primary"===e?"./img/menu_white.png":"./img/menu_gray.png"}),d.default.createElement("ul",null,y.default[l].pageMenu.map(function(n){var t;return d.default.createElement("li",{className:(0,f.default)((t={"menu-item":!0},s(t,"menu-item-"+e,!0),s(t,"menu-item-"+e+"-active",window.location.hash.split("?")[0].slice(1).split("/")[1]===n.link.split("/")[1]),t))},d.default.createElement(h.Link,{to:n.link},n.text))})))))}}]),e}(d.default.Component),i(c.prototype,"toggleMenu",[m.autobind],Object.getOwnPropertyDescriptor(c.prototype,"toggleMenu"),c.prototype),i(c.prototype,"switchLang",[m.autobind],Object.getOwnPropertyDescriptor(c.prototype,"switchLang"),c.prototype),c);k.defaultProps=w,e.default=k},function(n,e,t){"use strict";function a(n){return n&&n.__esModule?n:{default:n}}function s(n,e){if(!(n instanceof e))throw new TypeError("Cannot call a class as a function")}function o(n,e){if(!n)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?n:e}function r(n,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);n.prototype=Object.create(e&&e.prototype,{constructor:{value:n,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(n,e):n.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var l,i=function(){function n(n,e){for(var t=0;t<e.length;t++){var a=e[t];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(n,a.key,a)}}return function(e,t,a){return t&&n(e.prototype,t),a&&n(e,a),e}}(),c=t(0),p=a(c),u=t(16),d=t(18),h=a(d),b=t(21),f=a(b),m=(l=function(n){function e(){return s(this,e),o(this,(e.__proto__||Object.getPrototypeOf(e)).apply(this,arguments))}return r(e,n),i(e,[{key:"onLanguageChange",value:function(n){this.props.location;h.default.set("docsite_language",n,{expires:365,path:""});var e=window.location.hash.split("?");if(e&&e.length){var t=f.default.parse(e[1]||"");t.lang=n,window.location.hash=(e[0]||"")+"?"+f.default.stringify(t)}this.forceUpdate()}}]),e}(p.default.Component),function(n,e,t,a,s){var o={};return Object.keys(a).forEach(function(n){o[n]=a[n]}),o.enumerable=!!o.enumerable,o.configurable=!!o.configurable,("value"in o||o.initializer)&&(o.writable=!0),o=t.slice().reverse().reduce(function(t,a){return a(n,e,t)||t},o),s&&void 0!==o.initializer&&(o.value=o.initializer?o.initializer.call(s):void 0,o.initializer=void 0),void 0===o.initializer&&(Object.defineProperty(n,e,o),o=null),o}(l.prototype,"onLanguageChange",[u.autobind],Object.getOwnPropertyDescriptor(l.prototype,"onLanguageChange"),l.prototype),l);e.default=m},function(n,e,t){"use strict";function a(n,e){var t=n.prototype;for(var a in e)for(var r=e[a],l=0,i=r.length;l<i;l++){var c=r[l];s(t,a,c(t,a,o(t,a)))}return n}e.a=a;var s=Object.defineProperty,o=Object.getOwnPropertyDescriptor},function(n,e,t){"use strict";function a(n){if(Array.isArray(n)){for(var e=0,t=Array(n.length);e<n.length;e++)t[e]=n[e];return t}return Array.from(n)}function s(n,e){if("undefined"==typeof WeakMap)throw new Error("Using @autobind on "+e.name+"() requires WeakMap support due to its use of super."+e.name+"()\n      See https://github.com/jayphelps/core-decorators.js/issues/20");d||(d=new WeakMap),!1===d.has(n)&&d.set(n,new WeakMap);var a=d.get(n);return!1===a.has(e)&&a.set(e,t.i(c.a)(e,n)),a.get(e)}function o(n){for(var e=t.i(c.f)(n.prototype),a=t.i(c.g)(e),s=0,o=a.length;s<o;s++){var l=a[s],i=e[l];"function"==typeof i.value&&"constructor"!==l&&p(n.prototype,l,r(n.prototype,l,i))}}function r(n,e,a){var o=a.value,r=a.configurable,l=a.enumerable;if("function"!=typeof o)throw new SyntaxError("@autobind can only be used on functions, not: "+o);var i=n.constructor;return{configurable:r,enumerable:l,get:function(){if(this===n)return o;if(this.constructor!==i&&u(this).constructor===i)return o;if(this.constructor!==i&&e in this.constructor.prototype)return s(this,o);var a=t.i(c.a)(o,this);return p(this,e,{configurable:!0,writable:!0,enumerable:!1,value:a}),a},set:t.i(c.e)(e)}}function l(n){return 1===n.length?o.apply(void 0,a(n)):r.apply(void 0,a(n))}function i(){for(var n=arguments.length,e=Array(n),t=0;t<n;t++)e[t]=arguments[t];return 0===e.length?function(){return l(arguments)}:l(e)}e.a=i;var c=t(15),p=Object.defineProperty,u=Object.getPrototypeOf,d=void 0},function(n,e,t){"use strict";function a(n,e,a,s){var c=l(s,2),p=c[0],u=void 0===p?i:p,d=c[1],h=void 0!==d&&d,b=a.value;if("function"!=typeof b)throw new SyntaxError("Only functions can be debounced");return r({},a,{value:function(){var n=this,a=t.i(o.c)(this),s=a.debounceTimeoutIds,r=s[e],l=h&&!r,i=arguments;clearTimeout(r),s[e]=setTimeout(function(){delete s[e],h||b.apply(n,i)},u),l&&b.apply(this,i)}})}function s(){t.i(o.h)("@debounce is deprecated and will be removed shortly. Use @debounce from lodash-decorators.\n\n  https://www.npmjs.com/package/lodash-decorators");for(var n=arguments.length,e=Array(n),s=0;s<n;s++)e[s]=arguments[s];return t.i(o.d)(a,e)}e.a=s;var o=t(15),r=Object.assign||function(n){for(var e=1;e<arguments.length;e++){var t=arguments[e];for(var a in t)Object.prototype.hasOwnProperty.call(t,a)&&(n[a]=t[a])}return n},l=function(){function n(n,e){var t=[],a=!0,s=!1,o=void 0;try{for(var r,l=n[Symbol.iterator]();!(a=(r=l.next()).done)&&(t.push(r.value),!e||t.length!==e);a=!0);}catch(n){s=!0,o=n}finally{try{!a&&l.return&&l.return()}finally{if(s)throw o}}return t}return function(e,t){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return n(e,t);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),i=300},function(n,e,t){"use strict";function a(n){if(Array.isArray(n)){for(var e=0,t=Array(n.length);e<n.length;e++)t[e]=n[e];return t}return Array.from(n)}function s(n){return Array.isArray(n)?n:Array.from(n)}function o(n,e,o,r){var c=s(r),p=c[0],u=c.slice(1),d=o.configurable,h=o.enumerable,b=o.writable,f=o.get,m=o.set,g=o.value,y=!!f;return{configurable:d,enumerable:h,get:function(){var n=y?f.call(this):g,t=p.call.apply(p,[this,n].concat(a(u)));if(y)return t;var s={configurable:d,enumerable:h};return s.value=t,s.writable=b,i(this,e,s),t},set:y?m:t.i(l.e)()}}function r(){for(var n=arguments.length,e=Array(n),a=0;a<n;a++)e[a]=arguments[a];return t.i(l.d)(o,e)}e.a=r;var l=t(15),i=Object.defineProperty},function(n,e,t){"use strict";function a(n,e,a,s){var c=l(s,2),p=c[0],u=void 0===p?i:p,d=c[1],h=void 0===d?{}:d;if("function"!=typeof a.value)throw new SyntaxError("Only functions can be marked as deprecated");var b=n.constructor.name+"#"+e;return h.url&&(u+="\n\n    See "+h.url+" for more details.\n\n"),r({},a,{value:function(){return t.i(o.b)("DEPRECATION "+b+": "+u),a.value.apply(this,arguments)}})}function s(){for(var n=arguments.length,e=Array(n),s=0;s<n;s++)e[s]=arguments[s];return t.i(o.d)(a,e)}e.a=s;var o=t(15),r=Object.assign||function(n){for(var e=1;e<arguments.length;e++){var t=arguments[e];for(var a in t)Object.prototype.hasOwnProperty.call(t,a)&&(n[a]=t[a])}return n},l=function(){function n(n,e){var t=[],a=!0,s=!1,o=void 0;try{for(var r,l=n[Symbol.iterator]();!(a=(r=l.next()).done)&&(t.push(r.value),!e||t.length!==e);a=!0);}catch(n){s=!0,o=n}finally{try{!a&&l.return&&l.return()}finally{if(s)throw o}}return t}return function(e,t){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return n(e,t);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),i="This function will be removed in future versions."},function(n,e,t){"use strict";function a(n,e,t){return t.enumerable=!0,t}function s(){for(var n=arguments.length,e=Array(n),s=0;s<n;s++)e[s]=arguments[s];return t.i(o.d)(a,e)}e.a=s;var o=t(15)},function(n,e,t){"use strict";function a(n,e,t){var a=l(n),s=i(a,e);return r({},s,{value:t.value,initializer:t.initializer,get:t.get||s.get,set:t.set||s.set})}function s(){for(var n=arguments.length,e=Array(n),s=0;s<n;s++)e[s]=arguments[s];return t.i(o.d)(a,e)}e.a=s;var o=t(15),r=Object.assign||function(n){for(var e=1;e<arguments.length;e++){var t=arguments[e];for(var a in t)Object.prototype.hasOwnProperty.call(t,a)&&(n[a]=t[a])}return n},l=Object.getPrototypeOf,i=Object.getOwnPropertyDescriptor},function(n,e,t){"use strict";function a(n,e,t){return e in n?Object.defineProperty(n,e,{value:t,enumerable:!0,configurable:!0,writable:!0}):n[e]=t,n}function s(n,e){return e===Object(e)?e:n[e]||(n[e]={})}function o(n,e,t,a,s){var o=e.apply(n,t);return a[s]=o,o}function r(n){var e=void 0,t=void 0;return n.value?(e=n.value,t="value"):n.get?(e=n.get,t="get"):n.set&&(e=n.set,t="set"),{fn:e,wrapKey:t}}function l(n,e,t){var l=r(t),i=l.fn,c=l.wrapKey,u=new WeakMap,d=Object.create(null),h=Object.create(null),b=0;return p({},t,a({},c,function(){for(var n=arguments.length,e=Array(n),t=0;t<n;t++)e[t]=arguments[t];for(var a="0",r=0,l=e.length;r<l;r++){var c=e[r],p=s(h,c),f=u.get(p);void 0===f&&(f=++b,u.set(p,f)),a+=f}return d[a]||o(this,i,arguments,d,a)}))}function i(){t.i(c.h)("@memoize is deprecated and will be removed shortly. Use @memoize from lodash-decorators.\n\n  https://www.npmjs.com/package/lodash-decorators");for(var n=arguments.length,e=Array(n),a=0;a<n;a++)e[a]=arguments[a];return t.i(c.d)(l,e)}e.a=i;var c=t(15),p=Object.assign||function(n){for(var e=1;e<arguments.length;e++){var t=arguments[e];for(var a in t)Object.prototype.hasOwnProperty.call(t,a)&&(n[a]=t[a])}return n}},function(n,e,t){"use strict";function a(n){return"[object Symbol]"===Object.prototype.toString.call(n)&&"object"===(void 0===n?"undefined":i(n))}function s(n,e){if(a(n)){do{if(e===Object.prototype)return void 0!==e[n];if(e.hasOwnProperty(n))return!0}while(e=p(e));return!1}return n in e}function o(n,e){if(!e.length)throw new SyntaxError("@mixin() class "+n.name+" requires at least one mixin as an argument");for(var a=0,o=e.length;a<o;a++)for(var r=t.i(l.f)(e[a]),i=t.i(l.g)(r),p=0,u=i.length;p<u;p++){var d=i[p];s(d,n.prototype)||c(n.prototype,d,r[d])}}function r(){for(var n=arguments.length,e=Array(n),a=0;a<n;a++)e[a]=arguments[a];return t.i(l.h)("@mixin is deprecated and will be removed shortly. Use @mixin from lodash-decorators.\n\n  https://www.npmjs.com/package/lodash-decorators"),"function"==typeof e[0]?o(e[0],[]):function(n){return o(n,e)}}e.a=r;var l=t(15),i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(n){return typeof n}:function(n){return n&&"function"==typeof Symbol&&n.constructor===Symbol&&n!==Symbol.prototype?"symbol":typeof n},c=Object.defineProperty,p=Object.getPrototypeOf},function(n,e,t){"use strict";function a(n,e,t){return t.configurable=!1,t}function s(){for(var n=arguments.length,e=Array(n),s=0;s<n;s++)e[s]=arguments[s];return t.i(o.d)(a,e)}e.a=s;var o=t(15)},function(n,e,t){"use strict";function a(n,e,t){return t.enumerable=!1,t}function s(){for(var n=arguments.length,e=Array(n),s=0;s<n;s++)e[s]=arguments[s];return t.i(o.d)(a,e)}e.a=s;var o=t(15)},function(n,e,t){"use strict";function a(n,e){if(!(n instanceof e))throw new TypeError("Cannot call a class as a function")}function s(n){return n.hasOwnProperty("value")?"data":n.hasOwnProperty("get")||n.hasOwnProperty("set")?"accessor":"data"}function o(n,e,t){t.assert(n.length===e.length)}function r(n,e,t){var a=h(n.value),s=h(e.value);if("undefined"===a&&"undefined"===s&&t.error("descriptor values are both undefined. (class properties are are not currently supported)'"),a!==s){("function"===s&&void 0===a||void 0!==a)&&t.error('value types do not match. {parent} is "'+a+'", {child} is "'+s+'"')}switch(s){case"function":o(n.value,e.value,t);break;default:t.error('Unexpected error. Please file a bug with: {parent} is "'+a+'", {child} is "'+s+'"')}}function l(n,e,t){var a="function"==typeof n.get,s="function"==typeof e.get,r="function"==typeof n.set,l="function"==typeof e.set;(a||s)&&(!a&&r&&t.error("{parent} is setter but {child} is getter"),!s&&l&&t.error("{parent} is getter but {child} is setter"),o(n.get,e.get,t)),(r||l)&&(!r&&a&&t.error("{parent} is getter but {child} is setter"),!l&&s&&t.error("{parent} is setter but {child} is getter"),o(n.set,e.set,t))}function i(n,e,t){var a=s(n),o=s(e);switch(a!==o&&t.error('descriptor types do not match. {parent} is "'+a+'", {child} is "'+o+'"'),o){case"data":r(n,e,t);break;case"accessor":l(n,e,t)}}function c(n,e){for(var t=0,a=g.length;t<a;t++){var s=g[t],o=s(e);if(o in n)return o}return null}function p(n,e,t){t.key=e;var a=Object.getPrototypeOf(n),s=Object.getOwnPropertyDescriptor(a,e),o=new m(a,n,s,t);if(void 0===s){var r=c(a,e),l=r?'\n\n  Did you mean "'+r+'"?':"";o.error("No descriptor matching {child} was found on the prototype chain."+l)}return i(s,t,o),t}function u(){for(var n=arguments.length,e=Array(n),a=0;a<n;a++)e[a]=arguments[a];return t.i(d.d)(p,e)}e.a=u;var d=t(15),h="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(n){return typeof n}:function(n){return n&&"function"==typeof Symbol&&n.constructor===Symbol&&n!==Symbol.prototype?"symbol":typeof n},b=function(){function n(n,e){for(var t=0;t<e.length;t++){var a=e[t];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(n,a.key,a)}}return function(e,t,a){return t&&n(e.prototype,t),a&&n(e,a),e}}(),f=/^function ([_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*)?(\([^\)]*\))[\s\S]+$/,m=function(){function n(e,t,s,o){a(this,n),this.parentKlass=e,this.childKlass=t,this.parentDescriptor=s,this.childDescriptor=o}return b(n,[{key:"_getTopic",value:function(n){return void 0===n?null:"value"in n?n.value:"get"in n?n.get:"set"in n?n.set:void 0}},{key:"_extractTopicSignature",value:function(n){switch(void 0===n?"undefined":h(n)){case"function":return this._extractFunctionSignature(n);default:return this.key}}},{key:"_extractFunctionSignature",value:function(n){var e=this;return n.toString().replace(f,function(n){return(arguments.length>1&&void 0!==arguments[1]?arguments[1]:e.key)+arguments[2]})}},{key:"key",get:function(){return this.childDescriptor.key}},{key:"parentNotation",get:function(){return this.parentKlass.constructor.name+"#"+this.parentPropertySignature}},{key:"childNotation",get:function(){return this.childKlass.constructor.name+"#"+this.childPropertySignature}},{key:"parentTopic",get:function(){return this._getTopic(this.parentDescriptor)}},{key:"childTopic",get:function(){return this._getTopic(this.childDescriptor)}},{key:"parentPropertySignature",get:function(){return this._extractTopicSignature(this.parentTopic)}},{key:"childPropertySignature",get:function(){return this._extractTopicSignature(this.childTopic)}}]),b(n,[{key:"assert",value:function(n){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";!0!==n&&this.error("{child} does not properly override {parent}"+e)}},{key:"error",value:function(n){var e=this;throw n=n.replace("{parent}",function(n){return e.parentNotation}).replace("{child}",function(n){return e.childNotation}),new SyntaxError(n)}}]),n}(),g=[function(n){return n.toLowerCase()},function(n){return n.toUpperCase()},function(n){return n+"s"},function(n){return n.slice(0,-1)},function(n){return n.slice(1,n.length)}]},function(n,e,t){"use strict";function a(n,e,a,c){var p=l(c,3),u=p[0],d=void 0===u?null:u,h=p[1],b=void 0!==h&&h,f=p[2],m=void 0===f?i:f;if(!s.__enabled)return s.__warned||(m.warn("console.profile is not supported. All @profile decorators are disabled."),s.__warned=!0),a;var g=a.value;if(null===d&&(d=n.constructor.name+"."+e),"function"!=typeof g)throw new SyntaxError("@profile can only be used on functions, not: "+g);return r({},a,{value:function(){var n=Date.now(),e=t.i(o.c)(this);(!0===b&&!e.profileLastRan||!1===b||"number"==typeof b&&n-e.profileLastRan>b||"function"==typeof b&&b.apply(this,arguments))&&(m.profile(d),e.profileLastRan=n);try{return g.apply(this,arguments)}finally{m.profileEnd(d)}}})}function s(){for(var n=arguments.length,e=Array(n),s=0;s<n;s++)e[s]=arguments[s];return t.i(o.d)(a,e)}e.a=s;var o=t(15),r=Object.assign||function(n){for(var e=1;e<arguments.length;e++){var t=arguments[e];for(var a in t)Object.prototype.hasOwnProperty.call(t,a)&&(n[a]=t[a])}return n},l=function(){function n(n,e){var t=[],a=!0,s=!1,o=void 0;try{for(var r,l=n[Symbol.iterator]();!(a=(r=l.next()).done)&&(t.push(r.value),!e||t.length!==e);a=!0);}catch(n){s=!0,o=n}finally{try{!a&&l.return&&l.return()}finally{if(s)throw o}}return t}return function(e,t){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return n(e,t);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),i=(console,{profile:console.profile?t.i(o.a)(console.profile,console):function(){},profileEnd:console.profileEnd?t.i(o.a)(console.profileEnd,console):function(){},warn:o.b});s.__enabled=!!console.profile,s.__warned=!1},function(n,e,t){"use strict";function a(n,e,t){return t.writable=!1,t}function s(){for(var n=arguments.length,e=Array(n),s=0;s<n;s++)e[s]=arguments[s];return t.i(o.d)(a,e)}e.a=s;var o=t(15)},function(n,e,t){"use strict";function a(){}function s(n,e,t){if("object"===("undefined"==typeof console?"undefined":c(console))){var s=console.warn;console.warn=a;var o=e.apply(n,t);return console.warn=s,o}return e.apply(n,t)}function o(n,e,t){return i({},t,{value:function(){return s(this,t.value,arguments)}})}function r(){for(var n=arguments.length,e=Array(n),a=0;a<n;a++)e[a]=arguments[a];return t.i(l.d)(o,e)}e.a=r;var l=t(15),i=Object.assign||function(n){for(var e=1;e<arguments.length;e++){var t=arguments[e];for(var a in t)Object.prototype.hasOwnProperty.call(t,a)&&(n[a]=t[a])}return n},c="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(n){return typeof n}:function(n){return n&&"function"==typeof Symbol&&n.constructor===Symbol&&n!==Symbol.prototype?"symbol":typeof n}},function(n,e,t){"use strict";function a(n,e,a,s){var c=l(s,2),p=c[0],u=void 0===p?i:p,d=c[1],h=void 0===d?{}:d,b=a.value;if("function"!=typeof b)throw new SyntaxError("Only functions can be throttled");return!1!==h.leading&&(h.leading=!0),!1!==h.trailing&&(h.trailing=!0),r({},a,{value:function(){var n=this,a=t.i(o.c)(this),s=a.throttleTimeoutIds,r=a.throttlePreviousTimestamps,l=s[e],i=r[e]||0,c=Date.now();h.trailing&&(a.throttleTrailingArgs=arguments),i||!1!==h.leading||(i=c);var p=u-(c-i);p<=0?(clearTimeout(l),delete s[e],r[e]=c,b.apply(this,arguments)):!l&&h.trailing&&(s[e]=setTimeout(function(){r[e]=!1===h.leading?0:Date.now(),delete s[e],b.apply(n,a.throttleTrailingArgs),a.throttleTrailingArgs=null},p))}})}function s(){t.i(o.h)("@throttle is deprecated and will be removed shortly. Use @throttle from lodash-decorators.\n\n  https://www.npmjs.com/package/lodash-decorators");for(var n=arguments.length,e=Array(n),s=0;s<n;s++)e[s]=arguments[s];return t.i(o.d)(a,e)}e.a=s;var o=t(15),r=Object.assign||function(n){for(var e=1;e<arguments.length;e++){var t=arguments[e];for(var a in t)Object.prototype.hasOwnProperty.call(t,a)&&(n[a]=t[a])}return n},l=function(){function n(n,e){var t=[],a=!0,s=!1,o=void 0;try{for(var r,l=n[Symbol.iterator]();!(a=(r=l.next()).done)&&(t.push(r.value),!e||t.length!==e);a=!0);}catch(n){s=!0,o=n}finally{try{!a&&l.return&&l.return()}finally{if(s)throw o}}return t}return function(e,t){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return n(e,t);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),i=300},function(n,e,t){"use strict";function a(n,e,t,a){var s=l(a,2),o=s[0],i=void 0===o?null:o,u=s[1],d=void 0===u?c:u,h=t.value;if(null===i&&(i=n.constructor.name+"."+e),"function"!=typeof h)throw new SyntaxError("@time can only be used on functions, not: "+h);return r({},t,{value:function(){var n=i+"-"+p;p++,d.time(n);try{return h.apply(this,arguments)}finally{d.timeEnd(n)}}})}function s(){for(var n=arguments.length,e=Array(n),s=0;s<n;s++)e[s]=arguments[s];return t.i(o.d)(a,e)}e.a=s;var o=t(15),r=Object.assign||function(n){for(var e=1;e<arguments.length;e++){var t=arguments[e];for(var a in t)Object.prototype.hasOwnProperty.call(t,a)&&(n[a]=t[a])}return n},l=function(){function n(n,e){var t=[],a=!0,s=!1,o=void 0;try{for(var r,l=n[Symbol.iterator]();!(a=(r=l.next()).done)&&(t.push(r.value),!e||t.length!==e);a=!0);}catch(n){s=!0,o=n}finally{try{!a&&l.return&&l.return()}finally{if(s)throw o}}return t}return function(e,t){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return n(e,t);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),i={},c={time:console.time?console.time.bind(console):function(n){i[n]=new Date},timeEnd:console.timeEnd?console.timeEnd.bind(console):function(n){var e=new Date,t=e-i[n];delete i[n],console.log(n+": "+t+"ms")}},p=0},function(n,e,t){t(14)(t(48))},function(n,e,t){t(14)(t(49))},function(n,e,t){n.exports=t(65)()},function(n,e,t){"use strict";function a(n,e){return Object.prototype.hasOwnProperty.call(n,e)}n.exports=function(n,e,t,o){e=e||"&",t=t||"=";var r={};if("string"!=typeof n||0===n.length)return r;var l=/\+/g;n=n.split(e);var i=1e3;o&&"number"==typeof o.maxKeys&&(i=o.maxKeys);var c=n.length;i>0&&c>i&&(c=i);for(var p=0;p<c;++p){var u,d,h,b,f=n[p].replace(l,"%20"),m=f.indexOf(t);m>=0?(u=f.substr(0,m),d=f.substr(m+1)):(u=f,d=""),h=decodeURIComponent(u),b=decodeURIComponent(d),a(r,h)?s(r[h])?r[h].push(b):r[h]=[r[h],b]:r[h]=b}return r};var s=Array.isArray||function(n){return"[object Array]"===Object.prototype.toString.call(n)}},function(n,e,t){"use strict";function a(n,e){if(n.map)return n.map(e);for(var t=[],a=0;a<n.length;a++)t.push(e(n[a],a));return t}var s=function(n){switch(typeof n){case"string":return n;case"boolean":return n?"true":"false";case"number":return isFinite(n)?n:"";default:return""}};n.exports=function(n,e,t,l){return e=e||"&",t=t||"=",null===n&&(n=void 0),"object"==typeof n?a(r(n),function(r){var l=encodeURIComponent(s(r))+t;return o(n[r])?a(n[r],function(n){return l+encodeURIComponent(s(n))}).join(e):l+encodeURIComponent(s(n[r]))}).join(e):l?encodeURIComponent(s(l))+t+encodeURIComponent(s(n)):""};var o=Array.isArray||function(n){return"[object Array]"===Object.prototype.toString.call(n)},r=Object.keys||function(n){var e=[];for(var t in n)Object.prototype.hasOwnProperty.call(n,t)&&e.push(t);return e}},function(n,e){n.exports=".footer-container {\n  background: #F8F8F8; }\n  .footer-container .footer-body {\n    max-width: 1280px;\n    margin: 0 auto;\n    box-sizing: border-box;\n    padding: 40px 40px 0; }\n    @media screen and (max-width: 640px) {\n      .footer-container .footer-body {\n        padding-left: 20px;\n        padding-right: 20px; } }\n    .footer-container .footer-body img {\n      width: 125px;\n      height: 26px;\n      margin-bottom: 28px;\n      margin-right: 20px;\n      vertical-align: middle; }\n    .footer-container .footer-body .apache {\n      width: 50px;\n      height: 50px; }\n    .footer-container .footer-body .cols-container .col {\n      display: inline-block;\n      box-sizing: border-box;\n      vertical-align: top; }\n    .footer-container .footer-body .cols-container .col-12 {\n      width: 50%;\n      padding-right: 125px; }\n    .footer-container .footer-body .cols-container .col-6 {\n      width: 25%; }\n    .footer-container .footer-body .cols-container h3 {\n      font-family: Avenir-Heavy;\n      font-size: 18px;\n      color: #333;\n      line-height: 18px; }\n    .footer-container .footer-body .cols-container p {\n      font-family: Avenir-Medium;\n      font-size: 12px;\n      color: #999;\n      line-height: 18px; }\n    .footer-container .footer-body .cols-container dl {\n      font-family: Avenir-Heavy;\n      line-height: 18px; }\n    .footer-container .footer-body .cols-container dt {\n      font-weight: bold;\n      font-size: 18px;\n      color: #333;\n      margin-bottom: 20px; }\n    .footer-container .footer-body .cols-container dd {\n      padding: 0;\n      margin: 0; }\n      .footer-container .footer-body .cols-container dd a {\n        text-decoration: none;\n        display: block;\n        font-size: 14px;\n        color: #999;\n        margin: 10px 0; }\n      .footer-container .footer-body .cols-container dd a:hover {\n        color: #2DACEC; }\n    .footer-container .footer-body .copyright {\n      border-top: 1px solid #ccc;\n      min-height: 60px;\n      line-height: 20px;\n      text-align: center;\n      font-family: Avenir-Medium;\n      font-size: 12px;\n      color: #999;\n      display: flex;\n      align-items: center; }\n      .footer-container .footer-body .copyright span {\n        display: inline-block;\n        margin: 0 auto; }\n\n@media screen and (max-width: 640px) {\n  .footer-container .footer-body .cols-container .col {\n    width: 100%;\n    text-align: center;\n    padding: 0; } }\n"},function(n,e){n.exports=".header-container {\n  position: fixed;\n  left: 0;\n  top: 0;\n  width: 100%;\n  z-index: 1000;\n  background-color: #fff; }\n  .header-container-primary {\n    background-color: transparent; }\n  .header-container-normal {\n    background-color: #fff;\n    box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.08); }\n  .header-container .header-body {\n    max-width: 1280px;\n    margin: 0 auto;\n    height: 66px;\n    line-height: 66px; }\n    .header-container .header-body .logo {\n      margin-left: 40px;\n      width: 96px;\n      vertical-align: sub; }\n    .header-container .header-body .header-menu {\n      float: right; }\n      .header-container .header-body .header-menu .header-menu-toggle {\n        display: none;\n        width: 19px;\n        margin-right: 40px;\n        margin-top: 18px;\n        cursor: pointer; }\n    .header-container .header-body ul {\n      padding: 0;\n      margin: 0; }\n    .header-container .header-body li {\n      display: inline-block;\n      margin-right: 40px; }\n    .header-container .header-body .menu-item {\n      font-family: Avenir-Heavy;\n      font-size: 14px; }\n    .header-container .header-body .menu-item-primary a {\n      color: #fff;\n      opacity: 0.6;\n      font-family: Avenir-Medium; }\n    .header-container .header-body .menu-item-primary:hover a {\n      opacity: 1; }\n    .header-container .header-body .menu-item-primary-active a {\n      opacity: 1; }\n    .header-container .header-body .menu-item-normal a {\n      color: #333;\n      opacity: 0.6;\n      font-family: Avenir-Medium; }\n    .header-container .header-body .menu-item-normal:hover a {\n      opacity: 1; }\n    .header-container .header-body .menu-item-normal-active a {\n      opacity: 1; }\n    .header-container .header-body .language-switch {\n      float: right;\n      display: inline-block;\n      box-sizing: border-box;\n      width: 24px;\n      height: 24px;\n      line-height: 20px;\n      margin-top: 21px;\n      margin-right: 40px;\n      text-align: center;\n      border-radius: 2px;\n      cursor: pointer;\n      font-family: PingFangSC-Medium;\n      font-size: 14px;\n      opacity: 0.6; }\n      .header-container .header-body .language-switch:hover {\n        opacity: 1; }\n    .header-container .header-body .language-switch-primary {\n      border: 1px solid #FFF;\n      color: #FFF; }\n    .header-container .header-body .language-switch-normal {\n      border: 1px solid #333;\n      color: #333; }\n\n@media screen and (max-width: 640px) {\n  .header-container .header-body .logo {\n    margin-left: 20px; }\n  .header-container .header-body .language-switch {\n    margin-right: 20px; }\n  .header-container .header-body .header-menu ul {\n    display: none; }\n  .header-container .header-body .header-menu .header-menu-toggle {\n    display: inline-block;\n    margin-right: 20px; }\n  .header-container .header-body .header-menu-open ul {\n    background-color: #f8f8f8;\n    display: inline-block;\n    position: absolute;\n    right: 0;\n    top: 66px;\n    z-index: 100; }\n  .header-container .header-body .header-menu-open li {\n    width: 200px;\n    display: list-item;\n    padding-left: 30px;\n    list-style: none;\n    line-height: 40px;\n    margin-right: 0; }\n    .header-container .header-body .header-menu-open li a {\n      color: #333;\n      display: inline-block;\n      width: 100%; }\n    .header-container .header-body .header-menu-open li:hover {\n      background: #8755FF; }\n      .header-container .header-body .header-menu-open li:hover a {\n        color: #fff;\n        opactiy: 1; }\n  .header-container .header-body .header-menu-open .menu-item-primary-active, .header-container .header-body .header-menu-open .menu-item-normal-active {\n    background: #8755FF; }\n    .header-container .header-body .header-menu-open .menu-item-primary-active a, .header-container .header-body .header-menu-open .menu-item-normal-active a {\n      color: #fff;\n      opactiy: 1; } }\n"},function(n,e,t){"use strict";function a(n){return n&&n.__esModule?n:{default:n}}Object.defineProperty(e,"__esModule",{value:!0});var s=Object.assign||function(n){for(var e=1;e<arguments.length;e++){var t=arguments[e];for(var a in t)Object.prototype.hasOwnProperty.call(t,a)&&(n[a]=t[a])}return n},o=t(22),r=a(o),l=t(59),i=a(l),c=t(52),p=a(c),u={},d=void 0;e.default={unmount:function(){u={}},register:function(n,e){u[n]=e},unregister:function(n){delete u[n]},get:function(n){return u[n]||document.getElementById(n)||document.getElementsByName(n)[0]||document.getElementsByClassName(n)[0]},setActiveLink:function(n){return d=n},getActiveLink:function(){return d},scrollTo:function(n,e){var t=this.get(n);if(!t)return void console.warn("target Element not found");e=s({},e,{absolute:!1});var a=e.containerId,o=e.container,l=void 0;l=a?document.getElementById(a):o&&o.nodeType?o:document,p.default.registered.begin&&p.default.registered.begin(n,t),e.absolute=!0;var c=r.default.scrollOffset(l,t)+(e.offset||0);if(!e.smooth)return l===document?window.scrollTo(0,c):l.scrollTop=c,void(p.default.registered.end&&p.default.registered.end(n,t));i.default.animateTopScroll(c,e,n,t)}}},function(n,e,t){"use strict";Object.defineProperty(e,"__esModule",{value:!0});e.addPassiveEventListener=function(n,e,t){var a=function(){var n=!1;try{var e=Object.defineProperty({},"passive",{get:function(){n=!0}});window.addEventListener("test",null,e)}catch(n){}return n}();n.addEventListener(e,t,!!a&&{passive:!0})},e.removePassiveEventListener=function(n,e,t){n.removeEventListener(e,t)}},function(n,e,t){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var a={registered:{},scrollEvent:{register:function(n,e){a.registered[n]=e},remove:function(n){a.registered[n]=null}}};e.default=a},function(n,e,t){"use strict";function a(n){return n&&n.__esModule?n:{default:n}}function s(n,e){if(!(n instanceof e))throw new TypeError("Cannot call a class as a function")}function o(n,e){if(!n)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?n:e}function r(n,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);n.prototype=Object.create(e&&e.prototype,{constructor:{value:n,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(n,e):n.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var l=Object.assign||function(n){for(var e=1;e<arguments.length;e++){var t=arguments[e];for(var a in t)Object.prototype.hasOwnProperty.call(t,a)&&(n[a]=t[a])}return n},i=function(){function n(n,e){for(var t=0;t<e.length;t++){var a=e[t];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(n,a.key,a)}}return function(e,t,a){return t&&n(e.prototype,t),a&&n(e,a),e}}(),c=t(0),p=a(c),u=t(2),d=(a(u),t(22)),h=(a(d),t(54)),b=a(h),f=t(50),m=a(f),g=t(45),y=a(g),v=t(61),j=a(v),w={to:y.default.string.isRequired,containerId:y.default.string,container:y.default.object,activeClass:y.default.string,spy:y.default.bool,smooth:y.default.oneOfType([y.default.bool,y.default.string]),offset:y.default.number,delay:y.default.number,isDynamic:y.default.bool,onClick:y.default.func,duration:y.default.oneOfType([y.default.number,y.default.func]),absolute:y.default.bool,onSetActive:y.default.func,onSetInactive:y.default.func,ignoreCancelEvents:y.default.bool,hashSpy:y.default.bool};e.default=function(n,e){var t=e||m.default,a=function(e){function a(n){s(this,a);var e=o(this,(a.__proto__||Object.getPrototypeOf(a)).call(this,n));return c.call(e),e.state={active:!1},e}return r(a,e),i(a,[{key:"getScrollSpyContainer",value:function(){var n=this.props.containerId,e=this.props.container;return n&&!e?document.getElementById(n):e&&e.nodeType?e:document}},{key:"componentDidMount",value:function(){if(this.props.spy||this.props.hashSpy){var n=this.getScrollSpyContainer();b.default.isMounted(n)||b.default.mount(n),this.props.hashSpy&&(j.default.isMounted()||j.default.mount(t),j.default.mapContainer(this.props.to,n)),b.default.addSpyHandler(this.spyHandler,n),this.setState({container:n})}}},{key:"componentWillUnmount",value:function(){b.default.unmount(this.stateHandler,this.spyHandler)}},{key:"render",value:function(){var e="";e=this.state&&this.state.active?((this.props.className||"")+" "+(this.props.activeClass||"active")).trim():this.props.className;var t=l({},this.props);for(var a in w)t.hasOwnProperty(a)&&delete t[a];return t.className=e,t.onClick=this.handleClick,p.default.createElement(n,t)}}]),a}(p.default.PureComponent),c=function(){var n=this;this.scrollTo=function(e,a){t.scrollTo(e,l({},n.state,a))},this.handleClick=function(e){n.props.onClick&&n.props.onClick(e),e.stopPropagation&&e.stopPropagation(),e.preventDefault&&e.preventDefault(),n.scrollTo(n.props.to,n.props)},this.spyHandler=function(e){var a=n.getScrollSpyContainer();if(!j.default.isMounted()||j.default.isInitialized()){var s=n.props.to,o=null,r=0,l=0,i=0;if(a.getBoundingClientRect){i=a.getBoundingClientRect().top}if(!o||n.props.isDynamic){if(!(o=t.get(s)))return;var c=o.getBoundingClientRect();r=c.top-i+e,l=r+c.height}var p=e-n.props.offset,u=p>=Math.floor(r)&&p<Math.floor(l),d=p<Math.floor(r)||p>=Math.floor(l),h=t.getActiveLink();d&&(s===h&&t.setActiveLink(void 0),n.props.hashSpy&&j.default.getHash()===s&&j.default.changeHash(),n.props.spy&&n.state.active&&(n.setState({active:!1}),n.props.onSetInactive&&n.props.onSetInactive(s,o))),!u||h===s&&!1!==n.state.active||(t.setActiveLink(s),n.props.hashSpy&&j.default.changeHash(s),n.props.spy&&(n.setState({active:!0}),n.props.onSetActive&&n.props.onSetActive(s,o)))}}};return a.propTypes=w,a.defaultProps={offset:0},a}},function(n,e,t){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var a=t(62),s=function(n){return n&&n.__esModule?n:{default:n}}(a),o=t(51),r=function(n){return(0,s.default)(n,66)},l={spyCallbacks:[],spySetState:[],scrollSpyContainers:[],mount:function(n){if(n){var e=r(function(e){l.scrollHandler(n)});l.scrollSpyContainers.push(n),(0,o.addPassiveEventListener)(n,"scroll",e)}},isMounted:function(n){return-1!==l.scrollSpyContainers.indexOf(n)},currentPositionY:function(n){if(n===document){var e=void 0!==window.pageXOffset,t="CSS1Compat"===(document.compatMode||"");return e?window.pageYOffset:t?document.documentElement.scrollTop:document.body.scrollTop}return n.scrollTop},scrollHandler:function(n){(l.scrollSpyContainers[l.scrollSpyContainers.indexOf(n)].spyCallbacks||[]).forEach(function(e){return e(l.currentPositionY(n))})},addStateHandler:function(n){l.spySetState.push(n)},addSpyHandler:function(n,e){var t=l.scrollSpyContainers[l.scrollSpyContainers.indexOf(e)];t.spyCallbacks||(t.spyCallbacks=[]),t.spyCallbacks.push(n),n(l.currentPositionY(e))},updateStates:function(){l.spySetState.forEach(function(n){return n()})},unmount:function(n,e){l.scrollSpyContainers.forEach(function(n){return n.spyCallbacks&&n.spyCallbacks.length&&n.spyCallbacks.splice(n.spyCallbacks.indexOf(e),1)}),l.spySetState&&l.spySetState.length&&l.spySetState.splice(l.spySetState.indexOf(n),1),document.removeEventListener("scroll",l.scrollHandler)},update:function(){return l.scrollSpyContainers.forEach(function(n){return l.scrollHandler(n)})}};e.default=l},,,,,function(n,e,t){"use strict";function a(n){return n&&n.__esModule?n:{default:n}}Object.defineProperty(e,"__esModule",{value:!0});var s=Object.assign||function(n){for(var e=1;e<arguments.length;e++){var t=arguments[e];for(var a in t)Object.prototype.hasOwnProperty.call(t,a)&&(n[a]=t[a])}return n},o=t(22),r=(a(o),t(73)),l=a(r),i=t(72),c=a(i),p=t(52),u=a(p),d=function(n){return l.default[n.smooth]||l.default.defaultEasing},h=function(n){return"function"==typeof n?n:function(){return n}},b=function(){if("undefined"!=typeof window)return window.requestAnimationFrame||window.webkitRequestAnimationFrame},f=function(){return b()||function(n,e,t){window.setTimeout(n,t||1e3/60,(new Date).getTime())}}(),m=function(){return{currentPositionY:0,startPositionY:0,targetPositionY:0,progress:0,duration:0,cancel:!1,target:null,containerElement:null,to:null,start:null,deltaTop:null,percent:null,delayTimeout:null}},g=function(n){var e=n.data.containerElement;if(e&&e!==document&&e!==document.body)return e.scrollTop;var t=void 0!==window.pageXOffset,a="CSS1Compat"===(document.compatMode||"");return t?window.pageYOffset:a?document.documentElement.scrollTop:document.body.scrollTop},y=function(n){var e=n.data.containerElement;if(e&&e!==document&&e!==document.body)return Math.max(e.scrollHeight,e.offsetHeight,e.clientHeight);var t=document.body,a=document.documentElement;return Math.max(t.scrollHeight,t.offsetHeight,a.clientHeight,a.scrollHeight,a.offsetHeight)},v=function n(e,t,a){var s=t.data;if(!t.ignoreCancelEvents&&s.cancel)return void(u.default.registered.end&&u.default.registered.end(s.to,s.target,s.currentPositionY));if(s.deltaTop=Math.round(s.targetPositionY-s.startPositionY),null===s.start&&(s.start=a),s.progress=a-s.start,s.percent=s.progress>=s.duration?1:e(s.progress/s.duration),s.currentPositionY=s.startPositionY+Math.ceil(s.deltaTop*s.percent),s.containerElement&&s.containerElement!==document&&s.containerElement!==document.body?s.containerElement.scrollTop=s.currentPositionY:window.scrollTo(0,s.currentPositionY),s.percent<1){var o=n.bind(null,e,t);return void f.call(window,o)}u.default.registered.end&&u.default.registered.end(s.to,s.target,s.currentPositionY)},j=function(n){n.data.containerElement=n?n.containerId?document.getElementById(n.containerId):n.container&&n.container.nodeType?n.container:document:null},w=function(n,e,t,a){if(e.data=e.data||m(),window.clearTimeout(e.data.delayTimeout),c.default.subscribe(function(){e.data.cancel=!0}),j(e),e.data.start=null,e.data.cancel=!1,e.data.startPositionY=g(e),e.data.targetPositionY=e.absolute?n:n+e.data.startPositionY,e.data.startPositionY===e.data.targetPositionY)return void(u.default.registered.end&&u.default.registered.end(e.data.to,e.data.target,e.data.currentPositionY));e.data.deltaTop=Math.round(e.data.targetPositionY-e.data.startPositionY),e.data.duration=h(e.duration)(e.data.deltaTop),e.data.duration=isNaN(parseFloat(e.data.duration))?1e3:parseFloat(e.data.duration),e.data.to=t,e.data.target=a;var s=d(e),o=v.bind(null,s,e);if(e&&e.delay>0)return void(e.data.delayTimeout=window.setTimeout(function(){f.call(window,o)},e.delay));f.call(window,o)},k=function(n){return n=s({},n),n.data=n.data||m(),n.absolute=!0,n},S=function(n){w(0,k(n))},x=function(n,e){w(n,k(e))},C=function(n){n=k(n),j(n),w(y(n),n)},E=function(n,e){e=k(e),j(e),w(g(e)+n,e)};e.default={animateTopScroll:w,getAnimationType:d,scrollToTop:S,scrollToBottom:C,scrollTo:x,scrollMore:E}},function(n,e,t){"use strict";function a(n){return n&&n.__esModule?n:{default:n}}function s(n,e){if(!(n instanceof e))throw new TypeError("Cannot call a class as a function")}function o(n,e){if(!n)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?n:e}function r(n,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);n.prototype=Object.create(e&&e.prototype,{constructor:{value:n,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(n,e):n.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var l=Object.assign||function(n){for(var e=1;e<arguments.length;e++){var t=arguments[e];for(var a in t)Object.prototype.hasOwnProperty.call(t,a)&&(n[a]=t[a])}return n},i=function(){function n(n,e){for(var t=0;t<e.length;t++){var a=e[t];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(n,a.key,a)}}return function(e,t,a){return t&&n(e.prototype,t),a&&n(e,a),e}}(),c=t(0),p=a(c),u=t(2),d=(a(u),t(50)),h=a(d),b=t(45),f=a(b);e.default=function(n){var e=function(e){function t(n){s(this,t);var e=o(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,n));return e.childBindings={domNode:null},e}return r(t,e),i(t,[{key:"componentDidMount",value:function(){if("undefined"==typeof window)return!1;this.registerElems(this.props.name)}},{key:"componentWillReceiveProps",value:function(n){this.props.name!==n.name&&this.registerElems(n.name)}},{key:"componentWillUnmount",value:function(){if("undefined"==typeof window)return!1;h.default.unregister(this.props.name)}},{key:"registerElems",value:function(n){h.default.register(n,this.childBindings.domNode)}},{key:"render",value:function(){return p.default.createElement(n,l({},this.props,{parentBindings:this.childBindings}))}}]),t}(p.default.Component);return e.propTypes={name:f.default.string,id:f.default.string},e}},function(n,e,t){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var a=(t(51),t(22)),s=function(n){return n&&n.__esModule?n:{default:n}}(a),o={mountFlag:!1,initialized:!1,scroller:null,containers:{},mount:function(n){this.scroller=n,this.handleHashChange=this.handleHashChange.bind(this),window.addEventListener("hashchange",this.handleHashChange),this.initStateFromHash(),this.mountFlag=!0},mapContainer:function(n,e){this.containers[n]=e},isMounted:function(){return this.mountFlag},isInitialized:function(){return this.initialized},initStateFromHash:function(){var n=this,e=this.getHash();e?window.setTimeout(function(){n.scrollTo(e,!0),n.initialized=!0},10):this.initialized=!0},scrollTo:function(n,e){var t=this.scroller;if(t.get(n)&&(e||n!==t.getActiveLink())){var a=this.containers[n]||document;t.scrollTo(n,{container:a})}},getHash:function(){return s.default.getHash()},changeHash:function(n){this.isInitialized()&&s.default.getHash()!==n&&s.default.pushHash(n)},handleHashChange:function(){this.scrollTo(this.getHash())},unmount:function(){this.scroller=null,this.containers=null,window.removeEventListener("hashchange",this.handleHashChange)}};e.default=o},function(n,e,t){(function(e){function t(n,e,t){function a(e){var t=f,a=m;return f=m=void 0,x=e,y=n.apply(a,t)}function o(n){return x=n,v=setTimeout(p,e),C?a(n):y}function r(n){var t=n-j,a=n-x,s=e-t;return E?k(s,g-a):s}function c(n){var t=n-j,a=n-x;return void 0===j||t>=e||t<0||E&&a>=g}function p(){var n=S();if(c(n))return u(n);v=setTimeout(p,r(n))}function u(n){return v=void 0,P&&f?a(n):(f=m=void 0,y)}function d(){void 0!==v&&clearTimeout(v),x=0,f=j=m=v=void 0}function h(){return void 0===v?y:u(S())}function b(){var n=S(),t=c(n);if(f=arguments,m=this,j=n,t){if(void 0===v)return o(j);if(E)return v=setTimeout(p,e),a(j)}return void 0===v&&(v=setTimeout(p,e)),y}var f,m,g,y,v,j,x=0,C=!1,E=!1,P=!0;if("function"!=typeof n)throw new TypeError(i);return e=l(e)||0,s(t)&&(C=!!t.leading,E="maxWait"in t,g=E?w(l(t.maxWait)||0,e):g,P="trailing"in t?!!t.trailing:P),b.cancel=d,b.flush=h,b}function a(n,e,a){var o=!0,r=!0;if("function"!=typeof n)throw new TypeError(i);return s(a)&&(o="leading"in a?!!a.leading:o,r="trailing"in a?!!a.trailing:r),t(n,e,{leading:o,maxWait:e,trailing:r})}function s(n){var e=typeof n;return!!n&&("object"==e||"function"==e)}function o(n){return!!n&&"object"==typeof n}function r(n){return"symbol"==typeof n||o(n)&&j.call(n)==p}function l(n){if("number"==typeof n)return n;if(r(n))return c;if(s(n)){var e="function"==typeof n.valueOf?n.valueOf():n;n=s(e)?e+"":e}if("string"!=typeof n)return 0===n?n:+n;n=n.replace(u,"");var t=h.test(n);return t||b.test(n)?f(n.slice(2),t?2:8):d.test(n)?c:+n}var i="Expected a function",c=NaN,p="[object Symbol]",u=/^\s+|\s+$/g,d=/^[-+]0x[0-9a-f]+$/i,h=/^0b[01]+$/i,b=/^0o[0-7]+$/i,f=parseInt,m="object"==typeof e&&e&&e.Object===Object&&e,g="object"==typeof self&&self&&self.Object===Object&&self,y=m||g||Function("return this")(),v=Object.prototype,j=v.toString,w=Math.max,k=Math.min,S=function(){return y.Date.now()};n.exports=a}).call(e,t(74))},function(n,e,t){(function(n){function t(n,e){for(var t=0,a=n.length-1;a>=0;a--){var s=n[a];"."===s?n.splice(a,1):".."===s?(n.splice(a,1),t++):t&&(n.splice(a,1),t--)}if(e)for(;t--;t)n.unshift("..");return n}function a(n,e){if(n.filter)return n.filter(e);for(var t=[],a=0;a<n.length;a++)e(n[a],a,n)&&t.push(n[a]);return t}var s=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/,o=function(n){return s.exec(n).slice(1)};e.resolve=function(){for(var e="",s=!1,o=arguments.length-1;o>=-1&&!s;o--){var r=o>=0?arguments[o]:n.cwd();if("string"!=typeof r)throw new TypeError("Arguments to path.resolve must be strings");r&&(e=r+"/"+e,s="/"===r.charAt(0))}return e=t(a(e.split("/"),function(n){return!!n}),!s).join("/"),(s?"/":"")+e||"."},e.normalize=function(n){var s=e.isAbsolute(n),o="/"===r(n,-1);return n=t(a(n.split("/"),function(n){return!!n}),!s).join("/"),n||s||(n="."),n&&o&&(n+="/"),(s?"/":"")+n},e.isAbsolute=function(n){return"/"===n.charAt(0)},e.join=function(){var n=Array.prototype.slice.call(arguments,0);return e.normalize(a(n,function(n,e){if("string"!=typeof n)throw new TypeError("Arguments to path.join must be strings");return n}).join("/"))},e.relative=function(n,t){function a(n){for(var e=0;e<n.length&&""===n[e];e++);for(var t=n.length-1;t>=0&&""===n[t];t--);return e>t?[]:n.slice(e,t-e+1)}n=e.resolve(n).substr(1),t=e.resolve(t).substr(1);for(var s=a(n.split("/")),o=a(t.split("/")),r=Math.min(s.length,o.length),l=r,i=0;i<r;i++)if(s[i]!==o[i]){l=i;break}for(var c=[],i=l;i<s.length;i++)c.push("..");return c=c.concat(o.slice(l)),c.join("/")},e.sep="/",e.delimiter=":",e.dirname=function(n){var e=o(n),t=e[0],a=e[1];return t||a?(a&&(a=a.substr(0,a.length-1)),t+a):"."},e.basename=function(n,e){var t=o(n)[2];return e&&t.substr(-1*e.length)===e&&(t=t.substr(0,t.length-e.length)),t},e.extname=function(n){return o(n)[3]};var r="b"==="ab".substr(-1)?function(n,e,t){return n.substr(e,t)}:function(n,e,t){return e<0&&(e=n.length+e),n.substr(e,t)}}).call(e,t(64))},function(n,e){function t(){throw new Error("setTimeout has not been defined")}function a(){throw new Error("clearTimeout has not been defined")}function s(n){if(p===setTimeout)return setTimeout(n,0);if((p===t||!p)&&setTimeout)return p=setTimeout,setTimeout(n,0);try{return p(n,0)}catch(e){try{return p.call(null,n,0)}catch(e){return p.call(this,n,0)}}}function o(n){if(u===clearTimeout)return clearTimeout(n);if((u===a||!u)&&clearTimeout)return u=clearTimeout,clearTimeout(n);try{return u(n)}catch(e){try{return u.call(null,n)}catch(e){return u.call(this,n)}}}function r(){f&&h&&(f=!1,h.length?b=h.concat(b):m=-1,b.length&&l())}function l(){if(!f){var n=s(r);f=!0;for(var e=b.length;e;){for(h=b,b=[];++m<e;)h&&h[m].run();m=-1,e=b.length}h=null,f=!1,o(n)}}function i(n,e){this.fun=n,this.array=e}function c(){}var p,u,d=n.exports={};!function(){try{p="function"==typeof setTimeout?setTimeout:t}catch(n){p=t}try{u="function"==typeof clearTimeout?clearTimeout:a}catch(n){u=a}}();var h,b=[],f=!1,m=-1;d.nextTick=function(n){var e=new Array(arguments.length-1);if(arguments.length>1)for(var t=1;t<arguments.length;t++)e[t-1]=arguments[t];b.push(new i(n,e)),1!==b.length||f||s(l)},i.prototype.run=function(){this.fun.apply(null,this.array)},d.title="browser",d.browser=!0,d.env={},d.argv=[],d.version="",d.versions={},d.on=c,d.addListener=c,d.once=c,d.off=c,d.removeListener=c,d.removeAllListeners=c,d.emit=c,d.prependListener=c,d.prependOnceListener=c,d.listeners=function(n){return[]},d.binding=function(n){throw new Error("process.binding is not supported")},d.cwd=function(){return"/"},d.chdir=function(n){throw new Error("process.chdir is not supported")},d.umask=function(){return 0}},function(n,e,t){"use strict";function a(){}var s=t(66);n.exports=function(){function n(n,e,t,a,o,r){if(r!==s){var l=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw l.name="Invariant Violation",l}}function e(){return n}n.isRequired=n;var t={array:n,bool:n,func:n,number:n,object:n,string:n,symbol:n,any:n,arrayOf:e,element:n,instanceOf:e,node:n,objectOf:e,oneOf:e,oneOfType:e,shape:e,exact:e};return t.checkPropTypes=a,t.PropTypes=t,t}},function(n,e,t){"use strict";n.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},function(n,e,t){"use strict";function a(n){return n&&n.__esModule?n:{default:n}}function s(n,e){if(!(n instanceof e))throw new TypeError("Cannot call a class as a function")}function o(n,e){if(!n)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?n:e}function r(n,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);n.prototype=Object.create(e&&e.prototype,{constructor:{value:n,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(n,e):n.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var l=function(){function n(n,e){for(var t=0;t<e.length;t++){var a=e[t];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(n,a.key,a)}}return function(e,t,a){return t&&n(e.prototype,t),a&&n(e,a),e}}(),i=t(0),c=a(i),p=t(53),u=a(p),d=function(n){function e(){return s(this,e),o(this,(e.__proto__||Object.getPrototypeOf(e)).apply(this,arguments))}return r(e,n),l(e,[{key:"render",value:function(){return c.default.createElement("input",this.props,this.props.children)}}]),e}(c.default.Component);e.default=(0,u.default)(d)},function(n,e,t){"use strict";function a(n){return n&&n.__esModule?n:{default:n}}function s(n,e){if(!(n instanceof e))throw new TypeError("Cannot call a class as a function")}function o(n,e){if(!n)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?n:e}function r(n,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);n.prototype=Object.create(e&&e.prototype,{constructor:{value:n,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(n,e):n.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var l=Object.assign||function(n){for(var e=1;e<arguments.length;e++){var t=arguments[e];for(var a in t)Object.prototype.hasOwnProperty.call(t,a)&&(n[a]=t[a])}return n},i=function(){function n(n,e){for(var t=0;t<e.length;t++){var a=e[t];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(n,a.key,a)}}return function(e,t,a){return t&&n(e.prototype,t),a&&n(e,a),e}}(),c=t(0),p=a(c),u=t(60),d=a(u),h=t(45),b=a(h),f=function(n){function e(){return s(this,e),o(this,(e.__proto__||Object.getPrototypeOf(e)).apply(this,arguments))}return r(e,n),i(e,[{key:"render",value:function(){var n=this,e=l({},this.props);return e.parentBindings&&delete e.parentBindings,p.default.createElement("div",l({},e,{ref:function(e){n.props.parentBindings.domNode=e}}),this.props.children)}}]),e}(p.default.Component);f.propTypes={name:b.default.string,id:b.default.string},e.default=(0,d.default)(f)},function(n,e,t){"use strict";function a(n){return n&&n.__esModule?n:{default:n}}function s(n,e){if(!(n instanceof e))throw new TypeError("Cannot call a class as a function")}function o(n,e){if(!n)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?n:e}function r(n,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);n.prototype=Object.create(e&&e.prototype,{constructor:{value:n,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(n,e):n.__proto__=e)}Object.defineProperty(e,"__esModule",{value:!0});var l=t(0),i=a(l),c=t(53),p=a(c),u=function(n){function e(){var n,t,a,r;s(this,e);for(var l=arguments.length,c=Array(l),p=0;p<l;p++)c[p]=arguments[p];return t=a=o(this,(n=e.__proto__||Object.getPrototypeOf(e)).call.apply(n,[this].concat(c))),a.render=function(){return i.default.createElement("a",a.props,a.props.children)},r=t,o(a,r)}return r(e,n),e}(i.default.Component);e.default=(0,p.default)(u)},function(n,e,t){"use strict";function a(n){return n&&n.__esModule?n:{default:n}}Object.defineProperty(e,"__esModule",{value:!0}),e.Helpers=e.ScrollElement=e.ScrollLink=e.animateScroll=e.scrollSpy=e.Events=e.scroller=e.Element=e.Button=e.Link=void 0;var s=t(69),o=a(s),r=t(67),l=a(r),i=t(68),c=a(i),p=t(50),u=a(p),d=t(52),h=a(d),b=t(54),f=a(b),m=t(59),g=a(m),y=t(53),v=a(y),j=t(60),w=a(j),k=t(71),S=a(k);e.Link=o.default,e.Button=l.default,e.Element=c.default,e.scroller=u.default,e.Events=h.default,e.scrollSpy=f.default,e.animateScroll=g.default,e.ScrollLink=v.default,e.ScrollElement=w.default,e.Helpers=S.default,e.default={Link:o.default,Button:l.default,Element:c.default,scroller:u.default,Events:h.default,scrollSpy:f.default,animateScroll:g.default,ScrollLink:v.default,ScrollElement:w.default,Helpers:S.default}},function(n,e,t){"use strict";function a(n,e){if(!(n instanceof e))throw new TypeError("Cannot call a class as a function")}function s(n,e){if(!n)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?n:e}function o(n,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);n.prototype=Object.create(e&&e.prototype,{constructor:{value:n,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(n,e):n.__proto__=e)}var r=Object.assign||function(n){for(var e=1;e<arguments.length;e++){var t=arguments[e];for(var a in t)Object.prototype.hasOwnProperty.call(t,a)&&(n[a]=t[a])}return n},l=function(){function n(n,e){for(var t=0;t<e.length;t++){var a=e[t];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(n,a.key,a)}}return function(e,t,a){return t&&n(e.prototype,t),a&&n(e,a),e}}(),i=t(0),c=(t(2),t(22),t(54)),p=t(50),u=t(45),d=t(61),h={to:u.string.isRequired,containerId:u.string,container:u.object,activeClass:u.string,spy:u.bool,smooth:u.oneOfType([u.bool,u.string]),offset:u.number,delay:u.number,isDynamic:u.bool,onClick:u.func,duration:u.oneOfType([u.number,u.func]),absolute:u.bool,onSetActive:u.func,onSetInactive:u.func,ignoreCancelEvents:u.bool,hashSpy:u.bool},b={Scroll:function(n,e){console.warn("Helpers.Scroll is deprecated since v1.7.0");var t=e||p,u=function(e){function p(n){a(this,p);var e=s(this,(p.__proto__||Object.getPrototypeOf(p)).call(this,n));return b.call(e),e.state={active:!1},e}return o(p,e),l(p,[{key:"getScrollSpyContainer",value:function(){var n=this.props.containerId,e=this.props.container;return n?document.getElementById(n):e&&e.nodeType?e:document}},{key:"componentDidMount",value:function(){if(this.props.spy||this.props.hashSpy){var n=this.getScrollSpyContainer();c.isMounted(n)||c.mount(n),this.props.hashSpy&&(d.isMounted()||d.mount(t),d.mapContainer(this.props.to,n)),this.props.spy&&c.addStateHandler(this.stateHandler),c.addSpyHandler(this.spyHandler,n),this.setState({container:n})}}},{key:"componentWillUnmount",value:function(){c.unmount(this.stateHandler,this.spyHandler)}},{key:"render",value:function(){var e="";e=this.state&&this.state.active?((this.props.className||"")+" "+(this.props.activeClass||"active")).trim():this.props.className;var t=r({},this.props);for(var a in h)t.hasOwnProperty(a)&&delete t[a];return t.className=e,t.onClick=this.handleClick,i.createElement(n,t)}}]),p}(i.Component),b=function(){var n=this;this.scrollTo=function(e,a){t.scrollTo(e,r({},n.state,a))},this.handleClick=function(e){n.props.onClick&&n.props.onClick(e),e.stopPropagation&&e.stopPropagation(),e.preventDefault&&e.preventDefault(),n.scrollTo(n.props.to,n.props)},this.stateHandler=function(){t.getActiveLink()!==n.props.to&&(null!==n.state&&n.state.active&&n.props.onSetInactive&&n.props.onSetInactive(),n.setState({active:!1}))},this.spyHandler=function(e){var a=n.getScrollSpyContainer();if(!d.isMounted()||d.isInitialized()){var s=n.props.to,o=null,r=0,l=0,i=0;if(a.getBoundingClientRect){i=a.getBoundingClientRect().top}if(!o||n.props.isDynamic){if(!(o=t.get(s)))return;var p=o.getBoundingClientRect();r=p.top-i+e,l=r+p.height}var u=e-n.props.offset,h=u>=Math.floor(r)&&u<Math.floor(l),b=u<Math.floor(r)||u>=Math.floor(l),f=t.getActiveLink();return b?(s===f&&t.setActiveLink(void 0),n.props.hashSpy&&d.getHash()===s&&d.changeHash(),n.props.spy&&n.state.active&&(n.setState({active:!1}),n.props.onSetInactive&&n.props.onSetInactive()),c.updateStates()):h&&f!==s?(t.setActiveLink(s),n.props.hashSpy&&d.changeHash(s),n.props.spy&&(n.setState({active:!0}),n.props.onSetActive&&n.props.onSetActive(s)),c.updateStates()):void 0}}};return u.propTypes=h,u.defaultProps={offset:0},u},Element:function(n){console.warn("Helpers.Element is deprecated since v1.7.0");var e=function(e){function t(n){a(this,t);var e=s(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,n));return e.childBindings={domNode:null},e}return o(t,e),l(t,[{key:"componentDidMount",value:function(){if("undefined"==typeof window)return!1;this.registerElems(this.props.name)}},{key:"componentWillReceiveProps",value:function(n){this.props.name!==n.name&&this.registerElems(n.name)}},{key:"componentWillUnmount",value:function(){if("undefined"==typeof window)return!1;p.unregister(this.props.name)}},{key:"registerElems",value:function(n){p.register(n,this.childBindings.domNode)}},{key:"render",value:function(){return i.createElement(n,r({},this.props,{parentBindings:this.childBindings}))}}]),t}(i.Component);return e.propTypes={name:u.string,id:u.string},e}};n.exports=b},function(n,e,t){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var a=t(51),s=["mousedown","mousewheel","touchmove","keydown"];e.default={subscribe:function(n){return"undefined"!=typeof document&&s.forEach(function(e){return(0,a.addPassiveEventListener)(document,e,n)})}}},function(n,e,t){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.default={defaultEasing:function(n){return n<.5?Math.pow(2*n,2)/2:1-Math.pow(2*(1-n),2)/2},linear:function(n){return n},easeInQuad:function(n){return n*n},easeOutQuad:function(n){return n*(2-n)},easeInOutQuad:function(n){return n<.5?2*n*n:(4-2*n)*n-1},easeInCubic:function(n){return n*n*n},easeOutCubic:function(n){return--n*n*n+1},easeInOutCubic:function(n){return n<.5?4*n*n*n:(n-1)*(2*n-2)*(2*n-2)+1},easeInQuart:function(n){return n*n*n*n},easeOutQuart:function(n){return 1- --n*n*n*n},easeInOutQuart:function(n){return n<.5?8*n*n*n*n:1-8*--n*n*n*n},easeInQuint:function(n){return n*n*n*n*n},easeOutQuint:function(n){return 1+--n*n*n*n*n},easeInOutQuint:function(n){return n<.5?16*n*n*n*n*n:1+16*--n*n*n*n*n}}},function(n,e){var t;t=function(){return this}();try{t=t||Function("return this")()||(0,eval)("this")}catch(n){"object"==typeof window&&(t=window)}n.exports=t},function(n,e,t){"use strict";n.exports={"zh-cn":[{filename:"dubbo-101.md",__html:'<h1>第一个 Dubbo 应用</h1>\n<h2>Java RMI 简介</h2>\n<p>Java RMI (Remote Method Invocation)- 远程方法调用,能够让客户端像使用本地调用一样调用服务端 Java 虚拟机中的对象方法。RMI 是面向对象语言领域对 RPC (Remote Procedure Call)的完善,用户无需依靠 IDL 的帮助来完成分布式调用,而是通过依赖接口这种更简单自然的方式。</p>\n<h3>Java RMI 工作原理</h3>\n<p>一个典型的 RMI 调用如下图所示:</p>\n<ol>\n<li>服务端向 RMI 注册服务绑定自己的地址,</li>\n<li>客户端通过 RMI 注册服务获取目标地址,</li>\n<li>客户端调用本地的 Stub 对象上的方法,和调用本地对象上的方法一致,</li>\n<li>本地存根对象将调用信息打包,通过网络发送到服务端,</li>\n<li>服务端的 Skeleton 对象收到网络请求之后,将调用信息解包,</li>\n<li>然后找到真正的服务对象发起调用,并将返回结果打包通过网络发送回客户端。</li>\n</ol>\n<p><img src="../../img/blog/rmi-flow.png" alt="RMI Flow"></p>\n<p>(来源:<a href="https://www.cs.rutgers.edu/~pxk/417/notes/images/rpc-rmi_flow.png">https://www.cs.rutgers.edu/~pxk/417/notes/images/rpc-rmi_flow.png</a>)</p>\n<h3>Java RMI 基本概念</h3>\n<p>Java RMI 是 Java 领域创建分布式应用的技术基石。后续的 EJB 技术,以及现代的分布式服务框架,其中的基本理念依旧是 Java RMI 的延续。在 RMI 调用中,有以下几个核心的概念:</p>\n<ol>\n<li>\n<p>通过<strong>接口</strong>进行远程调用</p>\n</li>\n<li>\n<p>通过客户端的 <strong>Stub 对象</strong>和服务端的 <strong>Skeleton 对象</strong>的帮助将远程调用伪装成本地调用</p>\n</li>\n<li>\n<p>通过 <strong>RMI 注册服务</strong>完成服务的注册和发现</p>\n</li>\n</ol>\n<p>对于第一点,客户端需要依赖接口,而服务端需要提供该接口的实现。</p>\n<p>对于第二点,在 J2SE 1.5 版本之前需要通过 rmic 预先编译好客户端的 Stub 对象和服务端的 Skeleton 对象。在之后的版本中,不再需要事先生成 Stub 和 Skeleton 对象。</p>\n<p>下面通过示例代码简单的展示 RMI 中的服务注册和发现</p>\n<h4>服务端的服务注册</h4>\n<pre><code class="language-java">Hello obj = <span class="hljs-keyword">new</span> HelloImpl(); <span class="hljs-comment">// #1</span>\nHello stub = (Hello) UnicastRemoteObject.exportObject(obj, <span class="hljs-number">0</span>); <span class="hljs-comment">// #2</span>\nRegistry registry = LocateRegistry.createRegistry(<span class="hljs-number">1099</span>); <span class="hljs-comment">// #3</span>\nregistry.rebind(<span class="hljs-string">"Hello"</span>, stub); <span class="hljs-comment">// #4</span>\n</code></pre>\n<p>说明:</p>\n<ol>\n<li>初始化服务对象实例,</li>\n<li>通过 <em>UnicastRemoteObject.exportObject</em> 生成可以与服务端通讯的 Stub 对象,</li>\n<li>创建一个本地的 RMI 注册服务,监听端口为 1099。该注册服务运行在服务端,也可以单独启动一个注册服务的进程,</li>\n<li>将 Stub 对象绑定到注册服务上,这样,客户端可以通过 <em>Hello</em> 这个名字查找到该远程对象。</li>\n</ol>\n<h4>客户端的服务发现</h4>\n<pre><code class="language-java">Registry registry = LocateRegistry.getRegistry(); <span class="hljs-comment">// #1</span>\nHello stub = (Hello) registry.lookup(<span class="hljs-string">"Hello"</span>); <span class="hljs-comment">// #2</span>\nString response = stub.sayHello(); <span class="hljs-comment">// #3</span>\n</code></pre>\n<p>说明:</p>\n<ol>\n<li>获取注册服务实例,在本例中,由于没有传入任何参数,假定要获取的注册服务实例部署在本机,并监听在 1099 端口上,</li>\n<li>从注册服务中查找服务名为 <em>Hello</em> 的远程对象,</li>\n<li>通过获取的 Stub 对象发起一次 RMI 调用并获得结果。</li>\n</ol>\n<p>理解 RMI 的工作原理和基本概念,对掌握现代分布式服务框架很有帮助,建议进一步的阅读 RMI 官方教材 <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>。</p>\n<h2>Dubbo 基本概念</h2>\n<p>现代的分布式服务框架的基本概念与 RMI 是类似的,同样是使用 Java 的 Interface 作为服务契约,通过注册中心来完成服务的注册和发现,远程通讯的细节也是通过代理类来屏蔽。具体来说,Dubbo 在工作时有以下四个角色参与:</p>\n<ol>\n<li>服务提供者 - 启动时在指定端口上暴露服务,并将服务地址和端口注册到注册中心上</li>\n<li>服务消费者 - 启动时向注册中心订阅自己感兴趣的服务,以便获得服务提供方的地址列表</li>\n<li>注册中心 - 负责服务的注册和发现,负责保存服务提供方上报的地址信息,并向服务消费方推送</li>\n<li>监控中心 - 负责收集服务提供方和消费方的运行状态,比如服务调用次数、延迟等,用于监控</li>\n<li>运行容器 - 负责服务提供方的初始化、加载以及运行的生命周期管理</li>\n</ol>\n<p><img src="../../img/blog/dubbo-architecture.png" alt="dubbo-architecture"></p>\n<p><strong>部署阶段</strong></p>\n<ul>\n<li>服务提供者在指定端口暴露服务,并向注册中心注册服务信息。</li>\n<li>服务消费者向注册中心发起服务地址列表的订阅。</li>\n</ul>\n<p><strong>运行阶段</strong></p>\n<ul>\n<li>注册中心向服务消费者推送地址列表信息。</li>\n<li>服务消费者收到地址列表后,从其中选取一个向目标服务发起调用。</li>\n<li>调用过程服务消费者和服务提供者的运行状态上报给监控中心。</li>\n</ul>\n<h2>基于 API 的 Dubbo 应用</h2>\n<p>Dubbo 的应用一般都是通过 Spring 来组装的。为了快速获得一个可以工作的 Dubbo 应用,这里的示例摒弃了复杂的配置,而改用面向 Dubbo API 的方式来构建服务提供者和消费者,另外,注册中心和监控中心在本示例中也不需要安装和配置。</p>\n<p>在生产环境,Dubbo 的服务需要一个分布式的服务注册中心与之配合,比如,ZooKeeper。为了方便开发,Dubbo 提供了直连<sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>以及组播<sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup>两种方式,从而避免额外搭建注册中心的工作。在本例中,将使用组播的方式来完成服务的注册和发现。</p>\n<h3>定义服务契约</h3>\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">GreetingsService</span> </span>{\n    <span class="hljs-function">String <span class="hljs-title">sayHi</span><span class="hljs-params">(String name)</span></span>; <span class="hljs-comment">// #1</span>\n}\n</code></pre>\n<p><strong>说明</strong>:</p>\n<ol>\n<li>定义了一个简单的服务契约 <em>GreetingsService</em>,其中只有一个方法 <em>sayHi</em> 可供调用,入参是 <em>String</em> 类型,返回值也是 <em>String</em> 类型。</li>\n</ol>\n<h3>提供契约的实现</h3>\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">GreetingsServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">GreetingsService</span> </span>{ <span class="hljs-comment">// #1</span>\n    <span class="hljs-meta">@Override</span>\n    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">sayHi</span><span class="hljs-params">(String name)</span> </span>{\n        <span class="hljs-keyword">return</span> <span class="hljs-string">"hi, "</span> + name; <span class="hljs-comment">// #2</span>\n    }\n}\n</code></pre>\n<p><strong>说明</strong>:</p>\n<ol>\n<li>服务提供者需要实现服务契约 <em>GreetingsService</em> 接口。</li>\n<li>该实现简单的返回一个欢迎信息,如果入参是 <em>dubbo</em>,则返回 <em>hi, dubbo</em>。</li>\n</ol>\n<h3>实现 Dubbo 服务提供方</h3>\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">Application</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> IOException </span>{\n        ServiceConfig&lt;GreetingsService&gt; service = <span class="hljs-keyword">new</span> ServiceConfig&lt;&gt;(); <span class="hljs-comment">// #1</span>\n        service.setApplication(<span class="hljs-keyword">new</span> ApplicationConfig(<span class="hljs-string">"first-dubbo-provider"</span>)); <span class="hljs-comment">// #2</span>\n        service.setRegistry(<span class="hljs-keyword">new</span> RegistryConfig(<span class="hljs-string">"multicast://224.5.6.7:1234"</span>)); <span class="hljs-comment">// #3</span>\n        service.setInterface(GreetingsService.class); <span class="hljs-comment">// #4</span>\n        service.setRef(<span class="hljs-keyword">new</span> GreetingsServiceImpl()); <span class="hljs-comment">// #5</span>\n        service.export(); <span class="hljs-comment">// #6</span>\n        System.in.read(); <span class="hljs-comment">// #7</span>\n    }\n}\n</code></pre>\n<p><strong>说明</strong>:</p>\n<ol>\n<li>创建一个 <em>ServiceConfig</em> 的实例,泛型参数信息是服务接口类型,即 <em>GreetingsService</em>。</li>\n<li>生成一个 <em>AplicatonConfig</em> 的实例,并将其装配进 <em>ServiceConfig</em>。</li>\n<li>生成一个 <em>RegistryConfig</em> 实例,并将其装配进 <em>ServiceConfig</em>,这里使用的是组播方式,参数是 <code>multicast://224.5.6.7:1234</code>。合法的组播地址范围为:<em>224.0.0.0 - 239.255.255.255</em></li>\n<li>将服务契约 <em>GreetingsService</em> 装配进 <em>ServiceConfig</em>。</li>\n<li>将服务提供者提供的实现 <em>GreetingsServiceImpl</em> 的实例装配进 <em>ServiceConfig</em>。</li>\n<li><em>ServiceConfig</em> 已经具备足够的信息,开始对外暴露服务,默认监听端口是 <em>20880</em>。</li>\n<li>为了防止服务端退出,按任意键或者 <em>ctrl-c</em> 退出。</li>\n</ol>\n<h3>实现 Dubbo 服务调用方</h3>\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">Application</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{\n        ReferenceConfig&lt;GreetingsService&gt; reference = <span class="hljs-keyword">new</span> ReferenceConfig&lt;&gt;(); <span class="hljs-comment">// #1</span>\n        reference.setApplication(<span class="hljs-keyword">new</span> ApplicationConfig(<span class="hljs-string">"first-dubbo-client"</span>)); <span class="hljs-comment">// #2</span>\n        reference.setRegistry(<span class="hljs-keyword">new</span> RegistryConfig(<span class="hljs-string">"multicast://224.5.6.7:1234"</span>)); <span class="hljs-comment">// #3</span>\n        reference.setInterface(GreetingsService.class); <span class="hljs-comment">// #4</span>\n        GreetingsService greetingsService = reference.get(); <span class="hljs-comment">// #5</span>\n        String message = greetingsService.sayHi(<span class="hljs-string">"dubbo"</span>); <span class="hljs-comment">// #6</span>\n        System.out.println(message); <span class="hljs-comment">// #7</span>\n    }\n}\n</code></pre>\n<p><strong>说明</strong>:</p>\n<ol>\n<li>创建一个 <em>ReferenceConfig</em> 的实例,同样,泛型参数信息是服务接口类型,即 <em>GreetingService</em>。</li>\n<li>生成一个 <em>AplicatonConfig</em> 的实例,并将其装配进 <em>ReferenceConfig</em>。</li>\n<li>生成一个 <em>RegistryConfig</em> 实例,并将其装配进 <em>ReferenceConfig</em>,注意这里的组播地址信息需要与服务提供方的相同。</li>\n<li>将服务契约 <em>GreetingsService</em> 装配进 <em>ReferenceConfig</em>。</li>\n<li>从 <em>ReferenceConfig</em> 中获取到 <em>GreetingService</em> 的代理。</li>\n<li>通过 <em>GreetingService</em> 的代理发起远程调用,传入的参数为 <em>dubbo</em>。</li>\n<li>打印返回结果 <em>hi, dubbo</em>。</li>\n</ol>\n<h3>运行</h3>\n<p>完整的示例在 <a href="https://github.com/dubbo/dubbo-samples/tree/master/dubbo-samples-api">https://github.com/dubbo/dubbo-samples/tree/master/dubbo-samples-api</a> 上提供。在完整的示例中,由于配置了 <em>exec-maven-plugin</em>,可以很方便的在命令行下通过 maven 的方式执行。当然,您也可以在 IDE 里直接执行,但是需要注意的是,由于使用了组播的方式来发现服务,运行时需要指定 <em>-Djava.net.preferIPv4Stack=true</em>。</p>\n<h4>构建示例</h4>\n<p>通过以下的命令来同步示例代码并完成构建:</p>\n<ol>\n<li>同步代码:git clone <a href="https://github.com/dubbo/dubbo-samples.git">https://github.com/dubbo/dubbo-samples.git</a></li>\n<li>构建:mvn clean package</li>\n</ol>\n<pre><code class="language-bash">$ git <span class="hljs-built_in">clone</span> https://github.com/dubbo/dubbo-samples.git\n$ <span class="hljs-built_in">cd</span> dubbo-samples/dubbo-samples-api/\n$ mvn clean package\nINFO] Scanning <span class="hljs-keyword">for</span> projects...\n[INFO]\n[INFO] ------------------------------------------------------------------------\n[INFO] Building dubbo-samples-api 1.0-SNAPSHOT\n[INFO] ------------------------------------------------------------------------\n[INFO]\n[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ dubbo-samples-api ---\n...\n[INFO] ------------------------------------------------------------------------\n[INFO] BUILD SUCCESS\n[INFO] ------------------------------------------------------------------------\n[INFO] Total time: 2.182 s\n[INFO] Finished at: 2018-05-28T14:56:08+08:00\n[INFO] Final Memory: 20M/353M\n[INFO] ------------------------------------------------------------------------\n</code></pre>\n<p>当看到 <em>BUILD SUCCESS</em> 的时候表明构建完成,下面就可以开始进入运行阶段了。</p>\n<h4>运行服务端</h4>\n<p>通过运行以下的 maven 命令来启动服务提供者:</p>\n<pre><code class="language-bash">$ mvn -Djava.net.preferIPv4Stack=<span class="hljs-literal">true</span> -Dexec.mainClass=com.alibaba.dubbo.samples.server.Application <span class="hljs-built_in">exec</span>:java\n[INFO] Scanning <span class="hljs-keyword">for</span> projects...\n[INFO]                                                                         \n[INFO] ------------------------------------------------------------------------\n[INFO] Building dubbo-samples-api 1.0-SNAPSHOT\n[INFO] ------------------------------------------------------------------------\n[INFO] \n[INFO] --- <span class="hljs-built_in">exec</span>-maven-plugin:1.6.0:java (default-cli) @ dubbo-samples-api ---\nlog4j:WARN No appenders could be found <span class="hljs-keyword">for</span> logger (com.alibaba.dubbo.common.logger.LoggerFactory).\nlog4j:WARN Please initialize the log4j system properly.\nlog4j:WARN See http://logging.apache.org/log4j/1.2/faq.html<span class="hljs-comment">#noconfig for more info.</span>\nfirst-dubbo-provider is running.\n</code></pre>\n<p>当 <em>first-dubbo-provider is running.</em> 出现时,代表服务提供者已经启动就绪,等待客户端的调用。</p>\n<h4>运行客户端</h4>\n<p>通过运行以下的 maven 命令来调用服务:</p>\n<pre><code class="language-bash">$ mvn -Djava.net.preferIPv4Stack=<span class="hljs-literal">true</span> -Dexec.mainClass=com.alibaba.dubbo.samples.client.Application <span class="hljs-built_in">exec</span>:java\n[INFO] Scanning <span class="hljs-keyword">for</span> projects...\n[INFO]                                                                         \n[INFO] ------------------------------------------------------------------------\n[INFO] Building dubbo-samples-api 1.0-SNAPSHOT\n[INFO] ------------------------------------------------------------------------\n[INFO] \n[INFO] --- <span class="hljs-built_in">exec</span>-maven-plugin:1.6.0:java (default-cli) @ dubbo-samples-api ---\nlog4j:WARN No appenders could be found <span class="hljs-keyword">for</span> logger (com.alibaba.dubbo.common.logger.LoggerFactory).\nlog4j:WARN Please initialize the log4j system properly.\nlog4j:WARN See http://logging.apache.org/log4j/1.2/faq.html<span class="hljs-comment">#noconfig for more info.</span>\nhi, dubbo\n</code></pre>\n<p>可以看到, <em>hi, dubbo</em> 是从服务提供者返回的执行结果。</p>\n<h2>快速生成 Dubbo 应用</h2>\n<p>Dubbo 还提供了一个公共服务快速搭建基于 Spring Boot 的 Dubbo 应用。访问 <a href="http://start.dubbo.io">http://start.dubbo.io</a> 并按照下图所示来生成示例工程:</p>\n<p><img src="../../img/blog/dubbo-initializr.png" alt="dubbo initializr"></p>\n<p><strong>说明</strong>:</p>\n<ol>\n<li>在 <em>Group</em> 中提供 maven groupId,默认值是 <em>com.example</em>。</li>\n<li>在 <em>Artifact</em> 中提供 maven artifactId,默认值是 <em>demo</em>。</li>\n<li>在 <em>DubboServiceName</em> 中提供服务名,默认值是 <em>com.example.HelloService</em>。</li>\n<li>在 <em>DubboServiceVersion</em> 中提供服务的版本,默认值是 <em>1.0.0</em>。</li>\n<li>在 <em>Client/Server</em> 中选取本次构建的工程是服务提供者 (Server) 还是服务消费者 (Client),默认值是 <em>server</em>。</li>\n<li>使用 <em>embeddedZookeeper</em> 作为服务注册发现,默认为勾选。</li>\n<li>是否激活 qos 端口,默认为不勾选,如果勾选可以通过 <em>22222</em> 端口访问。</li>\n<li>点击 <em>Generate Project</em> 即可下载生成好的工程。</li>\n</ol>\n<p>在本例中展示的是服务提供者,同样的,通过在生成界面选取 <em>client</em> 来生成对应的服务消费者。</p>\n<h3>运行</h3>\n<p>用 IDE 打开生成好的工程,可以发现应用是一个典型的 Spring Boot 应用。程序的入口如下所示:</p>\n<pre><code class="language-java"><span class="hljs-meta">@SpringBootApplication</span>\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DemoApplication</span> </span>{\n\t<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{\n\t\t<span class="hljs-keyword">new</span> EmbeddedZooKeeper(<span class="hljs-number">2181</span>, <span class="hljs-keyword">false</span>).start();  <span class="hljs-comment">// #1</span>\n\t\tSpringApplication.run(DemoApplication.class, args); <span class="hljs-comment">// #2</span>\n\t}\n}\n</code></pre>\n<p><strong>说明</strong>:</p>\n<ol>\n<li>在 <em>2181</em> 端口上启动嵌入式 <em>ZooKeeper</em>。</li>\n<li>启动 <em>Spring Boot</em> 上下文。</li>\n</ol>\n<p>可以直接在 IDE 中运行,输出结果如下:</p>\n<pre><code class="language-bash">2018-05-28 16:59:38.072  INFO 59943 --- [           main] a.b.d.c.e.WelcomeLogoApplicationListener : \n\n  ████████▄  ███    █▄  ▀█████████▄  ▀█████████▄   ▄██████▄  \n  ███   ▀███ ███    ███   ███    ███   ███    ███ ███    ███ \n  ███    ███ ███    ███   ███    ███   ███    ███ ███    ███ \n  ███    ███ ███    ███  ▄███▄▄▄██▀   ▄███▄▄▄██▀  ███    ███ \n  ███    ███ ███    ███ ▀▀███▀▀▀██▄  ▀▀███▀▀▀██▄  ███    ███ \n  ███    ███ ███    ███   ███    ██▄   ███    ██▄ ███    ███ \n  ███   ▄███ ███    ███   ███    ███   ███    ███ ███    ███ \n  ████████▀  ████████▀  ▄█████████▀  ▄█████████▀   ▀██████▀  \n                                                             \n\n :: Dubbo Spring Boot (v0.1.0) : https://github.com/dubbo/dubbo-spring-boot-project\n :: Dubbo (v2.0.1) : https://github.com/alibaba/dubbo\n :: Google group : http://groups.google.com/group/dubbo\n\n2018-05-28 16:59:38.079  INFO 59943 --- [           main] e.OverrideDubboConfigApplicationListener : Dubbo Config was overridden by externalized configuration {dubbo.application.name=dubbo-demo-server, dubbo.application.qosAcceptForeignIp=<span class="hljs-literal">false</span>, dubbo.application.qosEnable=<span class="hljs-literal">true</span>, dubbo.application.qosPort=22222, dubbo.registry.address=zookeeper://localhost:2181?client=curator, dubbo.registry.id=my-registry, dubbo.scan.basePackages=com.example} <span class="hljs-comment">#1</span>\n\n...\n\n2018-05-28 16:59:39.624  INFO 59943 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication <span class="hljs-keyword">in</span> 1.746 seconds (JVM running <span class="hljs-keyword">for</span> 2.963)\n</code></pre>\n<p><strong>说明</strong>:</p>\n<ol>\n<li>输出中打印的以 <em>dubbo.</em> 开头的配置信息,定义在 <em>main/resources/application.properties</em> 中。</li>\n</ol>\n<h3>通过 Telnet 管理服务</h3>\n<p>生成工程的时候如果选择了激活 <em>qos</em> 的话,就可以通过 <em>telnet</em> 或者 <em>nc</em> 来管理服务、查看服务状态。</p>\n<pre><code class="language-bash">$ telnet localhost 22222\nTrying 127.0.0.1...\nConnected to localhost.\nEscape character is <span class="hljs-string">\'^]\'</span>.\n  ████████▄  ███    █▄  ▀█████████▄  ▀█████████▄   ▄██████▄  \n  ███   ▀███ ███    ███   ███    ███   ███    ███ ███    ███ \n  ███    ███ ███    ███   ███    ███   ███    ███ ███    ███ \n  ███    ███ ███    ███  ▄███▄▄▄██▀   ▄███▄▄▄██▀  ███    ███ \n  ███    ███ ███    ███ ▀▀███▀▀▀██▄  ▀▀███▀▀▀██▄  ███    ███ \n  ███    ███ ███    ███   ███    ██▄   ███    ██▄ ███    ███ \n  ███   ▄███ ███    ███   ███    ███   ███    ███ ███    ███ \n  ████████▀  ████████▀  ▄█████████▀  ▄█████████▀   ▀██████▀  \n                                                             \n\ndubbo&gt;\ndubbo&gt;ls\nAs Provider side:\n+------------------------------+---+\n|     Provider Service Name    |PUB|\n+------------------------------+---+\n|com.example.HelloService:1.0.0| Y |\n+------------------------------+---+\nAs Consumer side:\n+---------------------+---+\n|Consumer Service Name|NUM|\n+---------------------+---+\n</code></pre>\n<p>目前 <em>qos</em> 支持以下几个命令,更详细的信息请查阅官方文档<sup class="footnote-ref"><a href="#fn4" id="fnref4">[4]</a></sup>:</p>\n<ul>\n<li><em>ls</em>:列出消费者、提供者信息</li>\n<li><em>online</em>:上线服务</li>\n<li><em>offline</em>:下线服务</li>\n<li><em>help</em>:联机帮助</li>\n</ul>\n<h2>总结</h2>\n<p>在本文中,从 RMI 开始,介绍了 Java 领域分布式调用的基本概念,也就是基于接口编程、通过代理将远程调用伪装成本地、通过注册中心完成服务的注册和发现。</p>\n<p>然后为了简单起见,使用简单的组播注册方式和直接面向 Dubbo API 编程的方式介绍了如何开发一个 Dubbo 的完整应用。深入的了解 <em>ServiceConfig</em> 和 <em>ReferenceConfig</em> 的用法,对于进一步的使用 Spring XML 配置、乃至 Spring Boot 的编程方式有这很大的帮助。</p>\n<p>最后,简单的介绍了如何通过 Dubbo 团队提供的公共服务 <a href="http://start.dubbo.io">start.dubbo.io</a> 快速搭建基于 Spring Boot 的 Dubbo 应用,并通过 <em>qos</em> 来做 Dubbo 服务的简单运维。</p>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p><a href="https://docs.oracle.com/javase/6/docs/technotes/guides/rmi/hello/hello-world.html">Getting Started Using JavaTM RMI</a> <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn2" class="footnote-item"><p><a href="http://dubbo.apache.org/books/dubbo-user-book/demos/explicit-target.html">直连提供者</a> <a href="#fnref2" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn3" class="footnote-item"><p><a href="http://dubbo.apache.org/books/dubbo-user-book/references/registry/multicast.html">Multicast 注册中心</a> <a href="#fnref3" class="footnote-backref">↩︎</a></p>\n</li>\n<li id="fn4" class="footnote-item"><p><a href="http://dubbo.apache.org/books/dubbo-user-book/references/qos.html">在线运维命令</a> <a href="#fnref4" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'},{filename:"dubbo-basic-usage-dubbo-provider-configuration.md",__html:'<h2>Dubbo基本用法-Dubbo Provider配置</h2>\n<h1>Dubbo基本用法</h1>\n<p>本章节主要讲述如何配置dubbo,按照配置方式上分,可以分为:XML配置,properties方式配置,注解方式配置,API调用方式配置。\n按照功能角度进行划分,可以分为Dubbo Provider和Dubbo Consumer。接下来章节中,分别对dubbo provider和Dubbo consumer进行讲解。</p>\n<h2>Dubbo Provider配置</h2>\n<h3>Provider 配置详解</h3>\n<p>配置Dubbo Provider有4种方式:XML配置,properties方式配置,API调用方式配置,注解方式配置。</p>\n<h4>XML配置</h4>\n<h6>最简单的配置的样例:</h6>\n<pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;\n&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;\n    xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;\n    xmlns:dubbo=&quot;http://dubbo.apache.org/schema/dubbo&quot;\n    xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd&quot;&gt;  \n    &lt;dubbo:application name=&quot;hello-world-app&quot; /&gt;  \n    &lt;dubbo:registry address=&quot;multicast://224.5.6.7:1234&quot; /&gt;  \n    &lt;dubbo:protocol name=&quot;dubbo&quot; port=&quot;20880&quot; /&gt;  \n    &lt;dubbo:service interface=&quot;com.alibaba.dubbo.demo.DemoService&quot; ref=&quot;demoServiceLocal&quot; /&gt;  \n    &lt;dubbo:reference id=&quot;demoServiceRemote&quot; interface=&quot;com.alibaba.dubbo.demo.DemoService&quot; /&gt;  \n&lt;/beans&gt;\n</code></pre>\n<p>上面样例中,注意下dubbo schema的写法:</p>\n<pre><code>&lt;beans xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;\n       xmlns:dubbo=&quot;http://code.alibabatech.com/schema/dubbo&quot;\n       xmlns=&quot;http://www.springframework.org/schema/beans&quot;\n       xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd\n       http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd&quot;&gt;\n</code></pre>\n<h6>支持的配置标签</h6>\n<table>\n<thead>\n<tr>\n<th>标签</th>\n<th>用途</th>\n<th style="text-align:left">解释</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>&lt;dubbo:service/&gt;</td>\n<td>服务配置</td>\n<td style="text-align:left">用于暴露一个服务,定义服务的元信息,一个服务可以用多个协议暴露,一个服务也可以注册到多个注册中心</td>\n</tr>\n<tr>\n<td>&lt;dubbo:reference/&gt;</td>\n<td>引用配置</td>\n<td style="text-align:left">用于创建一个远程服务代理,一个引用可以指向多个注册中心</td>\n</tr>\n<tr>\n<td>&lt;dubbo:protocol/&gt;</td>\n<td>协议配置</td>\n<td style="text-align:left">用于配置提供服务的协议信息,协议由提供方指定,消费方被动接受</td>\n</tr>\n<tr>\n<td>&lt;dubbo:application/&gt;</td>\n<td>应用配置</td>\n<td style="text-align:left">用于配置当前应用信息,不管该应用是提供者还是消费者</td>\n</tr>\n<tr>\n<td>&lt;dubbo:module/&gt;</td>\n<td>模块配置</td>\n<td style="text-align:left">用于配置当前模块信息,可选</td>\n</tr>\n<tr>\n<td>&lt;dubbo:registry/&gt;</td>\n<td>注册中心配置</td>\n<td style="text-align:left">用于配置连接注册中心相关信息</td>\n</tr>\n<tr>\n<td>&lt;dubbo:monitor/&gt;</td>\n<td>监控中心配置</td>\n<td style="text-align:left">用于配置连接监控中心相关信息,可选</td>\n</tr>\n<tr>\n<td>&lt;dubbo:provider/&gt;</td>\n<td>提供方配置</td>\n<td style="text-align:left">当 ProtocolConfig 和 ServiceConfig 某属性没有配置时,采用此缺省值,可选</td>\n</tr>\n<tr>\n<td>&lt;dubbo:consumer/&gt;</td>\n<td>消费方配置</td>\n<td style="text-align:left">当 ReferenceConfig 某属性没有配置时,采用此缺省值,可选</td>\n</tr>\n<tr>\n<td>&lt;dubbo:method/&gt;</td>\n<td>方法配置</td>\n<td style="text-align:left">用于 ServiceConfig 和 ReferenceConfig 指定方法级的配置信息</td>\n</tr>\n<tr>\n<td>&lt;dubbo:argument/&gt;</td>\n<td>参数配置</td>\n<td style="text-align:left">用于指定方法参数配置</td>\n</tr>\n</tbody>\n</table>\n<p><img src="https://cdn.yuque.com/lark/0/2018/png/15841/1527849348155-8423d401-9ea4-4dc6-8720-d9e3d90963b6.png" alt="undefined"></p>\n <center>配置之间关系图</center>\n<h6>配置项详解</h6>\n<ul>\n<li>\n<p>&lt;dubbo:application name=&quot;hello-world-app&quot; /&gt;<br>\n用于指定应用名,这里需要保证应用名唯一,这个应用名在后续的console admin中可以在列表中显示,方便管理。</p>\n</li>\n<li>\n<p>&lt;dubbo:registry address=&quot;multicast://224.5.6.7:1234&quot; /&gt;<br>\n注册中心配置,和服务发现的具体机制有关系。可以是zookeeper地质,也可以eureka地质。上面这个是广播地址,在本地服务调用的测试过程中非常方便。</p>\n</li>\n<li>\n<p>&lt;dubbo:protocol name=&quot;dubbo&quot; port=&quot;20880&quot; /&gt;<br>\n这里是传输的协议和默认端口,一般不需要更改。</p>\n</li>\n</ul>\n<blockquote>\n<p>接下来重点讲解下&lt;dubbo:service/&gt;的配置。</p>\n</blockquote>\n<ul>\n<li>&lt;dubbo:service/&gt;支持的主要属性列表:\n| 属性名 | 说明 |\n| -------- | ----- |\n| version | 版本号 |\n| scope | 服务可见性, 值为:local 或者 remote,默认为remote |\n| actives | 最大的激活的请求数 |\n| async | 方法调用是否异步,默认为false |\n| cache | 服务缓存,可选值:lru/threadlocal/jcache |\n| callbacks | callback实例的限制 |\n| generic | 泛化调用,可以绕过 |\n| class | Service的实现的类名 |\n| connections | 这个服务里的连接数 |\n| delay | 发布服务延迟的毫秒数 |\n| executes | 服务执行的请求上限 |\n| retries | 超时重试次数 |\n| timeout | 调用超时时间 |</li>\n</ul>\n<p>其他配置属性请参考xsd:<a href="http://dubbo.apache.org/schema/dubbo/dubbo.xsd">http://dubbo.apache.org/schema/dubbo/dubbo.xsd</a></p>\n<ul>\n<li>&lt;dubbo:method/&gt;作为&lt;dubbo:service/&gt;的子元素,它可以针对方法进行配置。比较常用的属性有:</li>\n</ul>\n<table>\n<thead>\n<tr>\n<th>属性名</th>\n<th>说明</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>executes</td>\n<td>服务执行的请求上限</td>\n</tr>\n<tr>\n<td>retries</td>\n<td>超时重试次数</td>\n</tr>\n<tr>\n<td>timeout</td>\n<td>调用超时时间</td>\n</tr>\n</tbody>\n</table>\n<p>其他属性,可以参考上面的xsd。</p>\n<h6>配置的覆盖关系</h6>\n<p><img src="https://cdn.yuque.com/lark/0/2018/png/15841/1527849374313-94a5ea24-0e72-4d83-871b-e0e95eab646a.png" alt="undefined"></p>\n<center>配置的覆盖关系图</center>\n<p>这里的覆盖关系包含了Provider和Consumer两端的配置,如果对consumer有疑问,可以参考后一章节的consumer章节之后再来理解。</p>\n<h4>dubbo.properties方式配置</h4>\n<blockquote>\n<p>如果公共配置很简单,没有多注册中心,多协议等情况,或者想多个 Spring 容器想共享配置,可以使用 dubbo.properties 作为缺省配置。</p>\n</blockquote>\n<p>Dubbo 将自动加载 classpath 根目录下的 dubbo.properties,可以通过JVM启动参数 -Ddubbo.properties.file=xxx.properties 改变缺省配置位置。</p>\n<h6>dubbo.properties配置样例</h6>\n<pre><code># 应用名\ndubbo.application.name=dubbodemo-provider\n# 注册中心地址\ndubbo.registry.address=zookeeper://localhost:2181\n# 广播的注册中心样例\n#dubbo.registry.address=multicast://224.5.6.7:1234\n# 调用协议地址\ndubbo.protocol.name=dubbo\ndubbo.protocol.port=28080\n</code></pre>\n<h6>映射规则</h6>\n<p>将 XML 配置的标签名,加属性名,用点分隔,多个属性拆成多行</p>\n<ul>\n<li>比如:dubbo.application.name=foo等价于&lt;dubbo:application name=&quot;foo&quot; /&gt;</li>\n<li>比如:dubbo.registry.address=10.20.153.10:9090等价于&lt;dubbo:registry address=&quot;10.20.153.10:9090&quot; /&gt;</li>\n</ul>\n<p>如果 XML 有多行同名标签配置,可用 id 号区分,如果没有 id 号将对所有同名标签生效</p>\n<ul>\n<li>比如:dubbo.protocol.rmi.port=1234等价于&lt;dubbo:protocol id=&quot;rmi&quot; name=&quot;rmi&quot; port=&quot;1099&quot; /&gt; 2</li>\n<li>比如:dubbo.registry.china.address=10.20.153.10:9090等价于&lt;dubbo:registry id=&quot;china&quot; address=&quot;10.20.153.10:9090&quot; /&gt;</li>\n</ul>\n<h6>覆盖策略</h6>\n<p><img src="https://cdn.yuque.com/lark/0/2018/png/15841/1527849393591-2c3de248-1b3d-47d3-bd10-8b415e9fcd39.png" alt="undefined"></p>\n<ul>\n<li>JVM 启动 -D 参数优先,这样可以使用户在部署和启动时进行参数重写,比如在启动时需改变协议的端口。</li>\n<li>XML 次之,如果在 XML 中有配置,则 dubbo.properties 中的相应配置项无效。</li>\n<li>Properties 最后,相当于缺省值,只有 XML 没有配置时,dubbo.properties 的相应配置项才会生效,通常用于共享公共配置,比如应用名。</li>\n</ul>\n<blockquote>\n<p>注意:</p>\n</blockquote>\n<ol>\n<li>如果 classpath 根目录下存在多个 dubbo.properties,比如多个 jar 包中有 dubbo.properties,Dubbo 会任意加载,并打印 Error 日志,后续可能改为抛异常。 ↩</li>\n<li>协议的 id 没配时,缺省使用协议名作为 id</li>\n</ol>\n<h4>annotation</h4>\n<h6>Service注解暴露服务</h6>\n<pre><code>import com.alibaba.dubbo.config.annotation.Service;\n\n@Service(timeout = 5000)\npublic class AnnotateServiceImpl implements AnnotateService { \n    // ...\n}\n</code></pre>\n<h6>javaconfig形式配置公共模块</h6>\n<pre><code>@Configuration\npublic class DubboConfiguration {\n\n    @Bean\n    public ApplicationConfig applicationConfig() {\n        ApplicationConfig applicationConfig = new ApplicationConfig();\n        applicationConfig.setName(&quot;provider-test&quot;);\n        return applicationConfig;\n    }\n\n    @Bean\n    public RegistryConfig registryConfig() {\n        RegistryConfig registryConfig = new RegistryConfig();\n        registryConfig.setAddress(&quot;zookeeper://127.0.0.1:2181&quot;);\n        registryConfig.setClient(&quot;curator&quot;);\n        return registryConfig;\n    }\n}\n</code></pre>\n<p>这种方式的配置和前面用xml配置的方式是一样的效果。</p>\n<h6>指定dubbo扫描路径</h6>\n<pre><code>@SpringBootApplication\n@DubboComponentScan(basePackages = &quot;com.alibaba.dubbo.test.service.impl&quot;)\npublic class ProviderTestApp {\n    // ...\n}\n</code></pre>\n<p>或者使用spring bean xml配置方式:</p>\n<pre><code>&lt;dubbo:annotation package=&quot;com.chanshuyi.service.impl&quot; /&gt;\n</code></pre>\n<h4>api直接触发</h4>\n<pre><code>import com.alibaba.dubbo.rpc.config.ApplicationConfig;\nimport com.alibaba.dubbo.rpc.config.RegistryConfig;\nimport com.alibaba.dubbo.rpc.config.ProviderConfig;\nimport com.alibaba.dubbo.rpc.config.ServiceConfig;\nimport com.xxx.XxxService;\nimport com.xxx.XxxServiceImpl;\n\n// 服务实现\nXxxService xxxService = new XxxServiceImpl();\n\n// 当前应用配置\nApplicationConfig application = new ApplicationConfig();\napplication.setName(&quot;xxx&quot;);\n\n// 连接注册中心配置\nRegistryConfig registry = new RegistryConfig();\nregistry.setAddress(&quot;10.20.130.230:9090&quot;);\nregistry.setUsername(&quot;aaa&quot;);\nregistry.setPassword(&quot;bbb&quot;);\n\n// 服务提供者协议配置\nProtocolConfig protocol = new ProtocolConfig();\nprotocol.setName(&quot;dubbo&quot;);\nprotocol.setPort(12345);\nprotocol.setThreads(200);\n\n// 注意:ServiceConfig为重对象,内部封装了与注册中心的连接,以及开启服务端口\n\n// 服务提供者暴露服务配置\nServiceConfig&lt;XxxService&gt; service = new ServiceConfig&lt;XxxService&gt;(); // 此实例很重,封装了与注册中心的连接,请自行缓存,否则可能造成内存和连接泄漏\nservice.setApplication(application);\nservice.setRegistry(registry); // 多个注册中心可以用setRegistries()\nservice.setProtocol(protocol); // 多个协议可以用setProtocols()\nservice.setInterface(XxxService.class);\nservice.setRef(xxxService);\nservice.setVersion(&quot;1.0.0&quot;);\n\n// 暴露及注册服务\nservice.export();\n</code></pre>\n<p>一般在spring应用中,不推荐使用这种方式。 具体的含义这里不做解释,可以通过github查看源码。</p>\n<h3>Provider 接口和实现</h3>\n<p>上面章节更多从配置角度出发,接下来通过一个完整的例子,来讲解下dubbo provider的完整使用。</p>\n<p>这个例子中只有一个服务UserReadService,有一个方法 getUserById。 需要将这个服务通过Dubbo暴露给远程的服务。具体的步骤如下:</p>\n<p>1.创建工程\n如果本来已经有工程,可以忽略。创建一个spring boot工程,可以通过 <a href="https://start.spring.io/">https://start.spring.io/</a> 创建。\n2.定义接口\n定义接口:UserReadService</p>\n<pre><code>public interface UserReadService{\npublic User getUserById(Long userId);\n}\n</code></pre>\n<p>这个接口一般来说会放到独立的jar包里,作为client包。 其他应用要消费这个服务的时候,一般来说需要应用引用这个client包。(除了泛化调用)\n3.实现接口\n实现UserReadService, 当前实现部署在Provider的应用中。</p>\n<pre><code>public UserReadServiceImpl implements UserReadService{\n    public User getUserById(Long userId){\n        return xxx;\n    }\n}\n</code></pre>\n<p>4.Dubbo配置</p>\n<pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;\n&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;\n    xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;\n    xmlns:dubbo=&quot;http://dubbo.apache.org/schema/dubbo&quot;\n    xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd&quot;&gt;  \n    &lt;dubbo:application name=&quot;hello-world-app&quot; /&gt;  \n    &lt;dubbo:registry address=&quot;multicast://224.5.6.7:1234&quot; /&gt;  \n    &lt;dubbo:protocol name=&quot;dubbo&quot; port=&quot;20880&quot; /&gt;  \n    &lt;bean id=&quot;userReadService&quot; class=&quot;com.package.UserReadServiceImpl&quot;/&gt;\n    &lt;dubbo:service interface=&quot;com.package.UserReadService&quot; ref=&quot;userReadService&quot; /&gt;  \n&lt;/beans&gt;\n</code></pre>\n<p>Dubbo配置的其他方式可以参考上一章节的相关配置,或者使用集成dubbo spring boot starter方式。</p>\n'},{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'},{filename:"introduction-to-dubbo-spi.md",__html:'<h2>Dubbo可扩展机制实战</h2>\n<h1>1. Dubbo的扩展机制</h1>\n<p>在Dubbo的官网上,Dubbo描述自己是一个高性能的RPC框架。今天我想聊聊Dubbo的另一个很棒的特性, 就是它的可扩展性。\n如同罗马不是一天建成的,任何系统都一定是从小系统不断发展成为大系统的,想要从一开始就把系统设计的足够完善是不可能的,相反的,我们应该关注当下的需求,然后再不断地对系统进行迭代。在代码层面,要求我们适当的对关注点进行抽象和隔离,在软件不断添加功能和特性时,依然能保持良好的结构和可维护性,同时允许第三方开发者对其功能进行扩展。在某些时候,软件设计者对扩展性的追求甚至超过了性能。</p>\n<p>在谈到软件设计时,可扩展性一直被谈起,那到底什么才是可扩展性,什么样的框架才算有良好的可扩展性呢?它必须要做到以下两点:</p>\n<ol>\n<li>作为框架的维护者,在添加一个新功能时,只需要添加一些新代码,而不用大量的修改现有的代码,即符合开闭原则。</li>\n<li>作为框架的使用者,在添加一个新功能时,不需要去修改框架的源码,在自己的工程中添加代码即可。</li>\n</ol>\n<p>Dubbo很好的做到了上面两点。这要得益于Dubbo的微内核+插件的机制。接下来的章节中我们会慢慢揭开Dubbo扩展机制的神秘面纱。</p>\n<h1>2. 可扩展的几种解决方案</h1>\n<p>通常可扩展的实现有下面几种:</p>\n<ul>\n<li>Factory模式</li>\n<li>IoC容器</li>\n<li>OSGI容器</li>\n</ul>\n<p>Dubbo作为一个框架,不希望强依赖其他的IoC容器,比如Spring,Guice。OSGI也是一个很重的实现,不适合Dubbo。最终Dubbo的实现参考了Java原生的SPI机制,但对其进行了一些扩展,以满足Dubbo的需求。</p>\n<h1>3. Java SPI机制</h1>\n<p>既然Dubbo的扩展机制是基于Java原生的SPI机制,那么我们就先来了解下Java SPI吧。了解了Java的SPI,也就是对Dubbo的扩展机制有一个基本的了解。如果对Java SPI比较了解的同学,可以跳过。</p>\n<p>Java SPI(Service Provider Interface)是JDK内置的一种动态加载扩展点的实现。在ClassPath的<code>META-INF/services</code>目录下放置一个与接口同名的文本文件,文件的内容为接口的实现类,多个实现类用换行符分隔。JDK中使用<code>java.util.ServiceLoader</code>来加载具体的实现。\n让我们通过一个简单的例子,来看看Java SPI是如何工作的。</p>\n<ol>\n<li>定义一个接口IRepository用于实现数据储存</li>\n</ol>\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">IRepository</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">save</span><span class="hljs-params">(String data)</span></span>;\n}\n</code></pre>\n<ol start="2">\n<li>提供IRepository的实现\nIRepository有两个实现。MysqlRepository和MongoRepository。</li>\n</ol>\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">MysqlRepository</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">IRepository</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">save</span><span class="hljs-params">(String data)</span> </span>{\n        System.out.println(<span class="hljs-string">"Save "</span> + data + <span class="hljs-string">" to Mysql"</span>);\n    }\n}\n</code></pre>\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">MongoRepository</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">IRepository</span> </span>{\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">save</span><span class="hljs-params">(String data)</span> </span>{\n        System.out.println(<span class="hljs-string">"Save "</span> + data + <span class="hljs-string">" to Mongo"</span>);\n    }\n}\n</code></pre>\n<ol start="3">\n<li>添加配置文件\n在<code>META-INF/services</code>目录添加一个文件,文件名和接口全名称相同,所以文件是<code>META-INF/services/com.demo.IRepository</code>。文件内容为:</li>\n</ol>\n<pre><code class="language-text">com.demo.MongoRepository\ncom.demo.MysqlRepository\n</code></pre>\n<ol start="4">\n<li>通过ServiceLoader加载IRepository实现</li>\n</ol>\n<pre><code class="language-java">ServiceLoader&lt;IRepository&gt; serviceLoader = ServiceLoader.load(IRepository.class);\nIterator&lt;IRepository&gt; it = serviceLoader.iterator();\n<span class="hljs-keyword">while</span> (it != <span class="hljs-keyword">null</span> &amp;&amp; it.hasNext()){\n    IRepository demoService = it.next();\n    System.out.println(<span class="hljs-string">"class:"</span> + demoService.getClass().getName());\n    demoService.save(<span class="hljs-string">"tom"</span>);\n}\n</code></pre>\n<p>在上面的例子中,我们定义了一个扩展点和它的两个实现。在ClassPath中添加了扩展的配置文件,最后使用ServiceLoader来加载所有的扩展点。\n最终的输出结果为:\nclass:testDubbo.MongoRepository\nSave tom to Mongo\nclass:testDubbo.MysqlRepository\nSave tom to Mysql</p>\n<h1>4. Dubbo的SPI机制</h1>\n<p>Java SPI的使用很简单。也做到了基本的加载扩展点的功能。但Java SPI有以下的不足:</p>\n<ul>\n<li>需要遍历所有的实现,并实例化,然后我们在循环中才能找到我们需要的实现。</li>\n<li>配置文件中只是简单的列出了所有的扩展实现,而没有给他们命名。导致在程序中很难去准确的引用它们。</li>\n<li>扩展如果依赖其他的扩展,做不到自动注入和装配</li>\n<li>不提供类似于Spring的IOC和AOP功能</li>\n<li>扩展很难和其他的框架集成,比如扩展里面依赖了一个Spring bean,原生的Java SPI不支持</li>\n</ul>\n<p>所以Java SPI应付一些简单的场景是可以的,但对于Dubbo,它的功能还是比较弱的。Dubbo对原生SPI机制进行了一些扩展。接下来,我们就更深入地了解下Dubbo的SPI机制。</p>\n<h1>5. Dubbo扩展点机制基本概念</h1>\n<p>在深入学习Dubbo的扩展机制之前,我们先明确Dubbo SPI中的一些基本概念。在接下来的内容中,我们会多次用到这些术语。</p>\n<h3>5.1 扩展点(Extension Point)</h3>\n<p>是一个Java的接口。</p>\n<h3>5.2 扩展(Extension)</h3>\n<p>扩展点的实现类。</p>\n<h3>5.3 扩展实例(Extension Instance)</h3>\n<p>扩展点实现类的实例。</p>\n<h3>5.4 扩展自适应实例(Extension Adaptive Instance)</h3>\n<p>第一次接触这个概念时,可能不太好理解(我第一次也是这样的...)。如果称它为扩展代理类,可能更好理解些。扩展的自适应实例其实就是一个Extension的代理,它实现了扩展点接口。在调用扩展点的接口方法时,会根据实际的参数来决定要使用哪个扩展。比如一个IRepository的扩展点,有一个save方法。有两个实现MysqlRepository和MongoRepository。IRepository的自适应实例在调用接口方法的时候,会根据save方法中的参数,来决定要调用哪个IRepository的实现。如果方法参数中有repository=mysql,那么就调用MysqlRepository的save方法。如果repository=mongo,就调用MongoRepository的save方法。和面向对象的延迟绑定很类似。为什么Dubbo会引入扩展自适应实例的概念呢?</p>\n<ul>\n<li>Dubbo中的配置有两种,一种是固定的系统级别的配置,在Dubbo启动之后就不会再改了。还有一种是运行时的配置,可能对于每一次的RPC,这些配置都不同。比如在xml文件中配置了超时时间是10秒钟,这个配置在Dubbo启动之后,就不会改变了。但针对某一次的RPC调用,可以设置它的超时时间是30秒钟,以覆盖系统级别的配置。对于Dubbo而言,每一次的RPC调用的参数都是未知的。只有在运行时,根据这些参数才能做出正确的决定。</li>\n<li>很多时候,我们的类都是一个单例的,比如Spring的bean,在Spring bean都实例化时,如果它依赖某个扩展点,但是在bean实例化时,是不知道究竟该使用哪个具体的扩展实现的。这时候就需要一个代理模式了,它实现了扩展点接口,方法内部可以根据运行时参数,动态的选择合适的扩展实现。而这个代理就是自适应实例。\n自适应扩展实例在Dubbo中的使用非常广泛,Dubbo中,每一个扩展都会有一个自适应类,如果我们没有提供,Dubbo会使用字节码工具为我们自动生成一个。所以我们基本感觉不到自适应类的存在。后面会有例子说明自适应类是怎么工作的。</li>\n</ul>\n<h3>5.5 @SPI</h3>\n<p>@SPI注解作用于扩展点的接口上,表明该接口是一个扩展点。可以被Dubbo的ExtentionLoader加载。如果没有此ExtensionLoader调用会异常。</p>\n<h3>5.6 @Adaptive</h3>\n<p>@Adaptive注解用在扩展接口的方法上。表示该方法是一个自适应方法。Dubbo在为扩展点生成自适应实例时,如果方法有@Adaptive注解,会为该方法生成对应的代码。方法内部会根据方法的参数,来决定使用哪个扩展。\n@Adaptive注解用在类上代表实现一个装饰类,类似于设计模式中的装饰模式,它主要作用是返回指定类,目前在整个系统中AdaptiveCompiler、AdaptiveExtensionFactory这两个类拥有该注解。</p>\n<h3>5.7 ExtentionLoader</h3>\n<p>类似于Java SPI的ServiceLoader,负责扩展的加载和生命周期维护。</p>\n<h3>5.8 扩展别名</h3>\n<p>和Java SPI不同,Dubbo中的扩展都有一个别名,用于在应用中引用它们。比如</p>\n<pre><code class="language-bash">random=com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance\nroundrobin=com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance\n</code></pre>\n<p>其中的random,roundrobin就是对应扩展的别名。这样我们在配置文件中使用random或roundrobin就可以了。</p>\n<h3>5.9 一些路径</h3>\n<p>和Java SPI从<code>/META-INF/services</code>目录加载扩展配置类似,Dubbo也会从以下路径去加载扩展配置文件:</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<h1>6. Dubbo的LoadBalance扩展点解读</h1>\n<p>在了解了Dubbo的一些基本概念后,让我们一起来看一个Dubbo中实际的扩展点,对这些概念有一个更直观的认识。</p>\n<p>我们选择的是Dubbo中的LoadBalance扩展点。Dubbo中的一个服务,通常有多个Provider,consumer调用服务时,需要在多个Provider中选择一个。这就是一个LoadBalance。我们一起来看看在Dubbo中,LoadBalance是如何成为一个扩展点的。</p>\n<h3>6.1 LoadBalance接口</h3>\n<pre><code class="language-java"><span class="hljs-meta">@SPI</span>(RandomLoadBalance.NAME)\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">LoadBalance</span> </span>{\n\n    <span class="hljs-meta">@Adaptive</span>(<span class="hljs-string">"loadbalance"</span>)\n    &lt;T&gt; <span class="hljs-function">Invoker&lt;T&gt; <span class="hljs-title">select</span><span class="hljs-params">(List&lt;Invoker&lt;T&gt;&gt; invokers, URL url, Invocation invocation)</span> <span class="hljs-keyword">throws</span> RpcException</span>;\n}\n</code></pre>\n<p>LoadBalance接口只有一个select方法。select方法从多个invoker中选择其中一个。上面代码中和Dubbo SPI相关的元素有:</p>\n<ul>\n<li>@SPI(<a href="http://RandomLoadBalance.NAME">RandomLoadBalance.NAME</a>)\n@SPI作用于LoadBalance接口,表示接口LoadBalance是一个扩展点。如果没有@SPI注解,试图去加载扩展时,会抛出异常。@SPI注解有一个参数,该参数表示该扩展点的默认实现的别名。如果没有显示的指定扩展,就使用默认实现。<code>RandomLoadBalance.NAME</code>是一个常量,值是&quot;random&quot;,是一个随机负载均衡的实现。\nrandom的定义在配置文件<code>META-INF/dubbo/internal/com.alibaba.dubbo.rpc.cluster.LoadBalance</code>中:</li>\n</ul>\n<pre><code class="language-bash">random=com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance\nroundrobin=com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance\nleastactive=com.alibaba.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance\nconsistenthash=com.alibaba.dubbo.rpc.cluster.loadbalance.ConsistentHashLoadBalance\n</code></pre>\n<p>可以看到文件中定义了4个LoadBalance的扩展实现。由于负载均衡的实现不是本次的内容,这里就不过多说明。只用知道Dubbo提供了4种负载均衡的实现,我们可以通过xml文件,properties文件,JVM参数显式的指定一个实现。如果没有,默认使用随机。</p>\n<p><img src="https://raw.githubusercontent.com/vangoleo/wiki/master/dubbo/dubbo_loadbalance.png" alt="dubbo-loadbalance | left"></p>\n<ul>\n<li>@Adaptive(&quot;loadbalance&quot;)\n@Adaptive注解修饰select方法,表明方法select方法是一个可自适应的方法。Dubbo会自动生成该方法对应的代码。当调用select方法时,会根据具体的方法参数来决定调用哪个扩展实现的select方法。@Adaptive注解的参数<code>loadbalance</code>表示方法参数中的loadbalance的值作为实际要调用的扩展实例。\n但奇怪的是,我们发现select的方法中并没有loadbalance参数,那怎么获取loadbalance的值呢?select方法中还有一个URL类型的参数,Dubbo就是从URL中获取loadbalance的值的。这里涉及到Dubbo的URL总线模式,简单说,URL中包含了RPC调用中的所有参数。URL类中有一个<code>Map&lt;String, String&gt; parameters</code>字段,parameters中就包含了loadbalance。</li>\n</ul>\n<h3>6.2 获取LoadBalance扩展</h3>\n<p>Dubbo中获取LoadBalance的代码如下:</p>\n<pre><code class="language-java">LoadBalance lb = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(loadbalanceName);\n</code></pre>\n<p>使用ExtensionLoader.getExtensionLoader(LoadBalance.class)方法获取一个ExtensionLoader的实例,然后调用getExtension,传入一个扩展的别名来获取对应的扩展实例。</p>\n<h1>7. 自定义一个LoadBalance扩展</h1>\n<p>本节中,我们通过一个简单的例子,来自己实现一个LoadBalance,并把它集成到Dubbo中。我会列出一些关键的步骤和代码,也可以从这个地址(<a href="https://github.com/vangoleo/dubbo-spi-demo">https://github.com/vangoleo/dubbo-spi-demo</a>)下载完整的demo。</p>\n<h3>7.1 实现LoadBalance接口</h3>\n<p>首先,编写一个自己实现的LoadBalance,因为是为了演示Dubbo的扩展机制,而不是LoadBalance的实现,所以这里LoadBalance的实现非常简单,选择第一个invoker,并在控制台输出一条日志。</p>\n<pre><code class="language-java"><span class="hljs-keyword">package</span> com.dubbo.spi.demo.consumer;\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DemoLoadBalance</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">LoadBalance</span> </span>{\n    <span class="hljs-meta">@Override</span>\n    <span class="hljs-keyword">public</span> &lt;T&gt; <span class="hljs-function">Invoker&lt;T&gt; <span class="hljs-title">select</span><span class="hljs-params">(List&lt;Invoker&lt;T&gt;&gt; invokers, URL url, Invocation invocation)</span> <span class="hljs-keyword">throws</span> RpcException </span>{\n        System.out.println(<span class="hljs-string">"DemoLoadBalance: Select the first invoker..."</span>);\n        <span class="hljs-keyword">return</span> invokers.get(<span class="hljs-number">0</span>);\n    }\n}\n</code></pre>\n<h3>7.2 添加扩展配置文件</h3>\n<p>添加文件:<code>META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.LoadBalance</code>。文件内容如下:</p>\n<pre><code class="language-bash">demo=com.dubbo.spi.demo.consumer.DemoLoadBalance\n</code></pre>\n<h3>7.3 配置使用自定义LoadBalance</h3>\n<p>通过上面的两步,已经添加了一个名字为demo的LoadBalance实现,并在配置文件中进行了相应的配置。接下来,需要显式的告诉Dubbo使用demo的负载均衡实现。如果是通过spring的方式使用Dubbo,可以在xml文件中进行设置。</p>\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">"helloService"</span> <span class="hljs-attr">interface</span>=<span class="hljs-string">"com.dubbo.spi.demo.api.IHelloService"</span> <span class="hljs-attr">loadbalance</span>=<span class="hljs-string">"demo"</span> /&gt;</span>\n</code></pre>\n<p>在consumer端的<a href="dubbo:reference">dubbo:reference</a>中配置&lt;loadbalance=&quot;demo&quot;&gt;</p>\n<h3>7.4 启动Dubbo</h3>\n<p>启动Dubbo,调用一次IHelloService,可以看到控制台会输出一条<code>DemoLoadBalance: Select the first invoker...</code>日志。说明Dubbo的确是使用了我们自定义的LoadBalance。</p>\n<h1>总结</h1>\n<p>到此,我们从Java SPI开始,了解了Dubbo SPI 的基本概念,并结合了Dubbo中的LoadBalance加深了理解。最后,我们还实践了一下,创建了一个自定义LoadBalance,并集成到Dubbo中。相信通过这里理论和实践的结合,大家对Dubbo的可扩展有更深入的理解。\n总结一下,Dubbo SPI有以下的特点:</p>\n<ul>\n<li>对Dubbo进行扩展,不需要改动Dubbo的源码</li>\n<li>自定义的Dubbo的扩展点实现,是一个普通的Java类,Dubbo没有引入任何Dubbo特有的元素,对代码侵入性几乎为零。</li>\n<li>将扩展注册到Dubbo中,只需要在ClassPath中添加配置文件。使用简单。而且不会对现有代码造成影响。符合开闭原则。</li>\n<li>dubbo的扩展机制设计默认值:@SPI(&quot;dubbo&quot;) 代表默认的spi对象</li>\n<li>Dubbo的扩展机制支持IoC,AoP等高级功能</li>\n<li>Dubbo的扩展机制能很好的支持第三方IoC容器,默认支持Spring Bean,可自己扩展来支持其他容器,比如Google的Guice。</li>\n<li>切换扩展点的实现,只需要在配置文件中修改具体的实现,不需要改代码。使用方便。</li>\n</ul>\n<p>下一篇,我们将会一起深入Dubbo的源码,更深入的了解Dubbo的可扩展机制。</p>\n'},{filename:"optimization-branch-prediction.md",__html:'<h1>优化技巧:提前if判断帮助CPU分支预测</h1>\n<hr>\n<h2>分支预测</h2>\n<p>在stackoverflow上有一个非常有名的问题:<a href="https://stackoverflow.com/questions/11227809/why-is-it-faster-to-process-a-sorted-array-than-an-unsorted-array">为什么处理有序数组要比非有序数组快?</a>,可见分支预测对代码运行效率有非常大的影响。</p>\n<p>现代CPU都支持分支预测(branch prediction)和指令流水线(instruction pipeline),这两个结合可以极大提高CPU效率。对于像简单的if跳转,CPU是可以比较好地做分支预测的。但是对于switch跳转,CPU则没有太多的办法。switch本质上是据索引,从地址数组里取地址再跳转。</p>\n<p>要提高代码执行效率,一个重要的原则就是尽量避免CPU把流水线清空,那么提高分支预测的成功率就非常重要。</p>\n<p>那么对于代码里,如果某个switch分支概率很高,是否可以考虑代码层面帮CPU把判断提前,来提高代码执行效率呢?</p>\n<h2>Dubbo里ChannelEventRunnable的switch判断</h2>\n<p>在<code>ChannelEventRunnable</code>里有一个switch来判断channel state,然后做对应的逻辑:<a href="https://github.com/hengyunabc/incubator-dubbo/blob/dubbo-2.6.1/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispatcher/ChannelEventRunnable.java#L54">查看</a></p>\n<p>一个channel建立起来之后,超过99.9%情况它的state都是<code>ChannelState.RECEIVED</code>,那么可以考虑把这个判断提前。</p>\n<h2>benchmark验证</h2>\n<p>下面通过jmh来验证下:</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">TestBenchMarks</span> </span>{\n\t<span class="hljs-keyword">public</span> <span class="hljs-keyword">enum</span> ChannelState {\n\t\tCONNECTED, DISCONNECTED, SENT, RECEIVED, CAUGHT\n\t}\n\n\t<span class="hljs-meta">@State</span>(Scope.Benchmark)\n\t<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ExecutionPlan</span> </span>{\n\t\t<span class="hljs-meta">@Param</span>({ <span class="hljs-string">"1000000"</span> })\n\t\t<span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> size;\n\t\t<span class="hljs-keyword">public</span> ChannelState[] states = <span class="hljs-keyword">null</span>;\n\n\t\t<span class="hljs-meta">@Setup</span>\n\t\t<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setUp</span><span class="hljs-params">()</span> </span>{\n\t\t\tChannelState[] values = ChannelState.values();\n\t\t\tstates = <span class="hljs-keyword">new</span> ChannelState[size];\n\t\t\tRandom random = <span class="hljs-keyword">new</span> Random(<span class="hljs-keyword">new</span> Date().getTime());\n\t\t\t<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; size; i++) {\n\t\t\t\t<span class="hljs-keyword">int</span> nextInt = random.nextInt(<span class="hljs-number">1000000</span>);\n\t\t\t\t<span class="hljs-keyword">if</span> (nextInt &gt; <span class="hljs-number">100</span>) {\n\t\t\t\t\tstates[i] = ChannelState.RECEIVED;\n\t\t\t\t} <span class="hljs-keyword">else</span> {\n\t\t\t\t\tstates[i] = values[nextInt % values.length];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t<span class="hljs-meta">@Fork</span>(value = <span class="hljs-number">5</span>)\n\t<span class="hljs-meta">@Benchmark</span>\n\t<span class="hljs-meta">@BenchmarkMode</span>(Mode.Throughput)\n\t<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">benchSiwtch</span><span class="hljs-params">(ExecutionPlan plan, Blackhole bh)</span> </span>{\n\t\t<span class="hljs-keyword">int</span> result = <span class="hljs-number">0</span>;\n\t\t<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; plan.size; ++i) {\n\t\t\t<span class="hljs-keyword">switch</span> (plan.states[i]) {\n\t\t\t<span class="hljs-keyword">case</span> CONNECTED:\n\t\t\t\tresult += ChannelState.CONNECTED.ordinal();\n\t\t\t\t<span class="hljs-keyword">break</span>;\n\t\t\t<span class="hljs-keyword">case</span> DISCONNECTED:\n\t\t\t\tresult += ChannelState.DISCONNECTED.ordinal();\n\t\t\t\t<span class="hljs-keyword">break</span>;\n\t\t\t<span class="hljs-keyword">case</span> SENT:\n\t\t\t\tresult += ChannelState.SENT.ordinal();\n\t\t\t\t<span class="hljs-keyword">break</span>;\n\t\t\t<span class="hljs-keyword">case</span> RECEIVED:\n\t\t\t\tresult += ChannelState.RECEIVED.ordinal();\n\t\t\t\t<span class="hljs-keyword">break</span>;\n\t\t\t<span class="hljs-keyword">case</span> CAUGHT:\n\t\t\t\tresult += ChannelState.CAUGHT.ordinal();\n\t\t\t\t<span class="hljs-keyword">break</span>;\n\t\t\t}\n\t\t}\n\t\tbh.consume(result);\n\t}\n\n\t<span class="hljs-meta">@Fork</span>(value = <span class="hljs-number">5</span>)\n\t<span class="hljs-meta">@Benchmark</span>\n\t<span class="hljs-meta">@BenchmarkMode</span>(Mode.Throughput)\n\t<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">benchIfAndSwitch</span><span class="hljs-params">(ExecutionPlan plan, Blackhole bh)</span> </span>{\n\t\t<span class="hljs-keyword">int</span> result = <span class="hljs-number">0</span>;\n\t\t<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; plan.size; ++i) {\n\t\t\tChannelState state = plan.states[i];\n\t\t\t<span class="hljs-keyword">if</span> (state == ChannelState.RECEIVED) {\n\t\t\t\tresult += ChannelState.RECEIVED.ordinal();\n\t\t\t} <span class="hljs-keyword">else</span> {\n\t\t\t\t<span class="hljs-keyword">switch</span> (state) {\n\t\t\t\t<span class="hljs-keyword">case</span> CONNECTED:\n\t\t\t\t\tresult += ChannelState.CONNECTED.ordinal();\n\t\t\t\t\t<span class="hljs-keyword">break</span>;\n\t\t\t\t<span class="hljs-keyword">case</span> SENT:\n\t\t\t\t\tresult += ChannelState.SENT.ordinal();\n\t\t\t\t\t<span class="hljs-keyword">break</span>;\n\t\t\t\t<span class="hljs-keyword">case</span> DISCONNECTED:\n\t\t\t\t\tresult += ChannelState.DISCONNECTED.ordinal();\n\t\t\t\t\t<span class="hljs-keyword">break</span>;\n\t\t\t\t<span class="hljs-keyword">case</span> CAUGHT:\n\t\t\t\t\tresult += ChannelState.CAUGHT.ordinal();\n\t\t\t\t\t<span class="hljs-keyword">break</span>;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tbh.consume(result);\n\t}\n}\n</code></pre>\n<ul>\n<li>benchSiwtch里是纯switch判断</li>\n<li>benchIfAndSwitch 里用一个if提前判断state是否<code>ChannelState.RECEIVED</code></li>\n</ul>\n<p>benchmark结果是:</p>\n<pre><code>Result &quot;io.github.hengyunabc.jmh.TestBenchMarks.benchSiwtch&quot;:\n  576.745 ±(99.9%) 6.806 ops/s [Average]\n  (min, avg, max) = (490.348, 576.745, 618.360), stdev = 20.066\n  CI (99.9%): [569.939, 583.550] (assumes normal distribution)\n\n\n# Run complete. Total time: 00:06:48\n\nBenchmark                         (size)   Mode  Cnt     Score    Error  Units\nTestBenchMarks.benchIfAndSwitch  1000000  thrpt  100  1535.867 ± 61.212  ops/s\nTestBenchMarks.benchSiwtch       1000000  thrpt  100   576.745 ±  6.806  ops/s\n</code></pre>\n<p>可以看到提前if判断的确提高了代码效率,这种技巧可以放在性能要求严格的地方。</p>\n<p>Benchmark代码:<a href="https://github.com/hengyunabc/jmh-demo">https://github.com/hengyunabc/jmh-demo</a></p>\n<h2>总结</h2>\n<ul>\n<li>switch对于CPU来说难以做分支预测</li>\n<li>某些switch条件如果概率比较高,可以考虑单独提前if判断,充分利用CPU的分支预测机制</li>\n</ul>\n'},{filename:"spring-boot-dubbo-start-stop-analysis.md",__html:'<h1>Spring-boot+Dubbo应用启停源码分析</h1>\n<h3>背景介绍</h3>\n<p><a href="https://github.com/apache/incubator-dubbo-spring-boot-project">Dubbo Spring Boot</a> 工程致力于简化 Dubbo RPC 框架在Spring Boot应用场景的开发。同时也整合了 Spring Boot 特性:</p>\n<ul>\n<li><a href="https://github.com/apache/incubator-dubbo-spring-boot-project/blob/master/dubbo-spring-boot-autoconfigure">自动装配</a> (比如: 注解驱动, 自动装配等).</li>\n<li><a href="https://github.com/apache/incubator-dubbo-spring-boot-project/blob/master/dubbo-spring-boot-actuator">Production-Ready</a> (比如: 安全, 健康检查, 外部化配置等).</li>\n</ul>\n<h3>DubboConsumer启动分析</h3>\n<p>你有没有想过一个问题?<code>incubator-dubbo-spring-boot-project</code>中的<code>DubboConsumerDemo</code>应用就一行代码,<code>main</code>方法执行完之后,为什么不会直接退出呢?</p>\n<pre><code class="language-java"><span class="hljs-meta">@SpringBootApplication</span>(scanBasePackages = <span class="hljs-string">"com.alibaba.boot.dubbo.demo.consumer.controller"</span>)\n<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DubboConsumerDemo</span> </span>{\n\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{\n        SpringApplication.run(DubboConsumerDemo.class,args);\n    }\n\n}\n</code></pre>\n<p>其实要回答这样一个问题,我们首先需要把这个问题进行一个抽象,即一个JVM进程,在什么情况下会退出?</p>\n<p>以Java 8为例,通过查阅JVM语言规范[1],在12.8章节中有清晰的描述:</p>\n<p>A program terminates all its activity and <em>exits</em> when one of two things happens:</p>\n<ul>\n<li>All the threads that are not daemon threads terminate.</li>\n<li>Some thread invokes the <code>exit</code> method of class <code>Runtime</code> or class <code>System</code>, and the <code>exit</code> operation is not forbidden by the security manager.</li>\n</ul>\n<p>也就是说,导致JVM的退出只有2种情况:</p>\n<ol>\n<li>所有的非daemon进程完全终止</li>\n<li>某个线程调用了<code>System.exit()</code>或<code>Runtime.exit()</code></li>\n</ol>\n<p>因此针对上面的情况,我们判断,一定是有某个非daemon线程没有退出导致。我们知道,通过jstack可以看到所有的线程信息,包括他们是否是daemon线程,可以通过jstack找出那些是非deamon的线程。</p>\n<pre><code class="language-sh">➜  jstack 57785 | grep tid | grep -v <span class="hljs-string">"daemon"</span>\n<span class="hljs-string">"container-0"</span> <span class="hljs-comment">#37 prio=5 os_prio=31 tid=0x00007fbe312f5800 nid=0x7103 waiting on condition  [0x0000700010144000]</span>\n<span class="hljs-string">"container-1"</span> <span class="hljs-comment">#49 prio=5 os_prio=31 tid=0x00007fbe3117f800 nid=0x7b03 waiting on condition  [0x0000700010859000]</span>\n<span class="hljs-string">"DestroyJavaVM"</span> <span class="hljs-comment">#83 prio=5 os_prio=31 tid=0x00007fbe30011000 nid=0x2703 waiting on condition  [0x0000000000000000]</span>\n<span class="hljs-string">"VM Thread"</span> os_prio=31 tid=0x00007fbe3005e800 nid=0x3703 runnable\n<span class="hljs-string">"GC Thread#0"</span> os_prio=31 tid=0x00007fbe30013800 nid=0x5403 runnable\n<span class="hljs-string">"GC Thread#1"</span> os_prio=31 tid=0x00007fbe30021000 nid=0x5303 runnable\n<span class="hljs-string">"GC Thread#2"</span> os_prio=31 tid=0x00007fbe30021800 nid=0x2d03 runnable\n<span class="hljs-string">"GC Thread#3"</span> os_prio=31 tid=0x00007fbe30022000 nid=0x2f03 runnable\n<span class="hljs-string">"G1 Main Marker"</span> os_prio=31 tid=0x00007fbe30040800 nid=0x5203 runnable\n<span class="hljs-string">"G1 Conc#0"</span> os_prio=31 tid=0x00007fbe30041000 nid=0x4f03 runnable\n<span class="hljs-string">"G1 Refine#0"</span> os_prio=31 tid=0x00007fbe31044800 nid=0x4e03 runnable\n<span class="hljs-string">"G1 Refine#1"</span> os_prio=31 tid=0x00007fbe31045800 nid=0x4d03 runnable\n<span class="hljs-string">"G1 Refine#2"</span> os_prio=31 tid=0x00007fbe31046000 nid=0x4c03 runnable\n<span class="hljs-string">"G1 Refine#3"</span> os_prio=31 tid=0x00007fbe31047000 nid=0x4b03 runnable\n<span class="hljs-string">"G1 Young RemSet Sampling"</span> os_prio=31 tid=0x00007fbe31047800 nid=0x3603 runnable\n<span class="hljs-string">"VM Periodic Task Thread"</span> os_prio=31 tid=0x00007fbe31129000 nid=0x6703 waiting on condition\n\n</code></pre>\n<blockquote>\n<p>此处通过grep tid 找出所有的线程摘要,通过grep -v找出不包含daemon关键字的行</p>\n</blockquote>\n<p>通过上面的结果,我们发现了一些信息:</p>\n<ul>\n<li>有两个线程<code>container-0</code>, <code>container-1</code>非常可疑,他们是非daemon线程,处于wait状态</li>\n<li>有一些GC相关的线程,和VM打头的线程,也是非daemon线程,但他们很有可能是JVM自己的线程,在此暂时忽略。</li>\n</ul>\n<p>综上,我们可以推断,很可能是因为<code>container-0</code>和<code>container-1</code>导致JVM没有退出。现在我们通过源码,搜索一下到底是谁创建的这两个线程。</p>\n<p>通过对spring-boot的源码分析,我们在<code>org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer</code>的<code>startDaemonAwaitThread</code>找到了如下代码</p>\n<pre><code class="language-java">\t<span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">startDaemonAwaitThread</span><span class="hljs-params">()</span> </span>{\n\t\tThread awaitThread = <span class="hljs-keyword">new</span> Thread(<span class="hljs-string">"container-"</span> + (containerCounter.get())) {\n\n\t\t\t<span class="hljs-meta">@Override</span>\n\t\t\t<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\t\t\t\tTomcatEmbeddedServletContainer.<span class="hljs-keyword">this</span>.tomcat.getServer().await();\n\t\t\t}\n\n\t\t};\n\t\tawaitThread.setContextClassLoader(getClass().getClassLoader());\n\t\tawaitThread.setDaemon(<span class="hljs-keyword">false</span>);\n\t\tawaitThread.start();\n\t}\n</code></pre>\n<p>在这个方法加个断点,看下调用堆栈:</p>\n<pre><code>initialize:115, TomcatEmbeddedServletContainer (org.springframework.boot.context.embedded.tomcat)\n&lt;init&gt;:84, TomcatEmbeddedServletContainer (org.springframework.boot.context.embedded.tomcat)\ngetTomcatEmbeddedServletContainer:554, TomcatEmbeddedServletContainerFactory (org.springframework.boot.context.embedded.tomcat)\ngetEmbeddedServletContainer:179, TomcatEmbeddedServletContainerFactory (org.springframework.boot.context.embedded.tomcat)\ncreateEmbeddedServletContainer:164, EmbeddedWebApplicationContext (org.springframework.boot.context.embedded)\nonRefresh:134, EmbeddedWebApplicationContext (org.springframework.boot.context.embedded)\nrefresh:537, AbstractApplicationContext (org.springframework.context.support)\nrefresh:122, EmbeddedWebApplicationContext (org.springframework.boot.context.embedded)\nrefresh:693, SpringApplication (org.springframework.boot)\nrefreshContext:360, SpringApplication (org.springframework.boot)\nrun:303, SpringApplication (org.springframework.boot)\nrun:1118, SpringApplication (org.springframework.boot)\nrun:1107, SpringApplication (org.springframework.boot)\nmain:35, DubboConsumerDemo (com.alibaba.boot.dubbo.demo.consumer.bootstrap)\n</code></pre>\n<p>可以看到,spring-boot应用在启动的过程中,由于默认启动了Tomcat暴露HTTP服务,所以执行到了上述方法,而Tomcat启动的所有的线程,默认都是daemon线程,例如监听请求的Acceptor,工作线程池等等,如果这里不加控制的话,启动完成之后JVM也会退出。因此需要显示的启动一个线程,在某个条件下进行持续等待,从而避免线程退出。</p>\n<p>下面我们在深挖一下,在Tomcat的<code>this.tomcat.getServer().await()</code>这个方法中,线程是如何实现不退出的。这里为了阅读方便,去掉了不相关的代码。</p>\n<pre><code class="language-java"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">await</span><span class="hljs-params">()</span> </span>{\n    \t<span class="hljs-comment">// ...</span>\n        <span class="hljs-keyword">if</span>( port==-<span class="hljs-number">1</span> ) {\n            <span class="hljs-keyword">try</span> {\n                awaitThread = Thread.currentThread();\n                <span class="hljs-keyword">while</span>(!stopAwait) {\n                    <span class="hljs-keyword">try</span> {\n                        Thread.sleep( <span class="hljs-number">10000</span> );\n                    } <span class="hljs-keyword">catch</span>( InterruptedException ex ) {\n                        <span class="hljs-comment">// continue and check the flag</span>\n                    }\n                }\n            } <span class="hljs-keyword">finally</span> {\n                awaitThread = <span class="hljs-keyword">null</span>;\n            }\n            <span class="hljs-keyword">return</span>;\n        }\n\t\t<span class="hljs-comment">// ...</span>\n    }\n</code></pre>\n<p>在await方法中,实际上当前线程在一个while循环中每10秒检查一次 <code>stopAwait</code>这个变量,它是一个<code>volatile</code>类型变量,用于确保被另一个线程修改后,当前线程能够立即看到这个变化。如果没有变化,就会一直处于while循环中。这就是该线程不退出的原因,也就是整个spring-boot应用不退出的原因。</p>\n<p>因为Springboot应用同时启动了8080和8081(management port)两个端口,实际是启动了两个Tomcat,因此会有两个线程<code>container-0</code>和<code>container-1</code>。</p>\n<p>接下来,我们再看看,这个Spring-boot应用又是如何退出的呢?</p>\n<h3>DubboConsumer退出分析</h3>\n<p>在前面的描述中提到,有一个线程持续的在检查<code>stopAwait</code>这个变量,那么我们自然想到,在Stop的时候,应该会有一个线程去修改<code>stopAwait</code>,打破这个while循环,那又是谁在修改这个变量呢?</p>\n<p>通过对源码分析,可以看到只有一个方法修改了<code>stopAwait</code>,即<code>org.apache.catalina.core.StandardServer#stopAwait</code>,我们在此处加个断点,看看是谁在调用。</p>\n<blockquote>\n<p>注意,当我们在Intellij IDEA的Debug模式,加上一个断点后,需要在命令行下使用<code>kill -s INT $PID</code>或者<code>kill -s TERM $PID</code>才能触发断点,点击IDE上的Stop按钮,不会触发断点。这是IDEA的bug</p>\n</blockquote>\n<p>可以看到有一个名为<code>Thread-3</code>的线程调用了该方法:</p>\n<pre><code class="language-java">stopAwait:<span class="hljs-number">390</span>, StandardServer (org.apache.catalina.core)\nstopInternal:<span class="hljs-number">819</span>, StandardServer (org.apache.catalina.core)\nstop:<span class="hljs-number">226</span>, LifecycleBase (org.apache.catalina.util)\nstop:<span class="hljs-number">377</span>, Tomcat (org.apache.catalina.startup)\nstopTomcat:<span class="hljs-number">241</span>, TomcatEmbeddedServletContainer (org.springframework.boot.context.embedded.tomcat)\nstop:<span class="hljs-number">295</span>, TomcatEmbeddedServletContainer (org.springframework.boot.context.embedded.tomcat)\nstopAndReleaseEmbeddedServletContainer:<span class="hljs-number">306</span>, EmbeddedWebApplicationContext (org.springframework.boot.context.embedded)\nonClose:<span class="hljs-number">155</span>, EmbeddedWebApplicationContext (org.springframework.boot.context.embedded)\ndoClose:<span class="hljs-number">1014</span>, AbstractApplicationContext (org.springframework.context.support)\nrun:<span class="hljs-number">929</span>, AbstractApplicationContext$<span class="hljs-number">2</span> (org.springframework.context.support)\n</code></pre>\n<p>通过源码分析,原来是通过Spring注册的<code>ShutdownHook</code>来执行的</p>\n<pre><code class="language-java">\t<span class="hljs-meta">@Override</span>\n\t<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">registerShutdownHook</span><span class="hljs-params">()</span> </span>{\n\t\t<span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.shutdownHook == <span class="hljs-keyword">null</span>) {\n\t\t\t<span class="hljs-comment">// No shutdown hook registered yet.</span>\n\t\t\t<span class="hljs-keyword">this</span>.shutdownHook = <span class="hljs-keyword">new</span> Thread() {\n\t\t\t\t<span class="hljs-meta">@Override</span>\n\t\t\t\t<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\t\t\t\t\t<span class="hljs-keyword">synchronized</span> (startupShutdownMonitor) {\n\t\t\t\t\t\tdoClose();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t};\n\t\t\tRuntime.getRuntime().addShutdownHook(<span class="hljs-keyword">this</span>.shutdownHook);\n\t\t}\n\t}\n</code></pre>\n<p>通过查阅Java的API文档[2], 我们可以知道ShutdownHook将在下面两种情况下执行</p>\n<blockquote>\n<p>The Java virtual machine <em>shuts down</em> in response to two kinds of events:</p>\n<ul>\n<li>The program <em>exits</em> normally, when the last non-daemon thread exits or when the <code>exit</code> (equivalently, <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/System.html#exit-int-"><code>System.exit</code></a>) method is invoked, or</li>\n<li>The virtual machine is <em>terminated</em> in response to a user interrupt, such as typing <code>^C</code>, or a system-wide event, such as user logoff or system shutdown.</li>\n</ul>\n</blockquote>\n<ol>\n<li>调用了System.exit()方法</li>\n<li>响应外部的信号,例如Ctrl+C(其实发送的是SIGINT信号),或者是<code>SIGTERM</code>信号(默认<code>kill $PID</code>发送的是<code>SIGTERM</code>信号)</li>\n</ol>\n<p>因此,正常的应用在停止过程中(<code>kill -9 $PID</code>除外),都会执行上述ShutdownHook,它的作用不仅仅是关闭tomcat,还有进行其他的清理工作,在此不再赘述。</p>\n<h3>总结</h3>\n<ol>\n<li>在<code>DubboConsumer</code>启动的过程中,通过启动一个独立的非daemon线程循环检查变量的状态,确保进程不退出</li>\n<li>在<code>DubboConsumer</code>停止的过程中,通过执行spring容器的shutdownhook,修改了变量的状态,使得程序正常退出</li>\n</ol>\n<h3>问题</h3>\n<p>在DubboProvider的例子中,我们看到Provider并没有启动Tomcat提供HTTP服务,那又是如何实现不退出的呢?我们将在下一篇文章中回答这个问题。</p>\n<h4>彩蛋</h4>\n<p>在<code>Intellij IDEA</code>中运行了如下的单元测试,创建一个线程执行睡眠1000秒的操作,我们惊奇的发现,代码并没有线程执行完就退出了,这又是为什么呢?(被创建的线程是非daemon线程)</p>\n<pre><code class="language-java">    <span class="hljs-meta">@Test</span>\n    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">test</span><span class="hljs-params">()</span> </span>{\n        <span class="hljs-keyword">new</span> Thread(<span class="hljs-keyword">new</span> Runnable() {\n            <span class="hljs-meta">@Override</span>\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">try</span> {\n                    Thread.sleep(<span class="hljs-number">1000000</span>);\n                } <span class="hljs-keyword">catch</span> (InterruptedException e) {\n                    e.printStackTrace();\n                }\n            }\n        }).start();\n    }\n</code></pre>\n<p>[1] <a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.8">https://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.8</a></p>\n<p>[2] <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#addShutdownHook">https://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#addShutdownHook</a></p>\n'}],"en-us":[{filename:"apachecon-na-2018.md",__html:'<h2>The ApacheCon NA schedule has been announced</h2>\n<p>Ian Luo(PPMC) and Jun Liu(PPMC) will talk about &quot;Introducing Apache Dubbo(Incubating): What is Dubbo and How it Works&quot; at ApacheCon NA this year in Montréal! Please check out the schedule <a href="https://apachecon.dukecon.org/acna/2018/#/scheduledEvent/b8db9dc580d85853f">here</a> and register <a href="https://www.eventbrite.com/e/apachecon-north-america-2018-registration-43200327342">here</a>.</p>\n'},{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'},{filename:"qcon-beijing-2018.md",__html:'<h2>Dubbo roadmap is announced in QCon Beijing 2018</h2>\n<p>Ian Luo has delivered a great talk at QCon Beijing 2018, where the roadmap of Dubbo has also be announced. Please enjoy the <a href="https://github.com/dubbo/awesome-dubbo/raw/master/slides/qcon2018/dubbo-present-and-future.pdf">slides</a>!</p>\n'}]}},,,,,,,,,,,,,,,,,,,,,function(n,e,t){t(14)(t(105))},,,,,,,,,function(n,e){n.exports=".blog-detail-page .blog-content {\n  padding: 80px 20%;\n  margin: 66px auto 0;\n  max-width: 735px; }\n"}]);
\ No newline at end of file
diff --git a/build/b04dbfd23d6672063557.js b/build/b04dbfd23d6672063557.js
new file mode 100644
index 0000000..993a0b5
--- /dev/null
+++ b/build/b04dbfd23d6672063557.js
@@ -0,0 +1,6 @@
+webpackJsonp([3],[,,,,,,function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function a(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(t,"__esModule",{value:!0});var l=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),u=n(0),c=r(u),s=n(1),f=n(18),d=r(f),p=n(21),h=r(p),b=n(25),g=r(b),y=n(24),m=r(y),v=n(55),w=r(v),x=n(81),O=r(x),k=n(85),j=r(k),E=n(23),S=r(E),_=n(77),A=r(_),P=n(17),z=r(P);n(95);var C=function(e){function t(){return o(this,t),i(this,(t.__proto__||Object.getPrototypeOf(t)).apply(this,arguments))}return a(t,e),l(t,[{key:"render",value:function(){var e=window.location.hash.split("?"),t=h.default.parse(e[1]||""),n=t.lang||d.default.get("docsite_language")||z.default.defaultLanguage;if("en-us"!==n&&"zh-cn"!==n&&(n=z.default.defaultLanguage),n!==d.default.get("docsite_language")&&d.default.set("docsite_language",n,{expires:365,path:""}),!t.lang)return c.default.createElement(s.Redirect,{to:this.props.match.url+"?lang="+n});var r=A.default[n],o=r.list;return c.default.createElement("div",{className:"blog-list-page"},c.default.createElement(m.default,{type:"normal",logo:"./img/dubbo_colorful.png",language:n,onLanguageChange:this.onLanguageChange}),c.default.createElement(w.default,{img:"./img/blog.png",text:r.barText}),c.default.createElement("section",{className:"blog-container"},c.default.createElement("div",{className:"col col-18 left-part"},c.default.createElement(O.default,{pageSize:5},o.map(function(e,t){return c.default.createElement(j.default,{key:t,dataSource:e})}))),c.default.createElement("div",{className:"col col-6 right-part"},c.default.createElement("h4",null,r.postsTitle),c.default.createElement("ul",null,o.map(function(e,t){return c.default.createElement("li",{key:t},c.default.createElement(s.Link,{to:e.link},c.default.createElement("span",null,e.dateStr,"  "),c.default.createElement("span",null,e.title)))})))),c.default.createElement(S.default,{logo:"./img/dubbo_gray.png"}))}}]),t}(g.default);t.default=C},,,,,,,,,function(e,t,n){"use strict";function r(e,t,n,r){n&&Object.defineProperty(e,t,{enumerable:n.enumerable,configurable:n.configurable,writable:n.writable,value:n.initializer?n.initializer.call(r):void 0})}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t,n,r,o){var i={};return Object.keys(r).forEach(function(e){i[e]=r[e]}),i.enumerable=!!i.enumerable,i.configurable=!!i.configurable,("value"in i||i.initializer)&&(i.writable=!0),i=n.slice().reverse().reduce(function(n,r){return r(e,t,n)||n},i),o&&void 0!==i.initializer&&(i.value=i.initializer?i.initializer.call(o):void 0,i.initializer=void 0),void 0===i.initializer&&(Object.defineProperty(e,t,i),i=null),i}function a(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t<e.length;t++)n[t]=e[t];return n}return Array.from(e)}function l(e){if(!e||!e.hasOwnProperty)return!1;for(var t=["value","initializer","get","set"],n=0,r=t.length;n<r;n++)if(e.hasOwnProperty(t[n]))return!0;return!1}function u(e,t){return l(t[t.length-1])?e.apply(void 0,a(t).concat([[]])):function(){return e.apply(void 0,a(Array.prototype.slice.call(arguments)).concat([t]))}}function c(e){return!1===e.hasOwnProperty(_)&&O(e,_,{value:new S}),e[_]}function s(e){var t={};return A(e).forEach(function(n){return t[n]=k(e,n)}),t}function f(e){return function(t){return Object.defineProperty(this,e,{configurable:!0,writable:!0,enumerable:!0,value:t}),t}}function d(e,t){return e.bind?e.bind(t):function(){return e.apply(t,arguments)}}function p(e){!0!==z[e]&&(z[e]=!0,P("DEPRECATION: "+e))}t.d=u,t.c=c,n.d(t,"g",function(){return A}),t.f=s,t.e=f,t.a=d,n.d(t,"b",function(){return P}),t.h=p;var h,b,g,y,m,v,w=n(20),x="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},O=Object.defineProperty,k=Object.getOwnPropertyDescriptor,j=Object.getOwnPropertyNames,E=Object.getOwnPropertySymbols,S=(h=function e(){o(this,e),r(this,"debounceTimeoutIds",b,this),r(this,"throttleTimeoutIds",g,this),r(this,"throttlePreviousTimestamps",y,this),r(this,"throttleTrailingArgs",m,this),r(this,"profileLastRan",v,this)},b=i(h.prototype,"debounceTimeoutIds",[w.a],{enumerable:!0,initializer:function(){return{}}}),g=i(h.prototype,"throttleTimeoutIds",[w.a],{enumerable:!0,initializer:function(){return{}}}),y=i(h.prototype,"throttlePreviousTimestamps",[w.a],{enumerable:!0,initializer:function(){return{}}}),m=i(h.prototype,"throttleTrailingArgs",[w.a],{enumerable:!0,initializer:function(){return null}}),v=i(h.prototype,"profileLastRan",[w.a],{enumerable:!0,initializer:function(){return null}}),h),_="function"==typeof Symbol?Symbol("__core_decorators__"):"__core_decorators__",A=E?function(e){return j(e).concat(E(e))}:j,P=function(){return"object"===("undefined"==typeof console?"undefined":x(console))&&console&&"function"==typeof console.warn?d(console.warn,console):function(){}}(),z={}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(37);n.d(t,"override",function(){return r.a});var o=n(30);n.d(t,"deprecate",function(){return o.a}),n.d(t,"deprecated",function(){return o.a});var i=n(40);n.d(t,"suppressWarnings",function(){return i.a});var a=n(33);n.d(t,"memoize",function(){return a.a});var l=n(27);n.d(t,"autobind",function(){return l.a});var u=n(39);n.d(t,"readonly",function(){return u.a});var c=n(31);n.d(t,"enumerable",function(){return c.a});var s=n(36);n.d(t,"nonenumerable",function(){return s.a});var f=n(35);n.d(t,"nonconfigurable",function(){return f.a});var d=n(28);n.d(t,"debounce",function(){return d.a});var p=n(41);n.d(t,"throttle",function(){return p.a});var h=n(29);n.d(t,"decorate",function(){return h.a});var b=n(34);n.d(t,"mixin",function(){return b.a}),n.d(t,"mixins",function(){return b.a});var g=n(20);n.d(t,"lazyInitialize",function(){return g.a});var y=n(42);n.d(t,"time",function(){return y.a});var m=n(32);n.d(t,"extendDescriptor",function(){return m.a});var v=n(38);n.d(t,"profile",function(){return v.a});var w=n(26);n.d(t,"applyDecorators",function(){return w.a})},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={defaultLanguage:"en-us","en-us":{pageMenu:[{text:"HOME",link:"/"},{text:"DOCS",link:"/docs/user/quick-start.md"},{text:"BLOG",link:"/blog"},{text:"COMMUNITY",link:"/community"}],disclaimer:{title:"Disclaimer",content:"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 process have stabilized in a manner consistent with other successful ASF projects. While incubation status is not necessarily a reflection of the completeness or stability of the code, it does indicate that the project has yet to be fully endorsed by the ASF."},documentation:{title:"Documentation",list:[{text:"Quick start",link:"/docs/user/quick-start.md"},{text:"Developer guide",link:"/docs/dev/build.md"},{text:"Admin manual",link:"/docs/admin/ops/dubbo-ops.md"}]},resources:{title:"Resources",list:[{text:"Blog",link:"/blog"},{text:"Community",link:"/community"}]},copyright:"Copyright © 2018 The Apache Software Foundation. Apache and the Apache feather logo are trademarks of The Apache Software Foundation."},"zh-cn":{pageMenu:[{text:"首页",link:"/"},{text:"文档",link:"/docs/user/quick-start.md"},{text:"博客",link:"/blog"},{text:"社区",link:"/community"}],disclaimer:{title:"Disclaimer",content:"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 process have stabilized in a manner consistent with other successful ASF projects. While incubation status is not necessarily a reflection of the completeness or stability of the code, it does indicate that the project has yet to be fully endorsed by the ASF."},documentation:{title:"文档",list:[{text:"快速开始",link:"/docs/user/quick-start.md"},{text:"开发者指南",link:"/docs/dev/build.md"},{text:"运维管理",link:"/docs/admin/ops/dubbo-ops.md"}]},resources:{title:"资源",list:[{text:"博客",link:"/blog"},{text:"社区",link:"/community"}]},copyright:"Copyright © 2018 The Apache Software Foundation. Apache and the Apache feather logo are trademarks of The Apache Software Foundation."}}},function(e,t,n){var r,o;!function(i){var a=!1;if(r=i,void 0!==(o="function"==typeof r?r.call(t,n,t,e):r)&&(e.exports=o),a=!0,e.exports=i(),a=!0,!a){var l=window.Cookies,u=window.Cookies=i();u.noConflict=function(){return window.Cookies=l,u}}}(function(){function e(){for(var e=0,t={};e<arguments.length;e++){var n=arguments[e];for(var r in n)t[r]=n[r]}return t}function t(n){function r(t,o,i){var a;if("undefined"!=typeof document){if(arguments.length>1){if(i=e({path:"/"},r.defaults,i),"number"==typeof i.expires){var l=new Date;l.setMilliseconds(l.getMilliseconds()+864e5*i.expires),i.expires=l}i.expires=i.expires?i.expires.toUTCString():"";try{a=JSON.stringify(o),/^[\{\[]/.test(a)&&(o=a)}catch(e){}o=n.write?n.write(o,t):encodeURIComponent(String(o)).replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g,decodeURIComponent),t=encodeURIComponent(String(t)),t=t.replace(/%(23|24|26|2B|5E|60|7C)/g,decodeURIComponent),t=t.replace(/[\(\)]/g,escape);var u="";for(var c in i)i[c]&&(u+="; "+c,!0!==i[c]&&(u+="="+i[c]));return document.cookie=t+"="+o+u}t||(a={});for(var s=document.cookie?document.cookie.split("; "):[],f=/(%[0-9A-Z]{2})+/g,d=0;d<s.length;d++){var p=s[d].split("="),h=p.slice(1).join("=");this.json||'"'!==h.charAt(0)||(h=h.slice(1,-1));try{var b=p[0].replace(f,decodeURIComponent);if(h=n.read?n.read(h,b):n(h,b)||h.replace(f,decodeURIComponent),this.json)try{h=JSON.parse(h)}catch(e){}if(t===b){a=h;break}t||(a[b]=h)}catch(e){}}return a}}return r.set=r,r.get=function(e){return r.call(r,e)},r.getJSON=function(){return r.apply({json:!0},[].slice.call(arguments))},r.defaults={},r.remove=function(t,n){r(t,"",e(n,{expires:-1}))},r.withConverter=t,r}return t(function(){})})},function(e,t,n){var r,o;/*!
+  Copyright (c) 2017 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";function r(e,t,r){var o=r.configurable,l=r.enumerable,u=r.initializer,c=r.value;return{configurable:o,enumerable:l,get:function(){if(this!==e){var n=u?u.call(this):c;return a(this,t,{configurable:o,enumerable:l,writable:!0,value:n}),n}},set:n.i(i.e)(t)}}function o(){for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];return n.i(i.d)(r,t)}t.a=o;var i=n(15),a=Object.defineProperty},function(e,t,n){"use strict";t.decode=t.parse=n(46),t.encode=t.stringify=n(47)},,function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var o=n(0),i=r(o),a=n(18),l=r(a),u=n(1),c=n(17),s=r(c);n(43);var f=function(e){var t=l.default.get("docsite_language")||s.default.defaultLanguage,n=s.default[t];return i.default.createElement("footer",{className:"footer-container"},i.default.createElement("div",{className:"footer-body"},i.default.createElement("img",{src:e.logo}),i.default.createElement("img",{className:"apache",src:"./img/apache_logo.png"}),i.default.createElement("div",{className:"cols-container"},i.default.createElement("div",{className:"col col-12"},i.default.createElement("h3",null,n.disclaimer.title),i.default.createElement("p",null,n.disclaimer.content)),i.default.createElement("div",{className:"col col-6"},i.default.createElement("dl",null,i.default.createElement("dt",null,n.documentation.title),n.documentation.list.map(function(e,t){return i.default.createElement("dd",{key:t},i.default.createElement(u.Link,{to:e.link},e.text))}))),i.default.createElement("div",{className:"col col-6"},i.default.createElement("dl",null,i.default.createElement("dt",null,n.resources.title),n.resources.list.map(function(e,t){return i.default.createElement("dd",{key:t},i.default.createElement(u.Link,{to:e.link},e.text))})))),i.default.createElement("div",{className:"copyright"},i.default.createElement("span",null,n.copyright))))};t.default=f},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function l(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function u(e,t,n,r,o){var i={};return Object.keys(r).forEach(function(e){i[e]=r[e]}),i.enumerable=!!i.enumerable,i.configurable=!!i.configurable,("value"in i||i.initializer)&&(i.writable=!0),i=n.slice().reverse().reduce(function(n,r){return r(e,t,n)||n},i),o&&void 0!==i.initializer&&(i.value=i.initializer?i.initializer.call(o):void 0,i.initializer=void 0),void 0===i.initializer&&(Object.defineProperty(e,t,i),i=null),i}Object.defineProperty(t,"__esModule",{value:!0});var c,s=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),f=n(0),d=r(f),p=n(1),h=n(19),b=r(h),g=n(16),y=n(17),m=r(y);n(44);var v=[{text:"中",value:"en-us"},{text:"En",value:"zh-cn"}],w=function(){},x={type:"primary",language:"en-us",onLanguageChange:w},O=(c=function(e){function t(e){i(this,t);var n=a(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e));return n.state={menuBodyVisible:!1,language:e.language},n}return l(t,e),s(t,[{key:"toggleMenu",value:function(){this.setState({menuBodyVisible:!this.state.menuBodyVisible})}},{key:"switchLang",value:function(){var e=void 0;e="zh-cn"===this.state.language?"en-us":"zh-cn",this.setState({language:e}),this.props.onLanguageChange(e)}},{key:"componentWillReceiveProps",value:function(e){this.setState({language:e.language})}},{key:"render",value:function(){var e=this.props,t=e.type,n=e.logo,r=e.onLanguageChange,i=this.state,a=i.menuBodyVisible,l=i.language;return d.default.createElement("header",{className:(0,b.default)(o({"header-container":!0},"header-container-"+t,!0))},d.default.createElement("div",{className:"header-body"},d.default.createElement(p.Link,{to:"/"},d.default.createElement("img",{className:"logo",alt:m.default.name,title:m.default.name,src:n})),r!==w?d.default.createElement("span",{className:(0,b.default)(o({"language-switch":!0},"language-switch-"+t,!0)),onClick:this.switchLang},v.find(function(e){return e.value===l}).text):null,d.default.createElement("div",{className:(0,b.default)({"header-menu":!0,"header-menu-open":a})},d.default.createElement("img",{className:"header-menu-toggle",onClick:this.toggleMenu,src:"primary"===t?"./img/menu_white.png":"./img/menu_gray.png"}),d.default.createElement("ul",null,m.default[l].pageMenu.map(function(e){var n;return d.default.createElement("li",{className:(0,b.default)((n={"menu-item":!0},o(n,"menu-item-"+t,!0),o(n,"menu-item-"+t+"-active",window.location.hash.split("?")[0].slice(1).split("/")[1]===e.link.split("/")[1]),n))},d.default.createElement(p.Link,{to:e.link},e.text))})))))}}]),t}(d.default.Component),u(c.prototype,"toggleMenu",[g.autobind],Object.getOwnPropertyDescriptor(c.prototype,"toggleMenu"),c.prototype),u(c.prototype,"switchLang",[g.autobind],Object.getOwnPropertyDescriptor(c.prototype,"switchLang"),c.prototype),c);O.defaultProps=x,t.default=O},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function a(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var l,u=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),c=n(0),s=r(c),f=n(16),d=n(18),p=r(d),h=n(21),b=r(h),g=(l=function(e){function t(){return o(this,t),i(this,(t.__proto__||Object.getPrototypeOf(t)).apply(this,arguments))}return a(t,e),u(t,[{key:"onLanguageChange",value:function(e){this.props.location;p.default.set("docsite_language",e,{expires:365,path:""});var t=window.location.hash.split("?");if(t&&t.length){var n=b.default.parse(t[1]||"");n.lang=e,window.location.hash=(t[0]||"")+"?"+b.default.stringify(n)}this.forceUpdate()}}]),t}(s.default.Component),function(e,t,n,r,o){var i={};return Object.keys(r).forEach(function(e){i[e]=r[e]}),i.enumerable=!!i.enumerable,i.configurable=!!i.configurable,("value"in i||i.initializer)&&(i.writable=!0),i=n.slice().reverse().reduce(function(n,r){return r(e,t,n)||n},i),o&&void 0!==i.initializer&&(i.value=i.initializer?i.initializer.call(o):void 0,i.initializer=void 0),void 0===i.initializer&&(Object.defineProperty(e,t,i),i=null),i}(l.prototype,"onLanguageChange",[f.autobind],Object.getOwnPropertyDescriptor(l.prototype,"onLanguageChange"),l.prototype),l);t.default=g},function(e,t,n){"use strict";function r(e,t){var n=e.prototype;for(var r in t)for(var a=t[r],l=0,u=a.length;l<u;l++){var c=a[l];o(n,r,c(n,r,i(n,r)))}return e}t.a=r;var o=Object.defineProperty,i=Object.getOwnPropertyDescriptor},function(e,t,n){"use strict";function r(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t<e.length;t++)n[t]=e[t];return n}return Array.from(e)}function o(e,t){if("undefined"==typeof WeakMap)throw new Error("Using @autobind on "+t.name+"() requires WeakMap support due to its use of super."+t.name+"()\n      See https://github.com/jayphelps/core-decorators.js/issues/20");d||(d=new WeakMap),!1===d.has(e)&&d.set(e,new WeakMap);var r=d.get(e);return!1===r.has(t)&&r.set(t,n.i(c.a)(t,e)),r.get(t)}function i(e){for(var t=n.i(c.f)(e.prototype),r=n.i(c.g)(t),o=0,i=r.length;o<i;o++){var l=r[o],u=t[l];"function"==typeof u.value&&"constructor"!==l&&s(e.prototype,l,a(e.prototype,l,u))}}function a(e,t,r){var i=r.value,a=r.configurable,l=r.enumerable;if("function"!=typeof i)throw new SyntaxError("@autobind can only be used on functions, not: "+i);var u=e.constructor;return{configurable:a,enumerable:l,get:function(){if(this===e)return i;if(this.constructor!==u&&f(this).constructor===u)return i;if(this.constructor!==u&&t in this.constructor.prototype)return o(this,i);var r=n.i(c.a)(i,this);return s(this,t,{configurable:!0,writable:!0,enumerable:!1,value:r}),r},set:n.i(c.e)(t)}}function l(e){return 1===e.length?i.apply(void 0,r(e)):a.apply(void 0,r(e))}function u(){for(var e=arguments.length,t=Array(e),n=0;n<e;n++)t[n]=arguments[n];return 0===t.length?function(){return l(arguments)}:l(t)}t.a=u;var c=n(15),s=Object.defineProperty,f=Object.getPrototypeOf,d=void 0},function(e,t,n){"use strict";function r(e,t,r,o){var c=l(o,2),s=c[0],f=void 0===s?u:s,d=c[1],p=void 0!==d&&d,h=r.value;if("function"!=typeof h)throw new SyntaxError("Only functions can be debounced");return a({},r,{value:function(){var e=this,r=n.i(i.c)(this),o=r.debounceTimeoutIds,a=o[t],l=p&&!a,u=arguments;clearTimeout(a),o[t]=setTimeout(function(){delete o[t],p||h.apply(e,u)},f),l&&h.apply(this,u)}})}function o(){n.i(i.h)("@debounce is deprecated and will be removed shortly. Use @debounce from lodash-decorators.\n\n  https://www.npmjs.com/package/lodash-decorators");for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];return n.i(i.d)(r,t)}t.a=o;var i=n(15),a=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},l=function(){function e(e,t){var n=[],r=!0,o=!1,i=void 0;try{for(var a,l=e[Symbol.iterator]();!(r=(a=l.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(e){o=!0,i=e}finally{try{!r&&l.return&&l.return()}finally{if(o)throw i}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),u=300},function(e,t,n){"use strict";function r(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t<e.length;t++)n[t]=e[t];return n}return Array.from(e)}function o(e){return Array.isArray(e)?e:Array.from(e)}function i(e,t,i,a){var c=o(a),s=c[0],f=c.slice(1),d=i.configurable,p=i.enumerable,h=i.writable,b=i.get,g=i.set,y=i.value,m=!!b;return{configurable:d,enumerable:p,get:function(){var e=m?b.call(this):y,n=s.call.apply(s,[this,e].concat(r(f)));if(m)return n;var o={configurable:d,enumerable:p};return o.value=n,o.writable=h,u(this,t,o),n},set:m?g:n.i(l.e)()}}function a(){for(var e=arguments.length,t=Array(e),r=0;r<e;r++)t[r]=arguments[r];return n.i(l.d)(i,t)}t.a=a;var l=n(15),u=Object.defineProperty},function(e,t,n){"use strict";function r(e,t,r,o){var c=l(o,2),s=c[0],f=void 0===s?u:s,d=c[1],p=void 0===d?{}:d;if("function"!=typeof r.value)throw new SyntaxError("Only functions can be marked as deprecated");var h=e.constructor.name+"#"+t;return p.url&&(f+="\n\n    See "+p.url+" for more details.\n\n"),a({},r,{value:function(){return n.i(i.b)("DEPRECATION "+h+": "+f),r.value.apply(this,arguments)}})}function o(){for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];return n.i(i.d)(r,t)}t.a=o;var i=n(15),a=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},l=function(){function e(e,t){var n=[],r=!0,o=!1,i=void 0;try{for(var a,l=e[Symbol.iterator]();!(r=(a=l.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(e){o=!0,i=e}finally{try{!r&&l.return&&l.return()}finally{if(o)throw i}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),u="This function will be removed in future versions."},function(e,t,n){"use strict";function r(e,t,n){return n.enumerable=!0,n}function o(){for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];return n.i(i.d)(r,t)}t.a=o;var i=n(15)},function(e,t,n){"use strict";function r(e,t,n){var r=l(e),o=u(r,t);return a({},o,{value:n.value,initializer:n.initializer,get:n.get||o.get,set:n.set||o.set})}function o(){for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];return n.i(i.d)(r,t)}t.a=o;var i=n(15),a=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},l=Object.getPrototypeOf,u=Object.getOwnPropertyDescriptor},function(e,t,n){"use strict";function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){return t===Object(t)?t:e[t]||(e[t]={})}function i(e,t,n,r,o){var i=t.apply(e,n);return r[o]=i,i}function a(e){var t=void 0,n=void 0;return e.value?(t=e.value,n="value"):e.get?(t=e.get,n="get"):e.set&&(t=e.set,n="set"),{fn:t,wrapKey:n}}function l(e,t,n){var l=a(n),u=l.fn,c=l.wrapKey,f=new WeakMap,d=Object.create(null),p=Object.create(null),h=0;return s({},n,r({},c,function(){for(var e=arguments.length,t=Array(e),n=0;n<e;n++)t[n]=arguments[n];for(var r="0",a=0,l=t.length;a<l;a++){var c=t[a],s=o(p,c),b=f.get(s);void 0===b&&(b=++h,f.set(s,b)),r+=b}return d[r]||i(this,u,arguments,d,r)}))}function u(){n.i(c.h)("@memoize is deprecated and will be removed shortly. Use @memoize from lodash-decorators.\n\n  https://www.npmjs.com/package/lodash-decorators");for(var e=arguments.length,t=Array(e),r=0;r<e;r++)t[r]=arguments[r];return n.i(c.d)(l,t)}t.a=u;var c=n(15),s=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e}},function(e,t,n){"use strict";function r(e){return"[object Symbol]"===Object.prototype.toString.call(e)&&"object"===(void 0===e?"undefined":u(e))}function o(e,t){if(r(e)){do{if(t===Object.prototype)return void 0!==t[e];if(t.hasOwnProperty(e))return!0}while(t=s(t));return!1}return e in t}function i(e,t){if(!t.length)throw new SyntaxError("@mixin() class "+e.name+" requires at least one mixin as an argument");for(var r=0,i=t.length;r<i;r++)for(var a=n.i(l.f)(t[r]),u=n.i(l.g)(a),s=0,f=u.length;s<f;s++){var d=u[s];o(d,e.prototype)||c(e.prototype,d,a[d])}}function a(){for(var e=arguments.length,t=Array(e),r=0;r<e;r++)t[r]=arguments[r];return n.i(l.h)("@mixin is deprecated and will be removed shortly. Use @mixin from lodash-decorators.\n\n  https://www.npmjs.com/package/lodash-decorators"),"function"==typeof t[0]?i(t[0],[]):function(e){return i(e,t)}}t.a=a;var l=n(15),u="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},c=Object.defineProperty,s=Object.getPrototypeOf},function(e,t,n){"use strict";function r(e,t,n){return n.configurable=!1,n}function o(){for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];return n.i(i.d)(r,t)}t.a=o;var i=n(15)},function(e,t,n){"use strict";function r(e,t,n){return n.enumerable=!1,n}function o(){for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];return n.i(i.d)(r,t)}t.a=o;var i=n(15)},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(e){return e.hasOwnProperty("value")?"data":e.hasOwnProperty("get")||e.hasOwnProperty("set")?"accessor":"data"}function i(e,t,n){n.assert(e.length===t.length)}function a(e,t,n){var r=p(e.value),o=p(t.value);if("undefined"===r&&"undefined"===o&&n.error("descriptor values are both undefined. (class properties are are not currently supported)'"),r!==o){("function"===o&&void 0===r||void 0!==r)&&n.error('value types do not match. {parent} is "'+r+'", {child} is "'+o+'"')}switch(o){case"function":i(e.value,t.value,n);break;default:n.error('Unexpected error. Please file a bug with: {parent} is "'+r+'", {child} is "'+o+'"')}}function l(e,t,n){var r="function"==typeof e.get,o="function"==typeof t.get,a="function"==typeof e.set,l="function"==typeof t.set;(r||o)&&(!r&&a&&n.error("{parent} is setter but {child} is getter"),!o&&l&&n.error("{parent} is getter but {child} is setter"),i(e.get,t.get,n)),(a||l)&&(!a&&r&&n.error("{parent} is getter but {child} is setter"),!l&&o&&n.error("{parent} is setter but {child} is getter"),i(e.set,t.set,n))}function u(e,t,n){var r=o(e),i=o(t);switch(r!==i&&n.error('descriptor types do not match. {parent} is "'+r+'", {child} is "'+i+'"'),i){case"data":a(e,t,n);break;case"accessor":l(e,t,n)}}function c(e,t){for(var n=0,r=y.length;n<r;n++){var o=y[n],i=o(t);if(i in e)return i}return null}function s(e,t,n){n.key=t;var r=Object.getPrototypeOf(e),o=Object.getOwnPropertyDescriptor(r,t),i=new g(r,e,o,n);if(void 0===o){var a=c(r,t),l=a?'\n\n  Did you mean "'+a+'"?':"";i.error("No descriptor matching {child} was found on the prototype chain."+l)}return u(o,n,i),n}function f(){for(var e=arguments.length,t=Array(e),r=0;r<e;r++)t[r]=arguments[r];return n.i(d.d)(s,t)}t.a=f;var d=n(15),p="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},h=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),b=/^function ([_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*)?(\([^\)]*\))[\s\S]+$/,g=function(){function e(t,n,o,i){r(this,e),this.parentKlass=t,this.childKlass=n,this.parentDescriptor=o,this.childDescriptor=i}return h(e,[{key:"_getTopic",value:function(e){return void 0===e?null:"value"in e?e.value:"get"in e?e.get:"set"in e?e.set:void 0}},{key:"_extractTopicSignature",value:function(e){switch(void 0===e?"undefined":p(e)){case"function":return this._extractFunctionSignature(e);default:return this.key}}},{key:"_extractFunctionSignature",value:function(e){var t=this;return e.toString().replace(b,function(e){return(arguments.length>1&&void 0!==arguments[1]?arguments[1]:t.key)+arguments[2]})}},{key:"key",get:function(){return this.childDescriptor.key}},{key:"parentNotation",get:function(){return this.parentKlass.constructor.name+"#"+this.parentPropertySignature}},{key:"childNotation",get:function(){return this.childKlass.constructor.name+"#"+this.childPropertySignature}},{key:"parentTopic",get:function(){return this._getTopic(this.parentDescriptor)}},{key:"childTopic",get:function(){return this._getTopic(this.childDescriptor)}},{key:"parentPropertySignature",get:function(){return this._extractTopicSignature(this.parentTopic)}},{key:"childPropertySignature",get:function(){return this._extractTopicSignature(this.childTopic)}}]),h(e,[{key:"assert",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";!0!==e&&this.error("{child} does not properly override {parent}"+t)}},{key:"error",value:function(e){var t=this;throw e=e.replace("{parent}",function(e){return t.parentNotation}).replace("{child}",function(e){return t.childNotation}),new SyntaxError(e)}}]),e}(),y=[function(e){return e.toLowerCase()},function(e){return e.toUpperCase()},function(e){return e+"s"},function(e){return e.slice(0,-1)},function(e){return e.slice(1,e.length)}]},function(e,t,n){"use strict";function r(e,t,r,c){var s=l(c,3),f=s[0],d=void 0===f?null:f,p=s[1],h=void 0!==p&&p,b=s[2],g=void 0===b?u:b;if(!o.__enabled)return o.__warned||(g.warn("console.profile is not supported. All @profile decorators are disabled."),o.__warned=!0),r;var y=r.value;if(null===d&&(d=e.constructor.name+"."+t),"function"!=typeof y)throw new SyntaxError("@profile can only be used on functions, not: "+y);return a({},r,{value:function(){var e=Date.now(),t=n.i(i.c)(this);(!0===h&&!t.profileLastRan||!1===h||"number"==typeof h&&e-t.profileLastRan>h||"function"==typeof h&&h.apply(this,arguments))&&(g.profile(d),t.profileLastRan=e);try{return y.apply(this,arguments)}finally{g.profileEnd(d)}}})}function o(){for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];return n.i(i.d)(r,t)}t.a=o;var i=n(15),a=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},l=function(){function e(e,t){var n=[],r=!0,o=!1,i=void 0;try{for(var a,l=e[Symbol.iterator]();!(r=(a=l.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(e){o=!0,i=e}finally{try{!r&&l.return&&l.return()}finally{if(o)throw i}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),u=(console,{profile:console.profile?n.i(i.a)(console.profile,console):function(){},profileEnd:console.profileEnd?n.i(i.a)(console.profileEnd,console):function(){},warn:i.b});o.__enabled=!!console.profile,o.__warned=!1},function(e,t,n){"use strict";function r(e,t,n){return n.writable=!1,n}function o(){for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];return n.i(i.d)(r,t)}t.a=o;var i=n(15)},function(e,t,n){"use strict";function r(){}function o(e,t,n){if("object"===("undefined"==typeof console?"undefined":c(console))){var o=console.warn;console.warn=r;var i=t.apply(e,n);return console.warn=o,i}return t.apply(e,n)}function i(e,t,n){return u({},n,{value:function(){return o(this,n.value,arguments)}})}function a(){for(var e=arguments.length,t=Array(e),r=0;r<e;r++)t[r]=arguments[r];return n.i(l.d)(i,t)}t.a=a;var l=n(15),u=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},c="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e}},function(e,t,n){"use strict";function r(e,t,r,o){var c=l(o,2),s=c[0],f=void 0===s?u:s,d=c[1],p=void 0===d?{}:d,h=r.value;if("function"!=typeof h)throw new SyntaxError("Only functions can be throttled");return!1!==p.leading&&(p.leading=!0),!1!==p.trailing&&(p.trailing=!0),a({},r,{value:function(){var e=this,r=n.i(i.c)(this),o=r.throttleTimeoutIds,a=r.throttlePreviousTimestamps,l=o[t],u=a[t]||0,c=Date.now();p.trailing&&(r.throttleTrailingArgs=arguments),u||!1!==p.leading||(u=c);var s=f-(c-u);s<=0?(clearTimeout(l),delete o[t],a[t]=c,h.apply(this,arguments)):!l&&p.trailing&&(o[t]=setTimeout(function(){a[t]=!1===p.leading?0:Date.now(),delete o[t],h.apply(e,r.throttleTrailingArgs),r.throttleTrailingArgs=null},s))}})}function o(){n.i(i.h)("@throttle is deprecated and will be removed shortly. Use @throttle from lodash-decorators.\n\n  https://www.npmjs.com/package/lodash-decorators");for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];return n.i(i.d)(r,t)}t.a=o;var i=n(15),a=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},l=function(){function e(e,t){var n=[],r=!0,o=!1,i=void 0;try{for(var a,l=e[Symbol.iterator]();!(r=(a=l.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(e){o=!0,i=e}finally{try{!r&&l.return&&l.return()}finally{if(o)throw i}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),u=300},function(e,t,n){"use strict";function r(e,t,n,r){var o=l(r,2),i=o[0],u=void 0===i?null:i,f=o[1],d=void 0===f?c:f,p=n.value;if(null===u&&(u=e.constructor.name+"."+t),"function"!=typeof p)throw new SyntaxError("@time can only be used on functions, not: "+p);return a({},n,{value:function(){var e=u+"-"+s;s++,d.time(e);try{return p.apply(this,arguments)}finally{d.timeEnd(e)}}})}function o(){for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];return n.i(i.d)(r,t)}t.a=o;var i=n(15),a=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},l=function(){function e(e,t){var n=[],r=!0,o=!1,i=void 0;try{for(var a,l=e[Symbol.iterator]();!(r=(a=l.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(e){o=!0,i=e}finally{try{!r&&l.return&&l.return()}finally{if(o)throw i}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),u={},c={time:console.time?console.time.bind(console):function(e){u[e]=new Date},timeEnd:console.timeEnd?console.timeEnd.bind(console):function(e){var t=new Date,n=t-u[e];delete u[e],console.log(e+": "+n+"ms")}},s=0},function(e,t,n){n(14)(n(48))},function(e,t,n){n(14)(n(49))},,function(e,t,n){"use strict";function r(e,t){return Object.prototype.hasOwnProperty.call(e,t)}e.exports=function(e,t,n,i){t=t||"&",n=n||"=";var a={};if("string"!=typeof e||0===e.length)return a;var l=/\+/g;e=e.split(t);var u=1e3;i&&"number"==typeof i.maxKeys&&(u=i.maxKeys);var c=e.length;u>0&&c>u&&(c=u);for(var s=0;s<c;++s){var f,d,p,h,b=e[s].replace(l,"%20"),g=b.indexOf(n);g>=0?(f=b.substr(0,g),d=b.substr(g+1)):(f=b,d=""),p=decodeURIComponent(f),h=decodeURIComponent(d),r(a,p)?o(a[p])?a[p].push(h):a[p]=[a[p],h]:a[p]=h}return a};var o=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)}},function(e,t,n){"use strict";function r(e,t){if(e.map)return e.map(t);for(var n=[],r=0;r<e.length;r++)n.push(t(e[r],r));return n}var o=function(e){switch(typeof e){case"string":return e;case"boolean":return e?"true":"false";case"number":return isFinite(e)?e:"";default:return""}};e.exports=function(e,t,n,l){return t=t||"&",n=n||"=",null===e&&(e=void 0),"object"==typeof e?r(a(e),function(a){var l=encodeURIComponent(o(a))+n;return i(e[a])?r(e[a],function(e){return l+encodeURIComponent(o(e))}).join(t):l+encodeURIComponent(o(e[a]))}).join(t):l?encodeURIComponent(o(l))+n+encodeURIComponent(o(e)):""};var i=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)},a=Object.keys||function(e){var t=[];for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&t.push(n);return t}},function(e,t){e.exports=".footer-container {\n  background: #F8F8F8; }\n  .footer-container .footer-body {\n    max-width: 1280px;\n    margin: 0 auto;\n    box-sizing: border-box;\n    padding: 40px 40px 0; }\n    @media screen and (max-width: 640px) {\n      .footer-container .footer-body {\n        padding-left: 20px;\n        padding-right: 20px; } }\n    .footer-container .footer-body img {\n      width: 125px;\n      height: 26px;\n      margin-bottom: 28px;\n      margin-right: 20px;\n      vertical-align: middle; }\n    .footer-container .footer-body .apache {\n      width: 50px;\n      height: 50px; }\n    .footer-container .footer-body .cols-container .col {\n      display: inline-block;\n      box-sizing: border-box;\n      vertical-align: top; }\n    .footer-container .footer-body .cols-container .col-12 {\n      width: 50%;\n      padding-right: 125px; }\n    .footer-container .footer-body .cols-container .col-6 {\n      width: 25%; }\n    .footer-container .footer-body .cols-container h3 {\n      font-family: Avenir-Heavy;\n      font-size: 18px;\n      color: #333;\n      line-height: 18px; }\n    .footer-container .footer-body .cols-container p {\n      font-family: Avenir-Medium;\n      font-size: 12px;\n      color: #999;\n      line-height: 18px; }\n    .footer-container .footer-body .cols-container dl {\n      font-family: Avenir-Heavy;\n      line-height: 18px; }\n    .footer-container .footer-body .cols-container dt {\n      font-weight: bold;\n      font-size: 18px;\n      color: #333;\n      margin-bottom: 20px; }\n    .footer-container .footer-body .cols-container dd {\n      padding: 0;\n      margin: 0; }\n      .footer-container .footer-body .cols-container dd a {\n        text-decoration: none;\n        display: block;\n        font-size: 14px;\n        color: #999;\n        margin: 10px 0; }\n      .footer-container .footer-body .cols-container dd a:hover {\n        color: #2DACEC; }\n    .footer-container .footer-body .copyright {\n      border-top: 1px solid #ccc;\n      min-height: 60px;\n      line-height: 20px;\n      text-align: center;\n      font-family: Avenir-Medium;\n      font-size: 12px;\n      color: #999;\n      display: flex;\n      align-items: center; }\n      .footer-container .footer-body .copyright span {\n        display: inline-block;\n        margin: 0 auto; }\n\n@media screen and (max-width: 640px) {\n  .footer-container .footer-body .cols-container .col {\n    width: 100%;\n    text-align: center;\n    padding: 0; } }\n"},function(e,t){e.exports=".header-container {\n  position: fixed;\n  left: 0;\n  top: 0;\n  width: 100%;\n  z-index: 1000;\n  background-color: #fff; }\n  .header-container-primary {\n    background-color: transparent; }\n  .header-container-normal {\n    background-color: #fff;\n    box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.08); }\n  .header-container .header-body {\n    max-width: 1280px;\n    margin: 0 auto;\n    height: 66px;\n    line-height: 66px; }\n    .header-container .header-body .logo {\n      margin-left: 40px;\n      width: 96px;\n      vertical-align: sub; }\n    .header-container .header-body .header-menu {\n      float: right; }\n      .header-container .header-body .header-menu .header-menu-toggle {\n        display: none;\n        width: 19px;\n        margin-right: 40px;\n        margin-top: 18px;\n        cursor: pointer; }\n    .header-container .header-body ul {\n      padding: 0;\n      margin: 0; }\n    .header-container .header-body li {\n      display: inline-block;\n      margin-right: 40px; }\n    .header-container .header-body .menu-item {\n      font-family: Avenir-Heavy;\n      font-size: 14px; }\n    .header-container .header-body .menu-item-primary a {\n      color: #fff;\n      opacity: 0.6;\n      font-family: Avenir-Medium; }\n    .header-container .header-body .menu-item-primary:hover a {\n      opacity: 1; }\n    .header-container .header-body .menu-item-primary-active a {\n      opacity: 1; }\n    .header-container .header-body .menu-item-normal a {\n      color: #333;\n      opacity: 0.6;\n      font-family: Avenir-Medium; }\n    .header-container .header-body .menu-item-normal:hover a {\n      opacity: 1; }\n    .header-container .header-body .menu-item-normal-active a {\n      opacity: 1; }\n    .header-container .header-body .language-switch {\n      float: right;\n      display: inline-block;\n      box-sizing: border-box;\n      width: 24px;\n      height: 24px;\n      line-height: 20px;\n      margin-top: 21px;\n      margin-right: 40px;\n      text-align: center;\n      border-radius: 2px;\n      cursor: pointer;\n      font-family: PingFangSC-Medium;\n      font-size: 14px;\n      opacity: 0.6; }\n      .header-container .header-body .language-switch:hover {\n        opacity: 1; }\n    .header-container .header-body .language-switch-primary {\n      border: 1px solid #FFF;\n      color: #FFF; }\n    .header-container .header-body .language-switch-normal {\n      border: 1px solid #333;\n      color: #333; }\n\n@media screen and (max-width: 640px) {\n  .header-container .header-body .logo {\n    margin-left: 20px; }\n  .header-container .header-body .language-switch {\n    margin-right: 20px; }\n  .header-container .header-body .header-menu ul {\n    display: none; }\n  .header-container .header-body .header-menu .header-menu-toggle {\n    display: inline-block;\n    margin-right: 20px; }\n  .header-container .header-body .header-menu-open ul {\n    background-color: #f8f8f8;\n    display: inline-block;\n    position: absolute;\n    right: 0;\n    top: 66px;\n    z-index: 100; }\n  .header-container .header-body .header-menu-open li {\n    width: 200px;\n    display: list-item;\n    padding-left: 30px;\n    list-style: none;\n    line-height: 40px;\n    margin-right: 0; }\n    .header-container .header-body .header-menu-open li a {\n      color: #333;\n      display: inline-block;\n      width: 100%; }\n    .header-container .header-body .header-menu-open li:hover {\n      background: #8755FF; }\n      .header-container .header-body .header-menu-open li:hover a {\n        color: #fff;\n        opactiy: 1; }\n  .header-container .header-body .header-menu-open .menu-item-primary-active, .header-container .header-body .header-menu-open .menu-item-normal-active {\n    background: #8755FF; }\n    .header-container .header-body .header-menu-open .menu-item-primary-active a, .header-container .header-body .header-menu-open .menu-item-normal-active a {\n      color: #fff;\n      opactiy: 1; } }\n"},,,,,,function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e){var t=e.text,n=e.img,r=(0,u.default)({bar:!0});return a.default.createElement("div",{className:r},a.default.createElement("div",{className:"bar-body"},a.default.createElement("img",{src:n,className:"front-img"}),a.default.createElement("span",null,t),a.default.createElement("img",{src:n,className:"back-img"})))}Object.defineProperty(t,"__esModule",{value:!0}),t.default=o;var i=n(0),a=r(i),l=n(19),u=r(l);n(57)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});t.throttle=function(e,t){var n=null;return function(){for(var r=arguments.length,o=Array(r),i=0;i<r;i++)o[i]=arguments[i];var a=this;clearTimeout(n),n=setTimeout(function(){e.apply(a,o)},t)}},t.getScrollTop=function(){var e=0;return document.documentElement&&document.documentElement.scrollTop?e=document.documentElement.scrollTop:document.body&&(e=document.body.scrollTop),e}},function(e,t,n){n(14)(n(58))},function(e,t){e.exports=".bar {\n  margin-top: 66px;\n  background-image: linear-gradient(-90deg, #03DDE4 0%, #30AFED 51%, #8755FF 100%); }\n  .bar .bar-body {\n    max-width: 1280px;\n    margin: 0 auto;\n    height: 200px;\n    line-height: 200px;\n    font-family: Avenir-Heavy;\n    font-size: 36px;\n    color: #FFF;\n    position: relative; }\n    .bar .bar-body::before {\n      content: '';\n      height: 100%;\n      position: absolute;\n      left: 42px;\n      top: 0;\n      opacity: 0.3;\n      border-left: 1px solid #FFFFFF; }\n    .bar .bar-body::after {\n      content: '';\n      height: 16px;\n      position: absolute;\n      left: 40px;\n      top: 50%;\n      margin: auto 0;\n      border-left: 4px solid #FFFFFF; }\n    .bar .bar-body .front-img {\n      width: 80px;\n      height: 80px;\n      vertical-align: middle;\n      margin: 0 28px 0 70px; }\n    .bar .bar-body .back-img {\n      width: 160px;\n      height: 160px;\n      position: absolute;\n      right: 168px;\n      bottom: 0;\n      opacity: 0.15; }\n    @media screen and (max-width: 640px) {\n      .bar .bar-body::before {\n        left: 22px; }\n      .bar .bar-body::after {\n        left: 20px; }\n      .bar .bar-body .front-img {\n        margin-left: 50px; } }\n"},,,,,,,,,,,,,,,,,,,function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={"en-us":{barText:"Blog",postsTitle:"All posts",list:[{title:"The first Dubbo meetup has been held in Beijing",author:"Huxing Zhang",dateStr:"May 12nd,2018",desc:"The first Dubbo meetup has successfully been held in Beijing, over 400+ people were present. What a great event! ",link:"/blog/dubbo-meetup-beijing-may-12th-2018.md"},{title:"The ApacheCon NA schedule has been announced",author:"Huxing Zhang",dateStr:"May 2nd,2018",desc:'Ian Luo and Jun Liu will talk about "Introducing Apache Dubbo(Incubating): What is Dubbo and How it Works" at ApacheCon NA this year in Montréal!',link:"/blog/apachecon-na-2018.md"},{title:"The GSoC (Google Summer of Code) 2018 projects has been announced",author:"Huxing Zhang",dateStr:"April 25th,2018",desc:'Raghu Reddy\'s project "Extending Serialization protocols support for Apache Dubbo" has been accepted! Congratulations!',link:"/blog/gsoc-2018.md"},{title:"Dubbo roadmap is announced in QCon Beijing 2018",author:"Huxing Zhang",dateStr:"April 22nd,2018",desc:"Ian Luo has delivered a great talk at QCon Beijing 2018, where the roadmap of Dubbo has also be announced",link:"/blog/qcon-beijing-2018.md"}]},"zh-cn":{barText:"博客",postsTitle:"所有文章",list:[{title:"Dubbo 的同步与异步调用方式",author:"@Jerrick Zhu",dateStr:"July 10th, 2018",desc:"主要讲述了 Dubbo 在底层异步通信机制的基础上实现的同步调用、异步调用、参数回调以及事件通知几种方式及示例。",link:"/blog/dubbo-invoke.md"},{title:"第一个 Dubbo 应用",author:"@beiwei30",dateStr:"June 2nd, 2018",desc:"现代的分布式服务框架的基本概念与 RMI 是类似的,同样是使用 Java 的 Interface 作为服务契约,通过注册中心来完成服务的注册和发现,远程通讯的细节也是通过代理类来屏蔽。",link:"/blog/dubbo-101.md"},{title:"Dubbo基本用法-Dubbo Provider配置",author:"@cvictory",dateStr:"June 1st, 2018",desc:"主要讲述如何配置dubbo,按照配置方式上分,可以分为:XML配置,properties方式配置,注解方式配置,API调用方式配置。",link:"/blog/dubbo-basic-usage-dubbo-provider-configuration.md"},{title:"Spring Boot+Dubbo应用启停源码分析",author:"Huxing Zhang",dateStr:"May 28th, 2018",desc:"dubbo-spring-boot-project致力于简化 Dubbo RPC 框架在Spring Boot应用场景的开发,同时也整合了Spring Boot特性。",link:"/blog/spring-boot-dubbo-start-stop-analysis.md"},{title:"优化技巧:提前if判断帮助CPU分支预测",author:"@hengyunabc",dateStr:"May 20th, 2018",desc:"要提高代码执行效率,一个重要的原则就是尽量避免CPU把流水线清空,那么提高分支预测的成功率就非常重要。那么对于代码里,如果某个switch分支概率很高,是否可以考虑代码层面帮CPU把判断提前,来提高代码执行效率呢?",link:"/blog/optimization-branch-prediction.md"},{title:"Dubbo可扩展机制实战",author:"@vangoleo",dateStr:"May 10th, 2018",desc:"在谈到软件设计时,可扩展性一直被谈起,那到底什么才是可扩展性,什么样的框架才算有良好的可扩展性呢?",link:"/blog/introduction-to-dubbo-spi.md"},{title:"Dubbo可扩展机制源码解析",author:"@hengyunabc",dateStr:"May 10th, 2018",desc:"在前面的博客中,我们了解了Dubbo扩展机制的一些概念,初探了Dubbo中LoadBalance的实现,并自己实现了一个LoadBalance。接下来,我们就深入Dubbo的源码,一睹庐山真面目。",link:"/blog/introduction-to-dubbo-spi-2.md"}]}}},,,,function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function a(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function l(e,t,n,r,o){var i={};return Object.keys(r).forEach(function(e){i[e]=r[e]}),i.enumerable=!!i.enumerable,i.configurable=!!i.configurable,("value"in i||i.initializer)&&(i.writable=!0),i=n.slice().reverse().reduce(function(n,r){return r(e,t,n)||n},i),o&&void 0!==i.initializer&&(i.value=i.initializer?i.initializer.call(o):void 0,i.initializer=void 0),void 0===i.initializer&&(Object.defineProperty(e,t,i),i=null),i}Object.defineProperty(t,"__esModule",{value:!0});var u,c=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),s=n(0),f=r(s),d=n(16),p=n(19),h=r(p),b=n(56);n(91);var g={pageSize:5},y=(u=function(e){function t(e){o(this,t);var n=i(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e));return n.container=null,n.state={page:0,pageWidth:0},n}return a(t,e),c(t,[{key:"componentDidMount",value:function(){var e=this,t=this.container.getBoundingClientRect().width;this.setState({pageWidth:t}),this.throttleAdjust=(0,b.throttle)(function(){e.setState({pageWidth:e.container.getBoundingClientRect().width})},200),window.addEventListener("resize",this.throttleAdjust)}},{key:"componentWillUnmount",value:function(){window.removeEventListener("resize",this.throttleAdjust)}},{key:"changePage",value:function(e){this.setState({page:e})}},{key:"renderSliderList",value:function(){for(var e=this.props,t=e.children,n=e.pageSize,r=this.state,o=r.page,i=r.pageWidth,a=[],l=f.default.Children.count(t),u=Math.ceil(l/n),c=0;c<u;c++)a.push(Array.from(t).slice(c*n,(c+1)*n));return f.default.createElement("div",{className:"slider-list",style:{transform:"translateX(-"+o*i+"px)",transition:"transform 500ms ease",width:u*i}},a.map(function(e,t){return f.default.createElement("div",{className:"slider-page",style:{width:i},key:t},e.map(function(e,t){return f.default.createElement("div",{className:"slider-item",key:t},e)}))}))}},{key:"renderControl",value:function(){var e=this.props,t=e.children,n=e.pageSize,r=this.state.page,o=f.default.Children.count(t),i=Math.ceil(o/n);return f.default.createElement("div",{className:"slider-control"},f.default.createElement("img",{className:(0,h.default)({"slider-control-prev":!0,"slider-control-prev-hidden":0===r}),src:"./img/prev.png",onClick:this.changePage.bind(this,r-1)}),f.default.createElement("img",{className:(0,h.default)({"slider-control-next":!0,"slider-control-next-hidden":r===i-1}),src:"./img/next.png",onClick:this.changePage.bind(this,r+1)}))}},{key:"render",value:function(){var e=this;return f.default.createElement("div",{className:"page-slider",ref:function(t){e.container=t}},this.renderSliderList(),this.renderControl())}}]),t}(f.default.Component),l(u.prototype,"renderSliderList",[d.autobind],Object.getOwnPropertyDescriptor(u.prototype,"renderSliderList"),u.prototype),l(u.prototype,"renderControl",[d.autobind],Object.getOwnPropertyDescriptor(u.prototype,"renderControl"),u.prototype),u);y.defaultProps=g,t.default=y},,,,function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function i(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function a(e,t,n,r,o){var i={};return Object.keys(r).forEach(function(e){i[e]=r[e]}),i.enumerable=!!i.enumerable,i.configurable=!!i.configurable,("value"in i||i.initializer)&&(i.writable=!0),i=n.slice().reverse().reduce(function(n,r){return r(e,t,n)||n},i),o&&void 0!==i.initializer&&(i.value=i.initializer?i.initializer.call(o):void 0,i.initializer=void 0),void 0===i.initializer&&(Object.defineProperty(e,t,i),i=null),i}Object.defineProperty(t,"__esModule",{value:!0});var l,u=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),c=n(0),s=function(e){return e&&e.__esModule?e:{default:e}}(c),f=n(1),d=n(16);n(94);var p=(l=function(e){function t(e){r(this,t);var n=o(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e));return n.state={isHovered:!1},n}return i(t,e),u(t,[{key:"onMouseOver",value:function(){this.setState({isHovered:!0})}},{key:"onMouseOut",value:function(){this.setState({isHovered:!1})}},{key:"render",value:function(){var e=this.props.dataSource,t=e.link,n=e.title,r=e.author,o=e.companyIcon,i=e.companyIconHover,a=e.dateStr,l=e.desc,u=this.state.isHovered;return s.default.createElement(f.Link,{to:t,className:"blog-item",onMouseOver:this.onMouseOver,onMouseOut:this.onMouseOut},s.default.createElement("div",{className:"title"},s.default.createElement("img",{src:u?"./img/docs_hover.png":"./img/docs_normal.png"}),s.default.createElement("span",null,n)),s.default.createElement("div",{className:"brief-info"},s.default.createElement("span",{className:"author"},r),o?s.default.createElement("img",{src:u?i:o}):null,s.default.createElement("span",{className:"date"},a)),s.default.createElement("p",null,l))}}]),t}(s.default.Component),a(l.prototype,"onMouseOver",[d.autobind],Object.getOwnPropertyDescriptor(l.prototype,"onMouseOver"),l.prototype),a(l.prototype,"onMouseOut",[d.autobind],Object.getOwnPropertyDescriptor(l.prototype,"onMouseOut"),l.prototype),l);t.default=p},,,,,,function(e,t,n){n(14)(n(100))},,,function(e,t,n){n(14)(n(103))},function(e,t,n){n(14)(n(104))},,,,,function(e,t){e.exports=".page-slider {\n  overflow: hidden; }\n  .page-slider .slider-list {\n    overflow: visible; }\n    .page-slider .slider-list .slider-page {\n      overflow: hidden;\n      display: inline-block;\n      vertical-align: top; }\n  .page-slider .slider-control {\n    overflow: hidden; }\n    .page-slider .slider-control img {\n      display: inline-block;\n      width: 52px;\n      height: 52px;\n      cursor: pointer; }\n    .page-slider .slider-control .slider-control-prev {\n      float: left; }\n      .page-slider .slider-control .slider-control-prev-hidden {\n        display: none; }\n    .page-slider .slider-control .slider-control-next {\n      float: right; }\n      .page-slider .slider-control .slider-control-next-hidden {\n        display: none; }\n"},,,function(e,t){e.exports=".blog-item {\n  box-sizing: border-box;\n  display: block;\n  width: 100%;\n  padding: 20px;\n  margin-bottom: 40px;\n  background: #F8F8F8; }\n  .blog-item .title img {\n    width: 16px;\n    height: 20px;\n    margin-right: 8px; }\n  .blog-item .title span {\n    font-family: Avenir-Heavy;\n    font-size: 20px;\n    color: #666666; }\n  .blog-item .brief-info {\n    padding: 12px 0 20px; }\n    .blog-item .brief-info .author {\n      font-family: Avenir-Heavy;\n      font-size: 14px;\n      color: #999;\n      margin-right: 8px; }\n    .blog-item .brief-info img {\n      width: 14px;\n      height: 14px; }\n    .blog-item .brief-info .date {\n      float: right;\n      font-family: Avenir-Medium;\n      font-size: 12px;\n      color: #999; }\n  .blog-item p {\n    font-family: Avenir-Medium;\n    font-size: 14px;\n    color: #666;\n    margin: 0; }\n  .blog-item:hover .title span {\n    color: #333; }\n  .blog-item:hover .brief-info .author {\n    color: #666; }\n"},function(e,t){e.exports=".blog-list-page .blog-container {\n  max-width: 1280px;\n  margin: 0 auto;\n  box-sizing: border-box;\n  padding: 50px 8% 80px; }\n  .blog-list-page .blog-container .col {\n    display: inline-block;\n    box-sizing: border-box; }\n    .blog-list-page .blog-container .col-18 {\n      width: 75%;\n      border-right: 1px solid #CBCCCD;\n      padding-right: 6%; }\n    .blog-list-page .blog-container .col-6 {\n      width: 25%;\n      padding-left: 20px;\n      vertical-align: top; }\n      .blog-list-page .blog-container .col-6 h4 {\n        font-family: Avenir-Heavy;\n        font-size: 18px;\n        color: #333;\n        margin: 0 0 20px; }\n      .blog-list-page .blog-container .col-6 ul {\n        list-style: none;\n        margin: 0;\n        padding: 0; }\n        .blog-list-page .blog-container .col-6 ul li {\n          width: 100%;\n          margin-bottom: 14px; }\n          .blog-list-page .blog-container .col-6 ul li span {\n            font-family: Avenir-Medium;\n            font-size: 12px;\n            color: #666; }\n  @media screen and (max-width: 640px) {\n    .blog-list-page .blog-container .left-part {\n      width: 100%;\n      border-right: none;\n      padding-right: 0; }\n    .blog-list-page .blog-container .right-part {\n      display: none; } }\n"}]);
\ No newline at end of file
diff --git a/build/page.js b/build/page.js
index 43092ce..9009361 100644
--- a/build/page.js
+++ b/build/page.js
@@ -1 +1 @@
-!function(t){function e(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,e),o.l=!0,o.exports}var r=window.webpackJsonp;window.webpackJsonp=function(e,n,i){for(var a,u,c=0,f=[];c<e.length;c++)u=e[c],o[u]&&f.push(o[u][0]),o[u]=0;for(a in n)Object.prototype.hasOwnProperty.call(n,a)&&(t[a]=n[a]);for(r&&r(e,n,i);f.length;)f.shift()()};var n={},o={5:0};e.e=function(t){function r(){u.onerror=u.onload=null,clearTimeout(c);var e=o[t];0!==e&&(e&&e[1](new Error("Loading chunk "+t+" failed.")),o[t]=void 0)}var n=o[t];if(0===n)return new Promise(function(t){t()});if(n)return n[2];var i=new Promise(function(e,r){n=o[t]=[e,r]});n[2]=i;var a=document.getElementsByTagName("head")[0],u=document.createElement("script");u.type="text/javascript",u.charset="utf-8",u.async=!0,u.timeout=12e4,e.nc&&u.setAttribute("nonce",e.nc),u.src=e.p+""+{0:"00e53226daea7d0ed5a5",1:"4d064980eca0b316837d",2:"4965585aad0512047b35",3:"dc0e64fc4da0611e9cdc",4:"b128d786b03b56879ed7"}[t]+".js";var c=setTimeout(r,12e4);return u.onerror=u.onload=r,a.appendChild(u),i},e.m=t,e.c=n,e.i=function(t){return t},e.d=function(t,r,n){e.o(t,r)||Object.defineProperty(t,r,{configurable:!1,enumerable:!0,get:n})},e.n=function(t){var r=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(r,"a",r),r},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="./build/",e.oe=function(t){throw console.error(t),t},e(e.s=5)}([function(t,e){t.exports=React},function(t,e){t.exports=ReactRouterDOM},function(t,e){t.exports=ReactDOM},function(t,e,r){"use strict";function n(t){return t&&t.__esModule?t:{default:t}}function o(t){return function(){var e=t.apply(this,arguments);return new Promise(function(t,r){function n(o,i){try{var a=e[o](i),u=a.value}catch(t){return void r(t)}if(!a.done)return Promise.resolve(u).then(function(t){n("next",t)},function(t){n("throw",t)});t(u)}return n("next")})}}function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function a(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function u(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}function c(t){return function(e){function r(t){i(this,r);var e=a(this,(r.__proto__||Object.getPrototypeOf(r)).call(this,t));return e.state={component:null},e}return u(r,e),s(r,[{key:"componentDidMount",value:function(){function e(){return r.apply(this,arguments)}var r=o(l.default.mark(function e(){var r,n;return l.default.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,t();case 2:r=e.sent,n=r.default,this.setState({component:n});case 5:case"end":return e.stop()}},e,this)}));return e}()},{key:"render",value:function(){var t=this.state.component;return t?p.default.createElement(t,this.props):null}}]),r}(h.Component)}Object.defineProperty(e,"__esModule",{value:!0});var f=r(11),l=n(f),s=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}();e.default=c;var h=r(0),p=n(h)},function(t,e){},function(t,e,r){"use strict";function n(t){return t&&t.__esModule?t:{default:t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function a(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}var u=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}(),c=r(0),f=n(c),l=r(2),s=n(l),h=r(1),p=r(3),d=n(p);r(4);var y=(0,d.default)(function(){return r.e(4).then(r.bind(null,10))}),m=(0,d.default)(function(){return r.e(2).then(r.bind(null,8))}),v=(0,d.default)(function(){return r.e(3).then(r.bind(null,6))}),g=(0,d.default)(function(){return r.e(1).then(r.bind(null,7))}),w=(0,d.default)(function(){return r.e(0).then(r.bind(null,9))}),b=function(t){function e(){return o(this,e),i(this,(e.__proto__||Object.getPrototypeOf(e)).apply(this,arguments))}return a(e,t),u(e,[{key:"render",value:function(){return f.default.createElement(h.HashRouter,null,f.default.createElement(h.Switch,null,f.default.createElement(h.Redirect,{exact:!0,from:"/docs",to:"/docs/user/quick-start.md"}),f.default.createElement(h.Redirect,{exact:!0,from:"/docs/",to:"/docs/user/quick-start.md"}),f.default.createElement(h.Route,{exact:!0,path:"/",component:y}),f.default.createElement(h.Route,{exact:!0,path:"/community",component:m}),f.default.createElement(h.Route,{exact:!0,path:"/blog",component:v}),f.default.createElement(h.Route,{path:"/blog/*",component:g}),f.default.createElement(h.Route,{path:"/docs/*",component:w})))}}]),e}(f.default.Component);s.default.render(f.default.createElement(b,null),document.getElementById("root"))},,,,,,function(t,e,r){t.exports=r(12)},function(t,e,r){var n=function(){return this}()||Function("return this")(),o=n.regeneratorRuntime&&Object.getOwnPropertyNames(n).indexOf("regeneratorRuntime")>=0,i=o&&n.regeneratorRuntime;if(n.regeneratorRuntime=void 0,t.exports=r(13),o)n.regeneratorRuntime=i;else try{delete n.regeneratorRuntime}catch(t){n.regeneratorRuntime=void 0}},function(t,e){!function(e){"use strict";function r(t,e,r,n){var i=e&&e.prototype instanceof o?e:o,a=Object.create(i.prototype),u=new p(n||[]);return a._invoke=f(t,r,u),a}function n(t,e,r){try{return{type:"normal",arg:t.call(e,r)}}catch(t){return{type:"throw",arg:t}}}function o(){}function i(){}function a(){}function u(t){["next","throw","return"].forEach(function(e){t[e]=function(t){return this._invoke(e,t)}})}function c(t){function e(r,o,i,a){var u=n(t[r],t,o);if("throw"!==u.type){var c=u.arg,f=c.value;return f&&"object"==typeof f&&g.call(f,"__await")?Promise.resolve(f.__await).then(function(t){e("next",t,i,a)},function(t){e("throw",t,i,a)}):Promise.resolve(f).then(function(t){c.value=t,i(c)},a)}a(u.arg)}function r(t,r){function n(){return new Promise(function(n,o){e(t,r,n,o)})}return o=o?o.then(n,n):n()}var o;this._invoke=r}function f(t,e,r){var o=j;return function(i,a){if(o===P)throw new Error("Generator is already running");if(o===R){if("throw"===i)throw a;return y()}for(r.method=i,r.arg=a;;){var u=r.delegate;if(u){var c=l(u,r);if(c){if(c===k)continue;return c}}if("next"===r.method)r.sent=r._sent=r.arg;else if("throw"===r.method){if(o===j)throw o=R,r.arg;r.dispatchException(r.arg)}else"return"===r.method&&r.abrupt("return",r.arg);o=P;var f=n(t,e,r);if("normal"===f.type){if(o=r.done?R:L,f.arg===k)continue;return{value:f.arg,done:r.done}}"throw"===f.type&&(o=R,r.method="throw",r.arg=f.arg)}}}function l(t,e){var r=t.iterator[e.method];if(r===m){if(e.delegate=null,"throw"===e.method){if(t.iterator.return&&(e.method="return",e.arg=m,l(t,e),"throw"===e.method))return k;e.method="throw",e.arg=new TypeError("The iterator does not provide a 'throw' method")}return k}var o=n(r,t.iterator,e.arg);if("throw"===o.type)return e.method="throw",e.arg=o.arg,e.delegate=null,k;var i=o.arg;return i?i.done?(e[t.resultName]=i.value,e.next=t.nextLoc,"return"!==e.method&&(e.method="next",e.arg=m),e.delegate=null,k):i:(e.method="throw",e.arg=new TypeError("iterator result is not an object"),e.delegate=null,k)}function s(t){var e={tryLoc:t[0]};1 in t&&(e.catchLoc=t[1]),2 in t&&(e.finallyLoc=t[2],e.afterLoc=t[3]),this.tryEntries.push(e)}function h(t){var e=t.completion||{};e.type="normal",delete e.arg,t.completion=e}function p(t){this.tryEntries=[{tryLoc:"root"}],t.forEach(s,this),this.reset(!0)}function d(t){if(t){var e=t[b];if(e)return e.call(t);if("function"==typeof t.next)return t;if(!isNaN(t.length)){var r=-1,n=function e(){for(;++r<t.length;)if(g.call(t,r))return e.value=t[r],e.done=!1,e;return e.value=m,e.done=!0,e};return n.next=n}}return{next:y}}function y(){return{value:m,done:!0}}var m,v=Object.prototype,g=v.hasOwnProperty,w="function"==typeof Symbol?Symbol:{},b=w.iterator||"@@iterator",x=w.asyncIterator||"@@asyncIterator",E=w.toStringTag||"@@toStringTag",_="object"==typeof t,O=e.regeneratorRuntime;if(O)return void(_&&(t.exports=O));O=e.regeneratorRuntime=_?t.exports:{},O.wrap=r;var j="suspendedStart",L="suspendedYield",P="executing",R="completed",k={},T={};T[b]=function(){return this};var N=Object.getPrototypeOf,S=N&&N(N(d([])));S&&S!==v&&g.call(S,b)&&(T=S);var G=a.prototype=o.prototype=Object.create(T);i.prototype=G.constructor=a,a.constructor=i,a[E]=i.displayName="GeneratorFunction",O.isGeneratorFunction=function(t){var e="function"==typeof t&&t.constructor;return!!e&&(e===i||"GeneratorFunction"===(e.displayName||e.name))},O.mark=function(t){return Object.setPrototypeOf?Object.setPrototypeOf(t,a):(t.__proto__=a,E in t||(t[E]="GeneratorFunction")),t.prototype=Object.create(G),t},O.awrap=function(t){return{__await:t}},u(c.prototype),c.prototype[x]=function(){return this},O.AsyncIterator=c,O.async=function(t,e,n,o){var i=new c(r(t,e,n,o));return O.isGeneratorFunction(e)?i:i.next().then(function(t){return t.done?t.value:i.next()})},u(G),G[E]="Generator",G[b]=function(){return this},G.toString=function(){return"[object Generator]"},O.keys=function(t){var e=[];for(var r in t)e.push(r);return e.reverse(),function r(){for(;e.length;){var n=e.pop();if(n in t)return r.value=n,r.done=!1,r}return r.done=!0,r}},O.values=d,p.prototype={constructor:p,reset:function(t){if(this.prev=0,this.next=0,this.sent=this._sent=m,this.done=!1,this.delegate=null,this.method="next",this.arg=m,this.tryEntries.forEach(h),!t)for(var e in this)"t"===e.charAt(0)&&g.call(this,e)&&!isNaN(+e.slice(1))&&(this[e]=m)},stop:function(){this.done=!0;var t=this.tryEntries[0],e=t.completion;if("throw"===e.type)throw e.arg;return this.rval},dispatchException:function(t){function e(e,n){return i.type="throw",i.arg=t,r.next=e,n&&(r.method="next",r.arg=m),!!n}if(this.done)throw t;for(var r=this,n=this.tryEntries.length-1;n>=0;--n){var o=this.tryEntries[n],i=o.completion;if("root"===o.tryLoc)return e("end");if(o.tryLoc<=this.prev){var a=g.call(o,"catchLoc"),u=g.call(o,"finallyLoc");if(a&&u){if(this.prev<o.catchLoc)return e(o.catchLoc,!0);if(this.prev<o.finallyLoc)return e(o.finallyLoc)}else if(a){if(this.prev<o.catchLoc)return e(o.catchLoc,!0)}else{if(!u)throw new Error("try statement without catch or finally");if(this.prev<o.finallyLoc)return e(o.finallyLoc)}}}},abrupt:function(t,e){for(var r=this.tryEntries.length-1;r>=0;--r){var n=this.tryEntries[r];if(n.tryLoc<=this.prev&&g.call(n,"finallyLoc")&&this.prev<n.finallyLoc){var o=n;break}}o&&("break"===t||"continue"===t)&&o.tryLoc<=e&&e<=o.finallyLoc&&(o=null);var i=o?o.completion:{};return i.type=t,i.arg=e,o?(this.method="next",this.next=o.finallyLoc,k):this.complete(i)},complete:function(t,e){if("throw"===t.type)throw t.arg;return"break"===t.type||"continue"===t.type?this.next=t.arg:"return"===t.type?(this.rval=this.arg=t.arg,this.method="return",this.next="end"):"normal"===t.type&&e&&(this.next=e),k},finish:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var r=this.tryEntries[e];if(r.finallyLoc===t)return this.complete(r.completion,r.afterLoc),h(r),k}},catch:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var r=this.tryEntries[e];if(r.tryLoc===t){var n=r.completion;if("throw"===n.type){var o=n.arg;h(r)}return o}}throw new Error("illegal catch attempt")},delegateYield:function(t,e,r){return this.delegate={iterator:d(t),resultName:e,nextLoc:r},"next"===this.method&&(this.arg=m),k}}}(function(){return this}()||Function("return this")())},function(t,e){t.exports=function(t){if("undefined"!=typeof DEBUG&&DEBUG&&"object"!=typeof document)throw new Error("The style-loader cannot be used in a non-browser environment");var e=document.createElement("style");e.type="text/css";var r=document.getElementsByTagName("head")[0];return r.appendChild(e),e.styleSheet?e.styleSheet.cssText=t:e.appendChild(document.createTextNode(t)),function(){r.removeChild(e)}}}]);
\ No newline at end of file
+!function(t){function e(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,e),o.l=!0,o.exports}var r=window.webpackJsonp;window.webpackJsonp=function(e,n,i){for(var a,u,c=0,f=[];c<e.length;c++)u=e[c],o[u]&&f.push(o[u][0]),o[u]=0;for(a in n)Object.prototype.hasOwnProperty.call(n,a)&&(t[a]=n[a]);for(r&&r(e,n,i);f.length;)f.shift()()};var n={},o={5:0};e.e=function(t){function r(){u.onerror=u.onload=null,clearTimeout(c);var e=o[t];0!==e&&(e&&e[1](new Error("Loading chunk "+t+" failed.")),o[t]=void 0)}var n=o[t];if(0===n)return new Promise(function(t){t()});if(n)return n[2];var i=new Promise(function(e,r){n=o[t]=[e,r]});n[2]=i;var a=document.getElementsByTagName("head")[0],u=document.createElement("script");u.type="text/javascript",u.charset="utf-8",u.async=!0,u.timeout=12e4,e.nc&&u.setAttribute("nonce",e.nc),u.src=e.p+""+{0:"232598f226cf6f343d6b",1:"5a6160d1904d4b715bba",2:"2b4e297caa93bedd5375",3:"b04dbfd23d6672063557",4:"2c33718a291dda18fd19"}[t]+".js";var c=setTimeout(r,12e4);return u.onerror=u.onload=r,a.appendChild(u),i},e.m=t,e.c=n,e.i=function(t){return t},e.d=function(t,r,n){e.o(t,r)||Object.defineProperty(t,r,{configurable:!1,enumerable:!0,get:n})},e.n=function(t){var r=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(r,"a",r),r},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="./build/",e.oe=function(t){throw console.error(t),t},e(e.s=5)}([function(t,e){t.exports=React},function(t,e){t.exports=ReactRouterDOM},function(t,e){t.exports=ReactDOM},function(t,e,r){"use strict";function n(t){return t&&t.__esModule?t:{default:t}}function o(t){return function(){var e=t.apply(this,arguments);return new Promise(function(t,r){function n(o,i){try{var a=e[o](i),u=a.value}catch(t){return void r(t)}if(!a.done)return Promise.resolve(u).then(function(t){n("next",t)},function(t){n("throw",t)});t(u)}return n("next")})}}function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function a(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function u(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}function c(t){return function(e){function r(t){i(this,r);var e=a(this,(r.__proto__||Object.getPrototypeOf(r)).call(this,t));return e.state={component:null},e}return u(r,e),s(r,[{key:"componentDidMount",value:function(){function e(){return r.apply(this,arguments)}var r=o(l.default.mark(function e(){var r,n;return l.default.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,t();case 2:r=e.sent,n=r.default,this.setState({component:n});case 5:case"end":return e.stop()}},e,this)}));return e}()},{key:"render",value:function(){var t=this.state.component;return t?p.default.createElement(t,this.props):null}}]),r}(h.Component)}Object.defineProperty(e,"__esModule",{value:!0});var f=r(11),l=n(f),s=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}();e.default=c;var h=r(0),p=n(h)},function(t,e){},function(t,e,r){"use strict";function n(t){return t&&t.__esModule?t:{default:t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function a(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}var u=function(){function t(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,r,n){return r&&t(e.prototype,r),n&&t(e,n),e}}(),c=r(0),f=n(c),l=r(2),s=n(l),h=r(1),p=r(3),d=n(p);r(4);var y=(0,d.default)(function(){return r.e(4).then(r.bind(null,10))}),m=(0,d.default)(function(){return r.e(2).then(r.bind(null,8))}),v=(0,d.default)(function(){return r.e(3).then(r.bind(null,6))}),g=(0,d.default)(function(){return r.e(1).then(r.bind(null,7))}),b=(0,d.default)(function(){return r.e(0).then(r.bind(null,9))}),w=function(t){function e(){return o(this,e),i(this,(e.__proto__||Object.getPrototypeOf(e)).apply(this,arguments))}return a(e,t),u(e,[{key:"render",value:function(){return f.default.createElement(h.HashRouter,null,f.default.createElement(h.Switch,null,f.default.createElement(h.Redirect,{exact:!0,from:"/docs",to:"/docs/user/quick-start.md"}),f.default.createElement(h.Redirect,{exact:!0,from:"/docs/",to:"/docs/user/quick-start.md"}),f.default.createElement(h.Route,{exact:!0,path:"/",component:y}),f.default.createElement(h.Route,{exact:!0,path:"/community",component:m}),f.default.createElement(h.Route,{exact:!0,path:"/blog",component:v}),f.default.createElement(h.Route,{path:"/blog/*",component:g}),f.default.createElement(h.Route,{path:"/docs/*",component:b})))}}]),e}(f.default.Component);s.default.render(f.default.createElement(w,null),document.getElementById("root"))},,,,,,function(t,e,r){t.exports=r(12)},function(t,e,r){var n=function(){return this}()||Function("return this")(),o=n.regeneratorRuntime&&Object.getOwnPropertyNames(n).indexOf("regeneratorRuntime")>=0,i=o&&n.regeneratorRuntime;if(n.regeneratorRuntime=void 0,t.exports=r(13),o)n.regeneratorRuntime=i;else try{delete n.regeneratorRuntime}catch(t){n.regeneratorRuntime=void 0}},function(t,e){!function(e){"use strict";function r(t,e,r,n){var i=e&&e.prototype instanceof o?e:o,a=Object.create(i.prototype),u=new p(n||[]);return a._invoke=f(t,r,u),a}function n(t,e,r){try{return{type:"normal",arg:t.call(e,r)}}catch(t){return{type:"throw",arg:t}}}function o(){}function i(){}function a(){}function u(t){["next","throw","return"].forEach(function(e){t[e]=function(t){return this._invoke(e,t)}})}function c(t){function e(r,o,i,a){var u=n(t[r],t,o);if("throw"!==u.type){var c=u.arg,f=c.value;return f&&"object"==typeof f&&g.call(f,"__await")?Promise.resolve(f.__await).then(function(t){e("next",t,i,a)},function(t){e("throw",t,i,a)}):Promise.resolve(f).then(function(t){c.value=t,i(c)},a)}a(u.arg)}function r(t,r){function n(){return new Promise(function(n,o){e(t,r,n,o)})}return o=o?o.then(n,n):n()}var o;this._invoke=r}function f(t,e,r){var o=j;return function(i,a){if(o===P)throw new Error("Generator is already running");if(o===R){if("throw"===i)throw a;return y()}for(r.method=i,r.arg=a;;){var u=r.delegate;if(u){var c=l(u,r);if(c){if(c===k)continue;return c}}if("next"===r.method)r.sent=r._sent=r.arg;else if("throw"===r.method){if(o===j)throw o=R,r.arg;r.dispatchException(r.arg)}else"return"===r.method&&r.abrupt("return",r.arg);o=P;var f=n(t,e,r);if("normal"===f.type){if(o=r.done?R:L,f.arg===k)continue;return{value:f.arg,done:r.done}}"throw"===f.type&&(o=R,r.method="throw",r.arg=f.arg)}}}function l(t,e){var r=t.iterator[e.method];if(r===m){if(e.delegate=null,"throw"===e.method){if(t.iterator.return&&(e.method="return",e.arg=m,l(t,e),"throw"===e.method))return k;e.method="throw",e.arg=new TypeError("The iterator does not provide a 'throw' method")}return k}var o=n(r,t.iterator,e.arg);if("throw"===o.type)return e.method="throw",e.arg=o.arg,e.delegate=null,k;var i=o.arg;return i?i.done?(e[t.resultName]=i.value,e.next=t.nextLoc,"return"!==e.method&&(e.method="next",e.arg=m),e.delegate=null,k):i:(e.method="throw",e.arg=new TypeError("iterator result is not an object"),e.delegate=null,k)}function s(t){var e={tryLoc:t[0]};1 in t&&(e.catchLoc=t[1]),2 in t&&(e.finallyLoc=t[2],e.afterLoc=t[3]),this.tryEntries.push(e)}function h(t){var e=t.completion||{};e.type="normal",delete e.arg,t.completion=e}function p(t){this.tryEntries=[{tryLoc:"root"}],t.forEach(s,this),this.reset(!0)}function d(t){if(t){var e=t[w];if(e)return e.call(t);if("function"==typeof t.next)return t;if(!isNaN(t.length)){var r=-1,n=function e(){for(;++r<t.length;)if(g.call(t,r))return e.value=t[r],e.done=!1,e;return e.value=m,e.done=!0,e};return n.next=n}}return{next:y}}function y(){return{value:m,done:!0}}var m,v=Object.prototype,g=v.hasOwnProperty,b="function"==typeof Symbol?Symbol:{},w=b.iterator||"@@iterator",x=b.asyncIterator||"@@asyncIterator",E=b.toStringTag||"@@toStringTag",_="object"==typeof t,O=e.regeneratorRuntime;if(O)return void(_&&(t.exports=O));O=e.regeneratorRuntime=_?t.exports:{},O.wrap=r;var j="suspendedStart",L="suspendedYield",P="executing",R="completed",k={},T={};T[w]=function(){return this};var N=Object.getPrototypeOf,S=N&&N(N(d([])));S&&S!==v&&g.call(S,w)&&(T=S);var G=a.prototype=o.prototype=Object.create(T);i.prototype=G.constructor=a,a.constructor=i,a[E]=i.displayName="GeneratorFunction",O.isGeneratorFunction=function(t){var e="function"==typeof t&&t.constructor;return!!e&&(e===i||"GeneratorFunction"===(e.displayName||e.name))},O.mark=function(t){return Object.setPrototypeOf?Object.setPrototypeOf(t,a):(t.__proto__=a,E in t||(t[E]="GeneratorFunction")),t.prototype=Object.create(G),t},O.awrap=function(t){return{__await:t}},u(c.prototype),c.prototype[x]=function(){return this},O.AsyncIterator=c,O.async=function(t,e,n,o){var i=new c(r(t,e,n,o));return O.isGeneratorFunction(e)?i:i.next().then(function(t){return t.done?t.value:i.next()})},u(G),G[E]="Generator",G[w]=function(){return this},G.toString=function(){return"[object Generator]"},O.keys=function(t){var e=[];for(var r in t)e.push(r);return e.reverse(),function r(){for(;e.length;){var n=e.pop();if(n in t)return r.value=n,r.done=!1,r}return r.done=!0,r}},O.values=d,p.prototype={constructor:p,reset:function(t){if(this.prev=0,this.next=0,this.sent=this._sent=m,this.done=!1,this.delegate=null,this.method="next",this.arg=m,this.tryEntries.forEach(h),!t)for(var e in this)"t"===e.charAt(0)&&g.call(this,e)&&!isNaN(+e.slice(1))&&(this[e]=m)},stop:function(){this.done=!0;var t=this.tryEntries[0],e=t.completion;if("throw"===e.type)throw e.arg;return this.rval},dispatchException:function(t){function e(e,n){return i.type="throw",i.arg=t,r.next=e,n&&(r.method="next",r.arg=m),!!n}if(this.done)throw t;for(var r=this,n=this.tryEntries.length-1;n>=0;--n){var o=this.tryEntries[n],i=o.completion;if("root"===o.tryLoc)return e("end");if(o.tryLoc<=this.prev){var a=g.call(o,"catchLoc"),u=g.call(o,"finallyLoc");if(a&&u){if(this.prev<o.catchLoc)return e(o.catchLoc,!0);if(this.prev<o.finallyLoc)return e(o.finallyLoc)}else if(a){if(this.prev<o.catchLoc)return e(o.catchLoc,!0)}else{if(!u)throw new Error("try statement without catch or finally");if(this.prev<o.finallyLoc)return e(o.finallyLoc)}}}},abrupt:function(t,e){for(var r=this.tryEntries.length-1;r>=0;--r){var n=this.tryEntries[r];if(n.tryLoc<=this.prev&&g.call(n,"finallyLoc")&&this.prev<n.finallyLoc){var o=n;break}}o&&("break"===t||"continue"===t)&&o.tryLoc<=e&&e<=o.finallyLoc&&(o=null);var i=o?o.completion:{};return i.type=t,i.arg=e,o?(this.method="next",this.next=o.finallyLoc,k):this.complete(i)},complete:function(t,e){if("throw"===t.type)throw t.arg;return"break"===t.type||"continue"===t.type?this.next=t.arg:"return"===t.type?(this.rval=this.arg=t.arg,this.method="return",this.next="end"):"normal"===t.type&&e&&(this.next=e),k},finish:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var r=this.tryEntries[e];if(r.finallyLoc===t)return this.complete(r.completion,r.afterLoc),h(r),k}},catch:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var r=this.tryEntries[e];if(r.tryLoc===t){var n=r.completion;if("throw"===n.type){var o=n.arg;h(r)}return o}}throw new Error("illegal catch attempt")},delegateYield:function(t,e,r){return this.delegate={iterator:d(t),resultName:e,nextLoc:r},"next"===this.method&&(this.arg=m),k}}}(function(){return this}()||Function("return this")())},function(t,e){t.exports=function(t){if("undefined"!=typeof DEBUG&&DEBUG&&"object"!=typeof document)throw new Error("The style-loader cannot be used in a non-browser environment");var e=document.createElement("style");e.type="text/css";var r=document.getElementsByTagName("head")[0];return r.appendChild(e),e.styleSheet?e.styleSheet.cssText=t:e.appendChild(document.createTextNode(t)),function(){r.removeChild(e)}}}]);
\ No newline at end of file
diff --git a/src/pages/home/index.scss b/src/pages/home/index.scss
index 2832d02..d1d6260 100644
--- a/src/pages/home/index.scss
+++ b/src/pages/home/index.scss
@@ -87,8 +87,7 @@
         z-index: -1;
         left: 0;
         top: -36px;
-        width: 1494px;
-        height: 318px;
+        width: 100%;
       }
     }
     .product-name {


 

----------------------------------------------------------------
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