首页
归档
留言
友链
广告合作
壁纸
更多
美女主播
Search
1
博瑞GE车机升级/降级
5,610 阅读
2
Mac打印机设置黑白打印
4,952 阅读
3
修改elementUI中el-table树形结构图标
4,895 阅读
4
Mac客户端添加腾讯企业邮箱方法
4,674 阅读
5
intelliJ Idea 2022.2.X破解
4,357 阅读
后端开发
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
登录
/
注册
Search
标签搜索
Spring Boot
Java
Vue
Spring Cloud
Mac
MyBatis
WordPress
MacOS
asp.net
Element UI
Nacos
.Net
Spring Cloud Alibaba
MySQL
Mybatis-Plus
Typecho
jQuery
Java Script
IntelliJ IDEA
微信小程序
Laughing
累计撰写
627
篇文章
累计收到
1,421
条评论
首页
栏目
后端开发
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
页面
归档
留言
友链
广告合作
壁纸
美女主播
搜索到
103
篇与
的结果
2021-07-27
Spring Cloud使用Nacos作为服务中心编写服务提供者
在Nacos集群配置中,我们介绍了Nacos的安装以及集群配置,本文我们介绍一下在Spring Cloud中如何使用Nacos作为服务注册中心。编写父工程父工程只提供基本的依赖关系,一定要注意版本,否则会出现很多乱七八糟的错误。父工程依赖如下<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>cc.lisen</groupId> <artifactId>Cloud2020</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging> <modules> <module>cloud-provider-payment8001</module> <module>cloud-consumer-order80</module> <module>cloud-api-common</module> <module>cloud-provider-payment8004</module> <module>cloud-consumerzk-order80</module> <module>cloud-consumer-feign-order80</module> <module>cloud-provider-hystrix-payment8001</module> <module>cloud-consumer-feign-hystrix-order80</module> <module>cloud-consumer-hystrix-dashboard9001</module> <module>cloud-gateway-gateway9527</module> <module>cloud-config-center-3344</module> <module>cloud-config-client-3355</module> <module>cloud-config-client-3366</module> <module>cloud-stream-rabbitmq-provider8801</module> <module>cloud-stream-rabbitmq-consumer8802</module> <module>cloud-stream-rabbitmq-consumer8803</module> <module>cloud-stream-rabbitmq-consumer8802</module> <module>cloud-stream-rabbitmq-consumer8803</module> <module>cloud-stream-rabbitmq-consumer8803</module> <module>cloudalibaba-provider-payment9001</module> <module>cloudalibaba-provider-payment9002</module> <module>cloudalibaba-consumer-order8888</module> <module>cloudalibaba-config-nacos-client3377</module> <module>cloudalibaba-sentinel-service8401</module> <module>seata-order-service2001</module> <module>seata-storage-service2002</module> <module>seata-account-service2003</module> </modules> <!-- 统一管理jar包版本--> <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> <junit.version>4.12</junit.version> <log4j.version>1.2.17</log4j.version> <lombok.version>1.16.18</lombok.version> <mysql.version>5.1.47</mysql.version> <druid.version>1.1.16</druid.version> <mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.3.12.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Hoxton.SR12</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <!-- <version>2.1.0.RELEASE</version>--> <version>2.2.1.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>${druid.version}</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>${mybatis.spring.boot.version}</version> </dependency> </dependencies> </dependencyManagement> <build> <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --> <plugins> <plugin> <artifactId>maven-clean-plugin</artifactId> <version>3.1.0</version> </plugin> <plugin> <artifactId>maven-site-plugin</artifactId> <version>3.7.1</version> </plugin> <plugin> <artifactId>maven-project-info-reports-plugin</artifactId> <version>3.0.0</version> </plugin> </plugins> </pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-site-plugin</artifactId> <configuration> <locales>en,fr</locales> </configuration> </plugin> </plugins> </build> <reporting> <plugins> <plugin> <artifactId>maven-project-info-reports-plugin</artifactId> </plugin> </plugins> </reporting> </project> 请忽略其他的项目模块。编写服务提供者编写一个简单的服务提供者,输出一个字符串。添加依赖<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>Cloud2020</artifactId> <groupId>cc.lisen</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloudalibaba-provider-payment9002</artifactId> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.5</version> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> </dependencies> </project>修改配置文件server: port: 9001 spring: application: name: nacos-provider-payment cloud: nacos: discovery: server-addr: 192.168.120.180:8848 management: endpoints: web: exposure: include: "*"修改启动类@SpringBootApplication public class PaymentProviderMain9001 { public static void main(String[] args){ SpringApplication.run(PaymentProviderMain9001.class,args); } }编写服务接口@RestController @Slf4j public class NacosController { @Value("${server.port}") private String serverPort; @GetMapping(value = "/echo/{string}") public String echo(@PathVariable String string) { return "Hello Nacos Discovery " + string+"\t当前服务端口:"+serverPort; } }启动服务,我们可以查看一下Nacos是否有对应的服务。集群一般情况下,为了系统的健壮性,我们都会进行集群。我这里为了方便调试,复制了一个cloudalibaba-provider-payment9001工程,只是将端口号改成9002,然后,我们启动9002端口项目,再次查看Nacos可以看到当前有两个实例了。集群的关键时服务名称需要一致,也就是我们配置文件中的spring.application.name必须保持一致。
2021年07月27日
1,032 阅读
0 评论
0 点赞
2021-07-26
Seata基础教程下-Spring Cloud Alibaba使用Seata
在seata基础教程上-seata 1.4.2的安装及基于Nacos的配置中,我们基于Nacos完成了Seata的安装及配置,这篇文章我们介绍下在Spring Cloud Alibaba中的使用。环境准备学习本文之前,你应该准备好以下环境。Nacos 2.0.2版本并运行Seata Server 1.4.2版本,配置好Nacos并运行前置知识本文通过Spring Cloud Alibaba,使用Nacos作为服务中心,通过OpenFeign实现服务之间的调用,所以你必须具备以下基础知识:在Spring Cloud中使用Nacos作为服务注册、发现中心的配置及使用。在Spring Cloud中通过OpenFeign实现服务之间的调用。MyBatis的使用。场景描述本文参考尚硅谷阳哥的视频教程,使用最新的Nacos(2.0.2)及Seata(1.4.2),演示用户下单的业务场景,基本流程如下其实业务很简单,用户下单,扣减商品库存表数据然后减去账户余额,如果不是微服务架构,我们一般使用一个系统,基本上一个@Transactional注解就解决了。基于微服务就不同了,我们提供三个微服务,分别为订单服务、库存服务、账户服务,三个服务对应三个数据库,分别为seata_order、seata_storage、seata_account,现在不同业务分布在不同的数据库中,那么我们在进行数据回滚就不太好处理了。Seata的分布式交易解决方案有了Seata就简单了,我们只需要使用一个@GlobalTransactional注解在业务方法上就可以了。Spring Cloud使用Seata创建数据库创建三个数据库,分别为seata_order、seata_storage、seata_account。订单微服务表结构create table t_order ( id bigint auto_increment comment '主键', user_id bigint null comment '用户Id', product_id bigint null, count int null, money decimal(11) null, status int(1) null, constraint t_order_id_uindex unique (id) ); alter table t_order add primary key (id); create table undo_log ( id bigint auto_increment primary key, branch_id bigint not null, xid varchar(100) not null, context varchar(128) not null, rollback_info longblob not null, log_status int not null, log_created datetime not null, log_modified datetime not null, constraint ux_undo_log unique (xid, branch_id) ) charset=utf8;账户微服务表结构create table t_account ( id bigint auto_increment, user_id bigint null, total decimal null, used decimal null, residue decimal null, constraint t_account_id_uindex unique (id) ); alter table t_account add primary key (id); create table undo_log ( id bigint auto_increment primary key, branch_id bigint not null, xid varchar(100) not null, context varchar(128) not null, rollback_info longblob not null, log_status int not null, log_created datetime not null, log_modified datetime not null, constraint ux_undo_log unique (xid, branch_id) ) charset=utf8; INSERT INTO seata_account.t_account (id, user_id, total, used, residue) VALUES (1, 1, 1000, 0, 1000);库存微服务表结构create table t_storage ( id bigint auto_increment, product_id bigint null, total int null, used int null, residue int null comment '剩余库存', constraint t_storage_id_uindex unique (id) ); alter table t_storage add primary key (id); create table undo_log ( id bigint auto_increment primary key, branch_id bigint not null, xid varchar(100) not null, context varchar(128) not null, rollback_info longblob not null, log_status int not null, log_created datetime not null, log_modified datetime not null, constraint ux_undo_log unique (xid, branch_id) ) charset=utf8; INSERT INTO seata_storage.t_storage (id, product_id, total, used, residue) VALUES (2, 1, 100, 0, 100);创建工程工程结构,如下图,请忽略其他模块。{message type="warning" content="一定要注意依赖里面的版本号,不然会出现各种错误。"/}创建父工程父工程用于维护依赖管理,没有具体代码。增加依赖<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>cc.lisen</groupId> <artifactId>Cloud2020</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging> <modules> <module>cloud-provider-payment8001</module> <module>cloud-consumer-order80</module> <module>cloud-api-common</module> <module>cloud-provider-payment8004</module> <module>cloud-consumerzk-order80</module> <module>cloud-consumer-feign-order80</module> <module>cloud-provider-hystrix-payment8001</module> <module>cloud-consumer-feign-hystrix-order80</module> <module>cloud-consumer-hystrix-dashboard9001</module> <module>cloud-gateway-gateway9527</module> <module>cloud-config-center-3344</module> <module>cloud-config-client-3355</module> <module>cloud-config-client-3366</module> <module>cloud-stream-rabbitmq-provider8801</module> <module>cloud-stream-rabbitmq-consumer8802</module> <module>cloud-stream-rabbitmq-consumer8803</module> <module>cloud-stream-rabbitmq-consumer8802</module> <module>cloud-stream-rabbitmq-consumer8803</module> <module>cloud-stream-rabbitmq-consumer8803</module> <module>cloudalibaba-provider-payment9001</module> <module>cloudalibaba-provider-payment9002</module> <module>cloudalibaba-consumer-order80</module> <module>cloudalibaba-config-nacos-client3377</module> <module>cloudalibaba-sentinel-service8401</module> <module>seata-order-service2001</module> <module>seata-storage-service2002</module> <module>seata-account-service2003</module> </modules> <!-- 统一管理jar包版本--> <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> <junit.version>4.12</junit.version> <log4j.version>1.2.17</log4j.version> <lombok.version>1.16.18</lombok.version> <mysql.version>5.1.47</mysql.version> <druid.version>1.1.16</druid.version> <mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.3.12.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Hoxton.SR12</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <!-- <version>2.1.0.RELEASE</version>--> <version>2.2.1.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>${druid.version}</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>${mybatis.spring.boot.version}</version> </dependency> </dependencies> </dependencyManagement> <build> <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --> <plugins> <plugin> <artifactId>maven-clean-plugin</artifactId> <version>3.1.0</version> </plugin> <plugin> <artifactId>maven-site-plugin</artifactId> <version>3.7.1</version> </plugin> <plugin> <artifactId>maven-project-info-reports-plugin</artifactId> <version>3.0.0</version> </plugin> </plugins> </pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-site-plugin</artifactId> <configuration> <locales>en,fr</locales> </configuration> </plugin> </plugins> </build> <reporting> <plugins> <plugin> <artifactId>maven-project-info-reports-plugin</artifactId> </plugin> </plugins> </reporting> </project> 创建通用模块通用模块这里维护了一个CommonResult用于返回Json对象。增加依赖<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>Cloud2020</artifactId> <groupId>cc.lisen</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-api-common</artifactId> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.5</version> </dependency> </dependencies> </project>增加实体package cc.lisen.springcloud.entites; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; /** * 博客:https://lisen.cc * Description: * * @Author: 李森的博客 * DateTime: 2021-07-24 11:25 */ @Data @AllArgsConstructor @NoArgsConstructor public class CommonResult<T> { private Integer code; private String message; private T data; public CommonResult(Integer code, String message) { this(code, message, null); } }创建订单微服务模块订单模块维护订单微服务,通过Feign调用库存及账户微服务。增加依赖<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>Cloud2020</artifactId> <groupId>cc.lisen</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>seata-order-service2001</artifactId> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.5</version> </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> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> <exclusions> <exclusion> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> <version>1.4.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>cc.lisen</groupId> <artifactId>cloud-api-common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> </project>修改配置文件配置文件一定要注意,tx-service-group一定要与我们Seata服务端的配置保持一致。server: port: 2001 spring: application: name: seata-order-service cloud: nacos: discovery: server-addr: localhost:1111 loadbalancer: retry: enabled: false datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/seata_order?useSSL=false username: root password: root feign: hystrix: enabled: false mybatis: type-aliases-package: cc.lisen.springcloud.alibaba.seata.domain mapperLocations: classpath:/mapper/*.xml seata: tx-service-group: my_tx_group #与config.txt 中的service.vgroupMapping.my_tx_group=default一致 service: vgroup-mapping: my_tx_group: default #与config.txt 中的service.vgroupMapping.my_tx_group=default一致 config: type: nacos nacos: server-addr: localhost:1111 group: "SEATA_GROUP" namespace: "" username: "nacos" password: "nacos"增加实体@Data @AllArgsConstructor @NoArgsConstructor public class Order { /** * 主键 */ private Long id; /** * 用户Id */ private Long userId; private Long productId; private Integer count; private BigDecimal money; private Integer status; }增加Mapperpublic interface OrderDao { int create(Order record); int update(@Param("userId") Long userId, @Param("status") Integer status);数据库操作<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="cc.lisen.springcloud.alibaba.seata.dao.OrderDao"> <resultMap id="BaseResultMap" type="cc.lisen.springcloud.alibaba.seata.domain.Order"> <!--@Table t_order--> <result column="id" jdbcType="BIGINT" property="id"/> <result column="user_id" jdbcType="BIGINT" property="userId"/> <result column="product_id" jdbcType="BIGINT" property="productId"/> <result column="count" jdbcType="INTEGER" property="count"/> <result column="money" jdbcType="DECIMAL" property="money"/> <result column="status" jdbcType="INTEGER" property="status"/> </resultMap> <sql id="Base_Column_List"> <!--@mbg.generated--> id, user_id, product_id, `count`, money, `status` </sql> <insert id="create" parameterType="cc.lisen.springcloud.alibaba.seata.domain.Order"> <!--@mbg.generated--> insert into t_order (user_id, product_id, `count`, money, `status`) values (#{userId,jdbcType=BIGINT}, #{productId,jdbcType=BIGINT}, #{count,jdbcType=INTEGER}, #{money,jdbcType=DECIMAL}, #{status,jdbcType=INTEGER}) </insert> <update id="update"> update t_order set status = 1 where user_id = #{userId} and status = #{status} </update> </mapper>增加服务接口增加账户微服务Feign调用接口@FeignClient(value = "seata-account-service") public interface IAccountService { @PostMapping("/account/decrease") public CommonResult accountDecrease(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money); }增加库存微服务Feign调用接口@FeignClient(value = "seata-storage-service") public interface IStorageService { @PostMapping("/storage/decrease") public CommonResult storageDecrease(@RequestParam("productId") Long productId,@RequestParam("count") Integer count); } 增加订单接口public interface IOrderService { int create(Order record); }增加订单服务实现@Service @Slf4j public class OrderServiceImpl implements IOrderService { @Resource private OrderDao orderDao; @Resource private IStorageService storageService; @Resource private IAccountService accountService; @Override @GlobalTransactional(name = "fsp-create-order") // @Transactional public int create(Order order) { log.info("--------->开始新建订单"); orderDao.create(order); log.info("--------->订单微服务开始调用库存,做扣减Count"); storageService.storageDecrease(order.getProductId(), order.getCount()); log.info("--------->订单微服务开始调用库存,做扣减End"); log.info("--------->订单微服务开始调用账户,做扣减Money"); accountService.accountDecrease(order.getUserId(), order.getMoney()); log.info("--------->订单微服务开始调用账户,做扣减End"); log.info("--------->修改订单状态开始"); orderDao.update(order.getUserId(), 0); log.info("--------->修改订单状态End"); return 1; } }增加接口controller@RestController @Slf4j @RequestMapping("/order") public class OrderController { @Resource private IOrderService orderService; @PostMapping("/create") public CommonResult create(Order order) { orderService.create(order); return new CommonResult(200, "订单创建成功"); } }增加DataSource,让Seata代理数据库@Configuration public class DataSourceProxyConfig { @Bean @ConfigurationProperties(prefix = "spring.datasource") public DataSource druidDataSource(){ return new DruidDataSource(); } }修改启动类取消数据源自动配置,让Seata能够代理数据源。@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) @EnableDiscoveryClient @EnableFeignClients @MapperScan(basePackages = {"cc.lisen.springcloud.alibaba.seata.dao"}) public class SeataOrderServiceMain2001 { public static void main(String[] args) { SpringApplication.run(SeataOrderServiceMain2001.class, args); } }创建库存微服务模块库存微服务用于提供扣减库存的微服务接口。增加依赖<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>Cloud2020</artifactId> <groupId>cc.lisen</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>seata-storage-service2002</artifactId> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.5</version> </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> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> <exclusions> <exclusion> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> <version>1.4.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>cc.lisen</groupId> <artifactId>cloud-api-common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> </project>修改配置文件server: port: 2002 spring: application: name: seata-storage-service cloud: nacos: discovery: server-addr: localhost:1111 loadbalancer: retry: enabled: false datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/seata_storage?useSSL=false username: root password: root feign: hystrix: enabled: false mybatis: type-aliases-package: cc.lisen.springcloud.alibaba.seata.domain mapperLocations: classpath:/mapper/*.xml seata: tx-service-group: my_tx_group #与config.txt 中的service.vgroupMapping.my_tx_group=default一致 service: vgroup-mapping: my_tx_group: default #与config.txt 中的service.vgroupMapping.my_tx_group=default一致 config: type: nacos nacos: server-addr: localhost:1111 group: "SEATA_GROUP" namespace: "" username: "nacos" password: "nacos"增加实体@Data @AllArgsConstructor @NoArgsConstructor public class Storage { private Long id; private Long productId; private Integer total; private Integer used; private Integer residue; }增加Mapperpublic interface StorageDao { int decrease(@Param("productId") Long productId, @Param("count") Integer count); }数据库操作<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="cc.lisen.springcloud.alibaba.seata.dao.StorageDao"> <resultMap id="BaseResultMap" type="cc.lisen.springcloud.alibaba.seata.domain.Storage"> <!--@Table t_storage--> <result column="id" jdbcType="BIGINT" property="id"/> <result column="product_id" jdbcType="BIGINT" property="productId"/> <result column="total" jdbcType="INTEGER" property="total"/> <result column="used" jdbcType="INTEGER" property="used"/> <result column="residue" jdbcType="INTEGER" property="residue"/> </resultMap> <sql id="Base_Column_List"> id, product_id, total, used, residue </sql> <update id="decrease"> update t_storage set used = used + #{count}, residue = residue - #{count} where product_id = #{productId} </update> </mapper>增加服务接口public interface IStorageService { int decrease(Long productId, Integer count); } 增加服务实现@Service @Slf4j public class StorageServiceImpl implements IStorageService { @Resource private StorageDao storageDao; @Override // @Transactional public int decrease(Long productId, Integer count) { log.info("--------->storage-------decrease获取xid:" + RootContext.getXID()); return storageDao.decrease(productId, count); } }增加接口controller@RestController @RequestMapping("/storage") @Slf4j public class StorageController { @Resource private IStorageService storageService; @PostMapping("/decrease") public CommonResult decrease(@RequestParam("productId") Long productId, @RequestParam("count") Integer count) { storageService.decrease(productId, count); return new CommonResult(200, "扣减库存成功"); } }增加DataSource,让Seata代理数据源@Configuration public class DataSourceProxyConfig { @Bean @ConfigurationProperties(prefix = "spring.datasource") public DataSource druidDataSource(){ return new DruidDataSource(); } }修改启动类取消数据源自动配置,让Seata能够代理数据源。@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) @EnableDiscoveryClient @EnableFeignClients @MapperScan(basePackages = {"cc.lisen.springcloud.alibaba.seata.dao"}) public class SeataStorageServiceMain2002 { public static void main(String[] args) { SpringApplication.run(SeataStorageServiceMain2002.class, args); } }创建账户微服务模块账户微服务用于提供扣减账户金额的微服务接口。增加依赖<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>Cloud2020</artifactId> <groupId>cc.lisen</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>seata-account-service2003</artifactId> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.5</version> </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> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> <exclusions> <exclusion> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> <version>1.4.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>cc.lisen</groupId> <artifactId>cloud-api-common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> </project>修改配置文件server: port: 2003 spring: application: name: seata-account-service cloud: nacos: discovery: server-addr: localhost:1111 loadbalancer: retry: enabled: false datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/seata_account?useSSL=false username: root password: root feign: hystrix: enabled: false mybatis: type-aliases-package: cc.lisen.springcloud.alibaba.seata.domain mapperLocations: classpath:/mapper/*.xml seata: tx-service-group: my_tx_group #与config.txt 中的service.vgroupMapping.my_tx_group=default一致 service: vgroup-mapping: my_tx_group: default #与config.txt 中的service.vgroupMapping.my_tx_group=default一致 config: type: nacos nacos: server-addr: localhost:1111 group: "SEATA_GROUP" namespace: "" username: "nacos" password: "nacos"增加实体@Data @AllArgsConstructor @NoArgsConstructor public class Account { private Long id; private Long userId; private Long total; private Long used; private Long residue; }增加Mapperpublic interface AccountDao { int decrease(@Param("userId") Long userId, @Param("money") BigDecimal money); }数据库操作<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="cc.lisen.springcloud.alibaba.seata.dao.AccountDao"> <resultMap id="BaseResultMap" type="cc.lisen.springcloud.alibaba.seata.domain.Account"> <!--@Table t_account--> <result column="id" jdbcType="BIGINT" property="id"/> <result column="user_id" jdbcType="BIGINT" property="userId"/> <result column="total" jdbcType="DECIMAL" property="total"/> <result column="used" jdbcType="DECIMAL" property="used"/> <result column="residue" jdbcType="DECIMAL" property="residue"/> </resultMap> <sql id="Base_Column_List"> id, user_id, total, used, residue </sql> <update id="decrease"> update t_account set used = used + #{money}, residue=residue - #{money} where user_id = #{userId} </update> </mapper>增加服务接口public interface IAccountService { int decrease( Long userId, BigDecimal money); }增加服务实现这里注意以下,我们模拟了报错信息@Service @Slf4j public class AccountServiceImpl implements IAccountService { @Resource private AccountDao accountDao; @Override // @Transactional public int decrease(Long userId, BigDecimal money) { log.info("--------->storage-------decrease获取xid:" + RootContext.getXID()); //模拟报错 int age = 10 / 0; return accountDao.decrease(userId, money); } }增加接口controller@RestController @Slf4j @RequestMapping("/account") public class AccountController { @Resource private IAccountService accountService; @PostMapping("/decrease") public CommonResult decrease(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money) { accountService.decrease(userId, money); return new CommonResult(200, "账户扣减成功"); } }增加DataSource,让Seata代理数据源@Configuration public class DataSourceProxyConfig { @Value("${mybatis.mapperLocations}") private String mapperLocations; @Bean @ConfigurationProperties(prefix = "spring.datasource") public DataSource druidDataSource(){ return new DruidDataSource(); } }修改启动类取消数据源自动配置,让Seata能够代理数据源。@EnableDiscoveryClient @EnableFeignClients @MapperScan(basePackages = {"cc.lisen.springcloud.alibaba.seata.dao"}) @SpringBootApplication(exclude = DataSourceAutoConfiguration.class) public class SeataAccountServiceMain2003 { public static void main(String[] args) { SpringApplication.run(SeataAccountServiceMain2003.class, args); } }测试通过postman测试可以看到系统抛出了异常,然后我们查看以下数据库,可以看到数据没有发生任何变化。
2021年07月26日
1,067 阅读
0 评论
1 点赞
2021-07-26
Seata基础教程上-seata 1.4.2的安装及基于Nacos的配置
先吐槽一下,seata的文档真的是乱的一批。我们这里只介绍安装及基本使用过程。1.seata介绍Seata 是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。具体原理可以参考官网http://seata.io/zh-cn/docs/overview/what-is-seata.html。{message type="success" content="一定要注意版本问题,包括安装的Nacos版本、Seata版本、Spring Cloud版本、Spring Boot版本等等。"/}2.前置条件2.1安装JDK并配置环境变量安装JDK1.8及以上版本,并正确配置环境变量。2.2maven我这里用的是3.6.3,并配置阿里云仓库。2.3Git由于需要将Seata Server的配置文件导入Nacos,所以需要安装Git2.4安装NacosNacos 2.0.2版本,关于Nacos的安装,我们稍后会出一个专门的文章进行介绍。温馨提示 所以学习本篇文章之前,您至少应该改知道Nacos的安装以及在Spring Cloud中如何利用Nacos进行服务的注册与发现。Nacos默认端口为8848,由于我这里配置了集群,所以前端端口使用的是1111,大家在下面配置文件注意以下。3.seata安装我这里使用的seata是1.4.2(截止到目前最新的版本)。3.1.修改配置文件3.1.1.修改file.conf进入Seata的conf目录,找到file.conf,如下图修改以下内容## transaction log store, only used in seata-server store { ## store mode: file、db、redis mode = "db" #我们用数据库存储,改成db ## rsa decryption public key publicKey = "" ## file store property file { ## store location dir dir = "sessionStore" # branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions maxBranchSessionSize = 16384 # globe session size , if exceeded throws exceptions maxGlobalSessionSize = 512 # file buffer size , if exceeded allocate new buffer fileWriteBufferCacheSize = 16384 # when recover batch read size sessionReloadReadSize = 100 # async, sync flushDiskMode = async } ## database store property db { ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc. datasource = "druid" ## mysql/oracle/postgresql/h2/oceanbase etc. dbType = "mysql" # 改成MySql driverClassName = "com.mysql.jdbc.Driver" ## if using mysql to store the data, recommend add rewriteBatchedStatements=true in jdbc connection param url = "jdbc:mysql://ip:3306/数据库名称?rewriteBatchedStatements=true" user = "数据库用户" password = "数据库密码" minConn = 5 maxConn = 100 globalTable = "global_table" branchTable = "branch_table" lockTable = "lock_table" queryLimit = 100 maxWait = 5000 } ## redis store property redis { ## redis mode: single、sentinel mode = "single" ## single mode property single { host = "127.0.0.1" port = "6379" } ## sentinel mode property sentinel { masterName = "" ## such as "10.28.235.65:26379,10.28.235.65:26380,10.28.235.65:26381" sentinelHosts = "" } password = "" database = "0" minConn = 1 maxConn = 10 maxTotal = 100 queryLimit = 100 } } 3.1.2.修改registry.conf进入Seata的conf目录,找到registry.conf,如下图我们这里用Nacos,所以主要是配置Nacos的信息,配置内容包括两部分,registry服务,将type从file改成nacos,将seata服务配置进nacos;config将type从file改成nacos,这样不需要每个项目都放file.conf修改内容如下:registry { # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa type = "nacos" #改成Nacos nacos { application = "seata-server" serverAddr = "127.0.0.1:1111" #Nacos地址,我这里集群用的1111端口,默认为8848端口 group = "SEATA_GROUP" #Nacos分组 namespace = "" username = "nacos" #Nacos用户名 password = "nacos" #Nacos密码 } eureka { serviceUrl = "http://localhost:8761/eureka" application = "default" weight = "1" } redis { serverAddr = "localhost:6379" db = 0 password = "" cluster = "default" timeout = 0 } zk { cluster = "default" serverAddr = "127.0.0.1:2181" sessionTimeout = 6000 connectTimeout = 2000 username = "" password = "" } consul { cluster = "default" serverAddr = "127.0.0.1:8500" aclToken = "" } 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 = "nacos" #改成Nacos nacos { serverAddr = "127.0.0.1:1111" #Nacos地址,我这里集群用的1111端口,默认为8848端口 namespace = "" group = "SEATA_GROUP" #Nacos分组 username = "nacos" #Nacos用户名 password = "nacos" #Nacos密码 # dataId = "" } consul { serverAddr = "127.0.0.1:8500" aclToken = "" } apollo { appId = "seata-server" ## apolloConfigService will cover apolloMeta apolloMeta = "http://192.168.1.204:8801" apolloConfigService = "http://192.168.1.204:8080" namespace = "application" apolloAccesskeySecret = "" cluster = "seata" } zk { serverAddr = "127.0.0.1:2181" sessionTimeout = 6000 connectTimeout = 2000 username = "" password = "" nodePath = "/seata/seata.properties" } etcd3 { serverAddr = "http://localhost:2379" } file { name = "file.conf" } } 3.1.3.导入Seata相应的配置项到Nacos的配置中心(非注册中心)由于我这里注册中心跟配置中心用的一个,所以我其实导入的就是一个Nacos。3.1.3.1.创建导入脚本在conf文件夹下,创建nacos-config.sh,并将以下内容粘贴进去#!/usr/bin/env bash # Copyright 1999-2019 Seata.io Group. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at、 # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. while getopts ":h:p:g:t:u:w:" opt do case $opt in h) host=$OPTARG ;; p) port=$OPTARG ;; g) group=$OPTARG ;; t) tenant=$OPTARG ;; u) username=$OPTARG ;; w) password=$OPTARG ;; ?) echo " USAGE OPTION: $0 [-h host] [-p port] [-g group] [-t tenant] [-u username] [-w password] " exit 1 ;; esac done urlencode() { for ((i=0; i < ${#1}; i++)) do char="${1:$i:1}" case $char in [a-zA-Z0-9.~_-]) printf $char ;; *) printf '%%%02X' "'$char" ;; esac done } if [[ -z ${host} ]]; then host=localhost fi if [[ -z ${port} ]]; then port=8848 fi if [[ -z ${group} ]]; then group="SEATA_GROUP" fi if [[ -z ${tenant} ]]; then tenant="" fi if [[ -z ${username} ]]; then username="" fi if [[ -z ${password} ]]; then password="" fi nacosAddr=$host:$port contentType="content-type:application/json;charset=UTF-8" echo "set nacosAddr=$nacosAddr" echo "set group=$group" failCount=0 tempLog=$(mktemp -u) function addConfig() { curl -X POST -H "${contentType}" "http://$nacosAddr/nacos/v1/cs/configs?dataId=$(urlencode $1)&group=$group&content=$(urlencode $2)&tenant=$tenant&username=$username&password=$password" >"${tempLog}" 2>/dev/null if [[ -z $(cat "${tempLog}") ]]; then echo " Please check the cluster status. " exit 1 fi if [[ $(cat "${tempLog}") =~ "true" ]]; then echo "Set $1=$2 successfully " else echo "Set $1=$2 failure " (( failCount++ )) fi } count=0 for line in $(cat $(dirname "$PWD")/config.txt | sed s/[[:space:]]//g); do (( count++ )) key=${line%%=*} value=${line#*=} addConfig "${key}" "${value}" done echo "=========================================================================" echo " Complete initialization parameters, total-count:$count , failure-count:$failCount " echo "=========================================================================" if [[ ${failCount} -eq 0 ]]; then echo " Init nacos config finished, please start seata-server. " else echo " init nacos config fail. " fi3.1.3.2.创建配置文件在conf的上级目录,创建config.txt并粘贴以下内容transport.type=TCP transport.server=NIO transport.heartbeat=true transport.enableClientBatchSendRequest=true 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 service.vgroupMapping.my_tx_group=default #自定义,一定注意与spring cloud配置文件保持一致 service.default.grouplist=127.0.0.1:8091 #seata服务 service.enableDegrade=false service.disableGlobalTransaction=false 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=false client.rm.tableMetaCheckerInterval=60000 client.rm.sqlParserType=druid client.rm.reportSuccessEnable=false client.rm.sagaBranchRegisterEnable=false 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 store.mode=db store.lock.mode=db store.session.mode=db store.publicKey= 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 store.db.datasource=druid store.db.dbType=mysql store.db.driverClassName=com.mysql.jdbc.Driver store.db.url=jdbc:mysql://数据库IP:3306/seata?useUnicode=true&rewriteBatchedStatements=true store.db.user=数据库用户名 store.db.password=数据库密码 store.db.minConn=5 store.db.maxConn=30 store.db.globalTable=global_table store.db.branchTable=branch_table store.db.queryLimit=100 store.db.lockTable=lock_table store.db.maxWait=5000 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 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 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 log.exceptionRate=100 transport.serialization=seata transport.compressor=none metrics.enabled=false metrics.registryType=compact metrics.exporterList=prometheus metrics.exporterPrometheusPort=9898上图一定要注意配置数据库信息及service.vgroupMapping.my_tx_group=default,这个后续在spring cloud项目的配置文件中会用到。3.1.3.3.导入配置文件打开git bash,进入到conf目录中,执行以下命令sh [nacos-config.sh文件路径] -h [nacos-ip地址] -p 8848 -g SEATA_GROUP[导入的组] -u [用户名] -w [密码] 执行完成后,查看我们Nacos配置中心中,配置文件是否已存在。3.1.4.创建相关的数据库(在回滚或提交前会将日志保存在数据库中,成功后会删除)3.1.3.1.创建seata数据库,并预置数据这个数据库就是我们上面配置文件中使用到的数据库,脚本如下-- -------------------------------- The script used when storeMode is 'db' -------------------------------- -- the table to store GlobalSession data CREATE TABLE IF NOT EXISTS `global_table` ( `xid` VARCHAR(128) NOT NULL, `transaction_id` BIGINT, `status` TINYINT NOT NULL, `application_id` VARCHAR(32), `transaction_service_group` VARCHAR(32), `transaction_name` VARCHAR(128), `timeout` INT, `begin_time` BIGINT, `application_data` VARCHAR(2000), `gmt_create` DATETIME, `gmt_modified` DATETIME, PRIMARY KEY (`xid`), KEY `idx_gmt_modified_status` (`gmt_modified`, `status`), KEY `idx_transaction_id` (`transaction_id`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8; -- the table to store BranchSession data CREATE TABLE IF NOT EXISTS `branch_table` ( `branch_id` BIGINT NOT NULL, `xid` VARCHAR(128) NOT NULL, `transaction_id` BIGINT, `resource_group_id` VARCHAR(32), `resource_id` VARCHAR(256), `branch_type` VARCHAR(8), `status` TINYINT, `client_id` VARCHAR(64), `application_data` VARCHAR(2000), `gmt_create` DATETIME(6), `gmt_modified` DATETIME(6), PRIMARY KEY (`branch_id`), KEY `idx_xid` (`xid`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8; -- the table to store lock data CREATE TABLE IF NOT EXISTS `lock_table` ( `row_key` VARCHAR(128) NOT NULL, `xid` VARCHAR(128), `transaction_id` BIGINT, `branch_id` BIGINT NOT NULL, `resource_id` VARCHAR(256), `table_name` VARCHAR(32), `pk` VARCHAR(36), `gmt_create` DATETIME, `gmt_modified` DATETIME, PRIMARY KEY (`row_key`), KEY `idx_branch_id` (`branch_id`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8;3.1.4.2.创建undo_log表注意:在业务数据库中创建undo_log,与上面的脚本不在一个数据库中。比如我有三个微服务,三个数据库,那么就需要在三个业务数据库中创建undo_log表-- 在业务数据库中创建undo_log(与上面的脚本不在一个数据库中) CREATE TABLE IF NOT EXISTS `undo_log` ( `branch_id` BIGINT NOT NULL COMMENT 'branch transaction id', `xid` VARCHAR(128) NOT NULL COMMENT 'global transaction id', `context` VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization', `rollback_info` LONGBLOB NOT NULL COMMENT 'rollback info', `log_status` INT(11) NOT NULL COMMENT '0:normal status,1:defense status', `log_created` DATETIME(6) NOT NULL COMMENT 'create datetime', `log_modified` DATETIME(6) NOT NULL COMMENT 'modify datetime', UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`) ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';4.运行seata运行Nacos这里不多介绍。运行seata进入seata的bin目录,执行./seata-server.bat,运行成功后,查看Nacos的服务列表能看到我们配置的seata即代表成功。
2021年07月26日
1,251 阅读
0 评论
0 点赞
2021-07-22
Spring Data Jpa Listener事件的扩展之自定义EntityListener
随着DDD的设计模式逐渐被大家认可和热捧。JPA通过Listener这种机制可以很好地实现事件分离、状体分离。假如,订单的状态变化。可能对我们来说比较重要,我们需要定一个类去监听订单状态变更,通知相应的逻辑代码各自去干各自的活。新增一个UserAuditListener类,在相应的操作上添加Callbacks注解@Slf4j public class UserAuditListener { @PostPersist private void postPersist(SysUser sysUser) { recordLog(sysUser, OperateType.CREATE); } @PostRemove private void postRemove(SysUser sysUser) { recordLog(sysUser, OperateType.REMOVE); } @PostUpdate private void postUpdate(SysUser sysUser) { recordLog(sysUser, OperateType.UPDATE); } @PostLoad public void postLoad(SysUser sysUser) { recordLog(sysUser, OperateType.LOAD); } /** * 记录审计日志 * * @param sysUser 用户实体 * @param operateType 操作类型 */ private void recordLog(SysUser sysUser, OperateType operateType) { log.info("{}执行了{}操作", sysUser, operateType.getType()); } } enum OperateType { CREATE("创建"), UPDATE("更新"), REMOVE("删除"), LOAD("查询"); private final String type; OperateType(String type) { this.type = type; } public String getType() { return this.type; } }修改实体,增加@EntityListeners注解@Entity @Table(name = "sys_user") @Data @Slf4j @EntityListeners({UserAuditListener.class}) public class SysUser extends AbstractAuditable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long userId; @JoinColumn(name = "dept_id", referencedColumnName = "dept_id") @ManyToOne(cascade = CascadeType.ALL) private SysDept sysDept; private String userName; private String nickName; private String userType; private String email; @Column(name = "phonenumber") private String phoneNumber; private String sex; private String avatar; @JsonIgnore private String password; @Enumerated(EnumType.STRING) private Status status; private String delFlag; private String loginIp; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private java.sql.Timestamp loginDate; private String remark; private Long recycleCompanyId; private Long medicalInstitutionId; private Long ethnicity; @ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = "sys_user_role", joinColumns = {@JoinColumn(name = "user_id")}, inverseJoinColumns = {@JoinColumn(name = "role_id")}) private Set<SysRole> sysRoles; }测试这样,我们在增删改查方法调用时,就会执行我们对应的callback注解的方法比如我们插入用户@GetMapping("/save") public Long save(){ SysUser sysUser = new SysUser(); sysUser.setNickName("测试123321"); sysUser.setUserName("测试123321"); sysUser.setSex("0"); sysUserRepository.save(sysUser); return sysUser.getUserId(); }保存后,会自动调用postPersist方法
2021年07月22日
1,358 阅读
0 评论
1 点赞
2021-07-22
Spring Data Jpa扩展之Auditing
Auditing翻译过来是审计和审核。Spring的优秀之处在于帮我们想到了很多我们平时烦琐事情的解决方案,我们在实际的业务系统中,针对一张表的操作大部分是需要记录谁什么时间创建的,谁什么时间修改的,并且能让我们方便地记录操作日志。Spring Data JPA为我们提供了审计功能的架构实现,提供了4个注解专门解决这件事:@CreatedBy:创建人。@CreatedDate:创建时间。@LastModifiedBy:最后修改人。@LastModifiedDate:最后修改时间。增加公共虚拟类一般情况下,创建人、创建时间、最后修改人、最后修改时间四个字段都是统一的。所以,我们可以将其提取到公共的类中,然后所有的实体类继承这个虚拟类。@EntityListeners(AuditingEntityListener.class) @MappedSuperclass public abstract class AbstractAuditable { @CreatedBy private String createBy; @CreatedDate @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private java.sql.Timestamp createTime; @LastModifiedBy private String updateBy; @LastModifiedDate @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private java.sql.Timestamp updateTime; }必须添加@MappedSuperclass注解"修改实体,继承AbstractAuditable@Entity @Table(name = "sys_user") @Data @Slf4j public class SysUser extends AbstractAuditable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long userId; @JoinColumn(name = "dept_id", referencedColumnName = "dept_id") @ManyToOne(cascade = CascadeType.ALL) private SysDept sysDept; private String userName; private String nickName; private String userType; private String email; @Column(name = "phonenumber") private String phoneNumber; private String sex; private String avatar; @JsonIgnore private String password; @Enumerated(EnumType.STRING) private Status status; private String delFlag; private String loginIp; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private java.sql.Timestamp loginDate; private String remark; private Long recycleCompanyId; private Long medicalInstitutionId; private Long ethnicity; @ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = "sys_user_role", joinColumns = {@JoinColumn(name = "user_id")}, inverseJoinColumns = {@JoinColumn(name = "role_id")}) private Set<SysRole> sysRoles; }实现AbstractAuditable接口实现AbstractAuditable接口,提供创建者、最后修改者信息,这里我们随便模拟了以下常量。public class MyAuditorAware implements AuditorAware<String> { /** * Returns the current auditor of the application. * * @return the current auditor. */ @Override public Optional<String> getCurrentAuditor() { return Optional.of("测试用户"); } }开启Auditing功能通过@EnableJpaAuditing注解开启JPA的Auditing功能,并且告诉应用AuditorAware的实现类是谁。@SpringBootApplication @EnableJpaRepositories(queryLookupStrategy = QueryLookupStrategy.Key.CREATE_IF_NOT_FOUND) @EnableJpaAuditing public class Example1Application { public static void main(String[] args) { SpringApplication.run(Example1Application.class, args); } @Bean public AuditorAware<String> auditorAware(){ return new MyAuditorAware(); } }测试 @GetMapping("/save") public Long save(){ SysUser sysUser = new SysUser(); sysUser.setNickName("测试123321"); sysUser.setUserName("测试123321"); sysUser.setSex("0"); sysUserRepository.save(sysUser); return sysUser.getUserId(); }
2021年07月22日
1,273 阅读
0 评论
0 点赞
2021-07-22
Spring Data Jpa QueryByExampleExecutor及JpaSpecificationExecutor的用法
QueryByExampleExecutor可以通过实体进行简单的查询。比如我们查询用于昵称是超级开头的用户。@RestController @RequestMapping("/user") public class UserController { @Resource private SysUserRepository sysUserRepository; @GetMapping("/findAll") @Transactional public List<SysUser> findAll() { SysUser sysUser = new SysUser(); sysUser.setNickName("超级"); ExampleMatcher exampleMatcher = ExampleMatcher.matching() .withMatcher("nickName", ExampleMatcher.GenericPropertyMatchers.startsWith()) .withIgnorePaths("focus"); Example<SysUser> sysUserExample = Example.of(sysUser, exampleMatcher); return sysUserRepository.findAll(sysUserExample); } }其实跟MyBatis-Plus类似,功能比较简单,只能拼接and查询,一般使用不是很多。JpaSpecificationExecutor使用Specification的要点就是CriteriaBuilder,通过这个对象来创建条件,之后返回一个Predicate对象。这个对象中就有了相应的查询需求,我们同样可以定义多个Specification,之后通过Specifications对象将其连接起来。修改接口public interface SysUserRepository extends JpaRepository<SysUser, Long>, JpaSpecificationExecutor<SysUser> { }测试@RestController @RequestMapping("/user") public class UserController { @Resource private SysUserRepository sysUserRepository; @GetMapping("/findAll") @Transactional public List<SysUser> findAll() { return sysUserRepository.findAll((root, criteriaQuery, criteriaBuilder) -> { Predicate p1 = criteriaBuilder.like(root.get("nickName"), "%超级%"); Predicate p2 = criteriaBuilder.equal(root.get("userName"), "admin"); Predicate p3 = criteriaBuilder.equal(root.get("email"),"ry@163.com"); return criteriaBuilder.and(p1, p2); }); } }
2021年07月22日
1,245 阅读
0 评论
0 点赞
2021-07-21
Spring Data Jpa多表关联查询
在权限表中,我们一般会设计用户表、部门表、角色表,一般情况下,一个用户只能属于一个部门,但是一个用户能拥有多个角色,一个角色也可能对应多个部门。Spring Data Jpa中提供了多个注解,用于处理表之间的关联管理。常见注解@OneToOne@OneToOne代表一对一的关联关系,需要配合@JoinColumn一起使用。注意:可以双向关联,也可以只配置一方,需要视实际需求而定。@OneToOne注释五个属性:targetEntity、cascade、fetch、optional和mappedByfetch属性默认值是FetchType.EAGER。optional = true设置属性可以为null;targetEntity属性:Class类型的属性。定义关系类的类型,默认是该成员属性对应的类类型,所以通常不需要提供定义;cascade属性:CascadeType[]类型。该属性定义类和类之间的级联关系。定义的级联关系将被容器视为对当前类对象及其关联类对象采取相同的操作,而且这种关系是递归调用的。cascade的值只能从CascadeType.PERSIST(级联新建)、CascadeType.REMOVE(级联删除)、CascadeType.REFRESH(级联刷新)、CascadeType.MERGE(级联更新)中选择一个或多个。还有一个选择是使用CascadeType.ALL,表示选择全部四项。@OneToMany、@ManyToOne@OneToMany、@ManyToOne代表一对多和多对一的关系,需要配合@JoinColumn一起使用。比如我们下面例子的用户与部门表,一个用户只属于一个部门,一个部门可以包含多个用户。@ManyToMany@ManyToMany代表多对多的关系。例如我们用于与角色的关联表,一般我们会设计一个用户、角色关联表的对应关系,配合@JoinTable一起使用。示例创建表部门表create table sys_dept ( dept_id bigint auto_increment comment '部门id' primary key, parent_id bigint default 0 null comment '父部门id', ancestors varchar(50) default '' null comment '祖级列表', dept_name varchar(30) default '' null comment '部门名称', order_num int default 0 null comment '显示顺序', leader varchar(20) null comment '负责人', phone varchar(11) null comment '联系电话', email varchar(50) null comment '邮箱', status char default '0' null comment '部门状态(0正常 1停用)', del_flag char default '0' null comment '删除标志(0代表存在 2代表删除)', create_by varchar(64) default '' null comment '创建者', create_time datetime null comment '创建时间', update_by varchar(64) default '' null comment '更新者', update_time datetime null comment '更新时间' ) comment '部门表';用户表create table sys_user ( user_id bigint auto_increment comment '用户ID' primary key, dept_id bigint null comment '部门ID', user_name varchar(30) not null comment '用户账号', nick_name varchar(30) not null comment '用户昵称', user_type varchar(2) default '00' null comment '用户类型(00系统用户)', email varchar(50) default '' null comment '用户邮箱', phonenumber varchar(11) default '' null comment '手机号码', sex char default '0' null comment '用户性别(0男 1女 2未知)', avatar varchar(100) default '' null comment '头像地址', password varchar(100) default '' null comment '密码', status varchar(5) default 'start' null comment '帐号状态(0正常 1停用)', del_flag char default '0' null comment '删除标志(0代表存在 2代表删除)', login_ip varchar(50) default '' null comment '最后登录IP', login_date datetime null comment '最后登录时间', create_by varchar(64) default '' null comment '创建者', create_time datetime null comment '创建时间', update_by varchar(64) default '' null comment '更新者', update_time datetime null comment '更新时间', remark longtext null comment '备注', recycle_company_id bigint null comment '所属回收单位', medical_institution_id bigint null comment '所属医疗机构', ethnicity bigint null comment '民族' ) comment '用户信息表';角色表create table sys_role ( role_id bigint auto_increment comment '角色ID' primary key, role_name varchar(30) not null comment '角色名称', role_key varchar(100) not null comment '角色权限字符串', role_sort int not null comment '显示顺序', data_scope char default '1' null comment '数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)', menu_check_strictly tinyint(1) default 1 null comment '菜单树选择项是否关联显示', dept_check_strictly tinyint(1) default 1 null comment '部门树选择项是否关联显示', status char not null comment '角色状态(0正常 1停用)', del_flag char default '0' null comment '删除标志(0代表存在 2代表删除)', create_by varchar(64) default '' null comment '创建者', create_time datetime null comment '创建时间', update_by varchar(64) default '' null comment '更新者', update_time datetime null comment '更新时间', remark varchar(500) null comment '备注' ) comment '角色信息表';用户角色关联表create table sys_user_role ( user_id bigint not null comment '用户ID', role_id bigint not null comment '角色ID', primary key (user_id, role_id) ) comment '用户和角色关联表';创建实体用户实体@Entity @Table(name = "sys_user") @Data @Slf4j public class SysUser { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long userId; @JoinColumn(name = "dept_id", referencedColumnName = "dept_id") @ManyToOne(cascade = CascadeType.ALL) private SysDept sysDept; private String userName; private String nickName; private String userType; private String email; @Column(name = "phonenumber") private String phoneNumber; private String sex; private String avatar; @JsonIgnore private String password; @Enumerated(EnumType.STRING) private Status status; private String delFlag; private String loginIp; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private java.sql.Timestamp loginDate; private String createBy; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private java.sql.Timestamp createTime; private String updateBy; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private java.sql.Timestamp updateTime; private String remark; private Long recycleCompanyId; private Long medicalInstitutionId; private Long ethnicity; @ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = "sys_user_role", joinColumns = {@JoinColumn(name = "user_id")}, inverseJoinColumns = {@JoinColumn(name = "role_id")}) private Set<SysRole> sysRoles; } 部门实体@Entity @Table(name = "sys_dept") @Getter @Setter @ToString @RequiredArgsConstructor public class SysDept { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "dept_id") private long deptId; private long parentId; private String ancestors; private String deptName; private long orderNum; private String leader; private String phone; private String email; private String status; private String delFlag; private String createBy; private java.sql.Timestamp createTime; private String updateBy; private java.sql.Timestamp updateTime; @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) return false; SysDept sysDept = (SysDept) o; return Objects.equals(deptId, sysDept.deptId); } @Override public int hashCode() { return 866095534; } } 角色实体@Entity @Data public class SysRole { @Id @GeneratedValue(strategy = GenerationType.AUTO) private long roleId; private String roleName; private String roleKey; private long roleSort; private String dataScope; private long menuCheckStrictly; private long deptCheckStrictly; private String status; private String delFlag; private String createBy; private java.sql.Timestamp createTime; private String updateBy; private java.sql.Timestamp updateTime; private String remark; } 用户角色关联实体@Entity @IdClass(SysUserRole.class) @Data public class SysUserRole implements Serializable { @Id @Column(name = "user_id") private long userId; @Id @Column(name = "role_id") private long roleId; }Jpa查询接口这个接口不是必须的,可以调用任意的Crud接口。public interface SysUserRepository extends JpaRepository<SysUser, Long> { /** * 根据用户名称查找列表 * * @param userName 用户名称 * @return 列表 */ List<SysUser> findByUserName(String userName); // @Query("select u from SysUser u") Stream<SysUser> readAllBy(); Stream<SysUser> streamAllBy(); @Async Future<List<SysUser>> findAllBy(); @Query("select u from SysUser u where u.userName = :userName and u.email = :email") SysUser findByUserNameAndEmail(@Param("userName") String userName, @Param("email") String email); @Query("select u from SysUser u where u.nickName like %:nickName%") List<SysUser> findAllByNickName(@Param("nickName") String nickName); @Query(value = "select * from sys_user u where email like concat('%',:email,'%')", nativeQuery = true) List<SysUser> findAllByEmailLike(@Param("email") String email); @Query(value = "select u from SysUser u") Page<SysUser> findAllBy(Pageable pageable); SysUser findFirstByUserId(Long userId); @Modifying(clearAutomatically = true) @Query(value = "update SysUser set remark = '234' where userId = :userId") int setRemarkByUserId(@Param("userId") Long userId); }测试@RestController @RequestMapping("/user") public class UserController { @Resource private SysUserRepository sysUserRepository; @GetMapping("/findAll") @Transactional public SysUser findAll() { return sysUserRepository.findFirstByUserId(111L); } }
2021年07月21日
1,382 阅读
0 评论
0 点赞
2021-07-21
Spring Data Jpa使用枚举
有时候在数据库设计的时候,我们可能会使用一些固定的值,比如启用、停用,只有这两个值,我们设计数据库可能设计成stop、start。这个时候,我们在Jpa中设计实体时,一般设置成枚举就更合理了。创建枚举public enum Status { stop("停用"), start("启用"); private String value; Status(String value) { this.value = value; } }实体@Entity @Table(name = "sys_user") @Data @Slf4j public class SysUser { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long userId; private Long deptId; private String userName; private String nickName; private String userType; private String email; @Column(name = "phonenumber") private String phoneNumber; private String sex; private String avatar; @JsonIgnore private String password; @Enumerated(EnumType.STRING) private Status status; private String delFlag; private String loginIp; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private java.sql.Timestamp loginDate; private String createBy; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private java.sql.Timestamp createTime; private String updateBy; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private java.sql.Timestamp updateTime; private String remark; private Long recycleCompanyId; private Long medicalInstitutionId; private Long ethnicity; } 查询我们可以查看一些查询的结果
2021年07月21日
1,023 阅读
0 评论
0 点赞
2021-07-21
Spring Data Jpa流查询
Spring Data Jpa可以通过使用Java 8 Stream作为返回类型,来逐步处理查询方法的结果,而不是简单的将查询结果包装在Stream数据存储中。接口定义public interface SysUserRepository extends JpaRepository<SysUser, Long> { /** * 根据用户名称查找列表 * * @param userName 用户名称 * @return 列表 */ List<SysUser> findByUserName(String userName); // @Query("select u from SysUser u") Stream<SysUser> readAllBy(); Stream<SysUser> streamAllBy(); }使用@RestController @RequestMapping("/user") public class UserController { @Resource private SysUserRepository sysUserRepository; @GetMapping("/findAll") @Transactional public List<SysUser> findAll() { try (Stream<SysUser> sysUserStream = sysUserRepository.streamAllBy()) { return sysUserStream.collect(Collectors.toList()); } catch (Exception ex) { ex.printStackTrace(); return new ArrayList<>(); } } }注意事项必须通过try catch关闭流方法必须有@Transactional注解
2021年07月21日
1,664 阅读
0 评论
0 点赞
2021-07-20
docker部署ActiveMQ并整合到Spring Boot
ActiveMQ是由Apache基金会采用Java语言开发的一个开源的消息中间件,完美的遵循JMS规范。ActiveMQ易于实现高级场景,而且只需要付出低消耗。被誉为消息中间件的“瑞士军刀”。常用来处理高并发请求的流量削峰、事务处理。本文,我们通过docker部署ActiveMQ,然后结合ActiveMQ的queue及topic模式,分别介绍在spring boot中的应用。通过docker安装ActiveMQ搜索可用的ActiveMQdocker search activemq我这里下载评星最高的webcenter/activemq下载docker镜像docker pull webcenter/activemq运行dockerdocker run -d --name activemq -p 61616:61616 -p 8161:8161 webcenter/activemq检查是否运行成功打开http://139.198.172.114:8161/,出现以下页面可以点击Manage ActiveMQ broker,默认用户名、密码都是adminSpring Boot整合ActiveMQActiveMQ在实际企业开发中主要有两种模式:点对点模式(Queue)和发布订阅模式(Topic)Queue模式Queue模式即队列(先进先出),消息提供者生产消息发布到Queue中,然后消费者从Queue中取出,并消费消息。这里需要注意的是,消息被消费者消费之后,Queue不再存储,所以消息只能被消费一次。Queue支持存在多个消息消费者,但是对一个消息而言,只会有一个消费者可以消费。创建Spring Boot项目并添加依赖<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-activemq</artifactId> </dependency> <dependency> <groupId>org.apache.activemq</groupId> <artifactId>activemq-pool</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>定义常量Constants.javapublic class Constants { public final static String QUEUEMESSAGE = "queue"; }添加配置类ActiveMQConfig.java@Configuration public class ActiveMQConfig { @Bean public Queue queue(){ return new ActiveMQQueue(Constants.QUEUEMESSAGE); } }创建消息提供者@RestController public class ProducerController { @Resource private JmsMessagingTemplate jmsMessagingTemplate; @Resource private Queue queue; @RequestMapping("/sendQueueMessage/{msg}") public String sendQueueMessage(@PathVariable("msg") String msg) { this.jmsMessagingTemplate.convertAndSend(queue, msg); return msg; } }配置ActiveMQ修改项目配置文件,配置ActiveMQspring.activemq.broker-url=tcp://139.198.172.114:61616 #接收所有消息 spring.activemq.packages.trust-all=true创建消息消费者Consumer.java@Component @Slf4j public class Consumer { @JmsListener(destination = Constants.QUEUEMESSAGE) public void receiveQueueMessage1(String message) { if (null != message) { log.info("收到queue报文1:" + message); } } @JmsListener(destination = Constants.QUEUEMESSAGE) public void receiveQueueMessage2(String message) { if (null != message) { log.info("收到queue报文2:" + message); } } }测试我们访问http://localhost:8083/sendQueueMessage/123,可以查看控制台输出信息可以看到,虽然我们提供了两个消费者,但是实际上只有一个消费者消费成功。Topic模式消息提供者将消息发布到Topic中,同时有多个消费者(订阅者)消费该消息,与Queue模式不同,发布到Topic消息会被所有的订阅者消费。Topic模式与Queue模式类似,我们这里基于Queue模式的代码进行修改。修改配置类ActiveMQConfig.java增加Topic的Bean,修改后代码如下@Configuration public class ActiveMQConfig { @Bean public Queue queue(){ return new ActiveMQQueue(Constants.QUEUEMESSAGE); } @Bean public Topic topic(){ return new ActiveMQTopic(Constants.TOPICMESSAGE); } }修改消息消费者@RestController public class ProducerController { @Resource private JmsMessagingTemplate jmsMessagingTemplate; @Resource private Queue queue; @Resource private Topic topic; @RequestMapping("/sendQueueMessage/{msg}") public String sendQueueMessage(@PathVariable("msg") String msg) { this.jmsMessagingTemplate.convertAndSend(queue, msg); return msg; } @RequestMapping("/sendTopicMessage/{msg}") public String sendTopicMessage(@PathVariable("msg") String msg) { this.jmsMessagingTemplate.convertAndSend(topic, msg); return msg; } }修改消费者@Component @Slf4j public class Consumer { @JmsListener(destination = Constants.QUEUEMESSAGE) public void receiveQueueMessage1(String message) { if (null != message) { log.info("收到queue报文1:" + message); } } @JmsListener(destination = Constants.QUEUEMESSAGE) public void receiveQueueMessage2(String message) { if (null != message) { log.info("收到queue报文2:" + message); } } @JmsListener(destination = Constants.TOPICMESSAGE) public void receiveTopicMessage1(String message) { if (null != message) { log.info("收到topic报文1:" + message); } } @JmsListener(destination = Constants.TOPICMESSAGE) public void receiveTopicMessage2(String message) { if (null != message) { log.info("收到topic报文2:" + message); } } }修改项目配置文件通过spring.jms.pub-sub-domain=true配置启用Topic。server.port=8083 spring.activemq.broker-url=tcp://139.198.172.114:61616 #接收所有消息 spring.activemq.packages.trust-all=true #配置使用topic spring.jms.pub-sub-domain=true测试访问http://localhost:8083/sendTopicMessage/123,查看控制台。可以看到两个订阅者都输出了消息。ActiveMQ监控我们通过http://139.198.172.114:8161/admin/topics.jsp,也可以查看服务器的信息
2021年07月20日
870 阅读
0 评论
0 点赞
2021-07-20
使用docker部署dubbo/dubbox并实现服务提供者、消费者
实现目标基于centos 7,通过docker打包部署dubbox(2.8.4),并实现简单的服务提供者与消费者。前置条件服务器安装JDK1.8并正确配置环境变量开发环境安装JDK1.8并正确配置环境变量开发环境安装maven并正确配置环境变量(建议使用阿里云仓库)安装zookeeperZooKeeper 是 Apache 软件基金会的一个软件项目,它为大型分布式计算提供开源的分布式配置服务、同步服务和命名注册。下载压缩包下载压缩包curl -O https://www.apache.org/dyn/closer.lua/zookeeper/zookeeper-3.7.0/apache-zookeeper-3.7.0-bin.tar.gz解压压缩包tar -zxvf apache-zookeeper-3.7.0-bin.tar.gz修改zookeeper配置文件进入zookeeper配置文件所在目录cd /home/apache-zookeeper-3.7.0-bin/conf修改配置文件cp zoo_sample.cfg zoo.cfg运行zookeepercd /home/apache-zookeeper-3.7.0-bin/bin ./zkServer.sh start检查zookeeper运行状态cd /home/apache-zookeeper-3.7.0-bin/bin ./zkServer.sh statusdubbox打包Dubbox 是一个分布式服务框架,其前身是阿里巴巴开源项目Dubbo ,被国内电商及互联网项目中使用,后期阿里巴巴停止了该项目的维护,当当网便在Dubbo基础上进行优化,并继续维护,为了与原有的Dubbo区分,故将其命名为Dubbox。Dubbox 致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。简单的说,dubbox就是个服务框架,如果没有分布式的需求,其实是不需要用的,只有在分布式的时候,才有dubbox这样的分布式服务框架的需求,并且本质上是个服务调用的东东,说白了就是个远程服务调用的分布式框架。dubbox没有提供打包好的jar包或者war包,我们需要自己在github下载源码并编译。下载dubbox源码dubbox代码下载地址:https://github.com/dangdangdotcom/dubbox编译代码进入dubbox代码文件见,执行打包命令mvn install -D maven.test.skip=true打包完成后,在dubbo-admin\target中能看到一个dubbo-admin-2.8.4.war修改dubbox配置文件我们用压缩软件打开dubbo-admin-2.8.4.war,打开WEB-INF\dubbo.properties,修改zookeeper地址及对应用户密码,如下:dubbo.registry.address=zookeeper://IP地址:2181 dubbo.admin.root.password=root dubbo.admin.guest.password=guest注意修改成自己zookeeper对应的ip地址 war包修改完成后备用。安装docker我们通过docker部署dubbox。更新yumyum -y update安装依赖 yum install -y yum-utils device-mapper-persistent-data lvm2安装dockeryum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo yum install docker-ce-18.06.0.ce-3.el7启动docker并配置开机启动systemctl start docker systemctl enable docker配置docker为国内源vi /etc/docker/daemon.json输入以下内容{ "registry-mirrors": ["http://hub-mirror.c.163.com", "https://docker.mirrors.ustc.edu.cn"] }通过docker部署dubbox我们这里自己打包一个docker,并部署dubbox创建文件夹创建一个文件夹/home/makedocker,用于存储Dockerfile以及war包。mkdir /home/makedocker cd /home/makedocker将war包上传到/home/makedocker制作Dockerfilevi Dockerfile并输入以下内容FROM registry.cn-hangzhou.aliyuncs.com/shuodao/tomcat-8.5.27 MAINTAINER Laughing COPY dubbo.war /usr/tomcat/webapps/ EXPOSE 8080/tcp打包dockerdocker build -t tomcat-dubbo .启动dockerdocker run -d -p 8888:8080 --name tomcat-dubbo tomcat-dubbo查看docker运行状态docker ps显示以下内容,代表docker运行成功查看dubbox运行状态打开http://139.198.172.114:8888/dubbo,系统要求输入密码,密码为dubbo.properties中配置的密码,输入完成后,正常打开界面如下实现dubbox服务我们创建一个用户服务,作为服务提供者,判断用户是否登录。一个商品服务,作为服务消费者,调用用户是否登录的接口。实现通用服务接口创建一个maven项目,提供基本的接口定义。pom.xml内容如下<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>cc.lisen</groupId> <artifactId>dubbo-common</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> </project>创建接口package cc.lisen.dubbo.common.service; /** * 博客:https://lisen.cc * Description: * * @Author: 李森的博客 * DateTime: 2021-07-19 20:20 */ public interface RpcUserService { boolean checkUserLogin(String userName); }将代码部署到本地maven仓库由于我们没有远程maven仓库,所以我们将jar包install到本地仓库。在代码根目录,执行以下命令,打包到本地仓库。mvn install -D maven.test.skip=true实现服务提供者创建服务提供者。pom.xml如下<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.2</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>cc.lisen</groupId> <artifactId>dubbo-provider</artifactId> <version>0.0.1-SNAPSHOT</version> <name>dubbo-provider</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>2.8.4</version> </dependency> <dependency> <groupId>io.dubbo.springboot</groupId> <artifactId>spring-boot-starter-dubbo</artifactId> <version>1.0.0</version> </dependency> <dependency> <groupId>cc.lisen</groupId> <artifactId>dubbo-common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <includeSystemScope>true</includeSystemScope> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>实现接口package cc.lisen.dubboprovider.service; import cc.lisen.dubbo.common.service.RpcUserService; import org.springframework.stereotype.Service; /** * 博客:https://lisen.cc * Description: * * @Author: 李森的博客 * DateTime: 2021-07-19 19:56 */ @Service @com.alibaba.dubbo.config.annotation.Service(interfaceClass = RpcUserService.class) public class RpcUserServiceImpl implements RpcUserService{ @Override public boolean checkUserLogin(String userName){ return "admin".equals(userName); } } 修改配置文件配置dubboserver.port=8081 #dubbo提供者的别名,只是个标识 spring.dubbo.application.name=dubbo-provider #zookeeper地址 spring.dubbo.registry.address=zookeeper://139.198.172.114:2181 #dubbo协议 spring.dubbo.protocol.name=dubbo #dubbo端口号 spring.dubbo.protocol.port=20880 #这是你要发布到dubbo的接口所在包位置 spring.dubbo.scan=cc.lisen.dubboprovider.service测试运行项目,然后我们打开dubbo服务端,查看是否注册成功。实现服务消费者创建服务消费者。pom.xml如下<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.2</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>cc.lisen</groupId> <artifactId>dubbo-provider</artifactId> <version>0.0.1-SNAPSHOT</version> <name>dubbo-provider</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>2.8.4</version> </dependency> <dependency> <groupId>io.dubbo.springboot</groupId> <artifactId>spring-boot-starter-dubbo</artifactId> <version>1.0.0</version> </dependency> <dependency> <groupId>cc.lisen</groupId> <artifactId>dubbo-common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <includeSystemScope>true</includeSystemScope> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project> 创建商品服务package cc.lisen.dubboprovider.service; /** * 博客:https://lisen.cc * Description: * * @Author: 李森的博客 * DateTime: 2021-07-19 20:36 */ public interface GoodService { boolean checkUserLogin(String userName); }创建商品服务类,调用用户登录判断接口package cc.lisen.dubboprovider.service.impl; import com.alibaba.dubbo.config.annotation.Reference; import cc.lisen.dubbo.common.service.RpcUserService; import cc.lisen.dubboprovider.service.GoodService; import org.springframework.stereotype.Service; /** * 博客:https://lisen.cc * Description: * * @Author: 李森的博客 * DateTime: 2021-07-19 19:56 */ @Service public class GoodServiceImpl implements GoodService { @Reference private RpcUserService rpcUserService; @Override public boolean checkUserLogin(String userName) { return rpcUserService.checkUserLogin(userName); } } 测试消费者服务是否正常注册测试创建商品销售接口package cc.lisen.dubboprovider.controller; import cc.lisen.dubboprovider.service.GoodService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; /** * 博客:https://lisen.cc * Description: * * @Author: 李森的博客 * DateTime: 2021-07-19 20:40 */ @RestController public class GoodController { @Resource private GoodService goodService; @GetMapping("sell/good") public boolean sellGood(){ return goodService.checkUserLogin("admin"); } }
2021年07月20日
957 阅读
0 评论
1 点赞
2021-07-18
使用Hystrix实现微服务的熔断处理
Hystrix是Netflix开源的一个延迟和容错库,用于隔离访问远程系统、服务或者第三方库,防止级联失效,从而提升系统的可用性与容错性。Hystrix基本使用添加依赖<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> <version>2.2.8.RELEASE</version> </dependency>为启动类增加@EnableHystrix注解,从而为项目启用断路器支持@SpringBootApplication @EnableFeignClients @EnableHystrix public class MicroserviceConsumerMovieApplication { public static void main(String[] args) { SpringApplication.run(MicroserviceConsumerMovieApplication.class, args); } }修改方法,增加容错能力 @GetMapping("/user/{id}") @HystrixCommand(fallbackMethod = "findByIdFallback", commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000"), @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "10000") }, threadPoolProperties = { @HystrixProperty(name = "coreSize", value = "1"), @HystrixProperty(name = "maxQueueSize", value = "10") }) public User findById(@PathVariable Long id) { return userFeignClient.findById(id); } public User findByIdFallback(Long id, Throwable throwable) { log.info(throwable.getMessage()); User user = new User(); user.setName("默认用户"); return user; }测试我们断开此时的服务提供者,访问方法,可以看到进入了容错方法,并获取到了异常信息。feign集成hystrix{message type="info" content="关于soring Cloud版本提示"/}一定要注意Spring Cloud版本问题,Spring Cloud 2020 后,开启feign的hystrix支持需要在配置文件配置如下feign: circuitbreaker: enabled: true早期版本的Spring Cloud需要配置如下feign: hystrix: enabled: true为feign添加回退通过fallback添加回退通过fallback添加回退,不能获取回退原因。@FeignClient(name = "microservice-provider-user",fallback = UserFeignClientCallback.class) public interface UserFeignClient { /** * 使用Feign自带的注解 @RequestLine * @param id * @return */ @RequestLine("GET /{id}") User findById(@Param("id") Long id); } @Slf4j @Component class UserFeignClientCallback implements UserFeignClient{ /** * 使用Feign自带的注解 @RequestLine * * @param id * @return */ @Override public User findById(Long id) { User user = new User(); user.setName("默认用户"); return user; } }通过fallbackFactory添加回退fallbackFactory是fallback的升级版本,能够获取到回退原因。@FeignClient(name = "microservice-provider-user",fallbackFactory = UserFeignClientCallbackFactory.class) public interface UserFeignClient { /** * 使用Feign自带的注解 @RequestLine * @param id * @return */ @RequestLine("GET /{id}") User findById(@Param("id") Long id); } @Slf4j @Component class UserFeignClientCallbackFactory implements FallbackFactory<UserFeignClient> { @Override public UserFeignClient create(Throwable cause) { return new UserFeignClient() { @Override public User findById(Long id) { log.info(cause.getMessage()); User user = new User(); user.setName("默认用户"); return user; } }; } }温馨提示 日志最好放到各个fallback方法中,而不要放到create方法中,否则在引用启动时,会打印日志。feign项目中Hystrix的监控首先需要添加依赖<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> <version>2.2.8.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>启动类添加@EnableHystrix@SpringBootApplication @EnableFeignClients @EnableHystrix public class MicroserviceConsumerMovieApplication { public static void main(String[] args) { SpringApplication.run(MicroserviceConsumerMovieApplication.class, args); } }修改配置文件,暴漏地址management: endpoints: web: exposure: include: hystrix.stream测试打开地址http://localhost:8081/actuator/hystrix.stream使用hystrix-dashboard可视化监控项目我们刚才项目监控时,返回的都是Json数据,通过hystrix-dashboard我们可以实现可视化的监控。添加hystrix-dashboard依赖<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> <version>2.2.8.RELEASE</version> </dependency>在启动类添加@EnableHystrixDashboard注解@SpringBootApplication @EnableFeignClients @EnableHystrix @EnableHystrixDashboard public class MicroserviceConsumerMovieApplication { public static void main(String[] args) { SpringApplication.run(MicroserviceConsumerMovieApplication.class, args); } }修改配置文件配置文件添加hystrix: dashboard: proxy-stream-allow-list: "*"如果不添加,监控会提示Unable to connect to Command Metric Stream.测试打开http://localhost:8081/hystrix,监控地址输入http://localhost:8081/actuator/hystrix.stream,title随便输入即可。点击Monitor Stream,然后访问任意的接口,系统输出界面如下
2021年07月18日
1,029 阅读
0 评论
1 点赞
1
...
3
4
5
...
9