目录

  • 前言
  • 1. 什么是微服务?
  • 1.1 什么是SOA?
  • 1.2 SOA和微服务的区别?
  • 2. 为什么要用微服务?
  • 3. 使用微服务存在的问题以及解决办法
  • 3.1 微服务之间如何通信
  • 3.2 微服务如何发现
  • 3.3 微服务挂了,如何解决?
  • 3.3.1 重试机制
  • 3.3.2 限流
  • 3.3.3 熔断、降级
  • 4. 微服务Cloud体系
  • 4.1 服务发现 Eureka
  • 4.1.1 4月7更新
  • 4.1.2 RestTemplate
  • 4.2 服务负载均衡 Ribbon
  • 4.3 服务熔断器 Hystrix
  • 4.4 服务网关 Zuul
  • 4.5 分布式配置 Spring Cloud Config
  • 5. 微服务Dubbo体系
  • 5.1 dubbo
  • 5.2 zookpeer
  • 5.2.1 Paxos算法
  • 5.2.2 ZAB协议:
  • 5.2.3 选举算法和流程:FastLeaderElection(默认提供的选举算法)
  • 5.2.4 zookpeer实现分布式锁
  • 6. 二者对比
  • 7. 结尾
  • 8. 参考

前言

博主近期总结所学微服务相关知识,发布于CSDN,如有错误,请及时提出,我做更正!

1. 什么是微服务?

且看百度解释:

微服务是一个新兴的软件架构,就是把一个大型的单个应用程序和服务拆分为数十个的支持微服务。一个微服务的策略可以让工作变得更为简便,它可扩展单个组件而不是整个的应用程序堆栈,从而满足服务等级协议。 对于大型应用程序来说,增加更多的用户则意味着提供更大型的弹性计算云(EC2)实例规模,即便只是其中的一些功能扩大了规模亦是如此。其最终结果就是企业用户只需为支持超过微服务的那部分需求的EC2实例支付费用。

不得不提的是另一个概念:SOA

1.1 什么是SOA?

且看百度解释:

面向服务的架构(SOA)是一个组件模型,它将应用程序的不同功能单元(称为服务)通过这些服务之间定义良好的接口和契约联系起来。接口是采用中立的方式进行定义的,它应该独立于实现服务的硬件平台、操作系统和编程语言。这使得构建在各种各样的系统中的服务可以以一种统一和通用的方式进行交互。

1.2 SOA和微服务的区别?
  • 微服务是SOA的延续,SOA诞生于2000年,微服务则衍生与近期即互联网相当成熟的时代
  • SOA 高度依赖服务总线(ESB),而微服务不需要。
  • 具体讨论请看知乎大佬的回答:传送门
2. 为什么要用微服务?

假设我现在有一个商城系统,一开始系统体量很小的时候,单机完全可以抗住这些并发,但是当用户需求越来越多,单机环境肯定是扛不住这么大的压力

以典型的3层架构的传统web应用为例,该应用由用户界面、数据库、服务器端应用组成。这里的服务器端应用被称为一体化,包含表现、业务层、数据层。所有代码都存在于同一个代码库中。为了让代码工作起来,它被部署成为一个单元。任何一个小的改动变化,都需要重新构建和部署整个应用。

所以我们采取分布式的方式,即多机部署,并将系统各种服务进行拆分,独立成单个服务,每个服务部署在单个服务器上并做好负载均衡。如下图所示

若依微服务设置文件大小_服务器

3. 使用微服务存在的问题以及解决办法

上面提出了将各种服务进行拆分,那么各个服务之间肯定是互不通信的,就像tomcat里的各个webapp一样,目前可以总结出,微服务所存在的几大问题

3.1 微服务之间如何通信

一般也基本上常见的两种就是阿里dubbo和SpringCloud,本质上 为

  1. REST API(SpringCloud)
  2. RPC通信(dubbo)

这两者具体区别请移步这个老兄写的,简单来讲:

  • REST API基于HTTP协议,即服务之间通过发送HTTP请求的方式来进行各个服务之间的通信,即REST是基于资源的,且较为灵活,因为有HTTP各种状态码、报头等但相对来讲更为笨重一些
  • RPC是基于TCP,是面向接口的、且性能较高,但是编码较为繁琐

