首页
统计
关于
Search
1
Sealos3.0离线部署K8s集群
1,433 阅读
2
Spring Cloud OAuth2.0
982 阅读
3
类的加载
969 阅读
4
SpringBoot自动装配原理
848 阅读
5
集合不安全问题
702 阅读
笔记
Java
多线程
注解和反射
JVM
JUC
设计模式
Mybatis
Spring
SpringMVC
SpringBoot
MyBatis-Plus
Elastic Search
Netty
微服务
Dubbo
Zookeeper
SpringCloud
Nacos
Sentinel
数据库
MySQL
Oracle
PostgreSQL
Redis
MongoDB
工作流
Activiti7
Camunda
消息队列
RabbitMQ
前端
HTML5
CSS
CSS3
JavaScript
jQuery
Vue2
Vue3
Canvas
React
Linux
容器
Docker
Containerd
Podman
Kubernetes
Python
FastApi
OpenCV
数据分析
牛牛生活
登录
Search
标签搜索
Java
CSS
mysql
RabbitMQ
JavaScript
React
Redis
OpenCV
JVM
Mybatis-Plus
Netty
Camunda
多线程
CSS3
Python
Canvas
Spring Cloud
注解和反射
Activiti
工作流
蘇阿細
累计撰写
484
篇文章
累计收到
4
条评论
首页
栏目
笔记
Java
多线程
注解和反射
JVM
JUC
设计模式
Mybatis
Spring
SpringMVC
SpringBoot
MyBatis-Plus
Elastic Search
Netty
微服务
Dubbo
Zookeeper
SpringCloud
Nacos
Sentinel
数据库
MySQL
Oracle
PostgreSQL
Redis
MongoDB
工作流
Activiti7
Camunda
消息队列
RabbitMQ
前端
HTML5
CSS
CSS3
JavaScript
jQuery
Vue2
Vue3
Canvas
React
Linux
容器
Docker
Containerd
Podman
Kubernetes
Python
FastApi
OpenCV
数据分析
牛牛生活
页面
统计
关于
搜索到
169
篇与
的结果
2021-05-05
Seata分布式事务原理
1、TM开启分布式事务(TM向TC注册全局事务)2、按业务场景,编排数据库、服务等事务内的资源(RM向TC汇报资源准备状态)3、TM结束分布式事务,事务一阶段结束(TM通知TC提交/回滚)4、TC汇总事务信息,决定最终是提交还是回滚事务5、TC通知所有RM提交/回滚 资源,事务二阶段结束Seata提供了四个模式:AT、TCC、SAGA、XAAT模式两阶段提交协议的演变:一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源二阶段:提交异步化(能快速完成);回滚通过一阶段的回滚日志进行反向补偿一阶段:在一阶段,seata会拦截“业务SQL”:1、解析SQL语义,找到“业务SQL”要更新的业务数据,在业务数据被更新前,将其保存为before image2、执行“业务SQL”更新业务数据,更新之后将业务数据保存为after image3、最后生成行锁以上操作全部在一个数据库事务内完成,保证了一阶段操作的原子性二阶段:1、成功提交二阶段正常执行的话,只需将一阶段保存的快照数据和行锁删除,即可完成数据清理,因为“业务SQl”在一阶段已经在提交至数据库2、事务回滚二阶段如果出现异常,需要事务回滚,seata就需要回滚一阶段已经执行的“业务SQL”,还原业务数据;回滚方式使用before image还原业务数据,在还原之前需要做校验脏写,对比数据库当前的业务数据和after image数据快照,如果两份数据完全一致,就说明没有脏写,执行还原操作,如果不一致,就需要转人工处理
2021年05月05日
113 阅读
0 评论
0 点赞
2021-05-05
Seata
seata是一个分布式的解决方案,致力于在微服务架构下提供高性能和简单易用的分布式微服务,官网:http://seata.io/zh-cn/一、三组概念TC 事务协调者:维护全局和分支事务的状态,驱动全局事务提交或回滚TM 事务管理器:定义全局事务的范围:开始、提交、回滚RM 资源管理器:管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚二、安装GitHub地址:https://github.com/seata/seata(以Nacos 2.0、Seata 1.4.2为例)修改配置文件1、/conf/file.conf (注意与1.0之前的版本区分,1.0之后可以在yml文件中指定service配置)修改数据库模式(使用数据库存储事务信息)和数据源2、/conf/registry.conf3、mysql建表1、新建seata数据库2、运行mysql.sql注意:建表sql在/conf/REDME.md文件中,点击server出现存放sql的github地址:https://github.com/seata/seata/tree/develop/script/server4、启动先启动Nacos再启动Seata(bin/seata-server.bat)三、业务测试1、业务说明创建三个微服务:订单---库存---账户大致流程:当用户下单时,订单服务创建订单,远程调用库存服务扣减库存,再通过远程调用账户服务来扣减账户余额,最后在订单服务中修改订单状态为已完成。2、数据库建表创建三个数据库seata_order(订单),seata_storage(库存),seata_account(账户)创建对应的表-- t_order DROP TABLE IF EXISTS `t_order`; CREATE TABLE `t_order` ( `id` bigint(11) NOT NULL AUTO_INCREMENT, `user_id` bigint(11) NULL DEFAULT NULL COMMENT '用户id', `product_id` bigint(11) NULL DEFAULT NULL COMMENT '产品id', `count` int(11) NULL DEFAULT NULL COMMENT '数量', `money` decimal(11, 0) NULL DEFAULT NULL COMMENT '金额', `status` int(1) NULL DEFAULT NULL COMMENT '订单状态:0:创建中; 1:已完结', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 14 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- t_storage DROP TABLE IF EXISTS `t_storage`; CREATE TABLE `t_storage` ( `id` bigint(11) NOT NULL AUTO_INCREMENT, `product_id` bigint(11) NULL DEFAULT NULL COMMENT '产品id', `total` int(11) NULL DEFAULT NULL COMMENT '总库存', `used` int(11) NULL DEFAULT NULL COMMENT '已用库存', `residue` int(11) NULL DEFAULT NULL COMMENT '剩余库存', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; INSERT INTO `t_storage` VALUES (1, 1, 100, 0, 100); -- t_account DROP TABLE IF EXISTS `t_account`; CREATE TABLE `t_account` ( `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT 'id', `user_id` bigint(11) NULL DEFAULT NULL COMMENT '用户id', `total` decimal(10, 0) NULL DEFAULT NULL COMMENT '总额度', `used` decimal(10, 0) NULL DEFAULT NULL COMMENT '已用余额', `residue` decimal(10, 0) NULL DEFAULT 0 COMMENT '剩余可用额度', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; INSERT INTO `t_account` VALUES (1, 1, 1000, 0, 1000);undo回滚日志表,三个库每个都需要创建一张回滚表,分别执行三次DROP TABLE IF EXISTS `undo_log`; CREATE TABLE `undo_log` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `branch_id` bigint(20) NOT NULL, `xid` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `context` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `rollback_info` longblob NOT NULL, `log_status` int(11) NOT NULL, `log_created` datetime(0) NOT NULL, `log_modified` datetime(0) NOT NULL, `ext` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE, UNIQUE INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;3、seata-order-servicepom<dependencies> <!-- API --> <dependency> <groupId>com.sw</groupId> <artifactId>cloud-api-commons</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <!--nacos--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!--seata--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> <exclusions> <exclusion> <artifactId>seata-all</artifactId> <groupId>io.seata</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> <version>1.4.0</version> </dependency> <!--feign--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!--web-actuator--> <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> <!--mysql-druid--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.16</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies>ymlserver: port: 2001 spring: application: name: seata-order-service cloud: nacos: discovery: server-addr: localhost:8848 datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/seata_order?useSSL=true&serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8 username: root password: 123456 seata: enabled: true application-id: ${spring.application.name} # 自定义事务组名称 tx-service-group: test_tx_group service: vgroup-mapping: test_tx_group: default config: nacos: namespace: server-addr: 127.0.0.1:8848 group: SEATA_GROUP userName: "nacos" password: "nacos" registry: type: nacos nacos: application: seata-server server-addr: 127.0.0.1:8848 namespace: userName: "nacos" password: "nacos" feign: hystrix: enabled: false logging: level: io: seata: info启动类@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) //取消自动创建数据源 @EnableDiscoveryClient @EnableFeignClients @MapperScan("com.sw.mapper") public class SeataOrderMain2001 { public static void main(String[] args) { SpringApplication.run(SeataOrderMain2001.class, args); } }pojo@Data @NoArgsConstructor @AllArgsConstructor public class Order implements Serializable { private Long id; private Long userId; private Long productId; private Integer count; private BigDecimal money; private Integer status; //订单状态:0:创建中;1:已完结 }mapper@Repository public interface OrderMapper { /** * 新建订单 * @param order */ @Insert("insert into t_order(id,user_id,product_id,count,money,status) " + "values(null,#{userId},#{productId},#{count},#{money},0)") void create(Order order); /** * 更新订单 * @param userId * @param status */ @Update("update t_order set status = 1 " + "where user_id=#{userId} and status = #{status}") void update(@Param("userId")Long userId, @Param("status")Integer status); }serviceOrderServicepublic interface OrderService { void create(Order order); }StorageService@FeignClient("seata-storage-service") public interface StorageService { @PostMapping("/storage/decrease") CommonResult decrease(@RequestParam("productId")Long productId, @RequestParam("count")Integer count); }AccountService@FeignClient("seata-account-service") public interface AccountService { @PostMapping("/account/decrease") CommonResult decrease(@RequestParam("userId")Long userId, @RequestParam("money")BigDecimal money); }service实现@Service @Slf4j public class OrderServiceImpl implements OrderService { @Autowired private OrderMapper orderMapper; @Autowired private AccountService accountService; @Autowired private StorageService storageService; @Override public void create(Order order) { //1.新建订单 log.info("===开始新建订单==="); orderMapper.create(order); //2.扣减库存 log.info("===订单微服务调用库存==="); storageService.decrease(order.getProductId(), order.getCount()); log.info("===订单微服务调用库存,库存数量操作结束==="); //3.扣减账户 log.info("===订单微服务调用账户余额==="); accountService.decrease(order.getUserId(), order.getMoney()); log.info("===订单微服务调用账户余额,账户余额操作结束==="); //4.修改订单状态 log.info("===修改订单状态,开始==="); //从 0 到 1,1代表已经完成 orderMapper.update(order.getUserId(), 0); log.info("===修改订单状态,操作结束==="); log.info("订单操作完成(*^_^*)"); } }controller@RestController @RequestMapping("/order") public class OrderController { @Autowired private OrderService orderService; @GetMapping("/create") public CommonResult create(Order order){ orderService.create(order); return new CommonResult(200, "订单创建成功!"); } } config主启动类排除了数据源配置,此处需指定数据源代理@Configuration public class DataSourceProxyConfig { @Bean @ConfigurationProperties(prefix = "spring.datasource") public DataSource druidDataSource(){ return new DruidDataSource(); } @Primary @Bean("dataSource") public DataSourceProxy dataSourceProxy(DataSource dataSource){ return new DataSourceProxy(dataSource); } }4、seata-storage-serviceymlserver: port: 2002 spring: application: name: seata-storage-service cloud: nacos: discovery: server-addr: localhost:8848 datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/seata_storage?useSSL=true&serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8 username: root password: 123456 seata: enabled: true application-id: ${spring.application.name} tx-service-group: test_tx_group service: vgroup-mapping: test_tx_group: default config: nacos: namespace: server-addr: 127.0.0.1:8848 group: SEATA_GROUP userName: "nacos" password: "nacos" registry: type: nacos nacos: application: seata-server server-addr: 127.0.0.1:8848 namespace: userName: "nacos" password: "nacos" feign: hystrix: enabled: false logging: level: io: seata: infomapper@Repository public interface StorageMapper { /** * 扣减库存信息 * @param productId * @param count */ @Update("update t_storage " + "set used = used + #{count}," + "residue = residue - #{count} "+ "where product_id = #{productId}" ) void decrease(@Param("productId")Long productId, @Param("count")Integer count); }servicepublic interface StorageService { void decrease(Long productId, Integer count); }service实现@Slf4j @Service public class StorageServiceImpl implements StorageService { @Autowired private StorageMapper storageMapper; @Override public void decrease(Long productId, Integer count) { log.info("===storage-service 开始扣减库存==="); storageMapper.decrease(productId, count); log.info("===storage-service 扣减库存操作结束==="); } }其他配置同理order微服务模块的5、seata-account-servicemapper@Repository public interface AccountMapper { /** * 扣减账户余额 * @param userId * @param money */ @Update("update t_account set "+ "used = used + #{money},"+ "residue = residue - #{money} "+ "where user_id = #{userId}" ) void decrease(@Param("userId")Long userId, @Param("money") BigDecimal money); }其他配置同理storage微服务模块的创建6、不添加全局事务管理测试seata-account-service模块1、手动模拟超时@Slf4j @Service public class AccountServiceImpl implements AccountService { @Autowired private AccountMapper accountMapper; @Override public void decrease(Long userId, BigDecimal money) { log.info("===account-service 开始扣减账户余额==="); //模拟超时异常 try { TimeUnit.SECONDS.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } accountMapper.decrease(userId, money); log.info("===account-service 扣减账户余额操作结束==="); } }浏览器输入http://localhost:2001/order/create?userId=1&productId=1&count=10&money=100报错Read TimeOut异常:此时数据库的状态:7、开启全局事务在order模块中添加全局事务注解@GlobalTransactional使用同样的方法测试,发生调用超时异常后,数据库中被修改的数据回滚到了操作之前的状态
2021年05月05日
108 阅读
0 评论
0 点赞
2021-04-29
Nacos
服务注册和配置中心的组合:Nacos = Eureka + config + bus一、安装1、环境:java8、Mavennacos地址:https://github.com/alibaba/nacos2、启动# 注:单机启动,需修改bin目录下的startup.cmd为`standalone`模式 set MODE="standalone" set FUNCTION_MODE="all" set SERVER=nacos-server set MODE_INDEX=-1 set FUNCTION_MODE_INDEX=-1 set SERVER_INDEX=-1 set EMBEDDED_STORAGE_INDEX=-1 set EMBEDDED_STORAGE=""windows环境下,双击startup.cmd启动3、访问Nacos# nacos默认监听8848 # 默认账号:nacos,密码:nacos localhost:8848二、测试1、payment模块新建cloudalibaba-provider-payment9001、cloudalibaba-provider-payment9002两个模块pom<!-- Nacos --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <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.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>ymlserver: port: 9001 spring: application: name: nacos-payment-provider cloud: nacos: discovery: server-addr: localhost:8848 management: endpoints: web: exposure: include: '*'主启动类@SpringBootApplication @EnableDiscoveryClient public class PaymentMain9001 { public static void main(String[] args) { SpringApplication.run(PaymentMain9001.class, args); } }controller@RestController @RequestMapping("/payment") public class PaymentController { @Value("${server.port}") private String serverPort; @GetMapping("/nacos/{id}") public String getPayment(@PathVariable("id")int id){ return "nacos registry, serverPort: " + serverPort + "\t id: " + id; } }2、order模块pom同理paymentymlserver: port: 8088 spring: application: name: nacos-payment-consumer cloud: nacos: discovery: server-addr: localhost:8848 # 消费者将要去访问的微服务名称(注册进nacos的服务提供者) service-url: nacos-user-service: http://nacos-payment-provider主启动类@SpringBootApplication @EnableDiscoveryClient public class ConsumerMain80 { public static void main(String[] args) { SpringApplication.run(ConsumerMain80.class, args); } }config@Configuration public class ApplicationContextConfig { @Bean @LoadBalanced public RestTemplate getRestTemplate(){ return new RestTemplate(); } }controller@RestController @RequestMapping("/consumer") public class ConsumerController { @Autowired private RestTemplate restTemplate; @Value("${service-url.nacos-user-service}") private String serverURL; @GetMapping("/payment/nacos/{id}") public String paymentInfo(@PathVariable("id")int id){ return restTemplate.getForObject(serverURL + "/payment/nacos/" + id, String.class); } }
2021年04月29日
160 阅读
0 评论
0 点赞
2021-04-29
Spring Cloud Stream
一、概述 Spring Cloud Stream 是一个构建消息驱动微服务的框架,应用程序通过inputs(生产者)或outputs(消费者)来与Spring Cloud Stream中binder对象交互,binder主要负责与消息中间件交互(开发人员只需操作Stream,而不用关心底层使用的是什么MQ)。注:目前仅支持RabbitMQ和kafka二、如何屏蔽底层差异在没有绑定器这个概念的情况下,SpringBoot应用在直接与消息中间件交互的时候,由于各MQ的设计初衷有不同,细节上存在较大的差异。通过定义绑定器作为中间层,完美的实现了应用程序与消息中间件细节之间的隔离;通过向应用程序暴露统一的channel频道,使得应用程序不需要再考虑各种MQ的实现。Stream对消息中间件的进一步封装,可以做到代码层面对中间件的无感知,甚至于动态的切换中间件(RabbitMQ<==>kafka),使得开发高度解耦,更加专注于自己的业务流程。Spring Cloud Stream处理架构三、通信模式Stream消息通信遵循发布-订阅模式Spring Cloud Stream的业务流程source:发布消息sink:接收消息图片来源:尚硅谷 - 周阳 - Spring Cloud Alibaba四、常用注解和API名称说明Middleware中间件Binder应用与消息中间件之间的封装,目前实现了RabbitMQ和Kafka的Binder,利用Binder可以很方便的连接MQ,可以动态的改变消息类型(RabbitMQ--exchange,Kafaka--topic),可以通过yml文件的配置来实现@Input输入@Output输出@StreamListener监听队列@EnableBinding绑定频道五、测试实例1、提供者pom<!-- stream rabbitmq --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-stream-rabbit</artifactId> </dependency> <!-- Eureka-client --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <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>ymlserver: port: 8801 spring: application: name: cloud-stream-provider rabbitmq: host: x.x.x.x port: 5672 username: admin password: 123456 cloud: stream: binders: # 配置要绑定的rabbitmq的服务信息 defaultRabbit: # 定义的名称,用于binding整合 type: rabbit # 消息组件类型 bindings: # 服务整合处理 output: # 通道名称 destination: StreamExChange # 交换机的名字 content-type: application/json # 消息类型 binder: defaultRabbit # 设置要绑定的消息服务的具体设置 eureka: client: service-url: defaultZone: http://localhost:7001/eureka instance: lease-renewal-interval-in-seconds: 2 # 心跳时间间隔 lease-expiration-duration-in-seconds: 5 # 默认90s instance-id: send-8801.com # 在信息列表显示主机名称 prefer-ip-address: true # 访问路径显示ip 主启动类@SpringBootApplication public class StreamMQMain8801 { public static void main(String[] args) { SpringApplication.run(StreamMQMain8801.class, args); } }Service@EnableBinding(Source.class) //定义消息的推送通道 public class MessageProviderImpl implements MessageProvider { @Autowired private MessageChannel output; @Override public String send() { String serial = UUID.randomUUID().toString(); output.send(MessageBuilder.withPayload(serial).build()); System.out.println("======>" + serial); return null; } }controller@RestController public class SendMessageController { @Autowired private MessageProvider service; @GetMapping("/send") public String sendMessage(){ return service.send(); } }2、消费者==创建8802、8803两个消费者==pom同理消费者ymlserver: port: 8802 spring: application: name: cloud-stream-consumer rabbitmq: host: x.x.x.x port: 5672 username: admin password: 123456 cloud: stream: binders: # 配置要绑定的rabbitmq的服务信息 defaultRabbit: # 定义的名称,用于binding整合 type: rabbit # 消息组件类型 bindings: # 服务整合处理 input: # 通道名称 destination: StreamExChange # 交换机的名字 content-type: application/json # 消息类型 binder: defaultRabbit # 设置要绑定的消息服务的具体设置 group: GroupA8802 eureka: client: service-url: defaultZone: http://localhost:7001/eureka instance: lease-renewal-interval-in-seconds: 2 # 心跳时间间隔 lease-expiration-duration-in-seconds: 5 # 默认90s instance-id: recive-8802.com # 在信息列表显示主机名称 prefer-ip-address: true # 访问路径显示ip 启动类@SpringBootApplication public class StreamMQMain8802 { public static void main(String[] args) { SpringApplication.run(StreamMQMain8802.class, args); } }controller@Component @EnableBinding(Sink.class) public class ReciveController { @Value("${server.port}") private String serverPort; @StreamListener(Sink.INPUT) public void input(Message<String> msg){ System.out.println("消费者8802,接收到消息:" + msg.getPayload() + "\t serverPort:" + serverPort); } }六、重复消费的问题启动以上的测试实例,会发现8801生产者发送一条消息,8802、8803都收到了消息如何解决?通过将消费者分组来解决:即将原先GroupA8802、GroupA8803两个分组改为同一个分组GroupA注:在同一个分组中,多个消费者是竞争关系,这样就能保证消息只会被分组中的一个消费者消费server: port: 8802 spring: application: name: cloud-stream-consumer rabbitmq: host: x.x.x.x port: 5672 username: admin password: 123456 cloud: stream: binders: # 配置要绑定的rabbitmq的服务信息 defaultRabbit: # 定义的名称,用于binding整合 type: rabbit # 消息组件类型 bindings: # 服务整合处理 input: # 通道名称 destination: StreamExChange # 交换机的名字 content-type: application/json # 消息类型 binder: defaultRabbit # 设置要绑定的消息服务的具体设置 group: GroupA eureka: client: service-url: defaultZone: http://localhost:7001/eureka instance: lease-renewal-interval-in-seconds: 2 # 心跳时间间隔 lease-expiration-duration-in-seconds: 5 # 默认90s instance-id: recive-8802.com # 在信息列表显示主机名称 prefer-ip-address: true # 访问路径显示ip消息持久化问题在分组解决重复消费的问题是,分组同时也实现了消息的持久化
2021年04月29日
103 阅读
0 评论
0 点赞
2021-04-05
12.代码自动生成器
代码自动生成器package com.sw.mybatisplus; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.generator.AutoGenerator; import com.baomidou.mybatisplus.generator.config.DataSourceConfig; import com.baomidou.mybatisplus.generator.config.GlobalConfig; import com.baomidou.mybatisplus.generator.config.PackageConfig; import com.baomidou.mybatisplus.generator.config.StrategyConfig; import com.baomidou.mybatisplus.generator.config.po.TableFill; import com.baomidou.mybatisplus.generator.config.rules.DateType; import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; import java.util.ArrayList; /** * @Author suaxi * @Date 2021/4/5 22:10 */ public class CodeGenerator { public static void main(String[] args) { AutoGenerator generator = new AutoGenerator(); //全局配置 GlobalConfig gc = new GlobalConfig(); String projectPath = System.getProperty("user.dir"); gc.setOutputDir(projectPath + "/src/main/java"); gc.setAuthor("suaxi"); //是否打开输出目录 gc.setOpen(false); //是否覆盖之前的 gc.setFileOverride(false); //正则匹配去掉Serivce前的 I gc.setServiceName("%sService"); gc.setIdType(IdType.ID_WORKER); gc.setDateType(DateType.ONLY_DATE); gc.setSwagger2(true); generator.setGlobalConfig(gc); //设置数据源 DataSourceConfig dsc = new DataSourceConfig(); dsc.setUrl("jdbc:mysql://localhost:3306/mybatis_plus?useSSL=true&serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8"); dsc.setDriverName("com.mysql.cj.jdbc.Driver"); dsc.setUsername("root"); dsc.setPassword("123456"); dsc.setDbType(DbType.MYSQL); generator.setDataSource(dsc); //包配置 PackageConfig pc = new PackageConfig(); pc.setModuleName("demo"); pc.setParent("com.sw"); pc.setEntity("pojo"); pc.setMapper("mapper"); pc.setService("service"); pc.setController("controller"); generator.setPackageInfo(pc); //策略配置 StrategyConfig sc = new StrategyConfig(); //映射的表名 sc.setInclude(""); sc.setNaming(NamingStrategy.underline_to_camel); sc.setColumnNaming(NamingStrategy.underline_to_camel); sc.setEntityLombokModel(true); //逻辑删除 sc.setLogicDeleteFieldName("delete"); //自动填充 TableFill createTime = new TableFill("create_Time", FieldFill.INSERT); TableFill updateTime = new TableFill("update_Time", FieldFill.INSERT_UPDATE); ArrayList<TableFill> list = new ArrayList<>(); list.add(createTime); list.add(updateTime); sc.setTableFillList(list); //乐观锁 sc.setVersionFieldName("version"); //请求链接驼峰转下划线:localhost:8088/test/user_id_age sc.setControllerMappingHyphenStyle(true); generator.setStrategy(sc); //执行自动生成 generator.execute(); } }
2021年04月05日
102 阅读
0 评论
0 点赞
2021-04-05
11.条件构造器 QueryWrapper
条件构造器条件查询:@Test void findTest(){ //查询姓名、邮箱不为空,且年龄大于25的用户信息 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.isNotNull("name") .isNotNull("email") .ge("age",25); mapper.selectList(wrapper).forEach(System.out::println); } @Test void findTest01(){ //查询一个名字为刘波的用户 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.ge("name","刘波"); User user = mapper.selectOne(wrapper); System.out.println(user); } @Test void findTest02(){ //查询年龄在20-30岁之间的用户 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.between("age", 20, 30); Integer result = mapper.selectCount(wrapper); System.out.println(result); }模糊查询:@Test void findTest03(){ //查询名字中不包含“刘”,且邮箱以"t"开头的用户 //likeRight str% //likeLeft %str QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.notLike("name", "刘") .likeRight("email", "t"); List<Map<String, Object>> maps = mapper.selectMaps(wrapper); maps.forEach(System.out::println); }子查询:@Test void findTest04(){ //查询id大于5的用户 /* WHERE deleted=0 AND id IN ( select id from user where id > 5 ) */ QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.inSql("id", "select id from user where id > 5"); List<Object> maps = mapper.selectObjs(wrapper); maps.forEach(System.out::println); }排序:@Test void findTest05(){ //通过id降序排列 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.orderByDesc("id"); List<User> users = mapper.selectList(wrapper); users.forEach(System.out::println); }
2021年04月05日
151 阅读
0 评论
0 点赞
2021-04-05
10.性能分析
性能分析注册Bean://性能分析 @Bean @Profile({"dev", "test"}) public PerformanceInterceptor performanceInterceptor(){ PerformanceInterceptor interceptor = new PerformanceInterceptor(); //sql最大执行时间,超过则不执行,单位:ms interceptor.setMaxTime(100); //是否格式化 interceptor.setFormat(true); return interceptor; }
2021年04月05日
173 阅读
0 评论
0 点赞
2021-04-05
8.删除
删除根据id删除单条记录根据ids批量删除条件删除根据map删除
2021年04月05日
85 阅读
0 评论
0 点赞
2021-04-05
9.逻辑删除
逻辑删除物理删除:从数据库中直接移除逻辑删除:数据库中没有移除,而是通过一个变量来使该条数据失效1.数据库增加delete字段,默认值为02.实体类增加属性@TableLogic private int deleted;3.注册Bean//逻辑删除 @Bean public ISqlInjector sqlInjector(){ return new LogicSqlInjector(); }4.application.yml配置mybatis-plus: global-config: db-config: logic-delete-value: 1 logic-not-delete-value: 05.单元测试//逻辑删除测试 @Test void logicDeleteTest(){ userMapper.deleteById(1L); //sql:UPDATE user SET deleted=1 WHERE id=? AND deleted=0 User user = userMapper.selectById(1L); System.out.println(user); //输出结果为null //查询时会自动过滤delete=1的值 }
2021年04月05日
128 阅读
0 评论
0 点赞
2021-04-05
7.分页查询
分页查询1.在Config中配置分页插件//分页插件 @Bean public PaginationInterceptor paginationInterceptor(){ return new PaginationInterceptor(); }2.分页测试//分页测试 @Test void pageTest(){ //pageNum, pageSize Page<User> page = new Page<>(1,5); userMapper.selectPage(page,null); page.getRecords().forEach(System.out::println); //SELECT id,name,age,email,version,create_time,update_time FROM user LIMIT 0,5 }
2021年04月05日
89 阅读
0 评论
0 点赞
1
...
5
6
7
...
17