美文网首页轻巧springcloudalready
SpringCloud--Alibaba入门(OpenFeign

SpringCloud--Alibaba入门(OpenFeign

作者: aruba | 来源:发表于2022-08-22 21:33 被阅读0次

    接着SpringCloud--Alibaba入门(Nacos+Sentinel),上次使用了SpringCloudAlibaba中的Nacos和Sentinel,其中Nacos作为微服务的核心,不仅仅拥有服务注册中心、服务发现,还有配置中心的功能,并且自带Ribbon;Sentinel为保障整体微服务架构的高可用,拥有流控、熔断等功能,剩下还有OpenFeign、GateWay、Seata。

    一、OpenFeign

    Netflix也是使用的OpenFeign,所以用法是一致的

    创建项目,作为服务调用者:

    1.基本使用

    1.1 依赖

    除了nacos服务发现依赖外,还需要OpenFeign依赖,OpenFeign是SpringCloud中的组件,需要注意和SpringBoot的版本关系:

    Spring Cloud Alibaba Version Spring Cloud Version Spring Boot Version
    2021.0.1.0 Spring Cloud 2021.0.1 2.6.3
    2.2.7.RELEASE Spring Cloud Hoxton.SR12 2.3.12.RELEASE
    2021.1 Spring Cloud 2020.0.1 2.4.2
    2.2.6.RELEASE Spring Cloud Hoxton.SR9 2.3.2.RELEASE
    2.1.4.RELEASE Spring Cloud Greenwich.SR6 2.1.13.RELEASE
    2.2.1.RELEASE Spring Cloud Hoxton.SR3 2.2.5.RELEASE
    2.2.0.RELEASE Spring Cloud Hoxton.RELEASE 2.2.X.RELEASE
    2.1.2.RELEASE Spring Cloud Greenwich 2.1.X.RELEASE
    2.0.4.RELEASE(停止维护,建议升级) Spring Cloud Finchley 2.0.X.RELEASE
    1.5.1.RELEASE(停止维护,建议升级) Spring Cloud Edgware 1.5.X.RELEASE

    父项目SpringBoot版本为2.3.12.RELEASE,所以导入SpringCloud的Hoxton.SR12依赖:

        <parent>
            <artifactId>spring-boot-starter-parent</artifactId>
            <groupId>org.springframework.boot</groupId>
            <version>2.3.12.RELEASE</version>
            <relativePath/>
        </parent>
    
        <properties>
            ...
            <spring-cloud.version>Hoxton.SR12</spring-cloud.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>
            </dependencies>
        </dependencyManagement>
    

    子项目导入OpenFeign依赖:

            <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-discovery</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-openfeign</artifactId>
            </dependency>
    
    1.2 yml配置

    yml配置还是和一般的消费者相同,需要指定nacos地址

    server:
      port: 9002
    
    spring:
      application:
        name: openfeign-consumer
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848
    
    management:
      endpoints:
        web:
          exposure:
            include: '*'
    
    
    1.3 远程调用接口

    之前使用过openFeign了,所以这边也不多做介绍,openFeign需要定义一个接口来调用提供者的http接口,定义方式和调用的接口方法相同

    这是想要调用的远程服务接口:

    使用@FeignClient注解指定服务名,按照远程服务的http接口编写openFeign接口:

    @Component
    @FeignClient("provider")
    public interface DemoFeignClient {
    
        @RequestMapping("/demo")
        public String demo();
    
    }
    
    1.4 编写controller

    注入DemoFeignClient ,并调用远程服务:

    @RestController
    public class FeignDemoController {
        @Autowired
        private DemoFeignClient demoFeignClient;
    
        @RequestMapping("/feignDemo")
        public String feignDemo() {
            return demoFeignClient.demo();
        }
    }
    
    1.5 启动服务

    定义启动类,并添加@EnableFeignClients注解:

    @SpringBootApplication
    @EnableDiscoveryClient
    @EnableFeignClients
    public class OpenFeignConsumer9002Application {
    
        public static void main(String[] args) {
            SpringApplication.run(OpenFeignConsumer9002Application.class, args);
        }
    
    }
    

    启动消费者和提供者服务:

    由于OpenFeign也集成了Ribbon,自带负载均衡效果:

    2.服务降级

    Ribbon本身就支持服务降级,当调用达到超时时间时,会自动失败,默认超时时间为1s。Sentinel则是支持更细粒度的资源控制,至于项目中使用Sentinel还是OpenFeign自带的Ribbon,看具体的需求

    2.1 定义一个远程接口

    远程接口中模拟超时,睡眠3s后再返回:

        @RequestMapping("/timeout")
        public String timeout() throws InterruptedException {
            Thread.sleep(3000);
            return "timeout";
        }
    
    2.2 定义openFeign接口方法

    远程调用timeout接口:

    @Component
    @FeignClient(value = "provider")
    public interface DemoFeignClient {
    
        @RequestMapping("/demo")
        public String demo();
    
    
        @RequestMapping("/timeout")
        public String timeout();
    }
    
    2.3 配置超时时间

    超时默认1s,我们也可以通过yml配置自定义超时时间:

    #设置feign客户端超时时间(OpenFeign默认支持ribbon)
    ribbon:
      #指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
      ReadTimeout: 2000
      #指的是建立连接后从服务器读取到可用资源所用的时间
      ConnectTimeout: 2000
    
    2.4 controller层中调用
        @RequestMapping("/feignTimeout")
        public String feignTimeout() {
            return demoFeignClient.timeout();
        }
    

    访问效果,出现500错误:

    后台抛出的异常为SocketTimeoutException,如果项目中使用,需要使用SpringMVC的异常处理方法来手动处理该异常:

    3.整合Sentinel

    之前Netflix中使用Hystrix,当服务降级发生,openFeign可以使用@FeignClient注解的fallback属性指定服务降级的异常处理,让请求返回的更友好,原因是Hystrix和openFeign已经整合了。同样的Sentinel与openFeign配合使用,也进行了整合

    3.1 依赖

    加入Sentinel的依赖:

            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
            </dependency>
    
    3.2 yml配置

    告知openFeign支持Sentinel:

    # 开启sentinel
    feign:
      sentinel:
        enabled: true
    
    3.3 降级处理类

    使用fallback属性指定自定义的服务降级处理类

    @Component
    @FeignClient(value = "provider",fallback = DemoFeignClientFallback.class)
    public interface DemoFeignClient {
    
        @RequestMapping("/demo")
        public String demo();
    
    
        @RequestMapping("/timeout")
        public String timeout();
    }
    

    服务降级处理类实现openFeign接口:

    @Component
    public class DemoFeignClientFallback implements DemoFeignClient {
        @Override
        public String demo() {
            return "发生服务降级!";
        }
    
        @Override
        public String timeout() {
            return "发生服务降级!";
        }
    }
    

    此时再对接口进行访问:


    注:@FeignClient的fallback和@SentinelResource的fallback效果相同,只能处理其他异常,不能处理限流异常(BlockException)

    二、GateWay

    GateWay就是网关,用于提供对外的统一接口,以防止暴露内部服务地址,相比于Zuul,GateWay拥有更高的性能,底层由Netty框架实现
    GateWay的执行流程和过滤器Fliter相同,采用责任链模式,在请求前和请求后都可以对数据进行处理

    GateWay核心:

    • 路由:表示一条反向代理的规则,由ID、目标uri、断言、过滤器等组成
    • 断言:http请求中资源的匹配规则,如:请求路径、请求头、请求参数等是否匹配
    • Filter:请求前和请求后的特殊处理,如:对请求头修改、返回结果修改

    1.使用GateWay

    创建项目:

    1.1 依赖

    导入nacos和gateway依赖,注意不需要web依赖:

            <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-gateway</artifactId>
            </dependency>
    
    1.2 yml配置

    除了服务发现外,还需要配置gateway相关,主要是针对路由规则进行配置:

    server:
      port: 8050
    spring:
      application:
        name: gateway
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848
        gateway:
          routes: # 路由规则,可配置多个
            - id: provider # 定义一个id,一般与服务提供者的服务名相同
              uri: http://localhost:8001/provider
              predicates: # 断言,匹配路径用
                - Path=/** # ant匹配符,表示所有目录下的所有路径
    
    
    1.3 启动服务
    @SpringBootApplication
    @EnableDiscoveryClient
    public class GateWay8050Application {
    
        public static void main(String[] args) {
            SpringApplication.run(GateWay8050Application.class, args);
        }
    
    }
    

    访问GateWay反向代理的接口:

    2.路由规则:配置类方式

    除了在yml中配置,配置类也可以进行路由规则的配置,注入Spring容器,返回一个RouteLocator对象,方法入参为 RouteLocatorBuilder

    @Configuration
    public class GateWayConfig {
    
        @Bean
        public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {
            // 构建多个路由routes
            RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
            // 具体路由地址
            routes.route("consumer", r -> r.path("/consumer/**").uri("http://localhost:9001/consumer")).build();
            // 返回所有路由规则
            return routes.build();
        }
    
    }
    

    由于使用/**,会优先从yml配置的路由规则的服务进行调用,会出现404找不到资源,所以加上前缀consumer

    并为该服务配置context-path

    server:
      port: 9001
      servlet:
        context-path: /consumer
    

    启动后调用:

    注:该负载均衡效果是消费者调用时Ribbon实现的

    3.自动路由

    除了手动配置路由规则,GateWay还支持自动路由,由于GateWay本身就会注册到Nacos中,它会根据Nacos中注册的服务名进行匹配,并自带负载均衡,自动路由默认是关闭的,需要yml中配置:

    spring:
      application:
        name: gateway
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848
        gateway:
          discovery:
            locator:
              enabled: true #标识作为服务提供者注册到nacos中
    

    此时路由规则就不需要写了,启动后,通过服务名进行访问:

    4.手动负载均衡

    自动路由是根据服务名进行访问的,并自带负载均衡,但此时对外暴露服务名并不合适,想要在调用接口时去除服务名,并且带负载均衡效果,就需要进行手动配置路由规则,路由的uri使用lb协议:

    spring:
      application:
        name: gateway
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848
        gateway:
          discovery:
            locator:
              enabled: true #标识作为服务提供者注册到nacos中
          routes: # 路由规则,可配置多个
            - id: provider # 定义一个id,一般与服务提供者的服务名相同
              uri: lb://provider # 表示使用负载均衡协议,后面跟上服务名
              predicates: # 断言,匹配路径用
                - Path=/** # ant匹配符,表示所有目录下的所有路径
    

    此时访问就不需要带上服务名了,并且也是有负载均衡的效果:

    5.断言

    断言的种类有:

    种类 描述
    After 匹配在指定日期时间之后发生的请求。
    Before 匹配在指定日期之前发生的请求。
    Between 需要指定两个日期参数,设定一个时间区间,匹配此时间区间内的请求。
    Cookie 需要指定两个参数,分别为name和regexp(正则表达式),也可以理解Key和Value,匹配具有给定名称且其值与正则表达式匹配的Cookie。
    Header 需要两个参数header和regexp(正则表达式),也可以理解为Key和Value,匹配请求携带信息。
    Host 匹配当前请求是否来自于设置的主机。
    Method 可以设置一个或多个参数,匹配HTTP请求,比如GET、POST
    Path 匹配指定路径下的请求,可以是多个用逗号分隔
    Query 需要指定一个或者多个参数,一个必须参数和一个可选的正则表达式,匹配请求中是否包含第一个参数,如果有两个参数,则匹配请求中第一个参数的值是否符合正则表达式。
    RemoteAddr 匹配指定IP或IP段,符合条件转发。
    Weight 需要两个参数group和weight(int),实现了路由权重功能,按照路由权重选择同一个分组中的路由

    断言的使用主要看官网文档即可:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-request-predicates-factories

    6.过滤器

    GateWay的过滤器和Servlet的过滤器与SpringBoot的拦截器差不多,都能够针对请求前和请求后进行处理。
    官方提供的GateWay很多,使用yml配置即可,针对一些简单业务场景使用
    官方文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories

    自定义过滤器,需要实现GlobalFilterOrdered接口,Ordered接口用于设置过滤器的优先级:

    public class CustomGateWayFilter implements GlobalFilter, Ordered {
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            // 请求前
            System.out.println("请求前...");
            MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();
            System.out.println("请求参数:" + queryParams);
            Mono<Void> filter = chain.filter(exchange);
            // 其他过滤器执行后的请求结果
            System.out.println("请求后...");
            ServerHttpResponse response = exchange.getResponse();
            if (!queryParams.containsKey("token")) {
                response.setStatusCode(HttpStatus.NOT_ACCEPTABLE);
                return response.setComplete();
            }
    
            System.out.println(response.getStatusCode());
            return filter;
        }
    
        @Override
        public int getOrder() {
            return 0;
        }
    }
    

    注入到Spring容器中:

    @Configuration
    public class FilterConfig {
    
        @Bean
        public CustomGateWayFilter providerCustomGateWayFilter() {
            return new CustomGateWayFilter();
        }
    
    }
    

    日志打印:

    三、分布式事务

    分布式事务理论分别为CAP定律BASE理论

    1.CAP

    CAP由三个英文单词首字母组成,分别为:Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性)

    • C(一致性):在分布式系统中,所有数据备份是否在同一时刻都是相同的
    • A(可用性):在集群某个节点故障后,整体能否响应客户端的读写请求
    • P(分区容错性):由通信带来的时限要求。在时限达到后,还未达成数据一致性,那么必须在C和A之间做出选择,要么快速响应客户端,要么保证达成数据一致后再响应客户端,后者可能永远无法响应

    CAP定律证实:分布式事务由于网络等原因出现P后,只能够做到CP或AP,无法做到三者同时满足

    2.BASE

    BASE理论是基于CAP定律发展而来的,由于CAP的强一致性太苛刻了,BASE理论核心思想是:即使无法做到强一致性,但每个应用都可以根据自身业务特点,采用适当的方式来使系统达到最终一致性。

    BASE理论由三个英文短语组成:

    • Basically Available(基本可用)
      1.响应时间上,允许有一定时间的延迟
      2.系统功能上,允许服务降级的发生
    • Soft state(软状态)
      允许系统中的数据存在中间状态,并这种状态不影响整体可用性。即允许数据副本同步过程有一定的延迟
    • Eventually consistent(最终一致性)
      所有的数据副本,在经过一段时间同步后达到相同

    四、Seata

    Seata是提供处理分布式事务解决方案的一个组件,官网:https://seata.io/zh-cn/docs/overview/what-is-seata.html,首先来了解下什么是2PC

    1.2PC

    2PC就是两阶段提交协议,将整个分布式事务分为两个阶段:

    • P(准备阶段):由事务管理器通知每个事务成员执行本地事务,并形成Undo/Redo日志,Undo日志是记录修改前的日志,用于回滚操作,Redo日志是记录修改后的日志,用于提交操作
    • C(提交阶段):如果某个事务成员响应超时或失败,那么事务管理器通知每个成员回滚,如果全员通过,那么通知提交;由每个事务成员根据事务管理器的具体通知执行提交还是回滚,并释放资源

    2.Seata三大角色

    Seata也是基于2PC协议,并在2PC基础上进一步抽象,将分布式事务种的成员分为三个角色:

    • TC(Transaction Coordinator)- 事务协调者
      维护全局和分支事务状态,通知各成员全局事务的提交或回滚
    • TM(Transaction Manager)- 事务管理器
      发起全局准备事务、全局提交或回滚事务,交由TC处理
    • RM(Resource Manager)- 资源管理器
      分支事务具体参与者,注册和通知TC分支事务状态,TM也是RM

    3.Seata支持的模式

    模式 描述 侵入性
    AT 使用最多的模式,基于2PC协议,准备阶段生成undo日志,根据提交阶段提交/回滚,删除undo或使用undo回滚
    XA 基于数据库的XA协议实现2PC,只有全局事务完成,数据才会真正更新到数据库表中,强调事务一致性时使用
    TCC 自定义准备阶段,以及提交/回滚阶段的行为,可以针对不仅仅是数据库资源的分布式事务
    Saga 单个事务失败后,可以补偿前面的成功者,具体补偿行为由自定义业务处理,针对长事务时使用

    4.Seata安装

    下载最新版本1.5.2:https://github.com/seata/seata/releases

    4.1 修改配置

    来到conf目录下:

    1.5.2版本所有的配置都整合进了application.yml,1.4.2版本对registry.conf和file.conf的配置方式就不适用了,配置的例子可以看application.example.yml,我这边就是将application.example.yml内容复制进application.yml,并做了一些删减,主要是针对配置中心、服务注册中心、存储方式进行配置:

    server:
      port: 7091
      
    console: 
      user: 
        username: aruba
        password: aruba
      
    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
    
    seata:
      # 配置中心配置
      config:
        # support: nacos 、 consul 、 apollo 、 zk  、 etcd3
        type: nacos
        nacos:
          server-addr: 127.0.0.1:8848
          namespace:
          group: SEATA_GROUP
          username: nacos
          password: nacos
          ##if use MSE Nacos with auth, mutex with username/password attribute
          #access-key: ""
          #secret-key: ""
          # 配置中心的对应配置的DataID
          data-id: seataServer.properties
      # 服务注册中心配置
      registry:
        # support: nacos 、 eureka 、 redis 、 zk  、 consul 、 etcd3 、 sofa
        type: nacos
        preferred-networks: 30.240.*
        nacos:
          application: seata-server
          server-addr: 127.0.0.1:8848
          group: SEATA_GROUP
          namespace:
          cluster: default
          username: nacos
          password: nacos
          ##if use MSE Nacos with auth, mutex with username/password attribute
          #access-key: ""
          #secret-key: ""
      # 对外服务配置
      server:
        service-port: 8091 #If not configured, the default is '${server.port} + 1000'
        max-commit-retry-timeout: -1
        max-rollback-retry-timeout: -1
        rollback-retry-timeout-unlock-enable: false
        enable-check-auth: true
        enable-parallel-request-handle: true
        retry-dead-threshold: 130000
        xaer-nota-retry-timeout: 60000
        recovery:
          handle-all-session-period: 1000
        undo:
          log-save-days: 7
          log-delete-period: 86400000
        session:
          branch-async-queue-size: 5000 #branch async remove queue size
          enable-branch-async-remove: false #enable to asynchronous remove branchSession
      # 存储模式配置
      store:
        # support: file 、 db 、 redis
        mode: db
        session:
          mode: db
        lock:
          mode: db
        db:
          datasource: druid
          db-type: mysql
          driver-class-name: com.mysql.jdbc.Driver
          url: jdbc:mysql://127.0.0.1:3306/seata?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
          user: root
          password: root
          min-conn: 5
          max-conn: 100
          global-table: global_table
          branch-table: branch_table
          lock-table: lock_table
          distributed-lock-table: distributed_lock
          query-limit: 100
          max-wait: 5000
      metrics:
        enabled: false
        registry-type: compact
        exporter-list: prometheus
        exporter-prometheus-port: 9898
      transport:
        rpc-tc-request-timeout: 30000
        enable-tc-server-batch-send-response: false
        shutdown:
          wait: 3
        thread-factory:
          boss-thread-prefix: NettyBoss
          worker-thread-prefix: NettyServerNIOWorker
          boss-thread-size: 1
      security:
        secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
        tokenValidityInMilliseconds: 1800000
        ignore:
          urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/api/v1/auth/login
    

    配置完后,来到bin目录下,执行seata-server.bat批处理文件,此时可能会输出错误日志,原因是连接不到对应的数据库,后面我们会进行配置:

    当Nacos种出现seata-server的服务,说明seata启动成功了:

    4.2 存储方式

    上面我们针对seata的存储方式进行了配置,使用的是db,数据库为mysql,现在需要创建相应的数据库和表

    官方提供了建表语句:https://github.com/seata/seata/tree/develop/script/server/db

    执行完后:

    Seata默认使用的数据库驱动为mysql5版本,想要使用其他的数据库版本,需要自己手动导入jdbc的jar包,官方提供了mysql5和mysql8,我这边使用的是mysql8,所以只要删除mysql5的jdbc即可,来到lib/jdbc目录下:

    此时启动seata后,不会报数据库连接异常了

    4.3 配置中心

    既然是SpringCloudAlibaba,推荐使用Nacos作为配置中心,此外Seata也支持其他的配置中心,下面是我们配置的Nacos作为配置中心的配置:

    根据该配置在Nacos配置中心中,新建seataServer.properties配置,可配置的内容官网也提供了,其实配置项和上面yml中的配置项差不多:https://github.com/seata/seata/blob/develop/script/config-center/config.txt

    根据提供内容进行简单的修改,主要针对jdbc配置:

    #For details about configuration items, see https://seata.io/zh-cn/docs/user/configurations.html
    #Transport configuration, for client and server
    transport.type=TCP
    transport.server=NIO
    transport.heartbeat=true
    transport.enableTmClientBatchSendRequest=false
    transport.enableRmClientBatchSendRequest=true
    transport.enableTcServerBatchSendResponse=false
    transport.rpcRmRequestTimeout=30000
    transport.rpcTmRequestTimeout=30000
    transport.rpcTcRequestTimeout=30000
    transport.threadFactory.bossThreadPrefix=NettyBoss
    transport.threadFactory.workerThreadPrefix=NettyServerNIOWorker
    transport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandler
    transport.threadFactory.shareBossWorker=false
    transport.threadFactory.clientSelectorThreadPrefix=NettyClientSelector
    transport.threadFactory.clientSelectorThreadSize=1
    transport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThread
    transport.threadFactory.bossThreadSize=1
    transport.threadFactory.workerThreadSize=default
    transport.shutdown.wait=3
    transport.serialization=seata
    transport.compressor=none
    
    #Transaction routing rules configuration, only for the client
    #配置事务组,可以标识为机房地址
    service.vgroupMapping.default_tx_group=default
    #If you use a registry, you can ignore it
    service.default.grouplist=127.0.0.1:8091
    service.enableDegrade=false
    service.disableGlobalTransaction=false
    
    #Transaction rule configuration, only for the client
    client.rm.asyncCommitBufferLimit=10000
    client.rm.lock.retryInterval=10
    client.rm.lock.retryTimes=30
    client.rm.lock.retryPolicyBranchRollbackOnConflict=true
    client.rm.reportRetryCount=5
    client.rm.tableMetaCheckEnable=true
    client.rm.tableMetaCheckerInterval=60000
    client.rm.sqlParserType=druid
    client.rm.reportSuccessEnable=false
    client.rm.sagaBranchRegisterEnable=false
    client.rm.sagaJsonParser=fastjson
    client.rm.tccActionInterceptorOrder=-2147482648
    client.tm.commitRetryCount=5
    client.tm.rollbackRetryCount=5
    client.tm.defaultGlobalTransactionTimeout=60000
    client.tm.degradeCheck=false
    client.tm.degradeCheckAllowTimes=10
    client.tm.degradeCheckPeriod=2000
    client.tm.interceptorOrder=-2147482648
    client.undo.dataValidation=true
    client.undo.logSerialization=jackson
    client.undo.onlyCareUpdateColumns=true
    server.undo.logSaveDays=7
    server.undo.logDeletePeriod=86400000
    client.undo.logTable=undo_log
    client.undo.compress.enable=true
    client.undo.compress.type=zip
    client.undo.compress.threshold=64k
    #For TCC transaction mode
    tcc.fence.logTableName=tcc_fence_log
    tcc.fence.cleanPeriod=1h
    
    #Log rule configuration, for client and server
    log.exceptionRate=100
    
    #Transaction storage configuration, only for the server. The file, DB, and redis configuration values are optional.
    store.mode=file
    store.lock.mode=file
    store.session.mode=file
    #Used for password encryption
    #store.publicKey=
    
    #If `store.mode,store.lock.mode,store.session.mode` are not equal to `file`, you can remove the configuration block.
    store.file.dir=file_store/data
    store.file.maxBranchSessionSize=16384
    store.file.maxGlobalSessionSize=512
    store.file.fileWriteBufferCacheSize=16384
    store.file.flushDiskMode=async
    store.file.sessionReloadReadSize=100
    
    #These configurations are required if the `store mode` is `db`. If `store.mode,store.lock.mode,store.session.mode` are not equal to `db`, you can remove the configuration block.
    #jdbc的配置
    store.db.datasource=druid
    store.db.dbType=mysql
    store.db.driverClassName=com.mysql.jdbc.Driver
    store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    store.db.user=root
    store.db.password=root
    store.db.minConn=5
    store.db.maxConn=30
    store.db.globalTable=global_table
    store.db.branchTable=branch_table
    store.db.distributedLockTable=distributed_lock
    store.db.queryLimit=100
    store.db.lockTable=lock_table
    store.db.maxWait=5000
    
    #These configurations are required if the `store mode` is `redis`. If `store.mode,store.lock.mode,store.session.mode` are not equal to `redis`, you can remove the configuration block.
    store.redis.mode=single
    store.redis.single.host=127.0.0.1
    store.redis.single.port=6379
    #store.redis.sentinel.masterName=
    #store.redis.sentinel.sentinelHosts=
    store.redis.maxConn=10
    store.redis.minConn=1
    store.redis.maxTotal=100
    store.redis.database=0
    #store.redis.password=
    store.redis.queryLimit=100
    
    #Transaction rule configuration, only for the server
    server.recovery.committingRetryPeriod=1000
    server.recovery.asynCommittingRetryPeriod=1000
    server.recovery.rollbackingRetryPeriod=1000
    server.recovery.timeoutRetryPeriod=1000
    server.maxCommitRetryTimeout=-1
    server.maxRollbackRetryTimeout=-1
    server.rollbackRetryTimeoutUnlockEnable=false
    server.distributedLockExpireTime=10000
    server.xaerNotaRetryTimeout=60000
    server.session.branchAsyncQueueSize=5000
    server.session.enableBranchAsyncRemove=false
    server.enableParallelRequestHandle=false
    
    #Metrics configuration, only for the server
    metrics.enabled=false
    metrics.registryType=compact
    metrics.exporterList=prometheus
    metrics.exporterPrometheusPort=9898
    

    创建完成,以后就可以通过Nacos配置中心动态的改变配置了:

    5.Seata-AT

    AT是使用最广泛的模式,Seata-AT模式会自动生成事务的二阶段提交和回滚操作,具体流程为:

    • 第一阶段
      1.TM发起准备事务,执行本地事务并远程调用RM方法
      2.TC拦截业务SQL,并解析SQL,在数据更新前,在TM/RM本地undo_log表中生成对应的Undo日志
      3.TM/RM执行本地事务,数据更新,并形成行锁,防止其他事务修改
    • 第二阶段
      根据第一阶段是否有本地事务执行失败,如果有,发起回滚,TM/RM根据Undo日志执行反向更新操作;如果都执行成功,发起提交,TM/RM删除Undo日志和释放资源
    5.1 表设计

    设计两张表:订单表(order_tbl)和库存表(stock_tbl)

    order_tbl stock_tbl

    stock表中插入一条数据:

    5.2 创建项目

    分别创建order模块与stock模块:

    order模块 stock模块
    5.3 yml配置

    两个模块只是端口和服务名不同,所以就只贴出一个:

    server:
      port: 8201
    
    spring:
      application:
        name: seata-order
      cloud:
        discovery:
          server-addr: 127.0.0.1:8848
        alibaba:
          seata:
            tx-service-group: default_tx_group
      datasource:
        url: jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
        username: root
        password: root
        driver-class-name: com.mysql.cj.jdbc.Driver
        type: com.alibaba.druid.pool.DruidDataSource
    
    seata:
      tx-service-group: default_tx_group # 事务组名称,要和服务端对应
      service:
        vgroup-mapping:
          default_tx_group: default # key是事务组名称 value要和服务端的机房名称保持一致
    
    management:
      endpoint:
        web:
          exposure:
            include:'*'
    
    mybatis:
      mapper-locations: classpath:mybatis/*.xml #指定映射文件路径
      type-aliases-package: com.aruba.bean
    
    

    需要注意的是,我们先前为seata-server配置的事务组,要与客户端yml配置对应上:

    seata-server配置 客户端模块yml配置
    5.4 @GlobalTransactional

    其他的CURD操作就不展示了,在Service层相应方法上使用@GlobalTransactional注解开启分布式全局事务:

    @Service
    public class OrderServiceImpl implements OrderService {
        @Autowired
        private OrderMapper orderMapper;
        @Autowired
        private StockClient stockClient;
    
        @Override
        @GlobalTransactional// 开启分布式事务
        public void addOrder() {
            Order order = new Order(null, 1, "新增订单");
            orderMapper.addOrder(order);
            stockClient.reduceStock(1);
        }
    }
    

    StockClient是使用OpenFeign远程调用stock模块的方法,其方法中抛出一个异常:

    @RestController
    public class StockController {
        @Autowired
        private StockService stockService;
    
        @GetMapping("/reduceStock")
        public String reduceStock(@RequestParam("id") Integer id) {
            int i = 1 / 0;
            System.out.println("请求来了");
            stockService.reduceStock(id);
            return "减库存成功";
        }
    
    }
    

    启动服务后,经过测试,远程服务调用失败,数据库中的数据最终都回滚到了最初状态

    其他模式

    由于篇幅原因,其他模式暂时不做展示,另外三种模式使用的也不多,可以通过官方Demo进行参考:https://github.com/seata/seata-samples

    项目地址:

    https://gitee.com/aruba/spring-cloud-alibaba-study.git

    相关文章

      网友评论

        本文标题:SpringCloud--Alibaba入门(OpenFeign

        本文链接:https://www.haomeiwen.com/subject/jbhegrtx.html