记一次 GC 优化

现象

近一段时间,项目 account-service 出现高峰期经常性报警的问题,且发布越久情况愈发严重。

从 Service Metrics 图表 来看,报警的时段 account-service 确实存在大量接口超时的情况,且单一时间集中于某一个 Pod 上。

观察 Grafana 上的图标,发现在该时间段 Pod 出现 Major GC 时间增加的情况,高达几秒,猜测是由于 GC 所引发的接口超时现象。

1
2
// prometheus 查询
increase(jvm_gc_pause_seconds_sum{kubernetes_pod_name=~"$app_name-$env-.+", action="end of major GC"}[2m])

那么接下来我们要解决两个问题,一是 GC 为什么会花费这么长的时间,二是情况为什么会变得严重。

Kubernetes 通过 Ingress 实现灰度发布以及 CI 流程

ingress-nginx 在 0.22 添加了灰度发布的功能,可以通过简单的配置实现。这篇文章主要讲解如何配置以及如何和 CI 流程结合。
PS:简单说明下,我司的发布流程是通过 Gitlab 和 Kubernetes 实现的。在 Gitlab - Operations - Kubernetes 添加 Kubernetes 相关配置,在 .gitlab-ci.yml 配置 CI 流程,在 Gitlab CI Variables 里配置敏感信息。

Ingress 配置

Ingress 需要增加的配置比较简单,只需要添加几个 annotation 就可以。

1
2
3
# ingress annotation
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "20"

上面的配置表示开启 ingress canary 功能,设置的流量为 20%。除了基本的配置之外,还可以根据 Header、Cookie 进行流量配置,可以参考官方文档

接下来,只要发布的服务和原来的服务 Ingress host 保持一致就可以。现在发往 http://example.beta.com 的请求,有 80% 的流量发往原服务,有 20% 的流量发往新的服务。

1
2
3
4
5
6
7
8
9
10
11
12
# .gitlab-ci.yml
deploy_beta:
<<: *DEPLOY
environment:
name: beta
url: http://example.beta.com

deploy_beta_canary:
<<: *DEPLOY
environment:
name: beta-canary
url: http://example.beta.com

Kubernetes 使用 Zuul 网关时上游 Tomcat 400 报错问题排查

在 Kubernetes 里,使用 Zuul 转发请求到上游的 Tomcat 服务时,Tomcat 报了一个很诡异的错误:

1
The host [zuul.host,zuul.host] is not valid

其中 zuul.host 是 Zuul 服务的域名。

看到这个报错时,有两个点很奇怪。一是 [zuul.host, zuul.host] 是怎么来的,为什么它重复了一遍。二是为什么 host 的值会是这个,应该是 Tomcat 服务的域名。

图解密码技术

对称密码

对称密码是一种用相同的密钥进行加密和解密的技术,用于确保消息的机密性。尽管对称密码能够确保消息的机密性,但需要解决将解密密钥配送给接受者的密钥配送问题。
一次性密码本的原理是将明文与一串随机的比特序列进行 XOR 运算。即使用暴力破解法遍历整个密钥空间,也绝对无法被破译。
随着计算机的进步,现在 DES 已经能够被暴力破解,强度大不如前了。
AES 是取代其 DES 而成为新标准的一种对称密码算法。在 2000 年从候选算法中选出了一种名为 Rijndael 的对称密码算法,并将其确定为了 AES。

转码微信 speex

H5 可以使用微信 jssdk 提供的录音接口,将录音上传到微信的服务器。而后端可以通过获取微信录音的接口下载录音。
录音有两种格式,一种是 8K 采样率的 amr 格式。还有一种是 16K 采样率的 speex 格式。speex 格式的录音更清晰,当然文件也更大。同样的录音,speex 格式大约是 amr 格式的 4 倍。但同时,amr 格式的录音失真十分严重。如果有播放、语音识别的需求,建议还是采用 speex 格式的录音。
不知出于什么考虑,微信对 speex 格式的录音做了加工,得使用 speex 官方解码库结合微信的解码库才能进行转码。
下文介绍如何使用 docker 编译可转码微信 speex 的程序。

如何用 Markdown 写 PPT

reveal-js 是通过网页来制作 PPT 的 JavaScript 框架,通过它可以轻松制作精致的网页 PPT。它自身也支持 Markdown。
但使用过几次之后,感觉对于日常使用还是稍重了些。每次新建 PPT 都需要复制粘贴 boilerplate code。

reveal-md 让你从这些繁琐的步骤中解放出来。它使用 reveal-js 自带的 Markdown 功能,让你只需写 Markdown,其他的事它帮你搞定。

安装

简单地说下如何使用。

1
2
3
4
mkdir slides && cd slides
yarn init -y
yarn add reveal-md
yarn exec reveal-md demo

此时炫酷的 PPT 网页就在你眼前呈现了。

语法

标准的 Markdown 语法,默认 --- 作为 PPT 页面的分割线。

1
2
3
4
5
6
7
8
9
10
11
12
# Title

* Point 1
* Point 2

---

## Second slide

> Best quote ever.

Note: speaker notes FTW!

演示命令

演示命令 reveal-md,启动本地 server 并使用 reveal.js 渲染 Markdown 文件。

1
yarn exec reveal-md demo.md

更详细的配置请查看 reveal-md 文档reveal-js 文档

