为 Istio 做了点微小的贡献

Istio 1.7.0

最近 Istio 1.7.0 发布了,这是一个令人非常激动的版本,因为这个版本解决了大量我们遇到的 Bug。

更激动的是,其中几个是我提交的代码修复的。

所以想在这把这几个 Bug 过一遍,顺便也介绍一下如何 debug Istio 代码。

Service Mesh 实践(十):HorizontalPodAutoscaler 支持自定义 Metrics

HorizontalPodAutoscaler + Cluster Autoscaler

Kubernetes 内置HorizontalPodAutoscaler可以很方便地根据 CPU 和内存做水平扩容缩容。

Kubernetes 在启动Pod和销毁Pod的时候对生命周期的控制也做的非常好,并不会对一个服务有太大的影响。只要把优雅启动和优雅关闭做好,并且把 Requests 和 Limits 配置到合适的数值,这一切会变得非常便捷。

另外再配合 Kubernetes Cluster Autoscaler,还可以实现整个集群机器的自动扩容缩容。

Cluster Autoscaler 可不是简单地根据机器剩余多少资源来判断是否要扩容缩容的。一般的机器自动扩容缩容都只是判断一下 CPU 用了多少,内存用了多少,如果剩余很多就尝试缩减机器。但 Cluster Autoscaler 的判断逻辑没这么简单,它还会和 Kubernetes 污点,污点容忍性,Pod亲和性,Node 亲和性相结合。例如你有一台机器有特殊的 Label,上面有一个Pod只能跑在有这个 Label 的机器上,占用的资源非常少。如果是别的程序,看到这台机器占用资源少,就直接把它干掉了,但实际上它不能去掉,因为上面的这个Pod只能在这台机器上跑。

Service Mesh 实践(九):为什么 Golang 更适合 Service Mesh

Service Mesh 架构下 API 会慢多少

不可否认,当你把一个单体程序拆成微服务后,单次请求的延迟也必定会增加。

然后再做 Service Mesh 改造后,每个服务外层还要再加一层 Proxy,那延迟又要增加了。

如果是一个复杂点的接口,内部产生 10 次远程调用是很常见的。Service Mesh 的核心 Sidecar 可以认为是一层反向代理。在这种情况下你数数一个请求经过了多少层反向代理?就算他们都是 C 写的,把极致性能放到首位,但单次反向代理的损耗在 1ms 左右还是很常见的。

也就是说单体程序做 Service Mesh 改造后,简单的 API 延迟增加 5ms 左右,复杂的 API 延迟增加 20ms 左右都是很正常的。

不仅如此,因为远程调用的网络因素,P99 也会比单体程序高很多,这也没办法,经过了多次网络调用后稳定性肯定是不如程序内部函数调用的。

Service Mesh 实践(八):分布式上下文

什么是分布式上下文

产品经理突然来了个需求,希望在一些事件打点的地方记录一下用户的各种信息:IP,User Agent,Accept Language 等。

但数据打点是分散在各个地方的,而且需求变化非常快,我们怎么样才可以随时随地拿到这些信息呢?

一种笨办法就是在所有函数调用的地方把相关信息一层层往下传,但你应该没见过这样的代码,实在是太麻烦了。

另外一种方式就是直接从一个静态方法里拿到当前的 Request 对象,并从中拿到各种信息。例如 C# 中是这样的:HttpContext.Current.Request,Python Flask 中是这样的:from flask import request。如果进去看看源码的话就会发现一般它们都是通过 Thread Local 来实现的。大部分的 HTTP Server 都是一个 Request 只由一个线程处理,所以这么做没什么问题。

而 Golang 的并发模型不一样,所以 Golang 无法这么做,Golang 需要显示地传播 Context。同样,用 Netty 做 HTTP Server 的话,并发模型也是完全不一样的,同样无法直接使用 Thread Local,只能显示传播 Context。还有基于 RxJava 实现的也无法这么做。

上下文本质上是一种隐式传播的信息,简化工作量。上面提到的这些都是程序内部的上下文,如果把这个隐式的信息传播扩展到微服务之间,那么它就变成分布式上下文了。

Service Mesh 实践(七):CI / CD 的变迁

CI / CD 需要做成什么样

  • CI:持续集成
  • CD:持续发布

CI / CD 是我们整个 Service Mesh 转型过程中最艰难的一个环节。

为什么最艰难?

CI / CD 本质上要解决的问题是:标准 + 自动化脚本 + 可视化 + 通知。

