Ethan's Blog


  • Home

  • Archives

  • Tags

  • Search

从容器到容器云:谈谈 Kubernetes 的本质

Posted on 2021-05-25

容器的核心知识

一个容器,实际上是一个由 Linux Namespace、Linux Cgroups 和 rootfs 三种技术构建出来的进程的隔离环境。从这个结构中我们不难看出,一个正在运行的 Linux 容器,其实可以被“一分为二”地看待:

  1. 一组联合挂载在 /var/lib/docker/aufs/mnt 上的 rootfs,这一部分我们称为容器镜像(Container Image),是容器的静态视图;
  2. 一个由 Namespace + Cgroups 构成的隔离环境,这一部分我们称为容器运行时(Container Runtime),是容器的动态视图;

更进一步地说,作为一名开发者,我并不关心容器运行时的差异。因为,在整个“开发 -> 测试 -> 发布”的流程中,真正承载着容器信息进行传递的,是容器镜像,而不是容器运行时。这个重要假设,正是容器技术圈在 Docker 项目成功后不久,就迅速走向了“容器编排”这个“上层建筑”的主要原因:作为一家云服务商或者基础设施提供商,我只要能够将用户提交的 Docker 镜像以容器的方式运行起来,就能成为这个非常热闹的容器生态图上的一个承载点,从而将整个容器技术栈上的价值,沉淀在我的这个节点上。更重要的是,只要从我这个承载点向 Docker 镜像制作者和使用者方向回溯,整条路径上的各个服务节点,比如 CI/CD、监控、安全、网络、存储等等,都有我可以发挥和盈利的余地。这个逻辑,正是所有云计算提供商如此热衷于容器技术的重要原因:通过容器镜像,它们可以和潜在用户(即,开发者)直接关联起来。

Read more »

设计支持自定义规则的灰度发布组件

Posted on 2021-03-31

需求场景

最初,公共服务平台提供的是,基于某个开源 RPC 框架的 RPC 格式的接口。在上线一段时间后,我们发现这个开源 RPC 框架的 Bug 很多,多次因为框架本身的 Bug,导致整个公共服务平台的接口不可用,但又因为团队成员对框架源码不熟悉,并且框架的代码质量本身也不高,排查、修复起来花费了很长时间,影响面非常大。所以,我们评估下来,觉着这个框架的可靠性不够,维护成本、二次开发成本都太高,最终决定替换掉它。对于引入新的框架,我们的要求是成熟、简单,并且与我们现有的技术栈(Spring)相吻合。这样,即便出了问题,我们也能利用之前积累的知识、经验来快速解决。所以,我们决定直接使用 Spring 框架来提供 RESTful 格式的远程接口。

把 RPC 接口替换成 RESTful 接口,除了需要修改公共服务平台的代码之外,调用方的接口调用代码也要做相应的修改。除此之外,对于公共服务平台的代码,尽管我们只是改动接口暴露方式,对业务代码基本上没有改动,但是,我们也并不能保证就完全不出问题。所以,为了保险起见,我们希望灰度替换掉老的 RPC 服务,而不是一刀切,在某个时间点上,让所有的调用方一下子都变成调用新的 RESTful 接口。因为替换的过程是灰度的,所以老的 RPC 服务不能下线,同时还要部署另外一套新的 RESTful 服务。我们先让业务不是很重要、流量不大的某个调用方,替换成调用新的 RESTful 接口。经过这个调用方一段时间的验证之后,如果新的 RESTful 接口没有问题,我们再逐步让其他调用方,替换成调用新的 RESTful 接口。

但是,如果万一中途出现问题,我们就需要将调用方的代码回滚,再重新部署,这就会导致调用方一段时间内服务不可用。而且,如果新的代码还包含调用方自身新的业务代码,简单通过 Git 回滚代码重新部署,会导致新的业务代码也被回滚。所以,为了避免这种情况的发生,我们就得手动将调用新的 RESTful 接口的代码删除,再改回为调用老的 RPC 接口;除此之外,为了不影响调用方本身业务的开发进度,调用方基于回滚之后的老代码,来做新功能开发,那替换成新的 RESTful 接口的那部分代码,要想再重新 merge 回去就比较难了,有可能会出现代码冲突,需要再重新开发。

Read more »

设计通用的接口幂等框架

Posted on 2021-03-31

需求场景

