dubbo的几种异步调用

同步调用 是指调用线程在服务提供方返回结果前需要被阻塞,异步调用则是消费端发起调用后会马上返回。 (然后服务方处理完成之后,通过回调的形式将结果告知给消费方。)

在dubbo 2.6.*版本中

1.通过RpcContext.getContext().getFuture()来获取到 Future对象,然后通过

Future.get()方法获取到结果

案例

服务提供者

 public static void main(String[] args) throws IOException {
        //创建ServiceConfig实例
        ServiceConfig serviceConfig=new ServiceConfig();

        //设置应用程序配置
        serviceConfig.setApplication(new ApplicationConfig("first-dubbo-provider"));

        //设置服务注册中心信息
        RegistryConfig registryConfig=new RegistryConfig("multicast://224.5.6.7:1234");
        serviceConfig.setRegistry(registryConfig);

        //设置接口与实现类
        serviceConfig.setInterface(GreetingService.class);
        serviceConfig.setRef(new GreetingServiceImpl());


        //设置服务分组与版本
        serviceConfig.setVersion("1.0.0");
        serviceConfig.setGroup("dubbo");
        
        //导出服务
        //启动了nettyServer监听请求,并将服务注册到服务注册中心
        serviceConfig.export();

        //挂起线程 避免服务停止
        System.out.println("server is started");
        System.in.read();
    }

服务消费者

 public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建服务引用对象实例
        ReferenceConfig referenceConfig=new ReferenceConfig();
        //设置应用信息
        referenceConfig.setApplication(new ApplicationConfig("first-dubbo-consumer"));
        //设置服务注册中心 zookeeper://127.0.0.1:2181   广播模式 multicast://224.5.6.7:1234
        referenceConfig.setRegistry(new RegistryConfig("multicast://224.5.6.7:1234"));
        //设置服务接口和超时时间
        referenceConfig.setInterface(GreetingService.class);
        referenceConfig.setTimeout(5000);
        
        //设置服务分组与版本
        referenceConfig.setVersion("1.0.0");
        referenceConfig.setGroup("dubbo");
        //设置为异步
        referenceConfig.setAsync(true);
        //引用服务
        GreetingService greetingService=referenceConfig.get();
        //设置隐式参数
        RpcContext.getContext().setAttachment("company","alibaba");
        //调用服务
        System.out.println(greetingService.sayHello("world"));
        //等待结果
        Future future=RpcContext.getContext().getFuture();
        System.out.println(future.get());
    }

Future future=RpcContext.getContext().getFuture(); 这一步从上下文获取future对象 然后从里面获取真正的值。

缺点就是 future.get()是一个阻塞的方法,调用该方法 在结果返回前,当前线程将一直处于阻塞状态。

2.在future对象上设置回调方法

在future对象上设置回调方法,当服务端完成操作的时候,就自动调用回调方法 不阻塞当前线程。

服务消费者

 public static void main(String[] args) throws ExecutionException, InterruptedException {

        //创建服务引用对象实例
        ReferenceConfig referenceConfig=new ReferenceConfig();
        //设置应用信息
        referenceConfig.setApplication(new ApplicationConfig("first-dubbo-consumer"));

        //设置服务注册中心 zookeeper://127.0.0.1:2181   广播模式 multicast://224.5.6.7:1234
        referenceConfig.setRegistry(new RegistryConfig("multicast://224.5.6.7:1234"));

        //设置服务接口和超时时间
        referenceConfig.setInterface(GreetingService.class);
        referenceConfig.setTimeout(5000);

        //设置服务分组与版本
        referenceConfig.setVersion("1.0.0");
        referenceConfig.setGroup("dubbo");

        //设置为异步
        referenceConfig.setAsync(true);
        //引用服务
        GreetingService greetingService=referenceConfig.get();

        //设置隐式参数
        RpcContext.getContext().setAttachment("company","alibaba");

        //调用服务
        System.out.println(greetingService.sayHello("world"));


        //等待结果
        ((FutureAdapter)RpcContext.getContext().getFuture()).getFuture().setCallback(new ResponseCallback() {
            @Override
            public void done(Object o) {
                System.out.println(o);
            }

            @Override
            public void caught(Throwable throwable) {
                System.out.println("error:"+throwable);
            }
        });


    }