不同的公司情况不同,标准也会不同,因为在没有经验的情况下,很难制定出合理的标准。

没有标准也就不能有足够抽象好用的自动化脚本,为一个项目写一些脚本不难,写出抽象好用的脚本很难。

可视化和通知优先考虑开源产品,这两块在两年前也没什么可选的。

Service Mesh 实践(六):I18N Language

国际化

对于一个国际化的 App 来说,UI 和各种文案的国际化是必须的。

一般来说,开发只要根据用户的语言,然后调用对应方法,就可以拿到对应的文案了。

  String errorMessage = getErrorMessage("exception.HTTP_404", "en-US,en;q=0.5")

Service Mesh 实践(五):优雅启动和优雅关闭

传统做法

以前的微服务架构中优雅启动和优雅关闭其实不难,这些东西本身就是自己实现的。

启动后先打点流量预热一下,然后把实例注册一下就行了;要关闭的时候就先取消注册,等待一段时间尽量让所有请求结束后再关闭。

另外,传统微服务架构下的实例启动关闭的频率也远低于 Service Mesh,做了 Service Mesh 这个问题也会更突出一点。

那具体遇到了哪些问题又怎么解决呢?

Service Mesh 实践(四):从开源 Ingress 到自研 API Gateway

Kubernetes Ingress

Ingress 的相关概念可以直接看 Kubernetes 的文档,讲的很清楚了:

简单的来说,它和传统服务器架构中的负载均衡器是类似的,本质上就是把集群内部的服务暴露给集群外。

    internet
        |
   [ Ingress ]
   --|-----|--
   [ Services ]

这块技术方案非常多,要开发一个自己的 Kubernetes Ingress 也不难。看 Ingress Controllers 这篇文章,Ingress Controllers 的意思就是 Kubernetes Ingress 的具体实现。

两年前比较靠谱的方案主要是 Nginx Ingress 和 Istio Gateway,而现在技术方案已经非常多了。所有传统负载均衡厂商基本都为 Kubernetes 开发了 Ingress。

从严格的定义看,Istio Gateway 不能算是一个 Ingress Controller,因为它并不是根据 Kubernetes 里的Ingress资源来定义路由规则的。

Kubernetes Ingress 的理念是想做一层抽象,配置和实现解耦,所有的配置都是配置Ingress,而不需要关心具体的技术实现。

Istio Gateway 不用Ingress来配置,而是使用了自己的一套资源来配置,实际的功能上也比 Kubernetes Ingress 更丰富。因为技术实现脱离了 Kubernetes Ingress,所以我觉得严格的定义来看它不是一个 Ingress Controller。

Service Mesh 实践(三):数据库中间件

为什么要用数据库中间件

严格的来说,数据库中间件的选择和 Servic Mesh 无关,一般公司很早就应该上数据库中间件了。

数据库中间件一般有两个方案:SDK 模式或者 Proxy 模式。SDK 模式性能更好,Proxy 模式兼容性更好。

既然我们都在往 Service Mesh 方向走了,就是不想在业务代码去接 SDK 了,所以 Proxy 模式是我们优先选择的方案。虽然延迟会高一点,但还是那句话,不要只盯着单次调用的延时。

那数据库中间件到底解决了哪些问题?一般来说,利用数据库中间件可以实现如下功能:

  1. 读写分离
  2. 分库分表
  3. 故障转移
  4. 动态配置
  5. 统计分析
  6. SQL 防火墙
  7. 查询缓存

之前在大众点评做 Zebra 的时候,主要的技术方案就是 SDK 模式,因为整个大众点评是 Java 技术栈,没有多语言的问题,所以用 SDK 模式可以尽量提高性能。

Service Mesh 实践(二):Istio Mixer 模块的性能问题与替代方案

寄以厚望的 Mixer

Istio Architecture

Istio 的架构设计让人看着非常舒服,分工明确,扩展性强。

The overall architecture of an Istio-based application.

特别是 Mixer 模块,包含TelemetryPolicy Check两个模块,数据平面的 Envoy 会把所有请求异步发送给 Mixer 用作遥测,也会定时检查对应规则判断是否可以调用目标服务。

数据平面会把所有的请求上报到 Mixer,如果想要扩展任何功能,只需要扩展 Mixer 就行了。Istio 也把这一层做成了 CRDs,只需要创建对应的 CRDs 就可以了,而不需要对数据平面做任何改动。

