zookeeper可以作为微服务注册中心,spring cloud也提供了zookeeper注册中心的支持。

本文介绍如何实现一个简单的zookeeper注册中心,主要的实现方式:

n个服务提供者对外提供http接口获取数据,这些服务提供者把自己的主机、端口信息注册到zookeeper的某个节点上面;

当服务提供者宕机或者服务不可用时,zookeeper节点会删除该提供者的信息;

消费者也连接zookeeper获取可以使用的服务提供者(并且会持续监听节点,节点变化时也会实时更新本地的服务提供者列表),然后发起http请求调用服务接口获取数据

下面简单介绍一下实现方式

1、UserService服务

后台用户管理服务web工程,使用spring mvc注解方式开发,不提供用户操作界面。提供http接口根据用户名获取用户信息,入参用户名字符串,响应为json类型,注册的服务名为UserService

2、UserAdminService服务

用户管理系统web工程,使用spring mvc注解方式开发,这是一个给用户使用的系统。提供登录页面、和首页,登录操作调用UserService提供的http接口根据用户输入的用户名获取用户信息,然后比对密码是否正确,服务名为UserAdminService

3、修改UserService服务工程

1)application.properties配置
 ## zookeeper集群地址
# zk.url=192.168.0.28:2181,192.168.0.29:2181,192.168.0.30:2181
zk.url=127.0.0.1:2181 ## 注册服务使用的二级路径名
service.name=UserService
## IP根据实际部署的服务器修改
server.hostname=127.0.0.1
## 端口根据实际监听端口修改
server.port=8080 ## 注册服务时会创建
## /services/${service.name}/${server.hostname}:${server.port}
## 这样一个临时节点,值为${service.name}

三个服务部署在一台服务器上,分别监听7070、8080、9090,可以使用server.port参数配置

2)ZookeeperRegister类

编写ZookeeperRegister类,根据zk集群地址、服务名、IP、端口等配置注册服务到zookeeper集群

  a)   读取application.properties文件,加载配置

  b)   zkRegist方法会在依赖注入完成后由spring容器调用

  c)   获取zk集群地址、服务名、IP、端口等配置

  d)   创建ZooKeeper对象,来注册服务,也就是在zookeeper创建一个临时节点

  e)   节点规则是: /services/${service.name}/${server.hostname}:${server.port},值为: ${service.name}

  f)    此类对象会被spring容器管理

3)ZookeeperRegister类代码
 @Component
@PropertySource("classpath:application.properties")
public class ZookeeperRegister { private ZooKeeper zkClient; @Autowired
private Environment env; @PostConstruct
public void zkRegist() throws IOException, KeeperException,
InterruptedException { // 获取配置信息
String zkUrl = env.getProperty("zk.url");
String serviceName = env.getProperty("service.name");
String hostName = env.getProperty("server.hostname");
int port = Integer.parseInt(env.getProperty("server.port")); // 创建ZooKeeper对象
// 超时时间为2分
zkClient = new ZooKeeper(zkUrl, 120000, new Watcher() {
@Override
public void process(WatchedEvent event) { }
}); // path = /services/${service.name}/${server.hostname}:${server.port}
// value = ${service.name} Stat exists = zkClient.exists("/services", false);
if (exists == null) {
zkClient.create("/services", "services".getBytes(),
Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
exists = zkClient.exists("/services/UserService", false);
if (exists == null) {
zkClient.create("/services/" + serviceName,
serviceName.getBytes(), Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT);
}
}
// 创建znode节点
// 临时节点
zkClient.create("/services/" + serviceName + "/" + hostName + ":"
+ port, serviceName.getBytes(), Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL);
}
}

4、修改UserAdminService服务工程

1)application.properties配置
 ## zookeeper集群地址
# zk.url=192.168.0.28:2181,192.168.0.29:2181,192.168.0.30:2181
zk.url=127.0.0.1:2181
2)UserServiceImpl类

UserServiceImpl类实现Watcher和UserService接口,使用@Service标注,由spring容器管理

  a)    初始化方法中创建ZooKeeper对象,连接到zk集群

  b)    当ZooKeeper中服务提供者节点发生变化时调用getProviders方法重新获取可用的服务提供者

  c)    getUserByUsername方法先从当前可用的提供者集合中随机获取一个提供者,使用httpclient发送请求获取用户数据返回给调用者

把UserService注入到Controller即可使用

3)UserServiceImpl类代码
 @Service
