微服务学习入门

  1. 引入微服务

传统单体应用的优缺点:

优点:

  1. 简单 方便

缺点:

  1. 服务的耦合度高
  2. 高并发时数据库压力大 没扛住就崩了

微服务的优点:

  1. 服务解耦 数据库解耦
  2. 管理维护十分方便
  3. 请求压力被分散了 可用性好 还可以创建集群 负载均衡 进一步抗压

缺点:

  1. 创建复杂

  2. 维护成本高

  3. 项目拆分


2.1. 如何拆分?

2.1.1. 模块

  1. 创建新子模块准备为抽离服务做准备
  2. 分析原来项目的依赖情况 抽出公共的依赖 如 Lombok spring-test等
  3. 分析抽离的服务所需要的子模块和依赖
  4. 创建公共子模块 并导入公共的依赖 给新创建的服务子模块导入公共模块的坐标和自己所需的依赖

2.1.2. 代码

  1. 转移新服务所需要的实体类 方法 接口等
  2. 若新服务使用了数据库 还可以为新服务分配新的 mysql实例 数据库 和 表

2.1.3. 配置

  1. 对父模块进行依赖管理 管理cloud的依赖版本 (*)
xml
复制代码
<dependencyManagement> <dependencies> <!-- Spring Cloud 2021.0.x --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>2021.0.8</version> <type>pom</type> <scope>import</scope> </dependency> <!-- Spring Cloud Alibaba 2021.0.5.0 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2021.0.5.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
  1. 更新新服务配置 配置新的启动类

  2. 设置新服务的端口号

  3. Nacos


3.1. 现存问题:

  1. 抽离的服务启动的越来越多 数量一多就不好管理
  2. 服务可能存在集群 每个服务的端口不一样 调用时如果写死就做不到负载均衡

引入:

nacos --> 发现中心 对注册的微服务 发现和管理

功能:

  1. 注册服务 : 配置服务名称 启动端口自动出现在Nacos

  2. 发现服务: 配置服务名称 启动端口 引入提供的client 根据名称获取服务实例列表

  3. 使用服务: 使用随机算法获取服务实例(负载均衡) 根据实例的URl动态发送请求

3.2. Docker 安装 Nacos :

3.2.1. 安装Docker

ubantu安装Docker: https://blog.csdn.net/a1053765496/article/details/147691484

3.2.2. 安装Nacos

https://blog.csdn.net/qq_54794992/article/details/146902786?ops_request_misc=%257B%2522request%255Fid%2522%253A%25227261c6f1d59c53619abec593b02f344c%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=7261c6f1d59c53619abec593b02f344c&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_click~default-2-146902786-null-null.142^v102^pc_search_result_base4&utm_term=docker%E9%83%A8%E7%BD%B2nacos&spm=1018.2226.3001.4187

3.2.3. 检验是否启动

  1. 可以访问 http:// 你的naocs地址:8848/nacos 管理页面

  1. 8848 9848 9849 被占用 而且 docker logs 没有报错
shell
复制代码
# 查看运行的容器 dokcer ps # 查看容器的日志 docker logs your_container

3.2.4. 踩坑(docker版)

  1. 新版nacos 需要传入三个生成的base64凭证 麻烦的很
  2. 需要先导入数据库 并且要创建官方要求的配置表
  3. 内存小的服务器就在学习的话就在虚拟机部署了吧 我的2g的 一个nacos 一个mysql直接nacos挂了 命令行都卡飞
  4. 记得开放端口 8848 9848 9849 不然后续 在 nacos 的服务注册不了

3.3. nacos使用

  1. 导入nacos 发现依赖
xml
复制代码
<!-- Nacos 服务注册与配置 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>
  1. 启动类打开 服务发现开关
java
复制代码
//当引入了 spring-cloud-starter-alibaba-nacos-discovery 依赖后, //Spring Boot 的自动配置机制会加载相关的配置类。这些配置类会自动启动服务注册和发现功能, //可无需显式添加 @EnableDiscoveryClient @SpringBootApplication @EnableDiscoveryClient public class MainApplication { public static void main(String[] args) { SpringApplication.run(MainApplication.class, args); } }
  1. 配置nacos 地址 和 服务名称