dubbo官网有这样一段性能对比,可以看到相差不是很大

若依微服务设置文件大小_服务器_02

3.2 微服务如何发现

我们将服务拆分后,那么各个服务之间是如何发现的呢,这时候就引入了一个注册中心的概念,打个比方 假如我是租房的,房东直接和我联系会比较麻烦,因为互不认识,我们一般通过网上中介的方式去租房,那么这里的中介就相当于这里的微服务发现中心、 同样的一般的服务发现中心有如下:

  1. Zookpeer
  2. Eureka

说这两者区别之前先说一个概念:CAP定理

若依微服务设置文件大小_服务器_03

  • Consistency 一致性
  • Availability 可用性
  • Partition tolerance 分区容错性

第一 Consistency

Consistency 的意思是写操作之后的读操作,必须返回该值。举例来说,某条记录是 v0,用户向 G1 发起一个写操作,将其改为 v1。接下来,用户的读操作就会得到 v1。这就叫一致性。问题是,用户有可能向 G2 发起读操作,由于 G2 的值没有发生变化,因此返回的是 v0。G1 和 G2 读操作的结果不一致,这就不满足一致性了。为了让 G2 也能变为 v1,就要在 G1 写操作的时候,让 G1 向 G2 发送一条消息,要求 G2 也改成 v1。

若依微服务设置文件大小_服务器_04

这样的话,用户向 G2 发起读操作,也能得到 v1。

若依微服务设置文件大小_服务器_05

简而言之: 在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)

第二 Availability

Availability 中文叫做"可用性",意思是只要收到用户的请求,服务器就必须给出回应。 用户可以选择向 G1 或 G2 发起读操作。不管是哪台服务器,只要收到请求,就必须告诉用户,到底是 v0 还是 v1,否则就不满足可用性。

第三 Partition tolerance 分区容错性

大多数分布式系统都分布在多个子网络。每个子网络就叫做一个区(partition)。分区容错的意思是,区间通信可能失败。比如,一台服务器放在中国,另一台服务器放在美国,这就是两个区,它们之间可能无法通信。

若依微服务设置文件大小_分布式_06

上图中,A 和 B 是两台跨区的服务器。A向 B 发送一条消息,B 可能无法收到。系统设计的时候,必须考虑到这种情况。

一般来说,分区容错无法避免,因此可以认为 CAP 的 P 总是成立。CAP 定理告诉我们,剩下的 C 和 A 无法同时做到。

为什么一致性和可用性不可能同时成立?因为可能出现通信失败的情况(即出现分区容错)。

回过头来,在回顾一下常见的服务发现中心:

  1. Zookpeer zookpeer具体介绍请移步这里 简单来讲,

ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。 ZooKeeper的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。 ZooKeeper包含一个简单的原语集,提供Java和C的接口。 ZooKeeper代码版本中,提供了分布式独享锁、选举、队列的接口,代码在$zookeeper_home\src\recipes。其中分布锁和队列有Java和C两个版本,选举只有Java版本。

  1. Eureka Eureka 是 一个基于 REST 服务的、服务注册与发现的组件,它主要包括两个组件:Eureka Server 和 Eureka ClientEureka Client:一个Java客户端,用于简化与 Eureka Server 的交互Eureka Server:提供服务注册和发现的能力(即注册中心)
  2. Eureka 和Zookeeper 区别 前面提到了CAP定理,从而又指出了为什么不能同时满足CAP,Zookeeper保证CP而Eureka保证AP 3.1 Zookeeper当master挂了,会在30-120s进行leader选举,这点类似于redis的哨兵机制,在选举期间Zookeeper 是不可用的 ,这么长时间不能进行服务注册,用户是无法忍受的。Zookeeper保持节点的一致性,牺牲了A/高可用。而Eureka不会,即使Eureka有部分挂掉,还有其他节点可以使用的,他们保持平级的关系,只不过信息有可能不一致,这就是AP,牺牲了C/一致性。 3.2 Eureka有自我保护机制(15分钟内超过85%的服务节点没有心跳/down)即使服务不可用,也会保留当前失效的微服务,默认90秒,在这90秒Eureka不会注销微服务,在这90秒内仍然可以接受新的服务注册,只是不会同步到其他节点上。当坏掉的服务恢复的时候,会自动加入到节点上,也是高可用的一种。然后退出自我保护机制,这也是应对网络异常的一种机制 总之当Zookeeper出现网络等故障的时候导致整个服务注册就会瘫痪,而Eureka却能很好的应对网络故障导致失去节点的情况,相对来讲Eureka作为注册中心更专业一点,因为分布式集群中,任何一个节点都是不可靠的,如何保证不可靠的时候还能够使用,Eureka显得更为优秀一些
