高可用:通过集群的方式
高并发:减少响应时间、提高吞吐量 并发用户数等,通过增加服务器性能、 扩展服务实例的方式
高性能:程序处理速度
考虑 数据存储结构、通信机制、集群同步。
集群同步:涉及到数据同步,数据同步我们有哪些协议 raft 、distro、ZAB
1、使用Http发送服务注册
2、查询服务提供方列表
3、定时拉取(每10秒)
4、如果检测到服务提供者异常,基于UDP协议推送更新
5、定时心跳(每5秒向服务端发送心跳包),检测服务状态
6、服务端做定时心跳任务检查,超过15s将节点设置为非健康状态,超过30s将节点进行删除。
7、集群数据 同步任务使用Distro协议。
1、Nacos 每个节点自己负责部分的写请求。
2、每个节点会把自己负责的新增数据同步给其他节点。
3、每个节点定时发送自己负责数据的校验值 到其他节点来保持数据一致性。
4、每个节点独立处理读请求,及时从本地发出响应。
5、新加入的 Distro 节点会进行全量数据拉取。(具体操作是轮询所有的 Distro节点,通过向其他
的机器发送请求拉取全量数据。)
如何进行技术选型:
1、社区活跃度
2、自己的技术栈
我们选型rocketmq 和kafka ,rocketmq :不支持指定时间的延时消息
rocketmq 是Java语言 kafka是 scala语言 ,我们技术栈是java ,方便进行二次开发,所以我们选择rocketmq。
3、产品功能
rocketmq或者kafka技术栈是否hold住
客户端会轮询向服务端发出一个长连接请求,这个长连接最多30s就会超时,服务端收到客户
端的请求会先判断当前是否有配置更新,有则立即返回,如果没有 服务端会新建一个定时任务线
程 把这个请求放到队列29.5s,最后0.5s再检测配置文件无论有没有更新都进行正常返回,但等待的
29.5s期间有配置更新可以提前结束并返回。
优先级从高到低。
# ${application.name}-${profile}.${file- extension} nacos-config-prod.yaml
# ${application.name}.${file-extension} nacos-config.yaml
# ${application.name} nacos-config
# extensionConfigs 扩展配置文件
# sharedConfigs 多个微服务公共配置 redis
Nacos服务端会启动一个定时任务,每3秒执行一次,查看所有连接是否超过20s没有通信,
如果超过20秒没有通信,服务端就会给客户端发送一个请求,进行探活,如果能正常返回就表示这
个服务为正常服务,如果不能正常返回就将其连接删除。
查看源码,有一个map,key记录的是服务的名称,value记录的是该服务对应的Spring上下文,记录着相关的配置。
为不同服务创建不同的Spring上下文。
Feign的底层是依赖于Ribbon的。
ribbon默认是懒加载的,只有第一次调用的时候才会生成ribbon对应的组件,这就会导致首次调用
的会很慢的问题。
我们可以把ribbon的懒加载关闭掉,开启饥饿加载模式。
ribbon:
eager-load:
enabled: true
clients: business
类的优先级更高。
在RibbonClientConfiguration.java类中,我们发现配置负载均衡的时候,使用注解@ConditionalOnMissingBean来加载类中的配置,其次是从配置文件中进行加载,如果两个都没有配置,则使用默认的配置。
Feign底层默认使用的是 JDK自带的HttpURLConnection,它是单线程发送HTTP请求的,不能配置线程池,我们使用Okhttp或者HttpClient来发送http请求,并且它们两个都支持线程池。
了解常见HTTP客户端:
HttpClient
HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的
支持 Http 协 议的客户端编程工具包,并且它支持 HTTP 协议最新版本和建议。HttpClient 相
比传统 JDK 自带的 URLConnection,提升了易用性和灵活性,使客户端发送 HTTP 请求变得
容易,提高了开发的效率。
Okhttp
一个处理网络请求的开源项目,是安卓端最火的轻量级框架,由 Square 公司贡献,用于替代
HttpUrlConnection 和 Apache HttpClient。OkHttp 拥有简洁的 API、高效的性能,并支持
多种协议 (HTTP/2 和 SPDY)。
HttpURLConnection
HttpURLConnection 是 Java 的标准类,它继承自 URLConnection,可用于向指定网站发
送GET 请求、 POST 请求。HttpURLConnection 使用比较复杂,不像 HttpClient 那样容易使用。
RestTemplate
RestTemplate 是 Spring 提供的用于访问 Rest 服务的客户端,RestTemplate 提供了多种便
捷访问远程 HTTP 服务的方法,能够大大提高客户端的编写效率。
自定义一个类实现接口RequestInterceptor,通过向header设置值实现认证传递。
自定义拦截器类
public class FeignAuthRequestInteceptor implements RequestInterceptor {
private String serviceName;
public FeignAuthRequestInteceptor(String serviceName) {
this.serviceName = serviceName;
}
@Override
public void apply(RequestTemplate requestTemplate) {
// 向header中设置 值
requestTemplate.header("serviceName",serviceName);
}
}
将自定义的拦截器类 加载到spring中:
@Configuration
public class FeignConfig {
// @Value("在配置文件中进行配置")
private String serviceName;
// 自定义拦截器
@Bean
public FeignAuthRequestInteceptor feignAuthRequestInteceptor(){
return new FeignAuthRequestInteceptor(serviceName);
}
}
使用了计数器滑动窗口算法、漏桶算法、令牌桶算法。
通过维护一个单位时间内的计数值,每当一个请求通过时,就将计数值加1,当计数值超过预
先设定的阈值时,就拒绝单位时间内的其他请求。如果单位时间已经结束,则将计数器清零,开启
下一轮的计数。
问题:
假设我们设定1秒内允许通过的请求阈值是99,如果有用户在时间窗口的最后几毫秒发送了99
个请求,紧接着又在下一个时间窗口开始时发送了99个请求,那么这个用户其实在一秒内成功
请求了198次,显然超过了阈值但并不会被限流。
计数器滑动窗口法就是为了解决上述固定窗口计数存在的问题而诞生。
前面说了固定窗口存在临界值问题,要解决这种临界值问题,显然只用一个窗口是解决不了问题的。
假设我们仍然设定1秒内允许通过的请求是200个,但是在这里我们需要把1秒的时间分成多格,假
设分成5格(格数越多,流量过渡越平滑),每格窗口的时间大小是200毫秒,每过200毫秒,就将
窗口向前移动一格。
每个小窗口中的数字表示在这个窗口中请求数,所以通过观察上图,可知在当前时间块(200毫
秒)允许通过的请求数应该是70而不是200(只要超过70就会被限流),因为我们最终统计请求数
时是需要把当前窗口的值进行累加,进而得到当前请求数来判断是不是需要进行限流。
问题:
流量的过渡是否平滑依赖于我们设置的窗口格数也就是统计时间间隔,格数越多,统计越精
确,但是计算越多。
漏桶算法以一个常量限制了出口流量速率,因此漏桶算法可以平滑突发的流量。其中漏桶作
为流量容器我们可以看做一个FIFO的队列,当入口流量速率大于出口流量速率时,因为流量容器
是有限的,当超出流量容器大小时,超出的流量会被丢弃。
其中水龙头是入口流量,漏桶是流量容器,匀速流出的水是出口流量。
漏桶算法的特点
漏桶具有固定容量,出口流量速率是固定常量(流出请求)
入口流量可以以任意速率流入到漏桶中(流入请求)
如果入口流量超出了桶的容量,则流入流量会溢出(新请求被拒绝)
不过因为漏桶算法限制了流出速率是一个固定常量值,所以漏桶算法不支持出现突发流量。
令牌桶算法是漏桶算法的改进版,可以支持突发流量。
不过与漏桶算法不同的是,令牌桶算法的漏桶中存放的是令牌而不是流量。
执行流程:
> 最开始,令牌桶是空的,我们以恒定速率往令牌桶里加入令牌,令牌桶被装满时,多余的令
牌会被丢弃。
> 当请求到来时,会先尝试从令牌桶获取令牌(相当于从令牌桶移除一个令牌),获取成功则
请求被放行,获取失败则阻塞或拒绝请求。
令牌桶算法限制的是平均流量,因此其允许突发流量(只要令牌桶中有令牌,就不会被限流)
服务熔断一般有三种状态:
熔断关闭状态(Closed)
服务没有故障时,熔断器所处的状态,对调用方的调用不做任何限制。
熔断开启状态(Open)
后续对该服务接口的调用不再经过网络,直接执行本地的fallback方法。
半熔断状态(Half-Open)
尝试恢复服务调用,允许有限的流量调用该服务,并监控调用成功率。如果成功率达到预
期,则说明服务已恢复,进入熔断关闭状态;如果成功率仍旧很低,则重新进入熔断关闭状态。
原来的服务进行了修改,让原来服务的流量变成80%,修改后的服务流量变成20%,修改后的服务进行调用后没有问题再逐渐增加流量。
使用Weight路由的断言工厂进行服务权重的配置,并将配置放到Nacos配置中心进行动态迁移。
Weight路由权重的断言工厂
该断言工厂中包含两个参数,分别是用于表示组 group,与权重 weight。
spring:
cloud:
gateway:
routes:
- id: weight_high
uri: https://weighthigh.org
predicates:
‐ Path=/product/**
- Weight=group1, 8
- id: weight_low
uri: https://weightlow.org
predicates:
‐ Path=/product/**
- Weight=group1, 2
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。
Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。
这四种模式都是2PC(两阶段提交)
AT模式:(默认的模式)提供无侵入自动补偿的事务模式 【这里是基于本地能支持事务的关系型数据
库,然后java代码可以通过JDBC访问数据库, 这里的无侵入:我们只需要加上对应的注解就可以
开启全局事务】
XA模式:支持已实现XA接口的数据库的XA模式【这里一般是需要数据库实现对应的XA模式的接
口,一般像 mysql oracle 都实现了XA】
TCC模式:TCC则可以理解为在应用层面的 2PC,是需要我们编写业务逻辑来实现。
SAGA模式:为长事务提供有效的解决方案
优点: 尽量保证了数据的强一致,实现成本较低,在各大主流数据库都有自己实现,对于MySQL
是从5.5开始支持(XA)。
缺点:
单点问题:事务管理器在整个流程中扮演的角色很关键,如果其宕机,比如在第一阶段已经完
成,在第二阶段正准备提交的时候事务管理器宕机,资源管理器就会一直阻塞,导致数据库无法
使用。
同步阻塞:在准备就绪之后,资源管理器中的资源一直处于阻塞,直到提交完成,释放资源。
数据不一致:两阶段提交协议虽然为分布式数据强一致性所设计,但仍然存在数据不一致性的可
能,比如在第二阶段中,假设协调者发出了事务commit的通知,但是因为网络问题该通知仅被
一部分参与者所收到并执行了commit操作,其余的参与者则因为没有收到通知一直处于阻塞状
态,这时候就产生了数据的不一致性。
全局事务id通过Feign的header进行传递。
1、多服务的情况
微服务,SOA等服务架构模式正在被大规模的使用,会出现很多分布式事务。
2、跨库
跨库事务指的是,一个应用某个功能需要操作多个数据库。
3、分库分表
通常一个库数据量比较大或者预期未来的数据量比较大,都会进行水平拆分,也就是分库分表。
CAP理论和BASE理论是 分布式必备理论基础。
CAP理论指的是在一个分布式系统中、Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可以全部保证。
一致性(C)
一致性意思就是 写操作之后进行读操作 无论在哪个节点都需要返回写操作的值。
可用性(A)
非故障的节点在合理的时间内返回合理的响应。
分区容错性(P)
当出现网络分区后,系统能够继续工作。打个比方,这里个集群有多台机器,有一台机器网
络出现了问题,但是这个集群仍然可以正常工作。
在分布式系统中,网络无法100%可靠,分区其实是一个必然现象,如果我们选择了CA而放
弃了P,那么当发生分区现象时,为了保证一致性,这个时候必须拒绝请求,但是A又不允许,所
以分布式系统理论上不可能选择CA架构,只能选择CP或者AP架构。
对于CP来说,放弃可用性,追求一致性和分区容错性,我们的zookeeper就是追求的强一致。
对于AP来说,放弃一致性(这里说的一致性是强一致性),追求分区容错性和可用性,Nacos就是
AP模式,这是很多分布式系统设计时的选择,后面的BASE也是根据AP来扩展。
BASE是Basically Available(基本可用)、Soft state(软状态)和 Eventually consistent
(最终一致性)三个短语的缩写。BASE理论是对CAP中一致性和可用性权衡的结果,其来源于对
大规模互联网系统分布式实践的总结, 是基于CAP定理逐步演化而来的。
BASE理论的核心思想是:即使无法做到强一致性,但每个应用都可以根据自身业务特点,采用适
当的方式来使系统达到最终一致性。
1、基本可用
基本可用是指分布式系统在出现不可预知故障的时候,允许损失部分可用性,注意,这绝不
等价于系统不可用。比如:
(1)响应时间上的损失。正常情况下,一个在线搜索引擎需要在0.5秒之内返回给用户相应的查
询结果,但由于出现故障,查询结果的响应时间增加了1~2秒。
(2)系统功能上的损失:正常情况下,在一个电子商务网站上进行购物的时候,消费者几乎能
够顺利完成每一笔订单,但是在一些节日大促购物高峰的时候,由于消费者的购物行为激增,为
了保护购物系统的稳定性,部分消费者可能会被引导到一个降级页面
2、软状态
软状态指允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可
用性,即允许系统在不同节点的 数据副本之间 进行数据同步的过程存在延时。
3、最终一致性
最终一致性强调的是所有的数据副本,在经过一段时间的同步之后,最终都能够达到一个一
致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系
统数据的强一致性。
Seata的AT模式
一阶段:开启全局事务(返回全局事务id)、注册分支事务、储存全局锁、业务数据和日志(undoLog)
进行提交。
二阶段:事务协调者TC根据所有分支的情况,决定本次全局事务是Commit还是Rollback (二阶段
是完全异步,删除undolog日志,全局事务、分支事务、储存的全局锁)
Eureka服务端会检查最近15分钟内所有Eureka 实例正常心跳占比,如果低于85%就会触发
自我保护机制。触发了保护机制,Eureka将暂时把这些失效的服务保护起来,不让其过期,但这
些服务也并不是永远不会过期。Eureka在启动完成后,每隔60秒会检查一次服务健康状态,如果
这些被保护起来失效的服务过一段时间后(默认90秒)还是没有恢复,就会把这些服务剔除。如果
在此期间服务恢复了并且实例心跳占比高于85%时,就会自动关闭自我保护机制。
Register(服务注册):把自己的 IP 和端口注册给 Eureka Server。
Renew(服务续约):发送心跳包,每 30 秒发送一次。告诉 Eureka 自己还活着。
Cancel(服务下线):当 provider 关闭时会向 Eureka 发送消息,把自己从服务列表中删除。防
止 consumer 调用到不存在的服务。
Get Registry(获取服务注册列表):获取其他服务列表。
Make Remote Call(远程调用):完成服务的远程调用。
Replicate(集群中数据同步):eureka 集群中的数据复制与同步。
1、 Spring Boot 、Spring Cloud 及 Spring Cloud Alibaba 版本对应关系。
2、换依赖,在父工程的pom.xml文件中添加spring-cloud.alibaba的依赖和nacos-discovery的依赖
3、配置文件换成nacos的配置信息。
4、在启动类上使用@EnableDiscoveryClient 开启注解。
@EnableDiscoveryClient 这是SpringBoot官方提供的 ,我们以后可能切换其他的注册中心比如说
nacos,那我们就直接切换就行了。
@EnableEurekaClient 这是netflix提供的,如果用这个注解就只能服务于eureka
5、梳理项目关系
调用的基础服务先进行更改,进行迁移。
在 Zuul 中提供了一些默认的 Filter,分别是:
1、Pre:过滤器 在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请
求的微服务、记录调试信息等;
2、Routing:过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用
Apache HttpClient或Netfilx feign请求微服务;
3、Post:过滤器 在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP
Header、收集统计信息和指标、将响应从微服务发送给客户端;
4、Error:在其他阶段发生错误时执行该过滤器。除了默认的过滤器类型微服务;