Luckylau's Blog

微服务架构之Dubbo服务降级(2)

​ 在之前微服务架构之Dubbo服务降级(1)讲解了mock的服务降级机制,其实stub可以说范围更广阔,更全面的服务降级。

​ 官方是这样介绍的:Mock是Stub(本地存根)的一个子集,便于服务提供方在客户端执行容错逻辑,因经常需要在出现RpcException(比如网络失败,超时等)时进行容错,而在出现业务异常(比如登录用户名密码错误)时不需要容错,如果用Stub, 需要捕获并依赖RpcException类,而用Mock就可以不依赖RpcException,因为它的约定就是只有出现RpcException时才执行。Mock通常用于服务降级,比如某验权服务,当服务提供方全部挂掉后,客户端不抛出异常,而是通过Mock数据返回授权失败。

如何使用Stub

Stub 存根,可以在dubbo 提供者端实现,也可在调用消费方实现; 如果消费方实现存根,则服务方 存根 将不起作用。

1
<dubbo:reference id="demoService" check="false" interface="com.alibaba.dubbo.demo.DemoService" stub="com.alibaba.dubbo.demo.DemoServiceStub" />

我们在com.alibaba.dubbo.demo路径下定义DemoServiceStub类,实现DemoService接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class DemoServiceStub implements DemoService {
private DemoService demoService;//必须要有,用以接受dubbo在调用远程服务生成的服务代理类
public DemoServiceStub(DemoService demoService) {
this.demoService = demoService;
}
//相当于在远程调用时,人工封装一层,当出现异常才会有stub效果
@Override
public String sayHello(String name) {
try{
return demoService.sayHello(name);//远程调用
}catch(Exception e) {
      //远程调用失败
return "i am stub , hello";
    }
}

源码解析

我们在这篇文章介绍了dubbo的服务引用,ReferenceConfig的init()在执行ref = createProxy(map)之前有一步骤是checkStubAndMock,如果没有com.alibaba.dubbo.demo.DemoServiceStub实现会报错。在StubProxyFactoryWrapper后面我们看到处理stub的逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
//StubProxyFactoryWrapper实现
public <T> T getProxy(Invoker<T> invoker) throws RpcException {
//AbstractProxyFactory,一般配置就到这里结束
T proxy = proxyFactory.getProxy(invoker);
if (GenericService.class != invoker.getInterface()) {
String stub = invoker.getUrl().getParameter(Constants.STUB_KEY, invoker.getUrl().getParameter(Constants.LOCAL_KEY));
//处理stub逻辑
if (ConfigUtils.isNotEmpty(stub)) {
Class<?> serviceType = invoker.getInterface();
if (ConfigUtils.isDefault(stub)) {
if (invoker.getUrl().hasParameter(Constants.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 implemention 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
URL url = invoker.getUrl();
//我debug时候,这是默认是false,没有暴露
if (url.getParameter(Constants.STUB_EVENT_KEY, Constants.DEFAULT_STUB_EVENT)){
url = url.addParameter(Constants.STUB_EVENT_METHODS_KEY, StringUtils.join(Wrapper.getWrapper(proxy.getClass()).getDeclaredMethodNames(), ","));
url = url.addParameter(Constants.IS_SERVER_KEY, Boolean.FALSE.toString());
try{
export(proxy, (Class)invoker.getInterface(), url);
}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 implemention class " + stubClass.getName(), e);
}
} catch (Throwable t) {
LOGGER.error("Failed to create stub implemention class " + stub + " in consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", cause: " + t.getMessage(), t);
// ignore
}
}
}
//这里的proxy是由stub构造器重新实例化而来
return proxy;
}
Luckylau wechat
如果对您有价值,看官可以打赏的!