按照 Istio 的理念,遥测和规则检查是属于控制平面的,从解耦的角度看,这样的设计很棒。

Service Mesh 实践(一):从 kops 到 ESK

kops 问题出在哪?

如果仅从易用性这个角度看,kops 是完胜 EKS 的,下面有一个官方的演示视频。

kops 演示视频

所有的操作都可以在命令行里完成,包括建集群,改集群。

每次变更都需要如下几个步骤:

  1. 编辑 yaml 配置。
  2. kops update cluster,这一步相当于dry run,你可以检查即将产生的变更。
  3. kops update cluster --yes,这一步才是把配置推到线上。
  4. kops rolling-update cluster,这里也是dry run,上一步虽然把配置推送到线上了,但有些机器的配置需要重建机器。
  5. kops rolling-update cluster --yes,最后它会按顺序一台台更新机器。

整个集群升级过程非常直观可控,再配合 Kubernetes 的PodDisruptionBudget,整个升级过程会变得非常安全。PodDisruptionBudget可以控制一个Deployment至少存活多少Pod,保证服务可用。否则如果一个Deployment的两个Pod凑巧在一台机器上,没有PodDisruptionBudget的话它就直接干掉了。

然而, kops 在 Master 节点的可靠性上出了点问题。

Service Mesh 实践(零):转型之路

原始社会

刚来创业公司的时候,被这简单粗暴的“架构”设计震惊了,准确的说,这里并没有什么设计。整个后端就一个单体程序,整体结构和大学里写的三层架构差不多,好吧,好歹还做了一些分层。设计模式就别想找到了,面向对象都很少见。

作为初创员工加入后,发现其实这种代码在人少时还挺高效的。本地搞点配置写个脚本,几秒内就可以把新代码发布到线上。

此时我们大致的架构是这样的:

architecture_1

但随着团队的扩张,2人、5人、10人,当 10 个人往同一个没有良好设计的项目里 commit 代码的时候,问题也慢慢地开始显现了。

代码冲突,发布依赖,线上雪崩… 是时候开始微服务化了。

2018年7月,我们的转型之路正式开始。

罗技 G29 与 Playseat 挑战者评测

想要一套不占地方的赛车座椅

很早之前,在咸鱼买了一套罗技 G29 赛车游戏方向盘,全新的一套2500以上,用1600的价格买到一套二手的,算是捡了个便宜。

卖家顺便送了一套简易的支架,放在自己的椅子前就可以玩。但是那坐姿,根本不是开车啊,简直是一种煎熬。

于是在网上寻觅赛车座椅,价格倒不是问题,可以接受,但这个体积实在是…

Playseat

利用深度学习相关技术为照片打标签

初试深度学习

公司内容由用户生成的话,不可避免地会遇到各种色情图片的问题。虽然我们有了举报系统,但是靠人工处理也是挺累的。

正好在半年前,Yahoo 发布了一个开源的基于 Caffe 的色情图片模型。https://github.com/yahoo/open_nsfw

这部分经验我会另起一篇文章来介绍一下。

而本文,主要是我在接触到深度学习相关知识后,发现可以用它来解决自己的一个痛点。

所以就有了此文和一些开源小代码。

折腾 Linux 4.9 BBR 拥堵控制算法

新年折腾 BBR

人是一种很奇怪的东西,以前家里科学上网速度很慢的时候,只要能打开就很满意了。

现在换了联通,Google 明明已经秒开了,却又开始不满足现状了。

恰巧最近看到了各种 KCP, BBR 技术的介绍,就想给自己的梯子折腾折腾了。

入坑上海联通宽带

电信糟糕的国际链路

记得在一年前的某一天,电信宽带的国际链路突然变龟速,国外的网站甚至打都打不开。本来去年年底就想换联通宽带的,但是看到电信有200M体验活动,而联通只有50M,所以又被电信骗过去了。

然后安慰自己,电信国际联路只是一时的问题,总有一天会变好的。然后我又被坑了一年…

公司的很多服务都部署在 Amazon 云服务上,每次查看简直崩溃。

 

电信为什么这么慢?目前最合理的解释就是电信大规模宽带提速(这个是好事),国内网络互访没有问题,但国际链路带宽却增长缓慢。再加上近几年海淘兴起,最终导致电信的国际链路拥堵不堪。

而联通为什么“快”?其实联通的国际链路只有电信的 1/3,但是联通宽带发展太慢,而这太慢反而成了优势。特别是在上海,几乎没人用联通,很多老小区也没有通联通宽带。最后的结果就是,联通的国际链路访问速度比电信快很多。

