SpringClould微服务+分布式事务笔记

SpringClould微服务

拆分项目步骤:

创建模块
导入依赖
复制启动类
复制配置文件 application.yaml
复制有关此模块的代码(domain、controller、service、mapper)
新建数据库
配置启动项
服务注册:
<!--nacos 服务注册发现-->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
spring:application:name: item-service # 服务名称cloud:nacos:server-addr: 192.168.150.101:8848 # nacos地址

配置启动项 VM options中参数: -Dserver.port=8081

OpenFeign
  <!--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>

在启动类上添加开启注解 @EnableFeignClients

//1.声明扫描包
@EnableFeignClients(basePackages = "com.hmall.api.client")
//2.声明要用的FeignClient
@EnableFeignClients(clients = {ItemClient.class})

定义接口:

package com.hmall.cart.client;@FeignClient("item-service")
public interface ItemClient {@GetMapping("/items")List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids);
}

使用FeignClient,注入ItemClient使用

连接池

Feign底层发起http请求,依赖于其它的框架。其底层支持的http客户端实现包括:

  • HttpURLConnection:默认实现,不支持连接池
  • Apache HttpClient :支持连接池
  • OKHttp:支持连接池

使用带有连接池的客户端来代替默认的HttpURLConnection。

<!--OK http 的依赖 -->
<dependency><groupId>io.github.openfeign</groupId><artifactId>feign-okhttp</artifactId>
</dependency>
feign:okhttp:enabled: true # 开启OKHttp功能
日志配置
package com.hmall.api.config;import feign.Logger;
import org.springframework.context.annotation.Bean;// 配置类,定义Feign的日志级别
public class DefaultFeignConfig {@Beanpublic Logger.Level feignLogLevel(){return Logger.Level.FULL;}
}
  • 局部生效:在某个FeignClient中配置,只对当前FeignClient生效
@FeignClient(value = "item-service", configuration = DefaultFeignConfig.class)
  • 全局生效:在@EnableFeignClients中配置,针对所有FeignClient生效。
@EnableFeignClients(defaultConfiguration = DefaultFeignConfig.class)
网关路由
  • 创建网关微服务
  • 引入SpringCloudGateway、NacosDiscovery依赖
  • 编写启动类
  • 配置网关路由

创建新模块命名hm-gateway

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>hmall</artifactId><groupId>com.heima</groupId><version>1.0.0</version></parent><modelVersion>4.0.0</modelVersion><artifactId>hm-gateway</artifactId><properties><maven.compiler.source>11</maven.compiler.source><maven.compiler.target>11</maven.compiler.target></properties><dependencies><!--common--><dependency><groupId>com.heima</groupId><artifactId>hm-common</artifactId><version>1.0.0</version></dependency><!--网关--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><!--nacos discovery--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!--负载均衡--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency></dependencies><build><finalName>${project.artifactId}</finalName><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>
package com.hmall.gateway;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class GatewayApplication {public static void main(String[] args) {SpringApplication.run(GatewayApplication.class, args);}
}
server:port: 8080
spring:application:name: gatewaycloud:nacos:server-addr: 192.168.150.101:8848gateway:routes:- id: item # 路由规则id,自定义,唯一uri: lb://item-service # 路由的目标服务,lb代表负载均衡,会从注册中心拉取服务列表predicates: # 路由断言,判断当前请求是否符合当前规则,符合则路由到目标服务- Path=/items/**,/search/** # 这里是以请求路径作为判断规则- id: carturi: lb://cart-servicepredicates:- Path=/carts/**- id: useruri: lb://user-servicepredicates:- Path=/users/**,/addresses/**- id: tradeuri: lb://trade-servicepredicates:- Path=/orders/**- id: payuri: lb://pay-servicepredicates:- Path=/pay-orders/**

重点关注predicates,也就是路由断言。SpringCloudGateway中支持的断言类型有很多:

