Dubbo 实战经验总结

1. Dubbo Stub 本地根存

dubbo的本地存根的原理是 :远程服务后,客户端通常只剩下接口,而实现全在服务器端,但提供方有些时候想在客户端也执行部分逻辑,那么就在服务消费者这一端提供了一个Stub类,然后当消费者调用provider方提供的dubbo服务时,客户端生成 Proxy 实例,这个Proxy实例就是我们正常调用dubbo远程服务要生成的代理实例,然后消费者这方会把 Proxy 通过构造函数传给 消费者方的Stub ,然后把 Stub 暴露给用户,Stub 可以决定要不要去调 Proxy。会通过代理类去完成这个调用,这样在Stub类中,就可以做一些额外的事,来对服务的调用过程进行优化或者容错的处理
在这里插入图片描述

具体实现:

  • Provider Service

    public interface DemoFacade {
        Response<String> sayHello(String name);
    }
    
    @Service("demoFacade")
    public class DemoFacadeImpl implements DemoFacade {
    
        @Api
        @Override
        public Response<String> sayHello(String name) {
            Response<String> response = Response.success();
            response.setData("hello, " + name + " !");
            return response;
        }
    }
    
  • Provider Config

    <dubbo:provider dynamic="true" group="${dubbo.provider.group}" version="1.0"/>
    
    <dubbo:service dynamic="true" interface="com.gmr.dubbo.provider.remote.facade.DemoFacade" ref="demoFacade" />
    
  • Consumer Stub

    public class DemoFacadeStub implements DemoFacade {
        
        //必须定义这个接口,以便接收dubbo在调用远程服务生成的服务代理类
        private DemoFacade demoFacade;
        
        //这个构造函数必须要提供,dubbo框架会在消费者这一方调用这个方法
        public DemoFacadeStub(DemoFacade demoFacade ) {
            this.demoFacade = demoFacade;
        }
    
        public Response<String> sayHello(String name) {
            Response<String> response = null;
            if (name == null) {
                response = this.demoFacade.sayHello("all");
            }else {
                response = this.demoFacade.sayHello(name);
            }
            return response;
        }
    }
    
  • Consumer Config

    <dubbo:reference init="true" id="demoFacade" interface="com.gmr.dubbo.provider.remote.facade.DemoFacade" version="1.0" group="${dubbo.consumer.group}" stub="com.gmr.dubbo.consumer.remote.facade.DemoFacadeStub"/>
    

2. Dubbo Mock原理

Dubbo的mock的实例原理简单而言就是调用真实的接口实现类不通,就会调用你的mock类(mock类和真实实现类都Implements 同一个接口,自己mock的名字要是:接口类名+mock)。

具体实现:

  • Provider Service

    public interface DemoFacade {
        Response<String> sayHello(String name);
    }
    
    @Service("demoFacade")
    public class DemoFacadeImpl implements DemoFacade {
    
        @Api
        @Override
        public Response<String> sayHello(String name) {
            Response<String> response = Response.success();
            response.setData("hello, " + name + " !");
            return response;
        }
    }
    
    @Service("demoFacadeMock")
    public class DemoFacadeMock implements DemoFacade {
    
        @Api
        @Override
        public Response<String> sayHello(String name) {
            Response<String> response = Response.success();
            response.setData("hello, mock !");
            return response;
        }
    }
    
  • Provider Config

    <dubbo:provider dynamic="true" group="${dubbo.provider.group}" version="1.0"/>
    
    <dubbo:service dynamic="true" interface="com.gmr.dubbo.provider.remote.facade.DemoFacade" ref="demoFacade" mock="com.gmr.dubbo.provider.remote.facade.DemoFacadeMock"/>
    
  • Consumer Config

    <dubbo:reference init="true" id="demoFacade" interface="com.gmr.dubbo.provider.remote.facade.DemoFacade" version="1.0" group="${dubbo.consumer.group}" mock="true"/>
    

3. Dubbo GenericService作用

两个用途一直是泛化服务,一种是泛化引用。前者是为了provider省事,后者是消费者省事。先介绍泛化服务怎么做到的:

比如提供者想省事,虽然提供了10个方法,但是不想在接口里面写十个方法,也就不用给这十个方法做函数声明、参数声明了。直接通过:

ServiceConfig<GenericService> service = new ServiceConfig<GenericService>();
service.setApplication(new ApplicationConfig("generic-provider"));
service.setRegistry(new RegistryConfig("N/A"));
service.setProtocol(new ProtocolConfig("dubbo", 29581));
service.setInterface(DemoService.class.getName());
service.setRef(new GenericService() {

    public Object $invoke(String method, String[] parameterTypes, Object[] args)
        throws GenericException {
        if ("sayName".equals(method)) {
            return "Generic " + args[0];
        }
        if ("throwDemoException".equals(method)) {
            throw new GenericException(DemoException.class.getName(), "Generic");
        }
        if ("getUsers".equals(method)) {
            return args[0];
        }
        return null;
    }
});
service.export();

也就是通过method不同,直接返回不同处理结果,隐藏了方法和参数声明。上面虽然 service.setInterface(DemoService.class.getName()),这里传入的是接口的str名称。也就是说通过generic,provider已经完全不依赖interface了,只是用到这个接口的名称字符串用做servericekey用的。

之所以能这样操作,消费者在调用的时候,需要在url里面指定generic=true,那么在经过filter链的时候,在GenericImplFilter会做generic处理

把method-name设置成$invoke

parameter-types由于提供者不一定有,于是传入类型的string类型。

args按照pojo或者java-bean进行序列化成一个map,里面指定了参数的class类型、每个filed的value。

provider不会把这个map做逆序列化处理,直接原样返回这个map,所以可以看到这个generic的provider能够处理的东西比较有限。消费者在得到结果的时候,依然是一个map,那么在GenericImplFilter里面,把invoke得到的结果逆序列化成具体的对象。也就是说由GenericImplFilter序列化成一个map,你自己负责逆序列化。

对于泛化引用的话,例子如下:

ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>();
reference.setApplication(new ApplicationConfig("generic-consumer"));
reference.setInterface(DemoService.class);
reference.setUrl("dubbo://127.0.0.1:29581?scope=remote");
reference.setGeneric(true);
GenericService genericService = reference.get();
try {
    List<Map<String, Object>> users = new ArrayList<Map<String, Object>>();
    Map<String, Object> user = new HashMap<String, Object>();
    user.put("class", "com.alibaba.dubbo.config.api.User");
    user.put("name", "actual.provider");
    users.add(user);
    users = (List<Map<String, Object>>) genericService.$invoke("getUsers", new String[]{List.class.getName()}, new Object[]{users});

消费者直接使用$invoke方法,不用考虑provider到底提供了哪些方法,通过字符串getuser调用,参数类型也不用关心,直接用map描述参数的类型和value。

provider在被调用的时候,还是GenericImplFilter这个filter里面,由provider通过map进行逆序列化,毕竟provider提供的方法里面是按照这个参数类型进行处理的,所以必须从map转化成真正的类型,处理完成以后,还要逆序列化成map才能返回给consumer,因为consumer只认识map,不认识具体参数类型。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值