3.3 微服务挂了,如何解决?
  • 重试机制
  • 限流
  • 熔断机制
  • 降级(本地缓存)
3.3.1 重试机制

重试是一种保障业务运行的容错机制,比如页面查询、数据导出等业务场景,如果某个微服务出现异常,可以将请求动态路由到其他的服务。而重试机制要保证幂等性

网上是这样介绍的【接口的幂等性实际上就是接口可重复调用,在调用方多次调用的情况下,接口最终得到的结果是一致的】

一般情况下保证接口幂等性的手段:

  1. 唯一索引 防止新增数据
  2. token机制,防止页面重复提交
  3. 悲观锁/乐观锁
  4. 分布式锁 等等 SpringCloud在启动类enable retry即可使用重试机制
3.3.2 限流

在应对秒杀、大促、双 11、618 等高性能压力的场景时,限流已经成为了标配技术解决方案,为保证系统的平稳运行起到了关键性的作用。不管应用场景是哪种,限流无非就是针对超过预期的流量,通过预先设定的限流规则选择性的对某些请求进行限流“熔断”。 常见的限流算法:

  1. Leaky Bucket 漏桶顾名思义,漏桶是总容量是不变的, 水滴(请求) 以任意速率流入, 但总是以恒定速率流出, 如果请求来得太多太快, 桶的容量就会撑满, 后续的请求就会被拒绝, 也就是说当一个请求到来, 就流一滴水进桶里,如果可以放入, 则处理此请求, 否则漏桶已满, 则拒绝此请求, 直到桶中水滴不再满时
  2. Token Bucket 令牌桶 令牌桶与上面的漏斗有相似之处, 只不过它不是以固定速率流出, 而是以固定速率放入令牌到令牌桶中, 请求到来时从令牌桶中领取一个令牌才可继续处理服务, 如果取不到令牌, 则拒绝此请求
3.3.3 熔断、降级

假设我的微服务存在以下调用关系

若依微服务设置文件大小_分布式_07

服务A调用B,B调用C,原则上这样的调用关系没有任何问题,但是之前也提到过,微服务任何一个节点都不是可靠的,任何一个节点都存在宕机、不能使用的情况,如果Service C因为抗不住请求,变得不可用。那么Service B的请求也会阻塞,慢慢耗尽Service B的线程资源,Service B就会变得不可用。紧接着,Service A也会不可用,从而导致整个体系出现服务雪崩的情况,而服务熔断和服务降级就可以视为解决服务雪崩的手段之一。

服务熔断: 当下游的服务即现在的Service C因为某种原因突然变得不可用或响应过慢,上游服务(ServiceA、B)为了保证自己整体服务的可用性,不再继续调用目标服务(Service C),直接返回,快速释放资源。如果目标服务情况好转则恢复调用。服务降级: 降级是指自己的待遇下降了,从RPC调用环节来讲,就是去访问一个本地的伪装者而不是真实的服务。就比如,双十一的时候,页面显示:网络出小差啦这些,其实质上调用的是本地的HTML或者其他静态资源

服务熔断和服务降级的区别:

  1. 触发原因:服务熔断一般是某个服务(下游服务)故障引起,而服务降级一般是从整体负荷考虑;
  2. 管理目标的层次: 熔断其实是一个框架级的处理,每个微服务都需要(无层级之分),而降级一般需要对业务有层级之分(比如降级一般是从最外围服务开始)
  3. 实现方式: 服务降级具有代码侵入性(由控制器完成/或自动降级),熔断一般称为自我熔断。
