目录

Life in Flow

知不知,尚矣;不知知,病矣。
不知不知,殆矣。

X

Zookeeper

注册中心

 微服务应用和机器越来越多,调用方需要知道接口的网络地址,如果靠配置文件的方式去控制网络地址,对于动态新增机器,维护带来很大问题。常见的注册中心:zookeeper、Eureka、consul、etcd。
注册中心

  • 理解注册中心:服务管理,核心是有个服务注册表,心跳机制动态维护。
  • 服务提供者 provider: 启动的时候向注册中心上报自己的网络信息。
  • 服务消费者 consumer: 启动的时候向注册中心上报自己的网络信息,拉取 provider 的相关网络信息。

安装 JDK1.8

 1# 解压
 2tar -zxvf jdk-8u191-linux-x64.tar.gz
 3
 4# 移动到/usr/local目录下
 5 mv jdk1.8.0_191 /usr/local/
 6
 7# 修改/etc/profile,在文件的最后面加上下面几行
 8vim /etc/profile
 9JAVA_HOME=/usr/local/jdk1.8.0_191
10JRE_HOME=$JAVA_HOME/jre
11PATH=$JAVA_HOME/bin:$PATH
12export PATH
13
14# 激活配置
15source /etc/profile
16
17# 验证
18[root@localhost test]# java -version
19java version "1.8.0_191"
20Java(TM) SE Runtime Environment (build 1.8.0_191-b12)
21Java HotSpot(TM) 64-Bit Server VM (build 25.191-b12, mixed mode)

安装 Zookeeper

 1# 解压
 2tar -zxvf zookeeper-3.4.12.tar.gz 
 3mv zookeeper-3.4.12 /usr/local/
 4
 5# 修改配置文件
 6cp /usr/local/zookeeper-3.4.12/conf/zoo_sample.cfg  /usr/local/zookeeper-3.4.12/conf/zoo.cfg
 7vim /usr/local/zookeeper-3.4.12/conf/zoo.cfg 
 8	dataDir=/usr/local/zookeeper-3.4.12/data
 9
10# 创建数据目录
11mkdir /usr/local/zookeeper-3.4.12/data
12useradd zookeeper
13chown -R zookeeper:zookeeper /usr/local/zookeeper-3.4.12/
14
15# 启动服务
16bash /usr/local/zookeeper-3.4.12/bin/zkServer.sh  start
17
18# 使用zkCli连接Zookeeper
19.\zkCli.cmd -timeout 5000 -server 192.168.31.220:2181
20Connecting to 192.168.31.220:2181
21ientCnxn$SendThread@1302] - Session establishment complete on server 192.168.31.220/192.168.31.220:2181, sessionid = 0x10014aa999f0000, negotiated timeout = 5000
22WATCHER::
23WatchedEvent state:SyncConnected type:None path:null
24[zk: 192.168.31.220:2181(CONNECTED) 0] stat /
25cZxid = 0x0
26ctime = Thu Jan 01 08:00:00 CST 1970
27mZxid = 0x0
28mtime = Thu Jan 01 08:00:00 CST 1970
29pZxid = 0x0
30cversion = -1
31dataVersion = 0
32aclVersion = 0
33ephemeralOwner = 0x0
34dataLength = 0
35numChildren = 1

#Zookeeper 的数据模型
数据模型

  • zookeeper 的数据模型类似于 Linux 下的文件目录。
  • 每一个节点都叫做 zNode,可以有子节点,也可以有数据
  • 每个节点都能设置相应的权限控制用户的访问(ACL)。
  • 每个节点存储的数据不宜过大。
  • 每个节点都带有一个版本号,数据变更时,版本号变更(乐观锁)
  • 节点分永久节点跟临时节点(session 断开,临时节点便会自动删除)

zkCli

 1# 创建顺序节点soulboy 数据是soulboy_data
 2[zk: 192.168.31.220:2181(CONNECTED) 1] create -s /soulboy soulboy_data
 3Created /soulboy0000000000
 4
 5# 创建临时节点
 6[zk: 192.168.31.220:2181(CONNECTED) 2] create -e /zbk zbk_data
 7Created /zbk
 8
 9# 获取节点的子节点
