blob: 43a89e6d9a4852eab356d473e788caef22746308 [file] [log] [blame]
<!doctype html><html lang=zh-cn class=no-js><head><meta name=ROBOTS content="INDEX, FOLLOW"><link rel=canonical href=https://cn.dubbo.apache.org/zh-cn/docsv2.7/dev/source/refer-service/><script>var _hmt=_hmt||[];(function(){var e,t=document.createElement("script");t.src="https://hm.baidu.com/hm.js?3b78f49ba47181e4d998a66b689446e9",e=document.getElementsByTagName("script")[0],e.parentNode.insertBefore(t,e)})()</script><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta http-equiv=Content-Security-Policy content="frame-src *"><meta name=generator content="Hugo 0.122.0"><link rel="shortcut icon" type=image/png href=/imgs/favicon.png><link rel=apple-touch-icon href=/favicons/apple-touch-icon-180x180.png sizes=180x180><link rel=manifest href=/manifest.webmanifest><title>服务引用 | Apache Dubbo</title><meta property="og:title" content="服务引用">
<meta property="og:description" content="本文介绍了 Dubbo 服务引用的过程和实现细节"><meta property="og:type" content="article"><meta property="og:url" content="https://cn.dubbo.apache.org/zh-cn/docsv2.7/dev/source/refer-service/"><meta property="article:section" content="docsv2.7"><meta property="article:modified_time" content="2023-03-01T09:48:32+08:00"><meta itemprop=name content="服务引用"><meta itemprop=description content="本文介绍了 Dubbo 服务引用的过程和实现细节"><meta itemprop=dateModified content="2023-03-01T09:48:32+08:00"><meta itemprop=wordCount content="2878"><meta itemprop=keywords content><meta name=twitter:card content="summary"><meta name=twitter:title content="服务引用"><meta name=twitter:description content="本文介绍了 Dubbo 服务引用的过程和实现细节"><script async src="https://www.googletagmanager.com/gtag/js?id=G-NM6FFMT51J"></script><script>var doNotTrack=!1;if(!doNotTrack){window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments)}gtag("js",new Date),gtag("config","G-NM6FFMT51J",{anonymize_ip:!1})}</script><link rel=preload href=/scss/main.min.f77e221bcdbe0cadb996060fe82063c747b60c229a1f8bbf0ee529adbadd84fa.css as=style><link href=/scss/main.min.f77e221bcdbe0cadb996060fe82063c747b60c229a1f8bbf0ee529adbadd84fa.css rel=stylesheet integrity><script src=/js/jquery-3.5.1.min.js integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin=anonymous></script><meta name=theme-color content="#326ce5"><link rel=stylesheet href=/css/feature-states.css><meta name=description content="本文介绍了 Dubbo 服务引用的过程和实现细节"><meta property="og:description" content="本文介绍了 Dubbo 服务引用的过程和实现细节"><meta name=twitter:description content="本文介绍了 Dubbo 服务引用的过程和实现细节"><meta property="og:url" content="https://cn.dubbo.apache.org/zh-cn/docsv2.7/dev/source/refer-service/"><meta property="og:title" content="服务引用"><meta name=twitter:title content="服务引用"><meta name=twitter:image:alt content="Apache Dubbo"><meta property="og:type" content="article"><meta name=viewport content="width=device-width"><script async defer src=/js/github-buttons.js></script><link href=/css/community.css rel=stylesheet><link href=/css/contactus.css rel=stylesheet><link href=/css/language.css rel=stylesheet><script src=/js/script.js></script></head><body class="td-page td-documentation"><header><nav class="js-navbar-scroll navbar navbar-expand navbar-dark flex-column flex-md-row td-navbar" data-auto-burger=primary><a class=navbar-brand href=/zh-cn/><span class=navbar-logo></span><span class="text-uppercase font-weight-bold">Apache Dubbo</span></a><div class="td-navbar-nav-scroll ml-md-auto" id=main_navbar><ul class="navbar-nav mt-2 mt-lg-0"><li class="nav-item mr-4 mb-2 mb-lg-0"><a class=nav-link href=/zh-cn/overview/home/><span>文档</span></a></li><li class="nav-item mr-4 mb-2 mb-lg-0"><a class=nav-link href=/zh-cn/blog/><span>博客</span></a></li><li class="nav-item mr-4 mb-2 mb-lg-0"><a class=nav-link href=/zh-cn/download/><span>版本发布</span></a></li><li class="nav-item mr-4 mb-2 mb-lg-0"><a class=nav-link href=/zh-cn/contact/><span>联系社区</span></a></li><li class="nav-item mr-4 mb-2 mb-lg-0"><a class=nav-link href=https://start.dubbo.apache.org/bootstrap.html target=_blank><span>Initializer</span><i class='fas fa-external-link-alt'></i></a></li><li class="nav-item mr-4 mb-2 mb-lg-0"><a class=nav-link href=/zh-cn/github/><span>Github</span><i class='fa-brands fa-github'></i></a></li><li class="nav-item dropdown d-lg-block"><a class="nav-link dropdown-toggle" href=# id=navbarDropdownMenuLink role=button data-toggle=dropdown aria-haspopup=true aria-expanded=false>中文</a><div class="dropdown-menu dropdown-menu-right" aria-labelledby=navbarDropdownMenuLink><a class=dropdown-item href=/en/>English</a></div></li><li class="nav-item dropdown d-lg-block"><div class="nav-item d-none d-lg-block"></div></li></ul></div></nav><section class="header-hero text-white pb-0 light-text"></section></header><div class="container-fluid td-outer"><div class=td-main><div class="row flex-md-nowrap"><div class="col-12 col-md-3 col-xl-2 td-sidebar d-print-none"><script>$(function(){$("#td-section-nav a").removeClass("active"),$("#td-section-nav #m-zh-cndocsv27devsourcerefer-service").addClass("active"),$("#td-section-nav #m-zh-cndocsv27devsourcerefer-service-li span").addClass("td-sidebar-nav-active-item"),$("#td-section-nav #m-zh-cndocsv27devsourcerefer-service").parents("li").addClass("active-path"),$("#td-section-nav li.active-path").addClass("show"),$("#td-section-nav li.active-path").children("input").prop("checked",!0),$("#td-section-nav #m-zh-cndocsv27devsourcerefer-service-li").siblings("li").addClass("show"),$("#td-section-nav #m-zh-cndocsv27devsourcerefer-service-li").children("ul").children("li").addClass("show"),$("#td-sidebar-menu").toggleClass("d-none")})</script><div id=td-sidebar-menu class="td-sidebar__inner d-none"><div id=content-mobile><form class="td-sidebar__search d-flex align-items-center"><button class="btn btn-link td-sidebar__toggle d-md-none p-0 ml-3 fas fa-bars" type=button data-toggle=collapse data-target=#td-section-nav aria-controls=td-docs-nav aria-expanded=false aria-label="Toggle section navigation"></button></form></div><div id=content-desktop></div><nav class="collapse td-sidebar-nav foldable-nav" id=td-section-nav><ul class="td-sidebar-nav__section pr-md-3 ul-0"><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocsv27-li><ul class=ul-1><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocsv27user-li><input type=checkbox id=m-zh-cndocsv27user-check>
<label for=m-zh-cndocsv27user-check><a href=/zh-cn/docsv2.7/user/ class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocsv27user><span>用户文档</span></a></label><ul class="ul-2 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocsv27userpreface-li><input type=checkbox id=m-zh-cndocsv27userpreface-check>
<label for=m-zh-cndocsv27userpreface-check><a href=/zh-cn/docsv2.7/user/preface/ class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocsv27userpreface><span>入门介绍</span></a></label><ul class="ul-3 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userprefacebackground-li><input type=checkbox id=m-zh-cndocsv27userprefacebackground-check>
<label for=m-zh-cndocsv27userprefacebackground-check><a href=/zh-cn/docsv2.7/user/preface/background/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userprefacebackground><span>背景</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userprefacerequirements-li><input type=checkbox id=m-zh-cndocsv27userprefacerequirements-check>
<label for=m-zh-cndocsv27userprefacerequirements-check><a href=/zh-cn/docsv2.7/user/preface/requirements/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userprefacerequirements><span>需求</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userprefacearchitecture-li><input type=checkbox id=m-zh-cndocsv27userprefacearchitecture-check>
<label for=m-zh-cndocsv27userprefacearchitecture-check><a href=/zh-cn/docsv2.7/user/preface/architecture/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userprefacearchitecture><span>架构</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userprefaceusage-li><input type=checkbox id=m-zh-cndocsv27userprefaceusage-check>
<label for=m-zh-cndocsv27userprefaceusage-check><a href=/zh-cn/docsv2.7/user/preface/usage/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userprefaceusage><span>用法</span></a></label></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userquick-start-li><input type=checkbox id=m-zh-cndocsv27userquick-start-check>
<label for=m-zh-cndocsv27userquick-start-check><a href=/zh-cn/docsv2.7/user/quick-start/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userquick-start><span>快速开始</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userdependencies-li><input type=checkbox id=m-zh-cndocsv27userdependencies-check>
<label for=m-zh-cndocsv27userdependencies-check><a href=/zh-cn/docsv2.7/user/dependencies/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userdependencies><span>基本依赖</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27usermaturity-li><input type=checkbox id=m-zh-cndocsv27usermaturity-check>
<label for=m-zh-cndocsv27usermaturity-check><a href=/zh-cn/docsv2.7/user/maturity/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27usermaturity><span>成熟度</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocsv27userconfiguration-li><input type=checkbox id=m-zh-cndocsv27userconfiguration-check>
<label for=m-zh-cndocsv27userconfiguration-check><a href=/zh-cn/docsv2.7/user/configuration/ class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocsv27userconfiguration><span>配置手册</span></a></label><ul class="ul-3 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userconfigurationxml-li><input type=checkbox id=m-zh-cndocsv27userconfigurationxml-check>
<label for=m-zh-cndocsv27userconfigurationxml-check><a href=/zh-cn/docsv2.7/user/configuration/xml/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userconfigurationxml><span>XML 配置</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userconfigurationconfig-center-li><input type=checkbox id=m-zh-cndocsv27userconfigurationconfig-center-check>
<label for=m-zh-cndocsv27userconfigurationconfig-center-check><a href=/zh-cn/docsv2.7/user/configuration/config-center/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userconfigurationconfig-center><span>动态配置中心</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userconfigurationproperties-li><input type=checkbox id=m-zh-cndocsv27userconfigurationproperties-check>
<label for=m-zh-cndocsv27userconfigurationproperties-check><a href=/zh-cn/docsv2.7/user/configuration/properties/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userconfigurationproperties><span>属性配置</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userconfigurationenvironment-variables-li><input type=checkbox id=m-zh-cndocsv27userconfigurationenvironment-variables-check>
<label for=m-zh-cndocsv27userconfigurationenvironment-variables-check><a href=/zh-cn/docsv2.7/user/configuration/environment-variables/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userconfigurationenvironment-variables><span>自动加载环境变量</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userconfigurationapi-li><input type=checkbox id=m-zh-cndocsv27userconfigurationapi-check>
<label for=m-zh-cndocsv27userconfigurationapi-check><a href=/zh-cn/docsv2.7/user/configuration/api/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userconfigurationapi><span>API 配置</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userconfigurationannotation-li><input type=checkbox id=m-zh-cndocsv27userconfigurationannotation-check>
<label for=m-zh-cndocsv27userconfigurationannotation-check><a href=/zh-cn/docsv2.7/user/configuration/annotation/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userconfigurationannotation><span>注解配置</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userconfigurationconfiguration-load-process-li><input type=checkbox id=m-zh-cndocsv27userconfigurationconfiguration-load-process-check>
<label for=m-zh-cndocsv27userconfigurationconfiguration-load-process-check><a href=/zh-cn/docsv2.7/user/configuration/configuration-load-process/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userconfigurationconfiguration-load-process><span>配置加载流程</span></a></label></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocsv27userexamples-li><input type=checkbox id=m-zh-cndocsv27userexamples-check>
<label for=m-zh-cndocsv27userexamples-check><a href=/zh-cn/docsv2.7/user/examples/ class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocsv27userexamples><span>用法示例</span></a></label><ul class="ul-3 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplese9878de8af95e6aca1e695b0e9858de7bdae-li><input type=checkbox id=m-zh-cndocsv27userexamplese9878de8af95e6aca1e695b0e9858de7bdae-check>
<label for=m-zh-cndocsv27userexamplese9878de8af95e6aca1e695b0e9858de7bdae-check><a href=/zh-cn/docsv2.7/user/examples/%E9%87%8D%E8%AF%95%E6%AC%A1%E6%95%B0%E9%85%8D%E7%BD%AE/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplese9878de8af95e6aca1e695b0e9858de7bdae><span>服务重试</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplespreflight-check-li><input type=checkbox id=m-zh-cndocsv27userexamplespreflight-check-check>
<label for=m-zh-cndocsv27userexamplespreflight-check-check><a href=/zh-cn/docsv2.7/user/examples/preflight-check/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplespreflight-check><span>启动时检查</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesfault-tolerent-strategy-li><input type=checkbox id=m-zh-cndocsv27userexamplesfault-tolerent-strategy-check>
<label for=m-zh-cndocsv27userexamplesfault-tolerent-strategy-check><a href=/zh-cn/docsv2.7/user/examples/fault-tolerent-strategy/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesfault-tolerent-strategy><span>集群容错</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesloadbalance-li><input type=checkbox id=m-zh-cndocsv27userexamplesloadbalance-check>
<label for=m-zh-cndocsv27userexamplesloadbalance-check><a href=/zh-cn/docsv2.7/user/examples/loadbalance/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesloadbalance><span>负载均衡</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesthread-model-li><input type=checkbox id=m-zh-cndocsv27userexamplesthread-model-check>
<label for=m-zh-cndocsv27userexamplesthread-model-check><a href=/zh-cn/docsv2.7/user/examples/thread-model/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesthread-model><span>线程模型</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesexplicit-target-li><input type=checkbox id=m-zh-cndocsv27userexamplesexplicit-target-check>
<label for=m-zh-cndocsv27userexamplesexplicit-target-check><a href=/zh-cn/docsv2.7/user/examples/explicit-target/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesexplicit-target><span>直连提供者</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplessubscribe-only-li><input type=checkbox id=m-zh-cndocsv27userexamplessubscribe-only-check>
<label for=m-zh-cndocsv27userexamplessubscribe-only-check><a href=/zh-cn/docsv2.7/user/examples/subscribe-only/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplessubscribe-only><span>只订阅</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesmulti-protocols-li><input type=checkbox id=m-zh-cndocsv27userexamplesmulti-protocols-check>
<label for=m-zh-cndocsv27userexamplesmulti-protocols-check><a href=/zh-cn/docsv2.7/user/examples/multi-protocols/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesmulti-protocols><span>多协议</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesmulti-registry-li><input type=checkbox id=m-zh-cndocsv27userexamplesmulti-registry-check>
<label for=m-zh-cndocsv27userexamplesmulti-registry-check><a href=/zh-cn/docsv2.7/user/examples/multi-registry/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesmulti-registry><span>多注册中心</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesservice-group-li><input type=checkbox id=m-zh-cndocsv27userexamplesservice-group-check>
<label for=m-zh-cndocsv27userexamplesservice-group-check><a href=/zh-cn/docsv2.7/user/examples/service-group/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesservice-group><span>服务分组</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesstatic-service-li><input type=checkbox id=m-zh-cndocsv27userexamplesstatic-service-check>
<label for=m-zh-cndocsv27userexamplesstatic-service-check><a href=/zh-cn/docsv2.7/user/examples/static-service/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesstatic-service><span>静态服务</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesmulti-versions-li><input type=checkbox id=m-zh-cndocsv27userexamplesmulti-versions-check>
<label for=m-zh-cndocsv27userexamplesmulti-versions-check><a href=/zh-cn/docsv2.7/user/examples/multi-versions/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesmulti-versions><span>多版本</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesgroup-merger-li><input type=checkbox id=m-zh-cndocsv27userexamplesgroup-merger-check>
<label for=m-zh-cndocsv27userexamplesgroup-merger-check><a href=/zh-cn/docsv2.7/user/examples/group-merger/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesgroup-merger><span>分组聚合</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesparameter-validation-li><input type=checkbox id=m-zh-cndocsv27userexamplesparameter-validation-check>
<label for=m-zh-cndocsv27userexamplesparameter-validation-check><a href=/zh-cn/docsv2.7/user/examples/parameter-validation/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesparameter-validation><span>参数验证</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesgeneric-invoke-with-json-li><input type=checkbox id=m-zh-cndocsv27userexamplesgeneric-invoke-with-json-check>
<label for=m-zh-cndocsv27userexamplesgeneric-invoke-with-json-check><a href=/zh-cn/docsv2.7/user/examples/generic-invoke-with-json/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesgeneric-invoke-with-json><span>JSON泛化调用</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesmsgpack-serialization-li><input type=checkbox id=m-zh-cndocsv27userexamplesmsgpack-serialization-check>
<label for=m-zh-cndocsv27userexamplesmsgpack-serialization-check><a href=/zh-cn/docsv2.7/user/examples/msgpack-serialization/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesmsgpack-serialization><span>msgpack序列化</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesprovider-timeout-release-li><input type=checkbox id=m-zh-cndocsv27userexamplesprovider-timeout-release-check>
<label for=m-zh-cndocsv27userexamplesprovider-timeout-release-check><a href=/zh-cn/docsv2.7/user/examples/provider-timeout-release/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesprovider-timeout-release><span>provider超时打断</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesresult-cache-li><input type=checkbox id=m-zh-cndocsv27userexamplesresult-cache-check>
<label for=m-zh-cndocsv27userexamplesresult-cache-check><a href=/zh-cn/docsv2.7/user/examples/result-cache/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesresult-cache><span>结果缓存</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesbroadcast-resp-collect-li><input type=checkbox id=m-zh-cndocsv27userexamplesbroadcast-resp-collect-check>
<label for=m-zh-cndocsv27userexamplesbroadcast-resp-collect-check><a href=/zh-cn/docsv2.7/user/examples/broadcast-resp-collect/ title=收集Dubbo广播响应 class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesbroadcast-resp-collect><span>收集广播响应</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesinvoke-with-specified-ip-li><input type=checkbox id=m-zh-cndocsv27userexamplesinvoke-with-specified-ip-check>
<label for=m-zh-cndocsv27userexamplesinvoke-with-specified-ip-check><a href=/zh-cn/docsv2.7/user/examples/invoke-with-specified-ip/ title="指定Ip Port调用Provider" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesinvoke-with-specified-ip><span>指定IP</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesgeneric-reference-li><input type=checkbox id=m-zh-cndocsv27userexamplesgeneric-reference-check>
<label for=m-zh-cndocsv27userexamplesgeneric-reference-check><a href=/zh-cn/docsv2.7/user/examples/generic-reference/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesgeneric-reference><span>使用泛化调用</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesprotobuf-idl-li><input type=checkbox id=m-zh-cndocsv27userexamplesprotobuf-idl-check>
<label for=m-zh-cndocsv27userexamplesprotobuf-idl-check><a href=/zh-cn/docsv2.7/user/examples/protobuf-idl/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesprotobuf-idl><span>Protobuf</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplespb-generic-reference-li><input type=checkbox id=m-zh-cndocsv27userexamplespb-generic-reference-check>
<label for=m-zh-cndocsv27userexamplespb-generic-reference-check><a href=/zh-cn/docsv2.7/user/examples/pb-generic-reference/ title="GoogleProtobuf 对象泛化调用" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplespb-generic-reference><span>Protobuf 泛化调用</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesgeneric-service-li><input type=checkbox id=m-zh-cndocsv27userexamplesgeneric-service-check>
<label for=m-zh-cndocsv27userexamplesgeneric-service-check><a href=/zh-cn/docsv2.7/user/examples/generic-service/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesgeneric-service><span>实现泛化调用</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesecho-service-li><input type=checkbox id=m-zh-cndocsv27userexamplesecho-service-check>
<label for=m-zh-cndocsv27userexamplesecho-service-check><a href=/zh-cn/docsv2.7/user/examples/echo-service/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesecho-service><span>回声测试</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplescontext-li><input type=checkbox id=m-zh-cndocsv27userexamplescontext-check>
<label for=m-zh-cndocsv27userexamplescontext-check><a href=/zh-cn/docsv2.7/user/examples/context/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplescontext><span>上下文信息</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesattachment-li><input type=checkbox id=m-zh-cndocsv27userexamplesattachment-check>
<label for=m-zh-cndocsv27userexamplesattachment-check><a href=/zh-cn/docsv2.7/user/examples/attachment/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesattachment><span>隐式参数</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesasync-call-li><input type=checkbox id=m-zh-cndocsv27userexamplesasync-call-check>
<label for=m-zh-cndocsv27userexamplesasync-call-check><a href=/zh-cn/docsv2.7/user/examples/async-call/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesasync-call><span>异步调用</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesasync-execute-on-provider-li><input type=checkbox id=m-zh-cndocsv27userexamplesasync-execute-on-provider-check>
<label for=m-zh-cndocsv27userexamplesasync-execute-on-provider-check><a href=/zh-cn/docsv2.7/user/examples/async-execute-on-provider/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesasync-execute-on-provider><span>异步执行</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexampleslocal-call-li><input type=checkbox id=m-zh-cndocsv27userexampleslocal-call-check>
<label for=m-zh-cndocsv27userexampleslocal-call-check><a href=/zh-cn/docsv2.7/user/examples/local-call/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexampleslocal-call><span>本地调用</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplescallback-parameter-li><input type=checkbox id=m-zh-cndocsv27userexamplescallback-parameter-check>
<label for=m-zh-cndocsv27userexamplescallback-parameter-check><a href=/zh-cn/docsv2.7/user/examples/callback-parameter/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplescallback-parameter><span>参数回调</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesevents-notify-li><input type=checkbox id=m-zh-cndocsv27userexamplesevents-notify-check>
<label for=m-zh-cndocsv27userexamplesevents-notify-check><a href=/zh-cn/docsv2.7/user/examples/events-notify/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesevents-notify><span>事件通知</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexampleslocal-stub-li><input type=checkbox id=m-zh-cndocsv27userexampleslocal-stub-check>
<label for=m-zh-cndocsv27userexampleslocal-stub-check><a href=/zh-cn/docsv2.7/user/examples/local-stub/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexampleslocal-stub><span>本地存根</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexampleslocal-mock-li><input type=checkbox id=m-zh-cndocsv27userexampleslocal-mock-check>
<label for=m-zh-cndocsv27userexampleslocal-mock-check><a href=/zh-cn/docsv2.7/user/examples/local-mock/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexampleslocal-mock><span>本地伪装</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesdelay-publish-li><input type=checkbox id=m-zh-cndocsv27userexamplesdelay-publish-check>
<label for=m-zh-cndocsv27userexamplesdelay-publish-check><a href=/zh-cn/docsv2.7/user/examples/delay-publish/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesdelay-publish><span>延迟暴露</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesconcurrency-control-li><input type=checkbox id=m-zh-cndocsv27userexamplesconcurrency-control-check>
<label for=m-zh-cndocsv27userexamplesconcurrency-control-check><a href=/zh-cn/docsv2.7/user/examples/concurrency-control/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesconcurrency-control><span>并发控制</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesconfig-connections-li><input type=checkbox id=m-zh-cndocsv27userexamplesconfig-connections-check>
<label for=m-zh-cndocsv27userexamplesconfig-connections-check><a href=/zh-cn/docsv2.7/user/examples/config-connections/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesconfig-connections><span>连接控制</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexampleslazy-connect-li><input type=checkbox id=m-zh-cndocsv27userexampleslazy-connect-check>
<label for=m-zh-cndocsv27userexampleslazy-connect-check><a href=/zh-cn/docsv2.7/user/examples/lazy-connect/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexampleslazy-connect><span>延迟连接</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesstickiness-li><input type=checkbox id=m-zh-cndocsv27userexamplesstickiness-check>
<label for=m-zh-cndocsv27userexamplesstickiness-check><a href=/zh-cn/docsv2.7/user/examples/stickiness/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesstickiness><span>粘滞连接</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplestls-li><input type=checkbox id=m-zh-cndocsv27userexamplestls-check>
<label for=m-zh-cndocsv27userexamplestls-check><a href=/zh-cn/docsv2.7/user/examples/tls/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplestls><span>TLS</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplestoken-authorization-li><input type=checkbox id=m-zh-cndocsv27userexamplestoken-authorization-check>
<label for=m-zh-cndocsv27userexamplestoken-authorization-check><a href=/zh-cn/docsv2.7/user/examples/token-authorization/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplestoken-authorization><span>令牌验证</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesrouting-rule-li><input type=checkbox id=m-zh-cndocsv27userexamplesrouting-rule-check>
<label for=m-zh-cndocsv27userexamplesrouting-rule-check><a href=/zh-cn/docsv2.7/user/examples/routing-rule/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesrouting-rule><span>路由规则</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesrouting-rule-deprecated-li><input type=checkbox id=m-zh-cndocsv27userexamplesrouting-rule-deprecated-check>
<label for=m-zh-cndocsv27userexamplesrouting-rule-deprecated-check><a href=/zh-cn/docsv2.7/user/examples/routing-rule-deprecated/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesrouting-rule-deprecated><span>旧路由规则</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesconfig-rule-li><input type=checkbox id=m-zh-cndocsv27userexamplesconfig-rule-check>
<label for=m-zh-cndocsv27userexamplesconfig-rule-check><a href=/zh-cn/docsv2.7/user/examples/config-rule/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesconfig-rule><span>配置规则</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesservice-downgrade-li><input type=checkbox id=m-zh-cndocsv27userexamplesservice-downgrade-check>
<label for=m-zh-cndocsv27userexamplesservice-downgrade-check><a href=/zh-cn/docsv2.7/user/examples/service-downgrade/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesservice-downgrade><span>服务降级</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesconfig-rule-deprecated-li><input type=checkbox id=m-zh-cndocsv27userexamplesconfig-rule-deprecated-check>
<label for=m-zh-cndocsv27userexamplesconfig-rule-deprecated-check><a href=/zh-cn/docsv2.7/user/examples/config-rule-deprecated/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesconfig-rule-deprecated><span>旧配置规则</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesconsumer-threadpool-li><input type=checkbox id=m-zh-cndocsv27userexamplesconsumer-threadpool-check>
<label for=m-zh-cndocsv27userexamplesconsumer-threadpool-check><a href=/zh-cn/docsv2.7/user/examples/consumer-threadpool/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesconsumer-threadpool><span>消费端线程池模型</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesgraceful-shutdown-li><input type=checkbox id=m-zh-cndocsv27userexamplesgraceful-shutdown-check>
<label for=m-zh-cndocsv27userexamplesgraceful-shutdown-check><a href=/zh-cn/docsv2.7/user/examples/graceful-shutdown/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesgraceful-shutdown><span>优雅停机</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexampleshostname-binding-li><input type=checkbox id=m-zh-cndocsv27userexampleshostname-binding-check>
<label for=m-zh-cndocsv27userexampleshostname-binding-check><a href=/zh-cn/docsv2.7/user/examples/hostname-binding/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexampleshostname-binding><span>主机绑定</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userexamplesset-host-li><input type=checkbox id=m-zh-cndocsv27userexamplesset-host-check>
<label for=m-zh-cndocsv27userexamplesset-host-check><a href=/zh-cn/docsv2.7/user/examples/set-host/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userexamplesset-host><span>主机配置</span></a></label></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocsv27userreferences-li><input type=checkbox id=m-zh-cndocsv27userreferences-check>
<label for=m-zh-cndocsv27userreferences-check><a href=/zh-cn/docsv2.7/user/references/ class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocsv27userreferences><span>参考手册</span></a></label><ul class="ul-3 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocsv27userreferencesxml-li><input type=checkbox id=m-zh-cndocsv27userreferencesxml-check>
<label for=m-zh-cndocsv27userreferencesxml-check><a href=/zh-cn/docsv2.7/user/references/xml/ title="Schema 配置参考手册" class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocsv27userreferencesxml><span>XML 配置</span></a></label><ul class="ul-4 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userreferencesxmldubbo-application-li><input type=checkbox id=m-zh-cndocsv27userreferencesxmldubbo-application-check>
<label for=m-zh-cndocsv27userreferencesxmldubbo-application-check><a href=/zh-cn/docsv2.7/user/references/xml/dubbo-application/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userreferencesxmldubbo-application><span>dubbo:application</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userreferencesxmldubbo-argument-li><input type=checkbox id=m-zh-cndocsv27userreferencesxmldubbo-argument-check>
<label for=m-zh-cndocsv27userreferencesxmldubbo-argument-check><a href=/zh-cn/docsv2.7/user/references/xml/dubbo-argument/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userreferencesxmldubbo-argument><span>dubbo:argument</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userreferencesxmldubbo-config-center-li><input type=checkbox id=m-zh-cndocsv27userreferencesxmldubbo-config-center-check>
<label for=m-zh-cndocsv27userreferencesxmldubbo-config-center-check><a href=/zh-cn/docsv2.7/user/references/xml/dubbo-config-center/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userreferencesxmldubbo-config-center><span>dubbo:config-center</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userreferencesxmldubbo-consumer-li><input type=checkbox id=m-zh-cndocsv27userreferencesxmldubbo-consumer-check>
<label for=m-zh-cndocsv27userreferencesxmldubbo-consumer-check><a href=/zh-cn/docsv2.7/user/references/xml/dubbo-consumer/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userreferencesxmldubbo-consumer><span>dubbo:consumer</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userreferencesxmldubbo-method-li><input type=checkbox id=m-zh-cndocsv27userreferencesxmldubbo-method-check>
<label for=m-zh-cndocsv27userreferencesxmldubbo-method-check><a href=/zh-cn/docsv2.7/user/references/xml/dubbo-method/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userreferencesxmldubbo-method><span>dubbo:method</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userreferencesxmldubbo-module-li><input type=checkbox id=m-zh-cndocsv27userreferencesxmldubbo-module-check>
<label for=m-zh-cndocsv27userreferencesxmldubbo-module-check><a href=/zh-cn/docsv2.7/user/references/xml/dubbo-module/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userreferencesxmldubbo-module><span>dubbo:module</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userreferencesxmldubbo-monitor-li><input type=checkbox id=m-zh-cndocsv27userreferencesxmldubbo-monitor-check>
<label for=m-zh-cndocsv27userreferencesxmldubbo-monitor-check><a href=/zh-cn/docsv2.7/user/references/xml/dubbo-monitor/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userreferencesxmldubbo-monitor><span>dubbo:monitor</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userreferencesxmldubbo-parameter-li><input type=checkbox id=m-zh-cndocsv27userreferencesxmldubbo-parameter-check>
<label for=m-zh-cndocsv27userreferencesxmldubbo-parameter-check><a href=/zh-cn/docsv2.7/user/references/xml/dubbo-parameter/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userreferencesxmldubbo-parameter><span>dubbo:parameter</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userreferencesxmldubbo-protocol-li><input type=checkbox id=m-zh-cndocsv27userreferencesxmldubbo-protocol-check>
<label for=m-zh-cndocsv27userreferencesxmldubbo-protocol-check><a href=/zh-cn/docsv2.7/user/references/xml/dubbo-protocol/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userreferencesxmldubbo-protocol><span>dubbo:protocol</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userreferencesxmldubbo-provider-li><input type=checkbox id=m-zh-cndocsv27userreferencesxmldubbo-provider-check>
<label for=m-zh-cndocsv27userreferencesxmldubbo-provider-check><a href=/zh-cn/docsv2.7/user/references/xml/dubbo-provider/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userreferencesxmldubbo-provider><span>dubbo:provider</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userreferencesxmldubbo-reference-li><input type=checkbox id=m-zh-cndocsv27userreferencesxmldubbo-reference-check>
<label for=m-zh-cndocsv27userreferencesxmldubbo-reference-check><a href=/zh-cn/docsv2.7/user/references/xml/dubbo-reference/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userreferencesxmldubbo-reference><span>dubbo:reference</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userreferencesxmldubbo-registry-li><input type=checkbox id=m-zh-cndocsv27userreferencesxmldubbo-registry-check>
<label for=m-zh-cndocsv27userreferencesxmldubbo-registry-check><a href=/zh-cn/docsv2.7/user/references/xml/dubbo-registry/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userreferencesxmldubbo-registry><span>dubbo:registry</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userreferencesxmldubbo-service-li><input type=checkbox id=m-zh-cndocsv27userreferencesxmldubbo-service-check>
<label for=m-zh-cndocsv27userreferencesxmldubbo-service-check><a href=/zh-cn/docsv2.7/user/references/xml/dubbo-service/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userreferencesxmldubbo-service><span>dubbo:service</span></a></label></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocsv27userreferencesprotocol-li><input type=checkbox id=m-zh-cndocsv27userreferencesprotocol-check>
<label for=m-zh-cndocsv27userreferencesprotocol-check><a href=/zh-cn/docsv2.7/user/references/protocol/ class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocsv27userreferencesprotocol><span>协议参考手册</span></a></label><ul class="ul-4 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userreferencesprotocoldubbo-li><input type=checkbox id=m-zh-cndocsv27userreferencesprotocoldubbo-check>
<label for=m-zh-cndocsv27userreferencesprotocoldubbo-check><a href=/zh-cn/docsv2.7/user/references/protocol/dubbo/ title="dubbo 协议" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userreferencesprotocoldubbo><span>dubbo://</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userreferencesprotocolrest-li><input type=checkbox id=m-zh-cndocsv27userreferencesprotocolrest-check>
<label for=m-zh-cndocsv27userreferencesprotocolrest-check><a href=/zh-cn/docsv2.7/user/references/protocol/rest/ title="rest 协议" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userreferencesprotocolrest><span>rest://</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userreferencesprotocolhttp-li><input type=checkbox id=m-zh-cndocsv27userreferencesprotocolhttp-check>
<label for=m-zh-cndocsv27userreferencesprotocolhttp-check><a href=/zh-cn/docsv2.7/user/references/protocol/http/ title="http 协议" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userreferencesprotocolhttp><span>http://</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userreferencesprotocolhessian-li><input type=checkbox id=m-zh-cndocsv27userreferencesprotocolhessian-check>
<label for=m-zh-cndocsv27userreferencesprotocolhessian-check><a href=/zh-cn/docsv2.7/user/references/protocol/hessian/ title="hessian 协议" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userreferencesprotocolhessian><span>hessian://</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userreferencesprotocolredis-li><input type=checkbox id=m-zh-cndocsv27userreferencesprotocolredis-check>
<label for=m-zh-cndocsv27userreferencesprotocolredis-check><a href=/zh-cn/docsv2.7/user/references/protocol/redis/ title="redis 协议" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userreferencesprotocolredis><span>redis://</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userreferencesprotocolthrift-li><input type=checkbox id=m-zh-cndocsv27userreferencesprotocolthrift-check>
<label for=m-zh-cndocsv27userreferencesprotocolthrift-check><a href=/zh-cn/docsv2.7/user/references/protocol/thrift/ title="thrift 协议" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userreferencesprotocolthrift><span>thrift://</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userreferencesprotocolgrpc-li><input type=checkbox id=m-zh-cndocsv27userreferencesprotocolgrpc-check>
<label for=m-zh-cndocsv27userreferencesprotocolgrpc-check><a href=/zh-cn/docsv2.7/user/references/protocol/grpc/ title="gRPC 协议" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userreferencesprotocolgrpc><span>grpc://</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userreferencesprotocolmemcached-li><input type=checkbox id=m-zh-cndocsv27userreferencesprotocolmemcached-check>
<label for=m-zh-cndocsv27userreferencesprotocolmemcached-check><a href=/zh-cn/docsv2.7/user/references/protocol/memcached/ title="memcached 协议" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userreferencesprotocolmemcached><span>memcached://</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userreferencesprotocolrmi-li><input type=checkbox id=m-zh-cndocsv27userreferencesprotocolrmi-check>
<label for=m-zh-cndocsv27userreferencesprotocolrmi-check><a href=/zh-cn/docsv2.7/user/references/protocol/rmi/ title="rmi 协议" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userreferencesprotocolrmi><span>rmi://</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userreferencesprotocolwebservice-li><input type=checkbox id=m-zh-cndocsv27userreferencesprotocolwebservice-check>
<label for=m-zh-cndocsv27userreferencesprotocolwebservice-check><a href=/zh-cn/docsv2.7/user/references/protocol/webservice/ title="webservice 协议" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userreferencesprotocolwebservice><span>webservice://</span></a></label></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocsv27userreferencesregistry-li><input type=checkbox id=m-zh-cndocsv27userreferencesregistry-check>
<label for=m-zh-cndocsv27userreferencesregistry-check><a href=/zh-cn/docsv2.7/user/references/registry/ class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocsv27userreferencesregistry><span>注册中心参考手册</span></a></label><ul class="ul-4 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userreferencesregistrynacos-li><input type=checkbox id=m-zh-cndocsv27userreferencesregistrynacos-check>
<label for=m-zh-cndocsv27userreferencesregistrynacos-check><a href=/zh-cn/docsv2.7/user/references/registry/nacos/ title="Nacos 注册中心" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userreferencesregistrynacos><span>Nacos</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userreferencesregistryzookeeper-li><input type=checkbox id=m-zh-cndocsv27userreferencesregistryzookeeper-check>
<label for=m-zh-cndocsv27userreferencesregistryzookeeper-check><a href=/zh-cn/docsv2.7/user/references/registry/zookeeper/ title="Zookeeper 注册中心" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userreferencesregistryzookeeper><span>Zookeeper</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userreferencesregistrymulticast-li><input type=checkbox id=m-zh-cndocsv27userreferencesregistrymulticast-check>
<label for=m-zh-cndocsv27userreferencesregistrymulticast-check><a href=/zh-cn/docsv2.7/user/references/registry/multicast/ title="Multicast 注册中心" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userreferencesregistrymulticast><span>Multicast</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userreferencesregistryredis-li><input type=checkbox id=m-zh-cndocsv27userreferencesregistryredis-check>
<label for=m-zh-cndocsv27userreferencesregistryredis-check><a href=/zh-cn/docsv2.7/user/references/registry/redis/ title="Redis 注册中心" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userreferencesregistryredis><span>Redis</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userreferencesregistrysimple-li><input type=checkbox id=m-zh-cndocsv27userreferencesregistrysimple-check>
<label for=m-zh-cndocsv27userreferencesregistrysimple-check><a href=/zh-cn/docsv2.7/user/references/registry/simple/ title="Simple 注册中心" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userreferencesregistrysimple><span>Simple</span></a></label></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userreferencesmetadata-li><input type=checkbox id=m-zh-cndocsv27userreferencesmetadata-check>
<label for=m-zh-cndocsv27userreferencesmetadata-check><a href=/zh-cn/docsv2.7/user/references/metadata/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userreferencesmetadata><span>元数据参考手册</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userreferencesapi-li><input type=checkbox id=m-zh-cndocsv27userreferencesapi-check>
<label for=m-zh-cndocsv27userreferencesapi-check><a href=/zh-cn/docsv2.7/user/references/api/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userreferencesapi><span>API 参考手册</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userreferencesqos-li><input type=checkbox id=m-zh-cndocsv27userreferencesqos-check>
<label for=m-zh-cndocsv27userreferencesqos-check><a href=/zh-cn/docsv2.7/user/references/qos/ title=在线运维命令参考手册 class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userreferencesqos><span>QOS 手册</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userreferencestelnet-li><input type=checkbox id=m-zh-cndocsv27userreferencestelnet-check>
<label for=m-zh-cndocsv27userreferencestelnet-check><a href=/zh-cn/docsv2.7/user/references/telnet/ title="Telnet 命令参考手册" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userreferencestelnet><span>Telnet 手册</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userreferencesmaven-li><input type=checkbox id=m-zh-cndocsv27userreferencesmaven-check>
<label for=m-zh-cndocsv27userreferencesmaven-check><a href=/zh-cn/docsv2.7/user/references/maven/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userreferencesmaven><span>Maven 插件参考手册</span></a></label></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocsv27userversions-li><input type=checkbox id=m-zh-cndocsv27userversions-check>
<label for=m-zh-cndocsv27userversions-check><a href=/zh-cn/docsv2.7/user/versions/ class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocsv27userversions><span>版本升级</span></a></label><ul class="ul-3 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userversionsversion-270-li><input type=checkbox id=m-zh-cndocsv27userversionsversion-270-check>
<label for=m-zh-cndocsv27userversionsversion-270-check><a href=/zh-cn/docsv2.7/user/versions/version-270/ title=升级与可能的兼容性问题总结 class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userversionsversion-270><span>2.7.0</span></a></label></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userbest-practice-li><input type=checkbox id=m-zh-cndocsv27userbest-practice-check>
<label for=m-zh-cndocsv27userbest-practice-check><a href=/zh-cn/docsv2.7/user/best-practice/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userbest-practice><span>服务化最佳实践</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userrecommend-li><input type=checkbox id=m-zh-cndocsv27userrecommend-check>
<label for=m-zh-cndocsv27userrecommend-check><a href=/zh-cn/docsv2.7/user/recommend/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userrecommend><span>推荐用法</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27usercapacity-plan-li><input type=checkbox id=m-zh-cndocsv27usercapacity-plan-check>
<label for=m-zh-cndocsv27usercapacity-plan-check><a href=/zh-cn/docsv2.7/user/capacity-plan/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27usercapacity-plan><span>容量规划</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userperf-test-li><input type=checkbox id=m-zh-cndocsv27userperf-test-check>
<label for=m-zh-cndocsv27userperf-test-check><a href=/zh-cn/docsv2.7/user/perf-test/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userperf-test><span>性能测试报告</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27usercoveragence-li><input type=checkbox id=m-zh-cndocsv27usercoveragence-check>
<label for=m-zh-cndocsv27usercoveragence-check><a href=/zh-cn/docsv2.7/user/coveragence/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27usercoveragence><span>测试覆盖率报告</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userbenchmark-tool-li><input type=checkbox id=m-zh-cndocsv27userbenchmark-tool-check>
<label for=m-zh-cndocsv27userbenchmark-tool-check><a href=/zh-cn/docsv2.7/user/benchmark-tool/ title=基准测试工具包 class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userbenchmark-tool><span>基准测试</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userrest-li><input type=checkbox id=m-zh-cndocsv27userrest-check>
<label for=m-zh-cndocsv27userrest-check><a href=/zh-cn/docsv2.7/user/rest/ title="开发 REST 应用" class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userrest><span>REST 支持</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27usersimple-monitor-li><input type=checkbox id=m-zh-cndocsv27usersimple-monitor-check>
<label for=m-zh-cndocsv27usersimple-monitor-check><a href=/zh-cn/docsv2.7/user/simple-monitor/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27usersimple-monitor><span>简单监控</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userserialization-li><input type=checkbox id=m-zh-cndocsv27userserialization-check>
<label for=m-zh-cndocsv27userserialization-check><a href=/zh-cn/docsv2.7/user/serialization/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userserialization><span>Kryo 和 FST 序列化</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocsv27userlanguages-li><input type=checkbox id=m-zh-cndocsv27userlanguages-check>
<label for=m-zh-cndocsv27userlanguages-check><a href=/zh-cn/docsv2.7/user/languages/ class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocsv27userlanguages><span>其他语言支持</span></a></label><ul class="ul-3 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocsv27userlanguageserlang-li><input type=checkbox id=m-zh-cndocsv27userlanguageserlang-check>
<label for=m-zh-cndocsv27userlanguageserlang-check><a href=/zh-cn/docsv2.7/user/languages/erlang/ class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocsv27userlanguageserlang><span>Erlang</span></a></label><ul class="ul-4 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userlanguageserlangquick-start-li><input type=checkbox id=m-zh-cndocsv27userlanguageserlangquick-start-check>
<label for=m-zh-cndocsv27userlanguageserlangquick-start-check><a href=/zh-cn/docsv2.7/user/languages/erlang/quick-start/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userlanguageserlangquick-start><span>快速开始</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userlanguageserlangreference-li><input type=checkbox id=m-zh-cndocsv27userlanguageserlangreference-check>
<label for=m-zh-cndocsv27userlanguageserlangreference-check><a href=/zh-cn/docsv2.7/user/languages/erlang/reference/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userlanguageserlangreference><span>消费者配置</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userlanguageserlangservice-li><input type=checkbox id=m-zh-cndocsv27userlanguageserlangservice-check>
<label for=m-zh-cndocsv27userlanguageserlangservice-check><a href=/zh-cn/docsv2.7/user/languages/erlang/service/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userlanguageserlangservice><span>提供者配置</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27userlanguageserlangserialization-li><input type=checkbox id=m-zh-cndocsv27userlanguageserlangserialization-check>
<label for=m-zh-cndocsv27userlanguageserlangserialization-check><a href=/zh-cn/docsv2.7/user/languages/erlang/serialization/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27userlanguageserlangserialization><span>序列化配置项</span></a></label></li></ul></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27usernew-features-in-a-glance-li><input type=checkbox id=m-zh-cndocsv27usernew-features-in-a-glance-check>
<label for=m-zh-cndocsv27usernew-features-in-a-glance-check><a href=/zh-cn/docsv2.7/user/new-features-in-a-glance/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27usernew-features-in-a-glance><span></span></a></label></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocsv27dev-li><input type=checkbox id=m-zh-cndocsv27dev-check>
<label for=m-zh-cndocsv27dev-check><a href=/zh-cn/docsv2.7/dev/ class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocsv27dev><span>开发指南</span></a></label><ul class="ul-2 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devbuild-li><input type=checkbox id=m-zh-cndocsv27devbuild-check>
<label for=m-zh-cndocsv27devbuild-check><a href=/zh-cn/docsv2.7/dev/build/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devbuild><span>源码构建</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devdesign-li><input type=checkbox id=m-zh-cndocsv27devdesign-check>
<label for=m-zh-cndocsv27devdesign-check><a href=/zh-cn/docsv2.7/dev/design/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devdesign><span>框架设计</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devspi-li><input type=checkbox id=m-zh-cndocsv27devspi-check>
<label for=m-zh-cndocsv27devspi-check><a href=/zh-cn/docsv2.7/dev/spi/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devspi><span>扩展点加载</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devimplementation-li><input type=checkbox id=m-zh-cndocsv27devimplementation-check>
<label for=m-zh-cndocsv27devimplementation-check><a href=/zh-cn/docsv2.7/dev/implementation/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devimplementation><span>实现细节</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocsv27devimpls-li><input type=checkbox id=m-zh-cndocsv27devimpls-check>
<label for=m-zh-cndocsv27devimpls-check><a href=/zh-cn/docsv2.7/dev/impls/ class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocsv27devimpls><span>SPI 扩展实现</span></a></label><ul class="ul-3 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devimplsprotocol-li><input type=checkbox id=m-zh-cndocsv27devimplsprotocol-check>
<label for=m-zh-cndocsv27devimplsprotocol-check><a href=/zh-cn/docsv2.7/dev/impls/protocol/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devimplsprotocol><span>协议扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devimplsfilter-li><input type=checkbox id=m-zh-cndocsv27devimplsfilter-check>
<label for=m-zh-cndocsv27devimplsfilter-check><a href=/zh-cn/docsv2.7/dev/impls/filter/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devimplsfilter><span>调用拦截扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devimplsinvoker-listener-li><input type=checkbox id=m-zh-cndocsv27devimplsinvoker-listener-check>
<label for=m-zh-cndocsv27devimplsinvoker-listener-check><a href=/zh-cn/docsv2.7/dev/impls/invoker-listener/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devimplsinvoker-listener><span>引用监听扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devimplsexporter-listener-li><input type=checkbox id=m-zh-cndocsv27devimplsexporter-listener-check>
<label for=m-zh-cndocsv27devimplsexporter-listener-check><a href=/zh-cn/docsv2.7/dev/impls/exporter-listener/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devimplsexporter-listener><span>暴露监听扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devimplscluster-li><input type=checkbox id=m-zh-cndocsv27devimplscluster-check>
<label for=m-zh-cndocsv27devimplscluster-check><a href=/zh-cn/docsv2.7/dev/impls/cluster/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devimplscluster><span>集群扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devimplsrouter-li><input type=checkbox id=m-zh-cndocsv27devimplsrouter-check>
<label for=m-zh-cndocsv27devimplsrouter-check><a href=/zh-cn/docsv2.7/dev/impls/router/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devimplsrouter><span>路由扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devimplsload-balance-li><input type=checkbox id=m-zh-cndocsv27devimplsload-balance-check>
<label for=m-zh-cndocsv27devimplsload-balance-check><a href=/zh-cn/docsv2.7/dev/impls/load-balance/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devimplsload-balance><span>负载均衡扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devimplsmerger-li><input type=checkbox id=m-zh-cndocsv27devimplsmerger-check>
<label for=m-zh-cndocsv27devimplsmerger-check><a href=/zh-cn/docsv2.7/dev/impls/merger/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devimplsmerger><span>合并结果扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devimplsregistry-li><input type=checkbox id=m-zh-cndocsv27devimplsregistry-check>
<label for=m-zh-cndocsv27devimplsregistry-check><a href=/zh-cn/docsv2.7/dev/impls/registry/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devimplsregistry><span>注册中心扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devimplsmonitor-li><input type=checkbox id=m-zh-cndocsv27devimplsmonitor-check>
<label for=m-zh-cndocsv27devimplsmonitor-check><a href=/zh-cn/docsv2.7/dev/impls/monitor/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devimplsmonitor><span>监控中心扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devimplsextension-factory-li><input type=checkbox id=m-zh-cndocsv27devimplsextension-factory-check>
<label for=m-zh-cndocsv27devimplsextension-factory-check><a href=/zh-cn/docsv2.7/dev/impls/extension-factory/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devimplsextension-factory><span>扩展点加载扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devimplsproxy-factory-li><input type=checkbox id=m-zh-cndocsv27devimplsproxy-factory-check>
<label for=m-zh-cndocsv27devimplsproxy-factory-check><a href=/zh-cn/docsv2.7/dev/impls/proxy-factory/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devimplsproxy-factory><span>动态代理扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devimplscompiler-li><input type=checkbox id=m-zh-cndocsv27devimplscompiler-check>
<label for=m-zh-cndocsv27devimplscompiler-check><a href=/zh-cn/docsv2.7/dev/impls/compiler/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devimplscompiler><span>编译器扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devimplsconfig-center-li><input type=checkbox id=m-zh-cndocsv27devimplsconfig-center-check>
<label for=m-zh-cndocsv27devimplsconfig-center-check><a href=/zh-cn/docsv2.7/dev/impls/config-center/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devimplsconfig-center><span>配置中心扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devimplsdispatcher-li><input type=checkbox id=m-zh-cndocsv27devimplsdispatcher-check>
<label for=m-zh-cndocsv27devimplsdispatcher-check><a href=/zh-cn/docsv2.7/dev/impls/dispatcher/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devimplsdispatcher><span>消息派发扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devimplsthreadpool-li><input type=checkbox id=m-zh-cndocsv27devimplsthreadpool-check>
<label for=m-zh-cndocsv27devimplsthreadpool-check><a href=/zh-cn/docsv2.7/dev/impls/threadpool/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devimplsthreadpool><span>线程池扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devimplsserialize-li><input type=checkbox id=m-zh-cndocsv27devimplsserialize-check>
<label for=m-zh-cndocsv27devimplsserialize-check><a href=/zh-cn/docsv2.7/dev/impls/serialize/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devimplsserialize><span>序列化扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devimplsremoting-li><input type=checkbox id=m-zh-cndocsv27devimplsremoting-check>
<label for=m-zh-cndocsv27devimplsremoting-check><a href=/zh-cn/docsv2.7/dev/impls/remoting/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devimplsremoting><span>网络传输扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devimplsexchanger-li><input type=checkbox id=m-zh-cndocsv27devimplsexchanger-check>
<label for=m-zh-cndocsv27devimplsexchanger-check><a href=/zh-cn/docsv2.7/dev/impls/exchanger/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devimplsexchanger><span>信息交换扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devimplsnetworker-li><input type=checkbox id=m-zh-cndocsv27devimplsnetworker-check>
<label for=m-zh-cndocsv27devimplsnetworker-check><a href=/zh-cn/docsv2.7/dev/impls/networker/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devimplsnetworker><span>组网扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devimplstelnet-handler-li><input type=checkbox id=m-zh-cndocsv27devimplstelnet-handler-check>
<label for=m-zh-cndocsv27devimplstelnet-handler-check><a href=/zh-cn/docsv2.7/dev/impls/telnet-handler/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devimplstelnet-handler><span>Telnet 命令扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devimplsstatus-checker-li><input type=checkbox id=m-zh-cndocsv27devimplsstatus-checker-check>
<label for=m-zh-cndocsv27devimplsstatus-checker-check><a href=/zh-cn/docsv2.7/dev/impls/status-checker/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devimplsstatus-checker><span>状态检查扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devimplscontainer-li><input type=checkbox id=m-zh-cndocsv27devimplscontainer-check>
<label for=m-zh-cndocsv27devimplscontainer-check><a href=/zh-cn/docsv2.7/dev/impls/container/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devimplscontainer><span>容器扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devimplscache-li><input type=checkbox id=m-zh-cndocsv27devimplscache-check>
<label for=m-zh-cndocsv27devimplscache-check><a href=/zh-cn/docsv2.7/dev/impls/cache/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devimplscache><span>缓存扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devimplsvalidation-li><input type=checkbox id=m-zh-cndocsv27devimplsvalidation-check>
<label for=m-zh-cndocsv27devimplsvalidation-check><a href=/zh-cn/docsv2.7/dev/impls/validation/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devimplsvalidation><span>验证扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devimplslogger-adapter-li><input type=checkbox id=m-zh-cndocsv27devimplslogger-adapter-check>
<label for=m-zh-cndocsv27devimplslogger-adapter-check><a href=/zh-cn/docsv2.7/dev/impls/logger-adapter/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devimplslogger-adapter><span>日志适配扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devimplspage-li><input type=checkbox id=m-zh-cndocsv27devimplspage-check>
<label for=m-zh-cndocsv27devimplspage-check><a href=/zh-cn/docsv2.7/dev/impls/page/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devimplspage><span></span></a></label></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocsv27devprincipals-li><input type=checkbox id=m-zh-cndocsv27devprincipals-check>
<label for=m-zh-cndocsv27devprincipals-check><a href=/zh-cn/docsv2.7/dev/principals/ class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocsv27devprincipals><span>设计原则</span></a></label><ul class="ul-3 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devprincipalscode-detail-li><input type=checkbox id=m-zh-cndocsv27devprincipalscode-detail-check>
<label for=m-zh-cndocsv27devprincipalscode-detail-check><a href=/zh-cn/docsv2.7/dev/principals/code-detail/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devprincipalscode-detail><span>魔鬼在细节</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devprincipalsconfiguration-li><input type=checkbox id=m-zh-cndocsv27devprincipalsconfiguration-check>
<label for=m-zh-cndocsv27devprincipalsconfiguration-check><a href=/zh-cn/docsv2.7/dev/principals/configuration/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devprincipalsconfiguration><span>配置设计</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devprincipalsdummy-li><input type=checkbox id=m-zh-cndocsv27devprincipalsdummy-check>
<label for=m-zh-cndocsv27devprincipalsdummy-check><a href=/zh-cn/docsv2.7/dev/principals/dummy/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devprincipalsdummy><span>防痴呆设计</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devprincipalsexpansibility-li><input type=checkbox id=m-zh-cndocsv27devprincipalsexpansibility-check>
<label for=m-zh-cndocsv27devprincipalsexpansibility-check><a href=/zh-cn/docsv2.7/dev/principals/expansibility/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devprincipalsexpansibility><span>谈谈扩充式扩展与增量式扩展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devprincipalsextension-li><input type=checkbox id=m-zh-cndocsv27devprincipalsextension-check>
<label for=m-zh-cndocsv27devprincipalsextension-check><a href=/zh-cn/docsv2.7/dev/principals/extension/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devprincipalsextension><span>扩展点重构</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devprincipalsgeneral-knowledge-li><input type=checkbox id=m-zh-cndocsv27devprincipalsgeneral-knowledge-check>
<label for=m-zh-cndocsv27devprincipalsgeneral-knowledge-check><a href=/zh-cn/docsv2.7/dev/principals/general-knowledge/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devprincipalsgeneral-knowledge><span>一些设计上的基本常识</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devprincipalsrobustness-li><input type=checkbox id=m-zh-cndocsv27devprincipalsrobustness-check>
<label for=m-zh-cndocsv27devprincipalsrobustness-check><a href=/zh-cn/docsv2.7/dev/principals/robustness/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devprincipalsrobustness><span>设计实现的健壮性</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devprincipalsintroduction-li><input type=checkbox id=m-zh-cndocsv27devprincipalsintroduction-check>
<label for=m-zh-cndocsv27devprincipalsintroduction-check><a href=/zh-cn/docsv2.7/dev/principals/introduction/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devprincipalsintroduction><span></span></a></label></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devcontract-li><input type=checkbox id=m-zh-cndocsv27devcontract-check>
<label for=m-zh-cndocsv27devcontract-check><a href=/zh-cn/docsv2.7/dev/contract/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devcontract><span>公共契约</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocsv27devsource-li><input type=checkbox id=m-zh-cndocsv27devsource-check>
<label for=m-zh-cndocsv27devsource-check><a href=/zh-cn/docsv2.7/dev/source/ title="Dubbo 源代码分析" class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocsv27devsource><span>源代码</span></a></label><ul class="ul-3 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devsourcedubbo-spi-li><input type=checkbox id=m-zh-cndocsv27devsourcedubbo-spi-check>
<label for=m-zh-cndocsv27devsourcedubbo-spi-check><a href=/zh-cn/docsv2.7/dev/source/dubbo-spi/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devsourcedubbo-spi><span>Dubbo SPI</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devsourcerouter-li><input type=checkbox id=m-zh-cndocsv27devsourcerouter-check>
<label for=m-zh-cndocsv27devsourcerouter-check><a href=/zh-cn/docsv2.7/dev/source/router/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devsourcerouter><span>服务路由</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devsourceadaptive-extension-li><input type=checkbox id=m-zh-cndocsv27devsourceadaptive-extension-check>
<label for=m-zh-cndocsv27devsourceadaptive-extension-check><a href=/zh-cn/docsv2.7/dev/source/adaptive-extension/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devsourceadaptive-extension><span>SPI 自适应拓展</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devsourceexport-service-li><input type=checkbox id=m-zh-cndocsv27devsourceexport-service-check>
<label for=m-zh-cndocsv27devsourceexport-service-check><a href=/zh-cn/docsv2.7/dev/source/export-service/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devsourceexport-service><span>服务导出</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devsourcerefer-service-li><input type=checkbox id=m-zh-cndocsv27devsourcerefer-service-check>
<label for=m-zh-cndocsv27devsourcerefer-service-check><a href=/zh-cn/docsv2.7/dev/source/refer-service/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devsourcerefer-service><span>服务引用</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devsourceservice-invoking-process-li><input type=checkbox id=m-zh-cndocsv27devsourceservice-invoking-process-check>
<label for=m-zh-cndocsv27devsourceservice-invoking-process-check><a href=/zh-cn/docsv2.7/dev/source/service-invoking-process/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devsourceservice-invoking-process><span>服务调用过程</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devsourcedirectory-li><input type=checkbox id=m-zh-cndocsv27devsourcedirectory-check>
<label for=m-zh-cndocsv27devsourcedirectory-check><a href=/zh-cn/docsv2.7/dev/source/directory/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devsourcedirectory><span>服务目录</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devsourcecluster-li><input type=checkbox id=m-zh-cndocsv27devsourcecluster-check>
<label for=m-zh-cndocsv27devsourcecluster-check><a href=/zh-cn/docsv2.7/dev/source/cluster/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devsourcecluster><span>集群</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devsourceloadbalance-li><input type=checkbox id=m-zh-cndocsv27devsourceloadbalance-check>
<label for=m-zh-cndocsv27devsourceloadbalance-check><a href=/zh-cn/docsv2.7/dev/source/loadbalance/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devsourceloadbalance><span>负载均衡</span></a></label></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devrelease-li><input type=checkbox id=m-zh-cndocsv27devrelease-check>
<label for=m-zh-cndocsv27devrelease-check><a href=/zh-cn/docsv2.7/dev/release/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devrelease><span>版本管理</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devchecklist-li><input type=checkbox id=m-zh-cndocsv27devchecklist-check>
<label for=m-zh-cndocsv27devchecklist-check><a href=/zh-cn/docsv2.7/dev/checklist/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devchecklist><span>检查列表</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devcoding-li><input type=checkbox id=m-zh-cndocsv27devcoding-check>
<label for=m-zh-cndocsv27devcoding-check><a href=/zh-cn/docsv2.7/dev/coding/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devcoding><span>编码约定</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devcode-smell-li><input type=checkbox id=m-zh-cndocsv27devcode-smell-check>
<label for=m-zh-cndocsv27devcode-smell-check><a href=/zh-cn/docsv2.7/dev/code-smell/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devcode-smell><span>坏味道</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27devtck-li><input type=checkbox id=m-zh-cndocsv27devtck-check>
<label for=m-zh-cndocsv27devtck-check><a href=/zh-cn/docsv2.7/dev/tck/ title=技术兼容性测试 class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27devtck><span>TCK</span></a></label></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocsv27admin-li><input type=checkbox id=m-zh-cndocsv27admin-check>
<label for=m-zh-cndocsv27admin-check><a href=/zh-cn/docsv2.7/admin/ class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocsv27admin><span>运维指南</span></a></label><ul class="ul-2 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocsv27adminops-li><input type=checkbox id=m-zh-cndocsv27adminops-check>
<label for=m-zh-cndocsv27adminops-check><a href=/zh-cn/docsv2.7/admin/ops/ class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocsv27adminops><span>Dubbo Admin 运维指南</span></a></label><ul class="ul-3 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27adminopsfunctions-li><input type=checkbox id=m-zh-cndocsv27adminopsfunctions-check>
<label for=m-zh-cndocsv27adminopsfunctions-check><a href=/zh-cn/docsv2.7/admin/ops/functions/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27adminopsfunctions><span>管理控制台运维</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27adminopsintroduction-li><input type=checkbox id=m-zh-cndocsv27adminopsintroduction-check>
<label for=m-zh-cndocsv27adminopsintroduction-check><a href=/zh-cn/docsv2.7/admin/ops/introduction/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27adminopsintroduction><span>Dubbo 管理控制台介绍</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27adminopstest-li><input type=checkbox id=m-zh-cndocsv27adminopstest-check>
<label for=m-zh-cndocsv27adminopstest-check><a href=/zh-cn/docsv2.7/admin/ops/test/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27adminopstest><span>服务测试</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27adminopssearch-li><input type=checkbox id=m-zh-cndocsv27adminopssearch-check>
<label for=m-zh-cndocsv27adminopssearch-check><a href=/zh-cn/docsv2.7/admin/ops/search/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27adminopssearch><span>服务查询和详情展示</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27adminopsapidocs-li><input type=checkbox id=m-zh-cndocsv27adminopsapidocs-check>
<label for=m-zh-cndocsv27adminopsapidocs-check><a href=/zh-cn/docsv2.7/admin/ops/apidocs/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27adminopsapidocs><span>API文档&测试</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27adminopsgovernance-li><input type=checkbox id=m-zh-cndocsv27adminopsgovernance-check>
<label for=m-zh-cndocsv27adminopsgovernance-check><a href=/zh-cn/docsv2.7/admin/ops/governance/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27adminopsgovernance><span>服务治理和配置管理</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27adminopsskywalking-li><input type=checkbox id=m-zh-cndocsv27adminopsskywalking-check>
<label for=m-zh-cndocsv27adminopsskywalking-check><a href=/zh-cn/docsv2.7/admin/ops/skywalking/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27adminopsskywalking><span>使用 Apache Skywalking 做分布式跟踪</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27adminopspinpoint-li><input type=checkbox id=m-zh-cndocsv27adminopspinpoint-check>
<label for=m-zh-cndocsv27adminopspinpoint-check><a href=/zh-cn/docsv2.7/admin/ops/pinpoint/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27adminopspinpoint><span>使用 Pinpoint 做分布式跟踪</span></a></label></li></ul></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section with-child" id=m-zh-cndocsv27admininstall-li><input type=checkbox id=m-zh-cndocsv27admininstall-check>
<label for=m-zh-cndocsv27admininstall-check><a href=/zh-cn/docsv2.7/admin/install/ class="align-left pl-0 td-sidebar-link td-sidebar-link__section" id=m-zh-cndocsv27admininstall><span>安装手册</span></a></label><ul class="ul-3 foldable"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27admininstalladmin-console-li><input type=checkbox id=m-zh-cndocsv27admininstalladmin-console-check>
<label for=m-zh-cndocsv27admininstalladmin-console-check><a href=/zh-cn/docsv2.7/admin/install/admin-console/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27admininstalladmin-console><span>管理控制台安装</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27admininstallprovider-demo-li><input type=checkbox id=m-zh-cndocsv27admininstallprovider-demo-check>
<label for=m-zh-cndocsv27admininstallprovider-demo-check><a href=/zh-cn/docsv2.7/admin/install/provider-demo/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27admininstallprovider-demo><span>示例提供者安装</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27admininstallconsumer-demo-li><input type=checkbox id=m-zh-cndocsv27admininstallconsumer-demo-check>
<label for=m-zh-cndocsv27admininstallconsumer-demo-check><a href=/zh-cn/docsv2.7/admin/install/consumer-demo/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27admininstallconsumer-demo><span>示例消费者安装</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27admininstallredis-li><input type=checkbox id=m-zh-cndocsv27admininstallredis-check>
<label for=m-zh-cndocsv27admininstallredis-check><a href=/zh-cn/docsv2.7/admin/install/redis/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27admininstallredis><span>Redis 注册中心安装</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27admininstallzookeeper-li><input type=checkbox id=m-zh-cndocsv27admininstallzookeeper-check>
<label for=m-zh-cndocsv27admininstallzookeeper-check><a href=/zh-cn/docsv2.7/admin/install/zookeeper/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27admininstallzookeeper><span>Zookeeper 注册中心安装</span></a></label></li><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-zh-cndocsv27admininstallmonitor-center-li><input type=checkbox id=m-zh-cndocsv27admininstallmonitor-center-check>
<label for=m-zh-cndocsv27admininstallmonitor-center-check><a href=/zh-cn/docsv2.7/admin/install/monitor-center/ class="align-left pl-0 td-sidebar-link td-sidebar-link__page" id=m-zh-cndocsv27admininstallmonitor-center><span>Simple 监控中心安装</span></a></label></li></ul></li></ul></li></ul></li></ul></nav></div></div><main class="col-12 col-md-9 col-xl-8 pl-md-5" role=main><nav aria-label=breadcrumb class=td-breadcrumbs><ol class=breadcrumb><li class=breadcrumb-item><a href=https://cn.dubbo.apache.org/zh-cn/docsv2.7/>文档 2.x</a></li><li class=breadcrumb-item><a href=https://cn.dubbo.apache.org/zh-cn/docsv2.7/dev/>开发指南</a></li><li class=breadcrumb-item><a href=https://cn.dubbo.apache.org/zh-cn/docsv2.7/dev/source/>源代码</a></li><li class="breadcrumb-item active" aria-current=page><a href=https://cn.dubbo.apache.org/zh-cn/docsv2.7/dev/source/refer-service/ aria-disabled=true class="btn-link disabled">服务引用</a></li></ol></nav><div class=td-content><h1>服务引用</h1><div class=lead>本文介绍了 Dubbo 服务引用的过程和实现细节</div><header class=article-meta></header><h2 id=1-简介>1. 简介</h2><p>上一篇文章详细分析了服务导出的过程,本篇文章我们趁热打铁,继续分析服务引用过程。在 Dubbo 中,我们可以通过两种方式引用远程服务。第一种是使用服务直连的方式引用服务,第二种方式是基于注册中心进行引用。服务直连的方式仅适合在调试或测试服务的场景下使用,不适合在线上环境使用。因此,本文我将重点分析通过注册中心引用服务的过程。从注册中心中获取服务配置只是服务引用过程中的一环,除此之外,服务消费者还需要经历 Invoker 创建、代理类创建等步骤。这些步骤,将在后续章节中一一进行分析。</p><h2 id=2服务引用原理>2.服务引用原理</h2><p>Dubbo 服务引用的时机有两个,第一个是在 Spring 容器调用 ReferenceBean 的 afterPropertiesSet 方法时引用服务,第二个是在 ReferenceBean 对应的服务被注入到其他类中时引用。这两个引用服务的时机区别在于,第一个是饿汉式的,第二个是懒汉式的。默认情况下,Dubbo 使用懒汉式引用服务。如果需要使用饿汉式,可通过配置 &lt;dubbo:reference> 的 init 属性开启。下面我们按照 Dubbo 默认配置进行分析,整个分析过程从 ReferenceBean 的 getObject 方法开始。当我们的服务被注入到其他类中时,Spring 会第一时间调用 getObject 方法,并由该方法执行服务引用逻辑。按照惯例,在进行具体工作之前,需先进行配置检查与收集工作。接着根据收集到的信息决定服务用的方式,有三种,第一种是引用本地 (JVM) 服务,第二是通过直连方式引用远程服务,第三是通过注册中心引用远程服务。不管是哪种引用方式,最后都会得到一个 Invoker 实例。如果有多个注册中心,多个服务提供者,这个时候会得到一组 Invoker 实例,此时需要通过集群管理类 Cluster 将多个 Invoker 合并成一个实例。合并后的 Invoker 实例已经具备调用本地或远程服务的能力了,但并不能将此实例暴露给用户使用,这会对用户业务代码造成侵入。此时框架还需要通过代理工厂类 (ProxyFactory) 为服务接口生成代理类,并让代理类去调用 Invoker 逻辑。避免了 Dubbo 框架代码对业务代码的侵入,同时也让框架更容易使用。</p><p>以上就是服务引用的大致原理,下面我们深入到代码中,详细分析服务引用细节。</p><h2 id=3源码分析>3.源码分析</h2><p>服务引用的入口方法为 ReferenceBean 的 getObject 方法,该方法定义在 Spring 的 FactoryBean 接口中,ReferenceBean 实现了这个方法。实现代码如下:</p><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>public</span> Object <span style=color:#268bd2>getObject</span>() <span style=color:#268bd2>throws</span> Exception {
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> get();
</span></span><span style=display:flex><span>}
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span><span style=color:#268bd2>public</span> <span style=color:#268bd2>synchronized</span> T <span style=color:#268bd2>get</span>() {
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (destroyed) {
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> IllegalStateException(<span style=color:#2aa198>&#34;Already destroyed!&#34;</span>);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> <span style=color:#586e75>// 检测 ref 是否为空,为空则通过 init 方法创建</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (ref <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> <span style=color:#586e75>// init 方法主要用于处理配置,以及调用 createProxy 生成代理类</span>
</span></span><span style=display:flex><span> init();
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> ref;
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><p>以上两个方法的代码比较简短,并不难理解。这里需要特别说明一下,如果你对 2.6.4 及以下版本的 getObject 方法进行调试时,会碰到比较奇怪的的问题。这里假设你使用 IDEA,且保持了 IDEA 的默认配置。当你面调试到 get 方法的<code>if (ref == null)</code>时,你会发现 ref 不为空,导致你无法进入到 init 方法中继续调试。导致这个现象的原因是 Dubbo 框架本身有一些小问题。该问题已经在 pull request <a href=https://github.com/apache/dubbo/pull/2754>#2754</a> 修复了此问题,并跟随 2.6.5 版本发布了。如果你正在学习 2.6.4 及以下版本,可通过修改 IDEA 配置规避这个问题。首先 IDEA 配置弹窗中搜索 toString,然后取消<code>Enable 'toString' object view</code>勾选。具体如下:</p><p><img src=/imgs/docsv2.7/dev/source/refer-service/15417503733794.jpg alt=img></p><h3 id=31-处理配置>3.1 处理配置</h3><p>Dubbo 提供了丰富的配置,用于调整和优化框架行为,性能等。Dubbo 在引用或导出服务时,首先会对这些配置进行检查和处理,以保证配置的正确性。配置解析逻辑封装在 ReferenceConfig 的 init 方法中,下面进行分析。</p><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>private</span> <span style=color:#dc322f>void</span> <span style=color:#268bd2>init</span>() {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 避免重复初始化</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (initialized) {
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span>;
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> initialized <span style=color:#719e07>=</span> <span style=color:#cb4b16>true</span>;
</span></span><span style=display:flex><span> <span style=color:#586e75>// 检测接口名合法性</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (interfaceName <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span> <span style=color:#719e07>||</span> interfaceName.length() <span style=color:#719e07>==</span> 0) {
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> IllegalStateException(<span style=color:#2aa198>&#34;interface not allow null!&#34;</span>);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 检测 consumer 变量是否为空,为空则创建</span>
</span></span><span style=display:flex><span> checkDefault();
</span></span><span style=display:flex><span> appendProperties(<span style=color:#719e07>this</span>);
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (getGeneric() <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span> <span style=color:#719e07>&amp;&amp;</span> getConsumer() <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 设置 generic</span>
</span></span><span style=display:flex><span> setGeneric(getConsumer().getGeneric());
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 检测是否为泛化接口</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (ProtocolUtils.isGeneric(getGeneric())) {
</span></span><span style=display:flex><span> interfaceClass <span style=color:#719e07>=</span> GenericService.class;
</span></span><span style=display:flex><span> } <span style=color:#719e07>else</span> {
</span></span><span style=display:flex><span> <span style=color:#719e07>try</span> {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 加载类</span>
</span></span><span style=display:flex><span> interfaceClass <span style=color:#719e07>=</span> Class.forName(interfaceName, <span style=color:#cb4b16>true</span>, Thread.currentThread()
</span></span><span style=display:flex><span> .getContextClassLoader());
</span></span><span style=display:flex><span> } <span style=color:#719e07>catch</span> (ClassNotFoundException e) {
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> IllegalStateException(e.getMessage(), e);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> checkInterfaceAndMethods(interfaceClass, methods);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// -------------------------------✨ 分割线1 ✨------------------------------</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 从系统变量中获取与接口名对应的属性值</span>
</span></span><span style=display:flex><span> String resolve <span style=color:#719e07>=</span> System.getProperty(interfaceName);
</span></span><span style=display:flex><span> String resolveFile <span style=color:#719e07>=</span> <span style=color:#cb4b16>null</span>;
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (resolve <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span> <span style=color:#719e07>||</span> resolve.length() <span style=color:#719e07>==</span> 0) {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 从系统属性中获取解析文件路径</span>
</span></span><span style=display:flex><span> resolveFile <span style=color:#719e07>=</span> System.getProperty(<span style=color:#2aa198>&#34;dubbo.resolve.file&#34;</span>);
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (resolveFile <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span> <span style=color:#719e07>||</span> resolveFile.length() <span style=color:#719e07>==</span> 0) {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 从指定位置加载配置文件</span>
</span></span><span style=display:flex><span> File userResolveFile <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> File(<span style=color:#719e07>new</span> File(System.getProperty(<span style=color:#2aa198>&#34;user.home&#34;</span>)), <span style=color:#2aa198>&#34;dubbo-resolve.properties&#34;</span>);
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (userResolveFile.exists()) {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 获取文件绝对路径</span>
</span></span><span style=display:flex><span> resolveFile <span style=color:#719e07>=</span> userResolveFile.getAbsolutePath();
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (resolveFile <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span> <span style=color:#719e07>&amp;&amp;</span> resolveFile.length() <span style=color:#719e07>&gt;</span> 0) {
</span></span><span style=display:flex><span> Properties properties <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> Properties();
</span></span><span style=display:flex><span> FileInputStream fis <span style=color:#719e07>=</span> <span style=color:#cb4b16>null</span>;
</span></span><span style=display:flex><span> <span style=color:#719e07>try</span> {
</span></span><span style=display:flex><span> fis <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> FileInputStream(<span style=color:#719e07>new</span> File(resolveFile));
</span></span><span style=display:flex><span> <span style=color:#586e75>// 从文件中加载配置</span>
</span></span><span style=display:flex><span> properties.load(fis);
</span></span><span style=display:flex><span> } <span style=color:#719e07>catch</span> (IOException e) {
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> IllegalStateException(<span style=color:#2aa198>&#34;Unload ..., cause:...&#34;</span>);
</span></span><span style=display:flex><span> } <span style=color:#719e07>finally</span> {
</span></span><span style=display:flex><span> <span style=color:#719e07>try</span> {
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (<span style=color:#cb4b16>null</span> <span style=color:#719e07>!=</span> fis) fis.close();
</span></span><span style=display:flex><span> } <span style=color:#719e07>catch</span> (IOException e) {
</span></span><span style=display:flex><span> logger.warn(e.getMessage(), e);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> <span style=color:#586e75>// 获取与接口名对应的配置</span>
</span></span><span style=display:flex><span> resolve <span style=color:#719e07>=</span> properties.getProperty(interfaceName);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (resolve <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span> <span style=color:#719e07>&amp;&amp;</span> resolve.length() <span style=color:#719e07>&gt;</span> 0) {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 将 resolve 赋值给 url</span>
</span></span><span style=display:flex><span> url <span style=color:#719e07>=</span> resolve;
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// -------------------------------✨ 分割线2 ✨------------------------------</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (consumer <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (application <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 从 consumer 中获取 Application 实例,下同</span>
</span></span><span style=display:flex><span> application <span style=color:#719e07>=</span> consumer.getApplication();
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (module <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> module <span style=color:#719e07>=</span> consumer.getModule();
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (registries <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> registries <span style=color:#719e07>=</span> consumer.getRegistries();
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (monitor <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> monitor <span style=color:#719e07>=</span> consumer.getMonitor();
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (module <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (registries <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> registries <span style=color:#719e07>=</span> module.getRegistries();
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (monitor <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> monitor <span style=color:#719e07>=</span> module.getMonitor();
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (application <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (registries <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> registries <span style=color:#719e07>=</span> application.getRegistries();
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (monitor <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> monitor <span style=color:#719e07>=</span> application.getMonitor();
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 检测 Application 合法性</span>
</span></span><span style=display:flex><span> checkApplication();
</span></span><span style=display:flex><span> <span style=color:#586e75>// 检测本地存根配置合法性</span>
</span></span><span style=display:flex><span> checkStubAndMock(interfaceClass);
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// -------------------------------✨ 分割线3 ✨------------------------------</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> Map<span style=color:#719e07>&lt;</span>String, String<span style=color:#719e07>&gt;</span> map <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> HashMap<span style=color:#719e07>&lt;</span>String, String<span style=color:#719e07>&gt;</span>();
</span></span><span style=display:flex><span> Map<span style=color:#719e07>&lt;</span>Object, Object<span style=color:#719e07>&gt;</span> attributes <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> HashMap<span style=color:#719e07>&lt;</span>Object, Object<span style=color:#719e07>&gt;</span>();
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 添加 side、协议版本信息、时间戳和进程号等信息到 map 中</span>
</span></span><span style=display:flex><span> map.put(Constants.SIDE_KEY, Constants.CONSUMER_SIDE);
</span></span><span style=display:flex><span> map.put(Constants.DUBBO_VERSION_KEY, Version.getProtocolVersion());
</span></span><span style=display:flex><span> map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (ConfigUtils.getPid() <span style=color:#719e07>&gt;</span> 0) {
</span></span><span style=display:flex><span> map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid()));
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 非泛化服务</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (<span style=color:#719e07>!</span>isGeneric()) {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 获取版本</span>
</span></span><span style=display:flex><span> String revision <span style=color:#719e07>=</span> Version.getVersion(interfaceClass, version);
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (revision <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span> <span style=color:#719e07>&amp;&amp;</span> revision.length() <span style=color:#719e07>&gt;</span> 0) {
</span></span><span style=display:flex><span> map.put(<span style=color:#2aa198>&#34;revision&#34;</span>, revision);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 获取接口方法列表,并添加到 map 中</span>
</span></span><span style=display:flex><span> String<span style=color:#719e07>[]</span> methods <span style=color:#719e07>=</span> Wrapper.getWrapper(interfaceClass).getMethodNames();
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (methods.length <span style=color:#719e07>==</span> 0) {
</span></span><span style=display:flex><span> map.put(<span style=color:#2aa198>&#34;methods&#34;</span>, Constants.ANY_VALUE);
</span></span><span style=display:flex><span> } <span style=color:#719e07>else</span> {
</span></span><span style=display:flex><span> map.put(<span style=color:#2aa198>&#34;methods&#34;</span>, StringUtils.join(<span style=color:#719e07>new</span> HashSet<span style=color:#719e07>&lt;</span>String<span style=color:#719e07>&gt;</span>(Arrays.asList(methods)), <span style=color:#2aa198>&#34;,&#34;</span>));
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> map.put(Constants.INTERFACE_KEY, interfaceName);
</span></span><span style=display:flex><span> <span style=color:#586e75>// 将 ApplicationConfig、ConsumerConfig、ReferenceConfig 等对象的字段信息添加到 map 中</span>
</span></span><span style=display:flex><span> appendParameters(map, application);
</span></span><span style=display:flex><span> appendParameters(map, module);
</span></span><span style=display:flex><span> appendParameters(map, consumer, Constants.DEFAULT_KEY);
</span></span><span style=display:flex><span> appendParameters(map, <span style=color:#719e07>this</span>);
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// -------------------------------✨ 分割线4 ✨------------------------------</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> String prefix <span style=color:#719e07>=</span> StringUtils.getServiceKey(map);
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (methods <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span> <span style=color:#719e07>&amp;&amp;</span> <span style=color:#719e07>!</span>methods.isEmpty()) {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 遍历 MethodConfig 列表</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>for</span> (MethodConfig method : methods) {
</span></span><span style=display:flex><span> appendParameters(map, method, method.getName());
</span></span><span style=display:flex><span> String retryKey <span style=color:#719e07>=</span> method.getName() <span style=color:#719e07>+</span> <span style=color:#2aa198>&#34;.retry&#34;</span>;
</span></span><span style=display:flex><span> <span style=color:#586e75>// 检测 map 是否包含 methodName.retry</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (map.containsKey(retryKey)) {
</span></span><span style=display:flex><span> String retryValue <span style=color:#719e07>=</span> map.remove(retryKey);
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (<span style=color:#2aa198>&#34;false&#34;</span>.equals(retryValue)) {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 添加重试次数配置 methodName.retries</span>
</span></span><span style=display:flex><span> map.put(method.getName() <span style=color:#719e07>+</span> <span style=color:#2aa198>&#34;.retries&#34;</span>, <span style=color:#2aa198>&#34;0&#34;</span>);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 添加 MethodConfig 中的“属性”字段到 attributes</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 比如 onreturn、onthrow、oninvoke 等</span>
</span></span><span style=display:flex><span> appendAttributes(attributes, method, prefix <span style=color:#719e07>+</span> <span style=color:#2aa198>&#34;.&#34;</span> <span style=color:#719e07>+</span> method.getName());
</span></span><span style=display:flex><span> checkAndConvertImplicitConfig(method, map, attributes);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// -------------------------------✨ 分割线5 ✨------------------------------</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 获取服务消费者 ip 地址</span>
</span></span><span style=display:flex><span> String hostToRegistry <span style=color:#719e07>=</span> ConfigUtils.getSystemProperty(Constants.DUBBO_IP_TO_REGISTRY);
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (hostToRegistry <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span> <span style=color:#719e07>||</span> hostToRegistry.length() <span style=color:#719e07>==</span> 0) {
</span></span><span style=display:flex><span> hostToRegistry <span style=color:#719e07>=</span> NetUtils.getLocalHost();
</span></span><span style=display:flex><span> } <span style=color:#719e07>else</span> <span style=color:#719e07>if</span> (isInvalidLocalHost(hostToRegistry)) {
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> IllegalArgumentException(<span style=color:#2aa198>&#34;Specified invalid registry ip from property...&#34;</span> );
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> map.put(Constants.REGISTER_IP_KEY, hostToRegistry);
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 存储 attributes 到系统上下文中</span>
</span></span><span style=display:flex><span> StaticContext.getSystemContext().putAll(attributes);
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 创建代理类</span>
</span></span><span style=display:flex><span> ref <span style=color:#719e07>=</span> createProxy(map);
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 根据服务名,ReferenceConfig,代理类构建 ConsumerModel,</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 并将 ConsumerModel 存入到 ApplicationModel 中</span>
</span></span><span style=display:flex><span> ConsumerModel consumerModel <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> ConsumerModel(getUniqueServiceName(), <span style=color:#719e07>this</span>, ref, interfaceClass.getMethods());
</span></span><span style=display:flex><span> ApplicationModel.initConsumerModel(getUniqueServiceName(), consumerModel);
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><p>上面的代码很长,做的事情比较多。这里根据代码逻辑,对代码进行了分块,下面我们一起来看一下。</p><p>首先是方法开始到分割线1之间的代码。这段代码主要用于检测 ConsumerConfig 实例是否存在,如不存在则创建一个新的实例,然后通过系统变量或 dubbo.properties 配置文件填充 ConsumerConfig 的字段。接着是检测泛化配置,并根据配置设置 interfaceClass 的值。接着来看分割线1到分割线2之间的逻辑。这段逻辑用于从系统属性或配置文件中加载与接口名相对应的配置,并将解析结果赋值给 url 字段。url 字段的作用一般是用于点对点调用。继续向下看,分割线2和分割线3之间的代码用于检测几个核心配置类是否为空,为空则尝试从其他配置类中获取。分割线3与分割线4之间的代码主要用于收集各种配置,并将配置存储到 map 中。分割线4和分割线5之间的代码用于处理 MethodConfig 实例。该实例包含了事件通知配置,比如 onreturn、onthrow、oninvoke 等。分割线5到方法结尾的代码主要用于解析服务消费者 ip,以及调用 createProxy 创建代理对象。关于该方法的详细分析,将会在接下来的章节中展开。</p><h3 id=32-引用服务>3.2 引用服务</h3><p>本节我们要从 createProxy 开始看起。从字面意思上来看,createProxy 似乎只是用于创建代理对象的。但实际上并非如此,该方法还会调用其他方法构建以及合并 Invoker 实例。具体细节如下。</p><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>private</span> T <span style=color:#268bd2>createProxy</span>(Map<span style=color:#719e07>&lt;</span>String, String<span style=color:#719e07>&gt;</span> map) {
</span></span><span style=display:flex><span> URL tmpUrl <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> URL(<span style=color:#2aa198>&#34;temp&#34;</span>, <span style=color:#2aa198>&#34;localhost&#34;</span>, 0, map);
</span></span><span style=display:flex><span> <span style=color:#268bd2>final</span> <span style=color:#dc322f>boolean</span> isJvmRefer;
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (isInjvm() <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> <span style=color:#586e75>// url 配置被指定,则不做本地引用</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (url <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span> <span style=color:#719e07>&amp;&amp;</span> url.length() <span style=color:#719e07>&gt;</span> 0) {
</span></span><span style=display:flex><span> isJvmRefer <span style=color:#719e07>=</span> <span style=color:#cb4b16>false</span>;
</span></span><span style=display:flex><span> <span style=color:#586e75>// 根据 url 的协议、scope 以及 injvm 等参数检测是否需要本地引用</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 比如如果用户显式配置了 scope=local,此时 isInjvmRefer 返回 true</span>
</span></span><span style=display:flex><span> } <span style=color:#719e07>else</span> <span style=color:#719e07>if</span> (InjvmProtocol.getInjvmProtocol().isInjvmRefer(tmpUrl)) {
</span></span><span style=display:flex><span> isJvmRefer <span style=color:#719e07>=</span> <span style=color:#cb4b16>true</span>;
</span></span><span style=display:flex><span> } <span style=color:#719e07>else</span> {
</span></span><span style=display:flex><span> isJvmRefer <span style=color:#719e07>=</span> <span style=color:#cb4b16>false</span>;
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> } <span style=color:#719e07>else</span> {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 获取 injvm 配置值</span>
</span></span><span style=display:flex><span> isJvmRefer <span style=color:#719e07>=</span> isInjvm().booleanValue();
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 本地引用</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (isJvmRefer) {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 生成本地引用 URL,协议为 injvm</span>
</span></span><span style=display:flex><span> URL url <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> URL(Constants.LOCAL_PROTOCOL, NetUtils.LOCALHOST, 0, interfaceClass.getName()).addParameters(map);
</span></span><span style=display:flex><span> <span style=color:#586e75>// 调用 refer 方法构建 InjvmInvoker 实例</span>
</span></span><span style=display:flex><span> invoker <span style=color:#719e07>=</span> refprotocol.refer(interfaceClass, url);
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 远程引用</span>
</span></span><span style=display:flex><span> } <span style=color:#719e07>else</span> {
</span></span><span style=display:flex><span> <span style=color:#586e75>// url 不为空,表明用户可能想进行点对点调用</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (url <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span> <span style=color:#719e07>&amp;&amp;</span> url.length() <span style=color:#719e07>&gt;</span> 0) {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 当需要配置多个 url 时,可用分号进行分割,这里会进行切分</span>
</span></span><span style=display:flex><span> String<span style=color:#719e07>[]</span> us <span style=color:#719e07>=</span> Constants.SEMICOLON_SPLIT_PATTERN.split(url);
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (us <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span> <span style=color:#719e07>&amp;&amp;</span> us.length <span style=color:#719e07>&gt;</span> 0) {
</span></span><span style=display:flex><span> <span style=color:#719e07>for</span> (String u : us) {
</span></span><span style=display:flex><span> URL url <span style=color:#719e07>=</span> URL.valueOf(u);
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (url.getPath() <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span> <span style=color:#719e07>||</span> url.getPath().length() <span style=color:#719e07>==</span> 0) {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 设置接口全限定名为 url 路径</span>
</span></span><span style=display:flex><span> url <span style=color:#719e07>=</span> url.setPath(interfaceName);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 检测 url 协议是否为 registry,若是,表明用户想使用指定的注册中心</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 将 map 转换为查询字符串,并作为 refer 参数的值添加到 url 中</span>
</span></span><span style=display:flex><span> urls.add(url.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
</span></span><span style=display:flex><span> } <span style=color:#719e07>else</span> {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 合并 url,移除服务提供者的一些配置(这些配置来源于用户配置的 url 属性),</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 比如线程池相关配置。并保留服务提供者的部分配置,比如版本,group,时间戳等</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 最后将合并后的配置设置为 url 查询字符串中。</span>
</span></span><span style=display:flex><span> urls.add(ClusterUtils.mergeUrl(url, map));
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> } <span style=color:#719e07>else</span> {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 加载注册中心 url</span>
</span></span><span style=display:flex><span> List<span style=color:#719e07>&lt;</span>URL<span style=color:#719e07>&gt;</span> us <span style=color:#719e07>=</span> loadRegistries(<span style=color:#cb4b16>false</span>);
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (us <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span> <span style=color:#719e07>&amp;&amp;</span> <span style=color:#719e07>!</span>us.isEmpty()) {
</span></span><span style=display:flex><span> <span style=color:#719e07>for</span> (URL u : us) {
</span></span><span style=display:flex><span> URL monitorUrl <span style=color:#719e07>=</span> loadMonitor(u);
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (monitorUrl <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> <span style=color:#586e75>// 添加 refer 参数到 url 中,并将 url 添加到 urls 中</span>
</span></span><span style=display:flex><span> urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 未配置注册中心,抛出异常</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (urls.isEmpty()) {
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> IllegalStateException(<span style=color:#2aa198>&#34;No such any registry to reference...&#34;</span>);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 单个注册中心或服务提供者(服务直连,下同)</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (urls.size() <span style=color:#719e07>==</span> 1) {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 调用 RegistryProtocol 的 refer 构建 Invoker 实例</span>
</span></span><span style=display:flex><span> invoker <span style=color:#719e07>=</span> refprotocol.refer(interfaceClass, urls.get(0));
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 多个注册中心或多个服务提供者,或者两者混合</span>
</span></span><span style=display:flex><span> } <span style=color:#719e07>else</span> {
</span></span><span style=display:flex><span> List<span style=color:#719e07>&lt;</span>Invoker<span style=color:#719e07>&lt;?&gt;&gt;</span> invokers <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> ArrayList<span style=color:#719e07>&lt;</span>Invoker<span style=color:#719e07>&lt;?&gt;&gt;</span>();
</span></span><span style=display:flex><span> URL registryURL <span style=color:#719e07>=</span> <span style=color:#cb4b16>null</span>;
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 获取所有的 Invoker</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>for</span> (URL url : urls) {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 通过 refprotocol 调用 refer 构建 Invoker,refprotocol 会在运行时</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 根据 url 协议头加载指定的 Protocol 实例,并调用实例的 refer 方法</span>
</span></span><span style=display:flex><span> invokers.add(refprotocol.refer(interfaceClass, url));
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
</span></span><span style=display:flex><span> registryURL <span style=color:#719e07>=</span> url;
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (registryURL <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 如果注册中心链接不为空,则将使用 AvailableCluster</span>
</span></span><span style=display:flex><span> URL u <span style=color:#719e07>=</span> registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME);
</span></span><span style=display:flex><span> <span style=color:#586e75>// 创建 StaticDirectory 实例,并由 Cluster 对多个 Invoker 进行合并</span>
</span></span><span style=display:flex><span> invoker <span style=color:#719e07>=</span> cluster.join(<span style=color:#719e07>new</span> StaticDirectory(u, invokers));
</span></span><span style=display:flex><span> } <span style=color:#719e07>else</span> {
</span></span><span style=display:flex><span> invoker <span style=color:#719e07>=</span> cluster.join(<span style=color:#719e07>new</span> StaticDirectory(invokers));
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> Boolean c <span style=color:#719e07>=</span> check;
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (c <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span> <span style=color:#719e07>&amp;&amp;</span> consumer <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> c <span style=color:#719e07>=</span> consumer.isCheck();
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (c <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> c <span style=color:#719e07>=</span> <span style=color:#cb4b16>true</span>;
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// invoker 可用性检查</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (c <span style=color:#719e07>&amp;&amp;</span> <span style=color:#719e07>!</span>invoker.isAvailable()) {
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> IllegalStateException(<span style=color:#2aa198>&#34;No provider available for the service...&#34;</span>);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 生成代理类</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> (T) proxyFactory.getProxy(invoker);
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><p>上面代码很多,不过逻辑比较清晰。首先根据配置检查是否为本地调用,若是,则调用 InjvmProtocol 的 refer 方法生成 InjvmInvoker 实例。若不是,则读取直连配置项,或注册中心 url,并将读取到的 url 存储到 urls 中。然后根据 urls 元素数量进行后续操作。若 urls 元素数量为1,则直接通过 Protocol 自适应拓展类构建 Invoker 实例接口。若 urls 元素数量大于1,即存在多个注册中心或服务直连 url,此时先根据 url 构建 Invoker。然后再通过 Cluster 合并多个 Invoker,最后调用 ProxyFactory 生成代理类。Invoker 的构建过程以及代理类的过程比较重要,因此接下来将分两小节对这两个过程进行分析。</p><h4 id=321-创建-invoker>3.2.1 创建 Invoker</h4><p>Invoker 是 Dubbo 的核心模型,代表一个可执行体。在服务提供方,Invoker 用于调用服务提供类。在服务消费方,Invoker 用于执行远程调用。Invoker 是由 Protocol 实现类构建而来。Protocol 实现类有很多,本节会分析最常用的两个,分别是 RegistryProtocol 和 DubboProtocol,其他的大家自行分析。下面先来分析 DubboProtocol 的 refer 方法源码。如下:</p><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>public</span> <span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> Invoker<span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> <span style=color:#268bd2>refer</span>(Class<span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> serviceType, URL url) <span style=color:#268bd2>throws</span> RpcException {
</span></span><span style=display:flex><span> optimizeSerialization(url);
</span></span><span style=display:flex><span> <span style=color:#586e75>// 创建 DubboInvoker</span>
</span></span><span style=display:flex><span> DubboInvoker<span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> invoker <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> DubboInvoker<span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span>(serviceType, url, getClients(url), invokers);
</span></span><span style=display:flex><span> invokers.add(invoker);
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> invoker;
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><p>上面方法看起来比较简单,不过这里有一个调用需要我们注意一下,即 getClients。这个方法用于获取客户端实例,实例类型为 ExchangeClient。ExchangeClient 实际上并不具备通信能力,它需要基于更底层的客户端实例进行通信。比如 NettyClient、MinaClient 等,默认情况下,Dubbo 使用 NettyClient 进行通信。接下来,我们简单看一下 getClients 方法的逻辑。</p><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>private</span> ExchangeClient<span style=color:#719e07>[]</span> <span style=color:#268bd2>getClients</span>(URL url) {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 是否共享连接</span>
</span></span><span style=display:flex><span> <span style=color:#dc322f>boolean</span> service_share_connect <span style=color:#719e07>=</span> <span style=color:#cb4b16>false</span>;
</span></span><span style=display:flex><span> <span style=color:#586e75>// 获取连接数,默认为0,表示未配置</span>
</span></span><span style=display:flex><span> <span style=color:#dc322f>int</span> connections <span style=color:#719e07>=</span> url.getParameter(Constants.CONNECTIONS_KEY, 0);
</span></span><span style=display:flex><span> <span style=color:#586e75>// 如果未配置 connections,则共享连接</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (connections <span style=color:#719e07>==</span> 0) {
</span></span><span style=display:flex><span> service_share_connect <span style=color:#719e07>=</span> <span style=color:#cb4b16>true</span>;
</span></span><span style=display:flex><span> connections <span style=color:#719e07>=</span> 1;
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> ExchangeClient<span style=color:#719e07>[]</span> clients <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> ExchangeClient<span style=color:#719e07>[</span>connections<span style=color:#719e07>]</span>;
</span></span><span style=display:flex><span> <span style=color:#719e07>for</span> (<span style=color:#dc322f>int</span> i <span style=color:#719e07>=</span> 0; i <span style=color:#719e07>&lt;</span> clients.length; i<span style=color:#719e07>++</span>) {
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (service_share_connect) {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 获取共享客户端</span>
</span></span><span style=display:flex><span> clients<span style=color:#719e07>[</span>i<span style=color:#719e07>]</span> <span style=color:#719e07>=</span> getSharedClient(url);
</span></span><span style=display:flex><span> } <span style=color:#719e07>else</span> {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 初始化新的客户端</span>
</span></span><span style=display:flex><span> clients<span style=color:#719e07>[</span>i<span style=color:#719e07>]</span> <span style=color:#719e07>=</span> initClient(url);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> clients;
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><p>这里根据 connections 数量决定是获取共享客户端还是创建新的客户端实例,默认情况下,使用共享客户端实例。getSharedClient 方法中也会调用 initClient 方法,因此下面我们一起看一下这两个方法。</p><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>private</span> ExchangeClient <span style=color:#268bd2>getSharedClient</span>(URL url) {
</span></span><span style=display:flex><span> String key <span style=color:#719e07>=</span> url.getAddress();
</span></span><span style=display:flex><span> <span style=color:#586e75>// 获取带有“引用计数”功能的 ExchangeClient</span>
</span></span><span style=display:flex><span> ReferenceCountExchangeClient client <span style=color:#719e07>=</span> referenceClientMap.get(key);
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (client <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (<span style=color:#719e07>!</span>client.isClosed()) {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 增加引用计数</span>
</span></span><span style=display:flex><span> client.incrementAndGetCount();
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> client;
</span></span><span style=display:flex><span> } <span style=color:#719e07>else</span> {
</span></span><span style=display:flex><span> referenceClientMap.remove(key);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> locks.putIfAbsent(key, <span style=color:#719e07>new</span> Object());
</span></span><span style=display:flex><span> <span style=color:#268bd2>synchronized</span> (locks.get(key)) {
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (referenceClientMap.containsKey(key)) {
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> referenceClientMap.get(key);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 创建 ExchangeClient 客户端</span>
</span></span><span style=display:flex><span> ExchangeClient exchangeClient <span style=color:#719e07>=</span> initClient(url);
</span></span><span style=display:flex><span> <span style=color:#586e75>// 将 ExchangeClient 实例传给 ReferenceCountExchangeClient,这里使用了装饰模式</span>
</span></span><span style=display:flex><span> client <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> ReferenceCountExchangeClient(exchangeClient, ghostClientMap);
</span></span><span style=display:flex><span> referenceClientMap.put(key, client);
</span></span><span style=display:flex><span> ghostClientMap.remove(key);
</span></span><span style=display:flex><span> locks.remove(key);
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> client;
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><p>上面方法先访问缓存,若缓存未命中,则通过 initClient 方法创建新的 ExchangeClient 实例,并将该实例传给 ReferenceCountExchangeClient 构造方法创建一个带有引用计数功能的 ExchangeClient 实例。ReferenceCountExchangeClient 内部实现比较简单,就不分析了。下面我们再来看一下 initClient 方法的代码。</p><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>private</span> ExchangeClient <span style=color:#268bd2>initClient</span>(URL url) {
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 获取客户端类型,默认为 netty</span>
</span></span><span style=display:flex><span> String str <span style=color:#719e07>=</span> url.getParameter(Constants.CLIENT_KEY, url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_CLIENT));
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 添加编解码和心跳包参数到 url 中</span>
</span></span><span style=display:flex><span> url <span style=color:#719e07>=</span> url.addParameter(Constants.CODEC_KEY, DubboCodec.NAME);
</span></span><span style=display:flex><span> url <span style=color:#719e07>=</span> url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 检测客户端类型是否存在,不存在则抛出异常</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (str <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span> <span style=color:#719e07>&amp;&amp;</span> str.length() <span style=color:#719e07>&gt;</span> 0 <span style=color:#719e07>&amp;&amp;</span> <span style=color:#719e07>!</span>ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) {
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> RpcException(<span style=color:#2aa198>&#34;Unsupported client type: ...&#34;</span>);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> ExchangeClient client;
</span></span><span style=display:flex><span> <span style=color:#719e07>try</span> {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 获取 lazy 配置,并根据配置值决定创建的客户端类型</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (url.getParameter(Constants.LAZY_CONNECT_KEY, <span style=color:#cb4b16>false</span>)) {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 创建懒加载 ExchangeClient 实例</span>
</span></span><span style=display:flex><span> client <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> LazyConnectExchangeClient(url, requestHandler);
</span></span><span style=display:flex><span> } <span style=color:#719e07>else</span> {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 创建普通 ExchangeClient 实例</span>
</span></span><span style=display:flex><span> client <span style=color:#719e07>=</span> Exchangers.connect(url, requestHandler);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> } <span style=color:#719e07>catch</span> (RemotingException e) {
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> RpcException(<span style=color:#2aa198>&#34;Fail to create remoting client for service...&#34;</span>);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> client;
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><p>initClient 方法首先获取用户配置的客户端类型,默认为 netty。然后检测用户配置的客户端类型是否存在,不存在则抛出异常。最后根据 lazy 配置决定创建什么类型的客户端。这里的 LazyConnectExchangeClient 代码并不是很复杂,该类会在 request 方法被调用时通过 Exchangers 的 connect 方法创建 ExchangeClient 客户端,该类的代码本节就不分析了。下面我们分析一下 Exchangers 的 connect 方法。</p><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>public</span> <span style=color:#268bd2>static</span> ExchangeClient <span style=color:#268bd2>connect</span>(URL url, ExchangeHandler handler) <span style=color:#268bd2>throws</span> RemotingException {
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (url <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> IllegalArgumentException(<span style=color:#2aa198>&#34;url == null&#34;</span>);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (handler <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> IllegalArgumentException(<span style=color:#2aa198>&#34;handler == null&#34;</span>);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> url <span style=color:#719e07>=</span> url.addParameterIfAbsent(Constants.CODEC_KEY, <span style=color:#2aa198>&#34;exchange&#34;</span>);
</span></span><span style=display:flex><span> <span style=color:#586e75>// 获取 Exchanger 实例,默认为 HeaderExchangeClient</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> getExchanger(url).connect(url, handler);
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><p>如上,getExchanger 会通过 SPI 加载 HeaderExchanger 实例,这个方法比较简单,大家自己看一下吧。接下来分析 HeaderExchanger.connect 的实现。</p><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>public</span> ExchangeClient <span style=color:#268bd2>connect</span>(URL url, ExchangeHandler handler) <span style=color:#268bd2>throws</span> RemotingException {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 这里包含了多个调用,分别如下:</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 1. 创建 HeaderExchangeHandler 对象</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 2. 创建 DecodeHandler 对象</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 3. 通过 Transporters 构建 Client 实例</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 4. 创建 HeaderExchangeClient 对象</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> <span style=color:#719e07>new</span> HeaderExchangeClient(Transporters.connect(url, <span style=color:#719e07>new</span> DecodeHandler(<span style=color:#719e07>new</span> HeaderExchangeHandler(handler))), <span style=color:#cb4b16>true</span>);
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><p>这里的调用比较多,我们这里重点看一下 Transporters 的 connect 方法。如下:</p><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>public</span> <span style=color:#268bd2>static</span> Client <span style=color:#268bd2>connect</span>(URL url, ChannelHandler... handlers) <span style=color:#268bd2>throws</span> RemotingException {
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (url <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> IllegalArgumentException(<span style=color:#2aa198>&#34;url == null&#34;</span>);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> ChannelHandler handler;
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (handlers <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span> <span style=color:#719e07>||</span> handlers.length <span style=color:#719e07>==</span> 0) {
</span></span><span style=display:flex><span> handler <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> ChannelHandlerAdapter();
</span></span><span style=display:flex><span> } <span style=color:#719e07>else</span> <span style=color:#719e07>if</span> (handlers.length <span style=color:#719e07>==</span> 1) {
</span></span><span style=display:flex><span> handler <span style=color:#719e07>=</span> handlers<span style=color:#719e07>[</span>0<span style=color:#719e07>]</span>;
</span></span><span style=display:flex><span> } <span style=color:#719e07>else</span> {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 如果 handler 数量大于1,则创建一个 ChannelHandler 分发器</span>
</span></span><span style=display:flex><span> handler <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> ChannelHandlerDispatcher(handlers);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 获取 Transporter 自适应拓展类,并调用 connect 方法生成 Client 实例</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> getTransporter().connect(url, handler);
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><p>如上,getTransporter 方法返回的是自适应拓展类,该类会在运行时根据客户端类型加载指定的 Transporter 实现类。若用户未配置客户端类型,则默认加载 NettyTransporter,并调用该类的 connect 方法。如下:</p><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>public</span> Client <span style=color:#268bd2>connect</span>(URL url, ChannelHandler listener) <span style=color:#268bd2>throws</span> RemotingException {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 创建 NettyClient 对象</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> <span style=color:#719e07>new</span> NettyClient(url, listener);
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><p>到这里就不继续跟下去了,在往下就是通过 Netty 提供的 API 构建 Netty 客户端了,大家有兴趣自己看看。到这里,关于 DubboProtocol 的 refer 方法就分析完了。接下来,继续分析 RegistryProtocol 的 refer 方法逻辑。</p><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>public</span> <span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> Invoker<span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> <span style=color:#268bd2>refer</span>(Class<span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> type, URL url) <span style=color:#268bd2>throws</span> RpcException {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 取 registry 参数值,并将其设置为协议头</span>
</span></span><span style=display:flex><span> url <span style=color:#719e07>=</span> url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY)).removeParameter(Constants.REGISTRY_KEY);
</span></span><span style=display:flex><span> <span style=color:#586e75>// 获取注册中心实例</span>
</span></span><span style=display:flex><span> Registry registry <span style=color:#719e07>=</span> registryFactory.getRegistry(url);
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (RegistryService.class.equals(type)) {
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> proxyFactory.getInvoker((T) registry, type, url);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 将 url 查询字符串转为 Map</span>
</span></span><span style=display:flex><span> Map<span style=color:#719e07>&lt;</span>String, String<span style=color:#719e07>&gt;</span> qs <span style=color:#719e07>=</span> StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));
</span></span><span style=display:flex><span> <span style=color:#586e75>// 获取 group 配置</span>
</span></span><span style=display:flex><span> String group <span style=color:#719e07>=</span> qs.get(Constants.GROUP_KEY);
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (group <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span> <span style=color:#719e07>&amp;&amp;</span> group.length() <span style=color:#719e07>&gt;</span> 0) {
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> ((Constants.COMMA_SPLIT_PATTERN.split(group)).length <span style=color:#719e07>&gt;</span> 1
</span></span><span style=display:flex><span> <span style=color:#719e07>||</span> <span style=color:#2aa198>&#34;*&#34;</span>.equals(group)) {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 通过 SPI 加载 MergeableCluster 实例,并调用 doRefer 继续执行服务引用逻辑</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> doRefer(getMergeableCluster(), registry, type, url);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 调用 doRefer 继续执行服务引用逻辑</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> doRefer(cluster, registry, type, url);
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><p>上面代码首先为 url 设置协议头,然后根据 url 参数加载注册中心实例。然后获取 group 配置,根据 group 配置决定 doRefer 第一个参数的类型。这里的重点是 doRefer 方法,如下:</p><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>private</span> <span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> Invoker<span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> <span style=color:#268bd2>doRefer</span>(Cluster cluster, Registry registry, Class<span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> type, URL url) {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 创建 RegistryDirectory 实例</span>
</span></span><span style=display:flex><span> RegistryDirectory<span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> directory <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> RegistryDirectory<span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span>(type, url);
</span></span><span style=display:flex><span> <span style=color:#586e75>// 设置注册中心和协议</span>
</span></span><span style=display:flex><span> directory.setRegistry(registry);
</span></span><span style=display:flex><span> directory.setProtocol(protocol);
</span></span><span style=display:flex><span> Map<span style=color:#719e07>&lt;</span>String, String<span style=color:#719e07>&gt;</span> parameters <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> HashMap<span style=color:#719e07>&lt;</span>String, String<span style=color:#719e07>&gt;</span>(directory.getUrl().getParameters());
</span></span><span style=display:flex><span> <span style=color:#586e75>// 生成服务消费者链接</span>
</span></span><span style=display:flex><span> URL subscribeUrl <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> URL(Constants.CONSUMER_PROTOCOL, parameters.remove(Constants.REGISTER_IP_KEY), 0, type.getName(), parameters);
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 注册服务消费者,在 consumers 目录下新节点</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (<span style=color:#719e07>!</span>Constants.ANY_VALUE.equals(url.getServiceInterface())
</span></span><span style=display:flex><span> <span style=color:#719e07>&amp;&amp;</span> url.getParameter(Constants.REGISTER_KEY, <span style=color:#cb4b16>true</span>)) {
</span></span><span style=display:flex><span> registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY,
</span></span><span style=display:flex><span> Constants.CHECK_KEY, String.valueOf(<span style=color:#cb4b16>false</span>)));
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 订阅 providers、configurators、routers 等节点数据</span>
</span></span><span style=display:flex><span> directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,
</span></span><span style=display:flex><span> Constants.PROVIDERS_CATEGORY
</span></span><span style=display:flex><span> <span style=color:#719e07>+</span> <span style=color:#2aa198>&#34;,&#34;</span> <span style=color:#719e07>+</span> Constants.CONFIGURATORS_CATEGORY
</span></span><span style=display:flex><span> <span style=color:#719e07>+</span> <span style=color:#2aa198>&#34;,&#34;</span> <span style=color:#719e07>+</span> Constants.ROUTERS_CATEGORY));
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 一个注册中心可能有多个服务提供者,因此这里需要将多个服务提供者合并为一个</span>
</span></span><span style=display:flex><span> Invoker invoker <span style=color:#719e07>=</span> cluster.join(directory);
</span></span><span style=display:flex><span> ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> invoker;
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><p>如上,doRefer 方法创建一个 RegistryDirectory 实例,然后生成服务者消费者链接,并向注册中心进行注册。注册完毕后,紧接着订阅 providers、configurators、routers 等节点下的数据。完成订阅后,RegistryDirectory 会收到这几个节点下的子节点信息。由于一个服务可能部署在多台服务器上,这样就会在 providers 产生多个节点,这个时候就需要 Cluster 将多个服务节点合并为一个,并生成一个 Invoker。关于 RegistryDirectory 和 Cluster,本文不打算进行分析,相关分析将会在随后的文章中展开。</p><h4 id=322-创建代理>3.2.2 创建代理</h4><p>Invoker 创建完毕后,接下来要做的事情是为服务接口生成代理对象。有了代理对象,即可进行远程调用。代理对象生成的入口方法为 ProxyFactory 的 getProxy,接下来进行分析。</p><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>public</span> <span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> T <span style=color:#268bd2>getProxy</span>(Invoker<span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> invoker) <span style=color:#268bd2>throws</span> RpcException {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 调用重载方法</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> getProxy(invoker, <span style=color:#cb4b16>false</span>);
</span></span><span style=display:flex><span>}
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span><span style=color:#268bd2>public</span> <span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> T <span style=color:#268bd2>getProxy</span>(Invoker<span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> invoker, <span style=color:#dc322f>boolean</span> generic) <span style=color:#268bd2>throws</span> RpcException {
</span></span><span style=display:flex><span> Class<span style=color:#719e07>&lt;?&gt;[]</span> interfaces <span style=color:#719e07>=</span> <span style=color:#cb4b16>null</span>;
</span></span><span style=display:flex><span> <span style=color:#586e75>// 获取接口列表</span>
</span></span><span style=display:flex><span> String config <span style=color:#719e07>=</span> invoker.getUrl().getParameter(<span style=color:#2aa198>&#34;interfaces&#34;</span>);
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (config <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span> <span style=color:#719e07>&amp;&amp;</span> config.length() <span style=color:#719e07>&gt;</span> 0) {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 切分接口列表</span>
</span></span><span style=display:flex><span> String<span style=color:#719e07>[]</span> types <span style=color:#719e07>=</span> Constants.COMMA_SPLIT_PATTERN.split(config);
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (types <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span> <span style=color:#719e07>&amp;&amp;</span> types.length <span style=color:#719e07>&gt;</span> 0) {
</span></span><span style=display:flex><span> interfaces <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> Class<span style=color:#719e07>&lt;?&gt;[</span>types.length <span style=color:#719e07>+</span> 2<span style=color:#719e07>]</span>;
</span></span><span style=display:flex><span> <span style=color:#586e75>// 设置服务接口类和 EchoService.class 到 interfaces 中</span>
</span></span><span style=display:flex><span> interfaces<span style=color:#719e07>[</span>0<span style=color:#719e07>]</span> <span style=color:#719e07>=</span> invoker.getInterface();
</span></span><span style=display:flex><span> interfaces<span style=color:#719e07>[</span>1<span style=color:#719e07>]</span> <span style=color:#719e07>=</span> EchoService.class;
</span></span><span style=display:flex><span> <span style=color:#719e07>for</span> (<span style=color:#dc322f>int</span> i <span style=color:#719e07>=</span> 0; i <span style=color:#719e07>&lt;</span> types.length; i<span style=color:#719e07>++</span>) {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 加载接口类</span>
</span></span><span style=display:flex><span> interfaces<span style=color:#719e07>[</span>i <span style=color:#719e07>+</span> 1<span style=color:#719e07>]</span> <span style=color:#719e07>=</span> ReflectUtils.forName(types<span style=color:#719e07>[</span>i<span style=color:#719e07>]</span>);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (interfaces <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> interfaces <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> Class<span style=color:#719e07>&lt;?&gt;[]</span>{invoker.getInterface(), EchoService.class};
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 为 http 和 hessian 协议提供泛化调用支持,参考 pull request #1827</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (<span style=color:#719e07>!</span>invoker.getInterface().equals(GenericService.class) <span style=color:#719e07>&amp;&amp;</span> generic) {
</span></span><span style=display:flex><span> <span style=color:#dc322f>int</span> len <span style=color:#719e07>=</span> interfaces.length;
</span></span><span style=display:flex><span> Class<span style=color:#719e07>&lt;?&gt;[]</span> temp <span style=color:#719e07>=</span> interfaces;
</span></span><span style=display:flex><span> <span style=color:#586e75>// 创建新的 interfaces 数组</span>
</span></span><span style=display:flex><span> interfaces <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> Class<span style=color:#719e07>&lt;?&gt;[</span>len <span style=color:#719e07>+</span> 1<span style=color:#719e07>]</span>;
</span></span><span style=display:flex><span> System.arraycopy(temp, 0, interfaces, 0, len);
</span></span><span style=display:flex><span> <span style=color:#586e75>// 设置 GenericService.class 到数组中</span>
</span></span><span style=display:flex><span> interfaces<span style=color:#719e07>[</span>len<span style=color:#719e07>]</span> <span style=color:#719e07>=</span> GenericService.class;
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 调用重载方法</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> getProxy(invoker, interfaces);
</span></span><span style=display:flex><span>}
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span><span style=color:#268bd2>public</span> <span style=color:#268bd2>abstract</span> <span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> T <span style=color:#268bd2>getProxy</span>(Invoker<span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> invoker, Class<span style=color:#719e07>&lt;?&gt;[]</span> types);
</span></span></code></pre></div><p>如上,上面大段代码都是用来获取 interfaces 数组的,我们继续往下看。getProxy(Invoker, Class[]) 这个方法是一个抽象方法,下面我们到 JavassistProxyFactory 类中看一下该方法的实现代码。</p><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>public</span> <span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> T <span style=color:#268bd2>getProxy</span>(Invoker<span style=color:#719e07>&lt;</span>T<span style=color:#719e07>&gt;</span> invoker, Class<span style=color:#719e07>&lt;?&gt;[]</span> interfaces) {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 生成 Proxy 子类(Proxy 是抽象类)。并调用 Proxy 子类的 newInstance 方法创建 Proxy 实例</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> (T) Proxy.getProxy(interfaces).newInstance(<span style=color:#719e07>new</span> InvokerInvocationHandler(invoker));
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><p>上面代码并不多,首先是通过 Proxy 的 getProxy 方法获取 Proxy 子类,然后创建 InvokerInvocationHandler 对象,并将该对象传给 newInstance 生成 Proxy 实例。InvokerInvocationHandler 实现 JDK 的 InvocationHandler 接口,具体的用途是拦截接口类调用。该类逻辑比较简单,这里就不分析了。下面我们重点关注一下 Proxy 的 getProxy 方法,如下。</p><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#268bd2>public</span> <span style=color:#268bd2>static</span> Proxy <span style=color:#268bd2>getProxy</span>(Class<span style=color:#719e07>&lt;?&gt;</span>... ics) {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 调用重载方法</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> getProxy(ClassHelper.getClassLoader(Proxy.class), ics);
</span></span><span style=display:flex><span>}
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span><span style=color:#268bd2>public</span> <span style=color:#268bd2>static</span> Proxy <span style=color:#268bd2>getProxy</span>(ClassLoader cl, Class<span style=color:#719e07>&lt;?&gt;</span>... ics) {
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (ics.length <span style=color:#719e07>&gt;</span> 65535)
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> IllegalArgumentException(<span style=color:#2aa198>&#34;interface limit exceeded&#34;</span>);
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> StringBuilder sb <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> StringBuilder();
</span></span><span style=display:flex><span> <span style=color:#586e75>// 遍历接口列表</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>for</span> (<span style=color:#dc322f>int</span> i <span style=color:#719e07>=</span> 0; i <span style=color:#719e07>&lt;</span> ics.length; i<span style=color:#719e07>++</span>) {
</span></span><span style=display:flex><span> String itf <span style=color:#719e07>=</span> ics<span style=color:#719e07>[</span>i<span style=color:#719e07>]</span>.getName();
</span></span><span style=display:flex><span> <span style=color:#586e75>// 检测类型是否为接口</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (<span style=color:#719e07>!</span>ics<span style=color:#719e07>[</span>i<span style=color:#719e07>]</span>.isInterface())
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> RuntimeException(itf <span style=color:#719e07>+</span> <span style=color:#2aa198>&#34; is not a interface.&#34;</span>);
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> Class<span style=color:#719e07>&lt;?&gt;</span> tmp <span style=color:#719e07>=</span> <span style=color:#cb4b16>null</span>;
</span></span><span style=display:flex><span> <span style=color:#719e07>try</span> {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 重新加载接口类</span>
</span></span><span style=display:flex><span> tmp <span style=color:#719e07>=</span> Class.forName(itf, <span style=color:#cb4b16>false</span>, cl);
</span></span><span style=display:flex><span> } <span style=color:#719e07>catch</span> (ClassNotFoundException e) {
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 检测接口是否相同,这里 tmp 有可能为空</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (tmp <span style=color:#719e07>!=</span> ics<span style=color:#719e07>[</span>i<span style=color:#719e07>]</span>)
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> IllegalArgumentException(ics<span style=color:#719e07>[</span>i<span style=color:#719e07>]</span> <span style=color:#719e07>+</span> <span style=color:#2aa198>&#34; is not visible from class loader&#34;</span>);
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 拼接接口全限定名,分隔符为 ;</span>
</span></span><span style=display:flex><span> sb.append(itf).append(<span style=color:#2aa198>&#39;;&#39;</span>);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 使用拼接后的接口名作为 key</span>
</span></span><span style=display:flex><span> String key <span style=color:#719e07>=</span> sb.toString();
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> Map<span style=color:#719e07>&lt;</span>String, Object<span style=color:#719e07>&gt;</span> cache;
</span></span><span style=display:flex><span> <span style=color:#268bd2>synchronized</span> (ProxyCacheMap) {
</span></span><span style=display:flex><span> cache <span style=color:#719e07>=</span> ProxyCacheMap.get(cl);
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (cache <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> cache <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> HashMap<span style=color:#719e07>&lt;</span>String, Object<span style=color:#719e07>&gt;</span>();
</span></span><span style=display:flex><span> ProxyCacheMap.put(cl, cache);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> Proxy proxy <span style=color:#719e07>=</span> <span style=color:#cb4b16>null</span>;
</span></span><span style=display:flex><span> <span style=color:#268bd2>synchronized</span> (cache) {
</span></span><span style=display:flex><span> <span style=color:#719e07>do</span> {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 从缓存中获取 Reference&lt;Proxy&gt; 实例</span>
</span></span><span style=display:flex><span> Object value <span style=color:#719e07>=</span> cache.get(key);
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (value <span style=color:#719e07>instanceof</span> Reference<span style=color:#719e07>&lt;?&gt;</span>) {
</span></span><span style=display:flex><span> proxy <span style=color:#719e07>=</span> (Proxy) ((Reference<span style=color:#719e07>&lt;?&gt;</span>) value).get();
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (proxy <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> proxy;
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 并发控制,保证只有一个线程可以进行后续操作</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (value <span style=color:#719e07>==</span> PendingGenerationMarker) {
</span></span><span style=display:flex><span> <span style=color:#719e07>try</span> {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 其他线程在此处进行等待</span>
</span></span><span style=display:flex><span> cache.wait();
</span></span><span style=display:flex><span> } <span style=color:#719e07>catch</span> (InterruptedException e) {
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> } <span style=color:#719e07>else</span> {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 放置标志位到缓存中,并跳出 while 循环进行后续操作</span>
</span></span><span style=display:flex><span> cache.put(key, PendingGenerationMarker);
</span></span><span style=display:flex><span> <span style=color:#719e07>break</span>;
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> <span style=color:#719e07>while</span> (<span style=color:#cb4b16>true</span>);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#dc322f>long</span> id <span style=color:#719e07>=</span> PROXY_CLASS_COUNTER.getAndIncrement();
</span></span><span style=display:flex><span> String pkg <span style=color:#719e07>=</span> <span style=color:#cb4b16>null</span>;
</span></span><span style=display:flex><span> ClassGenerator ccp <span style=color:#719e07>=</span> <span style=color:#cb4b16>null</span>, ccm <span style=color:#719e07>=</span> <span style=color:#cb4b16>null</span>;
</span></span><span style=display:flex><span> <span style=color:#719e07>try</span> {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 创建 ClassGenerator 对象</span>
</span></span><span style=display:flex><span> ccp <span style=color:#719e07>=</span> ClassGenerator.newInstance(cl);
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> Set<span style=color:#719e07>&lt;</span>String<span style=color:#719e07>&gt;</span> worked <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> HashSet<span style=color:#719e07>&lt;</span>String<span style=color:#719e07>&gt;</span>();
</span></span><span style=display:flex><span> List<span style=color:#719e07>&lt;</span>Method<span style=color:#719e07>&gt;</span> methods <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> ArrayList<span style=color:#719e07>&lt;</span>Method<span style=color:#719e07>&gt;</span>();
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#719e07>for</span> (<span style=color:#dc322f>int</span> i <span style=color:#719e07>=</span> 0; i <span style=color:#719e07>&lt;</span> ics.length; i<span style=color:#719e07>++</span>) {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 检测接口访问级别是否为 protected 或 private</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (<span style=color:#719e07>!</span>Modifier.isPublic(ics<span style=color:#719e07>[</span>i<span style=color:#719e07>]</span>.getModifiers())) {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 获取接口包名</span>
</span></span><span style=display:flex><span> String npkg <span style=color:#719e07>=</span> ics<span style=color:#719e07>[</span>i<span style=color:#719e07>]</span>.getPackage().getName();
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (pkg <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>) {
</span></span><span style=display:flex><span> pkg <span style=color:#719e07>=</span> npkg;
</span></span><span style=display:flex><span> } <span style=color:#719e07>else</span> {
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (<span style=color:#719e07>!</span>pkg.equals(npkg))
</span></span><span style=display:flex><span> <span style=color:#586e75>// 非 public 级别的接口必须在同一个包下,否者抛出异常</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> IllegalArgumentException(<span style=color:#2aa198>&#34;non-public interfaces from different packages&#34;</span>);
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 添加接口到 ClassGenerator 中</span>
</span></span><span style=display:flex><span> ccp.addInterface(ics<span style=color:#719e07>[</span>i<span style=color:#719e07>]</span>);
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 遍历接口方法</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>for</span> (Method method : ics<span style=color:#719e07>[</span>i<span style=color:#719e07>]</span>.getMethods()) {
</span></span><span style=display:flex><span> <span style=color:#586e75>// 获取方法描述,可理解为方法签名</span>
</span></span><span style=display:flex><span> String desc <span style=color:#719e07>=</span> ReflectUtils.getDesc(method);
</span></span><span style=display:flex><span> <span style=color:#586e75>// 如果方法描述字符串已在 worked 中,则忽略。考虑这种情况,</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// A 接口和 B 接口中包含一个完全相同的方法</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (worked.contains(desc))
</span></span><span style=display:flex><span> <span style=color:#719e07>continue</span>;
</span></span><span style=display:flex><span> worked.add(desc);
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#dc322f>int</span> ix <span style=color:#719e07>=</span> methods.size();
</span></span><span style=display:flex><span> <span style=color:#586e75>// 获取方法返回值类型</span>
</span></span><span style=display:flex><span> Class<span style=color:#719e07>&lt;?&gt;</span> rt <span style=color:#719e07>=</span> method.getReturnType();
</span></span><span style=display:flex><span> <span style=color:#586e75>// 获取参数列表</span>
</span></span><span style=display:flex><span> Class<span style=color:#719e07>&lt;?&gt;[]</span> pts <span style=color:#719e07>=</span> method.getParameterTypes();
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 生成 Object[] args = new Object[1...N]</span>
</span></span><span style=display:flex><span> StringBuilder code <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> StringBuilder(<span style=color:#2aa198>&#34;Object[] args = new Object[&#34;</span>).append(pts.length).append(<span style=color:#2aa198>&#34;];&#34;</span>);
</span></span><span style=display:flex><span> <span style=color:#719e07>for</span> (<span style=color:#dc322f>int</span> j <span style=color:#719e07>=</span> 0; j <span style=color:#719e07>&lt;</span> pts.length; j<span style=color:#719e07>++</span>)
</span></span><span style=display:flex><span> <span style=color:#586e75>// 生成 args[1...N] = ($w)$1...N;</span>
</span></span><span style=display:flex><span> code.append(<span style=color:#2aa198>&#34; args[&#34;</span>).append(j).append(<span style=color:#2aa198>&#34;] = ($w)$&#34;</span>).append(j <span style=color:#719e07>+</span> 1).append(<span style=color:#2aa198>&#34;;&#34;</span>);
</span></span><span style=display:flex><span> <span style=color:#586e75>// 生成 InvokerHandler 接口的 invoker 方法调用语句,如下:</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// Object ret = handler.invoke(this, methods[1...N], args);</span>
</span></span><span style=display:flex><span> code.append(<span style=color:#2aa198>&#34; Object ret = handler.invoke(this, methods[&#34;</span> <span style=color:#719e07>+</span> ix <span style=color:#719e07>+</span> <span style=color:#2aa198>&#34;], args);&#34;</span>);
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 返回值不为 void</span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (<span style=color:#719e07>!</span>Void.TYPE.equals(rt))
</span></span><span style=display:flex><span> <span style=color:#586e75>// 生成返回语句,形如 return (java.lang.String) ret;</span>
</span></span><span style=display:flex><span> code.append(<span style=color:#2aa198>&#34; return &#34;</span>).append(asArgument(rt, <span style=color:#2aa198>&#34;ret&#34;</span>)).append(<span style=color:#2aa198>&#34;;&#34;</span>);
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> methods.add(method);
</span></span><span style=display:flex><span> <span style=color:#586e75>// 添加方法名、访问控制符、参数列表、方法代码等信息到 ClassGenerator 中 </span>
</span></span><span style=display:flex><span> ccp.addMethod(method.getName(), method.getModifiers(), rt, pts, method.getExceptionTypes(), code.toString());
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (pkg <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>)
</span></span><span style=display:flex><span> pkg <span style=color:#719e07>=</span> PACKAGE_NAME;
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 构建接口代理类名称:pkg + &#34;.proxy&#34; + id,比如 org.apache.dubbo.proxy0</span>
</span></span><span style=display:flex><span> String pcn <span style=color:#719e07>=</span> pkg <span style=color:#719e07>+</span> <span style=color:#2aa198>&#34;.proxy&#34;</span> <span style=color:#719e07>+</span> id;
</span></span><span style=display:flex><span> ccp.setClassName(pcn);
</span></span><span style=display:flex><span> ccp.addField(<span style=color:#2aa198>&#34;public static java.lang.reflect.Method[] methods;&#34;</span>);
</span></span><span style=display:flex><span> <span style=color:#586e75>// 生成 private java.lang.reflect.InvocationHandler handler;</span>
</span></span><span style=display:flex><span> ccp.addField(<span style=color:#2aa198>&#34;private &#34;</span> <span style=color:#719e07>+</span> InvocationHandler.class.getName() <span style=color:#719e07>+</span> <span style=color:#2aa198>&#34; handler;&#34;</span>);
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 为接口代理类添加带有 InvocationHandler 参数的构造方法,比如:</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// porxy0(java.lang.reflect.InvocationHandler arg0) {</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// handler=$1;</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// }</span>
</span></span><span style=display:flex><span> ccp.addConstructor(Modifier.PUBLIC, <span style=color:#719e07>new</span> Class<span style=color:#719e07>&lt;?&gt;[]</span>{InvocationHandler.class}, <span style=color:#719e07>new</span> Class<span style=color:#719e07>&lt;?&gt;[</span>0<span style=color:#719e07>]</span>, <span style=color:#2aa198>&#34;handler=$1;&#34;</span>);
</span></span><span style=display:flex><span> <span style=color:#586e75>// 为接口代理类添加默认构造方法</span>
</span></span><span style=display:flex><span> ccp.addDefaultConstructor();
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 生成接口代理类</span>
</span></span><span style=display:flex><span> Class<span style=color:#719e07>&lt;?&gt;</span> clazz <span style=color:#719e07>=</span> ccp.toClass();
</span></span><span style=display:flex><span> clazz.getField(<span style=color:#2aa198>&#34;methods&#34;</span>).set(<span style=color:#cb4b16>null</span>, methods.toArray(<span style=color:#719e07>new</span> Method<span style=color:#719e07>[</span>0<span style=color:#719e07>]</span>));
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 构建 Proxy 子类名称,比如 Proxy1,Proxy2 等</span>
</span></span><span style=display:flex><span> String fcn <span style=color:#719e07>=</span> Proxy.class.getName() <span style=color:#719e07>+</span> id;
</span></span><span style=display:flex><span> ccm <span style=color:#719e07>=</span> ClassGenerator.newInstance(cl);
</span></span><span style=display:flex><span> ccm.setClassName(fcn);
</span></span><span style=display:flex><span> ccm.addDefaultConstructor();
</span></span><span style=display:flex><span> ccm.setSuperClass(Proxy.class);
</span></span><span style=display:flex><span> <span style=color:#586e75>// 为 Proxy 的抽象方法 newInstance 生成实现代码,形如:</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// public Object newInstance(java.lang.reflect.InvocationHandler h) { </span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// return new org.apache.dubbo.proxy0($1);</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// }</span>
</span></span><span style=display:flex><span> ccm.addMethod(<span style=color:#2aa198>&#34;public Object newInstance(&#34;</span> <span style=color:#719e07>+</span> InvocationHandler.class.getName() <span style=color:#719e07>+</span> <span style=color:#2aa198>&#34; h){ return new &#34;</span> <span style=color:#719e07>+</span> pcn <span style=color:#719e07>+</span> <span style=color:#2aa198>&#34;($1); }&#34;</span>);
</span></span><span style=display:flex><span> <span style=color:#586e75>// 生成 Proxy 实现类</span>
</span></span><span style=display:flex><span> Class<span style=color:#719e07>&lt;?&gt;</span> pc <span style=color:#719e07>=</span> ccm.toClass();
</span></span><span style=display:flex><span> <span style=color:#586e75>// 通过反射创建 Proxy 实例</span>
</span></span><span style=display:flex><span> proxy <span style=color:#719e07>=</span> (Proxy) pc.newInstance();
</span></span><span style=display:flex><span> } <span style=color:#719e07>catch</span> (RuntimeException e) {
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> e;
</span></span><span style=display:flex><span> } <span style=color:#719e07>catch</span> (Exception e) {
</span></span><span style=display:flex><span> <span style=color:#719e07>throw</span> <span style=color:#719e07>new</span> RuntimeException(e.getMessage(), e);
</span></span><span style=display:flex><span> } <span style=color:#719e07>finally</span> {
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (ccp <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span>)
</span></span><span style=display:flex><span> <span style=color:#586e75>// 释放资源</span>
</span></span><span style=display:flex><span> ccp.release();
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (ccm <span style=color:#719e07>!=</span> <span style=color:#cb4b16>null</span>)
</span></span><span style=display:flex><span> ccm.release();
</span></span><span style=display:flex><span> <span style=color:#268bd2>synchronized</span> (cache) {
</span></span><span style=display:flex><span> <span style=color:#719e07>if</span> (proxy <span style=color:#719e07>==</span> <span style=color:#cb4b16>null</span>)
</span></span><span style=display:flex><span> cache.remove(key);
</span></span><span style=display:flex><span> <span style=color:#719e07>else</span>
</span></span><span style=display:flex><span> <span style=color:#586e75>// 写缓存</span>
</span></span><span style=display:flex><span> cache.put(key, <span style=color:#719e07>new</span> WeakReference<span style=color:#719e07>&lt;</span>Proxy<span style=color:#719e07>&gt;</span>(proxy));
</span></span><span style=display:flex><span> <span style=color:#586e75>// 唤醒其他等待线程</span>
</span></span><span style=display:flex><span> cache.notifyAll();
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> proxy;
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><p>上面代码比较复杂,我们写了大量的注释。大家在阅读这段代码时,要搞清楚 ccp 和 ccm 的用途,不然会被搞晕。ccp 用于为服务接口生成代理类,比如我们有一个 DemoService 接口,这个接口代理类就是由 ccp 生成的。ccm 则是用于为 org.apache.dubbo.common.bytecode.Proxy 抽象类生成子类,主要是实现 Proxy 类的抽象方法。下面以 org.apache.dubbo.demo.DemoService 这个接口为例,来看一下该接口代理类代码大致是怎样的(忽略 EchoService 接口)。</p><div class=highlight><pre tabindex=0 style=color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-java data-lang=java><span style=display:flex><span><span style=color:#719e07>package</span> org.apache.dubbo.common.bytecode;
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span><span style=color:#268bd2>public</span> <span style=color:#268bd2>class</span> <span style=color:#268bd2>proxy0</span> <span style=color:#268bd2>implements</span> org.apache.dubbo.demo.DemoService {
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#268bd2>public</span> <span style=color:#268bd2>static</span> java.lang.reflect.Method<span style=color:#719e07>[]</span> methods;
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#268bd2>private</span> java.lang.reflect.InvocationHandler handler;
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#268bd2>public</span> <span style=color:#268bd2>proxy0</span>() {
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#268bd2>public</span> <span style=color:#268bd2>proxy0</span>(java.lang.reflect.InvocationHandler arg0) {
</span></span><span style=display:flex><span> handler <span style=color:#719e07>=</span> $1;
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#268bd2>public</span> java.lang.String <span style=color:#268bd2>sayHello</span>(java.lang.String arg0) {
</span></span><span style=display:flex><span> Object<span style=color:#719e07>[]</span> args <span style=color:#719e07>=</span> <span style=color:#719e07>new</span> Object<span style=color:#719e07>[</span>1<span style=color:#719e07>]</span>;
</span></span><span style=display:flex><span> args<span style=color:#719e07>[</span>0<span style=color:#719e07>]</span> <span style=color:#719e07>=</span> ($w) $1;
</span></span><span style=display:flex><span> Object ret <span style=color:#719e07>=</span> handler.invoke(<span style=color:#719e07>this</span>, methods<span style=color:#719e07>[</span>0<span style=color:#719e07>]</span>, args);
</span></span><span style=display:flex><span> <span style=color:#719e07>return</span> (java.lang.String) ret;
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><p>好了,到这里代理类生成逻辑就分析完了。整个过程比较复杂,大家需要耐心看一下。</p><h2 id=4总结>4.总结</h2><p>本篇文章对服务引用的过程进行了较为详尽的分析,还有一些逻辑暂时没有分析到,比如 Directory、Cluster。这些接口及实现类功能比较独立,后续会单独成文进行分析。暂时我们可以先把这些类看成黑盒,只要知道这些类的用途即可。关于服务引用过程就分析到这里。</p><div id=pre-footer><h2>反馈</h2><p class=feedback--prompt>此页是否对您有帮助?</p><button class="btn btn-primary mb-4 feedback--yes"></button>
<button class="btn btn-primary mb-4 feedback--no"></button></div><script>const yes=document.querySelector(".feedback--yes"),no=document.querySelector(".feedback--no");document.querySelectorAll(".feedback--link").forEach(e=>{e.href=e.href+window.location.pathname});const sendFeedback=e=>{gtag||console.log("!gtag"),gtag("event","click",{event_category:"Helpful",event_label:window.location.pathname,value:e})},disableButtons=()=>{yes.disabled=!0,yes.classList.add("feedback--button__disabled"),no.disabled=!0,no.classList.add("feedback--button__disabled")};yes.addEventListener("click",()=>{sendFeedback(1),disableButtons(),document.querySelector(".feedback--response").classList.remove("feedback--response__hidden")}),no.addEventListener("click",()=>{sendFeedback(0),disableButtons(),document.querySelector(".feedback--response").classList.remove("feedback--response__hidden")})</script><br><div class="text-muted mt-5 pt-3 border-top">最后修改 March 1, 2023: <a href=https://github.com/apache/dubbo-website/commit/f2070b3fdd1b7b200db0f8f2a50fb09d7c398a64>Add /zh/ docs aliases (#2336) (f2070b3fdd1)</a></div></div></main><div class="d-none d-xl-block col-xl-2 td-toc d-print-none"><div class="td-page-meta ml-2 pb-1 pt-2 mb-0"><a href=https://github.com/apache/dubbo-website/edit/master/content/zh-cn/docsv2.7/dev/source/refer-service.md target=_blank><i class="fa fa-edit fa-fw"></i> 编辑此页</a>
<a href="https://github.com/apache/dubbo-website/new/master/content/zh-cn/docsv2.7/dev/source/refer-service.md?filename=change-me.md&amp;value=---%0Atitle%3A+%22Long+Page+Title%22%0AlinkTitle%3A+%22Short+Nav+Title%22%0Aweight%3A+100%0Adescription%3A+%3E-%0A+++++Page+description+for+heading+and+indexes.%0A---%0A%0A%23%23+Heading%0A%0AEdit+this+template+to+create+your+new+page.%0A%0A%2A+Give+it+a+good+name%2C+ending+in+%60.md%60+-+e.g.+%60getting-started.md%60%0A%2A+Edit+the+%22front+matter%22+section+at+the+top+of+the+page+%28weight+controls+how+its+ordered+amongst+other+pages+in+the+same+directory%3B+lowest+number+first%29.%0A%2A+Add+a+good+commit+message+at+the+bottom+of+the+page+%28%3C80+characters%3B+use+the+extended+description+field+for+more+detail%29.%0A%2A+Create+a+new+branch+so+you+can+preview+your+new+file+and+request+a+review+via+Pull+Request.%0A" target=_blank><i class="fa fa-edit fa-fw"></i> 创建子页面</a>
<a href="https://github.com/apache/dubbo-website/issues/new?title=%e6%9c%8d%e5%8a%a1%e5%bc%95%e7%94%a8" target=_blank><i class="fab fa-github fa-fw"></i> 登记问题</a>
<a href=https://github.com/apache/dubbo/issues/new target=_blank><i class="fas fa-tasks fa-fw"></i> 提交项目问题</a></div><nav id=TableOfContents><ul><li><a href=#1-简介>1. 简介</a></li><li><a href=#2服务引用原理>2.服务引用原理</a></li><li><a href=#3源码分析>3.源码分析</a><ul><li><a href=#31-处理配置>3.1 处理配置</a></li><li><a href=#32-引用服务>3.2 引用服务</a></li></ul></li><li><a href=#4总结>4.总结</a></li></ul></nav></div></div></div></div><footer class="bg-dark py-5 row d-print-none footer-margin-0"><div class="container-fluid mx-sm-5"><div class=row><div class="col-6 col-sm-4 text-xs-center order-sm-2"><ul class="list-inline mb-0"><li class="list-inline-item mx-2 h3" data-toggle=tooltip data-placement=top title="Dubbo mailing list archive" aria-label="Dubbo mailing list archive"><a class=text-white target=_blank rel="noopener noreferrer" href=https://lists.apache.org/list.html?dev@dubbo.apache.org><i class="fa fa-envelope"></i></a></li></ul></div><div class="col-6 col-sm-4 text-right text-xs-center order-sm-3"><ul class="list-inline mb-0"><li class="list-inline-item mx-2 h3" data-toggle=tooltip data-placement=top title=GitHub aria-label=GitHub><a class=text-white target=_blank rel="noopener noreferrer" href=https://github.com/apache/dubbo><i class="fab fa-github"></i></a></li><li class="list-inline-item mx-2 h3" data-toggle=tooltip data-placement=top title="Subscribe to mailing list" aria-label="Subscribe to mailing list"><a class=text-white target=_blank rel="noopener noreferrer" href=mailto:dev-subscribe@dubbo.apache.org><i class="fa fa-envelope"></i></a></li></ul></div><div class="col-12 col-sm-4 text-center py-2 order-sm-2"><small class=text-white>&copy; 2024 The Apache Software Foundation. Apache and the Apache feather logo are trademarks of The Apache Software Foundation. 保留所有权利</small></div></div></div></footer><div class="row pt-2 pb-2 footer-margin-0"><div class="container-fluid mx-sm-5"><div class=text-center id=my-footer><img alt=apache_logo src=/imgs/apache_logo.png><ul><li><a href=https://www.apache.org>Foundation</a></li><li><a href=https://www.apache.org/licenses/>License</a></li><li><a href=https://dubbo.apache.org/en/overview/notices/>Security</a></li><li><a href=https://www.apache.org/events/current-event>Events</a></li><li><a href=https://www.apache.org/foundation/sponsorship.html>Sponsorship</a></li><li><a href=https://privacy.apache.org/policies/privacy-policy-public.html>Privacy</a></li><li><a href=https://www.apache.org/foundation/thanks.html>Thanks</a></li></ul></div></div></div><script src=/js/popper.min.js integrity=sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49 crossorigin=anonymous></script><script src=/js/bootstrap.min.js integrity=sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy crossorigin=anonymous></script><script src=/js/main.min.b075178d232d3b0039b3cb6af2fc2e9d90071820167a60f4eea3a79169975ee8.js integrity="sha256-sHUXjSMtOwA5s8tq8vwunZAHGCAWemD07qOnkWmXXug=" crossorigin=anonymous></script><script async src=https://widget.kapa.ai/kapa-widget.bundle.js data-website-id=d763c4f2-f871-400b-aeca-d986c4af73c2 data-project-name="Apache Dubbo" data-project-color=#E8442E data-button-text="Ask AI" data-search-mode-enabled=true data-modal-open-on-command-k=true data-modal-disclaimer="The AI supports multiple languages, but it may not be accessible in China due to recaptcha, a proxy is required." data-project-logo=https://pbs.twimg.com/profile_images/1011849068283191302/FJbH5vbF_400x400.jpg data-modal-example-questions="What is Apache Dubbo?,How to run Apache Dubbo?" data-button-position-top data-button-position-right=20px data-button-position-bottom=200px data-button-position-left></script><script>(function(e,t,n,s){e[s]=e[s]||[];var a=t.getElementsByTagName(n)[0],i=t.createElement(n);i.async=!0,i.id="beacon-aplus",i.setAttribute("exparams","userid=&aplus&sidx=aplusSidex&ckx=aplusCkx"),i.src="//g.alicdn.com/alilog/mlog/aplus_v2.js",i.crossorigin="anonymous",a.parentNode.insertBefore(i,a)})(window,document,"script","aplus_queue"),function(e){var t=e.createElement("script");t.type="text/javascript",t.async=!0,t.src="//g.alicdn.com/aes/??tracker/3.3.4/index.js,tracker-plugin-pv/3.0.5/index.js,tracker-plugin-event/3.0.0/index.js,tracker-plugin-autolog/3.0.3/index.js,tracker-plugin-survey/3.0.3/index.js,tracker-plugin-jserror/3.0.3/index.js,tracker-plugin-resourceError/3.0.3/index.js",t.onload=function(){window.AES_CONFIG=window.AES_CONFIG||{env:"prod"},window.aes=new AES({pid:"zN245h",user_type:6}),window.AESPluginAutologConfig={exposure:"auto"},window.AEMPluginInstances=[aes.use(AESPluginPV,window.AESPluginPVConfig||{enableHistory:!0}),aes.use(AESPluginEvent,window.AESPluginEventConfig||{}),aes.use(AESPluginSurvey,window.AESPluginEventConfig||{}),aes.use(AESPluginAutolog,window.AESPluginAutologConfig||{}),aes.use(AESPluginJSError,window.AESPluginJSError||{}),aes.use(AESPluginResourceError,window.AESPluginResourceError||{})]},setTimeout(function(){e.getElementsByTagName("body")[0].appendChild(t)},800)}(document)</script></body></html>