4. 微服务Cloud体系
  • 服务发现——Netflix Eureka
  • 客户端负载均衡——Netflix Ribbon
  • 断路器——Netflix Hystrix
  • 服务网关——Netflix Zuul
  • 分布式配置——Spring Cloud Config
4.1 服务发现 Eureka

Eureka主要由两个组件:

  1. Eureka Client
  2. Eureka Server

前面也提到了Eureka组件

若依微服务设置文件大小_分布式_08

细化一些:Eureka 含有

  1. 服务发现
  2. 服务提供者
  3. 服务消费者
  4. 服务中介
  5. 服务注册:当 Eureka 客户端向[Eureka] Server注册时,它提供自身的元数据,比如IP地址、端口,运行状况指示符URL,主页等
  6. 服务续约: Eureka 客户会每隔30秒(默认情况下)发送一次心跳来续约。通过续约来告知Eureka Server该 Eureka 客户仍然存在,没有出现问题。正常情况下,如果 Eureka Server在90秒没有收到 Eureka 客户的续约,它会将实例从其注册表中删除。
  7. 获取注册列表信息 :Eureka 客户端从服务器获取注册表信息,并将其缓存在本地。客户端会使用该信息查找其他服务,从而进行远程调用。该注册列表信息定期(每30秒钟)更新一次。
  8. 服务下线
  9. 服务剔除
4.1.1 4月7更新

最近学了一些分布式的东西,感觉走完了Dubbo+zookpeer和SpringCloud体系的大部分流程,细节还没来得及看,有个重要的消息就是Eureka是在2.X版本之后就不在提供更新了,因为性能有了瓶颈,取而代之的可以是Zookpeer,还有一个更为优秀的,也是ALibaba开元出来的就是Nacos,简单来讲nacos就是注册中心+配置中心 下方会讲配置中心的相关概念,至于nacos的实现原理,有待博主亲自实践一番!

4.1.2 RestTemplate
  1. 什么是RestTemplate? Spring框架提供的RestTemplate类可用于在服务中调用REST服务,它简化了与http服务的通信方式,统一了RESTful的标准,封装了http链接, 我们只需要传入url及返回值类型即可。
  2. 为什么要提RestTemplate? 因为Eureka 框架中的注册、续约等,底层都是使用的RestTemplate。
  3. RestTemplate所包含内容
  • HttpMessageConverter 对象转换器
  • ClientHttpRequestFactory 默认是JDK的HttpURLConnection
  • ResponseErrorHandler 异常处理
  • ClientHttpRequestInterceptor 请求拦截器
  1. 使用样例!:
public MyRestClientServiceRequest(RestTemplateBuilder restTemplateBuilder) {  
        this.restTemplate = restTemplateBuilder  
            .basicAuthorization("username", "password")  
            .setConnectTimeout(3000)  
            .setReadTimeout(5000)  
            .rootUri("http://localhost:8080/mytest/test")  
            .build();  
    }
  1. 与HTTPcilent对比: 与HTTPcilent对相比,RestTemplate使用起来更简便、快捷
4.2 服务负载均衡 Ribbon

客户端负载均衡——Ribbon,没错就是客户端负载均衡,提起负载均衡,不得不提大名鼎鼎的nginx,小小轻量级服务器地扛得住这么大的QPS,那么这两者有什么不同呢?

首先nginx,它是一种集中式的反向代理的负载均衡器。

何为集中式呢?简单理解就是将所有请求都集中起来,然后再进行负载均衡。如下图。

若依微服务设置文件大小_若依微服务设置文件大小_09

而反向代理的解释,百度显得更为专业一点:

反向代理服务器位于用户与目标服务器之间,但是对于用户而言,反向代理服务器就相当于目标服务器,即用户直接访问反向代理服务器就可以获得目标服务器的资源。同时,用户不需要知道目标服务器的地址,也无须在用户端作任何设定。反向代理服务器通常可用来作为Web加速,即使用反向代理作为Web服务器的前置机来降低网络和服务器的负载,提高访问效率

我们的Ribbon工作机制是怎么样的呢?看图你就明白了:

若依微服务设置文件大小_服务器_10

在Nginx中,请求是先统一进入负载均衡器然后再进行分发,而在Ribbon中是先在客户端进行负载均衡才进行请求的。

