3.3 Dubbo结果缓存
3.3.1结果缓存
缓存的好处不用多说,在传统的业务开发中,引入缓存可以帮助减轻数据库的压力,在分布式的系统中,引入缓存有两种方式,第一种是在调用者,也就是消费者端引入缓存,如果能够命中缓存,则默认读取缓存中的内容,不需要再进行远程调用,这样就减少了一次IO 请求,也能减少服务提供者的压力,第二种就是在服务提供者提供缓存,同样的请求,把对请求的结果缓存在某个地方,比如Redis,或者java内存中,亦或者第三方的内存容器里,当数据库有更新的时候,同步更新内存,当新的请求在缓存中没有命中的时候,就会击穿缓存落到数据库实例上。
以上就是缓存设计的基本也是最简单的实现方式,不过如何提高缓存的命中率,有很多的算法,有Least Frequently Used(LFU),LeastRecently User(LRU),First in First out(FIFO)等等的算法。
这里不是我们讨论的重点,详细可以参考http://blog.jobbole.com/30940/
Dubbo也支持缓存,并且是在服务消费者实现了缓存,因为Dubbo是一个纯粹的服务调用框架,所以它并没有实现高性能高命中的缓存策略,其实也没有办法实现,因为没有具体的业务场景,如何提高缓存命中率,跟业务是息息相关的,哪些是热点数据,在具体业务中才会知道,所以Dubbo只提供了三种缓存方案
本小节,只对LRU的实现给出基本Demo,其他的算法实现将在后面的章节再一起分析
3.3.2 LRU缓存Demo的基本实现
因为刚才已经说了,Dubbo的缓存是在服务消费者调用端进行配置的,所以服务提供者端,不需要做任何特殊的处理,我们按照惯例,先定义一个接口(服务消费者和服务提供者端同路径下都要有):
package org.bazinga.service;
public interface CacheService {
public String getResultByName(String name);
}
服务提供者给出具体的实现CacheServiceImpl.java:
package org.bazinga.service.impl;
import java.util.concurrent.atomic.AtomicInteger;
import org.bazinga.service.CacheService;
public class CacheServiceImpl implements CacheService {
private AtomicInteger index = new AtomicInteger();
public String getResultByName(String name) {
Integer callCount = index.getAndIncrement();
System.out.println("paramter is "+ name +" call time is "+ callCount);
if(name == null){
return "hello hero";
}else if(name.equals("Lyn")){
return "hello Lyn";
}else{
return "hello War3";
}
}
}
服务提供者端的配置文件也不要做什么大改变,与普通的dubbo提供者配置文件是一样的spring-dubbo-provider-cache.xml
测试类DubboxProviderCacheService.java:
package org.bazinga.service.test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class DubboxProviderCacheService {
public static void main(String[] args) throws InterruptedException {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"spring-dubbo-provider-cache.xml");
context.start();
Thread.sleep(2000000l);
}
}
就是这么简单的配置就可以完成这样缓存的功能了,编写测试类:
package org.bazinga.service.test;
import org.bazinga.service.CacheService;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class DubboConsumerCacheService {
public static void main(String[] args) throws InterruptedException {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"spring-dubbo-consumer-cache.xml");
context.start();
CacheService cacheService = (CacheService)context.getBean("cacheService");
for(int i = 0;i <100;i++){
System.out.println(cacheService.getResultByName("Lyn"));
}
}
}
好了,到此为止,所有的配置文件就已经完成了,我们进入测试环节,我们在CacheServiceImpl.java记录了被调用的次数,服务消费者循环调用了100次,如果正常情况下,服务提供者的控制台应该打印被调用了100次,引入缓存之后,服务消费者缓存了结果,所以调用的次数被大大减小
,我们先启动DubboxProviderCacheService.java的main函数,然后再启动DubboConsumerCacheService.java的main函数:
服务消费者的控制台打印结果:
“Hello Lyn”这样的字段重复了100遍,我们再看看关键的服务提供端的控制台打印消息:
可以看到服务提供者只被消费了一次,这样就说明缓存生效了,虽然服务调用方调用了100次,但是真正请求发送到服务提供者的只有一次,LRU缓存算法就是如此,把最近最少使用的缓存对象给踢走,实现的策略就是把新的请求和对应的请求结果放在缓存的顶部,当缓存达到容量极限的时候,把底部对象剔除,所以经常被读取的缓存对象就会一直呆在缓存池中。有两种方法可以实现我,Array 或者是linked list
3.3.3 本章小结
本章只是简单的介绍了一下Dubbo对缓存的支持,简单的写了一个基于LRU的Demo,帮助大家入门对本模块的理解,更加详细的缓存,在下面的章节会再具体介绍。