原理
CAP原理
分布式系统中,多个节点之前的网络本来是连通的,但是因为某些故障(比如部分节点网络出了问题)某些节点之间不连通了,整个网络被分成了几块区域,这就叫网络分区。
- 一致性(Consistence) : 所有节点访问同一份最新的数据副本
- 可用性(Availability): 非故障的节点在合理的时间内返回合理的响应(不是错误或者超时的响应)
- 分区容错性(Partition tolerance) : 分布式系统出现网络分区的时候,仍然能够对外提供服务
BASE原理
BASE强调牺牲高一致性,从而获取可用性,数据允许在一段时间内不一致,只要保证最终一致性就可以了。
-
弱一致性:系统不能保证后续访问返回更新的值。需要在一些条件满足之后,更新的值才能返回。从更新操作开始,到系统保证任何观察者总是看到更新的值的这期间被称为不一致窗口。
-
最终一致性:这是弱一致性的特殊形式。存储系统保证如果没有对某个对象的新更新操作,最终所有的访问将返回这个对象的最后更新的值
概念
分布式和集群
- **集群:**一台服务器无法负荷高并发的数据访问量,就设置多台服务器一起分担压力,是在物理层面解决问题。
- **分布式:**将一个复杂的问题拆分成若干简单的小问题,将一个大型的项目架构拆分成若干个微服务来协同完成,在软件设计层面解决问题。
微服务优点
- 各个服务相互独立,只需要对外提供的接口正常即可,而不限制框架和语言的选择
Spring Cloud
就是微服务系统架构的一站式解决方案,在平时我们构建微服务的过程中需要做如 服务发现注册 、配置中心 、消息总线 、负载均衡 、断路器 、数据监控 等操作,而 Spring Cloud 为我们提供了一套简易的编程模型,使我们能在 Spring Boot 的基础上轻松地实现微服务项目的构建。
服务注册中心
实现服务注册与服务发现
Eureka
服务端:引入相关依赖并配置访问Eureka的地址
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
核心
Eureka Server
:提供服务注册服务
Eureka Client
:要注册的微服务可以通过Eureka Client
进行注册,以及进行服务发现@EnableDiscoveryClient
获得Server的服务信息
- 服务注册
- 服务续约:
Eureka
客户会每隔30秒(默认情况下)发送一次心跳来续约 - 服务下线:客户端正常下线
- 服务剔除:如果
Eureka Server
在90秒没有收到Eureka
客户的续约,它会将实例从其注册表中删除 - 获取注册列表信息:该注册列表信息定期(每30秒钟)更新一次,并缓存到消费者本地
集群
防止单点故障,实现负载均衡和故障容错
服务端节点互相注册,互相守望
使用@LoadBalanced
注解还可以实现RestTemplate
负载均衡的能力,默认引入了Ribbon
server:
port: 7001
spring:
application:
name: cloud-eureka-service
eureka:
instance:
# eureka服务端的实例名称
hostname: eureka7001.com
client:
# false表示不向注册中心注册自己
register-with-eureka: false
# false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要检索服务
fetch-registry: false
service-url:
# 设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址
defaultZone: http://eureka7002.com:7002/eureka/s
自我保护
- 当一个
server
节点出现了网络分区等不可抗力原因,那么它会因此收不到client
的续约心跳,如果网络波动比较大,也就可能导致server
因为一次网络波动剔除了所有或者绝大部分Client
。 - 默认是15分钟内收到的续约低于原来的85%(这是设置的续约配置比例)那么就会开启 自我保护 。这阶段
Eureka Server
不会剔除其列表中的实例,即使过了 90秒 也不会。 - 属于CAP里面的AP
Zookeeper
在linux中启动服务端
注意存在服务端zk与pom中版本不一致问题,zookeeper版本jar包冲突
服务注册创建的是临时节点
server:
# 8004表示注册到zookeeper服务器的支付服务提供者端口号
port: 8004
spring:
application:
# 服务别名---注册zookeeper到注册中心的名称
name: cloud-provider-payment
cloud:
zookeeper:
# 默认localhost:2181
connect-string: localhost:2181
Consul
基于go语言,下载exe文件运行
server:
# consul服务端口
port: 8006
spring:
application:
name: cloud-provider-payment
cloud:
consul:
# consul注册中心地址
host: localhost
port: 8500
discovery:
hostname: 127.0.0.1
service-name: ${spring.application.name}
注册中心异同
CAP原理不同
负载均衡
Ribbon
集中式LB
:Nginx是服务端负载均衡,客户端所有请求交给nginx实现转发请求
进程内LB
:Ribbon是本地负载均衡,会在注册中心中获取注册信息服务列表之后选择合适的服务器
-
Ribbon 是 Netflix 发布的均衡负载器,是一个用于对 HTTP 请求进行控制的负载均衡客户端
-
负载均衡+
RestTemplate
调用实现负载均衡 -
在注册中心对 Ribbon 进行注册之后,通过
@Bean
创建LB类选择不同的负载均衡算法(轮循、随机、加权轮询、加权随机等)自动帮助服务消费者调用接口 -
还可以根据具体需求自定义
Ribbon
负载均衡算法,去掉注解@LoadBalanced
。既可以实现IRule
接口,也可在Controller里面实现
@Configuration
public class MySelfRule {
@Bean
public IRule myRule() {
// 定义为随机
return new RoundRobinRule();
}
}
---
@GetMapping(value = "/consumer/payment/lb")
public String getPaymentLB() {
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
if (instances == null || instances.size() <= 0) {
return null;
}
//自定义的负载均衡策略
ServiceInstance instance = loadBalancer.instances(instances);
URI uri = instance.getUri();
return restTemplate.getForObject(uri + "/payment/lb", String.class);
}
OpenFeign
使用 Ribbon
进行负载均衡,Feign 也是 Netflix 提供的,Feign 是一个声明式、模板化的 Web Service 客户端,让编写Web服务客户端变得非常容易,只需创建一个接口并在接口上添加注解即可。
相比于 Ribbon + RestTemplate
的方式,Feign 可以大大简化代码开发,自带负载均衡配置项,支持服务调用。
- 支持超时控制,客户端默认等待1秒
- 支持日志打印,了解http请求的细节
- 结合hystrix,支持服务熔断
![image-20210526004005615]
@RestController
@Slf4j
public class OrderController {
@Resource
private PaymentFeignService paymentFeignService;
@GetMapping("/consumer/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") long id) {
return paymentFeignService.getPaymentById(id);
}
}
服务熔断 Hystrix
服务雪崩:调用服务链某一处阻塞从而导致整个系统崩溃
熔断机制是应对雪崩效应的微服务链路保护机制。当扇出链路的某个微服务出错不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后,恢复调用链路。
服务降级
服务器忙,请稍后再试,不让客户端等待并立刻返回一个友好提示,fallback。
程序运行异常、超时、服务熔断触发服务降级、线程池/信号量也会导致服务降级。
@HystrixCommand
注解设置自身调用超时时间的峰值,峰值内可以正常运行, 超过了需要有兜底的方法处理,做服务降级fallback
- 结合Feign客户端实现与业务代码的解耦
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OTBZi0ik-1623904934154)(E:\java\笔记\photo\SpringCloud.assets\image-20210526005659009.png)]
服务熔断
类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示
熔断关闭-熔断打开-熔断半开
- 当满足一定的阈值的时候(默认10秒钟超过20个请求次数)
- 当失败率达到一定的时候(默认10秒内超过50%的请求次数)
- 到达以上2个阈值,断路器将会开启
- 当开启的时候,所有请求都不会进行转发,进行服务降级
- 一段时间之后(默认5秒),这个时候断路器是半开状态,会让其他一个请求进行转发. 如果成功,断路器会关闭,若失败,继续开启.重复4和5
图形化的监控信息hystrixDashboard
,查看请求成功失败的信息
新版本Hystrix需要在主启动MainAppHystrix8001
中指定监控路径,并在页面填写监控路径
服务网关GateWay
-
Gatway会根据注册中心注册的服务列表, 以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能
-
可以进行流量控制、日志记录、请求路由、请求过滤
-
Gateway使用的是Webflux中的reactor-netty响应式编程组件,底层使用了Netty通讯框架,非阻塞异步AIO
特性
路由转发+执行过滤器链
- 动态路由:路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成
- 断言Predicate:断言为true则匹配该路由。Predicate就是为了实现一组匹配规则, 让请求过来找到对应的Route进行处理
-
过滤器:可以在请求被路由前pre或者之后post对请求进行修改
通过
implments GlobalFilter,OrderId
来自定义过滤器
@Component
@Slf4j
public class MyLogGatewayFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("come in global filter: {}", new Date());
ServerHttpRequest request = exchange.getRequest();
String uname = request.getQueryParams().getFirst("uname");
if (uname == null) {
log.info("用户名为null,非法用户");
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}
// 放行
return chain.filter(exchange);
}
/**
* 过滤器加载的顺序 越小,优先级别越高
*
* @return
*/
@Override
public int getOrder() {
return 0;
}
}
工作流程
配置中心Config
Spring Cloud Config 通过服务端可以为多个客户端提供配置服务,既可以将配置文件存储在本地,也可以将配置文件存储在远程的 Git 仓库,创建 Config Server,通过它管理所有的配置文件。
config客户端动态刷新问题,
@RefreshScope
修饰业务类,通过curl
手动刷新,避免每次更新配置都要重启客户端微服务
消息总线Bus
分布式自动刷新配置功能:SpringCloud Bus配合Springcloud Config使用可以实现配置的动态刷新
ConfigClient实例都监听MQ中的同一个topic(默认是
SpringcloudBus
)。当一个服务刷新数据的时候,它会把这个信息放入到topic中,这样其它监听同一个topic的服务就能得到通知,然后去更新自身的配置。既可以全局通知也可以定点通知
消息驱动Stream
- 屏蔽底层消息中间件的差异,降低切换成本,统一消息的编程模型
- 通过定义绑定器
Binder
作为中间件,实现了应用程序与消息中间件细节之间的隔离
Binder:很方便的连接中间件,屏蔽差异
Channel:通道,是队列Queue的一种抽象,在消息通讯系统中就是实现存储和转发的媒介,通过channel对队列进行配置
Source和Sink:简单的可理解为参照对象是Springcloud Stream自身,从Stream发布消息就是输出,接受消息就是输入
分组消费与持久化
微服务应用放置于同一个group
中,就能够保证消息只会被其中一个应用消费一次。不同的组是可以重复消费的,同一个组内会发生竞争关系,只有其中一个可以消费。
分组后消费者宕机恢复后还可以继续消费宕机期间产生的未被消费的消息
Sleuth分布式链路跟踪
在微服务框架中,一个由客户端发起的请求在后端系统中会经过多个不同的的服务节点调用来协同产生最后的请求结果,每个前段请求都会形成条复杂的分布式服务调用链路,链路中的任何环出现高延时或错误都会引起整个请求最后的失败。