当前位置: 首页 > news >正文

如何动态获取Dubbo服务提供方地址列表

一、前言

dubbo框架本身提供了丰富的负载均衡策略,比如轮询、随机、最少活跃调用数、一致性hash等,但是有时候我们需要自己根据业务指定某个ip来进行调用。要指定ip进行调用就需要先知道服务提供者的ip。本文我们先来探讨第一步,当服务注册中心使用zookeeper时候如何获取某一个服务的提供端的地址列表。

二、实现

我们知道当服务提供方启动时候,会注册服务到服务注册中心,本文我们通用zookeeper,比如服务com.books.dubbo.demo.api.GreetingService则注册到zk后,是下面树形结构


那么当消费端启动时候会去zookeeper上订阅path为/dubbo/com.books.dubbo.demo.api.GreetingService/providers下面的信息,也就是服务提供者列表信息,那么我们就可以基于这个原理来获取某一个服务提供者列表,然后对信息进行过滤加工,并且注册一个监听器,当服务提供者机器增减后,动态更新保存的地址列表。

基于上面原理实现代码如下:

public class ZookeeperIpList {

    private String dataId = "com.books.dubbo.demo.api.GreetingService/providers:1.0.0";
    private URL CONSUMER_URL;
    private static final Joiner j = Joiner.on("|").useForNull("nil");

    public final List<String> getIpList() {
        return ipList;
    }

    private volatile List<String> ipList = new ArrayList<String>();

    //对获取的列表内容进行过滤
    private static List<URL> toUrlsWithoutEmpty(URL consumer, List<String> providers) {
        List<URL> urls = new ArrayList<URL>();
        if (providers != null && providers.size() > 0) {
            urls = providers.stream().map(provider -> URL.decode(provider)).filter(provider -> provider.contains("://"))
                    .map(provider -> URL.valueOf(provider)).filter(url -> UrlUtils.isMatch(consumer, url))
                    .collect(Collectors.toList());
        }
        
        return urls;
    }

    // 解析服务提供者地址列表为ip:port格式
    private void parseIpList(List<String> ipSet) {

        List<URL> urlList = toUrlsWithoutEmpty(CONSUMER_URL, ipSet);
        final List<String> ipListTemp = urlList.stream().map(url -> url.getAddress()).collect(Collectors.toList());
        this.ipList = ipListTemp;

    }

    public void init(String zkServerAddr, String zkGroup, String dataId, String serviceGroup) {
        // 1.参数校验
        Assert.notNull(zkServerAddr, "zkServerAddr is null.");
        Assert.notNull(dataId, "dataId is null.");
        Assert.notNull(dataId, "zkGroup is null.");
        Assert.notNull(dataId, "serviceGroup is null.");

        // 2.拼接订阅的path
        String[] temp = dataId.split(":");
        if (temp.length != 2) {
            throw new RuntimeException("dataId is illegal");
        }

        this.dataId = "/" + zkGroup + "/" + temp[0] + "/providers";
        String consumeUrl = "consumer://127.0.0.1/?group=" + serviceGroup + "&interface=" + temp[0] + "&version="
                + temp[1];
        CONSUMER_URL = URL.valueOf(consumeUrl);

        // 3.开启zk,订阅path路径下服务提供者信息,并添加监听器
        System.out.println(j.join("init zk ", zkServerAddr, this.dataId, consumeUrl));
        ZkClient zkClient = new ZkClient(zkServerAddr);
        List<String> list = zkClient.subscribeChildChanges(this.dataId, new IZkChildListener() {

            @Override
            public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {
                // 3.1解析服务提供者地址列表
                parseIpList(currentChilds);

                try {
                    System.out.println((j.join("ipList changed:", JSON.json(ipList))));
                } catch (IOException e) {
                }
            }
        });

        //4. 解析服务提供者ip列表
        parseIpList(list);

    }

