zookeeper实现分布式应用系统服务器上下线动态感知程序、监听机制与守护线程

本文涉及的产品
任务调度 XXL-JOB 版免费试用,400 元额度,开发版规格
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
云原生网关 MSE Higress,422元/月
简介: zookeeper实现分布式应用系统服务器上下线动态感知程序、监听机制与守护线程

需求


在分布式系统中存在多个服务器,这些服务器可以动态上下线,而客户端可以连接任意服务器,但是如果连接的服务器突然下线那么客户端需要重新连接其他服务器,这就需要在服务器上下线的时候客户端能感知,获取哪些可以连接的服务器。


解决思路


每次服务器启动的时候去zookeeper上进行注册(注册规则自由指定,比如简单使用/servers/server001 hostname),而客户端上线就获取服务器列表,并对节点进行监听,一旦有服务器下线那么就能监听到事件从而重新获取服务器列表。

image.png


程序简单实现

服务器端:

/**
 * 服务端程序
 * @author
 *
 */
public class DistributedServer {
    private static final String connectionString = "192.168.47.141:2181";
    public static final Integer sessionTimeout = 2000;
    public static ZooKeeper zkClient = null;
    /**
     * 获取zookeeper连接
     * @throws Exception
     */
    public void getConnection() throws Exception{
        zkClient = new ZooKeeper(connectionString, sessionTimeout, new Watcher(){
            //收到事件通知后的回调函数(应该是我们自己的事件处理逻辑)
            public void process(WatchedEvent event) {
                System.out.println(event.getType()+","+event.getPath());
                try {
                    //为了能一直监听,调用一次注册一次
                    zkClient.getChildren("/", true);
                }catch(Exception e){
                }
            }});
    }
    /**
     * 注册服务器信息
     * @param hostname 注册的服务器名
     * @throws Exception
     */
    public void registerServer(String hostname) throws Exception{
        //创建的是带序号的临时节点 生成的节点像/servers/server000001,/servers/server000002等
        //节点数据即为注册的主机名
        String path = zkClient.create("/server", hostname.getBytes(),
                ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
        System.out.println(hostname+" --上线了-- "+path);
    }
    /**
     * 服务器注册完后,执行业务逻辑
     * @param hostname
     * @throws IOException
     */
    public void executeBusiness(String hostname) throws IOException {
        System.out.println(hostname+"开始工作了!");
        System.in.read();
    }
    public static void main(String[] args) throws Exception {
        //获取zookeeper连接
        DistributedServer server = new DistributedServer();
        server.getConnection();
        //服务器上线,完成注册
        Scanner scanner = new Scanner(System.in);
        System.out.println("输入hostname");
        String hostname = scanner.nextLine();
        server.registerServer(hostname);
        //执行业务逻辑
        server.executeBusiness(hostname);
    }
}

客户端:

/*
 * 客户端程序
 */
public class DistributeClient {
    private static final String connectionString = "192.168.47.141:2181";
    public static final Integer sessionTimeout = 2000;
    public static ZooKeeper zkClient = null;
    public static final String parentNode = "/";
    //注意:加volatile的意义何在?使得多线程看到的服务器列表一致而不会拷贝到自己的工作空间
    public volatile List<String> serverList = new ArrayList<String>();
    /**
     * 获取zookeeper连接
     * @throws Exception
     */
    public void getConnection() throws Exception{
        zkClient = new ZooKeeper(connectionString, sessionTimeout, new Watcher(){
            //收到事件通知后的回调函数(应该是我们自己的事件处理逻辑)
            public void process(WatchedEvent event) {
                System.out.println(event.getType()+","+event.getPath());
                try {
                    //重新获取(更新)服务器列表,并进行监听
                    getServerList();
                }catch(Exception e){
                }
            }});
    }
    /**
     * 获取服务器列表信息,并对父节点进行监听
     * @throws Exception
     */
    public void getServerList() throws Exception{
        //获取服务器列表,并对父节点进行监听
        //getChildren()相对于命令行 ls /znode,对子节点进行监听
        List<String> children = zkClient.getChildren(parentNode, true);
        //创建临时集合,将子节点存入
        List<String> childrenList = new ArrayList<String>();
        for (String child : children) {
            byte[] data = zkClient.getData(parentNode+child, false, null);
            childrenList.add(new String(data));
        }
        //将临时集合中的节点赋给服务器列表serverList,以便业务线程使用
        serverList = childrenList;
        System.out.println(serverList);
    }
    /**
     * 业务功能
     * @throws Exception
     */
    public void executeBusiness() throws Exception{
        System.out.println("获取的服务器列表:"+serverList);
        System.out.println("客户端开始工作了...");
        System.in.read();
    }
    public static void main(String[] args) throws Exception {
        //获取zookeeper连接
        DistributeClient client = new DistributeClient();
        client.getConnection();
        //获取服务器列表
        client.getServerList();
        //业务功能
        client.executeBusiness();
    }
}

测试

运行三次服务器端程序,输入的hostname分别为mini1,mini2,mini3当成注册了三个服务器


image.png

image.png



运行客户端程序(可以启动多次,简单起见这里就一次)


image.png


关闭其中2个(mini1,mini2)连接zookeeper的客户端(关闭后注册的服务器也就消失了),查看客户端输出


image.png


一旦服务器下线了,客户端能监听到并且重新获取服务器列表。


相关实践学习
基于MSE实现微服务的全链路灰度
通过本场景的实验操作,您将了解并实现在线业务的微服务全链路灰度能力。
目录
相关文章
|
2月前
|
存储 缓存 前端开发
如何优化 SSR 应用以减少服务器压力
优化SSR应用以减少服务器压力,可采用代码分割、缓存策略、数据预加载、服务端性能优化、使用CDN、SSR与SSG结合、限制并发请求、SSR与CSR平滑切换、优化前端资源及利用框架特性等策略。这些方法能有效提升性能和稳定性,同时保证用户体验。
|
2月前
|
弹性计算 开发工具 git
2分钟在阿里云ECS控制台部署个人应用(图文示例)
作为一名程序员,我在部署托管于Github/Gitee的代码到阿里云ECS服务器时,经常遇到繁琐的手动配置问题。近期,阿里云ECS控制台推出了一键构建部署功能,简化了这一过程,支持Gitee和GitHub仓库,自动处理git、docker等安装配置,无需手动登录服务器执行命令,大大提升了部署效率。本文将详细介绍该功能的使用方法和适用场景。
2分钟在阿里云ECS控制台部署个人应用(图文示例)
|
1月前
|
开发框架 .NET PHP
网站应用项目如何选择阿里云服务器实例规格+内存+CPU+带宽+操作系统等配置
对于使用阿里云服务器的搭建网站的用户来说,面对众多可选的实例规格和配置选项,我们应该如何做出最佳选择,以最大化业务效益并控制成本,成为大家比较关注的问题,如果实例、内存、CPU、带宽等配置选择不合适,可能会影响到自己业务在云服务器上的计算性能及后期运营状况,本文将详细解析企业在搭建网站应用项目时选购阿里云服务器应考虑的一些因素,以供参考。
|
2月前
|
缓存 Java 开发者
Java多线程并发编程:同步机制与实践应用
本文深入探讨Java多线程中的同步机制,分析了多线程并发带来的数据不一致等问题,详细介绍了`synchronized`关键字、`ReentrantLock`显式锁及`ReentrantReadWriteLock`读写锁的应用,结合代码示例展示了如何有效解决竞态条件,提升程序性能与稳定性。
237 6
|
1月前
|
监控 Java 数据库连接
Java线程管理:守护线程与用户线程的区分与应用
在Java多线程编程中,线程可以分为守护线程(Daemon Thread)和用户线程(User Thread)。这两种线程在行为和用途上有着明显的区别,了解它们的差异对于编写高效、稳定的并发程序至关重要。
48 2
|
2月前
|
数据采集 存储 数据处理
Python中的多线程编程及其在数据处理中的应用
本文深入探讨了Python中多线程编程的概念、原理和实现方法,并详细介绍了其在数据处理领域的应用。通过对比单线程与多线程的性能差异,展示了多线程编程在提升程序运行效率方面的显著优势。文章还提供了实际案例,帮助读者更好地理解和掌握多线程编程技术。
|
2月前
|
存储 监控 安全
深入理解ThreadLocal:线程局部变量的机制与应用
在Java的多线程编程中,`ThreadLocal`变量提供了一种线程安全的解决方案,允许每个线程拥有自己的变量副本,从而避免了线程间的数据竞争。本文将深入探讨`ThreadLocal`的工作原理、使用方法以及在实际开发中的应用场景。
97 2
|
2月前
|
安全 Java 开发者
Java 多线程并发控制:深入理解与实战应用
《Java多线程并发控制:深入理解与实战应用》一书详细解析了Java多线程编程的核心概念、并发控制技术及其实战技巧,适合Java开发者深入学习和实践参考。
85 7
|
2月前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
2月前
|
安全 开发工具 Swift
Swift 是苹果公司开发的现代编程语言,具备高效、安全、简洁的特点,支持类型推断、闭包、泛型等特性,广泛应用于苹果各平台及服务器端开发
Swift 是苹果公司开发的现代编程语言,具备高效、安全、简洁的特点,支持类型推断、闭包、泛型等特性,广泛应用于苹果各平台及服务器端开发。基础语法涵盖变量、常量、数据类型、运算符、控制流等,高级特性包括函数、闭包、类、结构体、协议和泛型。
45 2

热门文章

最新文章