随着业务发展,软件本身也需要不断进行迭代以适应业务变换。

我们可以说,软件存在的价值就是映射真实世界,解决业务问题,从而创造更多价值,为大众提供更加便利的服务。

面对复杂业务场景,我们往往通过不断增加业务分支,对原有的冰山一样的代码逻辑进行小心翼翼的修改,生怕因为自己的修改导致全局出现不可挽回的损失。

这种不断迭代的历史遗留代码,几十年前的《人月神话》已经下过一次定义–“焦油坑”。

我们也常常调侃,自己不是在写代码,而是在Ctrl C / V, 在代码的“shit mountain”山不断进行着业务逻辑的搬运,并时刻祈祷系统别出异常,从而成为背锅的那个人。

这种现实是否能够通过一定的方式/方法/技巧改善甚至说这种类型问题能否改善呢?

Read More

日常开发中,对于遗留系统代码逻辑的改造,通常不会粗暴地采用停机更新方式直接迁移到新业务逻辑,往往需要通过灰度方式逐步迁移到新逻辑,最后再把老业务逻辑下线。

这个过程中就涉及到新老业务逻辑并存的问题,而这个问题是需要研发同学去通过工具、编码、配置等方式实现灰度策略,制定灰度计划,并付诸实施,保障系统能够平稳地从老业务逻辑迁移至新业务逻辑。

通常情况下,如果是http接口灰度,我们可以利用网关的特性,如nginx自身的能力,根据cookie中传递的唯一标识进行百分比分流,通过免开发的方式进行灰度。

但在实际开发中,我们面对的系统不仅仅是网关类系统,大部分情况下,分布式系统开发场景下面对的是RPC类接口或者异步消息逻辑,对于这类型代码逻辑的灰度就需要研发同学通过编码方式,细粒度的进行灰度。

本文中,我们就后者展开代码级别的讨论和分享,向读者介绍笔者在日常开发中,面对接口级别的灰度场景是如何通过编码实现具体的灰度策略的,本文权当抛砖引玉,为读者全景展示​后端研发套路之灰度场景下的瑞士军刀具体是如何使用的。在今后的开发工作中如果你遇到类似场景,可以参考笔者思路,开发适合自己业务的灰度套路。

Read More

在之前的一篇文章 手写JDK组件之阻塞队列BlockedQueue 中 ,我们模仿Java的ArrayBlockingQueue实现了一个阻塞队列。并通过该案例对阻塞队列的实现机制有了一个初步的认识。

实际上,Java中的阻塞队列用处还是比较广泛的,尤其是当我们不需要使用复杂的分布式消息队列,只是想要基于生产者-消费者模型,解耦业务逻辑,那么我们就可以借助内存队列实现。

这类型业务场景往往具备以下特点:

  • 消息发送量不多
  • 消息的安全性不高,可以容忍丢失
  • 不需要保证HA
  • 不需要提供完备的failover机制

举个例子,比如说当订单下单成功后我们想发送一个站内信,通知商户或者用户下单成功,仅仅作为一个提醒。类似这种场景,我们就可以借助内存队列实现。

基于我们刚提出的这个场景,编写一个demo进行验证。

Read More

笔者在两年前写过一篇RestTemplate使用相关的文章,地址: springboot中使用RestTemplate调用web服务小结

文章写作时SpringBoot版本尚在1.x徘徊,随着SpringBoot版本升级,有些用法在2.x版本中已经不适用。恰逢最近又用到了RestTemplate进行HTTP接口对接,
因此写作本文对最新的使用方法进行小结,方便后续参考,也希望能够帮到读者更好的使用RestTemplate在2.x的SpringBoot中进行HTTP接口的调用。

对于Get方式请求,2.x与1.x是兼容的,因此可以直接阅读上文中提到的链接,本文就不再重复赘述。

Read More

本文我们重点讲解一下Stream中收集器Collector的主要使用方式。

收集器

收集器(Collector)的作用为:将流中元素累积成一个结果作用于终端操作collect()上

关于收集器,我们主要关注:

collect()(操作实现Collector接口的集合)、

Collector(接口)

Collectors(工具类)

日常开发中,我们使用最多的是预定义收集器功能(Collectors)

它的作用主要是:

将流元素规约和汇总为一个值

将流元素分组

将流元素分区

Read More

本文是Java8函数式编程的最后一个章节,到此我们的Stream相关的讲解就暂时告一段落。

本文中我将带领读者朋友一起学习一下Stream高级编程相关的知识。

规约与汇总

Stream操作中有两个相对高阶的概念,分别为规约和汇总。

规约(reduce)

将Stream流中元素转换成一个值

汇总(collect)

将Stream流中的元素转换成一个容器,如Map 、List 、 Set

上文我们已经讲过了汇总操作Collect,此处我们重点讲讲规约操作(reduce)

Read More

上文中主要学习了Stream编程中的中间操作,本文我们接着分析终端操作,它分为短路和非短路操作。

我们提前明确一个原则:一个流一旦经过终端操作,就不能进行后续操作了。

allMatch: 检测所有元素是否满足断言,如果都满足返回true,有一个不满足返回false

@Test
public void allMatchTest() {
    boolean isMatch = list.stream()
            // 打印出部门商品名称即结束,参考打印结果
            .peek(sku -> System.out.println(sku.getSkuName()))
            // allMatch是短路操作
            .allMatch(sku -> sku.getTotalPrice() > 100);
    System.out.println(isMatch);
}

运行结果:

无人机
VR一体机
牛仔裤
false

Read More

我始终认为通过案例学习是最好的掌握一个技能的方式,因此我们还是通过之前文章中的案例,对Stream编程中涉及到的具体的方法进行详细的学习。

初始化数据

首先初始化一个List集合,用于后续讲解所用。

List<Sku> list;

@Before
public void init() {
    list = CartService.getCartSkuList();
}

我们仍旧使用之前的购物车数据

Read More

Fork me on GitHub