nginx负载均衡算法:

  1. 轮询
  2. 加权轮值
  3. iphash
  4. fair
  5. url_hash

当然还有好多,印象中大概有八个,但是这几个是最常用的!Ribbon 的均衡算法:

  1. RoundRobinRule:轮询策略。Ribbon默认采用的策略。若经过一轮轮询没有找到可用的provider,其最多轮询 10 轮。若最终还没有找到,则返回 null。
  2. RandomRule: 随机策略,从所有可用的 provider 中随机选择一个。
  3. RetryRule: 重试策略。先按照 RoundRobinRule 策略获取 provider,若获取失败,则在指定的时限内重试。默认的时限为 500 毫秒。
4.3 服务熔断器 Hystrix

在分布式环境中,不可避免地会有许多服务依赖项中的某些失败。Hystrix是一个库,可通过添加等待时间容限和容错逻辑来帮助您控制这些分布式服务之间的交互。Hystrix通过隔离服务之间的访问点,停止服务之间的级联故障并提供后备选项来实现此目的,所有这些都可以提高系统的整体弹性。

前面也提到了服务的熔断、降级以避免服务雪崩,这里就简单探讨一下如何使用吧!

@HystrixCommand(fallbackMethod = "fallback2", commandProperties = {
            @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60"),
    })
    @GetMapping("/user/myTest")
    public String test(@RequestParam ("number")Integer number) {
        if (number == 1) {
            return "success";
        }
        //或者 注入restTemplate ,感觉这样还减少对象的创建
        RestTemplate restTemplate = new RestTemplate();
        String str = restTemplate.getForObject("http://127.0.0.1:8081/rw/user/test", String.class);
        return str;
    }

解释:

  1. circuitBreaker.enabled :true 打开熔断 默认开启
  2. circuitBreaker.requestVolumeThreshold: 当在配置时间窗口内达到此数量的失败后,进行短路。默认20个
  3. circuitBreaker.sleepWindowInMilliseconds:短路多久以后开始尝试是否恢复,默认5s
  4. circuitBreaker.errorThresholdPercentage:出错百分比阈值,当达到此阈值后,开始短路。默认50%
4.4 服务网关 Zuul

简单来讲呢,Zuul 是netflix开源的一个API 网关 服务器, 本质上是一个web servlet应用。 Zuul在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web网站后端所有请求的前门,也要注册入Eureka.

原理图:

若依微服务设置文件大小_若依微服务设置文件大小_11

zuul可实现的功能有:

  1. 身份认证
  2. 审查与监控
  3. 动态路由:将请求分发到不同的集群,以达到负载均衡的目的
  4. 负载分配:为每一种负载均衡分配对应容量,并弃用超出限定值的请求
  5. 静态响应处理
  6. 多区域弹性 除此之外还有路径屏蔽、敏感请求头屏蔽、过滤等功能
4.5 分布式配置 Spring Cloud Config

当我们的微服务系统开始慢慢地庞大起来,那么多Consumer、Provider、Eureka Server、Zuul系统都会持有自己的配置,这个时候我们在项目运行的时候可能需要更改某些应用的配置,如果我们不进行配置的统一管理,我们只能去每个应用下一个一个寻找配置文件然后修改配置文件再重启应用。

而Spring Cloud Config可以对配置文件统一地进行管理,又能在项目运行时动态修改配置文件。

若依微服务设置文件大小_服务器_12

但是查资料所了解到的,实际生产过程中一般采用Bus消息总线 +Spring Cloud Config进行配置的动态刷新

5. 微服务Dubbo体系

dubbo体系显得就简单很多,基本上就dubbo+zookpeer这个样子

5.1 dubbo

先看官方给的配置,虽然有点乱… …

若依微服务设置文件大小_若依微服务设置文件大小_13

