单体应用的特点
能够接纳的请求数量有限(服务器的内存,CPU配置有限)
表现层,控制层,持久层都在一个应用里面,调用方便,快速。单个请求响应结果快
开发简单,上手快,三五人团队建议使用
处理并发请求的能力和容量上增强,但是在单个请求的处理速度上下降。
针对分布式系统存在的问题,可以通过服务治理基础服务来解决,随着服务数量的不断增加,服务中的资源浪费和调度问题加重,需要增加一个调度中心来治理服务。调用中心可以基于访问压力来实时管理集群的容量,从而提高集群的利用率。“在服务治理架构中,需要一个企业服务总线(ESB)将基于不同协议的服务节点连接起来,它的工作是转换,解释消息和路由”。
将系统的业务功能划分为极小的独立微服务,每个微服务只关注于完成某个小的任务。系统中的单个微服务可以被独立部署和扩展,且各个微服务之间是高内聚、低耦合的。微服务之间采用轻量化通信机制暴露来实现通信。
组件解释
压力模型简单来说就是用户访问量,我们要识别出某些超高的并发量的业务,尽可能把这些业务拆分出来。
如商品详情页,不仅是个高频场景也是高并发场景(QPS极高)
如秒杀场景(偶尔发生)会突发大流量
后台运营团队的服务接口,比如商品图文编辑,添加新的优惠计算规则,上架新商品。它发生的频率比较低,而且也不会造成很高的并发量
业务模型拆分的维度很多,实际中应用综合各个不同维度做考量,主要有三个维度(主链路、领域模型、用户群体)
电商领域“主链路”是一个至关重要的业务链条,指的是用户完成下单场景所必须经过的场景。如商品搜索->商品详情页->购物车模块->订单结算->支付业务。
核心业务拆分的目的
异常容错:为主链路建立层次化的降级策略(多级降级),以及合理的熔断策略。
调配资源:主链路通常是高频场景,体现在集群分配的虚拟机数最多
服务隔离:把主链路和其它辅助的业务隔离开来,避免边缘服务的异常情况影响到主链路
用户群体相当于一个二级域,我们建议先根据主链路和领域模型做一级域的拆分,再结合具体的业务分析,看是否需要在用户领域方向上做更细粒度的拆分。
SpringCloud是一个基于SpringBoot实现的微服务架构开发工具。它为微服务架构中涉及的配置管理、服务治理、断路器、智能路由、控制总线、分布式会话和集群状态管理等等操作提供了一种简单的开发方式。
如果项目中已经使用到了Hadoop、kubernetes、Docker等的话,在SpringCloud实施过程中可以考虑其它组件,避免搭建两套注册中心,浪费资源
目前可选的分布式配置管理中心有,阿里的Nacos、携程的Apollo、SpringCloud Config
推荐使用SpringCloud GateWay在各方面都比Zuul好
2018年12月,Spring官方宣布Netflix的相关项目进入维护模式,不再开发新的功能,但是Hystrix整体上比较稳定。
在Hystrix停更之后,NetFlix官方推荐使用resilience4j,它是一个轻量级的、易用、可组装的高可用框架、支持熔断、高频控制、隔离、限流、重试等多种高可用机制。
Sentinel是阿里中间件团队开源的,面向分布式服务架构的轻量级高可用流量控制组件,主要以流量为切入点,从流浪控制、熔断降级、系统负载保护等多个维度来帮助用户保护服务的稳定性。
三大功能指的是微服务核心组件的功能维度,由浅入深层次递进;两大特性是构建在每个服务组件之上的高可用性和高扩展性。
在没有进行服务治理之前,服务之间的通信是通过服务间直接相互调用来实现的。
微服务系统中服务众多,这样会导致服务间的相互调用不方便,要记住提供服务的IP、名称、端口等。
Eureka是Netflix开发的注册发现组价,是一个基于REST的服务,不仅提供注册发现,还提供了负载均衡,故障转移的能力。
服务中心-服务提供者-服务消费者(三个角色)
Eureka Server:服务器端,提供服务的注册和发现功能,实现服务的治理
Service Provider:服务提供者,它将自身服务注册到Eureka Server中,方便服务消费者通过服务器端提供的服务清单(注册服务列表)来调用
Service Consumer:服务消费者。从Eureka获取已经注册的服务列表,实现服务消费
Eureka是AP架构,Zookeeper是CP架构。
CAP原则又称CAP定理,指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance),但是CAP 原则指示3个要素最多只能同时实现两点,不可能三者兼顾,由于网络硬件肯定会出现延迟丢包等问题,但是在分布式系统中,我们必须保证部分网络通信问题不会导致整个服务器集群瘫痪,另外即使分成了多个区,当网络故障消除的时候,我们依然可以保证数据一致性,所以我们必须保证分区容错性;
至于剩下的一致性和可用性,我们需要二选一,但是鱼和熊掌不可兼得,假设我们选择一致性,那我们就不能让用户访问无法进行数据同步的机器,毕竟该机器上的数据和其他正常机器上的不一致,但是这样我们就丢弃了可用性;假设我们选择可用性,那我们就可以让用户访问无法进行数据同步的服务器,虽然保证了可用性,但是我们无法保证数据一致性。
AP结构选择了高可用和分区容错性,此时,那个失去联系的节点依然可以向系统提供服务,不过它的数据就不能保证是同步的了(失去了C属性)。Eureka就是一个AP架构的例子,当Eureka客户端心跳消失的时候,那Eureka服务端就会启动自我保护机制,不会剔除该EurekaClient客户端的服务,依然可以提供需求;
CP结构选择的是一致性和分区容错性,如果选择一致性C(Consistency),为了保证数据库的一致性,我们必须等待失去联系的节点恢复过来,在这个过程中,那个节点是不允许对外提供服务的,这时候系统处于不可用状态(失去了A属性)。最好的例子就是zookeeper,如果客户端心跳消失的时候,zookeeper会很快剔除该服务,之后就无法提供需求;
参数
Environment: 环境,默认为test,该参数在实际使用过程中,可以不用更改
Data center**: 数据中心,使用的是默认的是** “MyOwn”
Current time**:当前的系统时间**
Uptime**:已经运行了多少时间**
Lease expiration enabled:是否启用租约过期 ,自我保护机制关闭时,该值默认是true,自我保护机制开启之后为false。
Renews threshold: 每分钟最少续约数,Eureka Server 期望每分钟收到客户端实例续约的总数。
Renews (last min): 最后一分钟的续约数量(不含当前,1分钟更新一次),Eureka Server最后 1 分钟收到客户端实例续约的总数。
参数:
这个下面的信息是这个Eureka Server相邻节点,互为一个集群。注册到这个服务上的实例信息
total-avail-memory : 总共可用的内存
environment : 环境名称,默认test
num-of-cpus : CPU的个数
current-memory-usage : 当前已经使用内存的百分比
server-uptime : 服务启动时间
registered-replicas : 相邻集群复制节点
unavailable-replicas :不可用的集群复制节点,如何确定不可用? 主要是server1 向
server2和server3发送接口查询自身的注册信息。
available-replicas :可用的相邻集群复制节点
ipAddr:eureka服务端IP
status:eureka服务端状态
服务自保和剔除,不能同时使用
服务剔除把服务节点剔除,即使续约的请求发过来也没有用
服务自保把当前所有节点保留。在实际应用里面,并不是所有无心跳的服务都不可用,可能是因为短暂的网络抖动等原因,导致服务节点和注册中心续约不上,但是服务节点之间的调用还是可用状态,这时候强行剔除服务节点可能会造成大范围的业务停滞。
服务自保模式是为了应对短暂的网络环境问题,在理想的服务节点的续约成功率为100%,如果突然发生网络问题,如一部分机房无法连接到注册中心,这时候续约的成功会降低。考虑到Eureka采用客户端的服务注册发现模式,客户端所有的节点的地址,如果服务节点只是因为网络原因无法续约,但其自身服务是可用的,那么客户端仍然可以成功发起调用请求,这样就避免了被服务剔除错杀。
关闭服务保护机制,默认为true
eureka.server.enable-self-preservation: false
RestTemplate是从Spring3.0开始支持的一个HTTP请求工具,它提供了常见的REST请求方案的模板,例如GET、POST、PUT、DELETE请求以及一些通用的请求方法exchange和execute。
@Configuration
public class CloudConfig{
@LoadBalanced
@Bean
public RestTemplate restTemplage(){
return new RestTemplate();
}
}
//测试Controller
@RestController
@RequestMapping("/test")
public class TestRestController{
//HTTP请求工具
@Autowired
private RestTemplate restTemplate;
@GetMapping("/index")
public String index(){
//1.远程调用方法的主机
//Stringhost="http://localhost:1000";
//将远程微服务调用地址从"IP地址+端口号改成"微服务名称""
String host = "http://xxx.xx.xx"; 123456789
// 2. 远程调用方法具体URL地址
String url = "/payment/index";
// 3. 发起远程调用
//getForObject:返回响应体中数据转化成的对象,可以理解为json
//getForEntity:返回的是ResponseEntity的对象包含了一些重要的信息
String forObject = restTemplate.getForObject(host + url,String.class);
return forObject;
}
}
以SpringCloud为基础的微服务架构,所有的微服务都需要注册到注册中心,如果这个注册中心阻塞或者崩了,那么整个系统都无法继续正常提供服务,所以这里就要对注册中心搭建高可用(HA)集群。
Eureka Server的设计从一开始就考虑到了高可用的问题,在Eureka的服务治理设计中,所有节点即是服务的提供方,也是服务消费方,服务注册中心也不例外。
负载均衡有,服务端负载均衡和客户端负载均衡
在服务集群内设置一个中心负载均衡器,比如Nginx。发起服务间调用的时候,服务请求并不直接发向目标服务器而是发给全局负载均衡器,再根据配置的负载均衡策略将请求转发到目标服务。
优点:
服务端负载均衡应用范围非常广泛,它不依赖服务发现技术,客户端并不需要拉取完整的服务列表,同时发起服务调用的客户端也不用操心使用什么负载均衡策略。
缺点:
网络消耗,复杂度和故障率提升
Spring Cloud LoadBalaner 采用了客户端负载均衡技术,每个发起服务调用的客户端都存有完整的目标服务地址列表,根据配置的负载均衡策略,由客户端决定向哪台服务器发起调用。
优点:网络开销小、配置简单
缺点:需要满足一个前置条件,发起服务调用的客户端需要获取所有目标服务的地址,这样才能使用负载均衡规则选取调用的服务。客户端负载均衡技术需要依赖服务发现来获取服务列表
Ribbon有助于Http和TCP的客户端,可以根据负载均衡算法(轮询、随机或者自动义)自动的帮助消费者的请求,默认就是轮询
Ribbon已经停更,替代方案(Spring Cloud LoadBalancer)
Spring Cloud OpenFeign 用于Springboot应用程序的声明式REST客户端
Feign是一个声明式WebService客户端,使用Feign能让编写WebService 客户端更加简单(使用方法是定义一个服务接口然后在上面加注解)。支持可插拔式的编码器和解码器。Spring Cloud 对Feign进行了封装,支持SpringMVC标准注解和HttpMessageConverters
OpenFeign提供了日志增强功能,默认是显示任何日志的可以配置
日志级别
NONE:默认不显示任何级别
BASIC:仅记录请求方法,URL、响应状态码
HEADER:除了BASIC中定义的信息之外,还有请求呵呵响应的头信息
FULL:除了HEADER中定义的信息之外,还有请求和响应的正文和元数据
@Configuration
public class OpenFeignConfig{
/**
* 日志级别定义
*/
//Logger包位于Feign
@Bean
Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
}
/**
配置文件配置接口日志级别
logging:
level:
com.service: debug #openfeign接口所在包名
*/
服务消费者在调用服务提供者的时候发生了阻塞、等待的情形,这个时候,服务消费者会一 直等待下去。 在某个峰值时刻,大呈的请求都在同时请求服务消费者,会造成线程的大呈堆积,势必会造成雪崩。 利用超时机制来解决这个问题,设置一个超时时间,在这个时间段内,无法完成服务访问, 则自动断开连接。
服务和服务之间的依赖性,故障会传播,造成连锁反应,会对整个微服务系统造成灾难性的严重后果
原因:
解决雪崩的三个方案
熔断就跟保险丝一样,当一个服务请求并发特别大,服务器已经招架不住了,调用错误率飙升,当错误率达到一定阈值后,就将这个服务熔断了。熔断之后,后续的请求就不会再请求服务器了,以减缓服务器的压力。
当失败率(如因网络故障/超时造成的失败率高)达到阀值自动触发降级,熔断器触发的快速失败会进行快速恢复。
出现原因:程序运行异常、超时、服务器熔断触发、线程池信号量打满
当下游的服务因为某种原因响应过慢,下游服务主动停掉一些不太重要的业务,释放服务器资源,增加响应速度。当下游的服务因为某种原因不可用,上游主动调用本地的一些降级逻辑,避免卡顿,迅速返回。
服务器繁忙,请稍后重试,不让客户端等待并且返回一个友好的提示。
避免服务之间的相互影响
将用户请求线程和服务执行线程分开,同时约定每个服务最多可用线程数
不同的http服务使用不同的线程池,当自己的资源用完,直接返回失败而不占用别人的资源
优点:可提高并发性
缺点:增加CPU调度开销
使用场景:第三方应用或接口;并发量大
原子计数器方式记录当前运行的线程数,超过则拒绝,不超过则+1,返回则-1使用场景:内部应用或中间件;并发需求不大
区别:信号量可动态调整,但线程池不可以调整
服务熔断和服务隔离都是出错后的容错机制而服务限流是预防的机制
限流模式主要是提前对各个类型的请求设置最高的QPS阈值,如果高于设置的阈值直接返回不再调用后续资源
限流的目的是通过对并发访问/请求进行限速,或者对一个时间窗口内的请求进行限速来保护系统,一旦达到限速速率则可以拒绝服务、排队或者等待、降级等处理
网关限流:防止大量请求进入系统,MQ实现消峰;用户交流限制:提交按钮限制点击频率限制等等。
Resilience4j是一个轻量级的容错组件,主要是JAVA8和函数式编程设计(Lambda)轻量级体现在只用了VAVR(前身javaslang),没有依赖任何外部组件。提供了一系列增强微服务的可用性功能:
断路器(circuitBreaker)通常存在三种状态(CLOSE、OPEN、HALF_OPEN)并且通过一个时间或者数量窗口来记录当前的请求成功率或者慢速率,从作出正确的容错响应。
三种重要的状态:
共有六种状态
网关统一向外部系统(访问者、服务)提供REST API。在Spring Cloud中,使用Zuul、Spring Cloud Gateway等作为API Gateway 来实现动态路由、监控、回退、安全等功能。
Spring Cloud Gateway是Spring Cloud生态系统中的网关,基于Spring5.0、springboot2.0和project reactor等技术开发。用“Netty+WebFlux”实现。皆在为微服务架构提供一种简单有效的,统一的API路由管理方式
特点
Webflux模式替换了旧的Servlet线程模型。用少量的线程处理request和response io操作,这些线程称为Loop线程,而业务交给响应式编程框架处理,响应式编程是非常灵活的,用户可以将业务中阻塞的操作提交到响应式框架的work线程中执行,而不阻塞的操作依然可以在Loop线程中进行处理,大大提高了Loop线程的利用率。
WebFlux可以兼容多个底层的通信框架,但是大多数情况下使用的是Netty,Netty是目前认可度最高的通信框架。WebFlux的loop线程,是著名的Reactor模式IO处理模型的Reactor线程。
Netty
首先任何请求进来,网关都会把它们拦住。根据请求的URL把它们分配到不同的路由上,路由上面会有断言,来判断请求能不能进来。进来之后会有一系列的过滤器对请求被转发前或转发后进行改动。 具体怎么个改动法,那就根据业务不同而自定义了。一般就是监控,限流,日志输出等等
Gateway基本构建模块,它由一个ID,一个目标URL,一组断言和一组过滤器定义,如果断言为真,则路由匹配。
输入类型是ServerWebExchange,可以用来匹配来自HTTP请求的任何内容,如HEADER或者参数
可以在请求被路由前或者之后对请求进行修改