关于GeneratorService泛化引用Dubbo的缓存问题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ydk888888/article/details/82774117

在泛化引用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;
    }

猜你喜欢

转载自blog.csdn.net/ydk888888/article/details/82774117