Rails 通过 path 实现 subdomain —— default_url_options 的妙用

最近在开发微信开放平台,需要通过 url 来区别不同的第三方。最简单直接的方法,就是通过 subdomain 来实现。
比如有两个第三方 A、B,那么 A 的域名为 A.example.com,B 的域名为 B.example.com。但这次由于 https 证书的问题只能通过 path 来实现,也就是 A 的域名为 www.example.com/A,B 的域名为 www.example.com/B。

但通过 path 实现会有一个严重的问题,就是如何保证生成的 url 带有第三方的信息。

1
2
3
scope path: '/:third_party' do
resources :orders
end

上面的配置使用 orders_path 时会报错 missing required keys: [:third_path]
最笨的办法莫过于给所有的 url 手动加上,但这方法随着项目增大会变得不可行。

我们希望 url 的生成可以更加智能,当访问 url 为 www.example.com/A 时,使用 orders_path 可以自动生成 /A/orders。从逻辑上,这是行得通的。

首先想到 route 可以添加 default 配置,于是做了以下尝试,可惜报错了。在 route 时只能配置静态的默认值。

1
2
3
4
# undefined local variable or method `params'
scope path: '/:third_party', default: {third_party: params[:third_party]} do
resources :orders
end

那么,有没有类似的方法,提供 url default 配置的功能呢?这时就轮到 default_url_options 出场了。
default_url_options 的用处不仅仅是在 config 时设置默认的 host,每个 Controller 都有该方法,调用 url_helper 时会结合该方法生成最终的 url。

在对应的 Controller 里添加以下代码,这时调用 orders_path 就会根据域名中的 third_parth 生成对应的域名了。

1
2
3
def default_url_options
{third_party: params[:third_party]}
end

解决了生成 url 的问题,我们还需要解决测试时 url 的问题。这里我对 ActionController::TestCase 进行了 monkey patch,供大家参考。

1
2
3
4
5
6
7
8
9
10
11
class ActionController::TestCase

alias_method :orig_process, :process

def process(action, method: "GET", params: {}, session: nil, body: nil, flash: {}, format: nil, xhr: false, as: nil)
if @controller.is_a? WechatBaseController
params.merge!({app_alias: 'test'})
end
orig_process(action, method: method, params: params, session: session, body: body, flash: flash, format: format, xhr: xhr, as: as)
end
end

微信开发总结

最近开发了基于微信的相关项目,主要是微信的网页开发、微信支付以及发送消息这块的内容。项目本身没什么难度,但由于微信封闭的体系、混乱的配置以及分散的文档,爬了不少的坑,这里总结下经验。

文档

先说说微信的文档。微信的文档不是统一的,比如公众平台技术文档在一个网站,而支付文档在另一个网站,但支付文档所使用的 js 的相关文档又在前一个网站。除此之外,还有小程序文档开放平台文档。从这混乱的文档相信你也能或多或少体会到开发的难处。

配置

出于安全性(更多是封闭性)的考虑,微信的配置繁琐且复杂,这里我把整个流程都记录下来以供参考。
首先登录公众平台,在开发-基本配置里获得 AppSecret 并保存下来,之后网站就不会再显示开发者密码,只能重置。
同样在该页面,配置 IP 白名单。如果你需要接收用户公众号的消息以及事件推送,同一页面添加服务器配置。
然后,在设置-公众号设置-功能设置,按照说明对业务域名、JS 接口安全域名、网页授权域名进行设置。
如果有微信支付的需求,去微信商户平台,商户平台-产品中心-开发配置中设置公众号支付的授权目录。注意,授权目录必须是发起支付网址的上一级目录。举例来说,
发起支付的网址为 www.xxx.com/orders/22/pay,那么支付目录就必须为 www.xxx.com/orders/22/,填 www.xxx.com/ 或者 www.xxx.com/orders/ 都是不行的。再加上支付的授权目录只能填 5 个,发起支付的域名得注意设计。

PS: 没有公众号的朋友可以通过微信的测试号来试验除微信支付以外大部分的功能。

互联网创业核心技术:构建可伸缩的 Web 应用

《互联网创业核心技术:构建可伸缩的 Web 应用》整本书围绕“可伸缩”三个字,对 Web 应用的每一层展开了全面细致的解说。
如果你和我一样,对负载均衡、水平伸缩之类的概念不是很了解,或者不清楚它在整个架构中的使用,那么你应该来读下这本书。
读完之后你会发现架构这东西是有迹可循的,是围绕一系列基本的原则建立起来的。

一图胜千言,这本书不仅有着易懂的语言,而且有大量简洁的示意图。

FactoryGirl 源码浅析

FactoryGirl 是我个人十分喜欢的 Gem,它能很方便地模拟测试数据。使用技巧可见 FactoryGirl 技巧。出于兴趣我研究了下它的源代码,说实话比想象得要复杂。由于 FactoryGirl 配置的灵活性以及 Ruby 本身的语言特点,使得它代码整体上比较飘逸。
这里我会对它最基础的方法进行分析,版本为 4.8.0。

FactoryGirl.define

先从定义 Factory 开始。下面的代码定义了名为 user 的 Factory,它有一个名为 name 的属性,值为 Kay。

1
2
3
4
5
FactoryGirl.define do
factory :user do
name 'Kay'
end
end