SpringCloudAlibaba-19-seata分布式事务解决方案AT

前言

参考:https://www.jianshu.com/p/940e2cfab67e
quickstart:https://seata.io/zh-cn/docs/user/quickstart.html

内容

1.seata简介

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

TM:transaction manager
RM:resource manager

1)TM:事务的发起者。用来告诉 TC,全局事务的开始,提交,回滚。
2)RM:具体的事务资源,每一个 RM 都会作为一个分支事务注册在 TC。
3)TC 事务的协调者。也可以看做是 Fescar-server,用于接收我们的事务的注册,提交和回滚。

2.实战

2.1 准备
  1. 1.我们从github上下载seata应用jar包
  1. 2.在本地数据源数据库中添加seata的undo_log日志(在alibaba-order,alibaba-pay库里面)
1
2
3
4
5
6
7
8
9
10
11
12
13
CREATE TABLE `undo_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(20) NOT NULL,
`xid` varchar(100) NOT NULL,
`context` varchar(128) NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(11) NOT NULL,
`log_created` datetime NOT NULL,
`log_modified` datetime NOT NULL,
`ext` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
  1. 3.需要使用tc,则需要启动,点击:seata-server.bat
2.2 工程集成
  1. 1.引入依赖(3个服务都添加)
<dependency>
       <groupId>com.alibaba.cloud</groupId>
       <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
       <version>2.1.1.RELEASE</version>
   </dependency>
  1. 2.所有的资源服务中加入register,conf,因为服务端,客户端都需要注册,以及客户端配置;存放到resources目录下

  2. 3.我们是在biz-service上开启一个全局的事物来管理:pay-service和order-service;所以需要在biz-service的对应业务层加一个全局的事务注解: @GlobalTransactional(在最早的服务上加这个注解就可以了)

  3. 4.pay/order服务里面的jdbc是spring自己的数据源,这个时候,其实还没有完,我们需要使用seata来代理各个服务的数据源

  4. 5.启动biz-service时候报错,

    我们需要在file里面配置group,规则:appliactionname-seata-service-group

  5. 6.测试访问biz-service
    我们重启:biz-service,然后访问:http://127.0.0.1:8010/biz
    此时pay/order服务已经没有插入了数据

SpringCloudAlibaba-18-seata分布式事务问题回显

前言

本节介绍SpringCloudAlibaba中分布式事务解决方案seta,首先我们不介绍seta,我们先模拟分布式事务这个问题。

内容

我们以案例出发,支付场景案例如下:

a.首先支付请求进来以后,经过我们的biz-service(前面的业务处理)对参数进行校验,校验完之后调用我们的订单服务:order-service统一下单,下单完之后,调用我们的支付服务pay-service去调用三方接口,在这个地方:order-service和pay-service都会操作自己的数据库,操作完之后通过biz-service给用户返回一个请求。那么这里有三个微服务,本节我们就实现这3个微服务。

1.创建3个服务

  1. 1.创建3个服务
  2. 2.修改对应端口为上图所示
  3. 3.3个服务创建完毕,我们需要让order(alibaba-order)和pay(alibaba-pay)服务能够操作单独的数据库。
  4. 4.数据库脚本为:
1
2
3
4
CREATE TABLE `order/pay` (
`id` int(11) DEFAULT NULL,
`username` varchar(20) DEFAULT NULL COMMENT '用户名'
) ENGINE=InnoDB DEFAULT CHARSET=utf8
  1. 5.操作数据库插入数据
1
2
3
4
5
6
7
8
9
10
@Service
public class OrderService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Transactional
public void save(){
jdbcTemplate.update("INSERT INTO `order`( `username`) VALUES ('123');");
}
}
```

@Service
public class PayService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Transactional
public void save(){
jdbcTemplate.update(“INSERT INTO pay( username) VALUES (‘123’);”);
}
}

1
2
6. 6.order/pay-service服务提供给biz-service访问的接口

