0%

SpringCloud笔记

SpringCloud

Nacos :服务发现与注册,配置管理

nacos_map

服务注册与发现

主模块依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring-cloud.version>Hoxton.SR8</spring-cloud.version>
<springboot.version>2.3.2.RELEASE</springboot.version>
<springcloudalibaba.version>2.2.5.RELEASE</springcloudalibaba.version>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${springboot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${springcloudalibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>

</dependencies>
</dependencyManagement>

==服务提供者==

依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- 配置中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

<!--服务注册与发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

</dependencies>

配置

1
2
3
4
5
6
7
8
9
10
11
12
server:
port: 8082

spring:
application:
name: nacos-stock
cloud:
nacos:
server-addr: 127.0.0.1:8848
discovery:
namespace: 793d1b0e-e601-4b41-bed1-7a8bc50768ab #指定命名空间
cluster-name: wang #集群名

主启动类

1
2
3
4
5
6
7
8
@SpringBootApplication
@EnableDiscoveryClient //能够让注册中心发现
public class NacosStockApplication {

public static void main(String[] args) {
SpringApplication.run(NacosStockApplication.class,args);
}
}

Controller

1
2
3
4
5
6
7
8
@RestController
public class StockController {

@RequestMapping("/stock/test")
public String test(String info){
return "库存模块接收到的信息:"+info;
}
}

==服务消费者==

依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- 配置中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

<!--服务注册与发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>3.0.3</version>
</dependency>

</dependencies>

配置

1
2
3
4
5
6
7
8
9
10
11
12
13
server:
port: 8081


spring:
application:
name: nacos-order #配置项目名称
cloud:
nacos:
server-addr: 127.0.0.1:8848 #配置中心地址
discovery:
namespace: 793d1b0e-e601-4b41-bed1-7a8bc50768ab #指定命名空间 相互调用的服务要在同一命名空间下
cluster-name: wang #集群名

主启动类

1
2
3
4
5
6
7
8
9
@SpringBootApplication
@EnableDiscoveryClient //能够让注册中心发现
@EnableFeignClients(basePackages = "com.wang.feign") //开启Feign客户端支持
public class NacosOrderApplication {

public static void main(String[] args) {
SpringApplication.run(NacosOrderApplication.class,args);
}
}

通过openfeign调用

1
2
3
4
5
6
7
@FeignClient("nacos-stock")  //服务名
public interface StockFeignService {

@RequestMapping("/stock/test")
String test(@RequestParam("info") String info);

}

Controller

1
2
3
4
5
6
7
8
9
10
11
12
@RestController
public class OrderController {

@Autowired
private StockFeignService stockFeignService;

@RequestMapping("/order/test")
public String test(){
return stockFeignService.test("order");
}

}

配置管理

依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- 配置中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

</dependencies>

配置

1
2
3
4
5
server:
port: 8086
spring:
profiles:
active: info
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#配置项目名称
spring.application.name=nacos-config-test

#配置服务注册中心地址
#spring.cloud.nacos.server-addr=127.0.0.1:8848
spring.cloud.nacos.server-addr=127.0.0.1:8848,127.0.0.1:8850,127.0.0.1:8852

#配置服务配置中心地址
spring.cloud.nacos.config.server-addr=127.0.0.1:8848,127.0.0.1:8850,127.0.0.1:8852

#指定properties格式的配置
spring.cloud.nacos.config.file-extension=properties
#指定分组
spring.cloud.nacos.config.group=TEST_GROUP

#配置命名空间
spring.cloud.nacos.config.namespace=793d1b0e-e601-4b41-bed1-7a8bc50768ab

#配置组 默认是DEFAULT_GROUP
#spring.cloud.nacos.config.group=DEFAULT_GROUP

#指定Data Id 命名规范:${spring.application.name 服务名}-${spring.profiles.active 环境}.${spring.cloud.nacos.config.file-extension 格式的配置}
#spring.cloud.nacos.config.name=nacos-config.properties


###################################多配置集
#data-id
spring.cloud.nacos.config.extension-configs[0].data-id=mysql-common.properties
#group
spring.cloud.nacos.config.extension-configs[0].group=DEFAULT_GROUP
#自动刷新
spring.cloud.nacos.config.extension-configs[0].refresh=true

spring.cloud.nacos.config.extension-configs[1].data-id=redis-common.properties
spring.cloud.nacos.config.extension-configs[1].group=DEFAULT_GROUP
spring.cloud.nacos.config.extension-configs[1].refresh=true

spring.cloud.nacos.config.extension-configs[2].data-id=crm.properties
spring.cloud.nacos.config.extension-configs[2].group=CRM_GROUP
spring.cloud.nacos.config.extension-configs[2].refresh=true

spring.cloud.nacos.config.extension-configs[3].data-id=oa-properties
spring.cloud.nacos.config.extension-configs[3].group=OA_GROUP
spring.cloud.nacos.config.extension-configs[3].refresh=true

spring.cloud.nacos.config.extension-configs[4].data-id=nacos-config.properties
spring.cloud.nacos.config.extension-configs[4].group=DEFAULT_GROUP
spring.cloud.nacos.config.extension-configs[4].refresh=true

users.name=wang
users.age=10

主启动类

1
2
3
4
5
6
@SpringBootApplication
public class NacosConfigApplication {
public static void main(String[] args) {
SpringApplication.run(NacosConfigApplication.class,args);
}
}

Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
@RestController
@RefreshScope //支持nacos的动态刷新功能
public class NacosConfigController {

@Value("${users.name}")
private String name;

@Value("${users.age}")
private Integer age;


@Value("${users.mysql.common}")
private String mysql;

@Value("${users.redis.common}")
private String redis;

@Value("${users.crm.config}")
private String crm;

@Value("${users.oa.config}")
private String oa;

@Value("${student.name}")
private String sname;

@GetMapping("/getConfigInfo")
public String getConfigInfo(){
return name+":"+ age;
}


@GetMapping("/getCRMConfigInfo")
public String getCRMConfigInfo(){
return mysql+":"+ redis+":"+crm+":"+oa;
}

@GetMapping("/getCRMConfigInfo1")
public String getCRMConfigInfo1(){
return sname;
}

}

Nacos配置MySQL数据库

1.到Mysql中执行nacos-mysql.sql

image-20210823175714502

2.application.properties中添加以下代码

image-20210823175837434

1
2
3
4
5
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://127.0.0.1:3306/config_info?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user=root
db.password=123456

image-20210823180038094

配置集群模式

默认是集群

image-20210823180233023

image-20210823180203778

单机 :standalone

配置端口号和本机ip地址

image-20210823180916565

设置三个关联Nacos的端口号和ip地址

image-20210823180351150

复制三个nacos,重复上面操作

image-20210823180555621

Sentinel分布式系统的流量防卫兵

Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

Sentinel-features-overview

导入依赖

1
2
3
4
5
<!--spring-cloud-alibaba-sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

编写配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
server:
port: 8401

spring:
application:
name: nacos-stock4-sentinel
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 #nacos服务注册中心地址
sentinel:
transport:
dashboard: 127.0.0.1:8080 #sentinel地址
port: 8719 #默认8719端口,假如被占用会自动从8719开始依次加1扫描,直到找到没有被占用的端口

management:
endpoints:
web:
exposure:
include: '*'

主启动类

1
2
3
4
5
6
7
@SpringBootApplication
@EnableDiscoveryClient
public class MainApp8401 {
public static void main(String[] args) {
SpringApplication.run(MainApp8401.class,args);
}
}

controller

1
2
3
4
5
6
7
8
9
10
11
12
@RestController
public class FlowLimitController {
@RequestMapping("/testA")
public String testA(){
return "--------------testA";
}

@RequestMapping("/testB")
public String testB(){
return "--------------testB";
}
}

image-20210823144619297

实时监控

image-20210823144940329

流量控制

QPS:每秒的响应请求数

快速失败RuleConstant.CONTROL_BEHAVIOR_DEFAULT)方式是默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException。这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时

image-20210823145338165

QPS关联模式

如果A关联B,当B请求达到阈值,A请求会挂掉

image-20210823150324368

==Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)==方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过”冷启动”,让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。初始阈值为最大阈值除以3. 经过预热时长到达最大阈值