10[zk: 192.168.31.220:2181(CONNECTED) 4] ls /
11[zbk, zookeeper, soulboy0000000000]
12
13# 获取节点的数据
14[zk: 192.168.31.220:2181(CONNECTED) 13] get /soulboy0000000000
15soulboy_data
16cZxid = 0x2
17ctime = Tue Jul 23 12:07:49 CST 2019
18mZxid = 0x2
19mtime = Tue Jul 23 12:07:49 CST 2019
20pZxid = 0x2
21cversion = 0
22dataVersion = 0
23aclVersion = 0
24ephemeralOwner = 0x0
25dataLength = 12
26numChildren = 0
27
28# 查看节点的状态
29[zk: 192.168.31.220:2181(CONNECTED) 12] stat /soulboy0000000000
30cZxid = 0x2 # 事务id(创建新节点)
31ctime = Tue Jul 23 12:07:49 CST 2019 #节点创建时间
32mZxid = 0x2 # 最后一次更新时,事务的id	(对节点进行修改)
33mtime = Tue Jul 23 12:07:49 CST 2019 # 最后一次更新的时间
34pZxid = 0x2 # 该节点的子节点列表最后一次被修改的事务id
35cversion = 0 # 子节点列表的版本
36dataVersion = 0 # 数据内容的版本
37aclVersion = 0 # ACL版本
38ephemeralOwner = 0x0 # 用于临时节点,表示创建该临时节点的事务id,如果不是临时节点,该字段为0
39dataLength = 12 # 数据内容长度
40numChildren = 0 # 子节点的数量
41
42# 创建子节点
43[zk: 192.168.31.220:2181(CONNECTED) 4] create -e  /soulboy0000000000/test test
44Created /soulboy0000000000/test
45
46# 获取节点的子节点以及当前节点的状态
47[zk: 192.168.31.220:2181(CONNECTED) 5] ls2 /soulboy0000000000
48[test]	# 子节点在此,下面是当前节点的状态
49cZxid = 0x2
50ctime = Tue Jul 23 12:07:49 CST 2019
51mZxid = 0x2
52mtime = Tue Jul 23 12:07:49 CST 2019
53pZxid = 0x6
54cversion = 1
55dataVersion = 0
56aclVersion = 0
57ephemeralOwner = 0x0
58dataLength = 12
59numChildren = 1
60
61# 修改节点数据
62[zk: 192.168.31.220:2181(CONNECTED) 7] set /soulboy0000000000 soulboy_data_new
63cZxid = 0x2
64ctime = Tue Jul 23 12:07:49 CST 2019
65mZxid = 0x7
66mtime = Tue Jul 23 12:37:50 CST 2019
67pZxid = 0x6
68cversion = 1
69dataVersion = 1 #原本是0,修改之后就会加1
70aclVersion = 0
71ephemeralOwner = 0x0
72dataLength = 16
73numChildren = 1
74
75# 删除节点(无法删除有子节点的节点)
76[zk: 192.168.31.220:2181(CONNECTED) 8] delete /soulboy0000000000
77Node not empty: /soulboy0000000000
78
79# 递归删除整个节点(可以递归删除有子节点的节点)
80[zk: 192.168.31.220:2181(CONNECTED) 8] rmr /soulboy0000000000

Zookeeper Session 机制

Session 机制
 临时节点的生命周期跟随 session 的生命周期。当 session 过期的时候,该 session 创建的所有临时节点都会被删除。

 1# 创建临时节点
 2create -e /test test
 3
 4# 关闭session
 5close
 6
 7# 登录查看刚才创建的临时节点 :发现test节点已经被删除
 8.\zkCli.cmd -timeout 5000 -server 192.168.31.220:2181
 9[zk: 192.168.31.220:2181(CONNECTED) 0] ls /
10[zookeeper, soulboy0000000000]

Zookeeper Watcher 机制

Watcher 机制
对节点的 watcher 操作 get stat
 针对每一个节点的操作,都可以有一个监控者,当节点数据发生变化,会触发 watcher 事件 zk 中 watcher 是一次性的,触发后立即销毁 所有有监控者的节点的变更操作都能触发 watcher 事件。
子节点的 watcher 操作 ls ls2
 监控父节点,当父节点对应的子节点发生变更的时候,父节点上的 watcher 事件会被触发, 增删会触发、修改不会数据则不会触发,如果子节点再去新增子节点,也不会触发(也就是说,触发 watcher 事件一定是直系子节点)。

1# 监控当前节点的数据变化:get、stat
2[zk: 192.168.31.220:2181(CONNECTED) 3] get /soulboy0000000000 watch
3
4#  监控当前的直系子节点们(增删子节点):ls、ls2
5[zk: 192.168.31.220:2181(CONNECTED) 3] ls /soulboy0000000000 watch

