dubbo系列二:dubbo常用功能总结(2)

8. 结果缓存

缓存类型
lru 基于最近最少使用原则删除多余缓存,保持最热的数据被缓存。

LRU的缺省cache.size为1000,执行1001次,会把最开始请求的缓存结果清除掉

在生产者dubbo-provider-web和消费者dubbo-consumer-web分别新建一个接口

复制代码

package com.study.service;

/**
 * 
 * @Description: 结果缓存接口
 * @author leeSmall
 * @date 2018年10月24日
 *
 */
public interface CacheService {
    
    String findCache(String id);
    
}

复制代码

在生产者dubbo-provider-web新建CacheService接口的实现类

复制代码

package com.study.service;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 
 * @Description: 结果缓存接口实现类
 * @author leeSmall
 * @date 2018年10月24日
 *
 */
public class CacheServiceImpl implements CacheService {
    
    private final AtomicInteger i = new AtomicInteger();
    
    public String findCache(String id) {
        System.out.println("request: " + id + ", response: "
                + i.getAndIncrement());
        return "request: " + id + ", response: " + i.getAndIncrement();
    }
}

复制代码

在生产者dubbo-provider-web的applicationProvider.xml配置结果缓存接口

    <!--结果缓存begin  -->              
    <bean id="cacheService" class="com.study.service.CacheServiceImpl"/>
    <dubbo:service interface="com.study.service.CacheService" ref="cacheService"/>
    <!--结果缓存end  -->

在消费者dubbo-consumer-web的applicationConsumer.xml配置调用的缓存接口

    <!--结果缓存begin  -->
    <dubbo:reference id="cacheService" interface="com.study.service.CacheService" cache="lru"/>
    <!--结果缓存end  -->

在消费者dubbo-consumer-web的CommonController.java里面创建缓存接口测试代码

