【RocketMQ】秒杀设计与实现
🎯 导读:本文档详细探讨了高并发场景下的秒杀系统设计与优化策略,特别是如何在短时间内处理大量请求。文档分析了系统性能指标如QPS(每秒查询率)和TPS(每秒事务数),并通过实例讲解了如何使用JMeter进行性能测试。此外,文档提供了技术选型指南,包括SpringBoot、Redis、RocketMQ等技术的应用,并给出了具体的用户量评估和服务器配置建议。最后,通过分析不同的库存扣减与订单创建实现方式,提出了使用Redis分布式锁等技术提高并发性能的方法。
文章目录
- 秒杀
- 介绍
- 性能指标
- QPS
- TPS
- 怎么优化接口性能
- 技术选型
- 用户量评估
- 技术要点
- 架构图
- 数据库
- 创建项目选择依赖seckill-web(接受用户秒杀请求)
- pom.xml
- 修改配置文件
- 创建SeckillController
- 创建项目选择依赖seckill-service(处理秒杀)
- 修改yml文件
- 逆向生成实体类
- 修改启动类
- 修改GoodsMapper
- 修改GoodsMapper.xml
- 同步MySQL数据到redis
- 方法1
- 方法2
- 秒杀业务监听器
- 修改GoodsService
- 修改GoodsServiceImpl
秒杀
介绍
秒杀:很短的时间内,要处理大量的请求
【高并发介绍】
并发:多个任务在同一时间段内执行,cpu不停切换来执行不同任务
并行:多核CPU上,多个任务在同一时刻执行
要想高并发,硬件很重要,但是成本很高,企业希望在有限的硬件上,最优化软件的性能
性能指标
QPS
- QPS:每秒钟处理请求的数量,业务处理时间越低,QPS越高
Tomcat 的 QPS:SpringBoot的Tomcat默认是最大是200个线程,如果请求处理消耗50ms,理论QPS就是1000*200/50=4000,实际大概率会更低
可以在配置文件中设置tomcat的线程数量
【使用Jmeter测试】
如果异常很大,超过0.5%,数据就没有太大的价值
Tomcat最大连接数改成400
如果并发量非常大,一个Tomcat顶不住,可以做服务集群。
- 一个nginx可以顶住5w的QPS,再负载均衡到多个tomcat服务中
-
并发量达到30w,nginx顶不住了,使用好机器来提供虚拟IP,然后再将请求分发到多个Nginx中
-
个人开发,100wQPS就很强了。如果有很大的流量,可以根据用户IP拆分到不同地区的机房。一个域名下面对应很多个服务器IP,按照用户IP区域将其分发大较近的机房IP即可
TPS
每秒钟能够处理的事务或交易的数量。
怎么优化接口性能
- 减少IO(批量查询、批量插入、批量删除)
- 尽早return(例如先去Redis判断的库存够不够,再去执行扣减库存)
- 能异步就异步(减库存放到MQ)
- 锁粒度尽量小
- 事务范围尽可能小
- 前端分流(如拼图滑块、计算,有人快、有人慢,同时可以验证是否为机器人)
- 做限制(一个人针对一个商品只能抢一次优惠券,Redis setnx,抢过就不让进来了)
- seckill-web:接受秒杀请求,然后把业务交给seckill-service执行
- seckill-service:处理秒杀真实业务
技术选型
- Springboot 接收请求并操作 redis 和 MySQL
- Redis 用于缓存+分布式锁
- RocketMQ 用于解耦、削峰、异步
- MySQL 用于存放真实的商品信息
- Mybatis 用于操作数据库的orm框架
用户量评估
总用户量:50w
日活量:1-2w(用户不会天天用,除非经常做活动)
qps:2w+(怎么统计,日志,统计次数)
几台服务器(什么配置):8C16G 4-6台
- seckill-web:4台
- seckill-service:2台
带宽:100M
技术要点
- 通过 redis 的 setnx 对用户和商品做去重判断, 防止用户刷接口
- 每天晚上 8 点通过定时任务把 MySQL 中参与秒杀的库存商品, 同步到 redis 中去, 做库存的预扣减, 提升接口性能
- 通过 RocketMQ 消息中间件的异步消息, 来将秒杀的业务异步化, 进一步提升性能
- seckill-service 使用并发消费模式, 并且设置合理的线程数量, 快速处理队列中堆积的消息
- 使用 redis 的分布式锁+自旋锁, 对商品的库存进行并发控制, 把并发压力转移到程序中和 redis 中去, 减少 db 压力
- 使用声明式事务注解 Transactional, 并且设置异常回滚类型, 控制数据库的原子性操作
- 使用 jmeter 压测工具, 对秒杀接口进行压力测试, 在 8C16G 的服务器上, qps2k+, 达到压测预期
架构图
数据库
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
-- Table structure for goods
-- ----------------------------
DROP TABLE IF EXISTS `goods`;
CREATE TABLE `goods` (`id` int(11) NOT NULL AUTO_INCREMENT,`goods_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,`price` decimal(10, 2) NULL DEFAULT NULL,`stocks` int(255) NULL DEFAULT NULL,`status` int(255) NULL DEFAULT NULL,`pic` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,`create_time` datetime(0) NULL DEFAULT NULL,`update_time` datetime(0) NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of goods
-- ----------------------------
INSERT INTO `goods` VALUES (1, '小米12s', 4999.00, 1000, 2, 'xxxxxx', '2023-02-23 11:35:56', '2023-02-23 16:53:34');
INSERT INTO `goods` VALUES (2, '华为mate50', 6999.00, 10, 2, 'xxxx', '2023-02-23 11:35:56', '2023-02-23 11:35:56');
INSERT INTO `goods` VALUES (3, '锤子pro2', 1999.00, 100, 1, NULL, '2023-02-23 11:35:56', '2023-02-23 11:35:56');-- ----------------------------
-- Table structure for order_records
-- ----------------------------
DROP TABLE IF EXISTS `order_records`;
CREATE TABLE `order_records` (`id` int(11) NOT NULL AUTO_INCREMENT,`user_id` int(11) NULL DEFAULT NULL,`order_sn` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,`goods_id` int(11) NULL DEFAULT NULL,`create_time` datetime(0) NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic;SET FOREIGN_KEY_CHECKS = 1;
创建项目选择依赖seckill-web(接受用户秒杀请求)
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.6.13</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.powernode</groupId><artifactId>seckill-web</artifactId><version>0.0.1-SNAPSHOT</version><name>seckill-web</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-data-redis</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></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><!-- RocketMQ的依赖 --><dependency><groupId>org.apache.RocketMQ</groupId><artifactId>RocketMQ-spring-boot-starter</artifactId><version>2.2.1</version></dependency><dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId><version>2.0.14</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build></project>
修改配置文件
server:port: 7001tomcat:threads:max: 400
spring:application:name: seckill-webredis:host: 127.0.0.1port: 6379database: 0lettuce:pool:enabled: truemax-active: 100max-idle: 20min-idle: 5
RocketMQ:name-server: 192.168.188.129:9876 # RocketMQ的nameServer地址producer:access-key: dsad secret-key: dsadasfasgroup: powernode-group # 生产者组别,不配置会报错send-message-timeout: 3000 # 消息发送的超时时间retry-times-when-send-async-failed: 2 # 异步消息发送失败重试次数max-message-size: 4194304 # 消息的最大长度
创建SeckillController
package com.powernode.controller;import com.alibaba.fastjson.JSON;
import org.apache.RocketMQ.client.producer.SendCallback;
import org.apache.RocketMQ.client.producer.SendResult;
import org.apache.RocketMQ.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;
import java.util.concurrent.atomic.AtomicInteger;@RestController
public class SeckillController {@Autowiredprivate StringRedisTemplate redisTemplate;@Autowiredprivate RocketMQTemplate RocketMQTemplate;/*** 压测时自动是生成用户id*/AtomicInteger ai = new AtomicInteger(0);/*** 1.用户去重,一个用户针对一种商品只能抢购一次* 2.做库存的预扣减 拦截掉大量无效请求* 3.放入mq 异步化处理订单* userId通过登录状态拿取* @return*/@GetMapping("doSeckill")public String doSeckill(Integer goodsId /*, Integer userId*/) {int userId = ai.incrementAndGet();// unique key 唯一标记 去重String uk = userId + "-" + goodsId;// set nx set if not exist。如果要每天刷新,key加上年月日即可,key再设置过期时间Boolean flag = redisTemplate.opsForValue().setIfAbsent("seckillUk:" + uk, "");if (!flag) {return "您已经参与过该商品的抢购,请参与其他商品抢购!";}// 假设库存已经同步了 key:goods_stock:1 val:10// 直接扣减数量,线程安全。如果先查出来,再减少,线程不安全Long count = redisTemplate.opsForValue().decrement("goods_stock:" + goodsId);// getkey java setkey 先查再写 再更新 有并发安全问题if (count < 0) {return "该商品已经被抢完,请下次早点来";}// 放入mqHashMap<String, Integer> map = new HashMap<>(4);map.put("goodsId", goodsId);map.put("userId", userId);RocketMQTemplate.asyncSend("seckillTopic3", JSON.toJSONString(map), new SendCallback() {@Overridepublic void onSuccess(SendResult sendResult) {System.out.println("发送成功" + sendResult.getSendStatus());}@Overridepublic void onException(Throwable throwable) {System.err.println("发送失败" + throwable.getMessage());}});// 不能直接返回抢购成功,因为MQ可能是有问题的return "拼命抢购中,请稍后去订单中心查看";}
}
创建项目选择依赖seckill-service(处理秒杀)
<?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.6.13</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.powernode</groupId><artifactId>seckill-service</artifactId><version>0.0.1-SNAPSHOT</version><name>seckill-service</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-data-redis</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.3.0</version></dependency><dependency><groupId>com.MySQL</groupId><artifactId>MySQL-connector-j</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></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>druid-spring-boot-starter</artifactId><version>1.2.6</version></dependency><!-- RocketMQ的依赖 --><dependency><groupId>org.apache.RocketMQ</groupId><artifactId>RocketMQ-spring-boot-starter</artifactId><version>2.2.1</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency><dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId><version>2.0.14</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build></project>
修改yml文件
server:port: 7002
spring:application:name: seckill-servicedatasource:driver-class-name: com.MySQL.cj.jdbc.Driverurl: jdbc:MySQL://127.0.0.1:3306/seckill?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTCusername: rootpassword: 123456redis:host: 127.0.0.1port: 6379database: 0lettuce:pool:enabled: truemax-active: 100max-idle: 20min-idle: 5
mybatis:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImplmapper-locations: classpath*:mapper/*.xml
RocketMQ:name-server: 192.168.188.129:9876
逆向生成实体类
修改启动类
@SpringBootApplication
@MapperScan(basePackages = {"com.powernode.mapper"}) // mapper上面有@Mapper注解,这里就不用加扫描了
@EnableScheduling // 开启定时任务
public class seckillServiceApplication {public static void main(String[] args) {SpringApplication.run(seckillServiceApplication.class, args);}
}
修改GoodsMapper
List<Goods> selectSeckillGoods();
修改GoodsMapper.xml
<!-- 查询数据库中需要参于秒杀的商品数据 status = 2 -->
<select id="selectSeckillGoods" resultMap="BaseResultMap">select `id`,`stocks` from goods where `status` = 2
</select>
同步MySQL数据到redis
方法1
package com.powernode.config;import com.powernode.domain.Goods;
import com.powernode.mapper.GoodsMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;import javax.annotation.PostConstruct;
import java.util.List;/*** 将MySQL的参与抢购的商品的数据* 同步到redis里面去* 在上游服务需要使用redis来做库存的预扣减*/
@Component
public class DataSyncConfig {@Autowiredprivate GoodsMapper goodsMapper;@Autowiredprivate StringRedisTemplate redisTemplate;// 业务场景是搞一个定时任务 每天10点开启// 为了 测试方便 项目已启动就执行一次/*** spring bean的生命周期* 在当前对象 实例化完以后* 属性注入以后* 执行 PostConstruct 注解的方法*/@PostConstruct// java的注解,不是Spring的注解,项目启动的时候,就执行这个方法@Scheduled(cron = "0 10 0 0 0 ?")public void initData() {List<Goods> goodsList = goodsMapper.selectSeckillGoods();if (CollectionUtils.isEmpty(goodsList)) {return;}goodsList.forEach(goods -> redisTemplate.opsForValue().set("goods_stock:" + goods.getId(), goods.getStocks().toString()));}}
不用上面的方法的话,可以在启动类中写,但是不推荐
Bean生命周期
-
实例化对象 new
-
属性赋值
-
初始化
- spring
-
boot (前:PostConstruct,或下面写法;中;后)
-
@Component public class DataSync implements InitializingBean, BeanPostProcessor{@Override public void afterropertiesSet() throws Exception {} @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException{}@Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException{} }
-
使用
-
销毁
方法2
package com.powernode.data;import com.powernode.domain.Goods;
import com.powernode.mapper.GoodsMapper;
import org.springframework.boot.CommandLineRunner;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.util.List;@Component
public class MySQLToRedis2 implements CommandLineRunner {@Resourceprivate GoodsMapper goodsMapper;@Resourceprivate StringRedisTemplate stringRedisTemplate;@Overridepublic void run(String... args) throws Exception {initData();}private void initData() {//1 查询数据库中需要参于秒杀的商品数据List<Goods> goodsList = goodsMapper.queryseckillGoods();ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();//2 把数据同步到Redisfor (Goods goods : goodsList) {operations.set("goods:" + goods.getGoodsId(), goods.getTotalStocks().toString());}}}
秒杀业务监听器
package com.powernode.listener;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.powernode.service.GoodsService;
import org.apache.RocketMQ.common.message.MessageExt;
import org.apache.RocketMQ.spring.annotation.RocketMQMessageListener;
import org.apache.RocketMQ.spring.core.RocketMQListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;import java.util.concurrent.TimeUnit;/*** 默认负载均衡模式* 默认多线程消费*/
@Component
@RocketMQMessageListener(topic = "seckillTopic3", consumerGroup = "seckill-consumer-group")
public class SeckillMsgListener implements RocketMQListener<MessageExt> {@Autowiredprivate GoodsService goodsService;@Autowiredprivate StringRedisTemplate redisTemplate;// 20sint time = 20000;/** 扣减库存* 写订单表*/@Overridepublic void onMessage(MessageExt message) {String s = new String(message.getBody());JSONObject jsonObject = JSON.parseObject(s);Integer goodsId = jsonObject.getInteger("goodsId");Integer userId = jsonObject.getInteger("userId");// 减库存,写订单表,使用同步代码块
// synchronized (this) {
// goodsService.realDoSeckill1(goodsId, userId);
// }// 减库存,写订单表,使用MySQL行锁// goodsService.realDoSeckill1(goodsId, userId);// 减库存,写订单表,使用Redis自旋加锁 int current = 0;// 如果有业务因为自旋时间限制,在有限时间内没有抢得到锁,可以增加限制时间上限,或者把循环改成truewhile (current <= time) {// 一般在做分布式锁的情况下,会给锁一个过期时间,防止出现死锁Boolean flag = redisTemplate.opsForValue().setIfAbsent("goods_lock:" + goodsId, "", 10, TimeUnit.SECONDS);if (flag) {// 加锁成功try {goodsService.realDoSeckill(goodsId, userId);return;} finally {// 解锁redisTemplate.delete("goods_lock:" + goodsId);}} else {// 获取锁失败,自旋加锁current += 200;try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}}}}}
修改GoodsService
void realDoSeckill(Integer goodsId, Integer userId);
修改GoodsServiceImpl
【基础方案:有问题】
@Resource
private GoodsMapper goodsMapper;@Autowired
private OrderRecordsMapper orderRecordsMapper;/*** 扣减库存* 写订单表* @param goodsId* @param userId*/
@Override
@Transactional(rollbackFor = RuntimeException.class)
public void realDoSeckill(Integer goodsId, Integer userId) {// 扣减库存 插入订单表Goods goods = goodsMapper.selectByPrimaryKey(goodsId);int finalStock = goods.getStocks() - 1;if (finalStock < 0) {// 只是记录日志 让代码停下来 这里的异常用户无法感知throw new RuntimeException("库存不足:" + goodsId);}goods.setStocks(finalStock);goods.setUpdateTime(new Date());// insert 要么成功 要么报错 update 会出现i<=0的情况// update goods set stocks = 1 where id = 1 没有行锁int i = goodsMapper.updateByPrimaryKey(goods);if (i > 0) {// 写订单表OrderRecords orderRecords = new OrderRecords();orderRecords.setGoodsId(goodsId);orderRecords.setUserId(userId);orderRecords.setCreateTime(new Date());// 时间戳生成订单号orderRecords.setOrderSn(String.valueOf(System.currentTimeMillis()));orderRecordsMapper.insert(orderRecords);}
}
上面的实现不是线程安全的,先查了库存,然后再去修改。并发中,可能一开始库存是够的,后面被其他用户抢走了,库存不够了,但是这里的程序还会继续往下执行
【加锁方案:效率低】
加锁:库存扣减不对,性能差
原因:加事务》加锁》提交事务,MySQL默认事务隔离级别是可重复读。原本有1000件,两个人消费,按理说是998件。但实际上,A进入了方法,修改完库存,释放了锁,但是还没有提交事务,@Transactional是包住整个方法的。B线程进来获得了锁,查询数据库,还是1000件,导致两个线程业务执行完成之后,还剩下999
解决:要先提交事务,才释放锁,这样才是正确的。将代码改成锁包住事务,数据正确性保证了。但是效率还是低
分布式系统要改成分布式锁
【使用MySQL行锁(innodb才有),并发性能不足】
update goods set stocks = stocks - 1
会触发行锁update goods set stocks = 具体值
不会触发行锁stocks > 1
加一个控制
/*** MySQL行锁 innodb 行锁* 分布式锁* todo 答案1** @param goodsId* @param userId*/
@Override
@Transactional(rollbackFor = RuntimeException.class)
public void realDoSeckill1(Integer goodsId, Integer userId) {// update goods set stocks = stocks - 1 ,update_time = now() where id = #{value} and stocks > 1 int i = goodsMapper.updateStocks(goodsId);if (i > 0) {// 写订单表OrderRecords orderRecords = new OrderRecords();orderRecords.setGoodsId(goodsId);orderRecords.setUserId(userId);orderRecords.setCreateTime(new Date());// 时间戳生成订单号orderRecords.setOrderSn(String.valueOf(System.currentTimeMillis()));orderRecordsMapper.insert(orderRecords);}
}
缺点:通过MySQL来控制锁,数据库压力大,如果并发数在1000以下还好,高一点还是建议其他方案
【在监听器中使用Redis自旋加锁】
详情看前面的秒杀业务监听器实现
相关文章:
【RocketMQ】秒杀设计与实现
🎯 导读:本文档详细探讨了高并发场景下的秒杀系统设计与优化策略,特别是如何在短时间内处理大量请求。文档分析了系统性能指标如QPS(每秒查询率)和TPS(每秒事务数),并通过实例讲解了…...
高级架构师面试题
一、技术深度方面 微服务架构的核心概念和优势: • 核心概念:将一个大型的应用拆分为多个小型的、独立部署的服务,每个服务都围绕着特定的业务功能进行构建,服务之间通过轻量级的通信机制进行交互。 • 优势: • 独…...
phpstudy简易使用
注意,本文所述的操作步骤均建立在电脑上已经完成php环境变量的配置与vscode的安装之上 、...
ubuntu server 常用配置
这里写目录标题 0001 虚拟机静态IP0002 vim tab 4个空格0003 设置时区0004 网络端口查看端口开放端口 0005 修噶机主机名 0001 虚拟机静态IP win网络链接,IP地址:192.168.220.1 - NAT网关:192.168.220.2 - ubuntu静态IP设置: ca…...
[Day 82] 區塊鏈與人工智能的聯動應用:理論、技術與實踐
AI在風險控制中的應用案例 風險控制是企業管理中至關重要的一環,AI技術的引入為風險控制帶來了前所未有的自動化和智能化。無論是在金融、保險、製造業,還是網絡安全中,AI都能有效地分析和預測潛在風險。本文將探討AI在風險控制中的應用&…...
微信小程序map组件自定义气泡真机不显示
最近遇到一个需求需要使用uniapp的map自定义气泡 ,做完之后发现在模拟器上好好的,ios真机不显示,安卓页数时好时不好的 一番查询发现是小程序的老问题了,网上的方法都试了也没能解决 后来看到有人说用nvue可以正常显示,…...
数据结构之链表(2),双向链表
目录 前言 一、链表的分类详细 二、双向链表 三、双向链表的实现 四、List.c文件的完整代码 五、使用演示 总结 前言 接着上一篇单链表来详细说说链表中什么是带头和不带头,“哨兵位”是什么,什么是单向什么是双向,什么是循环和不循环。然后实…...
STL之list篇(下)(从底层分析实现list容器,逐步剥开list的外表)
文章目录 前言一、list的数据结构和类实现需求1.1 数据结构1.2 list类实现需求 二、list迭代器的实现2.1 为什么list不能直接把迭代器当作指针一样使用?2.2 list迭代器的框架设计2.3 *和-> 操作符的重载2.4 和-- 操作符的重载2.5 !和 操作符的重载 三、 list的函…...
视频去水印的3个技巧,教你无痕去水印
许多视频平台为了推广自身品牌或者广告用途,会在视频上添加水印。这些水印不仅影响了视频的美观,还可能限制了内容的传播范围。幸运的是,有几种简单而有效的方法可以帮助我们去除视频中的水印,同时保持视频的原始画质和观感。以下…...
LSTM模型改进实现多步预测未来30天销售额
关于深度实战社区 我们是一个深度学习领域的独立工作室。团队成员有:中科大硕士、纽约大学硕士、浙江大学硕士、华东理工博士等,曾在腾讯、百度、德勤等担任算法工程师/产品经理。全网20多万粉丝,拥有2篇国家级人工智能发明专利。 社区特色…...
八LAMP搭建
# LAMP ## 一、知识回顾 ### FTP samba nfs 特点 - 借用Linux用户作为映射用户,进行权限管理 - 软件本身还有管理控制权限 #### 客户端连接到服务器后进行读写执行等操作 ,必须同时具有: - 软件许可的权限 vsftpd: anon upload enableYES - 文件…...
Windows——解除Windows系统中文件名和目录路径的最大长度限制
第一步:打开本地组策略编辑器 按下Win R键打开运行窗口,输入 gpedit.msc 并回车,打开本地组策略编辑器。 第二步:开启 长路径设置 第三步:重启计算机...
黑名单与ip禁令是同一个东西吗
黑名单和IP禁令相关,但它们并不是完全相同的概念。以下是它们之间的区别: 黑名单 定义:黑名单通常是一个包含不允许或被禁止的用户、IP地址、域名或其他实体的列表。用途:用于阻止特定用户或实体访问某个系统或服务。黑名单可以…...
FuTalk设计周刊-Vol.075
国庆75周年,也是第75期周刊~ 祝大家国庆快乐~! #AI漫谈 热点捕手 1.万字深剖!13位AI巨擘联袂,1.6万字解码生成式AI产品「全攻略」 “生成式人工智能产品”主题论坛,邀请到了来自腾讯、商汤科…...
PE节表中是否存在misc.VirtualSize 比SizeofRawData还要大的情况
确实是存在的,这是win10自带记事本,可以看到 确实是大.所以在申请imagebuffer的时候,还是需要比较大小.但是在还原的时候.只考虑sizeofRawData即可>...
栈及笔试题
目录 栈的实现 1、数组栈 2、链式栈 栈的创建 栈的打印 内存泄漏 栈溢出 练习 有效的括号 栈的实现 栈后入先出 1、数组栈 (最佳实现,且访问数据的时候CPU告诉访存命中率比较高,因为地址连续存放,访问时CPU从cache里一…...
【工程测试技术】第3章 测试装置的基本特性,静态特性和动态特性,一阶二阶系统的特性,负载效应,抗干扰性
目录 3.1 概述 1测量装置的静态特性 2.标准和标准传递 3.测量装置的动态特性 4.测量装置的负载特性 5.测量装置的抗干扰性 1.线性度 2.灵敏度 3.回程误差 4.分辨力 5.零点漂移和灵敏度漂移 3.3.1 动态特性的数学描述 1.传递函数 2.频率响应函数 3.脉冲响应函数 …...
ireport 5.1 中文生辟字显示不出来,生成PDF报字体找不到
ireport生成pdf里文字不显示。本文以宋体中文字不显示为例。 问题:由浅入深一步一步分析 问题1、预览正常,但生成pdf中文不显示 报告模板编辑后,预览正常,但生成pdf中文不显示。以下是试验过程: 先编辑好一个报告单模…...
给Ubuntu虚拟机设置静态IP地址(固定IP)
查看 为Ubuntu虚拟机配置静态IP地址(固定IP)的方法经过亲自测试,已被证实有效。 这里你记得网关就可以了,等下要用 查看配置前的网络信息 ifconfig 查看网关 route -n 配置 配置网络文件 cd /etc/netplan/ ls 查看自己的文件的名…...
spring boot文件上传之x-file-storage
spring boot文件上传之x-file-storage 今天看到一个文件上传的开源组件x-file-storage,官方地址如下: https://x-file-storage.xuyanwu.cn/#/ 该组件官网是这样介绍的,如下: 一行代码将文件存储到本地、FTP、SFTP、WebDAV、阿…...
Object.values() 、 Object.keys()
拿到当前对象里面的value值 // 假设你有一个对象 const myObject {name: Kimi,age: 30,country: Moon };// 获取对象的所有值 const values Object.values(myObject);// 输出值数组 console.log(values); // ["Kimi", 30, "Moon"] 如果你需要在 Vue 组…...
脸爱云管理系统存在任意文件上传漏洞
漏洞描述 脸爱云一脸通智慧管理平台是一套功能强大、运行稳定、操作简单方便、用户界面美观的一脸通系统。该平台整合了人脸识别技术和智能化解决方案,可以实现识别和管理个体身份,为各种场景提供便捷的身份验证和管理功能。其存在任意文件上传漏洞&…...
elasticsearch_exporter启动报错 failed to fetch and decode node stats
最近把服务器迁移到了ubuntu系统,结果发现在centos还正常运行的elasticsearch_exporter,用systemd启动后一直报错 failed to fetch and decode node stats 在网上翻了大半年,竟然都无解!这种报错,很明显就是你的ES密码…...
Git 使用方法
简介 Git常用命令 Git 全局设置 获取Git 仓库 方法二用的比较多 将仓库链接复制 在 git base here ----> git clone 仓库链接 工作区、暂存区、版本库 Git 工作区中文件中的状态 本地仓库的操作 远程仓库操作 git pull 将代码推送到远程仓库 1. git add 文件名 ---放…...
生产环境升级mysql流程及配置主从服务
之前写到过mysql升级8.4的文章, 因此不再介绍mysql的安装过程 避免服务器安装多个mysql引起冲突的安装方法_安装两个mysql会冲突吗-CSDN博客 生产环境升级mysql8.4.x流程 安装mysql 参考之前文章: 避免服务器安装多个mysql引起冲突的安装方法_安装两个mysql会冲突吗-CSDN博客…...
论软件体系结构的演化
摘要 2022年3月,我加入了公司的新智慧公交平台项目研发团队,并担任系统架构师角色,负责系统整体架构的设计与评审。该项目采用了物联网三层架构模型,其中设备接入层和网络交互层基于公司的中台战略,我们有效复…...
【go入门】常量
目录 定义枚举iota思考题 定义 go语言常量的定义和其他语言类似,常量中的数据类型只能是布尔型,数字型(整型、浮点型、复数)和字符串型 常量的定义方式和变量一样,只不过变量定义使用 var 关键字,而常量定…...
2.1 HuggingFists系统架构(二)
部署架构 上图为HuggingFists的部署架构。从架构图可知,HuggingFists主要分为服务器(Server)、计算节点(Node)以及数据库(Storage)三部分。这三部分可以分别部署在不同的机器上,以满足系统的性能需求。为部署方便,HuggingFists社区版将这三部…...
工具类:JWT
工具类:JWT 依赖JwtUtil.java 依赖 <!-- 创建、解析 和 验证JSON Web Tokens (JWT)--><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependenc…...
王道-计网
2 采用滑动窗口机制对两个相邻结点A(发送方)和B(接收方)的通信过程进行流量控制。假定帧的序号长度为3比特,发送窗口与接收窗口的大小均为7,当A发送了编号为0、1、2、3这4个帧后,而B接收了这4个帧,但仅应答了0、1两个帧,A继续发送4、5两个帧,且这两个帧已进入B的接收…...
广东网页制作网站/论坛如何做seo
裴波那切数在百度百科的定义是: 斐波那契数,亦称之为斐波那契数列(意大利语: Successione di Fibonacci),又称黄金分割数列、费波那西数列、费波拿契数、费氏数列,指的是这样一个数列:0、1、1、…...
地方网站不让做吗/智慧营销系统平台
其它说明:windows2000下IIS安装和Windows2003下IIS安装可以参照该教程,大同小异 安装步骤: 1、插入windows xp安装光盘,打开控制面板,然后打开其中的“添加/删除程序” 2、在添加或删除程序窗口左边点击“添加…...
网站国内空间和国外空间/赣州是哪个省
因为Sublime Text并不是需要安装,所以缺少Ubuntu桌面运行的一些基本配置,比如不能将它加入桌面侧边的启动器。 而Ubuntu上也没有快捷方式的说法,而通过软件中心安装的软件就有图标,并能加入到启动器上,这是因为它们有一…...
长沙公司网站建设/方象科技的企业愿景
理解TextView三部曲(三):倔强的StrokeTextView(我无论如何都要展示出来!而且要美美的!) 上一篇我们让StrokeTextView支持padding描边,如果有同学没有看过或者对上一篇内容有遗忘的&…...
wordpress 重复点赞/专业整站优化
经过两个小时的奋战,终于将博客园服务器抢救过来。问题的原因已经找到。等会我会写文章描述这次抢救过程。 这两天博客园服务器的不稳定,给大家带来了很大的麻烦,在这里博客园向大家表示深深的歉意!希望得到你的谅解!...
天津网站建设业务/广州网站建设公司
1)Chromely Chromely 是一个轻量级的类似 Electron.NET的框架,服务于 .NET/.NET Core 开发者 https://github.com/chromelyapps/Chromely...