博客地址

概念

微服务架构是一种架构概念,旨在通过将功能分解到各个离散的服务中以实现对解决方案的解耦,可以将其看作是在架构层面而非获取服务的类上应用很多SOLID原则,微服务架构是个很有趣的概念,它的主要作用是将功能分解到各个服务当中,从而降低系统的耦合性,并提供更加灵活的服务支持
把一个大型的单个应用程序和服务拆分为数个甚至数十个的支持微服务,它可扩展单个组件而不是整个的应用程序堆栈,从而满足服务等级协议

定义

围绕业务领域组件来创建应用,这些应用可独立的进行开发,管理和迭代,在分散的组件中使用云架构和平台式部署,管理和服务功能,使产品交付变得更加简单

本质

用一些功能比较明确,业务比较精炼的服务去解决更大,更实际的问题

何为微服务

指的是开发一个单个小型的但有业务能力的服务,每个服务都有自己的处理和轻量通讯机制,可以部署子啊单个或者多个服务器上,微服务也指一种 松耦合,有一定的有界上下文的面对服务架构。也就是说,如果每个服务又要同时修改,他们他们就不是微服务,因为他们紧耦合在一起;如果你需要掌握一个服务太多的上下文场景使用条件,那么它就是一个有上下文边界的服务,这个定义来自DDD领域驱动设计
简单来说,微服务就是将原本庞大的单体架构的各个业务模块变为独立的可运行的服务,一些微服务会暴露一个供其他微服务或者应用客户端消费的API,模块与模块之间通过API互相调用,各个业务模块都是一个服务,整个项目有众多服务组成,这就是微服务

优点

第一,解决了复杂问题。它把可能会变得庞大的单体应用程序分解成一套服务,虽然项目的功能数量不变,但是应用程序被分解成可管理的块或者服务,每个服务都有一个明确定义边界的方式,如远程调用(RPC)驱动或消息驱动API。微服务架构模式强制一定程度的模块化,实际上,使用单体代码来实现是及其困难的,因此,使用微服务架构模式,个体服务能被更快的开发,并更容易理解和维护
第二,这种架构使得每个服务都可以由一个团队独立专注开发。开发者可以自由选择任何符合服务API契约的技术,当然,更多的组织是希望通过技术选型限制来避免完全混乱的状态,然而,这种自由意味着开发人员不在有可能在这种自由的新项目开始时使用过时的技术,当编写一个新服务时,他们可以选择当前的技术,此外,由于服务较小,使用当前技术重写服务将变得更加可行
第三,微服务架构模式可以实现每个微服务独立部署,开发人员根本不需要去协调部署本地变更到服务,这些变更一经测试即可立即部署,比如UI团队可以执行AIB测试,并快速迭代UI变更,微服务架构模式使得持续部署成为可能
微服务架构模式使得每个服务能够独立扩展,可以仅部署满足每个服务的容量和可用性约束的示例数目,此外,可以使用与服务资源要求最匹配的硬件

缺点

第一,由于微服务是一个分布式系统,使得整体变得复杂,开发者需要选择和实现基于消息或者RPC的进程间通信机制,此外,由于目标请求可能很慢或不可用,他们必须要编写代码来处理局部故障,虽然这些并不是很复杂高手,但模块间通过语言级方法/过程调用互相调用,这比单体应用要复杂得多
第二,分区数据库架构,更新多个业务实体的业务事务时相当普遍的,这些事务在单体应用中的实现显得微不足道,,因为单体只存在一个单独的数据库,在基于微服务的应用程序中,需要更新不同服务所用的数据库,通常不会选择分布式事务,不仅仅是因为CAP定理,他们根本不支持如今高度可扩展的NoSQL数据库和消息代理,最后不得不使用基于最终一致性的方法,对开发人员开说更具有跳转性
第三,测试微服务应用程序也很复杂,单体的话,编写一个测试类,执行启动就可以,微服务的话需要开启该服务以及所依赖的所有服务,配置这些服务的配置,
第四,部署基于微服务的应用程序也是相当复杂的,一个单体应用很容易的部署到基于传统负载均衡器的一组相同服务器上,每个应用程序实例都配置有基础设置服务的位置(主机和端口),相比之下,微服务应用程序通常由大量的服务组成

总结

