不想造轮子的程序员成为不了技术专家(一)
小马哥,一个不是在造轮子,就是在造轮子路上的程序员,追求优雅编码。写过博客,参与过开源项目,热衷于基础架构。
- CSDN 博客专家,掘金年度人气作者;
- 龙台的技术笔记,公众号 1w+ 读者;
- 开源 Hippo-4J 线程池框架,Github 2.4k+ star,码云 GVP 项目;
- Apache ShardingSphere Contributor。
星球介绍
做星球的初衷,希望通过商城项目,模拟出日常开发的不同难题,并给出合适的解决方案,帮助大家收获成长!
小马哥以为,星球还是要以 代码实战课 为中心,围绕以下五个点进行讲述:
1)DDD 开发模式
DDD 全称为(Domain-Driven Design,简称 DDD),领域驱动设计。
在互联网初期,对于一些简单的业务,只需要使用一个项目,编写多个业务逻辑就可以搞定,也就是最初的 单体项目阶段。
随着互联网的的发展,以及业务复杂度增长,分布式技术快速兴起,将单体项目根据业务拆分为多个服务,就到了 微服务时代。
但是,随着业务越来越复杂,当需要修改其中某项功能时,发现不太容易修改,因为改了这个功能后会 引起其它模块代码的级联反应。造成开发者业务开发周期增加,人力成本过高等问题。
DDD 模型可以很好的解决上述问题。所以,星球产出的商城系统会在 多个业务模块中使用 DDD 开发,梳理业务模型,帮助对 DDD 一知半解的开发者树立正确的开发思路。
2)商城业务
大部分同学在开发简单业务系统中,很难遇到有挑战性的技术难点,这也是导致技术进步缓慢的主要问题。
星球推出的 Mall4J 系统,参照商城类系统原型,推出 C 端用户、消息中台、订单、优惠券、支付 等业务模块,帮助大家梳理商城相关的业务;同时,也会在商城业务中使用到下述技术:
- 分布式锁;
- 分布式事务;
- 搜索技术;
- 微服务流量监控;
- 分库分表等。
3)场景实战
当业务系统持续开发过程中,会遇到 各式各样的场景问题,小马哥根据工作中遇到的和帮助他人已解决的,总结了以下几点,并把解决方案和代码实战放在 Mall4J 这个系统里。
- MySQL 百万级别数据,如何导出 Execl?
- 线上 Java 应用触发 OOM,如何第一时间通知到开发者?
- 如何解决雪花算法在集群环境下,生成 ID 重复问题?
- 如何通过 MyBatis 查询千万数据,并保证内存不溢出?
- MySQL 单表千万级数据,如何避免深分页引起的线上故障?
- MySQL 数据库表快速初始化千万数据到 ElasticSearch?
- ……
4)基础架构
每天面向业务编码,工作几年写的都是不变花样的 CRUD,开发者不知道如何对技术做提升?
相信这上面这段话是很多小伙伴的困扰,找不到一个好的方式 提升自己的基础架构能力。
为此,在 Mall4J 系统中,小马哥会带着大家造很多的“轮子”,包括不限于:规约、缓存、公共、分布式 ID、数据持久层、脱敏、日志、文档 API 等组件。
另外,之前和水友群沟通,发现大家对 Java Agent 概念不是很清楚,也没有在实际场景中使用过。
在基础架构部分,会根据实际的业务场景,写一个 中台业务个性化 Java Agent,它主要做两件事情:
- 统计调用中台接口的来源系统,比如说中台消息发送接口被用户、订单、支付等几个系统调用;
- 采集中台系统在不同的时间窗口下被调用的频率,可以精确到接口级别。
5)设计模式
为什么要学习设计模式?或者说设计模式有什么好处?
设计模式主要是为了 应对代码的复杂性,让其满足开闭原则,提高代码的扩展性。避免做功能迭代时,牵一发而动全身。
同时,设计模式也是看各种底层框架的敲门砖,如果不会设计模式,点不了几个类就会被绕晕过去。
以上种种,无不彰显设计模式对于开发者的重要性。在 Mall4J 系统里,会根据不同的业务场景带大家掌握常用的设计模式,比如:单例、Builder、策略、模板方法、装饰器、动态代理等。
代码示例
星球注重代码实战,这里放两段 Mall4J 中的真实代码,都是和大家工作中息息相关的技术。
1)分布式缓存封装
获取缓存时,防止缓存击穿、缓存穿透问题。
其中用到了分布式锁防止缓存击穿,并加上双重判断,最大可能性 减少流量请求数据库的次数;其次,使用布隆过滤器缓存空的结果,避免缓存穿透。
@Override
public <T> T safeGet(@NotBlank String key, Class<T> clazz, CacheLoader<T> cacheLoader, long timeout) {
T result = get(key, clazz);
if (!CacheUtil.isNullOrBlank(result)) {
return result;
}
// 判断布隆过滤器是否存在,存在返回空
if (cachePenetrationBloomFilter.contains(key)) {
return null;
}
RLock lock = redissonClient.getLock(CacheUtil.buildKey("distributed_lock_lock_get", key));
lock.lock();
try {
// 双重判定锁,减轻数据库访问压力
if (CacheUtil.isNullOrBlank(result = get(key, clazz))) {
// 通过 load 接口加载为空,触发缓存穿透条件,把 key 放入布隆过滤器
if (CacheUtil.isNullOrBlank(result = loadAndSet(key, cacheLoader, timeout))) {
cachePenetrationBloomFilter.add(key);
}
}
} finally {
lock.unlock();
}
return result;
}
2)封装策略模式
封装设计模式中的策略模式,规避不同业务定义多个选择器和策略接口。
定义抽象策略接口:
public interface AbstractExecuteStrategy<REQUEST, RESPONSE> {
/**
* 执行策略标识
*/
String mark();
/**
* 执行策略
*/
default void execute(REQUEST requestParam) {
}
/**
* 执行策略,带返回值
*/
default RESPONSE executeResp(REQUEST requestParam) {
return null;
}
}
定义抽象策略选择器:
public class AbstractStrategyChoose implements ApplicationListener<ApplicationInitializingEvent> {
/**
* 执行策略集合
*/
private final Map<String, AbstractExecuteStrategy> abstractExecuteStrategyMap = new HashMap<>();
/**
* 根据 mark 查询具体策略
*/
public AbstractExecuteStrategy choose(String mark) {
return Optional.ofNullable(abstractExecuteStrategyMap.get(mark)).orElseThrow(() -> new ServiceException(String.format("[%s] 策略未定义", mark)));
}
/**
* 根据 mark 查询具体策略并执行
*/
public <REQUEST> void chooseAndExecute(String mark, REQUEST requestParam) {
AbstractExecuteStrategy executeStrategy = choose(mark);
executeStrategy.execute(requestParam);
}
/**
* 根据 mark 查询具体策略并执行,带返回结果
*/
public <REQUEST, RESPONSE> RESPONSE chooseAndExecuteResp(String mark, REQUEST requestParam) {
AbstractExecuteStrategy executeStrategy = choose(mark);
return (RESPONSE) executeStrategy.executeResp(requestParam);
}
@Override
public void onApplicationEvent(ApplicationInitializingEvent event) {
Map<String, AbstractExecuteStrategy> actual = ApplicationContextHolder.getBeansOfType(AbstractExecuteStrategy.class);
actual.forEach((beanName, bean) -> {
AbstractExecuteStrategy beanExist = abstractExecuteStrategyMap.get(bean.mark());
if (beanExist != null) {
throw new ServiceException(String.format("[%s] Duplicate execution policy", bean.mark()));
}
abstractExecuteStrategyMap.put(bean.mark(), bean);
});
}
}
3)项目结构
文章转自公众号:龙台的技术笔记