调用方访问公共服务平台的接口,会有三种可能的结果:成功、失败和超时。前两种结果非常明确,调用方可以自己决定收到结果之后如何处理。结果为“成功”,万事大吉;结果为“失败”,一般情况下,调用方会将失败的结果,反馈给用户(移动端 App),让用户自行决定是否重试。但是,当接口请求超时时,处理起来就没那么容易了。有可能业务逻辑已经执行成功了,只是公共服务平台返回结果给调用方的时候超时了,但也有可能业务逻辑没有执行成功,比如,因为数据库当时存在集中写入,导致部分数据写入超时。总之,超时对应的执行结果是未决的。

如果接口只包含查询、删除、更新这些操作,那接口天然是幂等的。所以,超时之后,重新再执行一次,也没有任何副作用。不过,这里有两点需要特殊说明一下。

删除操作需要当心 ABA 问题。删除操作超时了,又触发一次删除,但在这次删除之前,又有一次新的插入。后一次删除操作删除了新插入的数据,而新插入的数据本不应该删除。不过,大部分业务都可以容忍 ABA 问题。对于少数不能容忍的业务场景,我们可以针对性的特殊处理。除此之外,细究起来,update x = x + delta 这样格式的更新操作并非幂等,只有 update x = y 这样格式的更新操作才是幂等的。不过,后者也存在跟删除同样的 ABA 问题。

Read more »

设计支持各种算法的限流框架

Posted on 2021-03-31

项目背景

公司成立初期,团队人少。公司集中精力开发一个金融理财产品(我们把这个项目叫做 X 项目)。整个项目只做了简单的前后端分离,后端的所有代码都在一个 GitHub 仓库中,整个后端作为一个应用来部署,没有划分微服务。遇到了行业风口,公司发展得不错,公司开始招更多人,开发更多的金融产品,比如专注房贷的理财产品、专注供应链的产品、专注消费贷的借款端产品等等。在产品形态上,每个金融产品都做成了独立的 App。对于不同的金融产品,尽管移动端长得不一样,但是后端的很多功能、代码都是可以复用的。为了快速上线,针对每个应用,公司都成立一个新的团队,然后拷贝 X 项目的代码,在此基础之上修改、添加新的功能。

这样成立新团队,拷贝老代码,改改就能上线一个新产品的开发模式,在一开始很受欢迎。产品上线快,也给公司赢得了竞争上的优势。但时间一长,这样的开发模式暴露出来的问题就越来越多了。而且随着公司的发展,公司也过了急速扩张期,人招得太多,公司开始考虑研发效率问题了。因为所有的项目的代码都是从 X 项目拷贝来的,多个团队同时维护相似的代码,显然是重复劳动,协作起来也非常麻烦。任何团队发现代码的 bug,都要同步到其他团队做相同的修改。而且,各个团队对代码独立迭代,改得面目全非,即便要添加一个通用的功能,每个团队也都要基于自己的代码再重复开发。

除此之外,公司成立初期,各个方面条件有限,只能招到开发水平一般的员工,而且追求快速上线,所以,X 项目的代码质量很差,结构混乱、命名不规范、到处是临时解决方案、埋了很多坑,在烂代码之上不停地堆砌烂代码,时间长了,代码的可读性越来越差、维护成本越来越高,甚至高过了重新开发的成本。

Read more »

MyBatis

Posted on 2021-03-31

MyBatis 和 ORM 框架介绍

MyBatis 是一个 ORM(Object-Relational Mapping)框架。ORM 框架主要是根据类和数据库表之间的映射关系,帮助程序员自动实现对象与数据库中数据之间的互相转化。说得更具体点就是,ORM 负责将程序中的对象存储到数据库中、将数据库中的数据转化为程序中的对象。实际上,Java 中的 ORM 框架有很多,除了刚刚提到的 MyBatis 之外,还有 Hibernate、TopLink 等。

如果用一句话来总结框架作用的话,那就是简化开发。MyBatis 框架也不例外,它简化的是数据库方面的开发。因为 MyBatis 依赖 JDBC 驱动,所以,在项目中使用 MyBatis,除了需要引入 MyBatis 框架本身(mybatis.jar)之外,还需要引入 JDBC 驱动(比如,访问 MySQL 的 JDBC 驱动实现类库 mysql-connector-java.jar)。将两个 jar 包引入项目之后,我们就可以开始编程了。使用 MyBatis 来访问数据库中用户信息的代码如下所示:

Read more »
1…131415…55
necusjz

necusjz

271 posts
16 tags
© 2016 - 2026 necusjz
Powered by Hexo
Theme - NexT.Mist