SpringCloud
Spring Cloud是一种微服务开发的解决方案
Spring Cloud Alibaba同理
一般微服务项目需要有:服务发现注册(Nacos) 、配置中心(Nacos) 、消息总线(openfeign远程调用或者消息队列)、网关(GateWay) 、负载均衡(Ribbon) 、断路器 (Hytrix,sentinel)、全链路监控(SkyWalking、Grafana) 等功能
常见的分布式场景: 分布式Id(Redis/雪花算法),分布式事务(Seata/RocketMQ),分布式限流(sentinel),分布式任务调度(xxl-job,es-job)
微服务架构**特点**
(1)测试、部署问题
每个微服务组件都是简单灵活的,能够独立部署。不像单体系统,需要一个庞大的应用服务器来支援。
(2)伸缩性
微服务之间是松耦合,微服务内部是高内聚,每个微服务很容易按需扩展。水平扩展只要按服务进行扩展即可。
(3)可靠性
不会因为某个bug,而导致整个系统宕机。因为服务之间是松耦合的关系,不是强依赖。
(4)独立开发
每个微服务可以由不同的团队、不同语言独立开发。同时只需要关注服务本身和服务上游服务下游,不需要理解整个复杂的项目架构
微服务架构**带来的问题**
(1)运维成本过高,部署物数量多、监控进程多导致整体运维复杂度提升。
(2)分布式系统的复杂性(本来就一个系统,把他拆成多个服务,就会引发,网络不稳定延迟、服务容错性差、服务的负载均衡等等)
(3)分布式事务(微服务开发,带来的难题就是分布式事务的处理,关于分布式事务业界也有很多处理的方法)
实际我们项目中只需要:会拆分服务,会用gateway,openfeign,nacos,就能说是一个微服务项目了
SpringCloud Gateway
配置路由
uri通常都是目标服务的路由地址 比如:http://localhost:8001
断言-Path用来匹配请求URL,以/auth/**开头的url会被匹配
filters:过滤器:后面是过滤的规则 stripprefix指转发之后忽略/auth
通过服务名实现**动态路由**
默认情况下Gatway会根据注册中心Nacos 注册的服务列表, 以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能
lb://后面跟上的是在Nacos注册的服务名
lb用了全局过滤器LoadBalancerClientFilter,会转发请求到对应服务,并且配置了负载均衡策略
Nacos是支持负载均衡的,当一个服务有两个实例,会启动负载均衡,默认策略是轮询
网关**是什么**
一般情况下,网关可以为各个服务提供请求转发、安全认证(身份/权限认证)、流量控制、负载均衡、降级熔断、接口文档等功能,这样单个服务就不需要重复实现这些功能了
网关主要做了两件事情:请求转发请求过滤。
网关**高可用**
我们需要保障网关服务的高可用,避免单点风险。
如下图所示,网关服务外层通过 Nginx(其他负载均衡设备/软件也行) 进⾏负载转发以达到⾼可⽤。Nginx 在部署的时候,尽量也要考虑高可用,避免单点风险。
什么是springcloudgateway
为了提升网关的性能,Spring Cloud Gateway 基于 Spring WebFlux 。Spring WebFlux 使用 Reactor 库来实现响应式编程模型,底层基于 Netty 实现同步非阻塞的 I/O。
Spring Cloud Gateway 不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/指标,限流。
流程
具体的流程分析:
路由判断:客户端的请求到达网关后,先经过 Gateway Handler Mapping 处理,这里面会做断言(Predicate)判断,看下符合哪个路由规则,这个路由映射后端的某个服务。
请求过滤:然后请求到达 Gateway Web Handler,这里面有很多过滤器,组成过滤器链(Filter Chain),这些过滤器可以对请求进行拦截和修改,比如添加请求头、参数校验等等,有点像净化污水。然后将请求转发到实际的后端服务。这些过滤器逻辑上可以称作 Pre-Filters,Pre 可以理解为“在...之前”。
服务处理:后端服务会对请求进行处理。
响应过滤:后端处理完结果后,返回给 Gateway 的过滤器再次做处理,逻辑上可以称作 Post-Filters,Post 可以理解为“在...之后”。
响应返回:响应经过过滤处理后,返回给客户端。
总结:客户端的请求先通过匹配规则找到合适的路由,就能映射到具体的服务。然后请求经过过滤器处理后转发给具体的服务,服务处理后,再次经过过滤器处理,最后返回给客户端
核心概念
断言
对一个表达式进行 if 判断,结果为真或假,如果为真则做这件事,否则做那件事。
在 Gateway 中,如果客户端发送的请求满足了断言的条件,则映射到指定的路由器,就能转发到指定的服务上进行处理。
路由
一对多:一个路由规则可以包含多个断言。
同时满足:如果一个路由规则中有多个断言,则需要同时满足才能匹配。如上图中路由 Route2 配置了两个断言,客户端发送的请求必须同时满足这两个断言,才能匹配路由 Route2。
第一个匹配成功:如果一个请求可以匹配多个路由,则映射第一个匹配成功的路由
过滤器
过滤器 Filter 按照请求和响应可以分为两种:
Pre 类型:在请求被转发到微服务之前,对请求进行拦截和修改,例如参数校验、权限校验、流量监控、日志输出以及协议转换等操作。
Post 类型:微服务处理完请求后,返回响应给网关,网关可以再次进行处理,例如修改响应内容或响应头、日志输出、流量监控等。
另外一种分类是按照过滤器 Filter 作用的范围进行划分:
GatewayFilter:局部过滤器,应用在单个路由或一组路由上的过滤器。标红色表示比较常用的过滤器。
GlobalFilter:全局过滤器,应用在所有路由上的过滤器
限流
Spring Cloud Gateway 自带了限流过滤器,对应的接口是 RateLimiter
,RateLimiter
接口只有一个实现类 RedisRateLimiter
(基于 Redis + Lua 实现的限流),提供的限流功能比较简易且不易使用。
Spring Cloud Gateway 可以结合 Sentinel 实现更强大的网关流量控制。
全局异常处理
在 SpringBoot 项目中,我们捕获全局异常只需要在项目中配置 @RestControllerAdvice
和 @ExceptionHandler
就可以了。不过,这种方式在 Spring Cloud Gateway 下不适用。
Spring Cloud Gateway 提供了多种全局处理的方式,比较常用的一种是实现ErrorWebExceptionHandler
并重写其中的handle
方法。
@Order(-1)
@Component
@RequiredArgsConstructor
public class GlobalErrorWebExceptionHandler implements ErrorWebExceptionHandler {
private final ObjectMapper objectMapper;
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
// ...
}
}
PS:网关对外网开发,其他服务只能够内网调用如何,不太行,因为这样就不能分布式了
2.要想防止绕过网关的恶意请求,可以在各个服务带上一个加密过后的静态标识,转发过来的请求必须携带那个静态表,来表示自己是否是经过网关的请求,各服务在 自己的过滤器解密比对如果正确就说明是经过网关,可以放行 (也可以网关+AOP)
Nacos
安装部署
1.linux tar-zxvf下载安装包
2.创建nacos数据库
手动去创建nacos数据库,并执行官网的sql脚本建库建表
3.修改配置文件application.properties,添加mysql数据源
修改startup.sh文件,配置jdk目录
在2.2.0.1版本后,社区发布版本将移除以文档如下值作为默认值,需要自行填充,否则无法启动节点。
nacos.core.auth.plugin.nacos.token.secret.key=VGhpc0lzTXlDdXN0b21TZWNyZXRLZXkwMTIzNDU2Nzg=
给这个属性加上base64的密钥
4.启动nacos
sh /xxx/xxx/xxx/bin/startup.sh -m standalone
电梯达梦适配:
nacos 达梦 dameng 数据源 插件 开发(不用改源码,仅扩展插件包) - 掘金 (juejin.cn)
更改application.properties数据源为dm
向dm中找个表空间插入达梦建表语句
然后把插件放nacos-server包里
Nacos 2.2.1 部署
1.配置文件
nacos/conf/application.properties更改数据库连接
数据库就是第三步数据库准备的那个,用户名和密码 设为nacos吧
2.准备数据库
创建nacos数据库,执行sql文件建表
创建nacos用户,用户名和密码设为nacos
权限全勾上
介绍
注册中心,配置中心
Nacos就是注册中心+配置中心的组合 -> Nacos = Eureka+Config+Bus
什么是注册中心
注册中心是微服务架构中的纽带,类似于“通讯录”,它记录了服务和服务地址的映射关系。
注册中心本质上是为了解耦服务提供者和服务消费者
服务会注册到这里,当服务需要调用其它服务时,就到这里找到服务的地址并进行调用
注册中心的核心功能:
服务注册:服务实例将自身服务信息注册到注册中心
服务发现:服务实例通过注册中心,获取到注册到其中的服务实例的信息,通过这些信息去请求它们提供的服务
服务剔除:服务注册中心将出问题的服务自动剔除到可用列表之外,使其不会被调用到
注册中心解决的问题: (1)屏蔽、解耦服务之间相互依赖的细节:
服务之间的远程调用必须要知道对方IP、端口。但是该调用方式存在明显的问题,如被调用的IP、端口变化后,调用方也要同步修改。通过服务发现,将服务之间IP与端口的依赖转化为服务名的依赖,服务名可以根据具体微服务业务来做标识。
(2)对服务进行动态管理:
服务注册中心要实时管理服务的数据与状态,包括服务的注册上线、服务主动下线,异常服务的剔除。
(3)负载均衡
nacos可以做负载均衡
消费者 nacos服务消费者不是每次请求都会去注册中心拉取服务列表,会定时30s拉取一次,然后缓存到本地。
服务提供者列表发生变更还会有主动推送。
提供者 服务提供者会划分为临时实例和非临时实例,默认是临时实例
临时实例会采用心跳检测30s,不可访问则会主动剔除。
非临时实例naocs注册中心会主动询问,不可访问也不会主动剔除。 Nacos和**Eureka比较** 共同点: 1、都支持服务注册和服务拉取
2、都支持服务提供者心跳方式做健康检测
不同点:
1、Nacos支持服务端主动检测提供者状态:临时实例采用心跳模式,非临时实例采用主动检测模式
2、临时实例心跳不正常会被剔除,非临时实例则不会被剔除
3、Nacos支持服务列表变更的消息推送模式,服务列表更新更及时
nacos**负载均衡**
nacos集成ribbon实现**负载均衡:**
在restTemplate 加上@LoadBalanced实现轮询
nacos+openfeign
Nacos很好的兼容了Feign, Feign负载均衡默认集成了 Ribbon, 所以在Nacos下使用Fegin默认就实现了负载均衡的效果。
(Feign是Spring Cloud提供的一个声明式的伪Http客户端, 它使得调用远程服务就像调用本地服务一样简单, 只需要创建一个接口并添加一个注解即可。 )
配置中心
如何使用
NACOS动态配置 - barryzhou - 博客园 (cnblogs.com)
1.依赖引入
2.yml配置nacos
新增bootstrap.yml文件,写上application.name(服务注册名)和nacos的config信息
在项目初始化时,要保证先从配置中心进行配置拉取,拉取配置之后,才能保证项目的正常启动(比如在nacos配置了seata的信息,启动时得加载了seata才能启动成功)。
3.在nacos新增动态配置
登录NOCAS系统在配置列表页面,点击新增配置输入配置信息
DataID格式:${prefix}-${spring.profiles.active}.${file-extension} prefix为服务注册名 spring.profiles.active为当前配置的环境 file-extension是配置文件格式(yml/properties) 4.代码中获取动态参数 在使用配置的controller中新增 @RefreshScope 注解,以及在注入属性上新增 @Value("${Key名称}") 注解
加载顺序:
bootstrap.yml > application.yml > application-dev.yml > nacos-service.yaml > nacos-service-dev.yaml
如果存在重复配置,后面加载的配置会覆盖前面加载的配置内容
Nacos Config动态刷新原理_国服冰的博客-CSDN博客
长轮询的概念:客户端主动 发起请求后,服务端不会立即返回请求结果,而是将请求hold挂起
一段时间,如果此时间段内配置数据发生变更,则立即响应客户端,若一直无变更则等到指定超时时间后响应给客户端结果,客户端重新发起长链接
Nacos
动态刷新避免了服务端对客户端进行push
操作时需要保持双方的心跳连接和必须保持长连接,同样也避免了客户端对服务端进行pull
操作时数据的时效性问题,不必频繁去拉去服务端的数据
长轮询不是什么新技术,它其实就是由服务端控制响应客户端请求结果的返回时间,来减少客户端无效请求的一种优化手段
Seata
Seata 是阿里巴巴开源的分布式事务中间件
分布式**事务定义**
分布式事务是一个全局事务,由一批分支事务组成,通常分支事务只是本地事务。
模式
Seata中有两种分布式事务实现方案,AT和TCC。
AT
AT模式是基于XA事务演进而来,核心是对业务无侵入,是一种改进后的两阶段提交,需要数据库支持。
我们只需要关注自己的业务SQL,Seata会通过分析我们业务SQL,反向生成回滚数据
无侵入性,简单易用,强一致性,但性能低
TCC
要求每个接口实现prepare、commit、rollback。预留资源 和释放预留资源
实现复杂,高侵入性
原理
AT 包含两个阶段
一阶段,所有参与事务的分支,本地事务Commit 业务数据和回滚日志(undoLog)
二阶段,事务协调者(seata server)根据所有分支的情况,决定本次全局事务是Commit 还是 Rollback(二阶段是完全异步)
在一阶段,Seata 会拦截“业务 SQL”,首先解析 SQL 语义,找到“业务 SQL”要更新的业务数据,在业务数据被更新前,将其保存成“before image”,然后执行“业务 SQL”更新业务数据,在业务数据更新之后,再将其保存成“after image”,最后生成行锁。以上操作全部在一个本地数据库事务内完成,
(保存了元数据和更新后数据,并且有行锁保证数据不被其他事务更改了)。
二阶段如果是提交的话,因为“业务 SQL”在一阶段已经提交至数据库, 所以 Seata 框架只需将一阶段保存的快照数据和行锁删掉,完成数据清理即可。
二阶段如果是回滚的话,Seata 就需要回滚一阶段已经执行的“业务 SQL”,还原业务数据。回滚方式便是用“before image”还原业务数据;但在还原前要首先要校验脏写,对比“数据库当前业务数据”和 “after image”,如果两份数据完全一致就说明没有脏写,可以还原业务数据,如果不一致就说明有脏写,出现脏写就需要转人工处理。
seata at只有需要全局回滚时才两个阶段都占有锁。如果二阶段执行成功,只用把快照和行锁删除就行
TC是seata server,TM是事务发起者,RM是分支事务服务
使用流程
1.下载seata-server
2.数据库支持
建立seata数据库,根据官方建表语句在seata数据库建表global_table branch_table lock_table
同时在所有被调用/调用的服务的数据库中加入 undo_log表,用于事务的回滚
3.根据自己需求修改配置文件—— "file.conf" 和 "registry.conf"
file.conf
修改保存模式"mode"为"db",修改"db"中数据库相关的配置:
registry.conf
seata server会把自己注册到注册中心,像其它的微服务模块一样。registry.conf就是配置seata server使用何种注册中心以及如何注册。
修改注册中心为nacos
修改配置中心为nacos
4.整合到项目中
【精选】最详细SpringCloud+nacos整合Seata1.4.2 实现分布式事务_springcloud整合seata1.4.2-CSDN博客
1.引入依赖
2.yml配置
事务分组如何找到Seata集群:
首先应用程序(客户端)中通过seata.tx-service-group 配置了事务分组。
应用程序(客户端)会通过用户配置的配置中心去寻找service.vgroupMapping .[事务分组配置项],取得配置项的值就是TC集群的名称。若应用程序是SpringBoot则通过seata.service.vgroup-mapping.事务分组名=集群名称 配置
拿到集群名称程序通过一定的前后缀+集群名称去构造服务名,各配置中心的服务名实现不同(前提是Seata-Server已经完成服务注册,且Seata-Server向注册中心报告cluster名与应用程序(客户端)配置的集群名称一致)
拿到服务名去相应的注册中心去拉取相应服务名的服务列表,获得后端真实的TC服务列表(即Seata-Server集群节点列表)
3.nacos配置中心添加两条配置
第二条事务分组的值得和客户端事务分组一样
在第一条配置中配置seata的数据源
4.需要分布式事务的方法上加上@GlobalTransaction
Seata 1.4.1 部署
1.配置注册中心
打开conf文件夹,找到seata/conf/registry.conf
用nacos作注册中心,nacos的用户名和密码在nacos/conf/application.properties中配置
namespace = "xxx" #指定在nacos的命名空间,需要提前在nacos创建对应的命名空间
2.配置配置中心
同样在seata/conf/registry.conf
配置完registry文件,然后我们在nacos中添加如下两个配置文件,data id和上图的dataid保持一致。
3.数据库配置
创建seata数据库,导入表
创建seata用户,密码为seata
seata用户权限全勾选上
4.配置服务的yml
tx-service-group:与nacos配置文件中的service.vgroupMapping.xxx_tx_group 的xxx_tx_group保持一致
openfeign
Nacos+OpenFegin正确调用服务的姿势! - 掘金 (juejin.cn)
what
Spring 官方推出的一种声明式服务调用和**负载均衡组件**。它的出现就是为了替代已经进入停更维护状态的 Feign(Netflix Feign)的
它的核心是使用注解 + 接口的方式实现服务调用
注解
@EnableFeignClients:该注解用于开启 OpenFeign 功能,当 Spring Cloud 应用启动时,OpenFeign 会扫描标有 @FeignClient 注解的接口,生成代理并注册到 Spring 容器中。
@FeignClient:该注解用于通知 OpenFeign 组件对 @RequestMapping 注解下的接口进行解析,并通过动态代理的方式产生实现类,实现**负载均衡和服务调用。**
@RequestMapping:向服务提供者发起 Request 请求(默认为 GET 方式请求),这里需要注意 @RequestMapping/@GetMapping/@PostMapping 和 Spring MVC 中的同名注解的含义是完全不同的。
使用
1.创建一个服务提供者
如图,springcloud-nacos-provider模块的某一个接口
该模块需要和服务消费者在同一个nacos中注册
2.创建服务消费者
同样在nacos中注册后
在 Spring Boot 项目的启动文件上添加 @EnableFeignClients 注解,开启 OpenFeign
创建 OpenFeign 与服务提供者的调用接口(声明客户端)
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient("springcloud-nacos-provider") // nacos 服务 id
public interface SpringCloudNacosProviderClient {
@GetMapping("/call/{name}") // 使用 get 方式,调用服务提供者的 /call/{name} 接口
public String call(@PathVariable(value = "name") String name);
}
通过该接口调用服务提供者对应url的方法
import com.example.openfeignconsumer.feignclient.SpringCloudNacosProviderClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
public class ConsumerController {
@Resource
private SpringCloudNacosProviderClient providerClient; // 加载 openfeign client
@GetMapping("/consumer")
public String consumer(@RequestParam String name) {
// 向调用本地方法一样,调用 openfeign client 中的方法
return providerClient.call(name);
}
}
修改超时时间
Ope nFeign 默认的接口超时时间为 1s,所以如果接口的执行时间超过 1s,那么程序调用就会报错。
我们可以通过配置修改
ribbon:
ReadTimeout: 5000 # 请求连接的超时时间
ConnectionTimeout: 10000 # 请求处理的超时时间
spirngcloud部署
linux如何部署简单的springcloud项目步骤 ----奶奶级教学springcloud jar包部署槿十六的博客-CSDN博客
为什么有父工程,有父工程不是就不能分布式了吗?
错误的,父工程是为了统一管理配置和开发阶段方便开发测试对调,小型项目可以这么干的
如果特别大型的项目可以分模块独立开发,彼此之间通过通信对接就行。
但是父工程是可以分布式的,只要maven打包时在父工程打包就行,会在每个子模块打出一个jar包,这些jar包都可以独立部署
父工程打包方式选定pom,让打包时父工程的配置可以打入子工程
1.确定父工程和子模块的maven插件
2.父工程maven生命周期 clean package
3.在每个子模块target拿到jar包,上传服务器
4.后台启动jar包,指定依赖lib库,指定启动环境为prod
nohup java -jar -Xmx1024M -Xms256M -Dspring.profiles.active=prod -Dloader.path=$AUTH_LIB $AUTH >/home/elevator/null 2>&1 &
maven-dependency-plugin之copy-dependencies_maven-dependency-plugin copy-dependencies-CSDN博客
将依赖的 jar 包下载到指定的文件夹中。并不一起打包到最终jar包中。