Dubbo泛化调用网关初试

由于之前网关采用的是dubbo的rest协议,但使用一段时间发现速度有些慢,而且总感觉不如直接使用泛化调用来的爽,所以打算研究一下dubbo的泛化调用,在此记录一下,参照了很多大神的思路,水平有限,欢迎指正。

dubbo泛化调用的原理就不细讲了,网上有很多文章,底层基于netty做数据传输,进行rpc调用,netty是个重要的基础,有兴趣的同学可以研究一下,包括它的编码,解码,序列化等等,对理解dubbo框架有很好的帮助的,废话少说,我记录一下我的改造过程中涉及到的几个方面:

1.zkClient,用于监听zk的变化,这里我使用redis作为缓存,把zk的变化记录了下来,有变化时,实时更新缓存,保证时效性。

   在初始化的时候,获取zk的地址

@PostConstruct
public void init() {
    rootPath = zkConfiguration.getRootPath();
    zkServers = zkConfiguration.getServer();
    zkClient = new ZkClient(zkServers, 5000);
    if (!rootPath.startsWith(SLASH)) {
        rootPath = SLASH + rootPath;
    }

    if (loadBalancerService == null) {
        loadBalancerService = new RandomLoadBalanceImpl();
    }
    runaway(zkClient, rootPath);
}

  监听zk的变化,放入redis

