關於GeneratorService泛化引用Dubbo的快取問題
阿新 • • 發佈:2018-12-10
在泛化引用dubbo時,因為referencrConfig是一個很重的例項,所以需要使用到快取
簡單呼叫時
1.dubbo自帶的XX快取,快取自帶的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;
}