深入浅出微服务

前言

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

1. 什么是微服务?

且看百度解释:

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

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官网有这样一段性能对比,可以看到相差不是很大

在这里插入图片描述

扫描二维码关注公众号,回复: 9627547 查看本文章
3.2 微服务如何发现

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

  1. Zookpeer
  2. Eureka

说这两者区别之前先说一个概念:CAP定理
在这里插入图片描述

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

第一 Consistency

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

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

第三 Partition tolerance 分区容错性
大多数分布式系统都分布在多个子网络。每个子网络就叫做一个区(partition)。分区容错的意思是,区间通信可能失败。比如,一台服务器放在中国,另一台服务器放在美国,这就是两个区,它们之间可能无法通信。
在这里插入图片描述
上图中,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 Client
    Eureka 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 熔断、降级

假设我的微服务存在以下调用关系
在这里插入图片描述
服务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组件
在这里插入图片描述

细化一些: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.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,它是一种集中式的反向代理的负载均衡器
何为集中式呢?简单理解就是将所有请求都集中起来,然后再进行负载均衡。如下图。
在这里插入图片描述
而反向代理的解释,百度显得更为专业一点:

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

我们的Ribbon工作机制是怎么样的呢?看图你就明白了:
在这里插入图片描述
在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.
原理图:
在这里插入图片描述
zuul可实现的功能有:

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

当我们的微服务系统开始慢慢地庞大起来,那么多Consumer、Provider、Eureka Server、Zuul系统都会持有自己的配置,这个时候我们在项目运行的时候可能需要更改某些应用的配置,如果我们不进行配置的统一管理,我们只能去每个应用下一个一个寻找配置文件然后修改配置文件再重启应用。
而Spring Cloud Config可以对配置文件统一地进行管理,又能在项目运行时动态修改配置文件。
在这里插入图片描述
但是查资料所了解到的,实际生产过程中一般采用Bus消息总线 +Spring Cloud Config进行配置的动态刷新

5. 微服务Dubbo体系

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

5.1 dubbo

先看官方给的配置,虽然有点乱… …
在这里插入图片描述
各层说明

  • 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

乍一看确实有些懵,接着往下瞅
在这里插入图片描述
dubbo包含如下:

  • Provider:暴露服务的服务提供方
  • Consumer:调用远程服务的服务消费方
  • Registry:服务注册与发现的注册中心
  • Monitor:统计服务的调用次数和调用时间的监控中心
  • Container:服务运行容器
    高度概括一下,dubbo更像是生产者+消费者模型,然后加上了注册中心和监控中心进行微服务的治理。
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协议包括两种基本的模式:崩溃恢复和消息广播。当整个 Zookeeper 集群刚刚启动或者Leader服务器宕机、重启或者网络故障导致不存在过半的服务器与 Leader 服务器保持正常通信时,所有服务器进入崩溃恢复模式,首先选举产生新的 Leader 服务器,然后集群中 Follower 服务器开始与新的 Leader 服务器进行数据同步。当集群中超过半数机器与该 Leader 服务器完成数据同步之后,退出恢复模式进入消息广播模式,Leader 服务器开始接收客户端的事务请求生成事物提案来进行事务请求处理。

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

目前有5台服务器,每台服务器均没有数据,它们的编号分别是1,2,3,4,5,按编号依次启动,它们的选择举过程如下:

  1. 服务器1启动,给自己投票,然后发投票信息,由于其它机器还没有启动所以它收不到反馈信息,服务器1的状态一直属于Looking。
  2. 服务器2启动,给自己投票,同时与之前启动的服务器1交换结果,由于服务器2的编号大所以服务器2胜出,但此时投票数没有大于半数,所以两个服务器的状态依然是LOOKING。
  3. 服务器3启动,给自己投票,同时与之前启动的服务器1,2交换信息,由于服务器3的编号最大所以服务器3胜出,此时投票数正好大于半数,所以服务器3成为leader,服务器1,2成为follower。
  4. 服务器4启动,给自己投票,同时与之前启动的服务器1,2,3交换信息,尽管服务器4的编号大,但之前服务器3已经胜出,所以服务器4只能成为follower。
  5. 服务器5启动,后面的逻辑同服务器4成为follower。

6. 二者对比

名称\种类 Dubbo SpringCloud
注册中心 Zookeeper Eureka
调用方式 RPC REST API
服务监控 Dubbo-monitor Spring Boot Admin
断路器 不完善 Hystrix
网关 Zuul
分布式配置 Spring Cloud Config
服务跟踪 Spring Cloud Sleuth
消息总线 Spring Cloud Bus
数据流 Spring Cloud Stream
批量任务 Spring Cloud Task

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

7. 结尾

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

8. 参考

dubbo官网
https://www.jianshu.com/p/b6eab9d96ff4
https://mp.weixin.qq.com/s?__biz=MzAwNDA2OTM1Ng==&mid=2453141625&idx=1&sn=0e0618b184f7530f3eee15a62466ebe9&chksm=8cf2dafabb8553ec68563abed73193dd5ce76de3da699cb992a4aede4727d532a006db3cbbea&mpshare=1&scene=23&srcid=&sharer_sharetime=1583403299355&sharer_shareid=e104aa827cf9bc63ba9cf4760dc9367e#rd
https://blog.csdn.net/qq_34801169/article/details/89678063
https://blog.csdn.net/suchahaerkang/article/details/84141770

发布了19 篇原创文章 · 获赞 99 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/AAAhxz/article/details/104684856
今日推荐