Zookeeper微服务注册与发现
Linux下安装Zookeeper以及集群配置
为什么zookeeper节点是奇数?
我们知道,在每台机器数据保持一致的情况下,zookeeper集群可以保证,客户端发起的每次查询操作,集群节点都能返回同样的结果。
但是对于客户端发起的修改、删除等能改变数据的操作呢?集群中那么多台机器,你修改你的,我修改我的,最后返回集群中哪台机器的数据呢?
这就是一盘散沙,需要一个领导,于是在zookeeper集群中,leader的作用就体现出来了,只有leader节点才有权利发起修改数据的操作,而follower节点即使接收到了客户端发起的修改操作,也要将其转交给leader来处理,leader接收到修改数据的请求后,会向所有follower广播一条消息,让他们执行某项操作,follower 执行完后,便会向 leader 回复执行完毕。当 leader 收到半数以上的 follower 的确认消息,便会判定该操作执行完毕,然后向所有 follower 广播该操作已经生效。
所以zookeeper集群中leader是不可缺少的,但是 leader 节点是怎么产生的呢?其实就是由所有follower 节点选举产生的,讲究民主嘛,而且leader节点只能有一个,毕竟一个国家不能有多个总统。
这个时候回到我们的小标题,为什么 zookeeper 节点数是奇数,我们下面来一一来说明:
①容错率
首先从容错率来说明:(需要保证集群能够有半数进行投票)
2台服务器,至少2台正常运行才行(2的半数为1,半数以上最少为2),正常运行1台服务器都不允许挂掉,但是相对于 单节点服务器,2台服务器还有两个单点故障,所以直接排除了。
3台服务器:至少2台正常运行才行(3的半数为1.5,半数以上最少为2),正常运行可以允许1台服务器挂掉
4台服务器:至少3台正常运行才行(4的半数为2,半数以上最少为3),正常运行可以允许1台服务器挂掉
5台服务器:至少3台正常运行才行(5的半数为2.5,半数以上最少为3),正常运行可以允许2台服务器挂掉
②防脑裂
脑裂集群的脑裂通常是发生在节点之间通信不可达的情况下,集群会分裂成不同的小集群,小集群各自选出自己的leader节点,导致原有的集群出现多个leader节点的情况,这就是脑裂。
3台服务器:投票选举半数为1.5,一台服务裂开,和另外两台服务器无法通行,这时候2台服务器的集群(2票大于半数1.5票),所以可以选举出leader,而 1 台服务器的集群无法选举。
4台服务器:投票选举半数为2,可以分成 1,3两个集群或者2,2两个集群,对于 1,3集群,3集群可以选举;对于2,2集群,则不能选择,造成没有leader节点。
5台服务器:投票选举半数为2.5,可以分成1,4两个集群,或者2,3两集群,这两个集群分别都只能选举一个集群,满足zookeeper集群搭建数目。
以上分析,我们从容错率以及防止脑裂两方面说明了3台服务器是搭建集群的最少数目,4台发生脑裂时可能会造成没有leader节点的错误。
Linux环境下安装zookeeper,本文以zookeeper3.4.9为列:https://blog.csdn.net/qq_36763419/article/details/112189524
Zookeeper作为注册中心
1、zookeeper是一个分布式协调工具,可以实现注册中心功能。
2、关闭Linux服务器防火墙后,启动zookeeper服务器。
3、Zookeeper可以取代Eureka服务器,作为微服务注册中心。
微服务提供者
1、引入 pom 依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
<!--先排除自带的3.5.3-beta版本jar包-->
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--增加与zookeeper版本一致的jar包-->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.6.3</version>
</dependency>
2、写 yml 配置文件
spring:
application:
name: cloud-provider-payment
cloud:
zookeeper:
connect-string: ip:port,ip:port,ip:port #服务器的公网ip
3、主启动类
@SpringBootApplication
@EnableDiscoveryClient //该注解用于向consul或者zookeeper作为注册中心时注册服务
public class PaymentMain8004 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8004.class, args);
}
}
4、controller 验证服务是否注册到zookeeper 注册中心中
@RestController
@Slf4j
public class PaymentController {
@Autowired
private DiscoveryClient discoveryClient;
@Value("${server.port}")
private String serverPort;
@GetMapping(value = "/payment/zk")
public String paymentzk() {
return "springcloud with zookeeper:" + serverPort + "\t" + UUID.randomUUID().toString();
}
@GetMapping(value = "/payment/discovery")
public Object discovery() {
//获取服务列表
List<String> services = discoveryClient.getServices();
for (String element : services) {
log.info("*****element: " + element);
}
//根据微服务名称进一步获得该微服务的信息
List<ServiceInstance> instances = discoveryClient.getInstances("cloud-provider-payment");
for (ServiceInstance instance : instances) {
log.info(instance.getServiceId() + "\t" + instance.getHost() + "\t" + instance.getPort() + "\t" + instance.getUri());
}
return this.discoveryClient;
}
}
微服务消费者
1、引入 pom 依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
<!--先排除自带的3.5.3-beta版本jar包-->
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--增加与zookeeper版本一致的jar包-->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.6.3</version>
</dependency>
2、写 yml 配置文件
spring:
application:
name: cloud-consumer-order
cloud:
zookeeper:
connect-string: ip:port,ip:port,ip:port #服务器的公网ip
3、主启动类
@SpringBootApplication
@EnableDiscoveryClient
public class OrderZKMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderZKMain80.class, args);
}
}
4、配置类
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced //为客户端RestTemplate提供访问负载均衡能力
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
5、controller 验证服务是否注册到zookeeper 注册中心中
@RestController
@Slf4j
@RestController
@Slf4j
public class OrderZKController {
private static final String INVOKE_URL = "http://cloud-provider-payment";
@Resource
private RestTemplate restTemplate;
@GetMapping(value = "/consumer/payment/zk")
public String paymentInfo() {
String result = restTemplate.getForObject(INVOKE_URL + "/payment/zk", String.class);
return result;
}
@GetMapping(value = "/consumer/payment/discovery")
public CommonResult discovery() {
Object discoveryClient = restTemplate.getForObject(INVOKE_URL + "/payment/discovery", Object.class);
return new CommonResult(200,"查询成功",discoveryClient);
}
}