单体架构和微服务架构个有个的优缺点,重要的是根据项目做架构选型,比如,单体架构只适合用于简单,轻量级的应用程序,如果使用它来构建复杂应用,最终会非常痛苦,微服务架构麻烦,复杂,持续发展应用的一个更好的选择,
image.pngimage.png

需要思考的问题

  1. 客户端如何访问这些服务
    1. 原来的单体架构方式开发,所有的服务都是本地的,前端用户可以直接访问,现在按照功能拆分成独立的服务,一般都是跑在独立的虚拟机上的Java进程,客户端怎么访问
    2. 后台有N个服务,前台就需要记住管理N个服务,一个服务下线,更新,升级,前台就要重新部署,明显不符合我i们拆分的理念,特别是移动端,业务变化节奏比较快
    3. 另外,N个小服务的调用也是不小的网络开销,还有一般微服务在系统内部,通常是无状态的,用户登陆信息和权限管理最好有一个统一的地方管理维护(AO)
    4. 所以,一般在后台N个服务和UI前端之前一般会有一个代理或者叫API 他的作用是,
      1. 提供一个统一入口,让微服务对前台透明
      2. 聚合后台的服务,节省流量,提升性能
      3. 提供安全,过滤,流控等API管理功能
    5. 其实这个API Gateway 可以有很多广义的实现方法,可以是一个软硬一体的盒子,也可以是一个简单的MVC框架,甚至一个Node.js 的服务端,他们最重要的作用是为前台(通常是移动应用)提供后台服务的聚合,提供一个统一的服务出口,解除他们之间的耦合,不过API Gatrqay 也有可能成为单点故障点或者性能的瓶颈
  2. 微服务架构中,所有的微服务都是独立的Java进程,跑在独立的虚拟机中,怎么实现服务与服务之间的通信
    1. 服务间的通信是 IPC 已经有很多成熟的方案
    2. 基本最通用的有两种方式
    3. 同步调用:PEST(JAX-RS,Spring boot),RPC(Thrift,Dubbo)
    4. 异步调用:RabbitMQ,Kafka,之类的
  3. 如此多的服务,怎么实现
    1. 在微服务架构中,一般每一个服务都是有多个拷贝(也就是集群,实现高可用),来做负载均衡,一个服务随时可能下线,也可能应对临时访问压力增加新的服务节点,服务之间如何相互感知,服务如何管理
    2. 一般有两种做法,也各有优缺点,基本都是通过Zookeeper等类似的技术做服务注册信息的分布式管理,当服务上线时,服务提供者将自己的服务信息注册到ZK(或者类似框架),并通过心跳维持长连接,实现更新链路信息,服务调用者通过ZK寻址,根据可定制算法,找到一个服务器,还可以将服务信息缓存在本地,以提高性能,当服务下线时,ZK会发i西安通知服务客户端
  4. 服务挂了,如何解决

    1. 单体架构方式开发一个很大的风险是,所有鸡蛋放在一个篮子里,一荣俱损,而分布式最大的特性就是网络的不可靠,通过微服务拆分能降低这个风险,不过如果没有特别的保障,结局肯定是噩梦,所以当我们的系统是由一系列的服务调用链组成的时候,我们必须保证任一环节出问题都不至于影响整体链路,手段有

      1. 重试机制
      2. 限流
      3. 熔断机制
      4. 负载均衡
      5. 降级(本地缓存)

        微服务框架

        Spring Cloud ,一个微服务的框架,里面包含了需要用到的服务跟组件
        dubbo,阿里的框架

        微服务的核心

        微服务相关概念
        image.png

        服务注册与发现

        相当于婚姻介绍所,男生找对应的女生,女生把自己信息放在婚姻介绍所(注册),男生也注册,然后获取注册的女生列表,
        婚姻介绍所就是一个中心,那里的信息就是权威,再Spring Cloud 体系中,最常用的注册中心主要是 Eureka,Zookeeper,Consul,Nacos
        任何服务启动之后,都会把自己注册到注册中心的注册表中,当服务死亡的时候,也会通知注册中心
        image.png

        服务调用

        负载均衡之 Ribbon
        Restemplate 是Spring提供的一个访问Http服务的客户端类,怎么说呢,就是微服务之间的调用是使用的Restemplate, 比如这个时候我们消费者B 需要调用提供者A 所提供的服务,我们需要这么写
        Restemplate 官方网址 ```java public class OrderController { //private static final String PAYMENT_URL=”http://localhost:8001“; //单机版 private static final String PAYMENT_URL=”http://CLOUD-PROVIDER-SERVICE“; //集群版,(负载均衡)

      @Resource private RestTemplate restTemplate;

      @GetMapping(“/consumer/payment/create”) public CommonResult create(Payment payment){ return restTemplate.postForObject(PAYMENT_URL+”/payment/create”,payment,CommonResult.class); } }

  1. **为什么需要 Ribbon**<br />Ribbon Netfilx公司的一个开源的负载均衡 项目,是一个客户端/进程内负载均衡器,运行再消费端,<br />比如我们设计一个秒杀系统,但是为了整个系统的高可用,我们需要将整个系统做一个集群,而这个时候我们的消费者就可以拥有多台秒杀系统的调用途径了<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/22438777/1649297831912-af660dc3-024a-4afc-a949-dd0648d0ae87.png#clientId=uc75b87b0-3f4d-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=260&id=u5c58bb26&margin=%5Bobject%20Object%5D&name=image.png&originHeight=511&originWidth=516&originalType=binary&ratio=1&rotation=0&showTitle=false&size=99149&status=done&style=shadow&taskId=u3b3b07fe-3ee0-4833-9fdc-781f4e56eb7&title=&width=263)<br />如果整个时候没有进行一些均衡操作,如果我们对秒杀系统1进行大量的调用,而另外两个基本不请求,就会导致系统1崩溃,而另外两个就会变成了傀儡,所以集群的意义就没有了,没有高可用的体现<br />所以Ribbon出现了,是运行再消费端的,指的是Ribbon是运行在消费者端的负载均衡器<br />其工作原理就是,消费者端获取到所有的服务列表之后,在其内部使用负载均衡算法,进行对多个系统的调用<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/22438777/1649298080383-0f320f6c-1eb1-4db1-91df-8c3ab8b8f3cd.png#clientId=uc75b87b0-3f4d-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=262&id=ub2311944&margin=%5Bobject%20Object%5D&name=image.png&originHeight=480&originWidth=665&originalType=binary&ratio=1&rotation=0&showTitle=false&size=106606&status=done&style=shadow&taskId=u350eaf6a-fa10-4dac-bb01-89384f4c4b2&title=&width=363)<br />**Nginx和Ribbon 的对比**<br />提到负载均衡就不得不提到Nginx,和Ribbon不同的是,他是一种集中式的负载均衡器<br />什么是集中式,简单理解就是将所有请求都集中起来,然后再进行负载均衡<br />Nginx是接收了所有的请求进行负载均衡的,而对于Ribbon来说,他是在消费端进行的负载均衡<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/22438777/1649298965171-110e1dbc-7cad-4fdc-941b-683944afe5d0.png#clientId=uc75b87b0-3f4d-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=217&id=u548c8c61&margin=%5Bobject%20Object%5D&name=image.png&originHeight=329&originWidth=552&originalType=binary&ratio=1&rotation=0&showTitle=false&size=57356&status=done&style=shadow&taskId=u32c53a43-55a5-4e5d-80ef-973f291b435&title=&width=364)![image.png](https://cdn.nlark.com/yuque/0/2022/png/22438777/1649299066204-9d3a68f7-b811-4df8-bc77-f5c1f9508cee.png#clientId=uc75b87b0-3f4d-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=218&id=ua0b0f0e1&margin=%5Bobject%20Object%5D&name=image.png&originHeight=426&originWidth=634&originalType=binary&ratio=1&rotation=0&showTitle=false&size=136911&status=done&style=shadow&taskId=ud4b386bc-af09-4e8f-8dcc-4c04a947aa0&title=&width=324)<br />**Ribbon的几种负载均衡算法**<br />负载均衡 ,不管是Nginx还是Ribbon 都需要其算法的支持,Nginx 使用的是,轮询,轮询权值,ip_hash算法等五种,而在Ribbon中的负载均衡调度算法,其默认是使用的RoundRobinRule轮询策略<br />RoundRobinRule:轮询策略,Ribbon默认采用的策略,若经过一轮轮询没有找到可用的provider,其最多轮询10轮,如若最终没有找到,就返回null<br />RandomRule:随机策略,从所有可用的provider中随机选择一个<br />RetryRule:重试策略,先按照RoundRobinRule 策略获取provider,如获取失败,在指定的时限内重试,默认的时限是500毫秒<br />**什么是 Open Feign**<br />openFeign也是运行在消费端的,使用Ribbon进行负载均衡,所以openFeign直接内置了Ribbon,Feign集成了Ribbon,利用Ribbon维护了Payment的服务列表信息,并且通过轮询实现了客户端的负载均衡,而与Ribbon不同的是,通过feign只需要定义服务绑定接口且以声明的方法,简单的实现了服务调用
  2. ```java
  3. @Component
  4. @FeignClient(value = "CLOUD-PROVIDER-SERVICE") //指定调用哪个微服务
  5. public interface PaymentFeignService {
  6. @GetMapping(value = "/payment/get/{id}") //哪个地址
  7. CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);
  8. }

