Zookeeper实现服务注册发现

服务注册中心

​ 之所以需要访问注册和服务发现是因为分布式系统中,服务之间需要相互调用,但若每个服务自己维护一份依赖的服务信息的话,就显得很麻烦,且自身维护的数据无法保证其实时性,当依赖的服务信息发生变更时,无法及时获取更新,解决方案就是引入一个注册中心,服务提供方将自己的信息写入到注册中心,服务使用方从注册中心来获取服务信息; 如下图:

client表示服务使用方,server表示服务提供方。
在这里插入图片描述
客户端可自动发现服务信息,当服务状态发生变化时(上线,下线,更换地址),客户端可以及时响应变化。

实现

  1. 首先保证Zookeeper以安装启动,且可以正常访问
  2. 创建Maven项目并添加Zookeeper的Java客户端依赖(注意版本号需>3.6)
		<dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.6.1</version>
        </dependency>

3.编写服务提供方

package com.lbb.zoo.servers;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.ACL;

import java.io.IOException;
import java.io.InputStream;
import java.net.*;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

/**
 * 用户服务
 */
public class UserService {
    //端口号
    private int port = 8899;
    //节点
    private String node = "/userService";
    //socket服务端
    private ServerSocket socket;

    //获取本机ip地址
    public String getLocalHost() throws SocketException {
        //获取本机ip地址 可能有多个网卡信息,所以需要遍历筛选
        String ip = null;
        //获取全部网络接口
        Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
        while (networkInterfaces.hasMoreElements()) {
            //获取网络接口
            NetworkInterface ni = (NetworkInterface) networkInterfaces.nextElement();
            //获取InetAddress
            Enumeration<InetAddress> nias = ni.getInetAddresses();
            while (nias.hasMoreElements()) {
                InetAddress ia = (InetAddress) nias.nextElement();
                //获取需要的ip
                if (!ia.isLinkLocalAddress() && !ia.isLoopbackAddress() && ia instanceof Inet4Address) {
                    ip = ia.getHostAddress();
                }
            }
        }
        return ip;
    }

    //注册服务
    public void serverRegister(String ip) throws IOException, KeeperException, InterruptedException {
        //注册成服务信息到zookeeper(服务注册)
        //参数 1 ip:port(集群用,隔开) 2 超时时间 3 监视器
        //如果出现错误可以把超时时间延长试试
        ZooKeeper zooKeeper = new ZooKeeper("192.168.74.129:2181,192.168.74.130:2181,192.168.74.131:2181", 3000, null);
        System.out.println("ZooKeeper连接成功....");
        //创建节点 将自身的服务信息写入到zookeeper
        //参数 1.节点 2.节点值 3.访问控制列表,管理访问控制权限 4.创建模式:持久和临时 ,下面是临时的
        //访问控制列表,管理访问控制权限
        ArrayList<ACL> acls = new ArrayList<>();
        //所有都可以访问
        ACL acl = new ACL(31, ZooDefs.Ids.ANYONE_ID_UNSAFE);
        acls.add(acl);
        zooKeeper.create(node, (ip + ":" + port).getBytes(), acls, CreateMode.EPHEMERAL);

        System.out.println("服务发布成功....");
    }

    //处理客户端连接
    private void clientHandler() throws IOException {
        //时时监听
        while (true) {
            //获取客户端Socket
            Socket client_socket = this.socket.accept();
            //接收字符流
            InputStream inputStream = client_socket.getInputStream();
            byte[] barr = new byte[1024];
            //时时接收信息
            while (true) {
                //byte承载
                int size = inputStream.read(barr);
                if (size == -1){
                   // System.out.println("客户端下线了....");
                   client_socket.close();
                   break;
                }
                //读取数据
                String data = new String(barr, 0, size);
                System.out.println(client_socket.getInetAddress().getHostAddress() + ":" + data);
            }
        }
    }

    //启动服务并监听客户端请求
    public void start() throws Exception {
        //获取本机ip地址
        String ip = getLocalHost();
        //启动socket服务端
        //创建socket 默认就是本机地址,只需要给端口号
        socket = new ServerSocket(port);

        System.out.println("服务器已启动,正在监测中....");
        //注册服务
        serverRegister(ip);
        //监听socket处理客户端请求
        clientHandler();
    }


    public static void main(String[] args) throws Exception {
        //调用服务
        new UserService().start();
    }
}

4.客户端

package com.lbb.zoo.client;

import org.apache.zookeeper.*;

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Scanner;


/**
 * 客户端
 */