xml
复制代码
spring: application: name: #你的微服务名称: 用来访问实例的唯一特征 cloud: nacos: discovery: server-addr: # 你的naocs地址:8848 username: nacos #默认 password: nacos #默认
  1. 启动微服务 然后就能在 http:// 你的naocs地址:8848/nacos 管理页面看到了你的服务

当前缺点: 服务创建好了 怎么调用呢 ?

  1. OpenFeign

4.1. 介绍

OpenFeign :

  1. 提供像 SpringMVC 一样的操作发送请求的调用微服务的方式

  2. 提供了支持 负载均衡 的能力 多集群部署的时候分散访问压力

  3. 根据调用的服务名动态获取实例 不关心到底是微服务是哪个端口

4.2. 使用

依赖于nacos发现实例的能力 所以要先有上一节中 nacos的环境 和 发现配置的依赖

4.2.1. 导入依赖

xml
复制代码
<!-- openfeign 依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!-- 负载均衡依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-loadbalancer</artifactId> </dependency> <!-- okhttp支持线程池 默认的 openfeign 不支持线程池 --> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-okhttp</artifactId> </dependency>

4.2.2. 启动类打开开关

java
复制代码
@SpringBootApplication @EnableFeignClients //打开开关 public class MainApplication { public static void main(String[] args) { SpringApplication.run(MainApplication.class, args); } }

4.2.3. 配置 okhttp 提升连接效率

xml
复制代码
spring: application: name: HttpService cloud: nacos: discovery: server-addr: 192.168.51.128:8848 username: nacos password: nacos server: address: 0.0.0.0 port: 8085 feign: okhttp: #打开okhttp的支持 enabled: true

4.2.4. 使用示例

创建接口 创建请求接口

java
复制代码
import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; /** * 新建 feign 接口 声明接口 * **/ @FeignClient(value = "AIService") public interface AIServiceClient { @PostMapping ("/api/ai/genChart") String getAnswer(@RequestParam String question); }

注释:

@FeignClient(value = "AIService") 值是所创建的实例名称

@PostMapping ("/api/ai/genChart") 以Post的请求访问 /api/ai/genChart 接口

@RequestParam String question 传递的参数是 question

调用的地方

java
复制代码
//直接注入就能用 底层是 代理类 实现的改接口 @Resource pravite AIServiceClient aIServiceClient public void test(){ aIServiceClient.getAnswer("something......") }

执行的地方

java
复制代码
@RestController @RequestMapping("/ai") public class AIController { @PostMapping("/genChart") public String genChart(@RequestParam String question) { if (question == null) return null; String answer = AIUtils.getAnswer(question); return answer; } }
  1. SpringGateway

5.1. 介绍

网关: 就是根据路由进行请求的转发 SpringGateway 能够根据路由从而转发请求到对应的 微服务 并且能在微服务中 设置过滤器 做一些处理 使用时应该注意过滤器链的执行顺序

5.1.1. 现存的问题

  1. 后端的端口很容易变化 前端并不想关心后端的端口的变化 只想请求一个固定的地址和端口
  2. 后端的接口并不是所有人都能调 比如有些需要登录后的用户可以使用 然后在每个服务都写一遍登录校验的逻辑非常冗余
  3. 各个微服务可能都需要前端传来的jwt或者sessionId 用来获取保存的用户信息 然而给每个服务都发送jwt或者sessionId 服务和服务之间相互调用可能也要发送 然后各自校验太麻烦了

5.1.2. 解决

对于1问题: 网关开放一个固定端口 再根据路由信息转发到对应的微服务

对于2问题: 在请求到达微服务前先在网关中校验用户的权限 不适合的直接pass

对于3问题: 在网关设置过滤器 处理好jwt保存的信息保存在请求头中再转发给对应的微服务 微服务也不必都从请求头中获取 可以在请求到达的时候加一个拦截器 拦截器用ThreadLocal 或者 redis 保存用户的信息 微服务用的时候直接取就ok了

5.2. 使用

5.2.1. 导入依赖

xml
复制代码
<!-- 网关依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <!-- 如果父模块传递了 spring-boot-starter-web,手动排除 !!! --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> </exclusion> </exclusions> </dependency>

注意:

网关是基于flux的 而不是SpringMVC的 所以使用应该注意排除掉SpringMVC的相关依赖

5.2.2. 配置路由

yaml
复制代码
spring: cloud: gateway: routes: - id: AIService #服务名 uri: lb://AIService # lb:表示负载均衡 到服务 predicates: - Path=/api/ai/** #转发什么路径的请求