@RestController
public class OrderController {
@Autowired
private OrderService orderService;
@GetMapping(“/order”)
public String order(){
orderService.save();
return “success”;
}
}

1
2

@RestController
public class PayController {
@Autowired
private PayService payService;
@GetMapping(“/pay”)
public String pay(){
payService.save();
return “success”;
}
}

1
2
3
4
5
6
#### 2.biz服务调用其他服务
我们使用spring自带的RestTemplate模板
1. 1.创建service

@Service
public class BizService {
@Autowired
private RestTemplate restTemplate;
@Transactional
public void biz(){
//order
restTemplate.getForObject(“http://127.0.0.1:8020/order",String.class);
//pay
restTemplate.getForObject(“http://127.0.0.1:8030/pay",String.class);
}
}

1
2
3
4
2. 2.创建controller

@RestController
public class BizController {
@Autowired
private BizService bizService;
@GetMapping(“/biz”)
public String biz(){
bizService.biz();
return “success”;
}
}
```

  1. 3.重启3个服务
    我们访问:http://127.0.0.1:8010/biz
    结果返回:success
    查看数据库表时候发现我们表中插入了数据。

3.分布式事务出现情况

我们分析一下分布式事务会出现在什么情况下?
我们在biz服务调用其他两个服务的时候添加以下语句: int i = 1 / 0; 让其报错,这个时候pay/order服务会回滚吗?不会回滚的,因为pay/order是不同的jvm。本地事务是管不到其他jvm里面的(进程之间事务不具有传递性)。

我们重启:biz-service,然后访问:http://127.0.0.1:8010/biz
此时pay/order服务已经插入了数据

SpringCloudAlibaba-17-sentinel整合gateway限流

前言

本节主要讲解sentinel整合gateway网关


sentinel支持以下两种模式的限流

内容

实践

1.创建网关
  1. 1.创建一个非web依赖的工程:sentinel-gateway
  2. 2.作为一个网关,需要使用sentinel限流,并且注册到nacos上,需要引入出基本的spring cloud组件后,需要再次引入:sentinel依赖,服务发现依赖,网关依赖,网关和sentinel结合依赖。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!--gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--服务发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--sentinel和gateway整合-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
  1. 3.配置文件定义
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: 18084
spring:
application:
name: sentinel-gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
sentinel:
transport:
dashboard: 127.0.0.1:8080
#配置降级策略
scg:
fallback:
mode: response
response-status: 426
response-body: error request
#gateway路由规则
gateway:
routes:
- id: test_route
uri: lb://sentinel-gateway-service
predicates:
- Path=/test/**
  1. 4.启动类添加注解:@SpringCloudApplication
2.创建网关下游的服务
  1. 1.创建一个非web依赖的工程:sentinel-gateway-service
  2. 2.作为一个网关下测试服务,需要注册到nacos上。
1
2
3
4
5
<!--服务发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
  1. 3.配置文件定义
1
2
3
4
5
6
7
8
9
spring:
application:
name: sentinel-gateway-service
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
server:
port: 18086
  1. 4.启动类添加注解:@EnableDiscoveryClient、@SpringBootApplication

  2. 5.创建测试类:

1
2
3
4
5
6
7
8
9
10
11
@RestController
public class DemoController {
/**
* 因为在gateway中路由规则,都是test的会被拦截
* @return
*/
@GetMapping("/test")
public String test(){
return "test";
}
}
3.访问网关路由到对应的下游服务

访问网关:http://localhost:18084/test
返回:test

4.结合sentinel进行降级
  1. 1.第一种限流规则:针对route

http://127.0.0.1:8080/进入登录sentinel
然后”流控规则”

再次访问:
http://localhost:18084/test
返回:
error request

  1. 2.第二种限流规则:API分组限流
    a.先创建api分组
    Sentinel 控制台–>API管理–>新建API分组

再次访问:
http://localhost:18084/test
返回:
error request

SpringCloudAlibaba-16-sentinel整合feign实现服务调用

前言

使用sentinel实现服务间的调用,sentinel在这里面作为了是流量防护组件。他结合openfeign实现了服务降级等作用。 以下我们创建结合sentinel和feign的创建2个应用,服务消费方,服务提供方。

内容

服务提供方:provider创建

  1. 1.创建spring项目
  2. 2.引入cloud依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!--依赖版本定义-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
```
4. 4.使用sentinel、openfeign、discovery


com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery


com.alibaba.cloud
spring-cloud-starter-alibaba-sentinel


org.springframework.cloud
spring-cloud-starter-openfeign

1
2
3
5. 5.我们是服务提供方,我们书写一个接口

@RestController
public class DemoController {
@GetMapping(“/feign”)
public String feign(){
return “hello sentinel feign”;
}
}

1
2
6. 6.启动类添加注解

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

1
2
7. 7.添加配置文件application.yml

server:
port: 18082
spring:
application:
name: sentinel-feign-provider
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848

1
2
3
4
5
6
7
#### 服务消费方:consumer创建
我们复制sentinel-feign-provider修改成 sentinel-feign-consumer
启动类上添加注解:@EnableFeignClients
1. 1.创建feign客户端

@FeignClient(value = “sentinel-feign-provider”,fallback =DemoFeignClientFallback.class)
public interface DemoFeginClient {
@GetMapping(“/feign”)
String feign();
}

1
2
3
2. 2.创建服务降级实现类

@Component
public class DemoFeignClientFallback implements DemoFeginClient {
@Override
public String feign() {
return “error”;
}
}

1
2
3. 3.创建consumer的测试请求类:

@RestController
public class DemoController {
@Autowired
private DemoFeginClient demoFeginClient;
@GetMapping(“/hello”)
public String feign(){
return demoFeginClient.feign();
}
}
```

测试:
http://localhost:18083/hello
结果:
hello sentinel feign

SpringCloudAlibaba-15-sentinel规则持久化-nacos

1.引入依赖

1
2
3
4
5
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<version>1.7.0</version>
</dependency>

2.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
26
27
28
29
30
server:
port: 18080
spring:
application:
name: sentinel-core-example
cloud:
sentinel:
transport:
dashboard: 127.0.0.1:8080
datasource:
ds1:
file:
file: classpath:flowrule.json
data-type: json
rule-type: flow
ds2:
nacos:
server-addr: 127.0.0.0.1:8848
dataId: sentinel-core-example
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
```
#### 3.启动nacos
重启nacos,然后创建sentinel-core-example配置文件
![](https://raw.githubusercontent.com/startshineye/img/master/2020/02/33.png)
HellController添加:

@GetMapping(“/resource1”)
@SentinelResource(value = “hello2”, blockHandler = “exHandler”, blockHandlerClass = {ExceptionUtil.class})
public String resource1() {
return “hello resource1”;
}
```


title: SpringCloudAlibaba-14-sentinel规则持久化-file
date: 2020-02-02 10:07:44
tags:

SpringCloudAlibaba

前言

之前的限流及其其他操作,当我们重启应用后,对应的规则在sentinel里面已经丢失不复存在了。现在我们主要说明sentinel参数如何持久化?

内容

1.规则持久化

目前支持file,nacos,zk,apollo,redis这5种类型
如果你想扩展其他的也提供了对应的接口。

2.规则持久化-file

我们先讲解规则存放在file里面。

  1. 1.在应用程序的resources文件下创建file:flowrule.json
  2. 2.填写限流属性规则:

  3. 3.我们将上面配置添加到配置中去:application.yml

server:
  port: 18080
spring:
  application:
    name: sentinel-core-example
  cloud:
    sentinel:
      transport:
        dashboard: 127.0.0.1:8080
      datasource:
        ds1:
          file:
            file: classpath:flowrule.json
            data-type: json
            rule-type: flow


title: SpringCloudAlibaba-13-sentinel自适应限流
date: 2020-02-02 09:18:47
tags:

SpringCloudAlibaba

参考:https://www.jianshu.com/p/f1f052413d1c

1.什么是自适应限流?

比如说我们设置一个参数的阈值,当超过这个阈值时候,都把所有请求block掉,当低于这个阈值时候,正常处理,比如说:cpu设置70%负载,当超过这个阈值时候,我们系统不处理新的请求,保证业务稳定正常运行。

2.sentinel提供的限流类别

我们在sentinel控制台上有一个”系统规则”;点击新增时候里面有5个类别设置。

  1. 1.LOAD:linux/unix里面瞬间处理的进程数,比如cpu是8核,那么同时只能处理8个进程数。如果超过8会去等待。一般这个参数是:cpu个数x核数x0.7(保证系统70%临界)
  2. 2.RT:平均响应时间,其中RT单位为毫秒,比如我们设置1000ms都挂起,block掉。
  3. 3.线程数
  4. 4.入口QPS:全局的qps
  5. 5.CPU使用率:一般设置到0.7

3.实践

我们设置cpu使用率是0.1,测试下效果,因为我们本地的cpu使用率肯定超过了10%

请求后:

SpringCloudAlibaba-12-sentinel热点限流

前言

我们已经对单机限流和集群限流有过一定了解了,但是他们都是针对一些固定的资源进行流控的,在实际的应用场景中我们可能会遇到各种复杂的情况,不可能通过固定的资源来进行限流。

比如我们想要对一段时间内频繁访问的用户 ID 进行限制,又或者我们想统计一段时间内最常购买的商品 ID 并针对商品 ID 进行限制。那这里的用户 ID 和商品 ID 都是可变的资源,通过原先的固定资源已经无法满足我们的需求了,这时我们就可以通过 Sentinel 为我们提供的 热点参数限流 来达到这样的效果

内容

参考:https://www.jianshu.com/p/2efe5058fcf4

1.什么是热点(参数)?

  1. 首先我们需要知道什么是热点,热点就是访问非常频繁的参数
  2. 例如我们大家都知道的爬虫,就是通过脚本去爬取其他网站的数据,一般防爬虫的常用方法之一就是限制爬虫的 IP,那对应到这里 IP 就是一种热点的参数。
  3. 那么 Sentinel 是怎么知道哪些参数是热点,哪些参数不是热点的呢?Sentinel 利用 LRU 策略,结合底层的滑动窗口机制来实现热点参数统计。LRU 策略可以统计单位时间内,最近最常访问的热点参数,而滑动窗口机制可以帮助统计每个参数的 qps。

  4. 说简单点就是,Sentinel 会先检查出提交过来的参数,哪些是热点的参数,然后在应用热点参数的限流规则,将qps 超过设定阈值的请求给 block 掉,整个过程如下图所示:

如果参数携带:axb;他的qps大于5,会被阻塞掉

2.实战

  1. 1.我们创建一个ParamController
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@RestController
public class ParamController {
@GetMapping("/param")
@SentinelResource(value = "param",blockHandler = "exHandler")
public String param(String type){
return "success";
}
public String exHandler(String type){
/**
* 热点限流:参数限流
* 所以我们需要接受一个参数
* 如果不指定blockHandlerClass默认会从本类去寻找处理类:exHandler
*/
return "param request error";
}
}
  1. 2.访问
    curl http://127.0.0.1:18080/param
    然后打开sentinel,我们发现在param下面挂载了一个param资源,此时我们点进去:

    设置热线限流(全部限制):
  1. 3.测试访问

注意热线限流不支持接口资源,一定是带有@SentinelResource注解的资源


title: SpringCloudAlibaba-11-sentinel黑白名单控制
date: 2020-02-02 00:02:07
tags:

SpringCloudAlibaba

前言

上一节我们讲了sentinel的基于url和基于资源流量控制。
这一章节我们说一下黑白名单

内容

  1. 1.我们这一节做一个客户端黑白名单访问限制,通过使用ip规则
    自定义:IpRequestOriginParser实现RequestOriginParser接口:
1
2
3
4
5
6
public class IpRequestOriginParser implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest request) {
return request.getRemoteAddr();
}
}
  1. 2.将此配置绑定到Sentinel上去
1
2
3
4
5
6
7
8
9
10
11
@Configuration
public class SentinelConfig {
@PostConstruct
public void init(){
WebCallbackManager.setUrlBlockHandler(new DemoUrlBlockHandler());
WebCallbackManager.setRequestOriginParser(new IpRequestOriginParser());
}
}
```
3. 3.定义一个SecurityController用作测试

@RestController
public class SecurityController {
@GetMapping(“/white”)
public String white() {
return “hello white”;
}
@GetMapping(“/black”)
public String black() {
return “hello black”;
}
}
```

SpringCloudAlibaba-10-sentinel限流功能使用

1.Sentinel简介

Sentinel 是什么?
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
Sentinel 具有以下特征:

  1. 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
  2. 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
  3. 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
  4. 完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。

2.Sentinel控制台

我们也可以从官网下载源代码,然后mvn编译打包
https://github.com/alibaba/Sentinel

初始账号/密码:sentinel/sentinel

登录进去是空的,因为我们没有接入任何应用。

3.Sentinel接入应用

  1. 1.创建Spring Initializr子模块
  2. 2.引入sentinel依赖
1
2
3
4
5
6
7
8
9
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
```
3. 3.配置文件

server:
port: 18080
spring:
application:
name: sentinel-core-example
cloud:
sentinel:
transport:
dashboard: 127.0.0.1:8080

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
4. 4.启动
启动服务时候,发现sentinel的控制端没有新的消息出现。原因是因为sentinel控制台是一个懒加载,只有当有流量的时候才会显示具有流量的应用。我们创建controller
![](https://raw.githubusercontent.com/startshineye/img/master/2020/02/15.png)
默认情况下是根据url进行限流的,我们可以设置下流控
![](https://raw.githubusercontent.com/startshineye/img/master/2020/02/17.png)
![](https://raw.githubusercontent.com/startshineye/img/master/2020/02/16.png)
设置为1,说明我们每秒只能访问一次
![](https://raw.githubusercontent.com/startshineye/img/master/2020/02/18.png)
5. 5.基于资源限流的我们使用注解:

@GetMapping(“/resource”)
@SentinelResource(“/resource”)
public String resource(){
return “Hello resource”;
}

1
2
3
4
### 4.重写基于URL的限流自定义输出
1. 1.自定义url限流处理器

public class DemoUrlBlockHandler implements UrlBlockHandler {
@Override
public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws IOException {
httpServletResponse.getWriter().println(“error url request”);
}
}

1
2
2. 2.我们需要把自定义的DemoUrlBlockHandler添加到Sentinel限流配置中去

@Configuration
public class SentinelConfig {
@PostConstruct
public void innit(){
WebCallbackManager.setUrlBlockHandler(new DemoUrlBlockHandler());
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
然后我们重启服务,重启服务时候,之前的监控信息会丢失,重新设置流控为0,然后请求,返回如下结果:
![](https://raw.githubusercontent.com/startshineye/img/master/2020/02/19.png)
### 4.重写基于资源名称的限流自定义输出
通过blockHandler方法名,默认情况下,会找本类下的上面方法,返回值和目标方法一致,也是String,入参和请求的接口入参一致,但是会返回一个BlockException
![](https://raw.githubusercontent.com/startshineye/img/master/2020/02/20.png)
对于每一个资源resource,我们不可能都单独写一个限流的方法,能不能统一写一个统一的类作为限流。blockHandlerClass ={ExceptionUtil.class} 使用上面 blockHandlerClass属性自定义一个ExceptionUtil

public class ExceptionUtil {
public static String exHandler(BlockException ex) {
return “exHandler error”;
}
}
```

毕业于<br>相信技术可以改变人与人之间的生活<br>码农一枚