Zookeeper ACL 权限控制

 针对节点可以设置相关的读写等权限,目的是为了保证数据的安全性,权限 permissions 可以指定不同的权限范围及角色,只有拥有相应权限和角色的用户才可以访问对应的 ZNODE。

  • 开发环境跟测试环境,使用 acl 就可以进行分离,开发者无权去操作测试的节点。
  • 生产环境上控制指定 ip 的服务可以访问相关的节点。
 1# scheme授权机制
 2* acl组成[scheme🆔permissions]
 3* world下只有一个id,也就是anyone,表示所有人 
 4* world:anyone:permissions
 5* auth:代表认证登录,需要注册用户有权限才可以 
 6* auth:user:password:permissions
 7* digest 需要密码加密才能访问 
 8* digest:username:BASE64(SHA1(password)):permissions(跟auth区别在于,auth明文,digest为密文)
 9* ip:ip:localhost:psermissions
10* super:代表超管,拥有所有的权限;
11* id:允许访问的用户
12* permissions:权限组合字符串
13	cdrwa
14	c create 创建子节点
15	d delete 删除子节点
16	r read 读取节点的数据
17	w write 写入数据
18	a admin 可管理的权限
19	cdrwa cdr cdw 
20
21# 获取节点权限 getAcl
22[zk: 192.168.31.220:2181(CONNECTED) 4] getAcl /soulboy0000000000
23'world,'anyone
24: cdrwa
25
26#设置节点权限 setAcl 
27[zk: 192.168.31.220:2181(CONNECTED) 6] addauth digest soulboy:soulboy   # 添加用户
28[zk: 192.168.31.220:2181(CONNECTED) 7] setAcl /soulboy0000000000 auth:soulboy:soulboy:cdrwa
29cZxid = 0x2
30ctime = Tue Jul 23 12:07:49 CST 2019
31mZxid = 0x7
32mtime = Tue Jul 23 12:37:50 CST 2019
33pZxid = 0x9
34cversion = 2
35dataVersion = 1
36aclVersion = 1
37ephemeralOwner = 0x0
38dataLength = 16
39numChildren = 0
40
41# 查看权限soulboy0000000000节点权限
42[zk: 192.168.31.220:2181(CONNECTED) 8] getAcl /soulboy0000000000
43'digest,'soulboy:VYanT576Pu1fFRBjBed+WuMZJJI=
44: cdrwa
45
46# 使用zkCli新建终端来验证
47[zk: 192.168.31.220:2181(CONNECTED) 1] get /soulboy0000000000
48Authentication is not valid : /soulboy0000000000
49
50# 登录指定用户,再次查看soulboy0000000000节点
51[zk: 192.168.31.220:2181(CONNECTED) 2] addauth digest soulboy:soulboy
52[zk: 192.168.31.220:2181(CONNECTED) 3] get /soulboy0000000000
53soulboy_data_new
54cZxid = 0x2
55ctime = Tue Jul 23 12:07:49 CST 2019
56mZxid = 0x7
57mtime = Tue Jul 23 12:37:50 CST 2019
58pZxid = 0x9
59cversion = 2
60dataVersion = 1
61aclVersion = 1
62ephemeralOwner = 0x0
63dataLength = 16
64numChildren = 0
65
66# 修改权限:少了d,代表没有删除子节点的权限
67[zk: 192.168.31.220:2181(CONNECTED) 4] setAcl /soulboy0000000000 auth:soulboy:soulboy:crwa

Zookeeper Cluster Role

Zookeeper Cluster Role

  • leader:作为整个 zk 集群写请求的唯一处理者,并负责进行投票的发起和决议(集群中过半节点同意提议,才可以写入),更新系统的状态。
  • follower:接收客户端请求,处理读请求,并向客户端返回结果;将写请求转给 Leader;在选举 Leader 过程中参与投票。
  • observer:可以理解为无选举投票权的 Flollower,其主要是为了协助 Follower 处理更多的读请求。如果 Zookeeper 集群的读请求负载很高,或者客户端非常非常多,多到跨机房,则可以设置一些 Observer 服务器,以提高读取的吞吐量。ZK Server 节点宕机过半,则整个 Zookeeper Cluster 不可用,而 observer 宕机不会影响 Zookeeper Cluster。

