3. Dubbo高级应用
1. 开篇
本文主要介绍一些dubbo的高级应用。
在平时使用dubbo时,最常用的还是直接加@DubboService
、@DubboReference
注解,dubbo还提供了更多的高级功能供我们使用。
本文采用的示例是在dubbo系列第一篇Dubbo框架介绍的基础上进行修改的,这里不再赘述。
2. 服务降级
服务降级,是针对于某个服务提供者而言的,服务消费者在调用某个服务提供者时,如果该服务提供者报错了,所采取的措施。
比如消费者在调用提供者时,服务超时而导致调用失败了,就可以采用服务降级来进行容错。
例如,现在服务端配置超时为2s,消费端配置超时为1s,加上容错措施。
@Service
public class ConsumerService {
@DubboReference(timeout = 1000, retries = 3, mock = "fail: return Error")
private HelloService helloService;
public void sayHello() {
String result = helloService.sayHello("world");
System.out.println(result);
}
}
执行后,经过3次重试后,会直接返回 Error,而不是抛出异常。
mock=fail: return Error
表示,在消费者调用提供者失败后,返回Error。
mock=force: return Error
表示,不会执行调用,直接返回Error。
还可以创建一个Mock类来实现。
在dubbo-api
项目创建一个类,实现HelloService
接口来做Mock。
public class HelloServiceMock implements HelloService {
public String sayHello(String name) {
return "Mock " + name;
}
}
消费端调用
@EnableDubbo
@SpringBootApplication
public class ConsumerApplication {
@DubboReference(timeout = 1000, retries = 3, mock = "true")
private HelloService helloService;
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(ConsumerApplication.class, args);
ConsumerApplication consumerApplication = context.getBean(ConsumerApplication.class);
consumerApplication.sayHello("world");
}
public void sayHello(String name) {
String result = helloService.sayHello(name);
System.out.println(result);
}
}
3. 本地伪装
本地伪装和服务降级的功能是一样的,都是通过mock
来实现,具体用法可以参考服务降级。
4. 本地存根
本地存根是一段代码逻辑,这段代码逻辑一般是服务端提供者提供,但是在服务消费端执行。服务提供者可以利用这种机制在服务消费者远程调用服务提供者之前或之后再做一些其他事情,比如结果缓存、请求参数验证或其他逻辑。
现在我们来创建一个本地存根类HelloServiceStub
,同样需要实现HelloService
接口。本地存根类需要持有HelloService
一个对象。当消费者调用时会先执行本地存根这个类。
public class HelloServiceStub implements HelloService {
private final HelloService helloService;
public HelloServiceStub(HelloService helloService) {
this.helloService = helloService;
}
public String sayHello(String name) {
System.out.println("before execute remote service, parameter: " + name);
String result = this.helloService.sayHello(name);
System.out.println("after execute remote service, result: " + result);
return "Stub: " + name;
}
}
消费者执行代码
@EnableDubbo
@SpringBootApplication
public class ConsumerApplication {
@DubboReference(stub = "true")
private HelloService helloService;
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(ConsumerApplication.class, args);
ConsumerApplication consumerApplication = context.getBean(ConsumerApplication.class);
consumerApplication.sayHello("world");
}
public void sayHello(String name) {
String result = helloService.sayHello(name);
System.out.println(result);
}
}
查看执行结果
代码中的stub="true"
,代表启用本地存根,dubbo会根据调用的接口全限定名+"Stub"
来充当本地存根类,当然也支持stub="某个类的全限定名"
,如果找不到这个类,程序就会报错。
当消费者调用sayHello
方法时,是先执行的stub
里面的sayHello
方法,在stub
类里面执行一段逻辑后,才去真正执行sayHello
方法,这也相当于mock的另一种实现方式。
5. 参数回调
参数回调是指,服务消费端在调用服务提供者的时候,调用完成后给服务消费端一个回调(再调用服务消费端一个方法)。
一般都是消费端来调用服务端,如果在调用服务端后,想让服务端再调用对应的消费端就需要用到这种机制了。
dubbo-api`里增加回调接口`HelloServiceListener
public interface HelloServiceListener {
void changed(String msg);
}
dubbo-provider
服务提供者增加回调配置
@DubboService(methods = {@Method(name = "sayHello", arguments = {@Argument(index = 1, callback = true)})})
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String name, HelloServiceListener listener) {
System.out.println("provider received invoke of sayHello: " + name);
listener.changed("Callback " + name);
return "Hello, " + name;
}
}
dubbo-consumer
服务消费者进行调用
typescript复制代码@EnableDubbo
@SpringBootApplication
public class ConsumerApplication {
@DubboReference
private HelloService helloService;
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(ConsumerApplication.class, args);
ConsumerApplication consumerApplication = context.getBean(ConsumerApplication.class);
consumerApplication.sayHello("world");
}
public void sayHello(String name) {
String result = helloService.sayHello(name, new HelloServiceListenerImpl());
System.out.println(result);
}
}
public class HelloServiceListenerImpl implements HelloServiceListener {
@Override
public void changed(String msg) {
System.out.println("Changed :" + msg);
}
}
如果有多个回调的话,可以再加一个参数来区分。
6. 异步调用
Dubbo支持多种异步调用,可以通过注解或xml将一个方法标记为异步方法,也可以将一个类的方法的返回值类型设置为CompletableFuture
。
6.1 注解形式
@DubboReference(methods = @Method(name = "sayHello", async = true))
private HelloService helloService;
通过这种方式,直接调用方法会得到一个null
,而不是结果。需要通过RpcContext
来获取返回值。
// 会返回null
String result = helloService.sayHello(name);
System.out.println(result);
CompletableFuture<String> helloFuture = RpcContext.getContext().getCompletableFuture();
helloFuture.whenComplete((v, t) -> {
if (t == null) {
System.out.println("result: " + v);
} else {
t.printStackTrace();
}
});
或者通过asyncCall
来完成异步调用。
CompletableFuture<String> f = RpcContext.getContext().asyncCall(() ->
helloService.sayHello("async"));
System.out.println("result: " + f.get());
6.2 CompletableFuture
将方法声明为CompletableFuture
类型
CompletableFuture<String> sayHello(String name);
这样消费端就不用使用RpcContext
就可以完成异步调用。
CompletableFuture<String> helloFuture = helloService.sayHello(name);
helloFuture.whenComplete((v, t) -> {
if (t == null) {
System.out.println("result: " + v);
} else {
t.printStackTrace();
}
});
7. 泛化调用
Dubbo提供了一个类GenericService
,他可以调用所有的服务,通常用来做测试。使用这个类的一个好处是,我们可以不用引入额外的jar就可以调用服务,不过书写起来不叫麻烦。需要指定generic
为true
。
@EnableDubbo
@SpringBootApplication
public class GenericApplication {
@DubboReference(id = "genericService", interfaceName = "cn.juejin.dubbo.api.HelloService", generic = true)
private GenericService genericService;
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(GenericApplication.class, args);
GenericService genericService = (GenericService) context.getBean("genericService");
Object result = genericService.$invoke("sayHello", new String[]{"java.lang.String"}, new Object[]{"generic"});
System.out.println(result);
}
}
8. 泛化服务
在服务端实现了GenericService
接口的类,提供的服务称为泛化服务,是用来实现一个通用的服务测试框架。
服务端定义一个泛化服务
@DubboService(interfaceName = "cn.juejin.dubbo.api.HelloService")
public static class GenericServiceImpl implements GenericService {
@Override
public Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException {
System.out.println("执行泛化服务: " + method);
return "Hello, " + args[0];
}
}
在消费端调用时,依然可以按之前的方式调用。
@DubboReference
private HelloService helloService;
作者:Java星辰 链接:https://juejin.cn/post/7171075349676032014 来源:稀土掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。