您的位置:首页 > 科技 > 能源 > 备案查询站长工具_找游戏的手游平台_今日重大新闻头条十条_自媒体十大平台

备案查询站长工具_找游戏的手游平台_今日重大新闻头条十条_自媒体十大平台

2024/12/23 2:27:21 来源:https://blog.csdn.net/CSDN_LiMingfly/article/details/144533254  浏览:    关键词:备案查询站长工具_找游戏的手游平台_今日重大新闻头条十条_自媒体十大平台
备案查询站长工具_找游戏的手游平台_今日重大新闻头条十条_自媒体十大平台

前言:
Redis 的基本缓存使用、Redis 加锁(Redisson 提供了很多锁的方式,这里我们会展示独占锁和无锁化的性能测试)。之后还有一个非常重要的场景是关于 Redis 的发布和订阅。
功能实现
代码目录结构
在这里插入图片描述
在这里插入图片描述
工程分为,app、domain、infrastructure、trigger 这样的四层,其实还有一个 types 通用层。

1:app;用于配置 Redis 的相关启动操作,鉴于 SpringBoot 以及 Redis 版本问题,这里我们自己来创建客户端,更好兼容版本的差异。同时也可以扩展一些额外的功能。
2:domain;是领域服务层,order 可以看做是一个订单域,包括订单的创建、支付、查询,都可以在这个领域实现。这个订单领域涉及的表就是前面章节,所压测的表 【压测】MySQL 连接池 c3p0、dbcp、druid、hikari(opens new window)
3:infrastructure;基础层是对 domain 依赖倒置的实现,具体到库的操作、缓存的操作,都是用这一层来实现。所以我们操作 Redis 的加锁、缓存,也会放到这里来处理。
4:trigger;触发器层,一般也有叫接口层。一般 http、rpc、job、mq、listener 都是在这一层进来使用。所以我们订阅 Redis 的消息也是放到这一层中处理。
5:types;工程中还有一个通用类型层,定义一些非专属 domain 领域内的公共资源。如配置一个自定义注解,来处理一些类的动态加载和组件开发。本章中我们就定义了一个这样的注解,来动态注入实例化的 Bean 对象。这块非常值得学习一下,因为它是解决此类场景的高级编码。
数据缓存
Redis 的大部分操作其实都是缓存数据,提高系统的 QPS,在插入、更新、删除(逻辑删)、查询的时候,依赖于 Redis 进行提速操作。
在这里插入图片描述
设置代码

 // 设置到缓存redissonService.setValue(orderId, orderEntity);testRedisTopic.publish(JSON.toJSONString(orderEntity));testRedisTopic02.publish(JSON.toJSONString(orderEntity));testRedisTopic03.publish(JSON.toJSONString(orderEntity));return orderId;}@Overridepublic OrderEntity queryOrder(String orderId) {OrderEntity orderEntity = redissonService.getValue(orderId);if (null == orderEntity) {UserOrderPO userOrderPO = userOrderDao.selectByOrderId(orderId);orderEntity = new OrderEntity();orderEntity.setUserName(userOrderPO.getUserName());orderEntity.setUserId(userOrderPO.getUserId());orderEntity.setUserMobile(userOrderPO.getUserMobile());orderEntity.setSku(userOrderPO.getSku());orderEntity.setSkuName(userOrderPO.getSkuName());orderEntity.setOrderId(userOrderPO.getOrderId());orderEntity.setQuantity(userOrderPO.getQuantity());orderEntity.setUnitPrice(userOrderPO.getUnitPrice());orderEntity.setDiscountAmount(userOrderPO.getDiscountAmount());orderEntity.setTax(userOrderPO.getTax());orderEntity.setTotalAmount(userOrderPO.getTotalAmount());orderEntity.setOrderDate(userOrderPO.getOrderDate());orderEntity.setOrderStatus(userOrderPO.getOrderStatus());orderEntity.setUuid(userOrderPO.getUuid());orderEntity.setDeviceVO(JSON.parseObject(userOrderPO.getExtData(), DeviceVO.class));// 设置到缓存redissonService.setValue(orderId, orderEntity);}return orderEntity;}