网友的数据分析:https://oldcat.me/post/Number-Never-Lie-ChinaTelecom-Shanghai-Network

 

期间多次向电信和工信部投诉,并没有什么用。曾经还加入过某个QQ群,大家实名制签名,律师帮大家一起起诉电信。同样并没有什么用。

那怎么办?我只能选择联通了。

不敢开车的老司机

不敢开车的老司机

常说车开多了胆子会越来越小,写代码也是。其实不是老司机胆子小了,而是新手无知无畏罢了。

最近一个很简单的功能,我做了2-3天,要是在我刚毕业的是时候把这个任务交给我,啪啪啪,不是我吹牛,2-3小时我就搞定了!

直接看产出的结果可能没觉得怎么样,甚至还会觉得这么做不对,但我觉得其中的思考过程还是非常有价值的,所以想在这记录下来。

RingBuffer 在 Puma 中的应用

什么是 RingBuffer

环形缓冲区:https://zh.wikipedia.org/wiki/環形緩衝區

维基百科的解释是:它是一种用于表示一个固定尺寸、头尾相连的缓冲区的数据结构,适合缓存数据流。

底层数据结构非常简单,一个固定长度的数组加一个写指针和一个读指针。

RingBuffer

只要像这张图一样,把这个数组辦弯,它就成了一个 RingBuffer。

那它到底有什么精妙的地方呢?

我最近做的项目正好要用到类似的设计思路,所以翻出了以前在点评写的 Puma 系统,看了看以前自己写的代码。顺便写个文章总结一下。

Python 中的 MySQL 数据库连接池

从 Java 到 Python

本文为我和同事的共同研究成果

当跨语言的时候,有些东西在一门语言中很常见,但到了另一门语言中可能会很少见。

例如 C# 中,经常会关注拆箱装箱,但到了 Java 中却发现,根本没人关注这个。

后来才知道,原来是因为 Java 中没有真泛型,就算放到泛型集合中,一样会装箱。既然不可避免,那也就没人去关注这块的性能影响了。

而 C# 中要是写出这样的代码,那你明天不用来上班了。

 

同样的场景发生在了学习 Python 的过程中。

什么?数据库连接竟然没有连接池!?

完全不可理解啊,Java 中不用连接池对性能影响挺大的。

Python 程序员是因为 Python 本来就慢,然后就自暴自弃了吗?

 

突然想到一个笑话

问:为什么 Python 程序员很少谈论内存泄漏?

答:因为 Python 重启很快。

😂 说多了都是泪,我之前排查 Java 内存泄漏的问题,超高并发的程序跑了1-2个月后就崩溃。我排查了好久,Java GC 参数也研究了很多,最后还是通过控制变量法找到了原因。

如果在 Python 中,多简单的事啊,写一个定时重启脚本,解决…

偏光太阳镜的那些事

探索精神

我这人对待事物有一个习惯,一个东西只有把原理搞明白了,才会去相信它,才会去用它。

反之就会对它嗤之以鼻。因为国内无良商家实在太多,不去刨根问底,就容易交智商税。

 

例如朋友圈的微商,本质就是类似传销的金字塔模式,那些三无产品真的有人买?朋友圈见一个屏蔽一个。

 

还有之前流行的“大麦若叶”,功效被吹得那么神,还是来自于日本的正规品牌。

它还真让我困惑了很久,因为理论上,在日本、美国等地方相关法律健全,不太会有这种“神奇”的产品出现。

后来知乎上的解答让我豁然开朗,简单地说一下结论:

这东西的确有一定功效,而日本农产品不发达,所以产生了这个东西。

但是在中国不一样啊,蔬菜这么便宜,你为什么要花大价钱买这个呢?而且还这么难喝。

而且它的很多所谓的功效都是到了国内被吹出来的。

 

然后,偏光太阳镜也是一个传说中很神奇的东西,带上它开车就可以抗反光,让视野更清晰。嗯,一般价格至少100以上。

这不是逗我玩吗?电影院那种3D偏振眼镜5元就可以买一副了,不都是偏振吗?还能有什么区别?

于是,我就这样带着家里的3D偏振眼镜开了一年多的车…

我之前也一直以为那些偏光太阳镜是无良商家的虚假宣传,为了揭露他们的邪恶本质,我当然要去研究一下!

于是,就有了去年我在知乎提的一个问题:为什么偏光太阳镜可以抗反光?