image-20210823152957276

匀速排队

匀速排队(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。

image

这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。

注意:匀速排队模式暂时不支持 QPS > 1000 的场景。

线程数

image-20210823145500651

服务降级—>服务端

降级是给服务提供者准备的

除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。

chain

现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。以上的问题在链路调用中会产生放大的效果。复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。因此我们需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。熔断降级作为保护自身的手段,通常在客户端(调用端)进行配置。

Sentinel 提供以下几种熔断策略:

  • 慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
  • 异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
  • 异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
  • image-20210824150103255

image-20210824144936621

QQ图片20210824145135

Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@RestController
public class FlowLimitController {
@RequestMapping("/testA")
public String testA() {
return "--------------testA";
}

@RequestMapping("/testB")
public String testB() {
return "--------------testB";
}


//热点规则限流 如果没有定义blockHandler方法,流量超出后,会进入异常页面
@RequestMapping("/testC")
@SentinelResource(value = "testC", blockHandler = "deal_testC")
//value:热点资源名 blockHandler:超出流量部分进入blockHandler兜底方法
public String testC(@RequestParam(value = "p1", required = false) String p1,
@RequestParam(value = "p2", required = false) String p2)
{
return "--------------testC";
}

//兜底方法
public String deal_testC(String p1, String p2, BlockException exception) {
return "testC-------热点限流";
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@RestController
public class RateLimitController {

@GetMapping("/byResource")
@SentinelResource(value = "byResource", blockHandler = "handleException") //按照资源限流,可自定义处理程序
public CommonResult byResource() {
return new CommonResult(200, "按照资源限流", new User("wang", 10));
}

public CommonResult handleException(BlockException exception) { //如果没有自定义处理程序,会报500(找不到FlowException)
return new CommonResult(444, exception.getClass().getCanonicalName());
}

@GetMapping("/byResourceUrl") //按照Url地址限流,会走系统默认处理程序
public CommonResult byResourceUrl() {
return new CommonResult(200, "按照Url地址限流", new User("wang", 10));
}

@GetMapping("/CustomerHandle") //
@SentinelResource(value = "CustomerHandle", blockHandlerClass = CustomerBlockHandle.class, blockHandler = "handleException1")
public CommonResult CustomerHandle() {
return new CommonResult(200, "按照资源地址限流", new User("wang", 10));
}

}

启动类

1
2
3
4
5
6
7
@SpringBootApplication
@EnableDiscoveryClient
public class MainApp8401 {
public static void main(String[] args) {
SpringApplication.run(MainApp8401.class,args);
}
}

application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
server:
port: 8401

spring:
application:
name: nacos-stock4-sentinel
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 #nacos服务注册中心地址
sentinel:
transport:
dashboard: 127.0.0.1:8080 #sentinel地址
port: 8719 #默认8719端口,假如被占用会自动从8719开始依次加1扫描,直到找到没有被占用的端口

management:
endpoints:
web:
exposure:
include: '*'

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<dependencies>

<dependency>
<groupId>com.wang</groupId>
<artifactId>nacos-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- 配置中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

<!--服务注册与发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

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

<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>3.0.3</version>
</dependency>
</dependencies>

服务熔断—>客户端

熔断是给服务消费者准备的

服务提供者

Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@RestController
public class PaymentController {

@Value("${server.port}")
private String serverPort;

public static HashMap<Integer, User> hashMap = new HashMap<>();
static {
hashMap.put(1,new User("王1",20));
hashMap.put(2,new User("王2",21));
hashMap.put(3,new User("王3",22));
}

@GetMapping("/paymentSQL/{id}")
public CommonResult<User> paymentSQL(@PathVariable("id") Integer id){
User user = hashMap.get(id);
CommonResult<User> result = new CommonResult(200 ,"端口"+ serverPort ,user);
return result;
}
}

application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server:
port: 9003

spring:
application:
name: nacos-provider
cloud:
nacos:
server-addr: 127.0.0.1:8848


management:
endpoints:
web:
exposure:
include: '*'

依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<dependencies>

<dependency>
<groupId>com.wang</groupId>
<artifactId>nacos-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- 配置中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

<!--服务注册与发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

</dependencies>

服务消费者

Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@RestController
public class ConsumerController {


@Resource
private ProviderFeign providerFeign;



@GetMapping("/ConsumerSQL/{id}")
// @SentinelResource(value = "fallback") //没有配置
// @SentinelResource(value = "fallback",fallback = "handleFallback") //fallback只负责业务异常
// @SentinelResource(value = "fallback",blockHandler = "blockHandler") //blockHandler只负责sentinel控制台的配置违规
@SentinelResource(value = "fallback",blockHandler = "blockHandler",fallback = "handleFallback",
exceptionsToIgnore = {IllegalArgumentException.class}) //exceptionsToIgnore忽略某个异常,不走兜底方法
public CommonResult<User> fallback(@PathVariable("id") Integer id){
CommonResult<User> result = providerFeign.paymentSQL(id);
if(id == 4){
throw new IllegalArgumentException("非法参数异常........");
}else if(result.getData() == null){
throw new NullPointerException("空指针异常..........");
}
return result;
}

public CommonResult<User> handleFallback(Integer id,Throwable e){
User user = new User("null", null);
return new CommonResult<>(444,"handleFallback兜底异常"+e.getMessage(),user);
}

public CommonResult<User> blockHandler(Integer id, BlockException e){
User user = new User("null", null);
return new CommonResult<>(445,"blockHandler兜底异常"+e.getLocalizedMessage(),user);
}


@GetMapping("/ConsumerSQL1/{id}")
public CommonResult<User> fallback1(@PathVariable("id") Integer id){
return providerFeign.paymentSQL(id);
}

}

ProviderFeign

1
2
3
4
5
6
@FeignClient(value ="nacos-provider",fallback = ProviderFeignImpl.class)
public interface ProviderFeign {

@GetMapping("/paymentSQL/{id}")
CommonResult<User> paymentSQL(@PathVariable("id") Integer id);
}

ProviderFeignImpl

1
2
3
4
5
6
7
@Component
public class ProviderFeignImpl implements ProviderFeign {
@Override
public CommonResult<User> paymentSQL(Integer id) {
return new CommonResult<>(444444,"报错了.....",new User(null,null));
}
}

application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
server:
port: 84

spring:
application:
name: nacos-consumer
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 #nacos服务注册中心地址
sentinel:
transport:
dashboard: 127.0.0.1:8080 #sentinel地址
port: 8719 #默认8719端口,假如被占用会自动从8719开始依次加1扫描,直到找到没有被占用的端口



feign:
sentinel:
enabled: true #激活Sentinel对feign的支持

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<dependencies>

<dependency>
<groupId>com.wang</groupId>
<artifactId>nacos-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- 配置中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

<!--服务注册与发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>

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

<!-- https://mvnrepository.com/artifact/com.alibaba.csp/sentinel-datasource-nacos -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<version>1.5.2</version>
</dependency>


</dependencies>

Seata:分布式事务解决方案

Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。

img

全局唯一的事务ID

TC (Transaction Coordinator) - 事务协调者

​ 维护全局和分支事务的状态,驱动全局事务提交或回滚。

TM (Transaction Manager) - 事务管理器

​ 定义全局事务的范围:开始全局事务、提交或回滚全局事务。

RM (Resource Manager) - 资源管理器

​ 管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

![FEZ4$GV[OG]WWU2QE{FUX

img

QQ图片20210827192552

VT21LP}207$AM{QKV%NL{AF

QQ图片20210827192812

测试

下单–>减库存–>减账户–>下单成功

订单模块 seata-order-service2001

Controller

1
2
3
4
5
6
7
8
9
10
11
12
@RestController
public class T_orderController {

@Resource
T_orderService t_orderService;

@GetMapping("/order/create")
public CommonResult create(T_order t_order){
t_orderService.create(t_order);
return new CommonResult(200,"下单成功");
}
}

实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Data
@AllArgsConstructor
@NoArgsConstructor
public class T_order {
private Long id;
private Long user_id;
private Long product_id;
private Integer count;
private BigDecimal money;
/**
* 订单状态 0为下单 1下单成功
*/
private Integer status;

}

Mapper

1
2
3
4
5
6
7
8
9
@Mapper
public interface T_orderMapper {
//创建订单
void create(T_order tOrder);

//修改订单状态
void update(@Param("user_id") Long user_id ,@Param("status") Integer status);

}

service

1
2
3
4
5
public interface T_orderService {
//创建订单
void create(T_order tOrder);

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Service
public class T_orderServiceImpl implements T_orderService {

@Resource
T_orderMapper t_orderMapper;

@Resource
T_accountFeign t_accountService;

@Resource
T_storageFeign t_storageService;

@Override
@GlobalTransactional(name = "my_test_tx_group",rollbackFor = Exception.class) //name:唯一名 rollbackFor:发生异常就回滚
public void create(T_order tOrder) {
//创建新订单
t_orderMapper.create(tOrder);
//修改库存
t_storageService.decrease(tOrder.getProduct_id(),tOrder.getCount());
//修改余额
t_accountService.decrease(tOrder.getUser_id(),tOrder.getMoney());
//修改订单状态,下单成功
t_orderMapper.update(tOrder.getUser_id(),0);
}

}

OpenFeign

1
2
3
4
5
6
7
@Component
@FeignClient("seata-account-service2002")
public interface T_accountFeign {

@PostMapping(value = "/account/decrease")
public CommonResult decrease(@RequestParam("user_id") Long user_id, @RequestParam("money") BigDecimal money);
}
1
2
3
4
5
6
7
@Component
@FeignClient("seata-storage-service2003")
public interface T_storageFeign {

@PostMapping(value = "/storage/decrease")
public CommonResult decrease(@RequestParam("product_id") Long product_id, @RequestParam("count") Integer count);
}

启动类

1
2
3
4
5
6
7
8
@SpringBootApplication
@EnableFeignClients(basePackages = "com.wang.service.openFeign")
@EnableDiscoveryClient
public class Order2001Application {
public static void main(String[] args) {
SpringApplication.run(Order2001Application.class,args);
}
}

xml

1
2
3
4
5
6
7
8
9
10
11
<mapper namespace="com.wang.mapper.T_orderMapper">
<insert id="create" parameterType="t_order">
insert into t_order(user_id,product_id,count,money,status)
values (#{user_id},#{product_id},#{count},#{money},0)
</insert>

<update id="update">
update t_order set status=1
where user_id=#{user_id} and status=#{status}
</update>
</mapper>

application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
server:
port: 2001

spring:
application:
name: seata-order-service2001
cloud:
alibaba:
seata:
tx-service-group: my_test_tx_group #配置事务分组名称
nacos:
discovery:
server-addr: localhost:8848
datasource: ##数据源配置
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3306/seata_order?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
username: root
password: 123456


mybatis:
type-aliases-package: com.wang.entity #包取别名

# classpath就代表目录了
mapper-locations: classpath:mybatis/mapper/*.xml #配置xml文件路径

file.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
service {
#transaction service group mapping
vgroup_mapping.seata-server = "default"
#only support when registry.type=file, please don't set multiple addresses
default.grouplist = "127.0.0.1:8091"
#disable seata
disableGlobalTransaction = false
}

## transaction log store, only used in seata-server
store {
## store mode: file、db
mode = "db"

## file store property
file {
## store location dir
dir = "sessionStore"
}

## database store property
db {
## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.
datasource = "dbcp"
## mysql/oracle/h2/oceanbase etc.
db-type = "mysql"
driver-class-name = "com.mysql.jdbc.Driver"
url = "jdbc:mysql://127.0.0.1:3306/seata"
user = "root"
password = "123456"
}
}

registry.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "nacos"

nacos {
serverAddr = "localhost:8848"
namespace = ""
cluster = "default"
}
eureka {
serviceUrl = "http://localhost:8761/eureka"
application = "default"
weight = "1"
}
redis {
serverAddr = "localhost:6379"
db = "0"
}
zk {
cluster = "default"
serverAddr = "127.0.0.1:2181"
session.timeout = 6000
connect.timeout = 2000
}
consul {
cluster = "default"
serverAddr = "127.0.0.1:8500"
}
etcd3 {
cluster = "default"
serverAddr = "http://localhost:2379"
}
sofa {
serverAddr = "127.0.0.1:9603"
application = "default"
region = "DEFAULT_ZONE"
datacenter = "DefaultDataCenter"
cluster = "default"
group = "SEATA_GROUP"
addressWaitTime = "3000"
}
file {
name = "file.conf"
}
}

config {
# file、nacos 、apollo、zk、consul、etcd3
type = "file"

nacos {
serverAddr = "localhost"
namespace = ""
}
consul {
serverAddr = "127.0.0.1:8500"
}
apollo {
app.id = "seata-server"
apollo.meta = "http://192.168.1.204:8801"
}
zk {
serverAddr = "127.0.0.1:2181"
session.timeout = 6000
connect.timeout = 2000
}
etcd3 {
serverAddr = "http://localhost:2379"
}
file {
name = "file.conf"
}
}

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
<dependencies>

<dependency>
<groupId>com.wang</groupId>
<artifactId>nacos-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>

<!-- https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-seata -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<exclusions>
<exclusion>
<artifactId>seata-all</artifactId>
<groupId>io.seata</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
<version>1.2.0</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- 配置中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

<!--服务注册与发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>

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

<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>

<!-- 官方的-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>

</dependencies>

账户模块seata-account-service2002

Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@RestController
public class T_accountController {

T_accountService t_accountService;

public T_accountController(T_accountService t_accountService) {
this.t_accountService = t_accountService;
}

@PostMapping(value = "/account/decrease")
public CommonResult decrease(@RequestParam("user_id") Long user_id,
@RequestParam("money") BigDecimal money) {
t_accountService.decrease(user_id, money);
return new CommonResult(200,"账户余额扣减成功,哈哈哈");
}

}

实体类

1
2
3
4
5
6
7
8
9
10
@Data
@AllArgsConstructor
@NoArgsConstructor
public class T_account {
private Long id;
private Long user_id;
private BigDecimal total;
private BigDecimal used;
private BigDecimal residue;
}

Mapper

1
2
3
4
@Mapper
public interface T_accountMapper {
void decrease(@Param("user_id") Long user_id,@Param("money") BigDecimal money);
}

service

1
2
3
public interface T_accountService {
void decrease(@Param("user_id") Long user_id, @Param("money") BigDecimal money);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Service
public class T_accountServiceImpl implements T_accountService {

@Resource
T_accountMapper t_accountMapper;

@Override
public void decrease(Long user_id, BigDecimal money) {
// try {
// TimeUnit.SECONDS.sleep(20); //模拟请求超时
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
t_accountMapper.decrease(user_id,money);
}
}

启动类

1
2
3
4
5
6
7
@SpringBootApplication
@EnableDiscoveryClient
public class T_account2002Application {
public static void main(String[] args) {
SpringApplication.run(T_account2002Application.class,args);
}
}

xml

1
2
3
4
5
6
7
<mapper namespace="com.wang.mapper.T_accountMapper">
<update id="decrease">
update t_account
set used = used + #{money},residue = residue - #{money}
where user_id=#{user_id}
</update>
</mapper>

application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
server:
port: 2002

spring:
application:
name: seata-account-service2002
cloud:
alibaba:
seata:
tx-service-group: my_test_tx_group
nacos:
discovery:
server-addr: localhost:8848
datasource: ##数据源配置
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3306/seata_account?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
username: root
password: 123456


mybatis:
type-aliases-package: com.wang.entity #包取别名

# classpath就代表目录了
mapper-locations: classpath:mybatis/mapper/*.xml #配置xml文件路径

file.conf文件

registry.conf文件

依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
<dependencies>

<dependency>
<groupId>com.wang</groupId>
<artifactId>nacos-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>

<!-- https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-seata -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<exclusions>
<exclusion>
<artifactId>seata-all</artifactId>
<groupId>io.seata</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
<version>1.2.0</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- 配置中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

<!--服务注册与发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>

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

<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>

<!-- 官方的-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>


<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>

</dependencies>

库存模块seata-storage-service2003

Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@RestController
public class T_storageController {


T_storageService t_storageService;

public T_storageController(T_storageService t_storageService) {
this.t_storageService = t_storageService;
}

@PostMapping(value = "/storage/decrease")
public CommonResult decrease(@RequestParam("product_id") Long product_id,
@RequestParam("count") Integer count) {
t_storageService.decrease(product_id, count);
return new CommonResult(200,"库存扣减成功,哈哈哈哈");
}

}

实体类

1
2
3
4
5
6
7
8
9
10
@Data
@AllArgsConstructor
@NoArgsConstructor
public class T_storage {
private Long id;
private Long product_id;
private Integer total;
private Integer used;
private Integer residue;
}

Mapper

1
2
3
4
5
@Mapper
public interface T_storageMapper {

void decrease(@Param("product_id") Long product_id,@Param("count") Integer count);
}

Service

1
2
3
public interface T_storageService {
void decrease(@Param("product_id") Long product_id, @Param("count") Integer count);
}
1
2
3
4
5
6
7
8
9
10
11
@Service
public class T_storageServiceImpl implements T_storageService {

@Resource
T_storageMapper t_storageMapper;

@Override
public void decrease(Long product_id, Integer count) {
t_storageMapper.decrease(product_id,count);
}
}

启动类

1
2
3
4
5
6
7
@SpringBootApplication
@EnableDiscoveryClient
public class T_storage2003Application {
public static void main(String[] args) {
SpringApplication.run(T_storage2003Application.class,args);
}
}

xml

1
2
3
4
5
6
7
<mapper namespace="com.wang.mapper.T_storageMapper">
<update id="decrease">
update t_storage
set used=used+#{count},residue=residue-#{count}
where product_id=#{product_id}
</update>
</mapper>

application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
server:
port: 2003

spring:
application:
name: seata-storage-service2003
cloud:
alibaba:
seata:
tx-service-group: my_test_tx_group
nacos:
discovery:
server-addr: localhost:8848
datasource: ##数据源配置
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3306/seata_storage?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
username: root
password: 123456


mybatis:
type-aliases-package: com.wang.entity #包取别名

# classpath就代表目录了
mapper-locations: classpath:mybatis/mapper/*.xml #配置xml文件路径

file.conf文件

registry.conf文件

依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
<dependencies>

<dependency>
<groupId>com.wang</groupId>
<artifactId>nacos-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>

<!-- https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-seata -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<exclusions>
<exclusion>
<artifactId>seata-all</artifactId>
<groupId>io.seata</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
<version>1.2.0</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- 配置中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

<!--服务注册与发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>

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

<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>

<!-- 官方的-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>


<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>

</dependencies>