在controller 就可以像原来调用Server层代码一样调用它了

  1. @RestController
  2. @Slf4j
  3. public class OrderFeignController {
  4. @Resource
  5. private PaymentFeignService paymentFeignService;
  6. @GetMapping(value = "/consumer/payment/get/{id}")
  7. public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){
  8. return paymentFeignService.getPaymentById(id);
  9. }
  10. }

Ribbon 和Open Feign 他们都有一个比较通俗的名词,叫做RPC(远程调用)

服务降级,熔断和限流

服务雪崩
多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其他的微服务,这就是所谓的扇出,如果扇出的链路上某个微服务的调用相应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统的崩溃,所谓的“雪崩效应”
对于高流量的应用来说,单一的后端依赖可能会导致所有的服务器上的资源都子啊几秒内饱和,比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障,这次都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统
所以,通常当你发现一个模块下的某个实例失败后,这时候这个模块 依然还会接收流量,然后这个有问题的模块还调用了其他的模块,这样就会发生级联故障,或者叫雪崩
要避免这样的级联故障,就需要有一种链路中断的方案:服务降级,服务熔断
什么是Hystrix之服务熔断,降级和限流
在分布式环境中,不可避免地会有许多服务依赖项中的某些失败,Hystrix是一个库,可通过添加等待时间容限和容错逻辑来帮助您控制这些分布式服务之间的交互,Hystrix通过隔离服务之间的访问点,停止服务之间的级联故障并提供后备选项来实现此目的,所以这些都可以提高系统的整体弹性
熔断
熔断就是服务雪崩的一种有效解决方案,当指定时间窗口内的请求失败率达到设定阈值时,系统将通过断路器直接将此请求链路断开,也就是我们上面服务B调用服务C在指定时间窗内,调用的失败率达到一定的值,那么熔断器则会自动将服务B与C之间的请求都断了,以免导致服务雪崩现象
其实这里所讲的熔断,就是值得Hystrix中的熔断器模式,你可以使用简单的 @HystrixCommand 注解来标注某个方法,这样 Hystrix 就会使用断路器来包装这个方法,每当调用时间超过指定时间时(默认时1000ms),断路器就会中断这个方法的调用
当然你可以对这个注解的很多属性进行设置,比如设置超过时间,像这样,当请求超过1.5秒的时候,服务就会终端 固定的方法 paymentInfo_TimeOut 的调用

  1. @RestController
  2. public class OrderHystrixController {
  3. @Resource
  4. private PaymentHystrixService paymentHystrixService;
  5. @GetMapping("/consumer/payment/hystrix/timeout/{id}")
  6. @HystrixCommand(commandProperties = {
  7. @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1500")
  8. })
  9. public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
  10. return paymentHystrixService.paymentInfo_TimeOut(id);
  11. }
  12. }