((FutureAdapter)RpcContext.getContext().getFuture()).getFuture(). 获取到的是一个ResponseFuture对象,里面可以设置回调方法。
当前服务端执行完毕的时候,会调用该方法,不会阻塞业务线程。
这是借助了netty的异步通信机制,netty底层的io线程会在收到响应后自动回调注册的回调函数,不需要业务线程的干预。

dubbo的几种异步调用_第1张图片

3 dubbo 2.7.*版本提供的异步调用

使用了java8提供的completableFuture

  public static void main(String[] args) throws ExecutionException, InterruptedException {

        //创建服务引用对象实例
        ReferenceConfig referenceConfig=new ReferenceConfig();
        //设置应用信息
        referenceConfig.setApplication(new ApplicationConfig("first-dubbo-consumer"));

        //设置服务注册中心 zookeeper://127.0.0.1:2181   广播模式 multicast://224.5.6.7:1234
        referenceConfig.setRegistry(new RegistryConfig("multicast://224.5.6.7:1234"));

        //设置服务接口和超时时间
        referenceConfig.setInterface(GreetingService.class);
        referenceConfig.setTimeout(5000);
        
        //设置服务分组与版本
        referenceConfig.setVersion("1.0.0");
        referenceConfig.setGroup("dubbo");

        //设置为异步
        referenceConfig.setAsync(true);
        //引用服务
        GreetingService greetingService=referenceConfig.get();

        //设置隐式参数
        RpcContext.getContext().setAttachment("company","alibaba");

        //调用服务
        System.out.println(greetingService.sayHello("world"));


        //等待结果
        CompletableFuture completableFuture=RpcContext.getContext().getCompletableFuture();
        completableFuture.whenComplete((v,t)->{
            System.out.println("回调结果"+v);
        });
    }

CompletableFuture completableFuture=RpcContext.getContext().getCompletableFuture(); 从RpcContext中获取CompletableFuture ,使用whenComplete实现回调

4 服务端提供异步执行

可以让服务提供者返回的就是completableFuture对象,该接口就是一个异步接口。

定义异步接口 返回一个CompletableFuture对象

public interface GrettingServiceAsync {

    CompletableFuture sayHello(String name);
}


实现类

public class GrettingServiceAsyncImpl implements GrettingServiceAsync {


    //创建业务线程池
    private final ThreadPoolExecutor bizThreadpool=new ThreadPoolExecutor(8,16,1, TimeUnit.SECONDS,new SynchronousQueue<>(),new NamedThreadFactory("biz-thread-poool"),new ThreadPoolExecutor.CallerRunsPolicy());


    @Override
    public CompletableFuture sayHello(String name) {
        //为supplyAsync提供自定义线程池bizThreadpool 避免使用jdk公用线程池ForkJoinPool.commonPool();
        //使用CompleTableFuture.supplyAsync让服务处理异步化

        //保存当前线程的上下文
        RpcContext context=RpcContext.getContext();
        return CompletableFuture.supplyAsync(()->{
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            return "hello"+name+context.getAttachment("company");
        },bizThreadpool);

    }
}

服务提供者这里 返回一个CompletableFuture对象 ,且定义了一个线程池,然后使用CompletableFuture.supplyAsync接口,将业务逻辑放进去,返回一个future对象。 这里将本来在dubbo线程中处理的业务转到自定义的线程池中处理,这样dubbo线程就会立刻返回。
然后内部的业务逻辑 需要10秒后才能处理完
return CompletableFuture.supplyAsync(()->{ try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); }

