ARTS - 第一周

173 阅读4分钟

ARTS 源于极客时间《左耳听风》专栏组织的一个学习打卡活动,四个字母对应着四个行动准则:

  • Algorithm:每周至少做一个 leetcode 的算法题
  • Review:阅读并点评至少一篇英文技术文章
  • Tip:学习至少一个技术技巧
  • Share:分享一篇有观点和思考的技术文章

Review

阅读并点评至少一篇英文技术文章

Pattern: API Gateway / Backends for Frontends

API Gateway 的背景

该文章介绍了微服务架构设计中的一个重要模式 —— API 网关模式,文章先从一个例子出发,如何设计亚马逊的的产品详情页,一个详情页需要调用多个后端服务,面临的问题包括:

  • 客户端需要对多个服务发起多个请求,整体的传输效率较低,特别是手机端
  • 后端实例的地址可能会动态变化
  • 服务的划分也有可能是变化的,需要对客户端隐藏
  • 多终端场景下,不同的终端需要不同类型的数据格式
  • 有些后端服务使用的协议可能对 Web 是不友好的

解决方案

通过实现一个 API 网关来作为所有客户端请求的入口,网关收到请求后可以简单地代理/路由到某个服务,或者请求多个服务然后聚合他们的返回结果,这些处理对客户端都是透明的。

可以为不同的客户端提供不同的适配逻辑,还可以承担安全控制的职责。

P.S: 笔者曾经就遇到过,公司大规模采用的 Web 框架在 API 鉴权逻辑上被发现存在安全漏洞,需要逐个服务修复,修复成本很高,如果之前采用 API 网关模式,把鉴权逻辑交给网关来做的话,我们就只需要修复网关这一个地方了,整体的服务质量更为可控,这也是“权力集中”的好处之一。

还有一种变种形式,一种客户端对应一个 API 网关。

好处

  • 微服务的划分和各个实例的地址对客户端完全透明
  • 为不同类型的客户端提供最佳的 API
  • 减少客户端的网络请求数,提升传输效率,简化客户端的请求逻辑
  • 后端内部的服务可以自由选择协议,网关会对其进行转化,最终暴露为 Web 友好的 API

Tip

学习至少一个技术技巧

  • 开始在个人项目里实践 Gradle:

    • 相比较 Maven,配置文件可读性更强,语法更优雅,可定制性也更强
    • 增量构建的概念也更先进,提高构建效率
  • 进一步熟悉 jOOQ:

    • SQL 函数的使用,e.g: DSL.sum()
    • 构建 DSL 的关键,了解 Condition
  • Java Time API 最佳实践:
    Java 8 Time API 的优越性就不用赘述了,不过很多框架对 java 8 Time API 的原生支持不是很好,比如 MyBatisJackson 需要引入额外的插件才能支持 LocalDateTime 字段的(反)序列化。
    直接在 Web 层的 VO 和 持久层的 PO 声明 LocalDateTime 也许不是最好的选择,可以在 VO 和 PO 里使用旧的时间类型,当有操作时间/日期的需求时,再在 Service 层里将其转成 LocalDateTime, 享受其 API 的灵活性。
    发现 java.sql.TimestampLocalDateTime 互转十分方便:
    timestamp.toLocalDateTime() & Timestamp.valueOf(localDateTime)
    建议把 VO 和 PO 里字段的时间类型都设置成 java.sql.Timestamp,虽然 Web 层的 VO 使用 java.sql 包下的类会显得有些奇怪。

  • vim tips:

    • 安装 nedtree 等插件,尝试用 vim 写代码
    • vim split & tabs

Share

分享一篇有观点和思考的技术文章

Algorithm

integer-to-roman

中规中矩,比较简单的题目

class Solution {
    public String intToRoman(int num) {
        StringBuilder sb = new StringBuilder();
        List<BaseNum> baseNums = initBaseNums();
        for (BaseNum b : baseNums) {
            while (num >= b.value) {
                num -= b.value;
                sb.append(b.roman);
            }
        }        
        return sb.toString();
    }

    private List<BaseNum> initBaseNums() {
        List<BaseNum> baseNums = new ArrayList<>(); 
        baseNums.add(new BaseNum(1000, "M"));
        baseNums.add(new BaseNum(900, "CM"));
        baseNums.add(new BaseNum(500, "D"));
        baseNums.add(new BaseNum(400, "CD"));
        baseNums.add(new BaseNum(100, "C"));
        baseNums.add(new BaseNum(90, "XC"));
        baseNums.add(new BaseNum(50, "L"));
        baseNums.add(new BaseNum(40, "XL"));
        baseNums.add(new BaseNum(10, "X"));
        baseNums.add(new BaseNum(9, "IX"));
        baseNums.add(new BaseNum(5, "V"));
        baseNums.add(new BaseNum(4, "IV"));
        baseNums.add(new BaseNum(1, "I"));
        return baseNums;
    }

    private static class BaseNum {
        int value;
        String roman;

        public BaseNum(int value, String roman) {
            this.value = value;
            this.roman = roman;
        }
    }
}