Zookeeper Cluster Mode

 Zookeeper Cluster 的核心是广播机制,该机制保证了各个 zk 之间数据同步(数据一致性)。zk 实现的机制为 ZAB 协议。

  • 恢复模式: 如果 leader 崩溃,这个时候就会进入恢复模式,使整个 zk 集群恢复到正常的工作状态。(进行选举新 Leader 等一系列操作)
  • 同步模式:新的 leader 选举出来后,就乎进入同步模式(各个 follower 会去同步新的 leader 上的数据),当大多数 zkServer 完成了与 leader 的状态同步之后,恢复模式就结束。
  • 广播模式:客户端想写入数据,这个时候 leader 发起提议,当 leader 的提议被大多数的 zkServer 统一之后,leader 就会去修改自身的数据,并将修改后的数据广播给其他的 follower。

Zookeeper Cluster Mode of Election

核心概念

 1# myid
 2这是Zookeeper Cluster中服务器的唯一标识,称为 myid。例如,有三个 zk 服务器,那么编号分别是 1,2,3 3
 4# zxid
 5ReentranReadWriteLock 32 6高位              低位
 70000000000000000  0000000000000000
 8                    epoch                                 xid
 900000000000000000000000000000000   00000000000000000000000000000000
10zxidLong 类型,其中高 32 位表示 epoch,低 32 位表示 xid。即 zxid 由两部分构成:epochxid11每个 Leader 都会具有一个不同的 epoch 值,表示一个时期、时代。新的 Leader 产生,则会更新所有 zkServerzxid 中的 epoch。 而 xid 则为 zk 的事务 id,每一个写操作都是一个事务,都会有一个 xid。每一个写操作都需要由 Leader 发起一个提议,由所有 Follower 表决是否同意本次写操作。
12
13# 逻辑时钟
14逻辑时钟,Logicalclock,是一个整型数,该概念在选举时称为 logicalclock,而在 zxid 中则为 epoch 的值。即 epochlogicalclock 是同一个值,在不同情况下的不同名称。

Zookeeper Cluster 选举状态

  • LOOKING,选举状态(查找 Leader 的状态)。
  • LEADING,领导者状态。处于该状态的服务器称为 Leader。
  • FOLLOWING,随从状态,同步 leader 状态。处于该状态的服务器称为 Follower。
  • OBSERVING,观察状态,同步 leader 状态。处于该状态的服务器称为 Observer。

Zookeeper Cluster Electioneering

发生时机
 整个集群群龙无首的时候

  • Zookeeper Cluster 服务首次启动。
  • Zookeeper Cluster 运行中,Leader 宕机的时候。

选举机制
 集群中,半数 zkServer 同意,则产生新的 leader(搭建集群时,一般都是奇数个),三台服务器,最多允许一台宕机,四台服务器,也是最多允许一台宕机。
选举算法
 对比(myid,zxid),先对比 zxid,zxid 大者(大表示数据越新)胜出,成为 leader,如果 zxid 一致,则 myid 大者成为 leader。

Zookeeper Cluster 部署

 端口的作用

  • 2181:对 client 端提供服务
  • 2888:集群内及其通讯使用的端口
  • 3888:集群选举 leader
 1# 修改/etc/hosts文件
 2192.168.31.220 zkcluster1
 3192.168.31.240 zkcluster2
 4192.168.31.250 zkcluster3
 5
 6# 添加zookeeper用户
 7useradd zookeeper
 8
 9# 修改zk配置文件zoo.cfg