降级
降级是为了更好的用户体验,当一个方法调用异常时,通过执行另一种代码逻辑来给用户友好的回复,这就是对应着 Hystrix 的后备处理模式,你可以通过设置@Hystrix Command 注解的 fallbackMethod属性来给一个方法设置备用的代码逻辑,比如这个时候有一个微博里有一个热点新闻出现了,我们会推荐给用户查看详情,然后用户会通过ID去查询新闻的详情,但是因为这个新闻太火爆,大量用户同时访问可能会导致系统崩溃,那么我们就进行服务降级,一些请求会做一些降级处理,比如提示人数太多,请稍后再试 等等
限流
秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒N个,有序进行
Sentinel:分布式系统的流量防卫兵
随着微服务的流行,服务和服务之间的稳定性变得越来越重要,Sentinel是面对分布式服务架构的轻量级流量控制组件,主要以流量为切入点,从限流,流量整形,熔断降级,系统负载保护等多个维度来帮助保障微服务的稳定性

服务网关

网关—Zuul
是从设备到web站点到Netflix流应用后端的所有请求的前门,作为边界服务应用,Zuul是为了实现动态路由,监控,弹性和安全性而构建的,它还具有根据情况将请求路由到多个 Amazon Auto Scaling Groups (亚马逊自动缩放组,亚马逊的一种云计算方式)的能力
网关应该很熟吧,简单来讲网关是系统唯一对外的入口,介于客户端与服务端之间,用于对请求进行鉴权,限流,路由,监控等功能
Gateway 网关
Gateway支持 Reactor和webFlux,传统的web框架,比如说:struts2,springmvc等都是基于servletAPI与servlet容器基础之上运行的,但是Servlet3.1之后有了异步非阻塞的支持,而webFlux是一个典型的非阻塞异步的框架,它的核心是基于Reactor的相关API实现的,相对于传统的Web框架来说,它可以运行在诸多Netty,Undertow及支持Serlet3.1的容器上。非阻塞式+函数式编程
Spring webFlux 是Spring5.0 引入的新的响应式框架,区别于Spring MVC ,它不需要依赖Servlet API,它是完全异步非阻塞,并且基于Reactor 来实现响应式流规范

