-
快速啟動
git clone https://github.com/apache/incubator-dubbo.git
cd incubator-dubbo
運行 dubbo-demo-provider中的com.alibaba.dubbo.demo.provider.Provider
如果使用Intellij Idea 請加上-Djava.net.preferIPv4Stack=true
-
關(guān)鍵配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!-- 提供方應(yīng)用信息,用于計算依賴關(guān)系 -->
<dubbo:application name="hello-world-app" />
<!-- 使用multicast廣播注冊中心暴露服務(wù)地址 -->
<dubbo:registry address="zookeeper://224.5.6.7:1234" />
<!-- 用dubbo協(xié)議在20880端口暴露服務(wù) -->
<dubbo:protocol name="dubbo" port="20880" />
<!-- 聲明需要暴露的服務(wù)接口 -->
<dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService" />
-
啟動類
public class Provider {
/**
* To get ipv6 address to work, add
* System.setProperty("java.net.preferIPv6Addresses", "true");
* before running your application.
*/
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-demo-provider.xml"});
context.start();
System.in.read(); // press any key to exit
}
}
dubbo-admin搭建-github
dubbo-admin搭建-官方文檔
## java env
export JAVA_HOME=/usr/local/java/jdk1.8
export JRE_HOME=$JAVA_HOME/jre
nohup java -jar dubbo-admin.jar -Ddubbo.registry.address=zookeeper://eshop-cache01-64:2181 > /dev/null 2>&1 &
啟動時檢查
check="false"集群模式配置
<dubbo:service cluster="failsafe" />負(fù)載均衡
<dubbo:service interface="..." loadbalance="roundrobin" />-
線程模型
接口com.alibaba.dubbo.common.threadpool.ThreadPool
image.png 直連提供者
${user.home}/dubbo-resolve.properties
本地調(diào)試時可用之,消費端配置只訂閱
為本地開發(fā)環(huán)境不影響其他環(huán)境的,暫停注冊功能;提供端配置
<dubbo:registry address="10.20.153.10:9090" register="false" />
<dubbo:registry id="hzRegistry" address="10.20.153.10:9090" />
<dubbo:registry id="qdRegistry" address="10.20.141.150:9090" subscribe="false" />
-
靜態(tài)服務(wù)
<dubbo:registry address="10.20.141.150:9090" dynamic="false" /> - 多協(xié)議
- 不同服務(wù)不同協(xié)議
<!-- 多協(xié)議配置 -->
<dubbo:protocol name="dubbo" port="20880" />
<dubbo:protocol name="rmi" port="1099" />
<!-- 使用dubbo協(xié)議暴露服務(wù) -->
<dubbo:service interface="com.alibaba.hello.api.HelloService" version="1.0.0" ref="helloService" protocol="dubbo" />
<!-- 使用rmi協(xié)議暴露服務(wù) -->
<dubbo:service interface="com.alibaba.hello.api.DemoService" version="1.0.0" ref="demoService" protocol="rmi" />
- 同一服務(wù)多種協(xié)議
<dubbo:protocol name="dubbo" port="20880" />
<dubbo:protocol name="hessian" port="8080" />
<!-- 使用多個協(xié)議暴露服務(wù) -->
<dubbo:service id="helloService" interface="com.alibaba.hello.api.HelloService" version="1.0.0" protocol="dubbo,hessian" />
- 多注冊中心
- 同一服務(wù)注冊到多個注冊中心
<!-- 多注冊中心配置 -->
<dubbo:registry id="hangzhouRegistry" address="10.20.141.150:9090" />
<dubbo:registry id="qingdaoRegistry" address="10.20.141.151:9010" default="false" />
<!-- 向多個注冊中心注冊 -->
<dubbo:service interface="com.alibaba.hello.api.HelloService" version="1.0.0" ref="helloService" registry="hangzhouRegistry,qingdaoRegistry" />
- 同一服務(wù)引用不同注冊中心
<!-- 多注冊中心配置 -->
<dubbo:registry id="chinaRegistry" address="10.20.141.150:9090" />
<dubbo:registry id="intlRegistry" address="10.20.154.177:9010" default="false" />
<!-- 引用中文站服務(wù) -->
<dubbo:reference id="chinaHelloService" interface="com.alibaba.hello.api.HelloService" version="1.0.0" registry="chinaRegistry" />
<!-- 引用國際站站服務(wù) -->
<dubbo:reference id="intlHelloService" interface="com.alibaba.hello.api.HelloService" version="1.0.0" registry="intlRegistry" />
- 不同服務(wù)使用不同注冊中心
<!-- 多注冊中心配置 -->
<dubbo:registry id="chinaRegistry" address="10.20.141.150:9090" />
<dubbo:registry id="intlRegistry" address="10.20.154.177:9010" default="false" />
<!-- 向中文站注冊中心注冊 -->
<dubbo:service interface="com.alibaba.hello.api.HelloService" version="1.0.0" ref="helloService" registry="chinaRegistry" />
<!-- 向國際站注冊中心注冊 -->
<dubbo:service interface="com.alibaba.hello.api.DemoService" version="1.0.0" ref="demoService" registry="intlRegistry" />
-
服務(wù)分組
當(dāng)一個接口有多種實現(xiàn)時,可以用 group 區(qū)分。
<dubbo:service group="feedback" interface="com.xxx.IndexService" /> -
多版本
<dubbo:service interface="com.foo.BarService" version="1.0.0" /> -
分組聚合
<dubbo:reference interface="com.xxx.MenuService" group="aaa,bbb" merger="true" /> - 參數(shù)驗證
-
結(jié)果緩存
<dubbo:reference interface="com.foo.BarService" cache="lru" /> -
泛化引用
<dubbo:reference id="barService" interface="com.foo.BarService" generic="true" />
GenericService barService = (GenericService) applicationContext.getBean("barService");
Object result = barService.$invoke("sayHello", new String[] { "java.lang.String" }, new Object[] { "World" });
<bean id="genericService" class="com.foo.MyGenericService" />
<dubbo:service interface="com.foo.BarService" ref="genericService" />
-
回聲測試
所有服務(wù)自動實現(xiàn) EchoService 接口,只需將任意服務(wù)引用強制轉(zhuǎn)型為 EchoService,即可使用
// 遠(yuǎn)程服務(wù)引用
MemberService memberService = ctx.getBean("memberService");
EchoService echoService = (EchoService) memberService; // 強制轉(zhuǎn)型為EchoService
// 回聲測試可用性
String status = echoService.$echo("OK");
assert(status.equals("OK"));
-
上下文信息
RpcContext 是一個 ThreadLocal 的臨時狀態(tài)記錄器,當(dāng)接收到 RPC 請求,或發(fā)起 RPC 請求時,RpcContext 的狀態(tài)都會變化。比如:A 調(diào) B,B 再調(diào) C,則 B 機器上,在 B 調(diào) C 之前,RpcContext 記錄的是 A 調(diào) B 的信息,在 B 調(diào) C 之后,RpcContext 記錄的是 B 調(diào) C 的信息。
// 遠(yuǎn)程調(diào)用
xxxService.xxx();
// 本端是否為消費端,這里會返回true
boolean isConsumerSide = RpcContext.getContext().isConsumerSide();
// 獲取最后一次調(diào)用的提供方IP地址
String serverIP = RpcContext.getContext().getRemoteHost();
// 獲取當(dāng)前服務(wù)配置信息,所有配置信息都將轉(zhuǎn)換為URL的參數(shù)
String application = RpcContext.getContext().getUrl().getParameter("application");
// 注意:每發(fā)起RPC調(diào)用,上下文狀態(tài)會變化
yyyService.yyy();
注意:path, group, version, dubbo, token, timeout 幾個 key 是保留字段
// 隱式傳參,后面的遠(yuǎn)程調(diào)用都會隱式將這些參數(shù)發(fā)送到服務(wù)器端,類似cookie,
//用于框架集成,不建議常規(guī)業(yè)務(wù)使用
RpcContext.getContext().setAttachment("index", "1");
xxxService.xxx(); // 遠(yuǎn)程調(diào)用
<dubbo:reference id="fooService" interface="com.alibaba.foo.FooService">
<dubbo:method name="findFoo" async="true" sent="true" />
</dubbo:reference>
sent="true" 等待消息發(fā)出,消息發(fā)送失敗將拋出異常。
sent="false" 不等待消息發(fā)出,將消息放入 IO 隊列,即刻返回。
// 此調(diào)用會立即返回null
fooService.findFoo(fooId);
// 拿到調(diào)用的Future引用,當(dāng)結(jié)果返回后,會被通知和設(shè)置到此Future
Future<Foo> fooFuture = RpcContext.getContext().getFuture();
// 此調(diào)用會立即返回null
barService.findBar(barId);
// 拿到調(diào)用的Future引用,當(dāng)結(jié)果返回后,會被通知和設(shè)置到此Future
Future<Bar> barFuture = RpcContext.getContext().getFuture();
// 此時findFoo和findBar的請求同時在執(zhí)行,客戶端不需要啟動多線程來支持并行,而是借助NIO的非阻塞完成
// 如果foo已返回,直接拿到返回值,否則線程wait住,等待foo返回后,線程會被notify喚醒
Foo foo = fooFuture.get();
// 同理等待bar返回
Bar bar = barFuture.get();
// 如果foo需要5秒返回,bar需要6秒返回,實際只需等6秒,即可獲取到foo和bar,進(jìn)行接下來的處理。
-
本地調(diào)用
本地調(diào)用使用了 injvm 協(xié)議
<dubbo:service protocol="injvm" />
<dubbo:reference injvm="true" .../>
<dubbo:service injvm="true" .../>
<bean id ="demoCallback" class = "com.alibaba.dubbo.callback.implicit.NofifyImpl" />
<dubbo:reference id="demoService" interface="com.alibaba.dubbo.callback.implicit.IDemoService" version="1.0.0" group="cn" >
<dubbo:method name="get" async="true" onreturn = "demoCallback.onreturn" onthrow="demoCallback.onthrow" />
</dubbo:reference>
-
本地存根
image.png
<dubbo:service interface="com.foo.BarService" stub="com.foo.BarServiceStub" />
package com.foo;
public class BarServiceStub implements BarService {
private final BarService barService;
// 構(gòu)造函數(shù)傳入真正的遠(yuǎn)程代理對象
public (BarService barService) {
this.barService = barService;
}
public String sayHello(String name) {
// 此代碼在客戶端執(zhí)行, 你可以在客戶端做ThreadLocal本地緩存,或預(yù)先驗證參數(shù)是否合法,等等
try {
return barService.sayHello(name);
} catch (Exception e) {
// 你可以容錯,可以做任何AOP攔截事項
return "容錯數(shù)據(jù)";
}
}
}
-
本地偽裝
本地偽裝通常用于服務(wù)降級
<dubbo:reference interface="com.foo.BarService" mock="com.foo.BarServiceMock" />
package com.foo;
public class BarServiceMock implements BarService {
public String sayHello(String name) {
// 你可以偽造容錯數(shù)據(jù),此方法只在出現(xiàn)RpcException時被執(zhí)行
return "容錯數(shù)據(jù)";
}
}
如果服務(wù)的消費方經(jīng)常需要 try-catch 捕獲異常,請考慮改為 Mock 實現(xiàn),并在 Mock 實現(xiàn)中 return null
-
延遲暴露
延遲到 Spring 初始化完成后,再暴露服務(wù)
<dubbo:service delay="-1" />
Spring 2.x 初始化死鎖問題規(guī)避辦法
強烈建議不要在服務(wù)的實現(xiàn)類中有 applicationContext.getBean() 的調(diào)用,全部采用 IoC 注入的方式使用 Spring的Bean。
如果實在要調(diào) getBean(),可以將 Dubbo 的配置放在 Spring 的最后加載。
如果不想依賴配置順序,可以使用 <dubbo:provider delay=”-1” />,使 Dubbo 在 Spring 容器初始化完后,再暴露服務(wù)。
如果大量使用 getBean(),相當(dāng)于已經(jīng)把 Spring 退化為工廠模式在用,可以將 Dubbo 的服務(wù)隔離單獨的 Spring 容器。
-
并發(fā)控制
服務(wù)端:<dubbo:service interface="com.foo.BarService" executes="10" />
客戶端:<dubbo:reference interface="com.foo.BarService" actives="10" />
如果 <dubbo:service> 和 <dubbo:reference> 都配了actives,<dubbo:reference> 優(yōu)先
Load Balance 均衡
配置服務(wù)的客戶端的 loadbalance 屬性為 leastactive,此 Loadbalance 會調(diào)用并發(fā)數(shù)最小的 Provider(Consumer端并發(fā)數(shù))。
<dubbo:reference interface="com.foo.BarService" loadbalance="leastactive" /> -
連接控制
<dubbo:provider protocol="dubbo" accepts="10" />
<dubbo:reference interface="com.foo.BarService" connections="10" /> -
延遲連接
<dubbo:protocol name="dubbo" lazy="true" />
注意:該配置只對使用長連接的 dubbo 協(xié)議生效 - 粘滯連接
- 粘滯連接用于有狀態(tài)服務(wù),盡可能讓客戶端總是向同一提供者發(fā)起調(diào)用
- 自動開啟延遲連接
<dubbo:protocol name="dubbo" sticky="true" />
-
令牌驗證
可以在全局,服務(wù),協(xié)議級別設(shè)置
<dubbo:provider interface="com.foo.BarService" token="true" /> -
路由規(guī)則
分為條件路由規(guī)則和腳本路由規(guī)則,并且支持可擴展 -
配置規(guī)則
向注冊中心寫入動態(tài)配置覆蓋規(guī)則 -
服務(wù)降級
registry.register(URL.valueOf("override://0.0.0.0/com.foo.BarService?category=configurators&dynamic=false&application=foo&mock=force:return+null"));
mock=force:return+null 表示消費方對該服務(wù)的方法調(diào)用都直接返回 null 值,不發(fā)起遠(yuǎn)程調(diào)用。用來屏蔽不重要服務(wù)不可用時對調(diào)用方的影響。
還可以改為 mock=fail:return+null 表示消費方對該服務(wù)的方法調(diào)用在失敗后,再返回 null 值,不拋異常。用來容忍不重要服務(wù)不穩(wěn)定時對調(diào)用方的影響。
-
優(yōu)雅停機
Dubbo 是通過 JDK 的 ShutdownHook 來完成優(yōu)雅停機的
設(shè)置優(yōu)雅停機超時時間,缺省超時時間是 10 秒,如果超時則強制關(guān)閉
dubbo.service.shutdown.wait=15000 - 主機綁定
-
日志適配
<dubbo:application logger="log4j" /> -
訪問日志
<dubbo:protocol accesslog="true" />
<dubbo:protocol accesslog="http://10.20.160.198/wiki/display/dubbo/foo/bar.log" /> - 服務(wù)容器
- 自動加載 META-INF/spring 目錄下的所有 Spring 配置。
- 配置 spring 配置加載位置:
dubbo.spring.config=classpath*:META-INF/spring/*.xml
java com.alibaba.dubbo.container.Main -Ddubbo.container=spring,jetty,log4j
-
ReferenceConfig 緩存
ReferenceConfig 實例很重,封裝了與注冊中心的連接以及與提供者的連接,需要緩存,參見 ReferenceConfigCache
ReferenceConfigCache cache = ReferenceConfigCache.getCache();
// cache.get方法中會緩存 Reference對象,并且調(diào)用ReferenceConfig.get方法啟動ReferenceConfig
XxxService xxxService = cache.get(reference);
// 注意! Cache會持有ReferenceConfig,不要在外部再調(diào)用ReferenceConfig的destroy方法,導(dǎo)致Cache內(nèi)的ReferenceConfig失效!
// 使用xxxService對象
xxxService.sayHello();
<dubbo:application ...>
<dubbo:parameter key="dump.directory" value="/tmp" />
</dubbo:application>
默認(rèn)策略:
導(dǎo)出路徑,user.home標(biāo)識的用戶主目錄
導(dǎo)出間隔,最短間隔允許每隔10分鐘導(dǎo)出一次
-
序列化
啟用Kryo和FST
<dubbo:protocol name="dubbo" serialization="kryo"/>
com.alibaba.dubbo.config.ServiceConfig
com.alibaba.dubbo.config.ReferenceConfig
com.alibaba.dubbo.config.ProtocolConfig
com.alibaba.dubbo.config.RegistryConfig
com.alibaba.dubbo.config.MonitorConfig
com.alibaba.dubbo.config.ApplicationConfig
com.alibaba.dubbo.config.ModuleConfig
com.alibaba.dubbo.config.ProviderConfig
com.alibaba.dubbo.config.ConsumerConfig
com.alibaba.dubbo.config.MethodConfig
com.alibaba.dubbo.config.ArgumentConfig
-
schema 配置參考手冊
注意
只有 group,interface,version 是服務(wù)的匹配條件,三者決定是不是同一個服務(wù),其它配置項均為調(diào)優(yōu)和治理參數(shù) - 協(xié)議參考手冊
- 注冊中心參考手冊
-
Telnet 命令參考手冊
win7 --> CTRL + ] -->Enter,Enter看到dubbo>
dubbo>help
Please input "help [command]" show detail.
status [-l] - Show status.
pwd - Print working default service.
trace [service] [method] [times] - Trace the service.
help [command] - Show help.
exit - Exit the telnet.
invoke [service.]method(args) - Invoke the service method.
clear [lines] - Clear screen.
count [service] [method] [times] - Count the service.
ls [-l] [service] - List services and methods.
log level - Change log level or show log
ps [-l] [port] - Print server ports and connections.
cd [service] - Change default service.
- 將服務(wù)接口,服務(wù)模型,服務(wù)異常等均放在 API 包中,分包原則:重用發(fā)布等價原則(REP),共同重用原則(CRP)
- 服務(wù)接口盡可能大粒度,每個服務(wù)方法應(yīng)代表一個功能,而不是某功能的一個步驟,否則將面臨分布式事務(wù)問題,Dubbo 暫未提供分布式事務(wù)支持
- 不建議使用過于抽象的通用接口,如:Map query(Map),這樣的接口沒有明確語義,會給后期維護(hù)帶來不便。
- 每個接口都應(yīng)定義版本號,為后續(xù)不兼容升級提供可能,如:
<dubbo:service interface="com.xxx.XxxService" version="1.0" /> - 服務(wù)接口增加方法,或服務(wù)模型增加字段,可向后兼容,刪除方法或刪除字段,將不兼容,枚舉類型新增字段也不兼容,需通過變更版本號升級。詳細(xì)參見 服務(wù)協(xié)議
- 如果是完備集,可以用
Enum,比如:ENABLE, DISABLE。如果是業(yè)務(wù)種類,以后明顯會有類型增加,不建議用Enum,可以用String代替;注意升級的順序. - 服務(wù)參數(shù)及返回值建議使用 POJO 對象,即通過 setter, getter 方法表示屬性的對象
- 建議使用異常匯報錯誤,而不是返回錯誤碼,異常信息能攜帶更多信息,以及語義更友好。
如果擔(dān)心性能問題,在必要時,可以通過 override 掉異常類的fillInStackTrace()方法為空方法,使其不拷貝棧信息。 - 不要只是因為是 Dubbo 調(diào)用,而把調(diào)用 try...catch 起來。try...catch 應(yīng)該加上合適的回滾邊界上。
對于輸入?yún)?shù)的校驗邏輯在 Provider 端要有。如有性能上的考慮,服務(wù)實現(xiàn)者可以考慮在 API 包上加上服務(wù) Stub 類來完成檢驗。
- 在 Provider 上盡量多配置 Consumer 端屬性
<dubbo:service interface="com.alibaba.hello.api.WorldService" version="1.0.0" ref="helloService"
timeout="300" retry="2" loadbalance="random" actives="0" >
<dubbo:method name="findAllPerson" timeout="10000" retries="9" loadbalance="leastactive" actives="5" />
<dubbo:service/>
- Provider 上配置合理的 Provider 端屬性
- 配置管理信息
- 配置 Dubbo 緩存文件
<dubbo:registry file=”${user.home}/output/dubbo.cache” />
這個文件會緩存注冊中心的列表和服務(wù)提供者列表。有了這項配置后,當(dāng)應(yīng)用重啟過程中,Dubbo 注冊中心不可用時則應(yīng)用會從這個緩存文件讀取服務(wù)提供者列表的信息,進(jìn)一步保證應(yīng)用可靠性。 - 監(jiān)控配置
- 不要使用 dubbo.properties 文件配置,推薦使用對應(yīng) XML 配置
-
框架設(shè)計
image.png