public class UserClient implements Watcher {
    //节点
    private String node = "/userService";
    //server_ip
    private String server_ip;
    //server_port
    private int server_port;
    //ZooKeeper
    private ZooKeeper zooKeeper;

    public void run() throws Exception {
        //连接zookeeper
        zooKeeper = new ZooKeeper("192.168.74.129:2181,192.168.74.130:2181,192.168.74.131:2181", 3000, null);
        System.out.println("Zookeeper连接成功!");
        //尝试获取服务信息
        getServerInfo();
        //监听节点变化
        //参数 1.节点 2.监视器,出现变动时回调 3.模式:永久和临时
        zooKeeper.addWatch(node, this, AddWatchMode.PERSISTENT);

    }

    //连接服务器,发送数据
    public void sendMessage() throws IOException {
        //接受用户输入
        System.out.println("请输入要发送的信息,输入q退出:");
        while (true) {
            Scanner in = new Scanner(System.in);
            String message = in.next();

            if (message.equals("q")) {
                System.out.println();
                System.out.println("再见!!!");
                System.exit(0);
            }
            //连接服务器
            if (server_ip != null) {
                Socket socket = new Socket();
                socket.connect(new InetSocketAddress(server_ip, server_port));

                //发送数据
                OutputStream outputStream = socket.getOutputStream();
                outputStream.write(message.getBytes());
                socket.close();

                System.out.println("发送成功!");
            } else {
                System.err.println("当前服务不可用....");
            }
        }

    }

    //获取服务信息
    public void getServerInfo() {
        try {
            //获取节点数据
            //参数 1.节点 2.监视对象,不需要就填false,需要就天wactcher 4.状态
            byte[] data = zooKeeper.getData(node, false, null);
            //读取信息
            String[] infos = new String(data).split(":");
            //更新服务器信息
            server_ip = infos[0];
            server_port = Integer.parseInt(infos[1]);

            System.out.println("获取服务信息成功!");

        } catch (KeeperException e) {
            System.err.println("服务信息不存在! 等待服务上线........");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    //wactcher接口的方法实现,处理事件
    @Override
    public void process(WatchedEvent event) {
        if (event.getType() == Event.EventType.NodeCreated){
            getServerInfo();
            System.out.println("服务上线了");
        }else if (event.getType() == Event.EventType.NodeDataChanged){
            getServerInfo();
            System.out.println("服务更新了....");
        }else if (event.getType() == Event.EventType.NodeDeleted){
            server_ip = null;
            System.out.println("服务下线了....");
        }
    }

    public static void main(String[] args) throws Exception {
        UserClient userClient = new UserClient();
        //连接zookeeper,获取状态信息
        userClient.run();
        //发送数据
        userClient.sendMessage();
    }
}

5.打包服务端代码,下面步骤可忽略,仅为了测试客户端正确性, 为了在打包时附带其全部依赖,此处借助Spring的打包插件,在pom中添加以下内容:

			<!-- 为了方便运行,我们要将packaging 改为jar-->
			<groupId>com.lbb</groupId>
    		<artifactId>zookeeperdemo</artifactId>
    		<version>1.0-SNAPSHOT</version>
    		<packaging>jar</packaging>
			<!--     打包插件,该插件可以将jar包一起打包       -->
			<plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>1.5.6.RELEASE</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

注意:Spring-boot打包插件会自动获取项目中的主函数,必须保证主函数只有一个,所以需要暂时注释客户端的主函数,最后执行maven的package,得到jar包。

6.将jar上传至虚拟机并运行

在target里面会有打好的包zookeeperdemo.jar -> 点击idea下面terminal进入操作界面,进入target目录 -> 将zookeeperdemo.jar直接拖到terminal操作界面->输入下面命令执行jar文件:

java -jar zookeeperdemo.jar

若没有其他问题则客户端依然可以正常连接服务器发送消息;

7.将jar复制到虚拟机里,还是在terminal操作界面 -> 将zookeeperdemo.jar直接拖到terminal操作界面,进入target目录 -> 输入下面命令:

scp zookeeperdemo.jar root@192.168.74.129:/root

中间会让你输入yes和密码,都是不显示的打完直接回车即可。

8.在lunix系统根目录输入命令,执行jar包:

java -jar zookeeperdemo.jar

发布成功后客户端依然可以正常连接服务器发送消息;

​ 以上便是使用Zookeeper实现服务注册和服务发现的具体步骤,在实际开发中,我们会将提供的服务部署为集群,这时可将集群中的各个服务信息作为子节点注册到指定节点下,客户端监听该节点变化,获取子节点列表从而获取到服务列表,还可以在此基础上加上负载均衡算法实现对服务列表的合理访问;

在这里插入图片描述

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值