10dataDir=/usr/local/zookeeper-3.4.12/data	#数据文件存放目录
11server.1=zkcluster1:2888:3888	#分别添加集群中的每台ZK Server节点。
12server.2=zkcluster2:2888:3888
13server.3=zkcluster3:2888:3888	
14
15# 在/usr/local/zookeeper-3.4.12/data目录下新建myid的文件
16cat /usr/local/zookeeper-3.4.12/data myid	# zkcluster1
171
18cat /usr/local/zookeeper-3.4.12/data myid	# zkcluster2
192
20cat /usr/local/zookeeper-3.4.12/data myid	# zkcluster3
213
22
23# 赋权zookeeper用户
24chown -R zookeeper:zookeeper /usr/local/zookeeper-3.4.12/
25
26# 关闭防火墙
27systemctl stop firewalld.service
28
29# 启动服务
30bash /usr/local/zookeeper-3.4.12/bin/zkServer.sh  start
31
32# 删除所有windows风格的启动脚本
33rm -rf /usr/local/zookeeper-3.4.12/bin/*.cmd
34
35# 为启动脚本增加执行权限
36chmod +x /usr/local/zookeeper-3.4.12/bin/*.sh
37
38# 查看状态
39zkServer.sh status
40
41# 查看进程PID
42jsp

分布式锁作用及其原理

为什么要有分布式锁
 分布式服务中,如果各个服务节点需要去竞争资源,无法使用单机多线程中 JDK 自带的锁来解决此类问题,故此时需要分布式锁来协调。

企业中有哪些常见的手段来实现分布式锁
 zookeeper、Redis、memcache

分布式锁的原理
zookeeper
 去创建相应的节点,创建成功,则表示获取到相应的锁,创建失败,则表示获取锁失败,释放锁的时候删除节点。
Redis、memcache
 对应的去设置一个值做为锁的一标志,每次获取锁的时候,判断对应的值是否存在,存在则无法获取,不存在,则设置相应的值,表示获取到锁。(Redis 使用 setnx,memcache 使用 add)

Zookeeper 实现分布式锁的多种方式

 创建节点的时候,一定要创建临时节点,避免应用获取到锁后,宕机,导致锁一致被持有。

  • 程序连接上 ZK 之后,尝试去创建节点,如果创建成功则成果获取锁,如果节点创建失败,程序进行短暂的休眠,之后重试。缺点:不断重试,会消耗资源。
  • 使用 ZK 的 watcher 机制。缺点:引发同一时刻同时唤醒大批等待程序,引起羊群效应。
  • 创建有序的节点。目标节点:/a0 排队进程阻塞在有序节点 /a1 /a2 /an,这样/a0 释放锁之后,每次只需要唤醒一个等待的进程即可,例如:唤醒/a1

基于 Zookeeper 原生的 API 实现分布式锁

  • 原生 API 不支持递归创建节点
  • 如果是单一应用,尽量不要使用分布式锁

引入 Maven 依赖

1<dependency>
2    <groupId>org.apache.zookeeper</groupId>
3    <artifactId>zookeeper</artifactId>
4    <version>3.4.13</version>
5</dependency>

基于 zk 实现分布式锁

 1import org.apache.zookeeper.*;
 2import java.io.IOException;
 3import java.util.concurrent.CountDownLatch;
 4import static org.apache.zookeeper.CreateMode.EPHEMERAL;
 5import static org.apache.zookeeper.ZooDefs.Ids.OPEN_ACL_UNSAFE;
 6
 7/**
 8 * 基于zk实现分布式锁
 9 */
