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

配置路由

image-20240420125622860

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 链的方式提供了网关基本的功能,例如:安全,监控/指标,限流。

流程

具体的流程分析:

  1. 路由判断:客户端的请求到达网关后,先经过 Gateway Handler Mapping 处理,这里面会做断言(Predicate)判断,看下符合哪个路由规则,这个路由映射后端的某个服务。

  2. 请求过滤:然后请求到达 Gateway Web Handler,这里面有很多过滤器,组成过滤器链(Filter Chain),这些过滤器可以对请求进行拦截和修改,比如添加请求头、参数校验等等,有点像净化污水。然后将请求转发到实际的后端服务。这些过滤器逻辑上可以称作 Pre-Filters,Pre 可以理解为“在...之前”。

  3. 服务处理:后端服务会对请求进行处理。

  4. 响应过滤:后端处理完结果后,返回给 Gateway 的过滤器再次做处理,逻辑上可以称作 Post-Filters,Post 可以理解为“在...之后”。

  5. 响应返回:响应经过过滤处理后,返回给客户端。

总结:客户端的请求先通过匹配规则找到合适的路由,就能映射到具体的服务。然后请求经过过滤器处理后转发给具体的服务,服务处理后,再次经过过滤器处理,最后返回给客户端

核心概念

断言

对一个表达式进行 if 判断,结果为真或假,如果为真则做这件事,否则做那件事。

在 Gateway 中,如果客户端发送的请求满足了断言的条件,则映射到指定的路由器,就能转发到指定的服务上进行处理。

路由

一对多:一个路由规则可以包含多个断言。

同时满足:如果一个路由规则中有多个断言,则需要同时满足才能匹配。如上图中路由 Route2 配置了两个断言,客户端发送的请求必须同时满足这两个断言,才能匹配路由 Route2。

第一个匹配成功:如果一个请求可以匹配多个路由,则映射第一个匹配成功的路由

过滤器

过滤器 Filter 按照请求和响应可以分为两种:

  • Pre 类型:在请求被转发到微服务之前,对请求进行拦截和修改,例如参数校验、权限校验、流量监控、日志输出以及协议转换等操作。

  • Post 类型:微服务处理完请求后,返回响应给网关,网关可以再次进行处理,例如修改响应内容或响应头、日志输出、流量监控等。

另外一种分类是按照过滤器 Filter 作用的范围进行划分:

  • GatewayFilter:局部过滤器,应用在单个路由或一组路由上的过滤器。标红色表示比较常用的过滤器。

  • GlobalFilter:全局过滤器,应用在所有路由上的过滤器

限流

Spring Cloud Gateway 自带了限流过滤器,对应的接口是 RateLimiterRateLimiter 接口只有一个实现类 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更改数据库连接

image-20241027215909087

数据库就是第三步数据库准备的那个,用户名和密码 设为nacos吧

2.准备数据库

创建nacos数据库,执行sql文件建表

image-20241027220307922

创建nacos用户,用户名和密码设为nacos

权限全勾上

image-20241027220440024


介绍

注册中心,配置中心

Nacos就是注册中心+配置中心的组合 -> Nacos = Eureka+Config+Bus

什么是注册中心

注册中心是微服务架构中的纽带,类似于“通讯录”,它记录了服务和服务地址的映射关系。

注册中心本质上是为了解耦服务提供者和服务消费者

服务会注册到这里,当服务需要调用其它服务时,就到这里找到服务的地址并进行调用

注册中心的核心功能:

  • 服务注册:服务实例将自身服务信息注册到注册中心

  • 服务发现:服务实例通过注册中心,获取到注册到其中的服务实例的信息,通过这些信息去请求它们提供的服务

  • 服务剔除:服务注册中心将出问题的服务自动剔除到可用列表之外,使其不会被调用到

注册中心解决的问题: (1)屏蔽、解耦服务之间相互依赖的细节:

服务之间的远程调用必须要知道对方IP、端口。但是该调用方式存在明显的问题,如被调用的IP、端口变化后,调用方也要同步修改。通过服务发现,将服务之间IP与端口的依赖转化为服务名的依赖,服务名可以根据具体微服务业务来做标识。

(2)对服务进行动态管理:

服务注册中心要实时管理服务的数据与状态,包括服务的注册上线、服务主动下线,异常服务的剔除。

(3)负载均衡

nacos可以做负载均衡