1:在插入数据的时候,可以一并切入缓存。如果有更新操作,可以考虑删除缓存,在查询更新。因为更新操作,很多时候都是部分字段更新,这个时候直接更新缓存容易不准。
最后就是查询时,用缓存拦截,避免所有的查询都打到库上。这样可以提高系统的 QPS。
加锁处理
使用 Redis 加分布式锁,也是分布式架构设计中非常常用的手段。常用于的场景包括;流程较长,耗时较多的个人开户、下单行为。也包括;一些资源竞争时加分布式锁,排队处理请求。但对于资源竞争的这类库存占用,如果加分布式锁是非常影响系统的吞吐量的,因为所有的用户都在等待上一个用户做完流程后释放锁的处理,相当于你即使系统是分布式的了,但这里的分布式锁依然会把性能拖慢。
在这里插入图片描述
独占锁模式
主要是为了避免用户在一次操作后,又反复申请。系统上避免重复受理,所以添加分布式锁的方式进行拦截。如果不加分布式锁,就会进入到库表中通过唯一的索引拦截,这样对数据库的压力就比较大。

   @Overridepublic String createOrderByLock(OrderAggregate orderAggregate) {RLock lock = redissonService.getLock("create_order_lock_".concat(orderAggregate.getSkuEntity().getSku()));try {lock.lock();long decrCount = redissonService.decr(orderAggregate.getSkuEntity().getSku());if (decrCount < 0) {return "已无库存[初始化的库存和使用库存,保持一致。orderService.initSkuCount(\"13811216\", 10000);]";}return createOrder(orderAggregate);} finally {lock.unlock();}}

