SpringCloudAlibaba
2021-01-11 11:53:44 1 举报
AI智能生成
尚硅谷2020springcloud和springcloudAlibaba
作者其他创作
大纲/内容
微服务架构零基础理论入门
SpringCloud 是分布式微服务架构的一站式解决方案,(比如,小米手机,小米电脑,小米音响,小米耳机....)是多种微服务架构落地技术的集合体,俗称 微服务全家桶
springCloud 技术大概有多少种呢?
服务注册与发现 Eureka
zookeeper
Consul
Nacos
服务负载与调用 Netflix oss Ribbon
LoadBalaner
openFeign
服务熔断 Hystrix
sentinel
负载均衡
服务降级
服务消息队列
配置中心管理 spring cloud config
Nacos
服务网关 Zuul
gateway
服务监控
全链路追踪
自动化构建部署
服务定时任务调度操作
服务开发
springboot
服务总线 Bus
Nacos
从2.2.x 和H 版开始说起
SpringCloud 分为上半场合下半场
上半场
SpringBoot2.X 版本 和 SpringCloud H版
下半场
SpringCloud alibaba
SpringBoot 版本选择
git 源码地址
SpringBoot2.0新特性
官网SpringBoot 强烈建议你升级到2.x以上版本
官网查看boot版本
SpringCloud 版本选择
git 源码地址
springcloud 命名规则
springcloud 采用了 伦敦地铁站的名称来命名,并由地铁站名称字母A-Z依次类推的形式来发布迭代版本
SpringCloud 版本采用了名称而非版本号来命名,这些版本的名字采用了伦敦地铁站的名字,根据字母表的顺序来对应版本时间顺序
Springcloud 和 SpringBoot 之间的依赖关系如何看
查看springboot和springcloud版本对应版本
更详细查看对应版本信息
版本确定
cloud
Hoxton.SR9
boot
2.3.5.RELEASE
cloud alibaba
2.1.RELEASE
java
8
maven
3.5以上
mysql
5.7以上
关于cloud 各种组件的停更/升级/替换
停更引发的升级惨案
停更不停用
被动修复bugs
不再接受合并请求
不再发布新版本
参考资料
cloud
英文文档
boot
文档
微服务架构编码构建
约定 > 配置 > 编码
idea新建 project 工作空间
微服务cloud 整体聚合父工程 Project
1), New Project
2), 聚合总父工程名字
3), Maven 选版本
4), 工程名字
5), 字符编码
setting ->Editor -> File Encodings 里面全部选择UTF-8 然后apply
6), 注解生效激活
setting -> Build -> Compiler -> Annotation Processors -> 勾上 Enable annotiation processing
7), java编译版本选择8
setting -> Build -> Compiler -> Java Compiler -> 8
8), File Type 过滤
settings -> Editor -> File Types -> Recognized File Types (ActionScript) || 在 Ignored Files And Folders 中添加 *.idea , *.iml
父工程 pom.xml
maven 工程落地细节复习
Maven 中的 DependencyManagement 和 Dependencies
通常会在一个组织或项目的最顶层的父pom中看到 dependeyManagement 元素
好处: 如果多个子项目都引用了同一样依赖,则可以避免每个使用的子项目都声明一个版本号,这样当想升级或切换到另一个版本时,只需要在顶层父容器中更新,不需要一个一个子项目的修改,另外如果某个子项目需要另外一个版本,只需要声明 Version即可
dependencyManagement这里只是声明依赖,因此 子项目需要显示声明需要用的依赖
如果不在子项目中声明依赖,是不会从父项目中继承下来的,只有在子项目中写了该依赖,并且没有指定具体版本,才会从父项目中继承该项,并且version 和scope 都读取自父 pom
Maven 中跳过单元测试
idea右上角 ->小闪电的图标 点了会发现 test 意思是跳过单元测试
父工程创建完执行 mvn:install 将父工程发布到仓库方便子工程继承
Rest微服务工程构建
构建步骤
cloud-provider-payment8001 微服务提供者支付 Module 模块
创建完后回到父类pom.xml 查看文件是否有变化
改pom.xml
写yml
主启动
业务类
建表
entites
dao
service
controller
测试(postMain)
热部署 Devtools (开发阶段使用)
1),添加 Devtools Jar
2),添加一个插件到 pom.xml
3), settings -> Build -> Compiler
4), 热注册开启 (ctrl +shift + alt +/) -> registry -> compiler.automake.allow.when.app.running (勾上) && actionSystem.assertFocusAccessFromEdt (勾上)
5), 重启 idea
cloud-consumer-order80 微服务消费订单 Module 模块
创建 cloud-consumer-order80
改pom.xml
建 application.yml
主启动类
业务类
entities
主实体 Payment
Json 封装体 CommentResult
首说 RestTemplate
是什么?
RestTemplate 提供了多种便捷访问远程Http服务的方法,是一种简单便捷访问 restful 服务模板类,是spring 提供用于访问 Rest 服务的客户端模板工具集
官网
如何使用?
使用restTemplate 访问 restful 接口非常简单粗暴无脑, (url,requestMap,ResponseBean.class) 三个参数分别代表 REST 请求地址,请求参数,Http相应转换成的对象类型
测试
controller 对象传入不要忘记加 @RequestBody 注解
cloud 开启 Run Dashboard
是什么?
cloud的服务有多个时,管理多个服务的启动使用run会不好管理,这样我们就可以使用Run Dashboard。
怎么玩
view -> tools -> services ->Run Configuration Type -> Spring Boot
consumer 和 provider 存在 entities 实体类代码重复 这里我们进行代码重构
工程重构
新建cloud-api-common
修改pom.xml
把 entities 实体类放到工程内
maven 命令 clean install
订单 81 和 支付8001 分别改造
Windows 端口被占用
netstat -aon|findstr "8001"
taskkill /pid 3672 /F
服务注册发现
Eureka
Eureka 基础知识
eureka
什么是服务治理
Springcloud 封装了 Netfix 公司开发的 Eureka 模块来实现 服务治理
在传统的 RPC 远程调用框架中,管理每个服务与服务之间的依赖关系比较复杂,管理比较复杂,所以需要使用服务治理,管理服务于服务之间依赖关系,可以实现服务调用,负载均衡,容错等,实现服务发现与注册
什么是服务注册
Eureka 采用了CS设计架构,Eureka Server 作为服务注册功能的服务器,它是服务注册中心,而系统中的其它微服务,使用Eureka 的客户端连接到 Eureka Server 并维持心跳连接,这样系统的维护人员就可以通过 Eureka Server 来监控系统中各个微服务是否正常运行
在服务注册与发现中,有一个注册中心,当服务器启动的时候,会把当前自己的服务器的信息,比如服务地址等以别名方式注册到注册中心上,另一方面(消费者|服务提供者),以该别名的方式去注册中心上获取实际服务通讯地址,然后再实现本地RPC的调用RPC远程调用框架核心设计思想
在于注册中心的,因为使用注册中心关系每个服务与服务之间的一个依赖关系(服务治理概念),在任何RPC远程调用框架中,都会有一个注册中心(存放服务地址相关信息(接口地址))
Eureka 两插件
Eureka Server --提供服务注册服务
各个微服务节点通过配置启动后,会在Eureka Server 中进行注册,这样Eureka Server 中的服务注册表中将会存储所有可用服务节点的信息,服务各个节点的信息可以在界面中直观看到
Eureka Client --通过注册中文进行访问
是一个Java 客户端,用于简化Eureka Server 的交互,客户端同时具备一个内置的,使用轮询(round-robin)负载算法的负载均衡器
在应用启动后,将会向Eureka Server 发送心跳(默认为 30秒)如果Eureka Server 在多个心跳周期内没有接收某个节点的心跳,Eureka Server 将会从服务注册表中把这个服务节点移除(默认90秒)
单机Eureka 构建步骤
idea 生成Eureka Server 服务注册中心 类似于物业中心
新建 moudle
pom.xml
写yml文件
写启动类
测试 访问 http://localhost:7001/
EureClient 端 cloud-provider-payment8001 将注册进Eureka Server称为服务的提供者,类似于尚硅谷学校对外提供授课服务
改pom.xml
写yml
主启动
Eureka自我保护机制
EureClient 端 cloud-consumer-order80 将注册进EurekaServer 称为服务消费者的consumer ,类似于尚硅谷上课消费的各位同学
集群Eureka 构建步骤
问题: 微服务RPC远程调用最核心的是什么?
高可用,试想一下,你的注册中心只有一个 only one ,它出故障了那就GG,会导致整个服务环境不可用,所以 我们要搭建Eureka 注册中心集群,实现负载均衡+故障容错
Eureka 集群原理说明
相互观望,相互注册
Eureka Server 集群环境构建步骤
按照 cloud-eureka-server7001 建 cloud-eureka-server7002
改pom.xml
修改映射配置
C:/windows/system32/drivers/etc/hosts
7001 yml文件修改
7002 yml文件修改
添加cloud-eureka-server7002 主启动类
测试
http://eureka7002.com:7002/ 出现 eureka7001.com
http://eureka7001.com:7001/ 出现 eureka7002.com
说明Eureka 集群搭建成功
将支付服务 8001 微服务发布到上面2台Eureka 集群配置中
yml配置文件修改
将订单服务80微服务发布到上面2台Eureka 集群配置中
yml配置文件修改
测试01
先要启动Eureka Server ,7001/7002服务
再启动服务提供者 provider 8001
再启动消费者 80
http://localhost:81/consumer/payment/get/6
支付服务提供者8001集群环境构建
按照cloud-provider-payment8001 的方式新建 cloud-provider-payment8002
pom文件, 业务类 ,yml配置文件 与cloud-provider-payment8001 的一致
在controller 添加区分标识
测试 负载均衡
出现bug
订单服务访问地址不能写死
使用 @LoadBalanced 注解赋予RestTemplate 负载均衡的能力
测试02
先要启动 EurekaServer 7001/7002 服务
再启动服务提供者 provider 8001/8002服务
http://localhost:81/consumer/payment/get/6
结果
负载均衡结果达到
8001 /8002 端口交替出现
Ribbon 和 Eureka 整合后Consumer 可以直接调用服务而不再关心地址和端口号,而且服务还有负载均衡功能
actuator 微服务信息完善
主机名称: 服务名称修改
当前问题
修改 admin:cloud-provider-service:8001
8001 /8002 yml文件修改
修改之后
访问信息有ip信息提示
当前没有ip显示
在 8001 /8002 的yml配置文件添加
服务发现 Discovery
对于注册进 eureka 里面的微服务,可以通过 服务发现来获得服务的信息
修改 cloud-provider-payment8001 的controller
8001 主启动类修改
Eureka 自我保护
导致原因
一句话: 某时刻某一个微服务不可用了,Eureka 不会立刻清理,依旧会对该微服务的信息进行保存
属于CAP 里面的AP分支
怎么禁止自我保护
注册中心 eureka Server 端7001
出厂默认,自我保护机制是开启的
测试 http://eureka7002.com:7002/
关闭的效果为
生产者客户端 eurekaClient 端8001
默认配置
如果8001 服务关闭,Eureka Server 会立即把这个服务提供者剔除
Zookeeper
参考 zookeeper 文档
Consul
consul 简介
官网
是什么?
consul 是一套开源的分布式服务发现和配置管理系统,由HashiCorp 公司用Go 语言开发
提供了微服务系统中的服务治理,配置中心,控制总线等功能,这些功能中的每一个都可以根据需要单独使用,也可以使用以构建全方位的服务网络,总之,Consul 提供了一种完整的服务网络解决方案
它具有很多优点,包括 raft 协议,比较简洁,支持健康检查,同时支持Http 和 Dns 协议支持跨数据中心的 Wan 集群提供图形界面,跨平台,支持Linux ,Mac,Window
能干嘛
服务发现
提供Http和Dns 两种发现方式
健康检查
支持多种方式,http,tcp,docker ,shell 脚本定制化
kv存储
key,value,的存储方式
多数据中心
consul 支持多数据中心
可视化Web界面
去那下
怎么玩
安装并运行 consul
1)解压到 /opt/目录
2),解压zip
unzip x.zip
3),检查是否安装成功
./consul
2), ./consul agent -dev -ui -node=consul-dev -client=IP
3), http://localhost:8500
服务提供者
建 cloud-provider-payment8006
pom
yml
主启动器
业务类编写controller
测试
http://192.168.247.111:8500/ --查看服务是否注册成功
服务消费者
建 cloud-consumer-consul-order83
pom
yml
主启动类
业务类
测试
http://localhost:8006/payment/consul
http://localhost:83/order/payment/consul
三个注册中心异同点
组件名
语言
CAP
服务健康检查
对外暴露接口
springcloud继承
Eureka
java
AP
可配支持
http
已集成
zookeeper
java
CP
支持
客户端
已集成
consul
go
CP
支持
http/dns
已集成
CAP 理论
理论图
子主题
C -- Consistency (一致性)
A --Availability (可用性)
P -- Partition tolerance (分区容错性) --分布式微服务架构P都要保证所以要么是CP 要么是AP
CAP 理论关注粒度是数据,而不是整个系统设计的策略
最多只能同时较好的满足两个,CAP 理论的核心是: 一个分布式系统不可同时很好满足一致性,可用性和分区容错性这三个需求,因此根据CAP原理将NoSql 数据库分成了满足CA原则,满足CP原则和满足Ap原则三大类
CA 单点集群,满足一致性可用性的系统,通常在可扩展上不太强大
CP 满足一致性性,分区零容忍的系统,通常性能不是特别高 [Zokeeper / Consul]
AP 满足高可用,分区零容忍的系统,通常对一致性要求低一点 [Eureka]
服务调用
Ribbon 负载均衡
概述
是什么
SpringCloud Ribbon 是基于 Netflix Ribbon 实现一套客户端负载均衡的工具 (消费者模块)
简单的说,Ribbon 是Netflix 发布的开源项目,主要功能是提供 客户端的软件负载均衡算法和服务调用,Ribbon 客户端组件提供一系列完善的配置项如连接超时,重试等
简单的说,就是在配置文件中列出 LoadBalancer (简称LB) 后面所有的机器,Ribbon 会自动的帮助你基于某种规则(如简单轮询,随机连接等) 去连接这些机器,我们很容易使用Ribbon 实现自定义的负载均衡算法
能干嘛
LB(负载均衡)
集中式LB
即在服务的消费方和提供方之间之间使用独立的LB设施(可以是硬件,如F5,也可以是软件,如Nginx) 由该设施负责把访问请求通过某种策略转发至服务的提供方
进程式LB
将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器
Ribbon 属于进程内LB , 它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址
前面80 通过轮询的方式访问 8001 和8002
一句话来介绍 Ribbon 就是 负载均衡+RestTemplate
在哪下 --[Ribbon 目前已经进入了维护模式]
Ribbon 负载均衡演示
架构图
架构说明
总结: Ribbon 其实就是一个软负载均衡的客户端组件,它可以和其它所需请求的客户端结合使用,和eureka 接口只是其中一个实例
pom
二说 RestTemplate
getForObject方法 / postForObject 方法
getForEntity 方法/ postForEntity 方法
Ribbon 核心组件IRule
IRule : 根据特定算法中从服务列表中选取一个要访问的服务
com.netflix.loadbalancer.RoundRobinRule
轮询
com.netflix.loadbalancer.RandomRule
随机
com.netflix.loadbalancer.RetryRule
先按照 RoundRobinRule 的策略获取服务,如果获取服务失败则在指定的时间内会进行重试,获取可用的服务
com.netflix.loadbalancer.WeightedResponseTimeRule
对RoundRobinRule 的扩展,响应速度越快的实例选择权重越大,越容易被选择
com.netflix.loadbalancer.BestAvailableRule
会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择下一个并发量最小的服务
com.netflix.loadbalancer.AvailabilityFilteringRule
先过滤掉故障实例,再选择并发较小的实例
com.netflix.loadbalancer.ZoneAvoidanceRule
默认规则,复合判断 server 所在区域的性能和server 的可用选择服务器
如何替换
修改 cloud-consumer-order80
细节说明
官网文档明确警告 这个自定义配置类不能放在@ComponentScan 所扫描的当前包下以及子包下,否则我们自定义的配置类就会被所有的Ribbon 客户端所共享,达不到特殊定制化的目的
com.atguigu.rule.MySelfRule
主启动类
测试
http://localhost:81/consumer/payment/getEntity/6
Ribbon 负载均衡算法
原理
负载均衡算法
rest 接口第几次请求数 % 服务器集群总数量 =实际调用的服务器位置下标,每次服务器重新启动后,rest接口计数从1开始
源码
AbstractLoadBalancerRule extends IRule
RoundRobinRule extends AbstractLoadBalancerRule
手写
手写一个本地负载均衡器试试
7001 / 7002 集群启动
8001 / 8002 微服务改造
在controller加入
80订单微服务改造
注释掉 @LoadBalancer
添加 LoadBalancerImpl
在controller加入方法
测试
http://localhost:81/consumer/payment/lb
OpenFeign
概述
是什么
Feign 是一个声明式webService 客户端,使用Feign 能让编写 web Service 客户端更加简单
它的使用方法时定义一个服务接口然后在上面添加注解,Feign 也支持可拔插式的编码器和解码器,springcloud 对feign 进行了封装,使其支持springmvc 标准注解和httpmessageConverters ,feign 可以与eureka 和 ribbon 组合使用以支持负载均衡
官网
github地址
能干嘛
Feign 旨在编写Java Http 客户端变得更容易
前面在使用 Ribbon +RestTemplate 时,利用 RestTemplate 对http 请求的封装处理,形成了一套模板化的调用方法,但是在实际开发中,由于对服务依赖调用可能不止一处,往往是一个接口会被多次调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用
Feign 在此基础上做了一步封装,由它来帮助我们定义和实现依赖服务接口的定义,在Feign 的实现下我们只需创建一个接口并使用注解的方式配置它(以前Dao 接口上面标识 Mapper 注解现在是一个微服务接口上面标注一个Feign 即可)
即完成对服务提供方的接口绑定,简化了 springcloud Ribbon 时,自动封装服务调用客户端的开发量
Feign 继承了 Ribbon
利用Ribbon 维护了 Payment 的服务列表信息,并且通过轮询实现了客户端的负载均衡,而与Ribbon 不同的是,通过 feign 只需要定义服务绑定接口且以声明式的方法,优雅而简单的实现了服务调用
openFeign 和 feign 区别
Feign
是springcloud 组件中一个轻量级 RESTFul 的Http 服务客户端Feign 内置了Ribbon ,用来做客户端负载均衡,去调用服务注册中心的服务,Feign 的使用方式是 使用Feign 的注解定义接口,调用这个接口,就可以调用服务注册中心服务
OpenFeign
是Springcloud 在Feign 的基础上支持了springmvc 的注解如 RequestMapping 等等,OpenFeign 的@FeignClient 可以解析springmvc 的@requestMapping 注解下的接口,并通过动态代理的方式产生实现类,实现类中做服务负载均衡并调用其它服务
OpenFeign 使用步骤
接口 + @FeiginClient(values="xxxx")
建 cloud-consumer-feign-order85
pom
yml
主启动类
业务类
添加service接口 PaymentService
controller 类引用service接口
测试
http://localhost:85/consumer/payment/6
Feign 自带负载均衡配置项
OpenFeign 超时控制
超时设置,故意设置超时演示出错情况
8001 /8002 添加超时方法
85 调用超时方法
测试
http://localhost:8001/payment/timeout
http://localhost:85/consumer/payment/timeout
OpenFeign 默认等待1秒钟,超过后报错
是什么
默认Feign 客户端只等待1秒钟,但是服务端处理需要超过1秒钟,导致Feign 客户端不想等待,直接返回报错
如何解决--修改yml客户端文件
OpenFeign 日志打印功能
是什么?
feign 提供了日志打印功能,我们可以通过配置来调整日志级别,从而了解Feign 中Http 请求的细节 一句话: Feign 接口的调用情况进行监控的输出
日志级别
NONE(默认的)
不显示任何日志
BASIC
仅记录请求方法,URL,响应状态码及执行时间
HEADERS
除了basic 中定义的信息之外,还有请求和响应头信息
FULL
除了Headers 中定义的信息之外,还有请求和响应的正文及元数据
配置日志bean
yml 配置文件修改
服务降级
Hystrix
概述
分布式系统面临问题
服务雪崩
是什么
Hystrix 是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的调用失败,比如超时,异常等等,Hystrix 能够保证在一个依赖出现问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性
断路器,本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似于熔断保险丝),向服务方返回一个符合预期的,可处理的备选响应(fallback), 而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间,不必要地占用,从而避免了故障在分布式系统中蔓延,乃至雪崩
能干嘛
服务降级
服务熔断
接近实时的监控
.....
官网
Hystrix 官宣,停更进维
被动修复bugs
不再接受合并请求
不再发布新版本
Hystrix 重要概念
服务降级(fallback)
服务器忙,请善后再试,不让客户端等待并立即返回一个友好提示 ,fallback
那些情况会发出降级
程序运行异常
超时
服务熔断触发服务降级
线程池/信号量打满也会导致服务降级
服务熔断(break)
类比 保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后再调用服务降级的方法并返回友好提示
就是保险丝
服务的降级 -> 进而熔断 -> 恢复调用链路
服务限流( flowlimit)
秒杀高并发等操作严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进入
hystrix 案例
构建项目
建 cloud-provider-hystrix-payment8007
改pom
yml
主启动类
业务类
service
controller
测试
http://localhost:8007/provider/hystrix/1
http://localhost:8007/provider/hystrix/timeout
以上述为根基 正确--> 错误 ---> 降级熔断 -->恢复
高并发测试
上述在非高并发情形下,还能勉强满足 but ...
apach jmeter官网
Jmeter 压测测试
http://localhost:8007/provider/hystrix/timeout
开启jmeter 来2w个线程进行测试
Jmeter 压测结论
两个都在转圈圈
为什么会被卡死
tomcat 的默认的工作线程数被打满了,没有多余的线程来分解压力和处理
上面还是服务提供者8007自己测试,假如此时外部的消费者80也来访问,那消费者只能干等.最终导致消费者80不满意,服务端8001直接被拖死
看热闹不嫌事大,87新建加入
建 cloud-consumer-feign-hystrix-order87
pom
yml
主启动类
业务类
service
controller
测试
2w个线程jmeter 测试8007
http://localhost:87/order/provider/hystrix/1
消费者87
要么转圈圈等待
要么消费者端报超时错误
故障现象和导致原因
8007 同一层次的其它接口服务被困死,因为tomcat 线程池里面工作线程已经被挤占完毕
87调用8007 ,客户端访问响应变慢,转圈圈
上述结论
正因为有上述故障或不佳表现才有我们的降级/容错/限流等技术的诞生
如何解决? 解决要求
超时导致服务器变慢(转圈)
超时不再等待
出错(宕机或程序运行出错)
出错要有兜底
解决
对方服务(8007)超时了,调用这(87)不能一直卡死等待,必须要有服务降级
对方服务(8007)宕机了,调用者(87) 不能卡死等待,必须有服务降级
对方服务(8007)ok,调用者(87)自己出故障或有自我要求(自己的等待时间小于服务提供者),自己处理降级
服务降级
降级配置
@HystrixCommand
8007先从自身找问题
设置自身调用超时时间的峰值,峰值内可以正常运行,超过了需要兜底的方法处理,做服务降级fallback
8007 fallback
业务类启动 @HystrixCommand 注解
一旦调用服务方法失败并抛出了错误信息后,会自动调用 @HystrixCommand 标注好的 fallbackMethod 调用类中指定的方法
把sleep方法注释掉,然后 int a=10/0 ;运行程序查看是否会执行fallbackMethod 方法
结论: 当服务不可用了,做服务降级,兜底的方案都是 fallbackMethod 指定的方法
主启动类添加 @EnableCircuitBreaker
87 fallback
87订单微服务,也可以更好的保护自己,自己也一样画葫芦进行客户端降级保护
切记 ,我们自己配置过的热部署方式对java代码改动明显,但对 @HystrixCommand 内属性的修改建议重启微服务
yml
主启动类
业务类
目前问题
每个业务方法对应一个兜底的方法,代码膨胀
同一和自定义的分开
解决问题
每个方法配置一个???膨胀
Feign 接口系列
@DefaultProperties(defaultFallback = "")
1:1 每个方法配置一个服务降级方法,技术上可以,实际上不可能
1:N 除了个别重要核心业务有专属,其它普通的可以通过 @DefaultProperties(defaultfallback="xxx")同一跳转到统一处理结果页面
通用的和独享的各自分开,避免了代码膨胀,合理减少了代码量
和业务逻辑混合一起???混乱
服务降级,客户端取调用服务端,碰上服务端宕机或关闭
本次案例服务降级处理是 在客户端87实现完成的,与服务端8007没有关系只需要为 Feign 客户端定义的接口添加一个服务降级处理的实现类即可实现解耦
未来我们面对的异常
运行时异常
超时
宕机
PayService
建 PayServiceFallbackHandler 实现PayService
yml
测试
http://localhost:87/order/provider/hystrix/1
如果这时 8007宕机了 是否提供保护或降级
PayServiceFallbackHandler fallback getThreadNameOk o(╥﹏╥)o
服务熔断
断路器
类似家里面的保险丝
熔断是什么
熔断机制是应对雪崩效应的一种微服务链路保护机制,当扇出链路的某个微服务出错不可用或相应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息
当检测到该节点微服务调用响应正常后,恢复调用链路
在springcloud 框架里,熔断机制通过Hystrix 实现,Hystrix 会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是5秒内20次调用失败,就会启动熔断机制,熔断机制的注解是 @HystrixCommand
实操
service
controller
测试
http://localhost:8007/provider/hystrix/fusing/11 --正数正确
http://localhost:8007/provider/hystrix/fusing/-11 --执行错误
一次错误,一次正确
多次错误,然后慢慢正确,发现刚开始不满足条件,就算正确的访问地址也不能输出正确的值
小总结
大神结论
熔断类型
熔断打开
请求不再进行调用当前服务,内置设置时钟一般为MTTR(平均故障处理时间) ,当打开时长达到所设时钟侧则进入半熔断状态
熔断关闭
熔断关闭不会对服务进行熔断
熔断半开
部分请求根据规则调用当前服务,如果请求成功且符合规则认为当前服务恢复正常,关闭熔断
官方熔断路器流程图
子主题
熔断器在什么情况下开始起作用?
三个重要参数 , 快照时间窗, 请求总数阈值, 错误百分比阈值
1),快照时间窗
断路器确定是否打开需要统计一些请求和错误数据,而统计的时间范围就是快照时间窗,默认为最近10秒
2), 请求总数阈值
在快照时间窗内,必须满足请求总数阈值才有资格熔断,默认为20秒,意味着10秒以内,如果该hystrix 命令调用次数不足20次,即使所有的请求都超时或其它原因失败,断路器都不会打开
3),错误百分比阈值
当请求总数在快照时间窗内超过了阈值,比如发生了30次调用中,有15次发生了超时异常,也就睡超过了50%错误百分比,在默认50%的阈值情况下,这时候就会将熔断器打开
断路器开启/关闭的条件
当满足一定的阈值的时候(默认10秒内超过20个请求次数)
当失败率达到一定的时候(默认10秒内超过50%的请求失败)
到达以上阈值,断路器将会开启
当开启的时候,所有请求都不会进行转发
一段时间之后(默认是5秒) 这个时候断路器是半开状态,会让其中一个请求进行转发,如果成功,断路器会关闭,若失败继续开启,重复4和5
断路器打开之后
1), 再有请求调用的时候,将不会调用主逻辑,而是直接调用降级fallback ,通过断路器,实现了自动地发现错误并将降级逻辑切换为主逻辑,减少响应延迟的效果
2), 原来的主逻辑到底是如何恢复的?
对于这一问题,hystrix 也为我们实现了自动恢复功能,当熔断器打开后,hystrix 会启动一个休眠时间窗,在这个时间窗内,降级逻辑是临时的成为主逻辑,当休眠时间窗口到期,断路器将会进入半开状态,释放一次请求到原来的主逻辑上,如果此次请求正常返回,那么断路器将继续闭合,主逻辑恢复,如果这次请求依然有问题,段录取继续进入打开状态,休眠时间重新计时
all配置
服务限流
高级篇 alibaba 的 Sentinel 说明
hystrix 工作流程
github
子主题
服务监控 hystrixDashboard
概述
除了隔离依赖服务的调用以外,Hystrix 还提供了准实时的调用监控,(Hystrix Dashboard) ,Hystrix 会持续的记录所有通过Hystreix 发起的请求的执行信息,并以统计报表和图形的形式展示给用户,包括每秒执行多少请求多少成功,多少失败等,Netfilx 通过 Hystrix-metrics-event-stream 项目实现了对以上指标监控,springcloud 也提供了Hystrix Dashboard 的整合,对监控内容转为了可视化界面
创建9001 hystrix Dashboard9001
创建 cloud-hystrix-dashboard9001
pom
yml
主启动类
注意: 所有 provider 微服务提供者(8001/8002/8007) 都需要监控依赖配置
测试
http://localhost:9001/hystrix
hystrixdashboard 使用
错误: Unable to connect to Command Metric Stream.
9001 监控 8007
http://localhost:8007/hystrix.stream
2000
T3
测试地址
http://localhost:8007/provider/hystrix/fusing/-11
http://localhost:8007/provider/hystrix/fusing/11
上述测试通过
ok
先访问正确地址,再访问错误地址,会发现熔断器dashboard 都是慢慢放开的
监控结束,成功
监控结果,失败
如何看这个图呢?
7色
1圈
通过颜色的变化代表了实例的健康程度,它的监控度从 绿色<黄色<橙色<红色递减
该实心圈除了颜色之外的变化之外,它的大小也会根据实例的请求流量发生变化,流量越大该实心圈就越大,所以通过该实心圈展示,就可以在大量的实例中快速的发现故障实例和高压力实例
resilience4j
sentinel
服务网关
zuul路由网关
新一代网关 Gateway
概述介绍
zuul官网
gateway官网
是什么
cloud 全家桶中有个很重要的组件就是网关,在1.x版本中都是采用了Zuul网关,但在2.x版本中,zuul的升级一直跳票,springcloud 最后自己研发了一个网关来替代Zuul 那就是springcloud gateway 一句话,gateway 是原zuul1.x版的替代
一句话
springcloud gateway 使用的webflu 中的reactor-netty 响应式编程组件,底层使用了Netty 通讯框架
能干嘛
反向代理
流量控制
熔断
日志监控
鉴权
......
微服务架构中网关在哪里
有Zuul 怎么又出来了gateway
我们为什么选择gateway
1), Netflix 不太靠谱,zuul2.0一直跳票,迟迟不发布
一方面,Zuul1.0 已经进入了维护阶段,而且Gateway 是springcloud 团队研发的,是亲儿子产品,值得信赖,而且很多功能Zuul 都没有gateway 方便快捷
gateway 是基于异步非阻塞模型上进行开发的,性能方面不需要担心,虽然Netflix 早就发布了最新的Zuul2.x 但springcloud 貌似没有整合计划,而且Netflix 相关组件都宣布进入了维护期,不知前景如何?
2),springcloud gateway 具有以下特性
基于SpringFramework5,Project Reactor , 和springBoot2.0机型构建
动态路由,能够匹配任何请求属性
可以对路由指定 Predicate(断言) 和Filter (过滤器)
继承Hystrix 的断路器功能
集成springcloud 服务发现功能
易于编写的 Predicate(断言) 和Filter(过滤器)
请求限流功能
支持路径重写
3), springcloud gateway 与zuul 的区别
1), Zuul 1.x 是一个基于阻塞I/O的API gateway
2),Zuul 1.x 基于Servlet2.5使用的阻塞架构它不支持任何长连接(如 WebSocket) Zuul 的设计模式和Nginx 较像,每次I/O操作都是从工作线程中选择一个执行,请求线程被阻塞到工作线程完成,但是差别是 Nginx 是C++编写,Zuul用Java 编写,而且JVM 本身会有第一次加载较慢的情况,使得Zuul 的性能相对较差
3),Zuul 2.x 理念更先进,想基于 Netty 非阻塞和长连接,但springcloud 目前没有整合,Zuul 2.x 的性能较Zuul1.x 有较大的提升,在性能方面,根据官方的基准测试, gateway 是Zuul 的1.6倍
4), gateway 建立在 springFramewark5,Project Reactor 和 springboot 之上,使用非阻塞api
5),gateway 还支持 websocket 并且与spring紧密集成拥有更好的开发体验
gateway模型
webFlu是什么
非阻塞+函数式编程
三大核心概念
Route ---路由
是构建网关的基本模块,它由ID,目标url,一系列的断言和过滤器组成,如果断言为true 则匹配该路由
Predicate -- 断言
参考 java8的java.util.funcation.Predicate
开发人员可以匹配Http请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由
开发人员可以匹配Http请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由
Filter -- 过滤
是spring框架中gatewayFilter 的实例,使用过滤器,可以在请求被路由前或之后对请求进行修改
总体
图片
1),web请求,通过一些匹配条件,定位到真正的服务节点,并在这个转发过程的前后,进行一些精细化控制
2), Predicate 就是我们的匹配条件,而filter ,就可以理解为一个无所不能的拦截器,有了这两个元素,再加上目标url,就可以实现一个具体的路由了
Gateway 工作流程
图片
客户端向gateway 发送请求后,然后再 gateway Handler Mapping中找到与请求相匹配的路由,将其发送到 gateway web handler
handler 再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回,过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前 ("pre") 或之后 ("post")执行业务逻辑
Filter 在 "pre" 类型的过滤器可以做参数效验,权限效验,流量监控,日志输出,协议转换,在post 类型的过滤器中可以做响应内容,响应头的修改,日志的输出,流量监控等有着非常重要的作用
核心逻辑
路由转发+执行后过滤器链
建项目9527
建 cloud-gateway-gateway9527
pom
yml
启动类
9527网关如何做路由映射呢?
cloud-provider-payment8001 看看controller的访问路径
我们不想暴露8001 端口,希望8001 外面套一层9527
测试
添加网关前 : http://localhost:8001/payment/get/1
添加网关后: http://localhost:9527/payment/get/1
gateway 网关配置
在配置文件中配置
代码中注入 RouteLocator 的Bean
通过微服务名实现动态路由(9527)
默认情况下,gateway 会根据注册中心注册的服务列表,以注册中心上微服务名为 路径创建动态路由进行转发,从而实现动态路由的功能
pom
yml
需要注意的是uri 的协议是lb ,表示启用gateway 的负载均衡功能
lb://serviceName 是springcloud gateway 在微服务中自动为我们创建的负载均衡的url
测试
http://localhost:9527/payment/lb
8001/8002 替换输出端口
Predicate 的使用
查看我们9527控制台
Route Predicate Factories是什么
常见的 Route Predicate
after-route-predicate
yml
before-route-predicate
between-route-predicate
cookie-route-predicate
不带cookie 访问
带上cookie访问
yml
header-route-predicate
yml
curl
host-route-predicate
yml
测试
method-route-predicate
path-route-predicate
query-route-predicate
yml
测试
remoteaddr-route-predicate
说白了,Predicate 就是为了实现一组匹配规则,让请求过来找到对应的Route 进行处理
Filter的使用
是什么?
路由过滤器可用于修改进入的Http 请求和返回的Http 响应,路由过滤器只能指定路由进行使用
springcloud gateway 内置了多种路由过滤器,它们都由gateway filter 的工厂类产生
springcloud gateway 的Filter
生命周期 Only two
pre
post
种类 Only Two
gatewayFilter
globaFilter
常用的 gateway Filter
yml
controller
自定义过滤器
自定义全局 GlobaFilter
两个主要接口介绍
implements GlobalFilter, Ordered
能干嘛?
全局日志记录
统一网关鉴权
...
自定义类
测试
http://localhost:9527/payment/lb?uname=zs
服务配置
Config
概述
分布式系统面临的--配置问题
微服务意味着要将单体应用中的业务拆分成一个个子服务,每个服务的粒度相对较小,因此系统中出现大量重复的服务,由于每个服务都需要必要的配置信息才能运行,所以一套集中式的,动态的配置管理设施是必不可少的
springcloud 提供了configserver 来解决这个问题,我们每个微服务自己带着一个application.yml上百个配置文件的管理....
是什么?
springcloud config 为微服务架构中的微服务提供集中化的外部配置支持,配置服务器为各个不同微服务应用的所有环境提供了一个中心化的外部配置
怎么玩?
springcloud config 分为服务端和客户端两部分
服务端也称为分布式配置中心,它是一个独立的微服务应用,用来连接配置服务器并为客户端提供获取配置信息,加密/解密信息等访问接口
客户端则是通过指定的配置中心来管理应用资源,以及业务相关的配置内容,并在启动的时候从配置中心获取加载配置信息配置服务器默认采用git 来存储配置信息,这样有助于对环境配置进行版本管理,并且可以通过git 客户端工具来方便的管理和访问配置内容
能干嘛
集中管理配置文件
不同环境不同配置,动态化的配置更新,分布式部署比如 dev/test/prod/beta/release
运行期间动态调整配置,不再需要在每个服务部署的机器上编写配置文件,服务回向配置中心统一拉取配置自己的信息
当配置发生变动时,服务不需要重启即可感知到配置的变化并应用新的配置
将配置信息以REST接口的形式暴露
post.curl访问即可刷新
与github 整合配置
由于springcloud config 默认使用git来存储配置文件(也其有其它方式,比如支持svn 和本地文件),但推荐的还是git , 而且使用的是htt/https 访问形式
官网
Config 服务端配置与测试
github创建一个新仓库 springcloud-config 获取git地址
本地硬盘目录上新建 git仓库并clone
本地仓库地址为 D:\springcloudconfig
git clone https://github.com/zhaoguangxiao/springcloud-config.git
此时D:\springcloudconfig\springcloud-config
表示多环境配置文件
保存格式必须为UTF-8
如果需要修改
git add.
git commit -m "内容"
git push origin master
建 cloud-config-center3344
pom
yml
启动类
windows 下修改 hosts 文件,增加映射
测试通过config 微服务是否可以从github上获取内容
http://config3344.com:3344/main/config-dev.yml
配置的读取规则
{label}/{application}-{profile}.yml
main分支
http://config3344.com:3344/main/config-dev.yml
dev分支
http://config3344.com:3344/dev/config-dev.yml
{application}-{profile}.yml
http://config3344.com:3344/config-dev.yml
{application}/{profile}/{label}
http://config3344.com:3344/config/dev/main
成功实现了用 springcloud config通过github 获取配置信息
Config 客户端配置与测试
建 cloud-provider-config-client3355
pom
bootstrap.yml
application.yml 是用户级的资源配置项
bootstrap.yml 是系统级的,优先级更高
springcloud 会创建一个 bootstrap context 作为spring应用的 application context 的父上下文,初始化的时候,bootstrap context 负责从外部源加载配置属性并解析配置,这两个上下文共享一个从外部获取的 Environment
要将 Client 模块下的 application.yml 文件改为 bootstrap.yml 这是很重要的 因为bootstrap.yml 比application.yml 先加载,bootstrap.yml 级别高于 application.yml
主启动类
业务类
测试
http://localhost:3355/server/port
成功实现客户端访问 springcloud config 3344通过github获取配置信息
问题随之而来,分布式配置的动态刷新问题
Linux 运维修改了 github 上的配置文件内容做调整
刷新 3344 发现configserver 配置中心文件立刻响应
刷新3355 ,发现configclient 客户端没有任何响应
3355没有变化除非自己重启或重新加载
难道每次运维修改配置文件,客户端都需要进行重启?噩梦
Config 客户端之动态刷新
避免每次更新都要重启客户端微服务3355
动态刷新--手动版
修改3355模块
引入 actuator 监控
yml
@RefreshScope 业务类controller修改
测试
http://config3344.com:3344/main/config-test.yml
http://localhost:3355/server/port
配置没有生效
需要运维人员发送一个post请求刷新3355
post 请求
curl -X POST http://localhost:3355/actuator/refresh
再次测试
http://localhost:3355/server/port
数据同步成功,避免了微服务重启
想想还有什么问题
假设有多个微服务客户端 3355/3366/3377 ...
每个微服务都要执行一次post请求,手动刷新?
可否广播,一次通知,处处生效?
我们想大范围的自动刷新,求方法---服务总线 --Bus
Nacos
消息总线
Bus
概述
上一讲解的加深和扩充,一眼以蔽之
分布式自动刷新配置功能
springcloud Bus 配置spring cloud config 使用可以实现配置的动态刷新
是什么
Bus 支持两种消息代理 RabbitMQ 和 Kafka
能干嘛
spring cloud Bus 能管理和传播分布式系统间的消息,就像一个分布式执行器,可用于广播状态的修改,事件推送等,也可以当做微服务间的通信通道
为何称为总线
什么是总线
在微服务架构的系统中,通常会使用轻量级的消息代理来构建一个共用的消息主题,并让系统中所有的微服务实例都连接上来,由于该主题中产生的消息会被所有实例监听和消息,所以称它为消息总线,在总线上的各个实例,都可以方便地广播一些需要让其它连接到该主题上的实例都知道的消息
基本原理
configClient 实例都监听 MQ 中同一个topic (默认是springcloud Bus) 当一个服务刷新数据的时候,它会把这个信息放入到topic 中,这样其它监听同一topic 的服务就能得到通知,然后去更新自身配置
RabbitMQ 环境配置
安装Erlang 下载地址
yum -y install unixODBC
rpm -ivh esl-erlang_23.1.5-1_centos_7_amd64.rpm
socat 安装
wget http://www.dest-unreach.org/socat/download/socat-1.7.0.1.tar.gz
tar -zxvf socat-1.7.0.1.tar.gz
cd socat-1.7.0.1
./configure --disable-fips make && make install
安装RabbitMQ下载地址
rpm -ivh rabbitmq-server-3.8.9-1.el7.noarch.rpm
修改配置 --- /etc/rabbitmq/
rabbitmq.config
rabbitmq-plugins enable rabbitmq_management
启动
systemctl start rabbitmq-server
重启
systemctl restart rabbitmq-server
状态
systemctl status rabbitmq-server
Springcloud Bus 动态刷新全局广播
必须具备良好的RabbitMQ环境先
演示广播效果,增加复杂度,再以3355为例,再制作一个3366
建 cloud-provider-config-client3366
pom
bootstrap.yml
启动类
业务类
测试
设计思想
1),利用消息总线触发一个客户端 /bus /refresh ,而刷新所有客户端的配置
2), 利用消息总线触发一个服务端 ConfigServer 的 /bus /refresh 端点,而刷新所有客户端的配置
图二的结构显然更加合适,为什么图一不合适呢
打破了微服务的职责单一性,因为微服务本身是业务模块,它本不应该承担配置刷新的职责
破坏了微服务各个顶节点的对等性
有一定的局限性,例如,微服务在迁移时,它的网络地址常常发生变化,此时如果想要做到自动刷新,那就会增加更多的修改
给 cloud-config-center3344 配置中心服务端添加消息总线支持
pom
yml
给 cloud-provider-config-client3355 客户端添加消息总线支持
yml
yml
cloud-provider-config-client3366 同3355同理
测试
运维工程师修改github上配置文件增加版本号
发送post请求
curl -X POST http://localhost:3344/actuator/bus-refresh
一次修改,处处生效
http://localhost:3344/main/config-test.yml
http://localhost:3355/server/port
http://localhost:3366/server/port
3355/3366已经修改了
springcloud Bus 动态刷新定点通知
不想全部通知,只想定点通知
只通知3355
不通知3366
简单一句话
指定具体某一个实例生效而不是全部
http://localhost:3344/actuator/bus-refresh/{destination}
/bus/refresh 请求不再发送到具体的服务实例上,而是发送给 config server 通过 destination 参数类指定需要更新配置的服务或实例
我们这里以刷新运行在 3355 端口上的config-client为例
只通知 3355
不通知3366
curl -X POST http://localhost:3344/actuator/bus-refresh/c
loud-provider-config-client:3355
loud-provider-config-client:3355
SpringCloud Stream 消息驱动
消息驱动概述
是什么
官网
一句话
屏蔽底层消息中间件的差异,降低切换成本,统一消息的编程模型
设计思想
标准MQ
生产者/消费者之间靠消息媒介传递信息内容
message
消息必须走特定的通道
消息通道 messageChannel
消费通道里的消息如何被呢?谁负责收发处理
消息通道 messageChannel 的子接口 subscribableChannel ,由messageHandler 消息处理器所订阅
为什么使用springcloud stream
这些中间件的差异性导致我们实际项目开发给我们造成了一定的困扰,我们如果用了两个消息队列的其中一种,后面的业务需求,我们想往另外一种消息队列进行迁移,这时候无疑是一个灾难性的,一大堆东西都要重新推到重做,因为它跟我们的系统耦合了,这时候springcloud stream 给我们提供了一种解耦合的方式
stream 凭什么可以统一底层差异?
在没有绑定器这个概念的情况下,我们的springboot 应用要直接与消息中间件进行信息交互的时候,由于各消息中间件构建的初衷各不相同,它们实现的细节上会有较大的差异性,通过定义绑定器Binder作为中间层,完美实现了 应用程序与消息中间件细节之间的隔离 通过向应用程序暴露统一的 Channel 通道,使得应用程序不需要再考虑各种不同的消息中间件实现
Binder
input 对应于消费者
output 对应于生产者
stream 中的消息通信方式遵循了发布-订阅模式
topic 主题进行广播
在rabbitmq 就是Exchange
在 kafka 就是topic
springcloud stream 标准流程套路
1), binder
很方便的连接中间件
2), Channel
通道,是队列queue 的一种抽象,在消息通讯系统中就是实现存储和转发的媒介,通过channel 对队列进行配置
3), source 或 sink
简单的可理解为 参照对象是 spring cloud stream 自身从stream 发布消息就是输出,接受消息就是输入
编码api 和常见注释
案例说明
rabbitmq 环境已经ok
新建三个子模块
cloud-stream-rabbitmq-provider8801 生产者
pom
yml
主启动类
业务类
service
controller
测试
启动 注册中心eureka
启动消息中间件 15672访问
启动8801
http://localhost:8801/stream/send
cloud-stream-rabbitmq-consumer8802 消费者1号
pom
yml
主启动类
业务类
controller
测试
8801 点击发送消息 ,查看8802控制台日志是否打印
cloud-stream-rabbitmq-consumer8803 消费者2号
与8802相同
分组消息与持久化
同时运行8802 和8803 有两个问题
有重复消费问题
如何解决
分组和持久化属性group
生产实际案例
比如在如下场景中,订单系统我们做集群部署,都会从rabbitmq 中获取订单消息,那如果一个订单同时都被两个服务获取到,那么就会造成数据错误,我们得避免这种情况,这是我们就可以使用stream 中的消息分组来解决
注意 在stream 中处于一个group 中的多个消息者是竞争关系,就能保证消息只会被其中一个应用消息一次不同组是可以全面消费的(重复消费)
同一组内会发生竞争关系,只有其中一个可以消费
分组
原理
微服务应用放置于同一个group 中,就能保证消息只会被其中一个应用消费一次,不同组是可以消费的,同一个组会发生竞争关系,只有其中一个可以消费
8802 / 8803 都变成不同组,group 两个不同
group: AtguiguA,AtguiguB
8802yml
8803 yml
测试
查看rabbitmq 控制台--> Exchanges -->studyExchange
结果还是重复消费
虽然它们同属于一个应用,但是这个消息出现了被重复消费两次的情况,为了解决这个问题,在springcloud stream 中提供了消费组的概念
8802 /8803 实现了轮询分组,每次只有一个消费者 8801 模块发的消息只能被8802 /8803其中一个接收到,这样避免了重复消费
8802 / 8803 都变为相同组
8802 / 8803 的yml都修改为同一组
结论
同一组内多个微服务实例,每次只能有一个人拿到
消息持久化问题
通过上述,解决了重复消费问题,再看看持久化
停止8802/8803 并去除8802分组group: AtguiguA #自定义分组,8803yml不变
8801这时发送4条消息到rabbitmq
先启动8802无分组属性,查看是否能接受到rabbitmq消息
再启动8803 有分组属性,查看是否能打印mq消息
SpringCloud Sleuth 分布式请求链路跟踪
概述
为什么会出现这个技术? 需要解决那些问题
在微服务架构中,一个客户端发起的请求在后端系统会经过多个不同的服务节点来协同产生最后的请求结果,每一前段请求都会形成一条复杂的分布式服务调用链路,链路中的任何一环出现高延迟或错误都会引起整个请求最后的失败
是什么
github
springcloud sleuth 提供了一套完整的服务跟踪的解决方案
在分布式系统中提供追踪解决方案并且兼容支持zipkin
搭建链路监控步骤
zipkin 下载
springcloud 从F版本起不需要构建自己的 zipkin server了只需要调用jar即可
zipkin官网
启动
java -jar 压缩包.jar
运行控制台
http://localhost:9411/zipkin/
完整的调用链路
表示一请求链路,一条链路通过 trace id唯一标识,span 标识发起的请求信息,各span 通过 parent id 关联起来
名词解释
trace 类似于树结构的span 集合,表示一条调用链路,存在唯一标识
span 表示调用链路来源,通俗的理解就是一次请求信息
修改8001/8802服务提供者
pom
yml
业务类
controller
消费者80修改
pom
yml
业务类
controller
测试
http://localhost:9411/
SpringCloud Alibaba 入门简介
为什么会出来springcloud alibaba
springCloud Netflix 项目进入了维护模式
什么是维护模式
将模块置于维护模式,意味着springcloud 团队将不会再向模块添加新功能,我们修复block 级别以及安全问题,我们也会考虑并审查社区小型 pull request
进入维护模式意味着什么
1),springcloud Netflix 将不再开发新的组建
2),新组建功能将以其它替代平代替的方式实现
SpringCloud Alibaba带来了什么
中文官网
SpringCloud Alibaba 学习资料获取
springcloud alibaba官网
SpringCloud Alibaba
Nacos 服务注册和配置中心
Nacos 简介
为什么叫Nacos
前四个字母分别为 Naming 和 Configuration 的前两个字母,最后的s 为service
是什么
更易于构建云原生应用的动态服务发现,配置管理和服务管理平台
Nacos : Dynamic Naming And configuration services
Nacos 就是注册中心+配置中心的组合
能干嘛
替代eureka 做服务注册中心
替代 config 做服务配置中心
去那下
官网地址
各种注册中心对比
Eureka AP 模型,支持控制台,社区活跃度低
Zookeeper CP模型,不支持控制台管理,社区活跃度中
Consul CP模型,支持控制台管理,社区活跃度高
Nacos AP模型,支持控制台管理,社区活跃度高
Nacos 安装并运行
Java8+Maven 环境已经OK
从官网下载Nacos
解压并进入bin目录,启动
测试
http://192.168.247.111:8848/nacos/index.html#/login
Nacos 作为注册中心演示
官方文档
基于Nacos 的服务提供者
建 alibaba-nacos-payment-provider6001
pom
yml
主启动
业务类
controller
测试
http://192.168.247.111:8848/ 查看服务列表
启动 6001 查看 Nacos 控制台是否存在这个服务
以 6001 为例创建6002微服务,为后面测试 负载均衡
基于Nacos 的服务消费者
建 alibaba-nacos-order-consumer84
pom
yml
主启动类
业务类
config
controller
测试
http://localhost:84/order/payment/12
登录8848 查看是否注册进Nacos
服务注册中心对比
Nacos 支持AP和CP 模式的切换
C是所有节点在同一时间看到的数据是一致的,而A的定义时所有的请求都会受到响应
何时选择使用何种模式?
一般来说,如果不需要存储服务级别的信息且服务实例是通过 nacos-client 注册,并能够保持心跳上报,那么就可以选择AP模式,当前主流的服务 Springcloud 和dubbo 服务,都适用于 ap模式,AP模式为了服务的可能性而减弱了一致性,因此AP只支持注册临时实例
如果需要在服务级别编辑或者存储配置信息,那么CP是必须,K8S 服务和DNS服务则适用于CP模式,CP模式下则支持注册持久化实例,此时则支持注册持久化实例,此时则是以 Raft 协议为集群运行模式,该模式下注册实例之前必须先注册服务,如果服务不存在,则返回错误
Nacos 作为服务配置中心演示
Nacos 作为配置中心---基础配置
建 alibaba-config-nacos-client3377
pom
yml
application.yml
bootstrap.yml
为什么会有2个yml文件?
Nacos 同springcloud 一样,在项目初始化时,要保证先从配置中心进行配置拉取,拉取配置之后,才能保证项目正常运行
springboot中配置文件的加载顺序是存在优先级顺序的,bootstrap.yml 优先级高于 application.yml
主启动类
业务类
controller
在Nacos 添加配置信息
Nacos 中匹配规则
官网
Nacos 中的dataid 的组成格式及与springboot 配置文件中的匹配规则
实操
配置新增
Nacos 界面配置对应
设置DataId
${prefix}-${spring.profiles.active}.${file-extension}
prefix 默认为 spring.application.name 的值
spring.profiles.active 即为当前环境对应的 profile
file-exetension 为配置内容的数据格式,可以通过配置项 spring.cloud.nacos.config.file-extension 来配置
测试
启动前需要在nacos 客户端-配置管理-配置管理栏目下有对应的yaml 配置文件
运行3377服务配置中心
调用接口查看配置信息
http://localhost:3377/config/info
自带动态刷新
在Nacos控制台的列表修改yaml 配置文件
http://localhost:3377/config/info
Nacos 作为配置中心--分类配置
问题 多环境多项目管理
实际开发中,,通常一个系统会准备dev开发环境,test测试环境,prod 生产环境,如何保证指定环境启动时服务能正确读取到Nacos 上相应环境的配置文件
一个大型分布式微服务系统会有很多微服务子项目,每个微服务系统会有很多微服务子项目,每个微服务项目又都有相应的开发环境,测试环境,生产环境...那怎么对这些微服务于配置进行管理呢?
Nacos 的图形化管理界面
配置管理
命名空间
Namespace + Group +DataId 三者关系?
是什么
类似于Java 里面的package 名和类名,最外层的namespaces 是可以用来区分部署环境的,group 和 dataId =逻辑上区分两个目标对象
默认情况下
Namespaces =public ,group=Default_group , 默认cluster = default
案例Case
三种方案加载配置
DataId 方案
指定 spring.profile.active 和配置文件的 DataID 来使不同环境下读取不同的配置文件
默认空间+默认分组+新建dev和test两个dataid
新建dev 配置 DataID
新建test配置 DataID
通过修改3377配置文件application.yml文件的 spring.profile.active属性来进行多环境文件读取
测试
http://localhost:3377/config/info
Group 方案
通过Group 实现环境区分
新建group
DEV_GROUP 组的 nacos-config-client-info.yaml
TEST_GROUP 组的 nacos-config-client-info.yaml
在Nacos 图形化界面控制台上面新建配置文件DataID
修改3377的bootstrap.yml文件
测试
http://localhost:3377/config/info
Namespaces 方案
在Nacos 控制台建 dev 和test 命名空间
在Nacos 控制台-> 配置列表查看
修改3377的bootstrap.yml文件
测试
http://localhost:3377/config/info
Nacos 集群和持久化配置 (重要)
官网说明
Nacos 持久化配置需要我们使用mysql数据库
Nacos 持久化配置解释
Nacos 默认自带的是 derby数据库
derby 到mysql 切换配置步骤
在nocos/confi/目录下面找到 sql脚本
nacos-mysql.sql
复制文件的内容到本地mysqk 执行生成表结构
在nocos/conf/目录下面找到 application.properties
启动 Nacos ,可以看到是个全新的空记录界面,以前记录在deby
Linux 版Nacos +Mysql 生产环境配置
预计需要,1个Nginx +3个Nacos 注册中心 +1个mqsql
安装Nacos 参考之前安装
集群配置步骤
linux 服务器上mysql数据库配置
sql 脚本在 /conf/nacos-mysql.sql
docker run --restart=always --name mc_mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root -e TZ=Asia/Shanghai -d daocloud.io/library/mysql:5.7.5 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --default-time_zone='+8:00'
把脚本表生成在 mysql里面
application.properties 配置
位置 /conf/application.properteis
修改之前备份配置文件
cp application.properties application.properties.init
linux 服务器上nacos 的集群配置 cluster.conf
位置在 /conf/cluster.conf.example
cp cluster.conf.example cluster.conf
vim cluster.conf 文件内容不能写127.0.0.1 必须是 Linux 命令 hostname -i 能够识别的ip
编辑Nacos的启动脚本start.sh,使它能够接受不同的启动端口
/bin/目录下 有startup.sh
在什么地方,修改什么,如何修改
思考
平时单机版本启动都是startup.sh 即可
但是,集群启动,我们希望可以类似其它软件的shell命令,传递不同的端口号启动不同的nacos实例
命令 ./startup.sh -p 3333 表示启动端口号为 3333 的nacos 服务器实例,和上一步cluster.conf 配置一致
修改前
修改后
启动 ./startup.sh -p 5555/3333/4444
Nginx 的配置,由它做作为负载均衡器
修改nginx 的配置文件
nginx.conf
按照指定启动
进入sbin目录启动 ./nginx -c /配置文件路径
截止到此处,1个Nginx +3个Nacos 注册中心+1个mqsql
测试
http://192.168.247.111:1111/nacos/#/login
在Nacos 控制台 配置列表中添加一个DataID ,然后进 config_info表中进行查看
Nacos 集群搭建成功
测试
微服务6001 /6002 配置文件修改yml
查看nacos 控制台查看是否注册成功
Sentinel 实现熔断与限流
简介
官网
是什么
能干嘛
去那下
怎么玩
服务雪崩
服务降级
服务熔断
服务限流
Sentinel 控制台
sentinel 组件由两部分组成
前台 8080
后台
运行
java -jar xxx.jar
初始化演示工程
启动 Nacos 8848成功
建Moudle
建 alibaba-sentinel-service8401
pom
yml
主启动类
业务类
启动 sentinel 8080
启动微服务8401
启动8401 微服务后查看sentinel控制台
发现 sentinel 什么也没有
sentinel 采用的懒加载说明
执行一次访问即可
http://localhost:8401/testA
http://localhost:8401/testB
然后再查看 sentinel 控制台
流控规则
基本介绍
资源名: 唯一名称,默认请求路径
针对来源: sentinel 可以针对调用者进行限流,添写微服务名,默认为default(不区分来源)
阈值类型/单机阈值
QPS (每秒钟的请求总数): 当调用者该QPS达到阈值的时候,进行限流
线程数: 当调用该api的线程数到达阈值的时候,进行限流
是否集群: 不需要集群
流控模式
直接: api达到限流条件时,直接限流
关联: 当关联的资源到达阈值时,就限流自己
链路: 只记录指定链路上的流量(指定资源从入口资源进来的流量,如果到达阈值,就进行限流)[api级别的针对来源]
流控模式
快速失败: 直接失败,抛异常
Warm Up: 根据 codeFactor (冷加载因子,默认3)的值,从阈值/coderFactor ,经过预热时长,才达到设置的QPS阈值
排队等待: 均速排队,让请求以均速的速度通过,阈值类型必须设置为QPS ,否则无效
流控模式
直接(默认)
直接 ->快速失败
表示1秒内查询1次是ok,若超过1次就直接快速失败,报默认错误
测试
快速点击 http://localhost:8401/testA
出现 Blocked by Sentinel (flow limiting)
思考?
直接调用默认报错信息,技术方面OK,是否应该有我们自己处理的后续处理?
是否有fallback页面
线程数是当多个线程超过阈值的时候报错误和QPS每秒请求数超过阈值
关联
是什么
当关联的资源到达阈值时,就限流自己
当与A 关联的资源B达到阈值后,就限流A自己
B惹事了,A挂了
配置
postmain 模拟并发密集访问 testB
1),首先使用postmain 访问地址测试是否成功
2), SaveAs 保存在 Controllers 里面,如果Controllers 没有文件则新建
3), 点击 Controllers 的URL 然后点击Ran
4),
大批量线程高并发访问B,导致A失效了
运行后发现A挂了
http://localhost:8401/testA
Blocked by Sentinel (flow limiting)
链路
入口资源 ==Sentinel 控制台的簇点链路的
频繁访问A接口,超出访问限制时,快速失败
流控效果
直接->快速失败(默认的流控处理)
直接失败,抛出异常 Blocked by Sentinel (flow limiting)
源码 com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController
预热
公式: 阈值除以coldFactor (默认值为3),经过预热时长后才会达到阈值
官网
默认 coldFactor为3,即请求QPS从threshold /3开始,经预热时长逐渐升至设定的QPS值
限流冷启动
源码
com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController
WarmUp 配置
多次点击 http://localhost:8401/testA
应用场景
如秒杀系统在开启的瞬间,会有很多流量上来,很可能把系统打死,预热方式就是为了保护系统,可慢慢的把流量放进来,慢慢的把阈值增长到设置的阈值
排队等候
匀速排队,阈值必须设置为 QPS
源码 com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController
测试
降级规则
基本介绍
进一步说明
Sentinel 熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对于这个资源的调用进行限制,让请求快速失败,避免应用其它的资源而导致级联错误
当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出 DegreadeException)
sentinel 的短路器是没有半开状态的
半开的状态系统自动去监测是否请求有异常,没有异常就关闭断路器恢复使用,有异常则继续打开断路器不可用,
降级策略实战
慢调用比例
配置
controller
jmeter测试
结果
http://localhost:8401/testC
出现 Blocked by Sentinel (flow limiting)
异常比例
业务类
controller
jmeter
结论
单独访问一次,必然来一次错误一次,(int age=10/0) 调一次错误一次
开启jmeter 后,直接高并发发送请求,多次调用达到我们的配置条件了,断路器开启(保险丝跳闸),微服务不可用了,不在报错error而是服务降级错误
异常数
异常数是按分钟统计的
代码
controller
配置
http://localhost:8401/testC
第一次访问绝对报错,因为除数不能为0,我们看到error窗口,但是访问5次这个接口,进入熔断降级
热点key限流
承上启下复习start
之前case,限流出现问题,都是用sentinel 系统默认提示 Blocked by Sentinel (flow limiting)
我们能不能自定义?类似hystrix 某个方法出现问题了,就找对应的兜底方法? 从@HystrixCommand 到 @SentinelResource
源码
com.alibaba.csp.sentinel.slots.block.BlockException
代码
controller
配置
@SentinelResource(value = "hotkey", blockHandler = "deal_hotkey")
方法 hotkey里面第一个参数只要QPS超过每秒1次,马上降级处理,用我们自定义的方法
如果 blockHandler 不写,则处出现ErrorPage页面
测试 http://localhost:8401/hotkey?p2=b
http://localhost:8401/hotkey?p1=a
http://localhost:8401/hotkey?p1=a&p2=b
参数例外项
上述案例演示了第一个参数p1,当QPS超过1秒1次点击后马上被限流
特殊情况
普通: 超过1秒钟之后,达到阈值1后马上被限流
我们期望p1参数当它是某个特殊值时,它的限流和平时不一样
特例 : 假设p1的值等于5,它的阈值可以达到200,或者其它值
配置
测试
http://localhost:8401/hotkey?p1=b --被限流
http://localhost:8401/hotkey?p1=5 ---例外项除外
前提条件
参数必须是基本数据类型或者String
其它
controller
@SentinelResource 处理的是控制台配置的违规情况,有blockHandler 方法配置兜底
RuntimeException int age=10/0; 这个是java运行时报出的运行时异常 RuntimeException ,@SentinelResource不管
总结: @SentinelResource 主管配置出错,运行时出错走异常
系统规则
@SentinelResource
按资源名称限流+后续处理
启动Nacos
启动Sentinel
controller
配置规则
测试
http://localhost:8401/byResource
1秒点击1次OK
超过上述,疯狂点击,返回自己定义的限流处理信息,限流发生
额外问题
此时关闭微服务8401,查看 Sentinel 控制台流控规则消息了???
临时存储的
按照URL地址限流+后续处理
通过访问URL来限流,会返回Sentinel 自带默认的限流处理信息
controller
配置
子主题
测试
http://localhost:8401/byurl
Blocked by Sentinel (flow limiting)
上面兜底方案面临的问题
1),系统默认的,没有体现我们自己的业务要求
2),依照现有条件,我们自定义的处理方法有和业务代码耦合在一起,不直观
3),每个业务方法都添加一个兜底的,那代码膨胀加剧
4), 全局同一的处理方法没有体现
客户自定义限流处理逻辑
自定义handler
配置
controller
测试
http://localhost:8401/customer
更多注解属性说明
所有代码都要用 try-catch-finally 方式进行处理
sentinel 三个核心api
SphU 定义资源
Tracer 定义统计
ContextUtil 定义上下文
服务熔断功能
sentinel 整合 ribbon +openFeign +fallback
Ribbon系列
启动nacos 和sentinel
建微服务5001和5002
建 alibaba-provider-payment5001
pom
yml
主启动类
业务类
service
controller
测试
http://localhost:5001/payment/1
http://localhost:5002/payment/1
建微服务消费者 86
建 alibaba-consumer-order-86
pom
yml
主启动
controller
业务类
修改后请重启微服务
热部署对Java代码级生效及时
对 @SentinelResource 注解内属性,有时效果不好
目的
fallback 管运行时异常
blockHandler 管配置违规
测试地址
http://localhost:86/order/fallback/1
没有任何配置
给客户显示error页面,不友好
只配置 fallback
controller
测试
http://localhost:86/order/fallback/5
只配置blockHandler
controller
注意: blockHandler 降级方法不能缺少BlockException 参数
测试
http://localhost:86/order/fallback/6
fallback 和 blockHandler 都配置
controller
测试
http://localhost:86/order/fallback/4
若 fallback 和blockHandler 都进行了配置,则被限流而抛出 BlockException 时只会进入 blockHandler 处理逻辑
忽略属性...
exceptionsToIgnore 忽略方法的一个或多个异常,手工进行处理
controller
测试
http://localhost:86/order/fallback/4
结论: 没有走兜底的 fallback方法,而是报error异常,说明 直接把那个异常移除了
服务熔断功能
修改86消费者
pom
yml
主启动
业务类
service
熔断类
测试
http://localhost:86/order/payment/6
这时关闭微服务5001/5002,再次请求这个接口,发现出现的是熔断的数据
规则持久化
是什么
一旦我们重启应用,sentinel 规则消息,生产环境需要将配置规则进行持久化配置
怎么玩
将限流配置规则持久化进Nacos 保存,只刷新8401某个rest地址,sentinel 控制台的流控规则就能看到,只要Nacos里面的配置不删除,针对8401上sentinel 上的流控规则持续有效
修改微服务8401
pom
yml
nacos控制台添加配置文件
内容
- resource:资源名称
- limitApp:来源应用
- grade:阀值类型,0:线程数,1:QPS
- count:单机阀值
- strategy:流控模式,0:直接,1:关联,2:链路
- controlBehavior:流控效果,0:快速失败,1:warmUp,2:排队等待
- clusterMode:是否集群
测试
http://localhost:8401/byurl
然后登录 sentinel 控制台查看是否有流控规则
Seata 处理分布式事务
分布式事务问题
分布式前
单机单库没这个问题
从 1: 1 ->1:N -> N:N
分布式后
单体应用被拆分成微服务应用,原来的三个模块被拆分为三个独立应用,分别使用 三个独立的数据源
业务操作需要调用三个服务来完成,此时每个服务内部的数据一致性由本地事务来保证,但是全局的数据一致性问题就没办法保证
Seata 简介
是什么
Seata是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务
能干嘛
一个典型的分布式事务过程
分布式事务处理过程--ID+3组件模型
Transaction ID XID (全局唯一的事物ID)
3组件概念
TC--Transaction Coordinator--事务协调者
维护全局和分支事务的状态,驱动全局事务提交或回滚。
TM -- Transaction Manager--事务管理器
定义全局事务的范围:开始全局事务、提交或回滚全局事务。
RM --Resource Manager--资源管理器
管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
处理过程
1), TM向TC申请一个全局事务,全局事务创建成功并生成一个全局唯一的XID
2), XID 在微服务调用链路的上下文中传播
3),RM向TC注册分支事务,将其纳入XID对应全局事务的管辖
4), TM向TC发起针对XID的全局提交或回滚决议
5),TC调度XID下管辖的全部分支事务完成提交或回滚请求
在哪下
怎么玩
本地@Transctional
全局 @GlobalTransactional
Linux Seata -Server 安装
放入/opt/目录下,解压
cp file.conf file.conf.default
修改 file.conf文件
service模块
vgroup_mapping.my_test_tx_group = "qwe_tx_group"
store模块
在IP的mysql里建立 seata数据库
在seta数据库里面建表
cp registry.conf registry.conf.default
修改conf/registry.conf文件
先启动Nacos 端口号8848
在bin目录启动Seata
./seata-server.sh
订单/库存/账户业务数据库准备
以下演示都需要先启动Nacos 后启动Seata ,保证两个都OK
分布式事务业务说明
这里我们会创建3个服务,一个订单服务,一个库存服务,一个账户服务
当用户下单时,会在订单服务中创建一个订单,然后通过远程调用库存服务来扣减下单商品的库存,再通过远程调用账户服务来扣减账号里面的余额,最后订单服务中修改订单状态,订单状态为已完成
该操作跨越三个数据库,有2次远程调用,很明显有分布式事务问题
下订单---扣库存---减余额
创建业务数据库
seata_order : 存储订单的数据库
seata_storage : 存储库存的数据库
seata_account : 存储账户信息的数据库
sql
按照上述3库分别建立对应业务表
seata_order库下建t_order表
seata_storage库下建t_storage表
seata_account库下建t_account表
按照上述3库分别建对应的回复你日志表
订单---库存---账号 3个库下都需要建立各自的回滚日志表
sql
订单/库存/账户业务微服务准备
业务需求: 下订单---减库存---扣余额---改状态
Test
正常下单
没加 @GlobalTransaction
超时异常
加了没加 @GlobalTransaction
补充
Seata
2019年1月份蚂蚁金服和阿里巴巴共同开源的分布式事务解决方案
Simple Extensible Autonoumous Transaction Architecture ,简单可扩展自治事务框架
2020起始,参加工作后用1.0以后版本
再看看TC/TM/RM
执行流程
TM开启分布式事务,(TM向TC注册全局事务记录)
按业务场景,编排数据库,服务等事务内资源(RM向TC汇报资源准备状态)
TM 结束分布式事务,事务一阶段结束(TM通知TC提交/回滚分布式事务)
TC 汇总事务信息,决定分布式事务是提交还是回滚
TC通知所有RM提交/回滚资源,事务二阶段结束
AT 模式如何做到对业务的无侵入
分布式雪花算法
集群高并发情况下如何保证分布式唯一全局ID生成?
问题
为什么需要分布式全局唯一ID以及分布式ID的业务需求
在复杂分布式系统中,往往需要对大量的数据和消息进行唯一标识如美团点评金融,支付,餐饮,酒店等产品的系统中数据日益增长,对数据分库分表后需要有一个唯一ID来表示一条数据或消息
特别一点如订单,骑手,优惠卷也都需要一个唯一ID做标识
此时一个能够能生成全局唯一ID的系统时非常必要的
ID的规则部分硬性要求
全局唯一
不能出现重复的ID号,既然是唯一标识,这是最基本的要求
趋势递增
在mysql的Innodb引擎使用的是聚簇索引,由于多数RDBMS使用Btree的数据结构来存储索引数据,在主键的选择上面我们尽量使用有序的主键保证写入性能
单调递增
尽量保证下一个ID一定大于上一个ID,例如事务版本号,IM增量信息,排序等特殊需求
信息安全
如果ID是连续的,恶意用户的扒取就非常容易了,直接按照顺序下载指定URL即可
如果是订单号就更危险了,竞对可以直接知道我们一天的单量
所以在一些应用场景下,需要ID无规则不规则,让竞争对手不好猜
含时间戳
这样就能在开发中快速了解分布式ID的生成时间
ID号生成系统的可用性要求
高可用
发一个获取分布式ID的请求,服务器就保证99.999%的情况下给我创建一个唯一的分布式ID
低延迟
发一个分布式ID的请求,服务器就要快速
搞QPS
假如并发一口气10万个创建分布式ID请求同时杀过来,服务器要顶的住且一下子成功创建10万个分布式唯一ID
一般通用方案
UUID
UUID的标准式包含32个16进制数字,以连子号分为5段,形式为8-4-4-4-12的36个字符
性能非常高,本地生成,没有网络消耗
为什么无序的UUID会导致入库性能变差呢?
无序,无法预测它生成顺序,不能生成递增有序的数字
主键,ID作为主键时在特定的环境会存在一些问题
索引,B+tree索引的分裂
数据库自增主键
单机可以
在分布式里面,数据库自增ID机制的主要原理是 数据库自增ID和mysql 的 replace into 实现的
replace into 的含义是插入一条记录,如果表中唯一索引的值遇到冲突,则替换老数据
集群分布式不可以
数据库自增ID机制适合分布式ID吗? 答案是不合适
1),系统水平扩展比较困难,比如定义好了步长和机器台数后,如果要添加新机器该怎么做?水平扩展扩展方案比较难
2),数据库压力大,每次获取ID都得读写一次数据库非常影响性能
基于Redis生成全局ID策略
因为Redis 是单线程的天生保证原子性,可以使用原子操作Incr 和 incrby 来实现
集群分布式
在redis 集群情况下,同样和mysql一样需要设置不同的正常步长,同时key一定要设置有效期
可以使用redis集群来获取更高的吞吐量
例如 一个集群有5台redis,可以初始化每台redis值分别是1,2,3,4,5 ,然后步长为5
A: 1,6,11,16,21
B:2,7,12,17,22
C:3,8,13,18,23
D:4,9,14,19,24
E: 5,10,15,20,25
B:2,7,12,17,22
C:3,8,13,18,23
D:4,9,14,19,24
E: 5,10,15,20,25
配置麻烦,维护redis集群
snowflake
Twitter 的分布式自增ID算法 snowflake
概述
twitter 的分布式雪花算法snowflake ,经测试 每秒产生26万个自增可排序的ID
1), twitter 的snowflake 生成ID能够按照时间有序生成
2),snowflake 算法生成id的结果是一个64bit大小整数,为一个Long型(转换成字符串长度最多为19位)
3),分布式系统内不会产生ID碰撞(datacenter 和 workerld),并且效率高
结构
在Java中64位的整数是long类型,所以在snowflake 算法生成id就是long来存储
源码
工程落地经验
糊涂工具包
springboot 整合雪花算法
优缺点
优点
毫秒数在高位,自增列在低位,整个ID都是趋势递增的
不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成ID的性能也是非常高
可以根据自身业务特性分配bit位,非常灵活
缺点
依赖机器时钟,如果机器时钟回拨,会导致重复ID生成
在单机上是递增的,但是由于设计到分布式系统环境,每台机器上的时钟不可能完全相同,有时候出现不是全局递增的情况(此缺点可以认为无所谓,一般分布式ID只要求趋势递增,并不会严格要求递增,90%的需求都要求趋势递增)
其它补充
0 条评论
下一页