image-20240420125644987

消费者 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信息

image-20240420125652470

在项目初始化时,要保证先从配置中心进行配置拉取,拉取配置之后,才能保证项目的正常启动(比如在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

image-20240420125658662

如果存在重复配置,后面加载的配置会覆盖前面加载的配置内容

Nacos Config动态刷新原理_国服冰的博客-CSDN博客

长轮询的概念:客户端主动 发起请求后,服务端不会立即返回请求结果,而是将请求hold挂起一段时间,如果此时间段内配置数据发生变更,则立即响应客户端,若一直无变更则等到指定超时时间后响应给客户端结果,客户端重新发起长链接

Nacos动态刷新避免了服务端对客户端进行push操作时需要保持双方的心跳连接和必须保持长连接,同样也避免了客户端对服务端进行pull操作时数据的时效性问题,不必频繁去拉去服务端的数据

长轮询不是什么新技术,它其实就是由服务端控制响应客户端请求结果的返回时间,来减少客户端无效请求的一种优化手段

Seata

Seata 是阿里巴巴开源的分布式事务中间件

分布式**事务定义**

分布式事务是一个全局事务,由一批分支事务组成,通常分支事务只是本地事务。

image-20240420125705368

模式

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"中数据库相关的配置:

image-20240420125715358

registry.conf

seata server会把自己注册到注册中心,像其它的微服务模块一样。registry.conf就是配置seata server使用何种注册中心以及如何注册。

修改注册中心为nacos

image-20240420125723524

image-20240420125731405

修改配置中心为nacos

image-20240420125736591

4.整合到项目中

【精选】最详细SpringCloud+nacos整合Seata1.4.2 实现分布式事务_springcloud整合seata1.4.2-CSDN博客

1.引入依赖

2.yml配置

事务分组如何找到Seata集群:

image-20240420125744579

首先应用程序(客户端)中通过seata.tx-service-group 配置了事务分组。

应用程序(客户端)会通过用户配置的配置中心去寻找service.vgroupMapping .[事务分组配置项],取得配置项的值就是TC集群的名称。若应用程序是SpringBoot则通过seata.service.vgroup-mapping.事务分组名=集群名称 配置

拿到集群名称程序通过一定的前后缀+集群名称去构造服务名,各配置中心的服务名实现不同(前提是Seata-Server已经完成服务注册,且Seata-Server向注册中心报告cluster名与应用程序(客户端)配置的集群名称一致)

拿到服务名去相应的注册中心去拉取相应服务名的服务列表,获得后端真实的TC服务列表(即Seata-Server集群节点列表)

3.nacos配置中心添加两条配置

image-20240420125750489

第二条事务分组的值得和客户端事务分组一样

image-20240420125801700

在第一条配置中配置seata的数据源

4.需要分布式事务的方法上加上@GlobalTransaction

Seata 1.4.1 部署

1.配置注册中心

打开conf文件夹,找到seata/conf/registry.conf

image-20241027212036524

用nacos作注册中心,nacos的用户名和密码在nacos/conf/application.properties中配置

image-20241027212712396

namespace = "xxx" #指定在nacos的命名空间,需要提前在nacos创建对应的命名空间

2.配置配置中心

同样在seata/conf/registry.conf

image-20241027213313566

配置完registry文件,然后我们在nacos中添加如下两个配置文件,data id和上图的dataid保持一致。

image-20241027213918831

image-20241027213928954

3.数据库配置

创建seata数据库,导入表

image-20241027214301036

image-20241027214030620

image-20241027214348695

创建seata用户,密码为seata

seata用户权限全勾选上

4.配置服务的yml

image-20241027214651371

image-20241027214701100

tx-service-group:与nacos配置文件中的service.vgroupMapping.xxx_tx_groupxxx_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.创建一个服务提供者

image-20240420125808813

如图,springcloud-nacos-provider模块的某一个接口

该模块需要和服务消费者在同一个nacos中注册

2.创建服务消费者

同样在nacos中注册后

在 Spring Boot 项目的启动文件上添加 @EnableFeignClients 注解,开启 OpenFeign

image-20240420125822047

创建 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 &

image-20240420125830294

maven-dependency-plugin之copy-dependencies_maven-dependency-plugin copy-dependencies-CSDN博客

将依赖的 jar 包下载到指定的文件夹中。并不一起打包到最终jar包中。