在泛化引用dubbo时,因为referencrConfig是一个很重的实例,所以需要使用到缓存
简单调用时
1.dubbo自带的ReferenceConfig缓存,缓存自带的cacheKey
完整代码:
public static void main(String[] args) {
// 应用设置
ApplicationConfig application = new ApplicationConfig();
application.setName("dubbo-user");
// 注册中心
RegistryConfig registry = new RegistryConfig();
registry.setProtocol("zookeeper");
registry.setAddress("192.168.0.1");
registry.setPort(2181);
// 服务引用
ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>();
reference.setApplication(application);
reference.setRegistry(registry);
reference.setInterface(UserService.class.getName());//完整类名
reference.setVersion("0.0.2");
reference.setGroup("group001");
reference.setGeneric(true); // 泛化
reference.setAsync(true); // 异步
reference.setCache("lru");
// 直接引用,每次都要建立连接,比较重
//GenericService genericService = reference.get(); //泛化处理
//UserService xxxService = reference.get(); //非泛化处理
// 通过使用cache进行缓存
ReferenceConfigCache cache = ReferenceConfigCache.getCache();
GenericService genericService = cache.get(reference);
// 泛化调用
Object resultUser = genericService.$invoke("queryUser", new String[]{"java.lang.Integer"}, new Object[]{1});
// 异步获取泛化调用结果
Future<Object> fooFuture = RpcContext.getContext().getFuture();
try {
resultUser = fooFuture.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
Map<String, Object> userMap=(Map<String, Object>) resultUser;
String jsonObject = JSONObject.valueToString(userMap);
System.out.println(jsonObject);
核心代码:
ReferenceConfigCache cache = ReferenceConfigCache.getCache();
GenericService genericService = cache.get(reference);
2.使用默认的dubbo缓存,有时候会产生一些问题或者隐患:比如:dubbo提供的缓存,默认的cacheKey只有dubbo的三个属性:group/interfaceName:version;下面这种情况:两个接口A1和A2,同名同分组同版本号,发布在不同的注册中心,然后用泛化引用去调用:
假设首先调用接口A1,此时cache将对应的genericService以默认cacheKey存在其中;接着调用接口A2时,会直接获取到缓存中调用A1时的service来使用,因为两个服务的cache是相同的;这就容易产生错误了;(同样的同一接口发布在不同的注册中心时,若第一次调用服务的注册中心虚机停掉了,即使我们另一个注册中心的虚机正常运行,也只会调用前者而调用失败),基于这种情况,我们选择 指定缓存的KeyGenerator,即cacheKey,代码如下:
public ReferenceConfigCache.KeyGenerator CACHE_KEY_GENERATOR = new ReferenceConfigCache.KeyGenerator() {
@Override
public String generateKey(ReferenceConfig<?> referenceConfig) {
String iName = referenceConfig.getInterface();
if(com.alibaba.dubbo.common.utils.StringUtils.isBlank(iName)) {
Class<?> clazz = referenceConfig.getInterfaceClass();
iName = clazz.getName();
}
if(com.alibaba.dubbo.common.utils.StringUtils.isBlank(iName)) {
throw new IllegalArgumentException("No interface info in ReferenceConfig" + referenceConfig);
}
StringBuilder ret = new StringBuilder();
if(! com.alibaba.dubbo.common.utils.StringUtils.isBlank(referenceConfig.getGroup())) {
ret.append(referenceConfig.getGroup()).append("/");
}
ret.append(iName);
if(! com.alibaba.dubbo.common.utils.StringUtils.isBlank(referenceConfig.getVersion())) {
ret.append(":").append(referenceConfig.getVersion());
}
String registry = referenceConfig.getRegistry().getAddress();
ret.append(":").append(registry);
return ret.toString();
}
};
private GenericService getServiceByCache(RequestModel requestModel) {
ReferenceConfig<GenericService> referenceConfig = createReferenceConfig(requestModel);
//dubbo提供的缓存
ReferenceConfigCache cache = ReferenceConfigCache.getCache("dubboCache",CACHE_KEY_GENERATOR);//自定义cacheKey,KeyGenerator为 group/interface:version:registryAddress:port
return cache.get(referenceConfig);
}
自定义缓存的key,KeyGenerator为 分组/接口名:版本号:注册中心地址:注册中心端口号
3.然后,又遇到问题了。。。。我们调用一个dubbo服务,第一次调用的时候,dubbo是关闭的,此时缓存中存了一个GenericService ,当我们开启dubbo服务后再次调用,依然会失败,只有将缓存清空后调用才可以,(至于具体原因,暂未深入研究,后续补充);鉴于此,改为使用google guava的缓存来实现;
//缓存保存(接口名称,泛型service)
private Cache<String, GenericService> servicesCache= CacheBuilder.newBuilder().expireAfterAccess(
1L, TimeUnit.MINUTES).maximumSize(200).build();
//cacheKey方法
public String generateKey(ReferenceConfig<?> referenceConfig) {
String iName = referenceConfig.getInterface();
if(com.alibaba.dubbo.common.utils.StringUtils.isBlank(iName)) {
Class<?> clazz = referenceConfig.getInterfaceClass();
iName = clazz.getName();
}
if(com.alibaba.dubbo.common.utils.StringUtils.isBlank(iName)) {
throw new IllegalArgumentException("No interface info in ReferenceConfig" + referenceConfig);
}
StringBuilder ret = new StringBuilder();
if(! com.alibaba.dubbo.common.utils.StringUtils.isBlank(referenceConfig.getGroup())) {
ret.append(referenceConfig.getGroup()).append("/");
}
ret.append(iName);
if(! com.alibaba.dubbo.common.utils.StringUtils.isBlank(referenceConfig.getVersion())) {
ret.append(":").append(referenceConfig.getVersion());
}
String registry = referenceConfig.getRegistry().getAddress();
ret.append(":").append(registry);
return ret.toString();
}
//获取缓存中的service
private GenericService getServiceByCache(RequestModel requestModel) {
ReferenceConfig<GenericService> referenceConfig = createReferenceConfig(requestModel);
String serviceKey = generateKey(referenceConfig);
GenericService service = null;
try {
service = servicesCache.get(serviceKey.toString(), new Callable<GenericService>() {
@Override
public GenericService call() throws Exception {
GenericService genericService = referenceConfig.get();
servicesCache.put(serviceKey.toString(), genericService);
return genericService;
}
});
} catch (java.util.concurrent.ExecutionException e) {
log.error(e);
}
return service;
}
//refresh
/**
*
* @param
* @return
*/
private ReferenceConfig createReferenceConfig(RequestModel requestModel) {
ApplicationConfig applicationConfig = new ApplicationConfig(requestModel.getDubApplicationName());
//多个注册中心
List<RegistryConfig> registries = new ArrayList<>();
String[] dubboUrls = requestModel.getDubAddress().split(",");
for(String url:dubboUrls){
RegistryConfig registry = new RegistryConfig(String.join("", requestModel.getDubProtocol(), "://", url));
registries.add(registry);
}
ConsumerConfig consumerConfig = new ConsumerConfig();
if (!StringUtils.isEmpty(requestModel.getGroup())) {
consumerConfig.setGroup(requestModel.getGroup());
}
if (!StringUtils.isEmpty(requestModel.getDubTimeout())) {
consumerConfig.setTimeout(requestModel.getDubTimeout());
}
if (!StringUtils.isEmpty(requestModel.getVersion())) {
consumerConfig.setVersion(requestModel.getVersion());
}
consumerConfig.setRetries(0);
//接口引用
ReferenceConfig referenceConfig = new ReferenceConfig<>();
referenceConfig.setApplication(applicationConfig);
referenceConfig.setConsumer(consumerConfig);
referenceConfig.setRegistries(registries);
referenceConfig.setInterface(requestModel.getInterfaceName());
if(requestModel.getGroup() != null){
referenceConfig.setGroup(requestModel.getGroup());
}
if(requestModel.getVersion() != null){
referenceConfig.setVersion(requestModel.getVersion());
}
referenceConfig.setGeneric(true);
return referenceConfig;
}