各层说明

  • config 配置层:对外配置接口,以 ServiceConfig, ReferenceConfig 为中心,可以直接初始化配置类,也可以通过 spring 解析配置生成配置类
  • proxy 服务代理层:服务接口透明代理,生成服务的客户端 Stub 和服务器端 Skeleton, 以 ServiceProxy 为中心,扩展接口为 ProxyFactory
  • registry 注册中心层:封装服务地址的注册与发现,以服务 URL 为中心,扩展接口为 RegistryFactory, Registry, RegistryService
  • cluster 路由层:封装多个提供者的路由及负载均衡,并桥接注册中心,以 Invoker 为中心,扩展接口为 Cluster, Directory, Router, LoadBalance
  • monitor 监控层:RPC 调用次数和调用时间监控,以 Statistics 为中心,扩展接口为 MonitorFactory, Monitor, MonitorService
  • protocol 远程调用层:封装 RPC 调用,以 Invocation, Result 为中心,扩展接口为 Protocol, Invoker, Exporter
  • exchange 信息交换层:封装请求响应模式,同步转异步,以 Request, Response 为中心,扩展接口为 Exchanger, ExchangeChannel, ExchangeClient, ExchangeServer
  • transport 网络传输层:抽象 mina 和 netty 为统一接口,以 Message 为中心,扩展接口为 Channel, Transporter, Client, Server, Codec
  • serialize 数据序列化层:可复用的一些工具,扩展接口为 Serialization, ObjectInput, ObjectOutput, ThreadPool

乍一看确实有些懵,接着往下瞅

若依微服务设置文件大小_java_14

dubbo包含如下:

  • Provider:暴露服务的服务提供方
  • Consumer:调用远程服务的服务消费方
  • Registry:服务注册与发现的注册中心
  • Monitor:统计服务的调用次数和调用时间的监控中心
  • Container:服务运行容器

高度概括一下,dubbo更像是生产者+消费者模型,然后加上了注册中心和监控中心进行微服务的治理。

看官网,以dubbo为核心的微服务治理体系如图所示:

若依微服务设置文件大小_分布式_15

5.2 zookpeer

关于zookpeer,且看百度官方解释:

ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。 ZooKeeper是以Fast Paxos算法为基础的,Paxos 算法存在活锁的问题,即当有多个proposer交错提交时,有可能互相排斥导致没有一个proposer能提交成功,而Fast Paxos作了一些优化,通过选举产生一个leader (领导者),只有leader才能提交proposer,具体算法可见Fast Paxos。因此,要想弄懂ZooKeeper首先得对Fast Paxos有所了解。 ZooKeeper的基本运转流程: 1、选举Leader。 2、同步数据。 3、选举Leader过程中算法有很多,但要达到的选举标准是一致的。 4、Leader要具有最高的执行ID,类似root权限。 5、集群中大多数的机器得到响应并接受选出的Leader。

涉及到的知识点:

5.2.1 Paxos算法

关于Paxos算法,我至今仍觉得这是个难以理解的算法,具体内容请看这位前辈写的

5.2.2 ZAB协议:

ZAB协议包括两种基本的模式:

  1. 崩溃恢复
  2. 消息广播

当整个 Zookeeper 集群刚刚启动或者Leader服务器宕机、重启或者网络故障导致不存在过半的服务器与 Leader 服务器保持正常通信时,所有服务器进入崩溃恢复模式,首先选举产生新的 Leader 服务器,然后集群中 Follower 服务器开始与新的 Leader 服务器进行数据同步。当集群中超过半数机器与该 Leader 服务器完成数据同步之后,退出恢复模式进入消息广播模式,Leader 服务器开始接收客户端的事务请求生成事物提案来进行事务请求处理。

5.2.3 选举算法和流程:FastLeaderElection(默认提供的选举算法)