名称说明示例
After是某个时间点后的请求- After=2037-01-20T17:42:47.789-07:00[America/Denver]
Before是某个时间点之前的请求- Before=2031-04-13T15:14:47.433+08:00[Asia/Shanghai]
Between是某两个时间点之前的请求- Between=2037-01-20T17:42:47.789-07:00[America/Denver], 2037-01-21T17:42:47.789-07:00[America/Denver]
Cookie请求必须包含某些cookie- Cookie=chocolate, ch.p
Header请求必须包含某些header- Header=X-Request-Id, \d+
Host请求必须是访问某个host(域名)- Host=.somehost.org,.anotherhost.org
Method请求方式必须是指定方式- Method=GET,POST
Path请求路径必须符合指定规则- Path=/red/{segment},/blue/**
Query请求参数必须包含指定参数- Query=name, Jack或者- Query=name
RemoteAddr请求者的ip必须是指定范围- RemoteAddr=192.168.1.1/24
weight权重处理
网关登录校验
  • GatewayFilter:路由过滤器,作用范围比较灵活,可以是任意指定的路由Route.
  • GlobalFilter:全局过滤器,作用范围是所有路由,不可配置。

Gateway中内置了很多的GatewayFilter,详情可以参考官方文档:

https://docs.spring.io/spring-cloud-gateway/docs/3.1.7/reference/html/#gatewayfilter-factories

自定义GlobalFilter 登陆过滤器

package com.hmall.gateway.filter;@Component
@RequiredArgsConstructor
@EnableConfigurationProperties(AuthProperties.class)
public class AuthGlobalFilter implements GlobalFilter, Ordered {private final JwtTool jwtTool;private final AuthProperties authProperties;private final AntPathMatcher antPathMatcher = new AntPathMatcher();@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 1.获取RequestServerHttpRequest request = exchange.getRequest();// 2.判断是否不需要拦截if(isExclude(request.getPath().toString())){// 无需拦截,直接放行return chain.filter(exchange);}// 3.获取请求头中的tokenString token = null;List<String> headers = request.getHeaders().get("authorization");if (!CollUtils.isEmpty(headers)) {token = headers.get(0);}// 4.校验并解析tokenLong userId = null;try {userId = jwtTool.parseToken(token);} catch (UnauthorizedException e) {// 如果无效,拦截ServerHttpResponse response = exchange.getResponse();response.setRawStatusCode(401);return response.setComplete();}// 5.如果有效,传递用户信息String userInfo = userId.toString();ServerWebExchange ex = exchange.mutate().request(b -> b.header ("user-info",userInfo)).build();// 6.放行return chain.filter(exchange);}private boolean isExclude(String antPath) {for (String pathPattern : authProperties.getExcludePaths()) {if(antPathMatcher.match(pathPattern, antPath)){return true;}}return false;}@Overridepublic int getOrder() {return 0;}
}
微服务获取用户

保存用户到请求头

	// 5.如果有效,传递用户信息String userInfo = userId.toString();ServerWebExchange ex = exchange.mutate().request(b -> b.header ("user-info",userInfo)).build();// 6.放行return chain.filter(exchange);

拦截器获取用户

hm-common模块下定义一个拦截器:

package com.hmall.common.interceptor;public class UserInfoInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 1.获取请求头中的用户信息String userInfo = request.getHeader("user-info");// 2.判断是否为空if (StrUtil.isNotBlank(userInfo)) {// 不为空,保存到ThreadLocalUserContext.setUser(Long.valueOf(userInfo));}// 3.放行return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {// 移除用户UserContext.removeUser();}
}

接着在hm-common模块下编写SpringMVC的配置类,配置登录拦截器

package com.hmall.common.config;import com.hmall.common.interceptor.UserInfoInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class MvcConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new UserInfoInterceptor());}
}

不过,需要注意的是,这个配置类默认是不会生效的,因为它所在的包是com.hmall.common.config,与其它微服务的扫描包不一致,无法被扫描到,因此无法生效。

基于SpringBoot的自动装配原理,我们要将其添加到resources目录下的META-INF/spring.factories文件中:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.hmall.common.config.MyBatisConfig,\com.hmall.common.config.MvcConfig
OpenFeign传递用户

微服务之间调用是基于OpenFeign来实现的,并不是我们自己发送的请求。

这里要借助Feign中提供的一个拦截器接口:feign.RequestInterceptor

hm-api模块的com.hmall.api.config.DefaultFeignConfig中编写这个拦截器:

@Bean
public RequestInterceptor userInfoRequestInterceptor(){return new RequestInterceptor() {@Overridepublic void apply(RequestTemplate template) {// 获取登录用户Long userId = UserContext.getUser();if(userId == null) {// 如果为空则直接跳过return;}// 如果不为空则放入请求头中,传递给下游微服务template.header("user-info", userId.toString());}};
}

现在微服务之间通过OpenFeign调用时也会传递登录用户信息了。

配置管理

可以把微服务共享的配置抽取到Nacos中统一管理,这样就不需要每个微服务都重复配置了。分为两步:

  • 在Nacos中添加共享配置
  • 微服务拉取配置

在nacos控制台分别添加这些配置。

首先是jdbc相关配置,在配置管理->配置列表中点击+新建一个配置:

spring:datasource:url: jdbc:mysql://${hm.db.host:192.168.150.101}:${hm.db.port:3306}/${hm.db.database}?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghaidriver-class-name: com.mysql.cj.jdbc.Driverusername: ${hm.db.un:root}password: ${hm.db.pw:123}
mybatis-plus:configuration:default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandlerglobal-config:db-config:update-strategy: not_nullid-type: auto

注意这里的jdbc的相关参数并没有写死,例如:

  • 数据库ip:通过${hm.db.host:192.168.150.101}配置了默认值为192.168.150.101,同时允许通过${hm.db.host}来覆盖默认值
  • 数据库端口:通过${hm.db.port:3306}配置了默认值为3306,同时允许通过${hm.db.port}来覆盖默认值
  • 数据库database:可以通过${hm.db.database}来设定,无默认值

日志配置,命名为shared-log.yaml,配置内容如下:

logging:level:com.hmall: debugpattern:dateformat: HH:mm:ss:SSSfile:path: "logs/${spring.application.name}"

swagger配置,命名为shared-swagger.yaml,配置内容如下:

knife4j:enable: trueopenapi:title: ${hm.swagger.title:黑马商城接口文档}description: ${hm.swagger.description:黑马商城接口文档}email: ${hm.swagger.email:zhanghuyi@itcast.cn}concat: ${hm.swagger.concat:虎哥}url: https://www.itcast.cnversion: v1.0.0group:default:group-name: defaultapi-rule: packageapi-rule-resources:- ${hm.swagger.package}

拉取共享配置

SpringCloud在初始化上下文的时候会先读取一个名为bootstrap.yaml(或者bootstrap.properties)的文件,如果我们将nacos地址配置到bootstrap.yaml中,那么在项目引导阶段就可以读取nacos中的配置了。

在cart-service模块引入依赖:

  <!--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>

新建bootstrap.yaml

spring:application:name: cart-service # 服务名称profiles:active: devcloud:nacos:server-addr: 192.168.150.101 # nacos地址config:file-extension: yaml # 文件后缀名shared-configs: # 共享配置- dataId: shared-jdbc.yaml # 共享mybatis配置- dataId: shared-log.yaml # 共享日志配置- dataId: shared-swagger.yaml # 共享日志配置

修改application.yaml

由于一些配置挪到了bootstrap.yaml,因此application.yaml需要修改为:

server:port: 8082
feign:okhttp:enabled: true # 开启OKHttp连接池支持
hm:swagger:title: 购物车服务接口文档package: com.hmall.cart.controllerdb:database: hm-cart

配置热更新

  • 在Nacos中添加配置
  • 在微服务读取配置

注意文件的dataId格式:

[服务名]-[spring.active.profile].[后缀名]

文件名称由三部分组成:

  • 服务名:我们是购物车服务,所以是cart-service
  • spring.active.profile:就是spring boot中的spring.active.profile,可以省略,则所有profile共享该配置
  • 后缀名:例如yaml

这里我们直接使用cart-service.yaml这个名称,则不管是dev还是local环境都可以共享该配置。

配置内容如下:

hm:cart:maxAmount: 1 # 购物车商品数量上限

cart-service中新建一个属性读取类:

package com.hmall.cart.config;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;@Data
@Component
@ConfigurationProperties(prefix = "hm.cart")
public class CartProperties {private Integer maxAmount;
}

接着,在业务中使用该属性加载类

无需重启服务,配置热更新就生效了!

微服务保护

微服务保护的方案有很多,比如:

  • 请求限流
  • 线程隔离
  • 服务熔断

都属于服务降级的方案

1.请求限流

限制或控制接口访问的并发流量,避免服务因流量激增而出现故障。请求限流往往会有一个限流器,数量高低起伏的并发请求曲线,经过限流器就变的非常平稳。

2.线程隔离

当一个业务接口响应时间长,而且并发高时,就可能耗尽服务器的线程资源,导致服务内的其它接口受到影响。

线程隔离的思想来自轮船的舱壁模式:轮船的船舱会被隔板分割为N个相互隔离的密闭舱,假如轮船触礁进水,只有损坏的部分密闭舱会进水,而其他舱由于相互隔离,并不会进水。

我们可以限定每个接口可以使用的资源范围,也就是将其“隔离”起来。

3.服务熔断

  • 编写服务降级逻辑:就是服务调用失败后的处理逻辑,根据业务场景,可以抛出异常,也可以返回友好提示或默认数据。
  • 异常统计和熔断:统计服务提供方的异常比例,当比例过高表明该接口会影响到其它服务,应该拒绝调用该接口,而是直接走降级逻辑。

Sentinel

启动命令

java -Dserver.port=8090 -Dcsp.sentinel.dashboard.server=localhost:8090 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar

微服务整合

cart-service模块中整合sentinel,连接sentinel-dashboard控制台,步骤如下:

1)引入sentinel依赖

<!--sentinel-->
<dependency><groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

2)配置控制台

修改application.yaml文件,添加下面内容:

spring:cloud: sentinel:transport:dashboard: localhost:8090

3)访问cart-service的任意端点

重启cart-service,然后访问查询购物车接口,sentinel的客户端就会将服务访问的信息提交到sentinel-dashboard控制台。并展示出统计信息:

我们的SpringMVC接口是按照Restful风格设计,因此购物车的查询、删除、修改等接口全部都是/carts路径:

选择打开Sentinel的请求方式前缀,把请求方式 + 请求路径作为簇点资源名:

spring:cloud:sentinel:transport:dashboard: localhost:8090http-method-specify: true # 开启请求方式前缀

配置 请求限流QPS:6

配置 线程隔离

OpenFeign整合Sentinel 修改cart-service模块的application.yml文件,开启Feign的sentinel功能:

feign:sentinel:enabled: true # 开启feign对sentinel的支持

重启cart-service服务,可以看到查询商品的FeignClient自动变成了一个簇点资源

点击查询商品的FeignClient对应的簇点资源后面的流控按钮

配置 并发线程数:5

服务熔断

我们利用线程隔离对查询购物车业务进行隔离,保护了购物车服务的其它接口。由于查询商品的功能耗时较高(我们模拟了500毫秒延时),再加上线程隔离限定了线程数为5,导致接口吞吐能力有限,最终QPS只有10左右。这就导致了几个问题:

第一,超出的QPS上限的请求就只能抛出异常,从而导致购物车的查询失败。但从业务角度来说,即便没有查询到最新的商品信息,购物车也应该展示给用户,用户体验更好。也就是给查询失败设置一个降级处理逻辑。

第二,由于查询商品的延迟较高(模拟的500ms),从而导致查询购物车的响应时间也变的很长。这样不仅拖慢了购物车服务,消耗了购物车服务的更多资源,而且用户体验也很差。对于商品服务这种不太健康的接口,我们应该直接停止调用,直接走降级逻辑,避免影响到当前服务。也就是将商品查询接口熔断

给FeignClient编写失败后的降级逻辑有两种方式:

  • 方式一:FallbackClass,无法对远程调用的异常做处理
  • 方式二:FallbackFactory,可以对远程调用的异常做处理,我们一般选择这种方式。

编写降级策略

1.在hm-api模块中给ItemClient定义降级处理类,实现FallbackFactory

package com.hmall.api.client.fallback;@Slf4j
public class ItemClientFallback implements FallbackFactory<ItemClient> {@Overridepublic ItemClient create(Throwable cause) {return new ItemClient() {@Overridepublic List<ItemDTO> queryItemByIds(Collection<Long> ids) {log.error("远程调用ItemClient#queryItemByIds方法出现异常,参数:{}", ids, cause);// 查询购物车允许失败,查询失败,返回空集合return CollUtils.emptyList();}@Overridepublic void deductStock(List<OrderDetailDTO> items) {// 库存扣减业务需要触发事务回滚,查询失败,抛出异常throw new BizIllegalException(cause);}};}
}

2.在hm-api模块中的com.hmall.api.config.DefaultFeignConfig类中将ItemClientFallback注册为一个Bean

@Bean
public ItemClientFallbackFactory itemClientFallbackFactory(){return new ItemClientFallbackFactory();
}

3.在hm-api模块中的ItemClient接口中使用ItemClientFallbackFactory

@FeignClient(name = "item-service",fallbackFactory = ItemClientFallbackFactory.class)
public interface ItemClient {@GetMapping("/items")List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids);@PutMapping("/items/stock/deduct")void deductStock(@RequestBody List<OrderDetailDTO> items);
}

服务熔断

状态机包括三个状态:

  • closed:关闭状态,断路器放行所有请求,并开始统计异常比例、慢请求比例。超过阈值则切换到open状态
  • open:打开状态,服务调用被熔断,访问被熔断服务的请求会被拒绝,快速失败,直接走降级逻辑。Open状态持续一段时间后会进入half-open状态
  • half-open:半开状态,放行一次请求,根据执行结果来判断接下来的操作。
    • 请求成功:则切换到closed状态
    • 请求失败:则切换到open状态

我们可以在控制台通过点击簇点后的**熔断**按钮来配置熔断策略:

熔断策略:慢调用比例最大ResponseTime:200ms比例阈值0.5熔断时长20s最小请求数5统计时长1000ms

这种是按照慢调用比例来做熔断,上述配置的含义是:

  • RT超过200毫秒的请求调用就是慢调用
  • 统计最近1000ms内的最少5次请求,如果慢调用比例不低于0.5,则触发熔断
  • 熔断持续时长20s

分布式事务

在Seata的事务管理中有三个重要的角色:

  • TC (Transaction Coordinator) - **事务协调者:**维护全局和分支事务的状态,协调全局事务提交或回滚。
  • TM (Transaction Manager) - **事务管理器:**定义全局事务的范围、开始全局事务、提交或回滚全局事务。
  • RM (Resource Manager) - **资源管理器:**管理分支事务,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

导入数据库表:

CREATE DATABASE IF NOT EXISTS `seata`;
USE `seata`;CREATE TABLE IF NOT EXISTS `global_table`
(`xid`                       VARCHAR(128) NOT NULL,`transaction_id`            BIGINT,`status`                    TINYINT      NOT NULL,`application_id`            VARCHAR(32),`transaction_service_group` VARCHAR(32),`transaction_name`          VARCHAR(128),`timeout`                   INT,`begin_time`                BIGINT,`application_data`          VARCHAR(2000),`gmt_create`                DATETIME,`gmt_modified`              DATETIME,PRIMARY KEY (`xid`),KEY `idx_status_gmt_modified` (`status` , `gmt_modified`),KEY `idx_transaction_id` (`transaction_id`)
) ENGINE = InnoDBDEFAULT CHARSET = utf8mb4;CREATE TABLE IF NOT EXISTS `branch_table`
(`branch_id`         BIGINT       NOT NULL,`xid`               VARCHAR(128) NOT NULL,`transaction_id`    BIGINT,`resource_group_id` VARCHAR(32),`resource_id`       VARCHAR(256),`branch_type`       VARCHAR(8),`status`            TINYINT,`client_id`         VARCHAR(64),`application_data`  VARCHAR(2000),`gmt_create`        DATETIME(6),`gmt_modified`      DATETIME(6),PRIMARY KEY (`branch_id`),KEY `idx_xid` (`xid`)
) ENGINE = InnoDBDEFAULT CHARSET = utf8mb4;CREATE TABLE IF NOT EXISTS `lock_table`
(`row_key`        VARCHAR(128) NOT NULL,`xid`            VARCHAR(128),`transaction_id` BIGINT,`branch_id`      BIGINT       NOT NULL,`resource_id`    VARCHAR(256),`table_name`     VARCHAR(32),`pk`             VARCHAR(36),`status`         TINYINT      NOT NULL DEFAULT '0' COMMENT '0:locked ,1:rollbacking',`gmt_create`     DATETIME,`gmt_modified`   DATETIME,PRIMARY KEY (`row_key`),KEY `idx_status` (`status`),KEY `idx_branch_id` (`branch_id`),KEY `idx_xid_and_branch_id` (`xid` , `branch_id`)
) ENGINE = InnoDBDEFAULT CHARSET = utf8mb4;CREATE TABLE IF NOT EXISTS `distributed_lock`
(`lock_key`       CHAR(20) NOT NULL,`lock_value`     VARCHAR(20) NOT NULL,`expire`         BIGINT,primary key (`lock_key`)
) ENGINE = InnoDBDEFAULT CHARSET = utf8mb4;INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('AsyncCommitting', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryCommitting', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryRollbacking', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('TxTimeoutCheck', ' ', 0);

准备配置文件:

server:port: 7099spring:application:name: seata-serverlogging:config: classpath:logback-spring.xmlfile: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_logstashconsole:user:username: adminpassword: adminseata:config:# support: nacos, consul, apollo, zk, etcd3type: nacosnacos:server-addr: 36.138.103.23:8848group : "DEFAULT_GROUP"namespace: ""dataId: "seataServer.properties"username: "nacos"password: "nacos"registry:# support: nacos, eureka, redis, zk, consul, etcd3, sofatype: nacosnacos:application: seata-serverserver-addr: 36.138.103.23:8848group : "DEFAULT_GROUP"namespace: ""username: "nacos"password: "nacos"
#  server:
#    service-port: 8091 #If not configured, the default is '${server.port} + 1000'security:secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017tokenValidityInMilliseconds: 1800000ignore:urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/api/v1/auth/loginserver:# service-port: 8091 #If not configured, the default is '${server.port} + 1000'max-commit-retry-timeout: -1max-rollback-retry-timeout: -1rollback-retry-timeout-unlock-enable: falseenable-check-auth: trueenable-parallel-request-handle: trueretry-dead-threshold: 130000xaer-nota-retry-timeout: 60000enableParallelRequestHandle: truerecovery:committing-retry-period: 1000async-committing-retry-period: 1000rollbacking-retry-period: 1000timeout-retry-period: 1000undo:log-save-days: 7log-delete-period: 86400000session:branch-async-queue-size: 5000 #branch async remove queue sizeenable-branch-async-remove: false #enable to asynchronous remove branchSessionstore:# support: file 、 db 、 redismode: dbsession:mode: dblock:mode: dbdb:datasource: druiddb-type: mysqldriver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://36.138.103.23:3306/seata?rewriteBatchedStatements=trueuser: rootpassword: 123zxc..min-conn: 10max-conn: 100global-table: global_tablebranch-table: branch_tablelock-table: lock_tabledistributed-lock-table: distributed_lockquery-limit: 1000max-wait: 5000redis:mode: singledatabase: 0min-conn: 10max-conn: 100password:max-total: 100query-limit: 1000single:host: 192.168.150.101port: 6379metrics:enabled: falseregistry-type: compactexporter-list: prometheusexporter-prometheus-port: 9898transport:rpc-tc-request-timeout: 15000enable-tc-server-batch-send-response: falseshutdown:wait: 3thread-factory:boss-thread-prefix: NettyBossworker-thread-prefix: NettyServerNIOWorkerboss-thread-size: 1

docker命令启动

docker run --name seata \
-p 8099:8099 \
-p 7099:7099 \
-e SEATA_IP=36.138.103.23 \
-v ./seata:/seata-server/resources \
--privileged=true \
-d \
seataio/seata-server:1.5.2
微服务集成Seata
<!--统一配置管理--><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><!--seata--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId></dependency><!--sentinel--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency>
改造配置

首先在nacos上添加一个共享的seata配置,命名为shared-seata.yaml

seata:registry: # TC服务注册中心的配置,微服务根据这些信息去注册中心获取tc服务地址type: nacos # 注册中心类型 nacosnacos:server-addr: 192.168.150.101:8848 # nacos地址namespace: "" # namespace,默认为空group: DEFAULT_GROUP # 分组,默认是DEFAULT_GROUPapplication: seata-server # seata服务名称username: nacospassword: nacostx-service-group: hmall # 事务组名称service:vgroup-mapping: # 事务组与tc集群的映射关系hmall: "default"data-source-proxy-mode: XA # 开启XA模式,强一致性(默认为AT模式)

添加bootstrap.yaml

spring:application:name: trade-service # 服务名称profiles:active: devcloud:nacos:server-addr: 192.168.150.101 # nacos地址config:file-extension: yaml # 文件后缀名shared-configs: # 共享配置- dataId: shared-jdbc.yaml # 共享mybatis配置- dataId: shared-log.yaml # 共享日志配置- dataId: shared-swagger.yaml # 共享日志配置- dataId: shared-seata.yaml # 共享seata配置

改造application.yaml文件

server:port: 8085
feign:okhttp:enabled: true # 开启OKHttp连接池支持sentinel:enabled: true # 开启Feign对Sentinel的整合
hm:swagger:title: 交易服务接口文档package: com.hmall.trade.controllerdb:database: hm-trade

添加数据库表seata-at.sql

分别文件导入hm-trade、hm-cart、hm-item微服务需要开启事务的每个数据库

-- for AT mode you must to init this sql for you business database. the seata server not need it.
CREATE TABLE IF NOT EXISTS `undo_log`
(`branch_id`     BIGINT       NOT NULL COMMENT 'branch transaction id',`xid`           VARCHAR(128) NOT NULL COMMENT 'global transaction id',`context`       VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',`rollback_info` LONGBLOB     NOT NULL COMMENT 'rollback info',`log_status`    INT(11)      NOT NULL COMMENT '0:normal status,1:defense status',`log_created`   DATETIME(6)  NOT NULL COMMENT 'create datetime',`log_modified`  DATETIME(6)  NOT NULL COMMENT 'modify datetime',UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDBAUTO_INCREMENT = 1DEFAULT CHARSET = utf8mb4 COMMENT ='AT transaction mode undo table';

将需要开启事务的方法上@Transactional注解改为Seata提供的@GlobalTransactional

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/605813.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Prometheus Blackbox_exporter笔记

一、安装Promtheus 在 Prometheus 官网 Download | Prometheus 获取适用于 Linux 的 Prometheus 安 装包&#xff0c;这里我选择最新的 2.46.0 版本&#xff0c;我是 Linux 系统&#xff0c;选择下载 prometheus-2.46.0.linux-amd64.tar.gz 下载安装包&#xff1a; wget htt…

Gateway相关问题及答案(2024)

1、什么是API Gateway&#xff1f;它在微服务架构中有什么作用&#xff1f; API Gateway是一个服务器&#xff0c;通常是一个可以管理和处理所有进出应用程序的请求的反向代理。在微服务架构中&#xff0c;它作为单一的入口点&#xff0c;统一接收来自客户端的调用请求&#x…

多国管理中心多语言区块链源码一元夺宝程序仿趣步奕跑/原生计步器/原生人脸识别

前后台分开的&#xff0c;后台是TP3.2的框架了&#xff0c;应该是比较老的程序了。 目前把整体UI 改版黄色系风格&#xff0c;集成了一元夺宝程序&#xff0c;用户数据同步趣步&#xff0c;效果看起来很棒&#xff0c;另外加入股票走势图&#xff08;K线图&#xff09;&#xf…

世微AP5126宽电压12-80V 1.5A平均电流型LED降压恒流驱动内置IC

产品描述 AP5126 是一款 PWM 工作模式,高效率、外 围简单、内置功率管&#xff0c;适用于 12-80V 输入的高 精度降压 LED 恒流驱动芯片。输出功率可达 15W&#xff0c;电流 1.2A。 AP5126 可实现全亮/半亮功能切换&#xff0c;通过 MODE 切换&#xff1a;全亮/半亮/循环模式…

创建数据库用户

要在MySQL数据库中创建用户&#xff0c;您可以使用以下命令&#xff1a; CREATE USER usernamelocalhost IDENTIFIED BY password;其中&#xff0c;username是您要创建的用户名&#xff0c;localhost表示用户只能从本地连接到数据库&#xff0c;password是用户的密码。 如果要…

使用 Jamf Pro 和 Okta 工作流程实现自动化苹果设备管理

Jamf的销售工程师Vincent Bonnin与Okta的产品经理Emily Wendell一起介绍了JNUC 2021的操作方法会议。它们涵盖了Okta工作流程&#xff08;Okta Workflow&#xff09;&#xff0c;并在其中集成了Jamf Pro&#xff0c;构建了一些工作流程&#xff0c;并提供了几个用例。 Okta 工作…

开发板VS评估板

共性 该设计是围绕特定组件构建的&#xff0c;使设计人员能够在已知可以正常工作的组装板上对该组件进行试验。所有数字和模拟接口都暴露在连接器和/或测试点处&#xff0c;以便可以连接外围设备和其他评估/开发板。基本功能所需的其他支持电路&#xff08;例如电源调节或天线…

HTML5和JS实现明媚月色效果

HTML5和JS实现明媚月色效果 先给出效果图&#xff1a; 源码如下&#xff1a; <!DOCTYPE html> <html> <head><title>明媚月光效果</title><style>body {margin: 0;overflow: hidden;background-color: #000; /* 添加一个深色背景以便看到…

SSM医院预约挂号系统【源码】【最详细运行文档】

SSM医院预约挂号系统【源码】【最详细运行文档】 系统简介系统涉及系统运行系统演示源码获取 系统简介 随着医疗水平的提高&#xff0c;以及人们对于健康的观念越来越重视&#xff0c;出入医院成了一种常见的现象。而随着看病人数增多&#xff0c;经常出现挂号难的现象。一部分…

ME8206 电流模式准谐振 PWM 控制器 输出功率可达100W

描述&#xff1a; ME8206 是一个电流模式准谐振 PWM 控制器&#xff0c;在 85V-265V 的宽电压范围内提供高达 100W 的输出功率&#xff0c; 在大范围的负载和输入电压变化情况下确保谷底电压导通。 ME8206 采用高压启动设计&#xff0c;芯片直接连接到高压&#xff0c; 以提供芯…

word技巧

word这个东西有的时候令人又爱又恨&#xff0c;很多用上的时候都是因为自己贫瘠的技巧而令人抓狂&#xff0c;比如设置一个页脚和页眉&#xff0c;首页如何不同&#xff1f;目录页如何设置罗马数字&#xff1f;页眉前面几页不要横线&#xff0c;后面几页要横线&#xff0c;谨以…

SQL注入是什么呢?

SQL注入是一种常见的攻击方式&#xff0c;攻击者通过在Web应用程序的输入字段中插入恶意的SQL代码&#xff0c;从而可以执行未经授权的数据库操作。这种攻击方式通常利用了应用程序对用户输入的不充分验证和过滤&#xff0c;使得攻击者能够通过输入恶意代码来绕过应用程序的安全…

【技术科普】什么是达芬奇架构?有什么优势?

芯片架构是指芯片设计的基本结构和组织方式&#xff0c;用于实现各种计算、存储和通信功能。芯片架构通常包括处理器核心、内存、输入输出接口等组成部分&#xff0c;这些部分的设计对芯片性能和功耗有着直接的影响&#xff0c;世界上主流的芯片架构主要包括x86、ARM、PowerPC和…

RAC 环境下spfile 下参数的修改,所有node或个别node

------ 和没有 SQL> create spfiledata from pfile/tmp/pfile; File created. SQL> exit Disconnected from Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production Version 19.20.0.0.0 [oraclerac1 ~]$ srvctl config database -d cdb Database …

【C语言】一种状态超时阻塞循环查询的办法

【C语言】一种状态超时阻塞循环查询的办法 文章目录 【C语言】一种状态超时阻塞循环查询的办法1.方法12.方法21.方法1 static void wait_notify_async(notify_type_t notify_type) {static rt_tick_t exit_tick;exit_tick = rt_time_get_msec();lb_int32 notify_success = RT_F…

vue-springboot基于java的社区志愿者活动信息管理系统 e2y4d

社区志愿者信息管理系统的主要开发目标如下&#xff1a; &#xff08;1&#xff09;对零碎化、分布散的数据信息进行收纳、整理&#xff0c;通过网络服务平台使这些信息内容更加调理&#xff0c;更加方便化和清晰化&#xff0c;让访问该系统的每个用户享受浏览的过程。 &#x…

5308. 公路

题意 有n 个站点&#xff0c;站点可以加油&#xff0c;站点之间的油的价格不一定相等&#xff0c;站点的编号从1到n&#xff0c;站点之间的距离用v表示&#xff0c;站点的油价用a表示&#xff0c;求从1站点到n站点所需要的最小的油价是多少 数据范围 对于所有测试数据保证&a…

google 内购记录

1.GooglePlay内购服务器验单配置 &#xff08;新版本好像不需要API 配置关联&#xff0c;好像不需要了&#xff09; 详细配置url: https://blog.csdn.net/wuyutaoktm/article/details/122450878 2. 用golang代码实现&#xff0c;主要是验证订单和发币,PAY_JSON 是上一个步骤申…

Java后端开发——Ajax、jQuery和JSON

Java后端开发——Ajax、jQuery和JSON 概述 Ajax全称是Asynchronous Javascript and XML&#xff0c;即异步的JavaScript和 XML。Ajax是一种Web应用技术&#xff0c;该技术是在JavaScript、DOM、服务器配合下&#xff0c;实现浏览器向服务器发送异步请求。 Ajax异步请求方式不…

螺纹钢负公差测径仪获得哪些信息?

螺纹钢负公差轧制是很多厂家在使用的一种轧制方式&#xff0c;蓝鹏测控在推出普通型轧钢测径仪和智能型轧钢测径仪后&#xff0c;又推出了可显示负公差信息的测径仪&#xff0c;本文来简单看一下螺纹钢负公差轧制能从测径仪中获得哪些信息&#xff1f; 负公差测径仪为了能对螺纹…