@PropertySource("classpath:application.properties")
public class UserServiceImpl implements UserService, Watcher { private static Logger log = LoggerFactory.getLogger(UserServiceImpl.class); private static final String SERVICE_URL =
"/UserService/user/getUserByUsername?username=";
private static final String SERVICE_REGIST_PATH = "/services/UserService"; @Autowired
private Environment env; private ZooKeeper zkClient;
private List<String> providers = new ArrayList<String>(); @PostConstruct
public void init() {
String zkUrl = env.getProperty("zk.url");
try {
zkClient = new ZooKeeper(zkUrl, 120000, this);
} catch (IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
} @Override
public User getUserByUsername(String username) { User user = null; // 获取一个可以使用的服务提供者
String provider = getProvider(); String url = "http://" + provider + SERVICE_URL + username; // 发送请求获取数据
CloseableHttpClient http = HttpClients.createDefault(); HttpGet httpGet = null;
CloseableHttpResponse response = null;
InputStream in = null; try {
// 构建httpget
httpGet = new HttpGet(new URI(url)); // 发送请求获取响应
response = http.execute(httpGet);
HttpEntity entity = response.getEntity(); // 获取输入流和数据字符串
in = entity.getContent();
String json = IOUtils.toString(in, "utf-8"); log.info(String.format("请求: %s, 响应: %s", url, json)); // 解析json字符串为user对象
user = json2User(json); } catch (URISyntaxException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭输入流和响应对象
IOUtils.closeQuietly(in);
IOUtils.closeQuietly(response);
}
return user;
} @Override
public void process(WatchedEvent event) {
getProviders();
} private void getProviders() {
try {
this.providers = zkClient.getChildren(SERVICE_REGIST_PATH, true);
log.info(String.format("可用服务提供者: %s", this.providers));
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
} private String getProvider() {
int size = this.providers.size();
Random r = new Random();
int i = r.nextInt(size);
String provider = this.providers.get(i);
log.info(String.format("可用服务提供者: %s, 随机获取提供者 [%s], 序号 [%s]",
this.providers, provider, i));
return provider;
} private User json2User(String json) {
ObjectMapper mapper = new ObjectMapper();
User user = null;
try {
user = mapper.readValue(json, User.class);
} catch (JsonParseException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return user;
}
}

5、部署测试

1)部署启动zookeeper

2)服务部署

服务提供者和消费者是部署在同一台机器上面的,监听端口不同

部署三个UserService服务,修改server.port参数,分别监听7070、8080、9090

部署UserAdminService服务,我测试的时候和其中一个服务提供者部署在了同一个tomcat里面,监听9090

其中7070和8080的tomcat使用cmd命令行启动,9090的tomcat在eclipse里面启动便于观察服务消费者的日志

启动了三个tomcat之后可以看看zookeeper的变化

另外,在UserAdminService服务的日志中可以看到当前可以使用的服务提供者列表

访问一下http://localhost:9090/UserAdminWeb/login登录,可以看到后台程序获取到了一个可以使用的服务提供者,然后发起了http请求查询用户信息

当某个服务提供者挂掉之后,可以看到消费者的日志,已经重新获取了提供者列表

6、存在的一些问题

a)    服务提供者突然挂掉之后,zookeeper并不能马上删除该提供者的znode信息,所以在服务消费者这边还需要做一些优化,即发起请求获取用户信息时如果出现问题,应该从可用服务提供者列表删除该提供者信息

b)    服务消费者使用的httpclient发起请求,如果每次获取用户信息都去重新建立tcp连接,效率很低,所以需要使用连接池技术管理用于发起http请求的tcp连接

7、源码下载

https://files.cnblogs.com/files/xugf/Zookeeper%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83%E6%BA%90%E7%A0%81.zip