private void runaway(final ZkClient zkClient, final String path) {
    zkClient.unsubscribeAll();
    ConcurrentHashMap<String, List<String>> newHosts = new ConcurrentHashMap<String, List<String>>();
    zkClient.subscribeChildChanges(path, new IZkChildListener() {

        public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {
            /*
             * System.out.println(parentPath +
             * " 's child changed, currentChilds:" + currentChilds);
             */
            logger.info("{}'s child changed, currentChilds:{}", parentPath, currentChilds);
            // 一级节点的子节点发生变化
            runaway(zkClient, path); // once more

        }

    });

    List<String> firstGeneration = zkClient.getChildren(path); // 二级节点
                                                               
    if (firstGeneration != null && firstGeneration.size() > 0) {
        for (String child : firstGeneration) {
            String firstNextPath = path + "/" + child;
            zkClient.subscribeChildChanges(firstNextPath, new IZkChildListener() {

                public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {
                    /*
                     * System.out.println(parentPath +
                     * " 's child changed, currentChilds:" + currentChilds);
                     */
                    logger.info("{}'s child changed, currentChilds:{}", parentPath, currentChilds);
                    // 2级节点的子节点发生
                    runaway(zkClient, path); 

                }

            });

            List<String> secondGeneration = zkClient.getChildren(firstNextPath); // 三级子节点
                                                                                
            if (secondGeneration != null && secondGeneration.size() > 0) {
                for (String secondChild : secondGeneration) {
                    if (secondChild.startsWith(PROVIDERS)) {
                        String secondNextPath = firstNextPath + "/" + secondChild;
                        zkClient.subscribeChildChanges(secondNextPath, new IZkChildListener() {

                            public void handleChildChange(String parentPath, List<String> currentChilds)
                                    throws Exception {
                                /*
                                 * System.out .println(parentPath +
                                 * " 's child changed, currentChilds:" +
                                 * currentChilds);
                                 */
                                logger.info("{}'s child changed, currentChilds:{}", parentPath, currentChilds);
                                // 3级节点的子节点发生
                                runaway(zkClient, path); 

                            }

                        });

                        List<String> thirdGeneration = zkClient.getChildren(secondNextPath);// 4级子节点
                                                                                            
                        if (thirdGeneration != null && thirdGeneration.size() > 0) {
                            for (String thirdChild : thirdGeneration) {
                                if (thirdChild.startsWith(DUBBO)) {

                                    try {
                                       initDubboService(URLDecoder.decode(thirdChild, CommonCodeConstants.MDF_CHARSET_UTF_8));
                                    } catch (UnsupportedEncodingException e) {
                                        e.printStackTrace();
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

    }

    synchronized (this) {
        hosts.clear();
        hosts.putAll(newHosts);
    }
}

截取的地址,不知道是否还有更好的方式,欢迎大家指导

public  void initDubboService(String url) {

   String[] urls =  url.split("\\?");

   String key = urls[0].substring(urls[0].lastIndexOf("/")+1);

   String path = urls[1];

   String[] paths = path.split("\\&");

   String serviceName = "";
   for (int i=0;i<paths.length;i++){
        if(paths[i].startsWith("interface")){

            serviceName = paths[i].split("=")[1];
            break;
        }

   }
    redisClient.setValue("dubbo_"+key,serviceName,666666666666l);
}

2.下面就是泛化调用的客户端了,这个ReferenceConfig实例比较重需要缓存一下

public class DubboCallbackUtil {

    private static Logger logger = LogManager.getLogger(DubboCallbackUtil.class);

    // 应用的信息
    private static ApplicationConfig application = new ApplicationConfig();
    // 注册中心缓存
    private static Map<String, RegistryConfig> registryConfigCache = new ConcurrentHashMap<>();

    // ReferenceConfig缓存
    private static Map<String, ReferenceConfig> referenceCache = new ConcurrentHashMap<>();

    static {
        application.setName("consumer-test");
    }

    /**
     * 注册中心信息
     *
     */
    private static RegistryConfig getRegistryConfig(String address, String group, String version) {
        String key = address + "-" + group + "-" + version;
        RegistryConfig registryConfig = registryConfigCache.get(key);
        if (null == registryConfig) {
            registryConfig = new RegistryConfig();
            if (StringUtils.isNotEmpty(address)) {
                registryConfig.setAddress(address);
            }
            if (StringUtils.isNotEmpty(version)) {
                registryConfig.setVersion(version);
            }
            if (StringUtils.isNotEmpty(group)) {
                registryConfig.setGroup(group);
            }
            registryConfigCache.put(key, registryConfig);
        }
        return registryConfig;
    }

    private static ReferenceConfig getReferenceConfig(String interfaceName, String address,
                                                      String group, String version) {
        String referenceKey = interfaceName;

        ReferenceConfig referenceConfig = referenceCache.get(referenceKey);
        if (null == referenceConfig) {

                referenceConfig = new ReferenceConfig<>();
                referenceConfig.setApplication(application);
                referenceConfig.setRegistry(getRegistryConfig(address, group, version));
                referenceConfig.setInterface(interfaceName);
                if (StringUtils.isNotEmpty(version)) {
                    referenceConfig.setVersion(version);
                }
                referenceConfig.setGeneric(true);

                referenceCache.put(referenceKey, referenceConfig);

        }
        return referenceConfig;
    }

    public static Object invoke(String interfaceName, String methodName, Object[] paramObject, String address, String version) {
        ReferenceConfig reference = getReferenceConfig(interfaceName, address, null, version);
        if (null != reference) {
            GenericService genericService = (GenericService) reference.get();
            if (genericService == null) {

                return null;
            }

            Object resultParam = genericService.$invoke(methodName,new String[]{new String().getClass().getName(),new String().getClass().getName()}, paramObject);
            return resultParam;
        }
        return null;
    }

    
}

3.调用的时候由于业务需求和兼容之前前端接口,往后端传了两个参数,这个可以随意,下面就是调用了

JSONObject jsonObject = JSON.parseObject(reqBody);
Token token = new Token();

String retContent = (String)DubboCallbackUtil.invoke((String)redisClient.getValue("dubbo_"+jsonObject.get("Id")),(String)jsonObject.get("method"),new String[]{jsonObject.toJSONString(),JSON.toJSONString(token,SerializerFeature.WriteMapNullValue)},"zookeeper://"+zkServer,"");

其中业务接口是监听zk变化存在缓存中的,method前端传过来的,剩下的就是json的参数了,先写到这里,后面还有服务端如何解析这些参数,因为后端接受的都是对象,需要通过过滤器来重新转换json的字符串为对应的类型,这个就需要个人发挥啦!

原创文章 2 获赞 5 访问量 5846

猜你喜欢

转载自blog.csdn.net/airyearth/article/details/105877699