springboot + seata + httpclient调用

Posted cn_yaojin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了springboot + seata + httpclient调用相关的知识,希望对你有一定的参考价值。

三个项目,彼此使用seata自带的httpclient来完成相互调用,三个项目分别是:seata_user、seata_msg、seata_online,对应三个数据库。其中seata_online是调用入口,分别调用seata_user、seata_msg,其中当参数等于5的时候,会抛出异常,3个数据库均回滚事务;参数不等于5的时候,3个数据库正常保存数据。

三个项目引入seata的版本如下

        <dependency>
            <groupId>io.seata</groupId>
            <artifactId>seata-spring-boot-starter</artifactId>
            <version>1.6.1</version>
        </dependency>
        <dependency>
            <groupId>io.seata</groupId>
            <artifactId>seata-core</artifactId>
            <version>1.6.1</version>
        </dependency>


        <dependency>
            <groupId>com.alibaba.nacos</groupId>
            <artifactId>nacos-client</artifactId>
            <version>2.2.0</version>
        </dependency>

一、seata_user

        1、application.yml

server:
  port: 40023
  undertow:
    # 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程
    io-threads: 4
    # 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
    # 它的值设置取决于系统线程执行任务的阻塞系数,默认值是IO线程数*8
    worker-threads: 200
    # 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
    # 每块buffer的空间大小,越小的空间被利用越充分
    buffer-size: 1024
    # 是否分配的直接内存
    direct-buffers: true