    public static void main(String[] a) throws InterruptedException {
        ZookeeperIpList zk = new ZookeeperIpList();
        zk.init("127.0.0.1:2181", "dubbo", "com.books.dubbo.demo.api.GreetingService:1.0.0", "dubbo");

        try {
            System.out.println((j.join("parseIpList", JSON.json(zk.getIpList()))));
        } catch (IOException e) {
        }
        Thread.currentThread().join();

    }
}复制代码

如上代码main函数创建了一个ZookeeperIpList对象,并且调用其init方法,参数分别为zk地址,zk分组,服务接口以及版本,服务分组。

init方法内首先拼接要订阅的zk的path,拼接完成后dataid为/dubbo/com.books.dubbo.demo.api.GreetingService/providers,然后创建zkclient订阅该dataid对应的path,并且注册监听器,当path下信息变化后会得到最新列表。

并且使用parseIpList方法解析获取的地址列表为ip:port个数,解析完毕后保存到ipList中。

三、总结

本节介绍了一个简单的基于zookeeper获取服务提供者地址列表的方法,后面我们看如何指定ip进行调用。


相关文章:

  • 谷歌智能音箱更新,玩转摄像头,但名归Nest麾下
  • C++STL算法
  • math模块
  • cs6.8-oracle挂载ceph
  • centos7二进制安装mysql8.0.16
  • PHP应用如何对接微信公众号JSAPI支付
  • Leetcode 11 - Container With Most Water
  • Oracle 12c:ORA-28040
  • 002-QC的使用
  • JavaScript 函数式编程技巧 - 反柯里化
  • 线程池没你想的那么简单
  • 第九周总结
  • 聊聊Dubbo(九):核心源码-服务端启动流程2
  • Nginx和Apache
  • 重学ES6 Set 数据结构(2)
  • Android单元测试 - 几个重要问题
  • AWS实战 - 利用IAM对S3做访问控制
  • HTTP中GET与POST的区别 99%的错误认识
  • Java精华积累:初学者都应该搞懂的问题
  • Laravel Telescope:优雅的应用调试工具
  • React-redux的原理以及使用
  • Sass 快速入门教程
  • Storybook 5.0正式发布:有史以来变化最大的版本\n
  • 记一次用 NodeJs 实现模拟登录的思路
  • 使用 QuickBI 搭建酷炫可视化分析
  • 用Python写一份独特的元宵节祝福
  • 走向全栈之MongoDB的使用
  • 白色的风信子
  • 3月7日云栖精选夜读 | RSA 2019安全大会:企业资产管理成行业新风向标,云上安全占绝对优势 ...
  • ​LeetCode解法汇总1410. HTML 实体解析器
  • # Apache SeaTunnel 究竟是什么?
  • #前后端分离# 头条发布系统
  • $(document).ready(function(){}), $().ready(function(){})和$(function(){})三者区别
  • (C语言)字符分类函数
  • (附源码)spring boot北京冬奥会志愿者报名系统 毕业设计 150947
  • (附源码)springboot社区居家养老互助服务管理平台 毕业设计 062027
  • (六)Hibernate的二级缓存
  • (十七)Flask之大型项目目录结构示例【二扣蓝图】
  • (四)汇编语言——简单程序
  • (转) Android中ViewStub组件使用
  • (转)fock函数详解
  • (转)真正的中国天气api接口xml,json(求加精) ...
  • .mkp勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .NET Framework 3.5中序列化成JSON数据及JSON数据的反序列化,以及jQuery的调用JSON
  • .Net Remoting(分离服务程序实现) - Part.3
  • .Net Winform开发笔记(一)
  • .net对接阿里云CSB服务
  • .NET开发人员必知的八个网站
  • [ Linux ] Linux信号概述 信号的产生
  • [ 蓝桥杯Web真题 ]-Markdown 文档解析
  • [C++]类和对象【上篇】
  • [Deepin 15] 编译安装 MySQL-5.6.35
  • [Git].gitignore失效的原因
  • [iphone-cocos2d]关于Loading的若干处理和讨论
  • [JDK工具-2] javap 类文件解析工具-帮助理解class文件,了解Java编译器机制