分段锁模式

 @Overridepublic String createOrderByNoLock(OrderAggregate orderAggregate) {SKUEntity skuEntity = orderAggregate.getSkuEntity();// 模拟锁商品库存long decrCount = redissonService.decr(skuEntity.getSku());if (decrCount < 0) {return "已无库存[初始化的库存和使用库存,保持一致。orderService.initSkuCount(\"13811216\", 10000);]";}String lockKey = skuEntity.getSku().concat("_").concat(String.valueOf(decrCount));RLock lock = redissonService.getLock(lockKey);try {lock.lock();return createOrder(orderAggregate);} finally {lock.unlock();}}

分段或者自增滑块的锁方式进行处理,减少对同一个锁的等待,而是生成一堆的锁,让用户去使用。
发布/订阅
会涉及到如何向 Spring 动态注入已经实例化后的 Bean 对象。为什么会出现这个场景呢?

// 创建Redisson客户端
RedissonClient redisson = Redisson.create();// 获取RTopic对象
RTopic<String> topic = redisson.getTopic("myTopic");// 发布消息
topic.publish("Hello, Redisson!");// 添加监听器
topic.addListener(String.class, (channel, msg) -> {System.out.println("Received message: " + msg);
});// 关闭Redisson客户端
redisson.shutdown();

发布和订阅,是我们需要对同一个 Topic 进行发布和监听操作。但这个操作的代码是一种手动编码,但在我们实际使用中,如果所有的都是手动编码,一个是非常麻烦,再有一个是非常累人。
来个高级编码,通过自定义注解,来完成动态监听和将对象动态注入到 Spring 容器中,让需要注入的属性,可以被动态注入。

自定义注解

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
public @interface RedisTopic {String topic() default "";}

注解使用

@Slf4j
@Service
@RedisTopic(topic = "testRedisTopic02")
public class RedisTopicListener02 implements MessageListener<String> {@Overridepublic void onMessage(CharSequence channel, String msg) {log.info("02-监听消息(Redis 发布/订阅): {}", msg);}}

动态注入

// 添加监听
String[] beanNamesForType = applicationContext.getBeanNamesForType(MessageListener.class);
for (String beanName : beanNamesForType) {MessageListener bean = applicationContext.getBean(beanName, MessageListener.class);Class<?> beanClass = bean.getClass();if (beanClass.isAnnotationPresent(RedisTopic.class)) {RedisTopic redisTopic = beanClass.getAnnotation(RedisTopic.class);RTopic topic = redissonClient.getTopic(redisTopic.topic());topic.addListener(String.class, bean);// 动态创建 bean 对象,注入到 spring 容器,bean 的名称为 redisTopic,对象为 RTopicConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();beanFactory.registerSingleton(redisTopic.topic(), topic);}
}

使用对象


@Slf4j
@Repository
public class OrderRepository implements IOrderRepository {@Resourceprivate IRedisService redissonService;@Resourceprivate IUserOrderDao userOrderDao;@Resourceprivate RTopic testRedisTopic;@Resource(name = "testRedisTopic02")private RTopic testRedisTopic02;@Resource(name = "testRedisTopic03")private RTopic testRedisTopic03;@Overridepublic String createOrder(OrderAggregate orderAggregate) {// 省略...testRedisTopic02.publish(JSON.toJSONString(orderEntity));testRedisTopic03.publish(JSON.toJSONString(orderEntity));return orderId;}    
}    

功能测试

@Test
public void test_createOrder() throws InterruptedException {String sku = RandomStringUtils.randomNumeric(9);int count = 10000;orderService.initSkuCount(sku, count);for (int i = 0; i < count; i++) {threadPoolExecutor.execute(() -> {UserEntity userEntity = UserEntity.builder().userId("小傅哥").userName("xfg".concat(RandomStringUtils.randomNumeric(3))).userMobile("+86 13521408***").build();SKUEntity skuEntity = SKUEntity.builder().sku(sku).skuName("《手写MyBatis:渐进式源码实践》").quantity(1).unitPrice(BigDecimal.valueOf(128)).discountAmount(BigDecimal.valueOf(50)).tax(BigDecimal.ZERO).totalAmount(BigDecimal.valueOf(78)).build();DeviceVO deviceVO = DeviceVO.builder().ipv4("127.0.0.1").ipv6("2001:0db8:85a3:0000:0000:8a2e:0370:7334".getBytes()).machine("IPhone 14 Pro").location("shanghai").build();long threadBeginTime = System.currentTimeMillis(); // 记录线程开始时间// 耗时:4毫秒String orderId = orderService.createOrder(new OrderAggregate(userEntity, skuEntity, deviceVO));// 耗时:106毫秒String orderId = orderService.createOrderByLock(new OrderAggregate(userEntity, skuEntity, deviceVO));// 耗时:4毫秒String orderId = orderService.createOrderByNoLock(new OrderAggregate(userEntity, skuEntity, deviceVO));long took = System.currentTimeMillis() - threadBeginTime;totalExecutionTime.addAndGet(took); // 累加线程耗时log.info("写入完成 {} 耗时 {} (ms)", orderId, took / 1000);});}new Thread(() -> {while (true) {if (threadPoolExecutor.getActiveCount() == 0) {log.info("执行完毕,总耗时:{} (ms)", (totalExecutionTime.get() / 1000));log.info("执行完毕,总耗时:{}", "\r\033[31m" + (totalExecutionTime.get() / 1000) + "\033[0m");break;}try {Thread.sleep(350);} catch (InterruptedException e) {throw new RuntimeException(e);}}}).start();// 等待new CountDownLatch(1).await();
}

测试前,记得修改代码 count 值,代表这要初始化内存多少个容量。另外是环境记得先执行安装。
接下来,我们进入了压测环节。createOrder 不使用锁、createOrderByLock 使用独占锁、createOrderByNoLock 是分段锁,也可以当做无锁处理。
其他测试
读写锁、异步锁、信号量、队列、延迟队列的相关测试。

/*** 延迟队列场景应用;https://mp.weixin.qq.com/s/jJ0vxdeKXHiYZLrwDEBOcQ*/
@Test
public void test_getDelayedQueue() throws InterruptedException {RBlockingQueue<Object> blockingQueue = redissonService.getBlockingQueue("xfg-dev-tech-task");RDelayedQueue<Object> delayedQueue = redissonService.getDelayedQueue(blockingQueue);new Thread(() -> {try {while (true){Object take = blockingQueue.take();log.info("测试结果 {}", take);Thread.sleep(10);}} catch (InterruptedException e) {e.printStackTrace();}}).start();int i = 0;while (true){delayedQueue.offerAsync("测试" + ++i, 100L, TimeUnit.MILLISECONDS);Thread.sleep(1000L);}
}

好了 至此 Redis 缓存、加锁(独占/分段)、发布/订阅,常用特性的使用和高级编码操作 学习结束了 友友们 点点关注不迷路 老铁们!!!!!

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com