predicates 断言:

就是判断的依据 官网还提供了根据时间的先后 cookie 路径的参数 等来设置

官网:

https://docs.spring.io/spring-cloud-gateway/reference/spring-cloud-gateway-server-webflux/request-predicates-factories.html

5.3. 过滤器

5.3.1. 全局过滤器

使用:

这里在全局过滤器实现登录的校验

java
复制代码
/** * 全局过滤器 */ @Component @RequiredArgsConstructor public class GlobalFilter implements org.springframework.cloud.gateway.filter.GlobalFilter, Ordered { private final PathWhiteListProperties pathWhiteListProperties; private final AntPathMatcher pathMatcher = new AntPathMatcher(); private final RedisTemplate<String, Object> redisTemplate; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 处理全局所有的请求 登录校验 //如果是登录注册请求 直接放行 RequestPath requestPath = exchange.getRequest().getPath(); if (isAuth(requestPath)) { //放行 return chain.filter(exchange); } System.out.println("拦截器启动!"); //其他请求 校验用户是否登录 //1.从 redis 获取用户信息 String sessionId = exchange.getRequest().getHeaders().get(UserConstant.USER_Seession_ID).get(0); String jsonStr = (String) redisTemplate.opsForValue().get(UserConstant.USER_LOGIN_STATE + sessionId); try { User currentUser = JSONUtil.toBean(jsonStr, User.class); if (currentUser == null || currentUser.getId() == null) { //如果用户信息不存在 拒绝请求 exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); return exchange.getResponse().setComplete(); //拒绝请求 } //对下游的请求头修改 把用户信息放入请求头 传递下去 (mutate 对请求修改) exchange.getRequest().mutate().header(UserConstant.USER_Seession_ID, sessionId); } catch (Exception e) { //如果用户信息不存在 拒绝请求 exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); return exchange.getResponse().setComplete(); } //2.放行 return chain.filter(exchange); } //设置过滤器的优先级 0 最高 @Override public int getOrder() { return 0; } boolean isAuth(RequestPath requestPath) { String path = requestPath.value(); List<String> exclude = pathWhiteListProperties.getExclude(); for (String excludePath : exclude) { if (pathMatcher.match(excludePath, path)) { return true; } } return false; } }

5.3.2. 与拦截器组合

现状: 正如上面的代码 实现了登录的校验 并且修改了请求头 把 USER_Seession_ID 放进去 以便后面接收到请求的微服务可以根据请求头获得用户信息

问题:

  1. 每个微服务都要从请求头获取 都写一遍太冗余了
  2. 如何把过滤后的处理后的sessionId传递给微服务

解决:

问题1. 在公共模块配置拦截器 所有微服务都会引入公共模块 这样就不必都写一遍了

问题2. 封装在请求头中传递过去

代码示例:

java
复制代码
@Component @RequiredArgsConstructor @Slf4j public class UserRequestIntercepter implements HandlerInterceptor { private final RedisTemplate<String, Object> redisTemplate; @Override public boolean preHandle(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Object handler) throws Exception { //从请求头获取 sessionId String sessionId = request.getHeader(UserConstant.USER_Seession_ID); //从redis取出登录态 String jsonStr = (String) redisTemplate.opsForValue().get(USER_LOGIN_STATE + sessionId); try { User currentUser = JSONUtil.toBean(jsonStr, User.class); if (currentUser == null || currentUser.getId() == null) { throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR); } LoginUserVO loginUserVO = new LoginUserVO(); BeanUtils.copyProperties(currentUser, loginUserVO); loginUserVO.setSessionId(sessionId); log.info("user服务的拦截器 从请求头获取 sessionId 成功!"); //保存 登录态 到 ThreadLocal 一个服务的应用调用ThreadLocal就可以使用了 ThreadLocalUtil.setAttribute(loginUserVO); return true; } catch (Exception e) { log.error("user服务的拦截器 从请求头获取 sessionId 失败!"); return true; } } }

要想拦截器生效 还需要在mvc配置

java
复制代码
@Configuration @RequiredArgsConstructor @ConditionalOnClass(DispatcherServlet.class) //注意 public class SpringMvcConfig implements org.springframework.web.servlet.config.annotation.WebMvcConfigurer { private final UserRequestIntercepter userRequestIntercepter; //注册拦截器 @Override public void addInterceptors(org.springframework.web.servlet.config.annotation.InterceptorRegistry registry) { registry.addInterceptor(userRequestIntercepter).addPathPatterns("/**"); } }

注意:

因为网关也是引用了common模块的 然而网关不是基于SpringMVC的 所以直接配置会报 找不到MVC包的错

解决:

使用 @ConditionalOnClass(DispatcherServlet.class) 条件

表示只有含有DispatcherServlet的服务拦截器才能够生效 ! ! !

5.3.3. 微服务间的信息传递

问题:

之前解决了网关和微服务的信息传递 那么微服务之间如何传递呢\

解决:

给 OpenFeign 加一层拦截器 保存信息到请求头 用来传递信息

java
复制代码
public class DefaultConfig { @Bean public Logger.Level feignLoggerLevel() { return Logger.Level.FULL; //配置日志等级 } //请求拦截器 @Bean public RequestInterceptor requestInterceptor() { return template -> { LoginUserVO loginUserVO = (LoginUserVO) ThreadLocalUtil.getAttribute(); if(loginUserVO!=null){ String sessionId = loginUserVO.getSessionId(); if (StringUtils.isNotBlank(sessionId)) { template.header(UserConstant.USER_Seession_ID, sessionId); } } }; } }

开启拦截器配置

在需要拦截器生效的服务的启动类上使用拦截器

java
复制代码
@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients(defaultConfiguration = DefaultConfig.class) public class MainApplication { public static void main(String[] args) { SpringApplication.run(MainApplication.class, args); } }
  1. 配置管理

6.1. 共享配置

6.1.1. 问题

问题: 现在每个微服务都引入了很多重复的配置 很冗余

解决: 可以提取公共配置交给nacos管理 自己再配置个性化配置就可以

6.1.2. 操作

6.1.2.1. 引入配置依赖
xml
复制代码
<!--nacos配置管理--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> <!-- 读取 bootstrap.文件 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bootstrap</artifactId> </dependency>
6.1.2.2. 在nacos 创建各自需要的公共配置

如:

yaml
复制代码
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://${yy.db.address:localhost}:${yy.db.port:3306}/${yy.db.database:es_db} username: ${yy.db.username:root} password: ${yy.db.password:123456}
6.1.2.3. 创建 bootstrap.yml 文件 定义加载nacos地址的配置等 !

原因: 如果不先配置bootstrap 它启动会首先加载nacos的 但是nacos还没配置地址 所以就会找不到配置

配置加载顺序:

在对应的微服务的resource下 创建bootstrap.yml 配置nacos地址 引入公共的配置

yaml
复制代码
spring: application: name: AIService # 服务名称 profiles: active: dev cloud: nacos: server-addr: 192.168.51.130:8848 #nacos地t址 config: file-extension: yaml # 文件后缀名 shared-configs: # 共享配置 - data-id: shared_seta.yaml # 共享seata配置
6.1.2.4. 4.个性化公共配置
yaml
复制代码
yy: db: address: localhost username: sheep2 .....诸如此类

6.2. 热更新配置

6.2.1. 问题

问题: 每次修改配置都需要重启微服务

解决: 使用NacosConfig 在启动时自动拉取配置文件

示例:

6.2.2. 操作

  1. 在 在nacos 客户端页面选择对应的服务创建配置中 创建配置

注意:

  1. 格式是: 服务名+环境(可选)+yaml
  2. 短横线表示一个集合
yaml
复制代码
hm: cart: name: sheep age: 10 sex: 男 address: 上海 phone: 13888888888 habit: - 吃饭 - 睡觉 - 打豆豆
  1. 创建对应的配置对象 使用注解自动填充属性

示例:

此时就可以在nacos中动态更改了

  1. Sentinel

7.1. 分布式事务雪崩问题

7.1.1. 介绍

原因: 比如在高并发的情况下 服务可能因为压力宕机或者本身就是慢请求 而其他调用该服务的机器并不知道 只是一味的发送请求 消耗线程资源 最后服务因为资源耗尽而崩溃 其他调用此服务的机器也碰到相同问题 最终引起大面积的机器宕机

示例:

如图所示 服务B C 需要调用服务A D 然后A D 又需要服务E

现在对E的调用执行缓慢或者E已经挂了 A D并不知情 一味的发请求 线程消耗完了最后A D也挂了

最后 B C也因为线程资源耗尽也挂了 于是 整个系统服务全部不可用 引起事务雪崩

7.1.2. 解决雪崩

服务保护机制:

  1. 接口限流

限制接口的QPS 以免高并发挂掉 引起雪崩

  1. 线程隔离

每个服务请求规定一定数量的能承受的线程数 线程耗尽就不允许继续请求

  1. 熔断降级

统计异常请求数 超过一定阈值就熔断该服务 拒绝请求 一段时间后再发一次请求 若通过则恢复服务 反之继续熔断

引入: Sentinel

7.2. Sentinel

7.2.1. 介绍

Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件,主要以流量为切入点,从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性。

7.2.2. 使用

  1. 下载并启动 sentinel 客户端
  2. 使用 sentinel 和密码 sentinel 登录 sentinel 控制台
  3. 引入 sentinel 依赖并配置 sentinel
xml
复制代码
<!-- 引入Sentinel --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
  1. 基于MVC的服务会自动加入到 sentinel 若要开启 sentinel 对 openfeign 的支持 也要配置
yaml
复制代码
feign: sentinel: enabled:true
  1. 点击式接口限流 线程隔离 熔断降级

官方文档: https://sentinelguard.io/zh-cn/docs/introduction.html

与SpringCloud 整合: https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel

7.2.3. Fallback

Fallback 机制保证服务在降级失败之后的处理逻辑

使用:

  1. 自定义类,实现FallbackFactory,编写对某个FeignClient 的 Fallbacki逻辑:
java
复制代码
/** * 失败后的降级处理 */ @Slf4j class A implements FallbackFactory<AIServiceClient> { @Override public AIServiceClient create(Throwable cause) { //创建AIServiceClient.接口实现类,实现其中的方法,编写失改降级的处理逻辑 return new AIServiceClient() { @Override public String getAnswer(String question) { //记录异常信息,可以返回空或抛出异常 log.error("查询用户失败",throwable); return null; } }; } }
  1. 将刚刚定义的 UserClientFallbackFactory 注册为一个Bean
java
复制代码
@Bean public UserClientFallbackFactory userclientFallback(){ return new UserclientFallbackFactory(); }
  1. 在 UserClient 接口中使用 UserClientFallbackFactory
java
复制代码
@Feignclient(value = "userservice",fallbackFactory = UserclientFallbackFactory.class) public interface Userclient{ @GetMapping("/user/[id]") User findById(@PathVariable("id")Long id); }

此时 被拒绝的亲请求就会走 Fallback

7.2.4. 服务熔断

添加熔断规则

注释:

最大RT: 超过该值就为慢调用

比例阈值: 请求的数量有多少比例的异常会被记录

最小请求数: 在统计时长里的最少几次请求才会被记录

  1. 分布式事务

yaml
复制代码
server: port: 7091 spring: application: name: seata-server logging: config: classpath:logback-spring.xml file: path: ${user.home}/logs/seata extend: logstash-appender: destination: 127.0.0.1:4560 kafka-appender: bootstrap-servers: 127.0.0.1:9092 topic: logback_to_logstash console: user: username: seata password: seata seata: config: type: nacos nacos: server-addr: 172.17.0.2:8848 namespace: group: DEFAULT_GROUP username: nacos password: nacos context-path: data-id: seata.properties registry: type: nacos nacos: application: seata-server server-addr: 172.17.0.2:8848 group: DEFAULT_GROUP namespace: cluster: default username: nacos password: nacos context-path: store: mode: db db: datasource: druid db-type: mysql driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://172.17.0.3:3306/seata_db?characterEncoding=utf8&connectTimeout=10000&socketTimeout=30000&autoReconnect=true&useUnicode=true&useSSL=false user: root password: 123456 min-conn: 10 max-conn: 100 global-table: global_table branch-table: branch_table lock-table: lock_table distributed-lock-table: distributed_lock query-limit: 1000 max-wait: 5000 security: secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017 tokenValidityInMilliseconds: 1800000 ignore: urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/api/v1/auth/login

8.1. 分布式事务介绍

如图: 此类事务因为是微服务部署 每个服务各司其职 对于下单操作可能调用如图的三个服务协同完成 然而其中的某个服务失败并不会影响其他服务的操作 这是不可以的 我们希望出现异常 事务应该回滚

引出: Seata

8.2. Seata

介绍: Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。

官方中文文档: https://seata.apache.org/zh-cn/docs/overview/what-is-seata

教程1: https://cloud.tencent.com/developer/article/2363585?policyId=20240001&frompage=seopage

教程2: https://blog.csdn.net/lchmyhua88/article/details/146136099

8.2.1. 重要术语

8.2.2. Docker 部署 Seata

  1. 建表

github: https://github.com/apache/incubator-seata/blob/master/script/server/db/mysql.sql

  1. 拉取镜像 创建容器 复制配置到宿主机 配置yaml 设置与数据库的连接
shell
复制代码
docker cp seata:/seata-server/resources /home/seata
  1. 删除容器 重新加载容器 挂载配置yaml 启动
shell
复制代码
docker run -d --name seata \ -p 8091:8091 \ -p 7091:7091 \ -e SEATA_IP=192.168.51.130 \ -v /home/seata/resources:/seata-server/resources \ seataio/seata-server:1.5.0

8.3. Seata整合微服务

  1. 添加依赖
java
复制代码
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> </dependency>
  1. nacos 添加seata 公共配置 需要的微服务自己引入
  2. 在事务的入口方法加上注解 即可开启
java
复制代码
@GlobalTransactional(name = "order",rollbackFor = Exception.class) public void AIService(){ start_AIService1..... start_AIService2..... start_AIService3..... }

8.4. 常见模型

Seata 把事务提交分为两个阶段 不同的模型对这两个阶段会有不同的处理方案

8.4.1. AT (默认)

一阶段:

  • TM 发起全局事务 注册到 TC
  • 每个 RM 执行 sql 完成后就提交事务 并生成快照 同步状态到 TC
  • TC 检查各个事物的状态 若有异常 则所有事务利用快照回滚

二阶段:

  • 若无异常 则批量删除快照

8.4.2. XA

一阶段:

  • 每个服务执行后 不提交 汇报状态给TC

二阶段:

  • 执行完成后 TC若发现异常 所有事物回滚
  • 若无 则提交提交事务
0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
羊子
作者分享
计算机网络思维导图 进程章
2
今天差点入职 java 实习了 还是一家大公司 结果走入职通道被告知上面领导说专升本不要 硬性条件 服了 原来我只写了两年本科 没有写专科 心情起伏太大了 专升本 gg 了吗🥹 我应该注意什么
7
周五和周一面了两家 今天又遭拷打 周五: 1.自我介绍 2.讲讲你的项目 3.ArrayList 和 linkedList 的区别(真的好爱问) 4.创建线程的方式 5.redis 有哪些数据结构 6.mysql 的 sql 优化的思路 7.索引失效的原因 8.模糊查询索引失效有什么解决办法 9.ThreadLocal 讲一下 反问 找人的倾向 实习生工作 上班时间 周一(今天): 1.自我介绍 2.为什么没有想去考研和考公 前端我不知道为啥问了我很多问题? 3.项目用到了 react ? 4.你怎么调用的 AI 哪家 AI 名字是什么 5.项目中你觉得难点在哪里 6.你的生成图表的项目临时自己更换生成的结果吗 结果是流式输出的吗 知道流式输出吗 后端怎么实现的(麻了) 7.vue 的生命周期 8.你用的 ts 那么 ts 和 js 的区别是什么 ts 有哪些类型 9.你用的 elementUI 你自定义过样式吗 怎么做的 10. 你是怎么调教 AI 提示词的 调过 openAI 吗 后端 1.ArrayList 和 linkedList 的区别 2.用过 mangodb 吗 用过 python 吗 3.Spring 和 springboot 的区别 4.自己搭建过 mysql 吗 遇到什么问题了吗(这些个问题什么意思) 自己搭建过 redis 吗 5.redis 怎么清除所有的 key 6.你知道哪些数据库 你知道 sql 和 nosql 的区别吗 7.你有深入了解过 Es 吗 讲一下 Es 8.创建线程的方式 9.你知道数据库的范式吗 现在让你设计一个课程表 你会怎么设计 有哪些范式要注意(真没见过) 10.你的绩点 3.5 平均分多少 你的课上完了吗 下学期有哪些课你知道吗 学分修了多少了 反问 实习生的业务 其他没问了 我感觉回答的很不好 很多都半懂 估计 g 了 太难啦!
7
面试总结
5
MQ总结 v1.0
10
下载 APP