带你读《2022技术人的百宝黑皮书》——谈一谈凑单页的那些优雅设计(3)

简介: 带你读《2022技术人的百宝黑皮书》——谈一谈凑单页的那些优雅设计(3)

带你读《2022技术人的百宝黑皮书》——谈一谈凑单页的那些优雅设计(2)https://developer.aliyun.com/article/1338385?groupCode=taobaotech

以上的代码相对比较完美了,却忽略了一个细节点,如果多台机器的本地缓存同时失效,恰好redis的可更新时间失    效了,这时就会有多个请求并发打到下游(由于凑单有本地缓存兜底,并发打到下游的个数非常有限,基本可以忽略)。但遇到问题就需要去解决,追求完美代码。我做了如下的改造:

private List<V> getCenter(String key, Callable<List<V>> loader, Function<String, List<V>> parse) throws Exception {
String updateKey = getUpdateKey(key); String value = centerCache.get(key);
boolean blankValue = StringUtils.isBlank(value);
List<V> cache = blankValue ? Collections.emptyList() : parse.apply(value);
// 如果抢不到锁,并且value没有过期
if (!centerCache.setNx(updateKey, currentTime) && !blankValue) { return cache;
}
centerCache.set(updateKey, currentTime, cacheUpdateSecond);
// 使用异步线程去更新value
CompletableFuture.runAsync(() -> updateCache(key, loader)); return cache;
}
private void updateCache(String key, Callable<List<V>> loader) { List<V> newCache = loader.call();
if (CollectionUtils.isNotEmpty(newCache)) {
centerCache.set(key, JSON.toJSONString(newCache), cacheExpireSecond);
}
}


 

本方案使用分布式锁 + 异步线程的方式来处理更新。只会有一个请求抢到更新锁,并发情况下,其他请求在可更新时间段内还是返回老数据。由于redis封装的方法中并没有抢锁后同时设置过期时间的原子性操作,我这里用了先抢    锁,再赋值过期时间的方式,在极端场景下可能会出现死锁的情况,就是刚好抢到了锁,然后机器出现异常宕机, 导致过期时间没有赋值上去,就会出现永远无法更新的情况。这种情况虽然极端,但还是要解,以下是我能想到的两个方案,我选择了第二种方式:

  1. 通过使用lua脚本将两步操作合成一个原子性操作
  2. image.png
  3. 利用value的过期时间来解该死锁问题

P.S. 一些从ThreadLocal中拿的通用信息,在使用异步线程处理的时候是拿不到的,得重新赋值

 

凑单核心处理流程设计

 


凑单本身是没有自己的数据源的,都是从其他服务读取,做各种加工后展示。这样的代码是最好写的,也是最难写的。就好比最简单的组装商品信息,一般的代码都会这么写:

 

  // 获取推荐商品
List<Map<String, String>> summaryItemList = recommend();
  List<ItemShow> itemShowList = summaryItemList.stream().map(v -> {
  ItemShow itemShow = new ItemShow();
// 设置商品基本信息
  itemShow.setItemId(NumberUtils.createLong(v.get("itemId")));
  itemShow.setItemImg(v.get("pic"));
  // 获取利益点
  GuideInfoDTO guideInfoDTO = new GuideInfoDTO();
  AtmosphereResult<Map<Long, List<AtmosphereFullDTO>>> atmosphereResult = guideAtmo-
  sphereClient
  .extract(guideInfoDTO, "gather", "item");
  List<IconText> iconTexts = parseAtmosphere(atmosphereResult);
  itemShow.setItemBenefits(iconTexts);
  // 预售处理
  String preSalePrice = getPreSale(v);
  if (Objects.nonNull(preSalePrice)) {
  itemShow.setItemPrice(preSalePrice); 18 }
 // ......
  return itemShow;
  }).collect(Collectors.toList());

 


