带你读《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

相关文章
|
9月前
|
设计模式 算法 搜索推荐
带你读《2022技术人的百宝黑皮书》——谈一谈凑单页的那些优雅设计(7)
带你读《2022技术人的百宝黑皮书》——谈一谈凑单页的那些优雅设计(7)
带你读《2022技术人的百宝黑皮书》——谈一谈凑单页的那些优雅设计(7)
|
9月前
带你读《2022技术人的百宝黑皮书》——谈一谈凑单页的那些优雅设计(5)
带你读《2022技术人的百宝黑皮书》——谈一谈凑单页的那些优雅设计(5)
|
9月前
带你读《2022技术人的百宝黑皮书》——谈一谈凑单页的那些优雅设计(6)
带你读《2022技术人的百宝黑皮书》——谈一谈凑单页的那些优雅设计(6)
|
9月前
|
存储 缓存 前端开发
带你读《2022技术人的百宝黑皮书》——谈一谈凑单页的那些优雅设计(8)
带你读《2022技术人的百宝黑皮书》——谈一谈凑单页的那些优雅设计(8)
|
9月前
|
设计模式 双11 索引
带你读《2022技术人的百宝黑皮书》——谈一谈凑单页的那些优雅设计(11)
带你读《2022技术人的百宝黑皮书》——谈一谈凑单页的那些优雅设计(11)
|
9月前
|
设计模式
带你读《2022技术人的百宝黑皮书》——谈一谈凑单页的那些优雅设计(4)
带你读《2022技术人的百宝黑皮书》——谈一谈凑单页的那些优雅设计(4)
带你读《2022技术人的百宝黑皮书》——谈一谈凑单页的那些优雅设计(4)
|
9月前
|
缓存 NoSQL Redis
带你读《2022技术人的百宝黑皮书》——谈一谈凑单页的那些优雅设计(1)
带你读《2022技术人的百宝黑皮书》——谈一谈凑单页的那些优雅设计(1)
带你读《2022技术人的百宝黑皮书》——谈一谈凑单页的那些优雅设计(1)
|
9月前
|
前端开发
带你读《2022技术人的百宝黑皮书》——谈一谈凑单页的那些优雅设计(10)
带你读《2022技术人的百宝黑皮书》——谈一谈凑单页的那些优雅设计(10)
|
9月前
|
前端开发
带你读《2022技术人的百宝黑皮书》——谈一谈凑单页的那些优雅设计(9)
带你读《2022技术人的百宝黑皮书》——谈一谈凑单页的那些优雅设计(9)
|
9月前
|
缓存 NoSQL fastjson
带你读《2022技术人的百宝黑皮书》——谈一谈凑单页的那些优雅设计(2)
带你读《2022技术人的百宝黑皮书》——谈一谈凑单页的那些优雅设计(2)