Zookeeper学习笔记:简单注册中心的更多相关文章

  1. 007 SpringCloud 学习笔记3-----Eureka注册中心

    1.Eureka概述 (1)引子 网约车出现以前,人们出门叫车只能叫出租车.一些私家车想做出租却没有资格,被称为黑车.而很多人想要约车,但是无奈出租车太少,不方便.私家车很多却不敢拦,而且满大街的车, ...

  2. ZooKeeper学习笔记三:使用ZooKeeper实现一个简单的配置中心

    作者:Grey 原文地址:ZooKeeper学习笔记三:使用ZooKeeper实现一个简单的配置中心 前置知识 完成ZooKeeper集群搭建以及熟悉ZooKeeperAPI基本使用 需求 很多程序往 ...

  3. ZooKeeper学习笔记四:使用ZooKeeper实现一个简单的分布式锁

    作者:Grey 原文地址: ZooKeeper学习笔记四:使用ZooKeeper实现一个简单的分布式锁 前置知识 完成ZooKeeper集群搭建以及熟悉ZooKeeperAPI基本使用 需求 当多个进 ...

  4. ZooKeeper学习笔记(二)——内部原理

    zookeeper学习笔记(二)--内部原理 1. zookeeper的节点的类型 总的来说可以分为持久型和短暂型,主要区别如下: 持久:客户端与服务器端断开连接的以后,创建的节点不会被删除: 持久化 ...

  5. ZooKeeper 学习笔记

    ZooKeeper学习笔记 1.   zookeeper基本概念 zookeeper是一个分布式的,开放源码的分布式应用程序协调服务,是hadoop和Habase的重要组件,是为分布式应用提供一致性服 ...

  6. ZooKeeper学习笔记(一)——概述

    zookeeper学习笔记(一)--概述 1. 概述 Zookeeper是一个开源的分布式的,为分布式应用提供协调服务的Apache项目.zookeeper从设计模式的角度来理解:是一个基于观察者设计 ...

  7. Zookeeper学习笔记(中)

    Zookeeper学习笔记(中) Zookeeper的基本原理和基本实现 深入了解ZK的基本原理 ZK的一致性: ZAB 协议: Zookeeper 原子消息广播协议 ZK通过选举保证 leader ...

  8. Zookeeper学习笔记(上)

    Zookeeper学习笔记 本篇主要是一些基本的介绍和API的使用介绍, 有些只是记录了知识点,而没有完全在笔记中详细解释, 需要自行查找资料补充相关概念 主要参考了课程中的内容: Zookeeper ...

  9. IIC驱动学习笔记,简单的TSC2007的IIC驱动编写,测试

    IIC驱动学习笔记,简单的TSC2007的IIC驱动编写,测试 目的不是为了编写TSC2007驱动,是为了学习IIC驱动的编写,读一下TSC2007的ADC数据进行练习,, Linux主机驱动和外设驱 ...

  10. ZooKeeper学习笔记二:API基本使用

    Grey ZooKeeper学习笔记二:API基本使用 准备工作 搭建一个zk集群,参考ZooKeeper学习笔记一:集群搭建. 确保项目可以访问集群的每个节点 新建一个基于jdk1.8的maven项 ...

随机推荐

  1. dapi 基于Django的轻量级测试平台二 前端页面

    QQ群: GitHub:https://github.com/yjlch1016/dapi 一.登录页login.html: 二.首页home.html: 三.产品线列表页product.html: ...

  2. Samba应用案例

    一.配置文件详解 Samba配置文件非常简洁明了,所有的设置都在 /etc/samba/smb.conf 配置文件中进行,通过对该配置文件的修改,可以将Samba配置为一台匿名文件服务器.基于账户的文 ...

  3. 使用WIFI网卡的AP功能

    前几篇博客中,wifi无线网卡都工作于STA模式,那么它能否工作于AP模式.本篇博客就研究使wifi 无线网卡工作于AP模式.使用一个应用程序hostapd,关于它的介绍可以去此网站https://w ...

  4. 使用WIFI网卡iw

    上篇博客中,配置修改了内核,以支持所选择的USB网卡,本篇博客需要去编写一些应用程序,将wifi网卡使用起来. 1.1 概念:认证/加密认证:就是用来判断哪些用户可以使用这个无线网络加密:是指手机和A ...

  5. Java 包扫描器

    包扫描器 获取一个包下的所有类,然后使用默认的类加载器加载到内存中 public static List<Class<?>> scanByPackage(String pack ...

  6. apache在linux下安装

    yum安装 主流Linux系统版本基本上都集成了apache服务器httpd,我们可以通过如下命令来查看我们的操作系统上是否已经安装了apache服务器httpd rpm -qa | grep htt ...

  7. ElementUI_NodeJS环境搭建

    ElementUI简介 我们学习VUE,知道它的核心思想式组件和数据驱动,但是每一个组件都需要自己编写模板,样式,添加事件,数据等是非常麻烦的, 所以饿了吗推出了基于VUE2.0的组件库,它的名称叫做 ...

  8. 洛谷 CF399B【Red and Blue Balls】题解

    n年没有更博客:我总结出了规律,当学的东西很难得时候都去学习,没有时间写博客,只有 内容对于我这种蒟蒻友好,又让我非常闲的慌时才写博客,这种博客以后也没有价值(也有些是做完一道题有成就感写的) 最近内 ...

  9. 以V8中js源码为例了解GitHub查看代码功能

    GitHub作为开源仓库,许多开源项目仓库这里,当然不乏十分优秀的,比如Node.V8,我一直比较好奇js源码,像java的话,因为环境是JDK,我们结合IDE很容易就能跳转到其源码内部去查看实现,但 ...

  10. 第10组 Beta冲刺(4/4)

    队名:凹凸曼 组长博客 作业博客 组员实践情况 童景霖 过去两天完成了哪些任务 文字/口头描述 继续学习Android studio和Java 制作剩余界面前端 展示GitHub当日代码/文档签入记录 ...