能快速写好代码并投入使用,但代码有点杂乱无章,对代码要求比较高的开发者可能会做如下的改进

 

  // 获取推荐商品
  List<Map<String, String>> summaryItemList = recommend();
  List<ItemShow> itemShowList = summaryItemList.stream().map(v -> {
  ItemShow itemShow = new ItemShow();
  // 设置商品基本信息
  buildCommon(itemShow, v);
  // 获取利益点
  buildAtmosphere(itemShow, v);
  // 预售处理
  buildPreSale(itemShow, v); 11 // ......
  return itemShow;
  }).collect(Collectors.toList());


一般这样的代码算是比较优质的处理了,但这仅仅是针对单个业务,如果遇到多个业务需要使用该组装后,最简单但就是需要判断是来自feeds流模块的请求商品组装不需要利益点,来自前N秒杀模块的不需要处理预售价格。

 

  // 获取推荐商品
  List<Map<String, String>> summaryItemList = recommend();
  List<ItemShow> itemShowList = summaryItemList.stream().map(v -> {
  ItemShow itemShow = new ItemShow();
  // 设置商品基本信息
  buildCommon(itemShow, v);
  // 获取利益点
  if (!Objects.equals(soluction, FiltrateFeedsSolution.class)) {
  buildAtmosphere(itemShow, v);
 }
  // 预售处理
  if (!Objects.equals(source, "seckill")) {
  buildPreSale(itemShow, v);
 }
 // ......
  return itemShow;
  }).collect(Collectors.toList());

 

带你读《2022技术人的百宝黑皮书》——谈一谈凑单页的那些优雅设计(4)https://developer.aliyun.com/article/1338378?groupCode=taobaotech

相关文章
|
前端开发 JavaScript 测试技术
|
数据采集 机器学习/深度学习 数据挖掘
python数据分析——数据预处理
数据预处理是数据分析过程中不可或缺的一环,它的目的是为了使原始数据更加规整、清晰,以便于后续的数据分析和建模工作。在Python数据分析中,数据预处理通常包括数据清洗、数据转换和数据特征工程等步骤。
779 0
|
SQL 分布式计算 Hadoop
大数据行业部署实战1:Hadoop伪分布式部署
大数据行业部署实战1:Hadoop伪分布式部署
1013 0
|
机器学习/深度学习 存储 编解码
RT-DETR改进策略【Neck】| ArXiv 2023,基于U - Net v2中的的高效特征融合模块:SDI(Semantics and Detail Infusion)
RT-DETR改进策略【Neck】| ArXiv 2023,基于U - Net v2中的的高效特征融合模块:SDI(Semantics and Detail Infusion)
485 16
RT-DETR改进策略【Neck】| ArXiv 2023,基于U - Net v2中的的高效特征融合模块:SDI(Semantics and Detail Infusion)
|
机器人
RPA如何影响就业市场?
【8月更文挑战第4天】RPA如何影响就业市场?
426 7
|
前端开发 JavaScript 数据库
在 React 项目中 Editable Table 的实现
由于我们是把 state 存放在父组件的,每次请求会造成 table 进行 render 一遍,如果再加入 loading 等状态,render 次数会更多。Table 组件默认情况下没有对 rerender 行为做优化,父组件更新,如果 columns 中的提供了自定义 render 方法, 对应的每个 Cell 都会重新 render 。 针对这种情况我们就需要进行优化,根据 shouldCellUpdate 来自定义渲染时机。 那么每个 Cell 的渲染时机应该是: 1. FormItem 增删位置变动时 2. 该 Cell 消费的对应 tableOptions 变动时 第一种情况很好判
511 1
|
JavaScript 索引
js【详解】arr.splice() 数组拼接
js【详解】arr.splice() 数组拼接
1184 0
|
运维 监控 Java
Spring Boot应用的性能监控与优化指南
Spring Boot应用的性能监控与优化指南
|
云计算
阿里云的认证有什么含金量?通过后有什么作用?
因此考证已经成为了很多打工人的首选方法,而对于从事云计算行业的人来说,阿里云的认证绝对是首选。
阿里云的认证有什么含金量?通过后有什么作用?
|
机器学习/深度学习 算法 大数据
【持续更新】阿里云大数据&AI开源项目合集
阿里云大数据&AI开源项目合集,了解全部阿里云AI&大数据开源项目,欢迎加入。