然后发布服务 跟之前的服务提供者暴露没有什么不同 只是换了一个接口

   public static void main(String[] args) throws IOException {
        //创建ServiceConfig实例
        ServiceConfig serviceConfig=new ServiceConfig();
        //设置应用程序配置
        serviceConfig.setApplication(new ApplicationConfig("first-dubbo-provider"));
        //设置服务注册中心信息
        RegistryConfig registryConfig=new RegistryConfig("multicast://224.5.6.7:1234");
        serviceConfig.setRegistry(registryConfig);
        
        //设置接口与实现类
        serviceConfig.setInterface(GrettingServiceAsync.class);
        serviceConfig.setRef(new GrettingServiceAsyncImpl());
        
        //设置服务分组与版本
        serviceConfig.setVersion("1.0.0");
        serviceConfig.setGroup("dubbo");
        
        //导出服务
        //启动了nettyServer监听请求,并将服务注册到服务注册中心
        serviceConfig.export();
        
        //挂起线程 避免服务停止
        System.out.println("server is started");
        System.in.read();
    }

服务消费者

 public static void main(String[] args) throws ExecutionException, InterruptedException {

        //创建服务引用对象实例
        ReferenceConfig referenceConfig=new ReferenceConfig();
        //设置应用信息
        referenceConfig.setApplication(new ApplicationConfig("first-dubbo-consumer"));

        //设置服务注册中心 zookeeper://127.0.0.1:2181   广播模式 multicast://224.5.6.7:1234
        referenceConfig.setRegistry(new RegistryConfig("multicast://224.5.6.7:1234"));

        //设置服务接口和超时时间
        referenceConfig.setInterface(GrettingServiceAsync.class);
        referenceConfig.setTimeout(15000);
        
        //设置服务分组与版本
        referenceConfig.setVersion("1.0.0");
        referenceConfig.setGroup("dubbo");

        //设置为异步
        referenceConfig.setAsync(true);
        //引用服务
        GrettingServiceAsync greetingService=referenceConfig.get();

        //设置隐式参数
        RpcContext.getContext().setAttachment("company","alibaba");

        //调用服务
        CompletableFuture completableFuture=greetingService.sayHello("world");

        //等待结果
        completableFuture.whenComplete((v,t)->{
            System.out.println("回调结果"+v);
        });
        Thread.currentThread().join();
        System.out.println("消费者主线程执行完毕");
    }

消费端就可以获取到该接口的completableFuture结果,而不再需要经过RpcContext来做转换。
也就是dubbo支持completableFuture返回值的远程方法了。

需要注意的是:
referenceConfig.setTimeout(15000); 消费端设置的超时时间是15秒,然后greetingService.sayHello内部的执行时间是10秒,所以可以在超时时间前返回,如果greetingService.sayHello内部的处理时间大于了超时时间,那么会立即返回null ,dubbo内部有一个检测线程会检测调用时间大于超时时间的调用接口。

还有一点需要注意的是,这里用了 Thread.currentThread().join(); 来保持main方法在执行完毕之后不退出,不然会因为还没到接口返回,main方法就已经结束掉了。

5 使用AsyncContext实现异步执行

这里服务端也是异步执行

服务提供者

public class GrettingServiceRpcContextImpl implements GreetingService {


    @Override
    public String sayHello(String name) {
        AsyncContext asyncContext= RpcContext.startAsync();
        new Thread(()->{
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            asyncContext.write("hello "+ name);

        }).start();

        return null;
    }

    @Override
    public Result testGeneric(Pojo pojo) {
        return null;
    }
}

消费端获取的时候还是使用
RpcContext.getContext().getFuture().get() 来获取返回值 最终得到的也是一个future
但是服务端AsyncContext asyncContext= RpcContext.startAsync(); 开启服务异步执行,返回AsyncContext方法 不会占用dubbo线程池里面的线程。

你可能感兴趣的:(dubbo源码分析)