服务配置

Spring Cloud Config
能进行配置管理的框架不止 Spring Cloud Config 一种,大家可以根据需求自己选择(disconf,阿波罗等)。并且对于Config 来说,有些地方实现的不是那么尽人意
为什么要进行配置管理
当我们的微服务系统开始慢慢地庞大起来,那么多 consumer,Provider,Server,Zuul,系统都会持有自己地配置,这个时候我们在项目运行地时候,可能需要更改某些应用地配置,如果我们不进行配置地统一管理,我们只能去每个应用下一个一个寻找配置文件,然后修改配置文件在重启应用
首先对于分布式系统而言,我们就不应该去每个应用下去分配修改配置文件,再者对于重启应用来说,服务无法访问,所以直接抛弃了可能性,这个式我们不愿意见到的
简单来说,Spring Cloud Config 就是能将各个应用/系统/某块 的配置文件存放到统一的地方然后进行管理(Git或者SVN)
想一下,我们的应用是不是只有启动的时候才会进行配置文件的加载 ,那么我们的 Spring Cloud Config 就暴露出一个接口给启动应用来获取它所想要的配置文件,应用获取到配置文件,然后再进行它的初始化工作,
image.png
怎么动态的修改配置文件,一般我们会使用Bus消息总线+Spring cloud config 进行配置的动态刷新
Spring Cloud Bus
用于将服务和服务实例于分布式消息系统链接在一起的事件总线,在集群中传播状态更改很有用(例如配置更改时间)
简单理解为 Spring Cloud Bus 的作用就是管理和广播分布式系统中的消息,也就是消息引擎系统中的广播模式,当然作为消息总线的 Spring Cloud Bus 可以做很多事,不仅仅事客户端的配置刷新功能
而拥有 Spring Cloud Bus 之后,我们只需要创建一个简单的请求,并且加上 @ResfreshScope,注解就能进行配置的动态修改了
image.png
Nacos 的动态配置服务
动态配置服务可以让你以中心化,外部化和动态化的方式管理所有环境的应用配置和服务配置
动态配置消除了配置变更时重新部署应用和服务的需要,让配置管理变得更加高效和敏捷
配置中心化管理让实现无状态服务变得更简单,让服务按需弹窗扩展变得更容易
Nacos 提供了一个简洁易用的UI帮助您管理所有的服务和应用的配置,Nacos还提供包括配置版本跟踪,金丝雀发布,一键回滚配置以及客户端配置更新状态跟踪在内的一系列开箱即用的配置管理特性,帮助您更安全的在生产环境中管理配置变更和减低配置变更带来的风险

总结

下面的图能看到,就说明已经多 Spring cloud 微服务有一定架构认识
image.png