mybatis-plus:
  mapper-locations: classpath:mapper/*.xml
  config-location: classpath:mapper/config/sqlMapConfig.xml

spring:
  servlet:
    multipart:
      enabled: true
      max-file-size: 100MB
      max-request-size: 100MB
  application:
    name: seata_user
  datasource:
    dynamic:
      primary: user #设置默认的数据源或者数据源组,默认值即为master
      strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
      datasource:
        user:
          url: jdbc:mysql://127.0.0.1:3306/business_platform_demo?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true
          username: root
          password: root
          type: com.alibaba.druid.pool.DruidDataSource
          driver-class-name: com.mysql.cj.jdbc.Driver
  redis:
    database: 0
    host: 127.0.0.1
    port: 6379
    password:
    max-active: 100
    max-wait: 1000

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

swagger:
  title: app端
  scanpackages: com.cn

logging:
  level:
    com.cn: info

servlet:
  multipart:
    enabled: true
    max-file-size: 100MB
    max-request-size: 100MB


# seata配置
seata:
  enabled: true
  # Seata 应用编号,默认为 $spring.application.name
  application-id: $spring.application.name
  # Seata 事务组编号,用于 TC 集群名
  tx-service-group: $spring.application.name-group
  # 开启自动代理
  enable-auto-data-source-proxy: true
  # 服务配置项
  service:
    # 虚拟组和分组的映射
    vgroup-mapping:
      gulimall-order-group: default
    grouplist:
      default: 192.168.3.21:8091
  config:
    type: nacos
    nacos:
      serverAddr: 192.168.3.133:8848
      group: SHOP_GROUP
      namespace: 53e24698-1032-41cc-966f-129931d10ec9
  registry:
    type: nacos
    nacos:
      application: seata-server
      server-addr: 192.168.3.133:8848
      namespace: 53e24698-1032-41cc-966f-129931d10ec9
      group: SHOP_GROUP

        2. 在nacos上新建如下配置:service.vgroupMapping.seata_user-group

         3.  新建mapper

@Mapper
public interface IUserMapper extends BaseMapper<PortalUserEntity> 

         4. 新建service

@Service
@Slf4j
public class UserService 

    @Autowired
    private IUserMapper userMapper;

    @Transactional
    public void saveTmp(PortalUserEntity info) 
        info.setId(SnowflakeIdWorkerUtil.getId());
        info.setAddTime(DateUtil.getTime("yyyy-MM-dd HH:mm:ss"));
        this.userMapper.insert(info);
    


        5. 新建controller

@RestController
@RequestMapping(value = "mobile/demo")
@Api(tags = "Demo")
public class DemoController 

    @Autowired
    private UserService userService;

    @ApiOperation(value = "saveUser")
    @PostMapping(value = "saveUser")
    public ResultMsg saveUser(
            @RequestBody PortalUserEntity user
    ) 
        this.userService.saveTmp(user);
        return ResultMsg.builder();
    

        6. 启动,启动后的地址及端口:http://127.0.0.1:40023/mobile/demo/saveUser

二、seata_msg

        1、application.yml

server:
  port: 40024
  undertow:
    # 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程
    io-threads: 4
    # 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
    # 它的值设置取决于系统线程执行任务的阻塞系数,默认值是IO线程数*8
    worker-threads: 200
    # 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
    # 每块buffer的空间大小,越小的空间被利用越充分
    buffer-size: 1024
    # 是否分配的直接内存
    direct-buffers: true

mybatis-plus:
  mapper-locations: classpath:mapper/*.xml
  config-location: classpath:mapper/config/sqlMapConfig.xml

spring:
  servlet:
    multipart:
      enabled: true
      max-file-size: 100MB
      max-request-size: 100MB
  application:
    name: seata_msg
  datasource:
    dynamic:
      primary: company #设置默认的数据源或者数据源组,默认值即为master
      strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
      datasource:
        company:
          url: jdbc:mysql://127.0.0.1:3306/business_company_demo?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true
          username: root
          password: root
          type: com.alibaba.druid.pool.DruidDataSource
          driver-class-name: com.mysql.cj.jdbc.Driver
      seata: true
  redis:
    database: 0
    host: 127.0.0.1
    port: 6379
    password:
    max-active: 100
    max-wait: 1000

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

swagger:
  title: seata_user
  scanpackages: com.cn

logging:
  level:
    com.cn: info

servlet:
  multipart:
    enabled: true
    max-file-size: 100MB
    max-request-size: 100MB


# seata配置
seata:
  enabled: true
  # Seata 应用编号,默认为 $spring.application.name
  application-id: $spring.application.name
  # Seata 事务组编号,用于 TC 集群名
  tx-service-group: $spring.application.name-group
  # 开启自动代理
  enable-auto-data-source-proxy: true
  # 服务配置项
  service:
    # 虚拟组和分组的映射
    vgroup-mapping:
      gulimall-order-group: default
    grouplist:
      default: 192.168.3.21:8091
  config:
    type: nacos
    nacos:
      serverAddr: 192.168.3.133:8848
      group: SHOP_GROUP
      namespace: 53e24698-1032-41cc-966f-129931d10ec9
  registry:
    type: nacos
    nacos:
      application: seata-server
      server-addr: 192.168.3.133:8848
      namespace: 53e24698-1032-41cc-966f-129931d10ec9
      group: SHOP_GROUP

        2、在nacos上加入以下配置:service.vgroupMapping.seata_msg-group

        3、新建mapper

@Mapper
public interface IMsgMapper extends BaseMapper<MsgEntity> 

        4、新建service

@Service
@Slf4j
public class MsgService 

    @Autowired
    private IMsgMapper msgMapper;


    @Transactional
    public void saveTmp(MsgEntity info) 
        info.setId(SnowflakeIdWorkerUtil.getId());
        info.setCreateTime(DateUtil.getTime("yyyy-MM-dd HH:mm:ss"));
        this.msgMapper.insert(info);

    

        5、新建controller

@RestController
@RequestMapping(value = "mobile/demo")
public class Demo2Controller 

    @Autowired
    private MsgService msgService;

    @ApiOperation(value = "saveMsg")
    @PostMapping(value = "saveMsg")
    public ResultMsg saveMsg(
            @RequestBody MsgEntity msg
    ) 
        this.msgService.saveTmp(msg);
        return ResultMsg.builder();
    


        6、启动后的访问地址:http://127.0.0.1:40024/mobile/demo/saveMsg

三、seata_online      

        1、application.yml

server:
  port: 40025
  undertow:
    # 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程
    io-threads: 4
    # 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
    # 它的值设置取决于系统线程执行任务的阻塞系数,默认值是IO线程数*8
    worker-threads: 200
    # 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
    # 每块buffer的空间大小,越小的空间被利用越充分
    buffer-size: 1024
    # 是否分配的直接内存
    direct-buffers: true

mybatis-plus:
  mapper-locations: classpath:mapper/*.xml
  config-location: classpath:mapper/config/sqlMapConfig.xml

spring:
  servlet:
    multipart:
      enabled: true
      max-file-size: 100MB
      max-request-size: 100MB
  application:
    name: seata_online
  datasource:
    dynamic:
      primary: master #设置默认的数据源或者数据源组,默认值即为master
      strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
      datasource:
        master:
          url: jdbc:mysql://127.0.0.1:3306/business_portal_demo?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true
          username: root
          password: root
          type: com.alibaba.druid.pool.DruidDataSource
          driver-class-name: com.mysql.cj.jdbc.Driver
      seata: true
  redis:
    database: 0
    host: 127.0.0.1
    port: 6379
    password:
    max-active: 100
    max-wait: 1000

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

swagger:
  title: seata_user
  scanpackages: com.cn

logging:
  level:
    com.cn: info

servlet:
  multipart:
    enabled: true
    max-file-size: 100MB
    max-request-size: 100MB



# seata配置
seata:
  enabled: true
  # Seata 应用编号,默认为 $spring.application.name
  application-id: $spring.application.name
  # Seata 事务组编号,用于 TC 集群名
  tx-service-group: $spring.application.name-group
  # 开启自动代理
  enable-auto-data-source-proxy: true
  # 服务配置项
  service:
    # 虚拟组和分组的映射
    vgroup-mapping:
      gulimall-order-group: default
    grouplist:
      default: 192.168.3.21:8091
  config:
    type: nacos
    nacos:
      serverAddr: 192.168.3.133:8848
      group: SHOP_GROUP
      namespace: 53e24698-1032-41cc-966f-129931d10ec9
  registry:
    type: nacos
    nacos:
      application: seata-server
      server-addr: 192.168.3.133:8848
      namespace: 53e24698-1032-41cc-966f-129931d10ec9
      group: SHOP_GROUP

        2、在nacos上加入以下配置:service.vgroupMapping.seata_online-group

        3、新建mapper

@Mapper
public interface IOnmessageMapper extends BaseMapper<OnlineMessageEntity> 

        4、新建service

@Service
@Slf4j
public class OnMessageService 

    @Autowired
    private IOnmessageMapper onmessageMapper;


    @Transactional
    public void add(OnmessageEdit info) 
        OnlineMessageEntity msg = new OnlineMessageEntity();
        BeanUtils.copyProperties(info, msg);
        msg.setId(SnowflakeIdWorkerUtil.getId());
        msg.setCreateDate(DateUtil.getTime("yyyy-MM-dd HH:mm:ss"));
        if (StringUtils.isEmpty(msg.getImgs())) 
            msg.setImgs("");
        
        this.onmessageMapper.insert(msg);
    

import com.alibaba.fastjson.JSONObject;
import com.cn.exception.MyException;
import com.cn.msg.ResultMsg;
import com.cn.util.JsonUtil;
import com.cn.web.portal.vo.OnmessageEdit;
import io.seata.integration.http.DefaultHttpExecutor;
import io.seata.spring.annotation.GlobalTransactional;
import org.apache.http.HttpResponse;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;



@Service
public class DemoService 

    @Autowired
    private OnMessageService onMessageService;

    private void saveUser() throws IOException 
        // 参数拼接
        JSONObject params = new JSONObject().fluentPut("title", "我爱我的祖国")
                .fluentPut("content", "通知们好,我爱我的祖国");
        // 执行调用
        HttpResponse response = DefaultHttpExecutor.getInstance().executePost("http://127.0.0.1:40024", "/mobile/demo/saveMsg",
                params, HttpResponse.class);
        // 解析结果
        ResultMsg result = JsonUtil.fromJson(EntityUtils.toString(response.getEntity()), ResultMsg.class);
        if (!result.isSuccess()) 
            throw new MyException(result.getMsg());
        
    

    private void saveMsg() throws IOException 
        // 参数拼接
        JSONObject params = new JSONObject().fluentPut("phone", "13909384351")
                .fluentPut("userName", "cn_yang").fluentPut("userType", "0").fluentPut("userStatus", 0);
        // 执行调用
        HttpResponse response = DefaultHttpExecutor.getInstance().executePost("http://127.0.0.1:40023", "/mobile/demo/saveUser",
                params, HttpResponse.class);
        ResultMsg result = JsonUtil.fromJson(EntityUtils.toString(response.getEntity()), ResultMsg.class);
        if (!result.isSuccess()) 
            throw new MyException(result.getMsg());
        
    

    @GlobalTransactional
    public void save0(Long id) throws IOException 
        saveUser();
        saveMsg();
        OnmessageEdit lineMsg = new OnmessageEdit();
        lineMsg.setTitle("标题").setContent("alsdkfja").setMsgType("0");
        onMessageService.add(lineMsg);
        if (id == 5) 
            throw new MyException("尝试跑出异常");
        
    



 以上DemoService中,注意一下4个依赖

import io.seata.integration.http.DefaultHttpExecutor; // seata自带的httpclient执行器
import io.seata.spring.annotation.GlobalTransactional; // 全局事务
import org.apache.http.HttpResponse;
import org.apache.http.util.EntityUtils;

        5、新建controller

@RestController
@RequestMapping(value = "mobile/demo")
public class Demo2Controller 

    @Autowired
    private DemoService demoService;

    @ApiOperation(value = "save2")
    @GetMapping(value = "save2")
    public ResultMsg save2(
            @ApiParam(name = "id") @RequestParam Long id
    ) 
        try 
            this.demoService.save0(id);
         catch (IOException e) 
            e.printStackTrace();
        
        return ResultMsg.builder();
    

        6、启动后的访问地址:http://127.0.0.1:40025/mobile/demo/save2?id=3  当id=5的时候,抛出异常,事务回滚,三个库里面的数据均恢复原样;当id不等于5的时候,正常插入数据。

SpringBoot集成nacos+seata1.5.1相关注意事项

1. 客户端配置:在项目的yml配置文件中,以下标红的两个地方要一致,并且和server。

seata:
  enabled: true
  application-id: $spring.application.name
  tx-service-group: demo-seata-service-group # 事务群组(可以每个应用独立取名,也可以使用相同的名字)
  service:
    vgroup-mapping:
      demo-seata-service-group: default # TC 集群(必须与seata-server保持一致)
    disable-global-transaction: false # 禁用全局事务(默认false)  
  registry:
    type: nacos
    nacos:
      application: seata-server
      server-addr:  xx.xx.xx.xx:8848
      namespace: platform
      group: DEFAULT_GROUP
      cluster: default     

2.  设置seata注册到nacos上的ip为外网ip

        设置环境变量 SEATA_IP = 外网IP,如在/etc/profile 中增加一行  

export SEATA_IP= xx.xx.xx.xx

        生效环境变量

source /etc/profile     

        再用 sh seata-server.sh 命令启动seata服务即可。

以上是关于springboot + seata + httpclient调用的主要内容,如果未能解决你的问题,请参考以下文章

SpringBoot+SpringCloud+Seata配置

分布式事务:SpringBoot+Dubbo+Seata+Nacos 实现案例

Seata集成与部署

SpringBoot集成nacos+seata1.5.1相关注意事项

SpringBoot集成nacos+seata1.5.1相关注意事项

SpringBoot集成Shardingjdbc+seata AT模式