前因
前几天的一个晚上从03:00开始,某一台业务服务器出现负载持续偏高(不会像往常一样自动下降),接口开始访问超时。开启IP黑名单后也没有降低负载。
通过 htop 指令,可以看到负载达到了 10 多了:
Quick notes
之前有个站点(foo.com)是同时允许 http 和 https 访问的。 后面策略调整了一下,不允许 http 访问了,只允许 https 访问,如果用户输入了 http 地址,然后就要强制跳转到 https。
如果啥都不改的话,前端的做法很简单,就是判断协议,如果是 http 协议的话,就重定向到 https 协议:1
2
3
4
5
6<script type="text/javascript">
if (location.protocol !== 'https:') {
// Automatically jump to https
location.href = location.href.replace('http:', 'https:');
}
</script>
有时候我们在开发的时候,尤其是多个人协同开发的时候,有时候某一个功能在 develop 分支,然后这时候要合并到 master 分支。这时候我们就会想要知道当前的 develop 分支和 master 分支到底有没有差异。如果有差异,并且差异就只是你要合并的内容,那就直接 merge 到 master 就行了,但是如果差异有很多的 commit,并且这些 commit 还包含其他人的提交,并且这些提交当下还不能合并到 master, 那么这时候是不能直接 merge 的。 这时候要么就把你的 commit 一个一个用 cherry-pick 拷贝过去,要么就单独对这几个 commit 做成一个 patch,然后再把 patch 合并到 master。
1 | git log develop ^master |
之前项目有做了一个企业版的功能,但是要在官网上推广,所以我们就在官网增加了一个页面,叫做bizHome.html的页面,专门用来介绍这个企业版的功能。
但是有一天商务过来说,能不能把这个页面 www.example.com/bizHome.html 变成这个页面 www.example.com/business ,这样会比较好推广,而且也比较正式,但是线上放出去的这些 bizHome.html 的外链也要可以访问。
通过 又记一次AWS SES 邮件服务账号被禁的情况 我们知道 AWS 之所以会禁掉我们的 SES 账号是因为我们的邮件的反弹率(bounce)太高了, 因此在服务稳定了之后,就打算好好研究一下 SES,看看有没有什么方式可以来处理硬反弹邮件。
事实上,SES 在发生邮件退回和投诉的时候,会通过电子邮件发送 SES 通知。这个是默认启用的功能。 具体文档:通过电子邮件发送的 Amazon SES 通知
去年有通过 aws 的 Lambda 服务来解决缩略图的问题: 项目使用 aws 的 lambda服务来生成s3的缩略图,不过那时候是国内七牛缩略图,国外 S3 缩略图各自的策略,他们的缩略图的 URL 是不一样的。因为我们会在用户上传图片的时候,判断这个用户的所在国家,如果是中国的话,就传到七牛那边去,否则就传到 S3。
但是这边会有一个问题,就是如果我在国内传了一个图片,这时候是会传到七牛那边去的,但是一旦我到国外去,访问这个图片的话,也是会访问七牛的图片(因为S3根本就没有上传,我们只上传到七牛,所以保存的也是七牛的路径),这时候线路就不会得到优化了。
现在我们有另一个全球的项目,在这个项目里面,要求用户上传的所有的图片资源无论是七牛还是S3都要传一份,这个是因为考虑到用户的使用场景有可能会有跨国的问题,所以就是相当于我们对用户上传的资源做了一层 CDN ,如果用户在国内访问的话,就会去取七牛的图片,如果在国外访问的话,就会去取 cloudfront 的图片。
如果要实现这种结果的话,那么无论是七牛还是S3,对外的域名肯定是同一个,比如 img.example.com, 只要用户访问 img.example.com/123.jpg ,那么这时候 DNS 解析如果是在中国的话,就会 CNAME 到七牛对应 bucket 的域名,然后获取对应路径的图片, 如果解析是国外的,那么就会 CNAME 到 cloudfront 对应的 S3 的 bucket, 然后去取对应路径的图片。
之前有做过官网的优化:官网构建优化流程(12) - 优化加载速度,资源分开存放,我们优化了官网在国内的加载速度。接下来也想优化一下,另一个站点在国内的加载速度,因为测试也在反馈,加载速度超级慢的。
我查看了一下,现在 router 53 该站点,比如 boo.foo.com 的配置:
前段时间在测试服上修改第三方(google, facebook, twitter)注册代码的时候,遇到了问题,因为我们的测试服在国内,因为没法翻墙,导致每次请求都超时。
所以就针对这三个 SNS 的使用都加上了代理,当然前提是这台服务器上已经配置了代理端口了,比如 127.0.0.1:8102 类似的。 我们用的是 php 的 yii2 框架。
google 很简单,SDK 有支持,直接去环境变量去取,所以在原来的代码的基础上加上这个就行了:1
2
3
4// 测试环境: google 的请求使用代理
if (env("YII_ENV") == 'dev' && yiicfg('tpProxy')) {
putenv('HTTPS_PROXY='.yiicfg('tpProxy'));
}
最近又双叒遇到了一个比较奇怪的需求,就是有一个站点A: we.foo.com , 当用户输入这个站点的时候,要变成官网的登录页:www.foo.com/signup/, 地址栏不能是官网的url,而是要变成, we.foo.com/signup/, 但是其他相关外链的请求,还是要跳到官网去, 只有登录页是变成 we.foo.com。
通过 将A站点代理到B站点的某一个页面 我们知道,我们可以通过 nginx 代理转发,将首页的请求都代理转发到官网的登录页面:1
2
3location / {
proxy_pass https://www.foo.com/signup/;
}
通过 将A站点代理到B站点的某一个页面 我们知道可以先建一个全新的域名,然后将其反向代理到另一个站点的某一个页面(地址栏url不能变)。结果现在还有一个更复杂的需求,就是有一个新的域名 foo.at(真实域名换成foo), 然后后面有带参数,比如 foo.at/112233 ,然后转发代理到另一个站点的另一个页面,比如 www.foo.com/en/action?code=112233 ,其中 code 112233 就是 foo.at 要带的参数。
还是一样用 nginx 反向代理的方式:刚开始是这样子:
之前有测试同学反馈他请求的转发服会分配非中国区。后面我们查了一下,发现我们程序获取的客户端 ip 不是真正的客户端ip,而是负载均衡服务器的ip 或者是 cdn 服务端的ip (反正就是反向代理服务器的ip)。
因此我们查看了一下获取 ip 的方法 (golang) :1
2
3
4
5
6
7
8
9//获取客户端ip, nginx代理传入的是 x-real-ip
func ClientIp(r *http.Request) string {
ip := r.Header.Get("X-Real-Ip")
if ip == "" {
s := strings.Split(r.RemoteAddr, ":")
ip = s[0]
}
return ip
}
发现这边只优先获取 x-real-ip, 如果找不到,那么就获取 remote addr。
时隔快两年,我们的 SES 账号又被封了,有运营在群里面说了,有用户反馈验证账号的邮件和重置密码的邮件又不能发送了。 我查了一下邮件服务队列的日志,果然又看到报错了(不要问我为什么是运营提示的,因为之前的那一次挂掉的经验,我还不涨记性,还没有把这种情况放到 cacti 报警里面):1
MessageRejected: Sending paused for this account. For more information, please check the inbox of the email address associated with your AWS account
看了一下被禁的理由,擦,又是反弹率超过阀值了(10%)
看了一下,又是无效邮件的占比太高了(反弹率) 达到了 12% , 预警值是 10%, 所以就被 AWS 禁了。要解禁,只能先提交工单了。因为之前就有出现这种情况了,具体看: 记一次AWS SES 邮件服务账号被禁的情况, 发 SES 邮件的账号被 AWS 禁了, 因此就先用之前的备用账号的的那个 SES 账号来代替。 换上了之后, 就正常了。
今天有客服在反应后台那边没法回复邮件了。然后看了一下我们的邮件发送服务,发现了这个报错:1
2
3, Call api time: 17.743922252s]
[Error] [queue.go 157] [2017-11-27 07:07:29] [MessageRejected: Sending suspended for this account. For more information, please check the inbox of the email address associated with your AWS account.
status code: 400, request id: xxxx]
发现竟然是 AWS SES (Simple Email Service AWS 的邮件发送服务) 那边的错误? 导致我们的邮件发送全部失败了。
整个邮件服务都有问题了。 上 AWS 后台看了一下,账号状态是 shutdown 的状态。
之前项目有用到了一些第三方支付,包括 paypal, google iap, stripe, apple iap, 还有国内的 alipay。其中每个支付类型都有一些坑,本章讲的是使用paypal 第三方支付的时候,遇到的一些问题,以及那时候是怎么去解决的。
之前客服有反馈一个情况,就是突然间有一天,很多初次付款的 paypal 用户,都在反馈他们付款成功了,但是没有升级上来,后面查了一下 log, 发现在收到 paypal 的 PAYMENT.SALE.COMPETED 的 webhook 的时候,我们马上去查交易记录。但是只有查到一条循环创建的记录,没有查到交易成功的记录,所以我们程序就误认为付款其实还没有成功,就没有再继续下去了, 但是其实这时候交易是有成功的。
我们项目有提供一个基础的 vip 服务,但是在这个基础的 vip 服务上,我们还提供了一些增值服务。因为我们的支付都是走循环订单的。无论是用户买基础的 vip 服务,还是直接在基于 vip 服务的含有增值项的服务,都是走循环订单。
但是还有一种情况,就是如果有一个用户刚开始买了一个月的基础的 vip 服务,但是用了半个月之后,想把另外半个月的时间换成含有增值项的 vip 服务,因为后者的价钱更贵,并且为了保证用户的循环付款周期不改变,我们没办法先将基础的 vip 服务的循环取消掉,再重新订阅一个新的循环,因为这样子循环的付款时间就会变。
所以为了保证循环付款周期不变,我们会算接下来这半个月时间,用户需要付新服务的差价,然后在下一个循环周期到来的时候,就换成新的循环订单, 在 stripe 支付上,我们考虑的是直接用 stripe 的循环订单升降级机制。