Dubbo本地调用、参数回调、事件通知(八)

本文详细介绍了Dubbo的本地调用、参数回调和事件通知功能。本地调用允许在同一主机上的Consumer直接调用Provider,减少网络开销。参数回调实现了Provider调用Consumer的逻辑。事件通知则提供了远程方法调用的异步回调和同步回调模式,有助于处理异步处理和错误响应。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、参考

本地调用:http://dubbo.apache.org/en-us/docs/user/demos/local-call.html

参数回调:http://dubbo.apache.org/en-us/docs/user/demos/callback-parameter.html

事件通知:http://dubbo.apache.org/zh-cn/docs/user/demos/events-notify.html

2、本地调用

如果Consumer与Provider部署在一台主机上,共用一个JVM,那么当Consumer调用Provider时就没有必要经过网络栈,直接调用即可,不需要通过网络,便是Dubbo的其它Filtter正常生效。

对于Provider端而言,从2.2.0开始默认自动支持本地调用,无需任务特殊配置。值得注意的是,如果只打算让Provider提供的服务支持本地调用,可以把服务的protocol设置成injvm一个协议。这样,被配置的服务将不用支持远程调用,服务在运行时也不会开端口。

对于Consumer端而言,当调用某个服务时,如果它在本地已经暴露,则默认直接调用,这个是从2.2.0开始后的默认行为。如果想关掉此行为,则如下配置:

<dubbo:reference ... scope="remote" />

有点鸡肋的功能,为服务本身就是要将功能打散然后分布到系统中的多个节点上,为什么会分布在一个节点上呢?另个就算不走injvm,走其它协议,开销能有多大呢?不能指着把服务部署在相同节点上节省网络流量吧。

3、参数回调

抛开Dubbo,在普通的开发中,当调用一个方法时,我们可能在参数中给方法传递一个引用,然后方法在执行的过程中调用引中的一个方法,这就是普通的回调。

Dubbo也有类似功能,Dubbo 将基于长连接生成反向代理,Consumer端临时允当Provider端,Provider端调用的接口,真正的逻辑将在Consumer端执行。

服务端接口示例,CallbackService.java:

package com.callback;
 
public interface CallbackService {
    void addListener(String key, CallbackListener listener);
}

后边一个参数是接口,下边是参数接口定义CallbackListener.java文件:

package com.callback;
 
public interface CallbackListener {
    void changed(String msg);
}

这个接口要在Consumer端实现。

服务端接口实现:

package com.callback.impl;
 
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
 
import com.callback.CallbackListener;
import com.callback.CallbackService;
 
public class CallbackServiceImpl implements CallbackService {
     
    private final Map<String, CallbackListener> listeners = new ConcurrentHashMap<String, CallbackListener>();
  
    public CallbackServiceImpl() {
        Thread t = new Thread(new Runnable() {
            public void run() {
                while(true) {
                    try {
                        for(Map.Entry<String, CallbackListener> entry : listeners.entrySet()){
                           try {
                               entry.getValue().changed(getChanged(entry.getKey()));
                           } catch (Throwable t) {
                               listeners.remove(entry.getKey());
                           }
                        }
                        Thread.sleep(5000); // 定时触发变更通知
                    } catch (Throwable t) { // 防御容错
                        t.printStackTrace();
                    }
                }
            }
        });
        t.setDaemon(true);
        t.start();
    }
  
    public void addListener(String key, CallbackListener listener) {
        listeners.put(key, listener);
        listener.changed(getChanged(key)); // 发送变更通知
    }
     
    private String getChanged(String key) {
        return "Changed: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
    }
}

在上边的实现中,在构造方法中启动了一个线程,里边是一个无限循环,按固定间隔调用从Consumer端注册进来的回调。

服务端配置:

<bean id="callbackService" class="com.callback.impl.CallbackServiceImpl" />
<dubbo:service interface="com.callback.CallbackService" ref="callbackService" connections="1" callbacks="1000">
    <dubbo:method name="addListener">
        <dubbo:argument index="1" callback="true" />
        <!--也可以通过指定类型的方式-->
        <!--<dubbo:argument type="com.demo.CallbackListener" callback="true" />-->
    </dubbo:method>
</dubbo:service>

需要明确指定那个参数是用来回调的。

Consumer端配置,一切如常:

<dubbo:reference id="callbackService" interface="com.callback.CallbackService" />

Consumer端代码:

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:consumer.xml");
context.start();
 
CallbackService callbackService = (CallbackService) context.getBean("callbackService");
 
callbackService.addListener("foo.bar", new CallbackListener(){
    public void changed(String msg) {
        System.out.println("callback1:" + msg);
    }
});

需要为回调的参数指定一个实例。

4、事件通知

对于一次远程方法调用,有oninvokeonreturnonthrow三个事件,分别为调用之前、返回之后,抛出异常三个事件。在Consumer端,可以为三个事件指定事件处理方法。

服务端接口:

interface IDemoService {
    public Person get(int id);
}

服务端实现:

class NormalDemoService implements IDemoService {
    public Person get(int id) {
        return new Person(id, "charles`son", 4);
    }
}

服务端配置:

<dubbo:application name="rpc-callback-demo" />
<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
<bean id="demoService" class="org.apache.dubbo.callback.implicit.NormalDemoService" />
<dubbo:service interface="org.apache.dubbo.callback.implicit.IDemoService" ref="demoService" version="1.0.0" group="cn"/>

没有什么特别的地方。

Consumer端的处理事件通知的接口:

interface Notify {
    public void onreturn(Person msg, Integer id);
    public void onthrow(Throwable ex, Integer id);
}

方法中,第一个参数应该是方法的返回值,其它是方法的参数。

然后Consumer端实现这个接口:

class NotifyImpl implements Notify {
    public Map<Integer, Person>    ret    = new HashMap<Integer, Person>();
    public Map<Integer, Throwable> errors = new HashMap<Integer, Throwable>();
    
    public void onreturn(Person msg, Integer id) {
        System.out.println("onreturn:" + msg);
        ret.put(id, msg);
    }
    
    public void onthrow(Throwable ex, Integer id) {
        errors.put(id, ex);
    }
}

服务端配置:

<bean id ="demoCallback" class = "org.apache.dubbo.callback.implicit.NofifyImpl" />
<dubbo:reference id="demoService" interface="org.apache.dubbo.callback.implicit.IDemoService" version="1.0.0" group="cn" >
      <dubbo:method name="get" async="true" onreturn = "demoCallback.onreturn" onthrow="demoCallback.onthrow" />
</dubbo:reference>

配置中async与onreturn、onthrow是一对,组合不同,功能也不同:

  • 异步回调模式:async=true onreturn="xxx"
  • 同步回调模式:async=false onreturn="xxx"
  • 异步无回调 :async=true
  • 同步无回调 :async=false

Consumer端测试代码:

IDemoService demoService = (IDemoService) context.getBean("demoService");
NofifyImpl notify = (NofifyImpl) context.getBean("demoCallback");
int requestId = 2;
Person ret = demoService.get(requestId);
Assert.assertEquals(null, ret);
//for Test:只是用来说明callback正常被调用,业务具体实现自行决定.
for (int i = 0; i < 10; i++) {
    if (!notify.ret.containsKey(requestId)) {
        Thread.sleep(200);
    } else {
        break;
    }
}
Assert.assertEquals(requestId, notify.ret.get(requestId).getId());

当前线程正在执行for循环,事件处理器中的代码应该是在Consumer端的IOThread中执行,可能涉及到线程同步的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值