用 Redis Bitmap 实现 12306 余票模型
【本文Bilibili视频地址】
希望大家多多关注
火车票的售卖模式和正常的商品不同,其他商品的库存比较单一,而 12306 库存基于车次中的城市到城市。举个例子来说:
G1 车次从北京到上海虹桥,中间经过站有济南和南京南:
在售票时意味着可以购买以下几种组合
- 北京南—济南西
- 北京南—南京南
- 北京南—上海虹桥
- 济南西—南京南
【本文Bilibili视频地址】
希望大家多多关注
火车票的售卖模式和正常的商品不同,其他商品的库存比较单一,而 12306 库存基于车次中的城市到城市。举个例子来说:
G1 车次从北京到上海虹桥,中间经过站有济南和南京南:
在售票时意味着可以购买以下几种组合
我们首先对业务场景进行定义,如何快速的从 1000w 数据中找到 10 个数据作为中奖用户,重复100次,每次作为我们终将的名单,切每一个用户不能重复中奖。基于这个过程我们如何把这个过程高效的实现呢?
使用数据库是最直接的方法,但在处理大规模数据时,性能可能成为瓶颈。
1 | select id from User where status=0 order by rand() limit 10 |
使用Set数据结构来存储中奖用户,可以提高抽奖效率。具体实现如下:
1 | sadd usrlist uuid1 |
说明:
本篇文章主要讲解 ,从redis原理的角度了解一个 set 命令从redis client发出到 redis server端接收到客户端请求的时候,到底经历了哪些过程?
同样会附带了解下面几个问题
为了了解redis请求流程,首先先了解下redis的网络模型。redis 支持 4中网络模式, select、poll、epoll、kqueue ,其中epoll 模型我个人认为是应用最广泛的模型,所以本篇文章以epoll 模型为 demo 进行讲解。
Select 和 poll 模型的缺点:
Epoll 模型为了解决 Select ,Poll的两次轮训和每次都需要传入文件描述符的问题,对整体的结构做了一个新的优化,具体架构如下:
欢迎继续关注本系列文章,下面我们继续讲解下DDD在实战落地时候,会具体碰到哪些问题,以及解决的方式有哪些。
DDD 是一种思想,主要知道我们方向,具体如何做,需要我们根据业务场景具体问题具体分析。
关于充血模型和Spring注入的问题,你提到了两种解决方案,即使用ApplicationContextAware
获取容器中的对象和将依赖作为参数传入。这两种方式都是可行的,但有一些考虑事项:
选择哪种方式取决于具体的需求和项目架构。通常来说,推荐将依赖作为参数传入,因为它更符合领域对象的独立性原则,有助于代码的可测试性和清晰性。但在某些情况下,如果需要在领域对象内部动态获取依赖或与第三方库进行集成,使用ApplicationContextAware
也是一个有效的选择。
大聚合根的加载性能问题是在领域驱动设计 (DDD) 中常见的挑战之一。当一个聚合根包含大量关联实体或值对象,并且需要在应用程序中频繁加载和操作这些关联对象时,可能会导致性能下降。以下是一些解决大聚合根加载性能问题的策略和最佳实践:
在前文中,我从基础代码的角度探讨了如何运用领域驱动设计(DDD)来实现高内聚低耦合的代码。本篇文章将从项目架构的角度,继续探讨三层架构与DDD之间的演化过程,以及DDD如何优化架构的问题。
三层架构作为一种常见的软件架构模式,将应用程序分为展示层、业务逻辑层和数据访问层,具有以下优点:
然而,尽管三层架构有其优点,在处理复杂业务时,三层架构也可能面临一些问题。具体有:
具体具体示意如下图:
随着业务的不断复杂化,service层变得越来越庞大,服务之间的引用也变得越来越混乱,这为项目带来了风险和不确定性。
在2019年我初次接触到领域驱动设计(Domain-Driven Design,简称DDD)的概念。在我的探索中,我发现许多有关DDD的教程过于偏重于战略设计,充斥着许多晦涩难懂的概念,导致阅读起来相当艰难。有些教程往往只是解释了DDD的概念,而未深入探讨为何要采用这种方式以及这样做能带来哪些好处,这导致很多人在实践应用DDD时遇到了诸多难题。甚至有些人为了引入DDD而在项目中强制采用DDD架构,结果却意外增加了代码的复杂性,带来了一系列潜在的风险。
为了解决这一问题,我计划从代码的基础入手,详细讲解如何将DDD的理念应用于实际开发中,以便解答为何DDD能使我们的代码更加整洁的问题。今天,我们将着重讨论如何运用DDD的思想来组织我们的代码,从而实现”高内聚、低耦合”的开发目标。
在接下来的讨论中,我将与大家分享我在将DDD理念融入实际项目中的一些心得和体会,以及如何在现实项目中充分发挥DDD的优势。无论是战略设计还是战术实施,我都将尽可能以通俗易懂的方式进行解释,希望能够帮助大家更好地理解和应用DDD,从而在编码的道路上越走越远。
首先,让我们看一个电商系统中下单功能的代码示例:
1 | @Autowired |
首先,我们对这段代码的逻辑进行整理,共涉及5个步骤:
我们从这几个过程入手,根据业务的重要性,我们可以将它们划分为核心业务和非核心业务。显然,下单及其相关操作属于核心代码(步骤1、2、3)。与此相比,写日志、写入缓存以及发送Kafka消息则属于下单过程的非核心业务
1 | Semaphore semaphore = new Semaphore(nThread);//定义几个许可 |
线程池的在 Java并发中使用最多的一种手段,也是性能和易用性相对来说比较均衡的方式,下面我们就一起探索先线程池的原理。
对于线程池的使用,在这篇文章中就不过多的赘述,首先我们先看下线程池的分配线程的逻辑。
我们知道,在创建线程池的有 7 个核心的参数:
corePoolSize:核心线程数
maximumPoolSize:最大线程数
keepAliveTime:空闲线程存活时间
TimeUnit: 单位
workQueue:阻塞队列
ThreadFactory: 线程工厂
RejectedExecutionHandler: 拒绝策略
在这 7 个参数中,其中我们最重要的几个参数是 corePoolSize,maximumPoolSize,workQueue ,这三个参数来决定线程池主要的线程数和任务队列长度。
具体的流程图如下(图片来自网上,侵删):
在学习 DDD 架构前,一直觉得三层架构结构在业务复杂的场景会带来很多很多的问题,但是一直都处于模糊不清的形态,无法准确的定义。直到学习了DDD 的概念。
为了更好的学习 DDD ,我们总结一下三层架构在业务复杂的场景带来的问题,首先看下正常的项目依赖图
我们正常有 5 个模块,UI(application), Service,Repository,Entity 和 Common,每个层代表的含义,大家都非常清楚,这样会带来什么样的问题呢?