启动时的选举算法:

  1. 每个Server发出一个投票。由于是初始情况,Server1和Server2都会将自己作为Leader服务器来进行投票,每次投票会包含所推举的服务器的myid和ZXID,使用(myid, ZXID)来表示,此时Server1的投票为(1, 0),Server2的投票为(2, 0),然后各自将这个投票发给集群中其他机器
  2. 接受来自各个服务器的投票。集群的每个服务器收到投票后,首先判断该投票的有效性,如检查是否是本轮投票、是否来自LOOKING状态的服务器。
  3. 处理投票。针对每一个投票,服务器都需要将别人的投票和自己的投票进行PK,PK规则如下:
  • 优先检查ZXID**。ZXID比较大的服务器优先作为Leader。(**这个很重要:是数据最新原则,保证数据的完整性
  • 如果ZXID相同,那么就比较myid**。myid较大的服务器作为Leader服务器。(集群的节点标识) 对于Server1而言,它的投票是(1, 0),接收Server2的投票为(2, 0),首先会比较两者的ZXID,均为0。再比较myid,此时Server2的myid最大,于是更新自己的投票为(2, 0),然后重新投票,对于Server2而言,其无须更新自己的投票,只是再次向集群中所有机器发出上一次投票信息即可。
  1. 统计投票 每次投票后,服务器都会统计投票信息,判断是否已经有过半机器接受到相同的投票信息,对于Server1、Server2而言,都统计出集群中已经有两台机器接受了(2, 0)的投票信息,此时便认为已经选出了Leader。
  2. 改变服务器状态 一旦确定了Leader,每个服务器就会更新自己的状态,如果是Follower,那么就变更为FOLLOWING,如果是Leader,就变更为LEADING。

运行期间的选举算法:

大体上与启动时没有什么不同,就是投票期间会先比较ZXID也就是事务ID,以确保数据完整性

5.2.4 zookpeer实现分布式锁

分布式锁大概有三种实现方式: 首先明白为何需要分布式锁:假设现在又三个及其,每个机器上运行一个JVM,那么假设这三个机器运行一整个微服务,各个微服务之间需要进行加锁同步的话,问题就会出现:每个JVM肯定是互不通信的,就像Tomcat里的各个webapp一样,根据我们的

  1. MySQL数据库锁 悲观锁:select * from tablename FOR update 乐观锁:select * from tablename lock in share mode,或者基于CAS思想加上版本号机制
  2. Redis实现分布式锁 这个也是目前用的比较广泛的一种分布式锁,具体方法为: (1)获取锁的时候,使用setnx命令加锁,并使用expire命令为锁添加一个超时时间,超过该时间则自动释放锁,锁的value值为一个随机生成的UUID,通过此在释放锁的时候进行判断。 (2)获取锁的时候还设置一个获取的超时时间,若超过这个时间则放弃获取锁。 (3)释放锁的时候,通过UUID判断是不是该锁,若是该锁,则执行delete进行锁释放。 上述条件并不能保证该分布式锁的原子性,在Redis2.8之后,Redis提供了set这样一个指令,内嵌了失效时间、加锁(setnx)等。
  3. Zookeeper实现分布式锁 (1)创建一个目录mylock; (2)线程A想获取锁就在mylock目录下创建临时顺序节点; (3)通过zookpeer的watch机制来监听mylock目录下所有的子节点,然后获取比自己小的兄弟节点,如果不存在,则说明当前线程顺序号最小,获得锁; (4)线程B获取所有节点,判断自己不是最小节点,设置监听比自己次小的节点; (5)线程A处理完,删除自己的节点,线程B监听到变更事件,判断自己是不是最小的节点,如果是则获得锁 缺点:需要频繁的创建和删除节点,性能上不如Redis方式创建分布式锁的方式。
6. 二者对比

名称\种类

Dubbo

SpringCloud

注册中心

Zookeeper/nacos

Eureka

调用方式

RPC

REST API

服务监控

Dubbo-monitor

Spring Boot Admin

断路器

Sentinel等

Hystrix

网关

kong

Zuul/getway

分布式配置

nacos

Spring Cloud Config

服务跟踪


Spring Cloud Sleuth

消息总线


Spring Cloud Bus

数据流


Spring Cloud Stream

批量任务


Spring Cloud Task

可以看出SpringCloud体系较为完全,而受dubbo停更五年的原因,大部分开发者还是流入了SpringCloud体系中。个人理解呢,dubbo更像是一个服务治理工具,而CLoud则是一整个体系,内容完整且大道至简

7. 结尾

分布式也好,微服务也罢,总之都是针对当前流量快速增长所采取的应对措施,以我在学校里做的某个项目为例,整体逻辑很简单,而难点在于如何抵抗住高并发,个人认为如何处理高并发是每个Java程序猿都需要思考的问题 恕我才疏学浅,应该仍有很多地方没有考虑到,希望看到这篇博客的前辈及时指出相关错误,我及时更正!