springboot + seata + httpclient调用

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

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

        
            io.seata
            seata-spring-boot-starter
            1.6.1
        
        
            io.seata
            seata-core
            1.6.1
        


        
            com.alibaba.nacos
            nacos-client
            2.2.0
        

一、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

springboot + seata + httpclient调用_第1张图片

         3.  新建mapper

@Mapper
public interface IUserMapper extends BaseMapper {
}

         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

springboot + seata + httpclient调用_第2张图片

        3、新建mapper

@Mapper
public interface IMsgMapper extends BaseMapper {
}

        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

springboot + seata + httpclient调用_第3张图片

        3、新建mapper

@Mapper
public interface IOnmessageMapper extends BaseMapper {
}

        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的时候,正常插入数据。

你可能感兴趣的:(spring,boot,后端,java)