Eureka
Spring Cloud Eureka主要是用于微服务的服务治理,服务治理微服务架构的最核心的模块,也就是注册中心。其功能主要实现了各个微服务实例的自动化发现和注册,他是基于Netflix Eureka 的一个二次封装,基于SpringBoot可以做到自动化配置。
最开始我们独立微服务之前,所独立的服务数目可能并不是很多,我们直接采取配置文件直连的方式即可,如果要实现服务调用的高可用,那么可以子啊业务程序出进行处理,比如配置多个服务提供端,在调用的时候有业务程序去判断调用其中的哪一个,以遍历的方式读取多个服务端,调用失败则轮询全部调用点或者随机访问,不论使用何种负载均衡的方式,那么不可避免的的话需要维护这个服务提供端的主机序列。如果之后我们的服务拆分独立的变得多的时候,那么整个配置文件的静态数据会越来越庞大,人工维护动态数据当然没问题,当然这不是我们所期望的。所以动态的服务注册中心就尤为重要。在分布式环境下要就要提一下CAP理论【C(一致性)、A(可用性)和P(分区容错性)】,该理论指出,一个分布式系统不可能同时满足三点。由于分区容错性在是分布式系统中必须要保证的,因此我们只能在A和C之间进行权衡。Zookeeper保证的是CP, 而Eureka则是AP。
Eureka在设计时就优先保证可用性。Eureka各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。只要有一个节点可以访问,那么整个服务注册中心就可以对外提供访问,而Eureka的客户端在向某个Eureka注册或如果发现连接失败,则会自动切换至其它节点,只不过查到的信息可能不是最新的(不保证强一致性)。Eureka还有一种自我保护机制,如果在15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,此时会出现以下几种情况:
- Eureka不再从注册列表中移除因为长时间没收到心跳而应该过期的服务
- Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上(即保证当前节点依然可用)
- 当网络稳定时,当前实例新的注册信息会被同步到其它节点中
实际上Eureka的高可用集群的实现机制就是相互注册到不同节点的注册中心上,用来把服务信息相互同步,来达到高可用。
服务注册
微服务架构中,每一个提供服务的单元都向这个节点注册中心去注册自己的服务,把自己的信息提供出去主机信息,协议等等信息,注册中心向一个管理员一样把这些服务单元分门归类,注册完毕之后,注册中心要对这些服务进行管理的过程就是双端要进行心跳连接,一直有连接那么该服务单元一直可以对外提供服务,如果心跳连接无效,那么注册中心就将其剔除在服务序列。
服务发现
微服务架构里由于注册中心的存在,各个服务调用端直接交互的是注册中心,并不需要去维护服务提供端的服务地址,只需要知道他叫什么服务名即可。由注册中心更具服务名去发现对应的服务提供端即可。
实战使用
Eureka的实战使用呢较为简单,但是配置稍差就会有神坑,比如版本问题,会导致无法访问到注册中心控制态,还有yml文件针对不同版本的配置项的-和驼峰命名,患有default-zone没有驼峰的文艺。不得不吐槽一下妈的真心垃圾配置
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
EurekaServer
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerHaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerHaApplication.class, args);
}
}
EurekaClient1提供服务端
@EnableEurekaClient
@RestController
@SpringBootApplication
public class EurekaHiHaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaHiHaApplication.class, args);
}
@GetMapping("/server")
public String server(){
return "11111";
}
}
着重提示下EurekaClient端的配置文件
server:
port: 8783
spring:
application:
name: Hello
eureka:
client:
service-url:
default-zone: http://peer1:8771/eureka/
EurekaClient2消费端
@EnableEurekaClient
@RestController
@SpringBootApplication
public class EurekaHiHaApplication {
ResttTemplete restTemplete=new ResttTemplete();;
public static void main(String[] args) {
SpringApplication.run(EurekaHiHaApplication.class, args);
}
@GetMapping("/call")
public String call(){
String result=restTemplete.getForObject("http://Hello/server",String.class);
return result;
}
}
ZooKeeper
当向ZookeeperServer查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的注册信息,但不能接受服务直接down掉不可用。也就是说,服务注册功能对可用性的要求要高于一致性。但是zk在选主期间是无法对外提供服务的,在云部署的环境下,因网络问题使得zk集群失去master节点是较大概率会发生的事,虽然服务能够最终恢复,但是一致性导致的服务不可用是不能容忍的。所以zk用于要求一致性的环境中,不要求可用性,必须保证一致性才会使用zk作为服务的注册中心,当然也有兜底机制,就是在zk不能使用的时候读取静态配置来保证可用性,这属于业务设计的容错范畴。
CAP和BASE
分布式架构的出现,就面临着数据一致,服务可用,分区容错等问题,ZooKeeper的诞生就是为了协调分布式系统一致性的问题,针对CAP理论,各分布式系统设计的同时都是咋保证P的前提下去在C,A之间达到的一种平衡,BASE理论就是对CAP的最好的一种总结,就是无法满足强一致性那就达到最终一致性即可。基本可用,也就是部分可用系统不会瞬间无法访问,可能带来的就是响应时间的损失,可能是提供服务的能力的损失类似服务的降级。但是可以保证用户可以使用。最终一致性,不要求实时数据的情况下,集群数据允许有中间状态,整个集群内部呢总会有数据同步的很慢的节点,在最终一致即可。
分布式系统中的事务问题
事务我们都知道,是指在系统对数据进行读写的一个逻辑执行单元,一般就是在关系型数据库中,事务的特点就是原子性,一致性,隔离性,持久性(ACID)。针对原子性呢就是操作不存在二义性,一致性也很好理解,就是不会破坏原数据的完整性,隔离性在 你真的了解MySQL吗一文中有提到,持久性就是,当事务被commit之后,就落盘了,除非机器坏掉,爆炸,一般是不会丢失的。这四个特性,在单机情况下很好被满足,但是在分布式情况下试想下如果是银行异地跨行转账,A转给B 10000元,A的账户减少一万元,B的账户增加一万元,这两个都是无状态的还相互独立,要想实现上述的目标就离不开得使用分布式事务,不然钱丢了,用户找谁说理去,A钱转完B没收到A的账户就没钱了,这就出问题了,所以要保证分布式下的ACID是很困难。我这里提供一种设计方案,调用A账户减少传输一个增加一个全局id(可以使用zk生成唯一ID),然后提交到事务处理中心去,然后调用B账户增加服务也是提交到事务处理中心去,然后在事务处理中心确认到二者本次调用的唯一id一直,然后再做事物的统一提交。
ZooKeeper集群
我们知道单一的ZooKeeper节点对外提供服务有单点问题,而在整个分布式系统中,不可能只有一个节点的ZooKeeper,不把鸡蛋放在一个篮子里的道理,所以就有了ZooKeeper集群的东西,众多的节点直接要协调好,才可以对外提供服务,主流的设计一般是Master-Salver设计,有主节点负责数据写入,从节点负责数据同步。ZooKeeper也是这种设计,是基于Paxos理论的变种ZAB协议,先是去所有的从节点去发起一个提案(myid,zxid),然后进过选举之后谁的携带zxid大谁会被选主,如果zxid一直比较myid大的选主。然后告诉从节点去同步,然后才可以对外提供访问。这就不得不提及ZooKeeper的三种角色了
- Leader
对外提供服务的节点,对内进行写入操作。
- Follower
在选主过程中投票的节点,在Leader存活期间,用于数据的同步
- Observer
在Zookeeeper3.0之后的版本中映入的用于提升Leader性能的节点,他会接受写请求然后转发给Leader。并没有增加写的负担,读请求自己处理,增加多个观察者对于用于大型系统而言,可以提高Zookeeper的读请求的性能,不参与选主环节,在zoo.cfg文件中设置
peerType=observer||server.3:localhost:2181:3181:observer
之后该节点就是观察者节点了,
说道了选主,那么就离不开这个集群可用的判定条件,可用节点大于全部节点的半数以上才可以对外提供服务,也就是可用节点数量 > 总节点数量/2,那我们为啥总是听人说集群设置一般为2n+1个也就是奇数个。
一方面从集群可用条件来看,4台机器和三台均可以提供访问,但是如果出现leader因网络原因短暂的心跳无法建连,过一会儿又恢复了,这时候可能出现俩个小的集群,也就是脑裂现象,原来三台那个可能是个A小集群1一台机器无法提供服务,但是B集群是2台机器是可以提供访问的。也就是部分节点不可以使用,可以人工处理,但是如果是4台机器的话,2个小集群都是2台的时候,那么久无法对外提供服务了,因为不满足集群可用的判断条件。另一方面少部署一台机器也能节省部分开支,何乐而不为呢