复制代码

    //结果缓存begin
    @Autowired
    CacheService cacheService;
    
    @RequestMapping("/cache")
    public @ResponseBody String cache() {
        // 测试缓存生效,多次调用返回同样的结果。(服务器端自增长返回值)
        String fix = null;
        for (int i = 0; i < 5; i++) {
            String result = cacheService.findCache("0");
            if (fix == null || fix.equals(result)) {
                System.out.println("OK: " + result);
            }
            else {
                System.err.println("ERROR: " + result);
            }
            fix = result;
            try {
                Thread.sleep(500);
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
        // LRU的缺省cache.size为1000,执行1001次,会把第一次请求的缓存结果清除掉
        for (int n = 0; n < 1001; n++) {
            String pre = null;
            for (int i = 0; i < 10; i++) {
                String result = cacheService.findCache(String.valueOf(n));
                if (pre != null && !pre.equals(result)) {
                    System.err.println("ERROR: " + result);
                }
                pre = result;
            }
        }
        
        // 测试LRU有移除最开始的一个缓存项
        String result = cacheService.findCache("0");
        if (fix != null && !fix.equals(result)) {
            System.out.println("OK: " + result);
        }
        else {
            System.err.println("ERROR: " + result);
        }
        return "OK";
    }
   //结果缓存end

复制代码

 在浏览器输入地址http://localhost:8081/dubbo-consumer-web/common/cache访问查看效果:

测试缓存生效,多次调用返回同样的结果。(服务器端自增长返回值):

生产者:

消费者:

LRU的缺省cache.size为1000,执行1001次,会把第一次请求的缓存结果清除掉:

 生产者:

 

测试LRU有移除最开始的一个缓存项:

 生产者:

 

消费者:

 

9. 使用泛化调用

泛化接口调用方式主要用于客户端没有API接口及模型类元的情况,参数及返回值中的所有POJO均用 Map表示,通常用于框架集成,比如:实现一个通用的服务测试框架,可通过GenericService 调用所有服务实现。

泛化调用就是服务消费者端因为某种原因并没有该服务接口,这个原因有很多,比如是跨语言的,一个PHP工程师想调用某个java接口,他并不能按照你约定,去写一个个的接口,Dubbo并不是跨语言的RPC框架,但并不是不能解决这个问题,这个PHP程序员搭建了一个简单的java web项目,引入了dubbo的jar包,使用dubbo的泛化调用,然后利用web返回json,这样也能完成跨语言的调用。泛化调用的好处之一就是这个了。
好了,简而言之,泛化调用,最直接的表现就是服务消费者不需要有任何接口的实现,就能完成服务的调用。

这个功能一般不用,因为代码的可读性很差

在生产者dubbo-provider-web新建一个接口:

复制代码

package com.study.service;

/**
 * 
 * @Description: 泛化调用接口
 * @author leeSmall
 * @date 2018年10月23日
 *
 */
public interface DemoService {
    String eat(Long id);
}

复制代码

在生产者dubbo-provider-web新建两个DemoService接口的实现类

复制代码

package com.study.service;

/**
 * 
 * @Description: 泛化调用接口实现类
 * @author leeSmall
 * @date 2018年10月23日
 *
 */
public class DemoServiceImpl implements DemoService {
    public String eat(Long id) {
        System.out.println("泛化调用");
        return "泛化调用" + id;
    }
}

复制代码

在生产者dubbo-provider-web的applicationProvider.xml配置泛化调用接口

    <!--泛化调用begin-->
    <dubbo:service interface="com.study.service.DemoService" ref="demoService" protocol="dubbo"/>
    <bean id="demoService" class="com.study.service.DemoServiceImpl"/>
    <!--泛化调用end-->

在消费者dubbo-consumer-web的applicationConsumer.xml配置调用的泛化调用接口

    <!--泛化调用begin -->
    <dubbo:reference id="demoService" interface="com.study.service.DemoService" generic="true"/>
    <!--泛化调用end -->

在消费者dubbo-consumer-web的CommonController.java里面创建泛化调用测试代码

复制代码

    //泛化调用begin
    @RequestMapping("/fanhua")
    public @ResponseBody String fanhuayinyong() {
        GenericService demoService = (GenericService)applicationContext.getBean("demoService");
        Object result = demoService.$invoke("eat", new String[] { "java.lang.Long" }, new Object[]{ 1L });
        System.out.println(result);
        return "OK";
    }
    //泛化调用end

复制代码

在浏览器输入地址http://localhost:8081/dubbo-consumer-web/common/fanhua访问查看效果

生产者:

消费者:

10. 实现泛化调用

泛接口实现方式主要用于服务器端没有API接口及模型类元的情况,参数及返回值中的所有POJO均用Map表示,通常用于框架集成,比如:实现一个通用的远程服务Mock框架,可通过实现GenericService接口处理所有服务请求。 

在生产者dubbo-provider-web新建dubbo提供的泛化接口的实现类:

复制代码

package com.study.service;

import com.alibaba.dubbo.rpc.service.GenericException;
import com.alibaba.dubbo.rpc.service.GenericService;

/**
 * 
 * @Description: dubbo提供的泛化接口的实现类
 * @author leeSmall
 * @date 2018年10月24日
 *
 */
public class MyGenericService implements GenericService {
    
    public Object $invoke(String method, String[] parameterTypes, Object[] args)
            throws GenericException {
        System.out.println("泛化调用实现!");
        return "泛化调用实现";
    }
    
}

复制代码

 在生产者dubbo-provider-web的applicationProvider.xml配置泛化接口的实现类

    <!--实现泛化调用begin  -->
    <bean id="genericService" class="com.study.service.MyGenericService" />
    <dubbo:service interface="com.alibaba.dubbo.rpc.service.GenericService" ref="genericService" />
    <!--实现泛化调用end  -->

 在消费者dubbo-consumer-web的applicationConsumer.xml配置调用的泛化接口的实现

    <!--实现泛化调用begin  -->
    <dubbo:reference id="genericService" interface="com.alibaba.dubbo.rpc.service.GenericService"/>
    <!--实现泛化调用end  -->

在消费者dubbo-consumer-web的CommonController.java里面创建泛化接口的实现测试代码

复制代码

    //实现泛化调用begin
    @RequestMapping("/fanhuashixian")
    public @ResponseBody String fanhuashixian() {
        GenericService barService = (GenericService)applicationContext.getBean("genericService");
        Object result = barService.$invoke("login",
                new String[] {"java.lang.String"},
                new Object[] {"World"});
        System.out.println(result);
        return "OK";
    }
    //实现泛化调用end

复制代码

在浏览器输入地址http://localhost:8081/dubbo-consumer-web/common/fanhuashixian访问查看效果

生产者:

 

消费者:

 

11. 回声测试

回声测试用于检测服务是否可用,回声测试按照正常请求流程执行,能够测试整个调用是否通畅,可用于监控。
所有服务自动实现EchoService接口,只需将任意服务引用强制转型为EchoService,即可使用。

直接使用第8个功能点(8.结果缓存)的接口进行回声测试的实现

 在消费者dubbo-consumer-web的CommonController.java里面创建回声测试的测试代码:

复制代码

    //回声测试begin
    @RequestMapping("/huisheng")
    public @ResponseBody String huisheng() {
        CacheService barService = (CacheService)applicationContext.getBean("cacheService");
        EchoService echoService = (EchoService)barService; // 强制转型为EchoService
        // 回声测试可用性
        String status = (String)echoService.$echo("OK");
        if(status.equals("OK")) {
            System.out.println("缓存接口服务可用");
        }
        assert (status.equals("OK"));
        return "OK";
    }
    //回声测试end

复制代码

在浏览器输入地址http://localhost:8081/dubbo-consumer-web/common/huisheng访问查看效果

 消费者:

12. 异步调用

 基于NIO的非阻塞实现并行调用,客户端不需要启动多线程即可完成并行调用多个远程服务,相对多线程开销较小。

在生产者dubbo-provider-web和消费者dubbo-consumer-web分别新建一个接口:

复制代码

package com.study.service;

/**
 * 
 * @Description: 异步调用接口
 * @author leeSmall
 * @date 2018年10月24日
 *
 */
public interface AsyncService {
    
    String sayHello(String name);
    
}

复制代码

在生产者dubbo-provider-web新建AsyncService接口的实现类

复制代码

package com.study.service;


/**
 * 
 * @Description: 异步调用接口实现类
 * @author leeSmall
 * @date 2018年10月24日
 *
 */
public class AsyncServiceImpl implements AsyncService {
    
    public String sayHello(String name) {
        for (int i = 0; i < 5; i++) {
            System.out.println("async provider received: " + name);
            try {
                Thread.sleep(1000);
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return "hello, " + name;
    }
    
}

复制代码

 在生产者dubbo-provider-web的applicationProvider.xml配置异步调用接口

    <!--异步调用begin  -->
    <bean id="asyncServiceImpl" class="com.study.service.AsyncServiceImpl"/>
    <dubbo:service interface="com.study.service.AsyncService" ref="asyncServiceImpl"/>
    <!--异步调用end  -->

在消费者dubbo-consumer-web的applicationConsumer.xml配置调用的异步调用接口

    <!--异步调用begin  -->
    <dubbo:reference id="asyncServiceImpl" interface="com.study.service.AsyncService"  timeout="1200000">
        <dubbo:method name="sayHello" async="true"/>      
    </dubbo:reference>
    <!--异步调用end  -->

在消费者dubbo-consumer-web的CommonController.java里面创建异步调用测试代码

复制代码

    //异步调用begin
    @Autowired
    AsyncService asyncService;
    
    
    @RequestMapping("/async")
    public @ResponseBody String async() {
        String result = asyncService.sayHello("我是异步调用客户端");
        System.out.println(result);
        // 拿到调用的Future引用,当结果返回后,会被通知和设置到此Future
        Future<String> fooFuture = RpcContext.getContext().getFuture();
        // 如果result已返回,直接拿到返回值,否则线程wait住,等待result返回后,线程会被notify唤醒
        try {
            //调用get方法会阻塞在这里直到拿到结果
            result = fooFuture.get();
            System.out.println(result);
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
        catch (ExecutionException e) {
            e.printStackTrace();
        }
        return "OK";
    }
    //异步调用end

复制代码

 在浏览器输入地址http://localhost:8081/dubbo-consumer-web/common/async访问查看效果:

生产者:

 

消费者:

 

13. 参数回调

 参数回调方式与调用本地callback或listener相同,只需要在Spring的配置文件中声明哪个参数是callback 类型即可。Dubbo将基于长连接生成反向代理,这样就可以从服务器端调用客户端逻辑。适用于服务端完成某个动作以后通知客户端

 在生产者dubbo-provider-web和消费者dubbo-consumer-web分别新建一个参数回调监听接口和参数回调接口

 参数回调监听接口:

复制代码

package com.study.callback;

/**
 * 
 * @Description: 参数回调监听接口
 * @author leeSmall
 * @date 2018年10月25日
 *
 */
public interface CallbackListener {
    
    void changed(String msg);
    
}

复制代码

 参数回调接口:

复制代码

package com.study.callback;

/**
 * 
 * @Description: 参数回调接口
 * @author leeSmall
 * @date 2018年10月25日
 *
 */
public interface CallbackService {
    
    void addListener(String key, CallbackListener listener);
    
}

复制代码

在生产者dubbo-provider-web新建CallbackService接口的实现类

复制代码

package com.study.callback;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 
 * @Description: 参数回调接口实现类
 * @author leeSmall
 * @date 2018年10月25日
 *
 */
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());
    }
    
}

复制代码

 在生产者dubbo-provider-web的applicationProvider.xml配置参数回调接口

复制代码

    <!--参数回调begin  -->
    <bean id="callbackService" class="com.study.callback.CallbackServiceImpl"/>
    <dubbo:service interface="com.study.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>
    <!--参数回调end  -->

复制代码

 在消费者dubbo-consumer-web的applicationConsumer.xml配置调用的参数回调接口

    <!--参数回调begin  -->
    <dubbo:reference id="callbackService" interface="com.study.callback.CallbackService"/>
    <!--参数回调end  -->

在消费者dubbo-consumer-web的CommonController.java里面创建参数回调接口测试代码

复制代码

    //参数回调begin
    @Autowired
    CallbackService callbackService;
    
    @RequestMapping("/callback")
    public @ResponseBody String callback() {
        callbackService.addListener("foo.bar", new CallbackListener() {
            public void changed(String msg) {
                System.out.println("callback1:" + msg);
            }
        });
        return "OK";
    }
    //参数回调end

复制代码

 在浏览器输入地址http://localhost:8081/dubbo-consumer-web/common/callback访问查看效果:

生产者:

 

消费者:

 14. 事件通知

在调用之前、调用之后、出现异常时,会触发 oninvoke、onreturn、onthrow 三个事件,可以配置当事件发生时,通知哪个类的哪个方法 。

在生产者dubbo-provider-web和消费者dubbo-consumer-web分别新建一个接口

复制代码

package com.study.event;

/**
 * 
 * @Description: 事件通知接口参与接口
 * @author leeSmall
 * @date 2018年10月25日
 *
 */
public interface Common {
    public String eat(String param);
}

复制代码

在生产者dubbo-provider-web新建Common接口的实现类

复制代码

package com.study.event;

/**
 * 
 * @Description: 事件通知接口参与接口实现类
 * @author leeSmall
 * @date 2018年10月25日
 *
 */
public class CommonImpl implements Common {
    
    public String eat(String param) {
        System.out.println("CommonImpl eat");
        return "CommonImpl eat";
    }
    
}

复制代码

在生产者dubbo-provider-web的applicationProvider.xml配置事件通知接口参与接口

    <!-- 事件通知接口参与接口begin -->
    <bean id="commonImpl" class="com.study.event.CommonImpl"/>
    <dubbo:service interface="com.study.event.Common" ref="commonImpl"/>
    <!-- 事件通知接口参与接口end -->

在消费者dubbo-consumer-webl创建事件通知接口

复制代码

package com.study.event;

/**
 * 
 * @Description: 事件通知接口
 * @author leeSmall
 * @date 2018年10月25日
 *
 */
public interface Notify {
    
    //调用之前
    public void oninvoke(String name); 
    
    //调用之后
    public void onreturn(String msg);
    
    // 出现异常
    public void onthrow(Throwable ex);
}

复制代码

在消费者dubbo-consumer-web创建事件通知接口实现类

复制代码

package com.study.event;

/**
 * 
 * @Description: 事件通知接口实现类
 * @author leeSmall
 * @date 2018年10月25日
 *
 */
public class NotifyImpl implements Notify {
    
    //调用之前
    public void oninvoke(String msg) {
        System.out.println("======oninvoke======, param: " + msg);
    }
    
    //调用之后
    public void onreturn(String msg) {
        System.out.println("======onreturn======, param: " + msg);
    }
    
     // 出现异常
    public void onthrow(Throwable ex) {
        System.out.println("======onthrow======, param: " + ex);
    }

    
    
}

复制代码

在消费者dubbo-consumer-web的applicationConsumer.xml配置调用事件通知接口参与接口和事件通知接口

复制代码

    <!--事件通知begin  -->
    <bean id ="demoCallback" class = "com.study.event.NotifyImpl" />
    <dubbo:reference id="commonImpl" interface="com.study.event.Common" >
        <dubbo:method name="eat" async="false" oninvoke="demoCallback.oninvoke" onreturn = "demoCallback.onreturn" onthrow=
            "demoCallback.onthrow" />
    </dubbo:reference>
    <!--事件通知end  -->

复制代码

在消费者dubbo-consumer-web的CommonController.java里面创建事件通知接口测试代码

复制代码

    //事件通知begin
    @Autowired
    Common common;
    
    
    @RequestMapping("/event")
    public @ResponseBody String event() {
        String result = common.eat("jdksk");
        System.out.println(result);
        return "OK";
    }
    //事件通知end

复制代码

 在浏览器输入地址http://localhost:8081/dubbo-consumer-web/common/cache访问查看效果:

生产者:

 

消费者:

 

 

 15. 本地存根

 远程服务后,客户端通常只剩下接口,而实现全在服务器端,但提供方有些时候想在客户端也执行部分逻辑,比如:做 ThreadLocal缓存,提前验证参数,调用失败后伪造容错数据等等,此时就需要在API中带上Stub,客户端生成Proxy实例,会把Proxy通过构造函数传给Stub ,然后把Stub暴露给用户,Stub可以决定要不要去调Proxy。

 在生产者dubbo-provider-web和消费者dubbo-consumer-web分别新建一个接口

复制代码

package com.study.stub;

/**
 * 
 * @Description: 本地存根接口
 * @author leeSmall
 * @date 2018年10月25日
 *
 */
public interface StubService {
    String stub(String param);
}

复制代码

在生产者dubbo-provider-web新建StubService接口的实现类

复制代码

package com.study.stub;

/**
 * 
 * @Description: 本地存根接口实现类
 * @author leeSmall
 * @date 2018年10月25日
 *
 */
public class StubServiceImpl implements StubService {
    
    public String stub(String param) {
        System.out.println("provider StubServiceImpl stub");
        return "provider StubServiceImpl stub";
    }
    
}

复制代码

在生产者dubbo-provider-web的applicationProvider.xml配置本地存根接口

    <!--本地存根begin  -->
    <bean id="stubServiceImpl" class="com.study.stub.StubServiceImpl"/>
    <dubbo:service interface="com.study.stub.StubService" ref="stubServiceImpl"/>
    <!--本地存根end  -->

在消费者dubbo-consumer-web创建本地存根Proxy实例

复制代码

package com.study.stub;

/**
 * 
 * @Description: 本地存根Proxy实例
 * @author leeSmall
 * @date 2018年10月25日
 *
 */
public class LocalStubServiceProxy implements StubService {
    
    private StubService stubService;
    
    public LocalStubServiceProxy(StubService stubService) {
        this.stubService = stubService;
    }
    
    public String stub(String arg0) {
        System.out.println("我是localstub,我就相当于一个过滤器,在代理调用远程方法的时候,我先做一下拦截工作");
        
        try {
            return stubService.stub("xxx");
        }
        catch (Exception e) {
            System.out.println("远程调用出现异常了,我是本地stub,我要对远程服务进行伪装,达到服务降级的目的");
            return "远程调用出现异常了,我是本地stub,我要对远程服务进行伪装,达到服务降级的目的";
        }
    }
    
}

复制代码

在消费者dubbo-consumer-web的applicationConsumer.xml配置本地存根接口和stub对应的本地存根Proxy实例

    <!--本地存根begin  -->
    <dubbo:reference id="stubServiceImpl" interface="com.study.stub.StubService" stub="com.study.stub.LocalStubServiceProxy"/>
    <!--本地存根end  -->

 在消费者dubbo-consumer-web的CommonController.java里面创建本地存根接口测试代码

复制代码

    //本地存根begin
    @Autowired
    StubService stub;
    
    @RequestMapping("/stub")
    public @ResponseBody String stub() {
        String result = stub.stub("eee");
        System.out.println(result);
        return "OK";
    }
    //本地存根end 

复制代码

 在浏览器输入地址http://localhost:8081/dubbo-consumer-web/common/stub访问查看效果:

生产者:

 

 

 消费者:

 

16. 本地伪装

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值