Dubbo配置參數(shù)詳解-stub
Dubbo是一個由阿里開源的服務(wù)治理框架瞧挤,筆者的公司重度使用Dubbo。Dubbo開源這么多年棋枕,配置項(xiàng)已經(jīng)非常豐富材部,了解各個配置項(xiàng)的作用也變得非常重要,本系列將從源代碼的角度分析Dubbo目前的最新版本(2.7.4)各個常用的配置項(xiàng)的具體含義以及是怎么起作用的杏节。
畫外音:目前Dubbo在開源中國舉辦的2019年度最受歡迎中國開源軟件中排名第3名唬渗,支持Dubbo的朋友可以去投票哇。2019年度最受歡迎中國開源軟件
stub是啥奋渔?
stub:存根镊逝,rmi需要存根(stub)和骨架(skeleton),stub用于客戶端嫉鲸,skeleton用于服務(wù)器端撑蒜。簡單來說,你調(diào)用provider玄渗,相關(guān)的服務(wù)方不是要提供給你一個jar包嗎座菠,那個jar包就是存根。
畫外音:使用過WebService的同學(xué)應(yīng)該很清楚
stub有啥用藤树?
一般的存根都是只有接口辈灼,不包含實(shí)現(xiàn)。但是dubbo提供的這個stub作用有點(diǎn)類似于攔截器也榄,它可以在調(diào)用正在的provider之前或之后對請求及結(jié)果進(jìn)行處理。
stub怎么使用司志?
存根主要有兩種使用方式甜紫,一種是服務(wù)提供者寫好,并隨jar包一起提供給調(diào)用方骂远,這種方式可以用來做前置驗(yàn)證囚霸,另外一種方式是調(diào)用方自己寫,這可以用來對結(jié)果進(jìn)行處理激才;
如果是服務(wù)方提供拓型,Stub一般都是定義在接口的同包目錄下额嘿,并且使用InterfaceName+Stub的命名方式,這時調(diào)用方只要設(shè)置stub="true"就可以使用了劣挫。
/**
* @description:
* @author: chengang6
* @create: 2019/5/10 10:38
**/
public interface HelloDubboService {
String say();
String hello();
String post(String key);
}
/**
* @description: Stub必須實(shí)現(xiàn)相同的接口册养,且跟接口在同一個目錄
* @author: chengang6
* @create: 2020/1/7 10:37
**/
public class HelloDubboServiceStub implements HelloDubboService {
private HelloDubboService helloDubboService;
//必須提供這種類似代理的構(gòu)造函數(shù),否則會報(bào)錯
public HelloDubboServiceStub(HelloDubboService helloDubboService) {
this.helloDubboService = helloDubboService;
}
@Override
public String say() {
return ">>>>>>>>Stub>>>>say";
}
@Override
public String hello() {
// 此代碼在客戶端執(zhí)行, 你可以在客戶端做ThreadLocal本地緩存压固,或預(yù)先驗(yàn)證參數(shù)是否合法球拦,等等
try {
return helloDubboService.hello();
} catch (Exception e) {
// 你可以容錯,可以做任何AOP攔截事項(xiàng)
return "容錯數(shù)據(jù)";
}
}
@Override
public String post(String key) {
return ">>>>>>>>Stub>>>>post";
}
}
//只要配置stub=true就可以使用存根了
@Reference(stub = "true")
private HelloDubboService helloDubboService;
也可以在@Service中配置stub="true"帐我,這樣就算Consumer端么有配置stub坎炼,也會強(qiáng)制Consumer端執(zhí)行Stub
@Service(stub = "true")
public class HelloDubboServiceImpl implements HelloDubboService {
Consumer端也可以自定義Stub,此時不需要遵循InterfaceName+Stub的命名方式拦键,但需要把完整的類名寫在參數(shù)中谣光,比如
@Reference(stub = "com.example.dubboconsumer.stub.MyStub")
private HelloDubboService helloDubboService;
stub的命名規(guī)則是什么樣的?
在Reference初始化的時候會判斷是否設(shè)置了stub芬为,如果設(shè)置了該參數(shù)會在AbstractInterfaceConfig類的checkStubAndLocal方法中判斷該參數(shù)的有效性
/**
* Legitimacy check of stub, note that: the local will deprecated, and replace with <code>stub</code>
*
* @param interfaceClass for provider side, it is the {@link Class} of the service that will be exported; for consumer
* side, it is the {@link Class} of the remote service interface
*/
void checkStubAndLocal(Class<?> interfaceClass) {
if (ConfigUtils.isNotEmpty(local)) {
Class<?> localClass = ConfigUtils.isDefault(local) ?
ReflectUtils.forName(interfaceClass.getName() + "Local") : ReflectUtils.forName(local);
verify(interfaceClass, localClass);
}
if (ConfigUtils.isNotEmpty(stub)) {
Class<?> localClass = ConfigUtils.isDefault(stub) ?
ReflectUtils.forName(interfaceClass.getName() + "Stub") : ReflectUtils.forName(stub);
verify(interfaceClass, localClass);
}
}
private void verify(Class<?> interfaceClass, Class<?> localClass) {
if (!interfaceClass.isAssignableFrom(localClass)) {
throw new IllegalStateException("The local implementation class " + localClass.getName() +
" not implement interface " + interfaceClass.getName());
}
try {
//Check if the localClass a constructor with parameter who's type is interfaceClass
ReflectUtils.findConstructor(localClass, interfaceClass);
} catch (NoSuchMethodException e) {
throw new IllegalStateException("No such constructor \"public " + localClass.getSimpleName() +
"(" + interfaceClass.getName() + ")\" in local implementation class " + localClass.getName());
}
}
可以看到Dubbo除了提供Stub萄金,還可以使用Local這種方式,但是這種方式Dubbo已經(jīng)不推薦使用碳柱;
如果stub="true"捡絮,Dubbo將會在相同接口的包下尋找InterfaceName+Stub這個類,否則將會根據(jù)全限定名尋找莲镣;并且Stub中必須有一個將接口作為形參的構(gòu)造函數(shù)福稳,否則報(bào)錯;
stub是如何生效的瑞侮?
Dubbo在為reference生成代理類的時候的圆,都會調(diào)用StubProxyFactoryWrapper判斷該reference是否需要使用stub,如果是則直接返回stub
public class StubProxyFactoryWrapper implements ProxyFactory {
@SuppressWarnings({"unchecked", "rawtypes"})
public <T> T getProxy(Invoker<T> invoker) throws RpcException {
T proxy = proxyFactory.getProxy(invoker);
if (GenericService.class != invoker.getInterface()) {
URL url = invoker.getUrl();
String stub = url.getParameter(STUB_KEY, url.getParameter(LOCAL_KEY));
if (ConfigUtils.isNotEmpty(stub)) {
Class<?> serviceType = invoker.getInterface();
if (ConfigUtils.isDefault(stub)) {
if (url.hasParameter(STUB_KEY)) {
stub = serviceType.getName() + "Stub";
} else {
stub = serviceType.getName() + "Local";
}
}
try {
Class<?> stubClass = ReflectUtils.forName(stub);
if (!serviceType.isAssignableFrom(stubClass)) {
throw new IllegalStateException("The stub implementation class " + stubClass.getName() + " not implement interface " + serviceType.getName());
}
try {
Constructor<?> constructor = ReflectUtils.findConstructor(stubClass, serviceType);
proxy = (T) constructor.newInstance(new Object[]{proxy});
//export stub service
URLBuilder urlBuilder = URLBuilder.from(url);
if (url.getParameter(STUB_EVENT_KEY, DEFAULT_STUB_EVENT)) {
urlBuilder.addParameter(STUB_EVENT_METHODS_KEY, StringUtils.join(Wrapper.getWrapper(proxy.getClass()).getDeclaredMethodNames(), ","));
urlBuilder.addParameter(IS_SERVER_KEY, Boolean.FALSE.toString());
try {
export(proxy, (Class) invoker.getInterface(), urlBuilder.build());
} catch (Exception e) {
LOGGER.error("export a stub service error.", e);
}
}
} catch (NoSuchMethodException e) {
throw new IllegalStateException("No such constructor \"public " + stubClass.getSimpleName() + "(" + serviceType.getName() + ")\" in stub implementation class " + stubClass.getName(), e);
}
} catch (Throwable t) {
LOGGER.error("Failed to create stub implementation class " + stub + " in consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", cause: " + t.getMessage(), t);
// ignore
}
}
}
return proxy;
}
}
問題
如果stub是在服務(wù)端配置半火,而沒有在consumer端配置越妈,并且consumer啟動在前,這時StubProxyFactoryWrapper將會判斷reference不需要stub钮糖,當(dāng)provider啟動后梅掠,就算@Service配置了stub="true",consumer也不會調(diào)用stub。
總結(jié):
- stub一般是服務(wù)提供方提供店归,并隨jar包一起提供給consumer;
- 可以設(shè)置@Service(stub="true")阎抒,這樣就能強(qiáng)制consumer端使用stub;
- stub是在consumer端執(zhí)行的消痛,可以做前置驗(yàn)證且叁,這樣可以過濾非法請求,提高性能秩伞;