SpringCloud微服务知识点
一、认识微服务
1.单体架构
单体架构:将业务的所有功能集中在一个项目中开发,打成一个包部署。
优点:
- 架构简单
- 部署成本低
缺点:
- 团队协作成本高:代码与代码之间有耦合,功能之间产生冲突,代码越复杂,解决冲突成本越高
- 系统发布效率低:随着功能越来越多,代码越来越多,代码打包的时间越来越长,在编译上花费很多时间
- 系统可用性差:功能打包在一块部署在一个Tomcat上,高并发情况下,tomcat资源可能被耗尽,造成某些功能使用卡顿,影响用户体验。不适合大型的、复杂的、用户量大的系统。
总结:
单体架构适合开发功能相对简单,规模较小的项目。
2.微服务架构
微服务架构,是服务化思想指导下的一套最佳实践架构方案。服务化,就是把单体架构中的功能模块拆分为多个独立项目。
粒度小:按照业务划分,每个模块负责自身的功能,降低代码耦合(遵守单一职责)
团队自治:把一大型项目拆分成N个小项目,每个小项目有专门团队开发,降低代码冲突
服务自治:每个功能模块分别打包部署,相当于每个模块都是一个小型服务-微服务。后期编译,功能更新,只针对相应功能模块即可。每个微服务都有自己的数据库-数据隔离,服务之间不会受到高并发影响。
3.SpringCloud
二、微服务拆分
1.熟悉单体架构
2.服务拆分原则
什么时候拆分?
创业型项目:先采用单体架构,快速开发,快速试错。随着规模扩大,逐
渐拆分。
确定的大型项目:资金充足,目标明确,可以直接选择微服务架构,避免
后续拆分的麻烦。
怎么拆分?
从拆分目标来说,要做到:
高内聚:每个微服务的职责要尽量单一,包含的业务相互关联度高、完整度高。
低耦合:每个微服务的功能要相对独立,尽量减少对其它微服务的依赖。
从拆分方式来说,一般包含两种方式:
纵向拆分:按照业务模块来拆分
横向拆分:抽取公共服务(重复的功能抽取),提高复用性
3.工程结构
工程结构有两种:
独立Project
Maven聚合
4.拆分服务实战
拆分商品服务---item-service
选中工程 hmall 快捷键 alt+insert 快速创建new module
模块创建好后,开始添加修改文件。从pom、yaml、启动文件application、domain、mapper、service、controller依次修改。
修改完成后,按快捷键 alt+8 快捷调出services窗口,此时商品服务的启动文件可能还未加载进来,刷新Maven即可,如下图所示
或者手动添加
选中一个启动文件,点击Copy Configuration
进入配置后,在1处修改启动文件名,在2处找到需要加载的启动项位置。
点击2处后弹出窗口,如图,选中要加载的application,应用即可。
之后启动商品服务,输入网址http://localhost:8081/doc.html,查看生成的swagger接口文档并测试其接口是否可用。
拆分购物车服务---cart-service
拆分购物车服务步骤和商品服务相同。只是购物车服务涉及远程调用其他模块的代码,暂时注释即可。
拆分作业
5.远程调用
查询购物车时,不仅要查购物车信息,还要查购物车中商品最新价格和状态信息,与购物车商品快照进行对比
产生的问题:现在两个功能分别拆分到两个独立微服务中,代码是隔离开的,且每个服务都有对应的独立数据库,数据也是隔离的,之前功能在一块,直接调用即可,现在不行。
解决方法:通过服务之间的网络请求获取数据。
拆分后,某些数据在不同服务,无法直接调用本地方法查询数据
利用RestTemplate发送Http请求,实现远程调用
在CartApplication注入RestTemplate
成员变量注入方式:
1.Autowired方式注入,但idea不推荐使用
2.构造方法注入,但如果成员变量很多,会导致构造函数很长
3.使用lombok
如果使用AllArgsConstructor,会把所有成员变量都注入,这不合适
可以使用RequiredArgsConstructor,在需要的成员变量前加final关键字,此时该变量必须被赋值
在代码中使用
三、服务治理
服务远程调用存在的问题
需要写死服务的请求路径,但在高并发情况下,可能为了缓解服务器压力,将商品服务部署到更多服务器,也可能遇到服务器挂了的情况,此时请求就不能写死。
1.注册中心原理
服务提供者微服务启动后,在注册中心注册自己的信息;服务调用者在注册中心订阅服务提供者的信息,如果有多个,使用负载均衡选择要调用服务的地址。如果服务提供者的服务有挂掉的,注册中心可以通过心跳续约感知,将挂掉的服务剔除并推送变更给订阅者。
2.Nacos注册中心
在linux中用docker部署nacos。
1.在mysql中创建保存nacos数据的数据库
2.下载nacos.tar,使用docker load -i nacos.tar将一个 Docker 镜像从本地的 .tar 文件加载到 Docker 的本地镜像库中
3.后台运行nacos
docker run -d \
--name nacos \
--env-file ./nacos/custom.env \
-p 8848:8848 \
-p 9848:9848 \
-p 9849:9849 \
--restart=always \
nacos/nacos-server:v2.1.0-slim
docker run -d
-d:以后台(detached)模式运行容器。
--name nacos
为容器指定一个名称 nacos,方便后续管理和操作。
--env-file ./nacos/custom.env
从 ./nacos/custom.env 文件中加载环境变量,用于配置 Nacos 服务。
-p 8848:8848
将主机的 8848 端口映射到容器的 8848 端口。Nacos 的默认 HTTP 服务端口是 8848。
-p 9848:9848
将主机的 9848 端口映射到容器的 9848 端口。这是 Nacos 2.x 版本新增的 gRPC 端口。
-p 9849:9849
将主机的 9849 端口映射到容器的 9849 端口。这是 Nacos 2.x 版本新增的 gRPC 端口,用于集群通信。
--restart=always
设置容器的重启策略为 always,即无论容器因何原因退出,Docker 都会自动重启该容器。
nacos/nacos-server:v2.1.0-slim
指定要运行的 Docker 镜像为 nacos/nacos-server:v2.1.0-slim,这是 Nacos 2.1.0 的轻量版镜像。
4.访问8848端口登录nacos
3.服务注册
按照步骤配置后,如果要实现多实例,如下操作:
手动配置,增加一个VM option,配置一个不同的端口 -Dserver.port=8083,这个优先级高于yaml。
在nacos如图
4.服务发现
使用示例
测试后可以发现多实例都可以被调用,服务宕机和服务新启动都不影响使用。
四、OpenFeign
只需定义一个接口。底层就会实现跨服务远程调用,服务拉取,负载均衡,发请求等等。
1.快速入门
如图,服务发现的代码实现过于复杂,仅仅获取uri要写很多代码
早期负载均衡组件使用Ribbon
使用示例
2.连接池
性能调优
连接池比起HttpURLConnection不带连接池性能是怎么提升的?
3.最佳实践
产生问题:如果有多个微服务调用同一个服务,都要写一个相同的client接口,会造成代码重复编写;且功能如果发生变化,也要修改对应client,耦合度较高。
第一种方案:
所有要被调用的服务,由其自身创建维护子模块提供其他微服务使用。
其他服务要使用直接引入pom坐标即可。
缺点:使项目结构变得更复杂。
第二种方案:
将提供调用的服务单独提取成一个模块,在这里统一管理所有向外暴露的服务客户端和公共访问的DTO,其他服务直接引用这个api模块即可。
优点:解决了代码通用性
缺点:代码耦合度增加,不同微服务要放在一起维护
不同方案的适用场景:
如果微服务本身是聚合的,有通用模块,可以使用方案二,提高代码通用性,使用方便,但增加了耦合度。
如果微服务各自高度独立,采用project形式,可以使用方案一,在每个project下放dto、client、业务代码等模块。代码耦合度更低。
4.日志输出
一般在调试时开启OpenFeign的日志输出,日常开发时关闭。
五、网关
1.什么是网关
单体架构时,前端只需请求8080端口就可拿到任意数据。现在项目被拆成多个服务,端口也不相同,且到线上部署时,ip和端口也可能发生改变,此时前端该怎么办呢?(服务地址过多,前端不知道请求谁)
在单体项目中,只要用户登录和校验成功,所有业务都能拿到用户的登录信息完成业务。现在每个服务都可能需要登录信息,如果各自做登录校验,不仅麻烦,因为要给服务发送密钥,还有密钥泄露风险。
对外只暴露网关接口,网关将后端的微服务隐藏起来,对于前端来说,和之前的单体架构没有不同。
2.快速入门
路由:网关根据请求来判断使用哪个微服务,这个过程叫做网关路由
转发:知道是哪个服务后,去注册中心拉取这个服务的实例列表,再根据负载均衡从中挑选一个实例,再把这个请求发给这个服务实例
主要做2件事:
1.搭建网关
2.配置网关
多controller配置yaml
3.路由属性
路由断言
路由过滤器
使用示例
过滤器对所有路由都生效的配置
4.网关登录校验
将JWT校验功能放在网关里做,当请求发到网关时统一进行,校验完成后再进行路由转发
ThreadLocal是部署在一个Tomcat,实现线程之间共享用户信息,而微服务部署在不同的tomcat上,所以不能用ThreadLocal。
由于之前网关的功能都是通过底层自己实现,如何完成转发前校验,需要分析底层源码。
自定义过滤器和配置执行顺序
网关的过滤器是非阻塞的,filter的执行逻辑用于pre过程,执行完后拿到Mono对象,这里用回调函数执行post的流程。
使用 Ctrl+h 查看所有接口实现
需要确保自定义过滤器执行顺序在NettyRoutingFilter之前,可以使用Order实现
下图实现的只是简单的无参路由过滤器
同时使用全局过滤器和路由过滤器怎么保证执行顺序?
由上图可知,路由过滤器使用了GatewayFilter匿名内部类实现过滤逻辑,但匿名内部类无法实现Order,此时可以使用GatewayFilter的装饰器OrderedGatewayFilter。
下图是带参的路由过滤器实现逻辑
实现登录校验
网关传递用户
在网关的过滤器中将用户信息保存到请求头
而微服务想要使用用户信息,如果每次都要从请求头中拿,会造成代码冗余,我们可以在转发的请求到达controller之前,通过拦截器将用户信息保存到ThreadLocal中,在要用时直接ThreadLocal获取即可。
定义拦截器->配置拦截器
由于拦截器定义在common模块下,其他微服务扫描不到,需要使用springboot自动装配原理,在common模块的resources目录的META-INF文件夹的spring.factories写入配置文件的位置
希望配置类只在微服务生效,不在网关生效,因为网关没有springmvc相关类,可以通过条件注解实现
OpenFeign传递用户
六、配置管理
将配置信息放在配置管理服务里,配置管理服务监听变化的配置信息推送到相应的服务中,这样配置信息变更后无需重启服务就可生效。
配置共享
拉取共享配置
微服务启动后,首先是拉取nacos配置,完成ApplicationContext初始化,然后再访问application.yml;但是先前的application.yml文件包含了nacos配置信息,而要想拉取nacos配置必须有这些信息,所以引入了bootstrap.yml专门放置nacos配置信息,这样先访问这个文件再拉取共享配置,最后合并application的配置信息完成ApplicationContext初始化。
配置热更新
动态路由
七、服务保护
雪崩问题
如果商品服务响应速度慢或挂了,购物车服务调用商品服务一直拿不到响应结果会一直占用资源。当高并发情况下,购物车服务的tomat资源耗尽导致不可用。此时发生级联问题,与这些服务相关的整个链路都不可用。
雪崩问题产生的原因是什么?
- 微服务相互调用,服务提供者出现故障或阻塞。
- 服务调用者没有做好异常处理,导致自身故障。
- 调用链中的所有服务级联失败,导致整个集群故障
解决问题的思路有哪些?
尽量避免服务出现故障或阻塞。
- 保证代码的健壮性:
- 保证网络畅通;
- 能应对较高的并发请求;
服务调用者做好远程调用异常的后备方案,避免故障扩散
雪崩解决方案
请求限流
线程隔离
服务熔断
解决雪崩问题的常见方案有哪些?
请求限流:限制流量在服务可以处理的范围,避免因突发流量而故障
线程隔离:控制业务可用的线程数量,将故障隔离在一定范围
服务熔断:将异常比例过高的接口断开,拒绝所有请求,直接走
fallback
失败处理:定义fallback逻辑,让业务失败时不再抛出异常,而是返
回默认数据或友好提示
服务保护技术
Sentinel快速入门
Sentinal安装和使用
1.下载jar包
2.将jar包放到非中文路径下,通过下列命令启动
java -Dserver.port=8090 -Dcsp.sentinel.dashboard.server=localhost:8090 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar
3.访问端口登录,账号密码都是sentinel
4.在要使用的服务中引入依赖
<!--sentinel-->
<dependency><groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
5.在application.yml文件配置
spring:cloud: sentinel:transport:dashboard: localhost:8090
sentinel请求限流
限制每秒钟的QPS最多为6
使用Jmeter模拟测试
sentinel线程隔离
Fallback
对Feign远程调用的接口进行流控
sentinel服务熔断
八、分布式事务
单体项目中数据库业务操作都在一个service中,一个事务,满足ACID特性。
微服务项目中业务操作被拆分到多个微服务中,每个微服务都有自己的数据库和tomcat,如下图,当下单时,会有下面三个服务操作,当库存不足导致库存扣减失败,事务没有回滚,这个时候ACID特性就不能保证。
初识seata
部署TC服务
1.准备数据库表
用于持久化存储数据
全局表、分支表、两个锁
2.配置文件
3.docker部署
微服务集成Seata
1.引入Seata依赖
2.application添加配置
XA模式
1.XA模式是什么
分支执行完sql不提交,数据库资源不释放,其他人无法访问这个资源,等待所有分支执行完再提交/回滚释放资源。
2.XA模式的优缺点
3.如何使用XA模式
AT模式
如果直接提交,显然就没有机会回滚,可能出现数据库不一致的情况。
解决方法:执行业务sql前记录快照undolog。
优点:解决了XA模式资源锁定周期过长的缺陷
缺点:中间会出现短暂的不一致,但最终执行完undolog会一致