10public class ZkLock {
11
12    private ZooKeeper zooKeeper;
13
14    private static CountDownLatch countDownLatch = new CountDownLatch(1);
15
16    private ZkLock() {
17        try {
18            zooKeeper = new ZooKeeper("192.1.31.220:2181,192.1.31.240:2181,192.1.31.250:2181", 5000, new ZkWatcher());
19            System.out.println(zooKeeper.getState());
20            try {
21                countDownLatch.await();
22            } catch (InterruptedException e) {
23                e.printStackTrace();
24            }
25            System.out.println("与zk建立连接=====>"+zooKeeper.getState());
26
27        } catch (IOException e) {
28            e.printStackTrace();
29        }
30    }
31
32    public static ZkLock getInstance() {
33        return Singleton.getInstance();
34    }
35
36    private class ZkWatcher implements Watcher {
37        @Override
38        public void process(WatchedEvent event) {
39            System.out.println("接收到监听事件=====》"+event);
40            if (Event.KeeperState.SyncConnected == event.getState()) {
41                countDownLatch.countDown();
42            }
43        }
44    }
45
46    public void lock(Integer id) {
47        String path = "/soulboy-product-lock-" + id;
48        //创建临时节点,如果创建成功的话,就表示获取锁,如果失败,则不断尝试
49        try {
50            zooKeeper.create(path,"".getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
51            System.out.println("成功获取锁");
52        } catch (Exception e) {
53            while (true) {
54                try {
55                    Thread.sleep(500L);
56                } catch (InterruptedException e1) {
57                    e1.printStackTrace();
58                }
59                try {
60                    zooKeeper.create(path,"".getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
61                } catch (Exception e1) {
62                    continue;
63                }
64                break;
65            }
66        }
67    }
68
69    /**
70     * 释放锁,直接删除zk节点
71     * @param id
72     */
73    public void unLock(Integer id) {
74        String path = "/soulboy-product-lock-" + id;
75        try {
76            zooKeeper.delete(path,-1);
77            System.out.println("成功释放锁");
78        } catch (InterruptedException e) {
79            e.printStackTrace();
80        } catch (KeeperException e) {
81            e.printStackTrace();
82        }
83    }
84
85    private static class Singleton {
86
87        private static ZkLock instance;
88        static {
89            instance = new ZkLock();
90        }
91
92        private static ZkLock getInstance() {
93            return instance;
94        }
95    }
96}

测试代码

 1import java.util.concurrent.CountDownLatch;
 2import java.util.concurrent.locks.Lock;
 3import java.util.concurrent.locks.ReentrantLock;
 4
 5/**
 6 * 线程安全的操作示例
 7 */
 8public class UnSafeThread {
 9
10    private static int num = 0;
11
12    private static CountDownLatch countDownLatch = new CountDownLatch(10);
13    private static ZkLock lock = ZkLock.getInstance();
14
15    /**y
16     * 每次调用对num进行++操作
17     */
18    public static void inCreate() {
19        lock.lock(1);
20        Thread th=Thread.currentThread();
21        System.out.println("当前num数值:" + num + ", Tread name:"+th.getName());
22        num++;
23        lock.unLock(1);
24    }
25
26    public static void test() {
27        System.out.println(Thread.currentThread().getName());
28    }
29
30    public static void main(String[] args) {
31        for (int i = 0; i < 10; i++) {
32            new Thread(() -> {
33                for (int j = 0; j < 100; j++) {
34                    inCreate();
35                    try {
36                        Thread.sleep(10);
37                    } catch (InterruptedException e) {
38                        e.printStackTrace();
39                    }
40                }
41                //每个线程执行完成之后,调用countdownLatch
42                countDownLatch.countDown();
43            }).start();
44        }
45
46        while (true) {
47            if (countDownLatch.getCount() == 0) {
48                System.out.println(num);
49                break;
50            }
51        }
52    }
53}

控制台输出

 1CONNECTING
 2接收到监听事件=====》WatchedEvent state:SyncConnected type:None path:null
 3与zk建立连接=====>CONNECTED
 4成功获取锁
 5当前num数值:0, Tread name:Thread-3
 6成功释放锁
 7成功获取锁
 8当前num数值:1, Tread name:Thread-3
 9成功释放锁
10成功获取锁
11当前num数值:2, Tread name:Thread-3
12成功释放锁
13...
14成功获取锁
15当前num数值:996, Tread name:Thread-7
16成功释放锁
17成功获取锁
18当前num数值:997, Tread name:Thread-7
19成功释放锁
20成功获取锁
21当前num数值:998, Tread name:Thread-7
22成功释放锁
23成功获取锁
24当前num数值:999, Tread name:Thread-7
25成功释放锁
261000

使用 Zookeeper 作为注册中心的问题

核心思想

  • 在实践中,注册中心不能因为自身的任何原因破坏服务之间本身的可连通性
  • 注册中心需要 AP,而 Zookeeper 是 CP

使用 Zookeeper 作为服务注册中心

启动 Docker 本地容器

1//获取镜像
2docker pull zookeeper:3.5
3
4//运行 Zookeeper 镜像
5docker run --name zookeeper -p 2181:2181 -d zookeeper:3.5

Provider 端几乎和使用 Euraka 没有区别
依赖

1		<dependency>
2			<groupId>org.springframework.cloud</groupId>
3			<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
4		</dependency>

bootstrap.properties

1spring.application.name=waiter-service

application.properties

 1spring.jpa.hibernate.ddl-auto=none
 2spring.jpa.properties.hibernate.show_sql=true
 3spring.jpa.properties.hibernate.format_sql=true
 4
 5management.endpoints.web.exposure.include=*
 6management.endpoint.health.show-details=always
 7
 8info.app.author=DigitalSonic
 9info.app.encoding=@project.build.sourceEncoding@
10
11server.port=0
12
13spring.cloud.zookeeper.connect-string=localhost:2181

Customer 端几乎和使用 Euraka 没有区别
依赖

1		<dependency>
2			<groupId>org.springframework.cloud</groupId>
3			<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
4		</dependency>

bootstrap.properties

1spring.application.name=customer-service

application.properties

1server.port=0
2
3spring.cloud.zookeeper.connect-string=localhost:2181

作者:Soulboy