1、项目结构
pom文件:
<!--spring boot 版本依赖-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<!-- https://mvnrepository.com/artifact/com.101tec/zkclient-->
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper-->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.5</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.8</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
</dependencies>
2、生产者
生产者把当前ip和端口注册到zk上
2.1、pom文件
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
2.2、application.yml
server:
port: 9000
zookeeper:
ip: 192.168.92.39
timeout: 10000
2.3、ZookeeperProviderConfig
@Configuration
public class ZookeeperProviderConfig implements InitializingBean {
/***
*功能描述 本地服务端口
*/
@Value("${server.port}")
private Integer port;
/***
*功能描述 zk ip
*/
@Value("${zookeeper.ip}")
private String zkAddress;
/***
*功能描述 超时时间
*/
@Value("${zookeeper.timeout}")
private Integer zkTimeout;
/***
*功能描述 zk注册的服务节点
*/
private static final String BASE_SERVICE = "/services";
private static final String PRODUCER_SERVICE = "/producer";
@Override
public void afterPropertiesSet() throws Exception {
//1、获取本地地址
String hostAddress = InetAddress.getLocalHost().getHostAddress();
//2、创建Zookeeper
ZkClient zkClient = new ZkClient(zkAddress, zkTimeout);
//3、判断父亲节点是否存在,如果不存在创建
if (!zkClient.exists(BASE_SERVICE)) {
zkClient.createPersistent(BASE_SERVICE);
}
//4、把当前服务注册到zk中
System.out.println(serverUrl);
zkClient.createEphemeralSequential(BASE_SERVICE + PRODUCER_SERVICE, serverUrl);
}
}
2.4、controller
@RestController
@RequestMapping("/zk")
public class ProviderController {
/***
*功能描述 本地服务端口
*/
@Value("${server.port}")
private Integer port;
@GetMapping("/hello")
public String hello() {
return "provider " + port + " say hello";
}
}
2.5、启动生产者,并且在idea复制一份,把端口改成9001启动
1)访问9000,9001端口
2)打开zk:
3、消费者
消费者用于监听生产者提供服务的根节点,如果根节点发生了变化,及时更新服务列表
3.1、pom
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
3.2、yml
server:
port: 8000
zookeeper:
ip: 192.168.92.39
timeout: 10000
3.3、LoadBalance
public abstract class LoadBalance {
/***
*功能描述 服务列表
*/
public volatile static List<String> SERVICE_LIST;
/***
*功能描述 获取一个服务
*/
public abstract String choseServiceHost();
}
3.4.、轮询
@Configuration
public class LunXunBalance extends LoadBalance {
private static Integer index = 0;
/***
*功能描述 如果服务列表不为空,就从服务列表获取一个url返回
*/
@Override
public String choseServiceHost() {
if (!CollectionUtils.isEmpty(SERVICE_LIST)) {
index++;
if (index >= SERVICE_LIST.size()) {
index = 0;
}
return SERVICE_LIST.get(index);
}
return null;
}
}
3.5、ZookeeperCustomerConfig
@Configuration
public class ZookeeperCustomerConfig {
/***
*功能描述 zk地址
*/
@Value("${zookeeper.ip}")
private String zkAddress;
/***
*功能描述 超时时间
*/
@Value("${zookeeper.timeout}")
private Integer zkTimeout;
/***
*功能描述 zk注册的服务节点
*/
private static final String BASE_SERVICE = "/services";
@Bean
public ZkClient zkClient() {
ZkClient zkClient = new ZkClient(zkAddress, zkTimeout);
updateServerList(zkClient);
zkClient.subscribeChildChanges(BASE_SERVICE, new ServerWatcher(zkClient));
return zkClient;
}
/**
* 功能描述 更新服务列表
*/
private void updateServerList(ZkClient zkClient) {
List<String> serverList = new ArrayList<>();
try {
//获取节点
List<String> childrens = zkClient.getChildren(BASE_SERVICE);
//获取节点的值
for (String child : childrens) {
Object host = zkClient.readData(BASE_SERVICE + "/" + child);
serverList.add((String) host);
}
//把值赋给LoadBalance
LoadBalance.SERVICE_LIST = serverList;
System.out.println("服务列表已经更新:" + serverList);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 功能描述 监听子节点的变换
*
* @author Zrs
* @date 2019/8/24
* @return: ......
*/
private class ServerWatcher implements IZkChildListener {
private ZkClient zkClient;
public ServerWatcher(ZkClient zkClient) {
this.zkClient = zkClient;
}
@Override
public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {
updateServerList(zkClient);
}
}
}
.3.6、RestConfig
@Configuration
public class RestConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
3.7、 CustomerController
@RestController
@RequestMapping("/customer")
public class CustomerController {
@Resource
private RestTemplate restTemplate;
@Autowired
private LoadBalance loadBalance;
@RequestMapping("/hello")
public String customer() {
String url = loadBalance.choseServiceHost();
String hello = restTemplate.getForObject("http://" + url + "/zk/hello/", String.class);
return hello;
}
}
3.8、启动消费者者
1)第一次访问controller
2)第二次访问controller
3)停掉9001端口的客户端
4)再次访问url