【第一部分】
题记:
三十几岁了,从 sina 大学肄业后,在外面漂泊,不好不坏。
从好的角度讲,这几年自己的技术没有减退,偶尔也还能够略有深入,同时能够更多的接触到产品、运营、甚至营销,而我自己也能够在很多场景下,不紧不慌,还算从容。从坏的角度讲,每一项都没有达到自己想要的成就。
从坏的角度讲,09 年底从 sina 离职后,至今已经结束了第三份工作。
此次离职实非我愿,具体细节不好也不愿再细说,概括来讲是我作为小公司的技术管理人员虽尽力但仍没有能够让老板理解我在某项目中的安排,反而产生了误解,以致后来越来越多的误解到其爆发,刻意为难。虽有后来的歉意,但间隙已生,且无合适的解决方案,遂,今日失业之现状。
如果你看此文时心有戚戚,请到这里:https://me.alipay.com/chunshengster
好吧,下面正题,首先是“万达”
正题:面试
万达:
万达的推荐工作是一位认识了6-7 年的猎头朋友推荐的,小姑娘爽快、伶俐,不好拒绝。坦言讲,我本人对万达还不了解,外面的传言里也大多是不好的,我仅仅是去试试看,职位是 web 主任开发工程师的职位,听起来也挺“高端”的嘛,当然最后发现整个过程被当作了一个乐子。
到万达下午 3 点半,20 分钟做完题目,一道数据库设计的题目,一道询问熟悉的 HTTP 协议头及其含义,一道大数据量(千万级别数据)的数据库技术方案,一道所谓的大数据计算,还有两道算法题,其中算法题中有我认为最可耻的“求最大公约数”。
笔试结束,同一位小朋友口头沟通,大约一个半小时,当然,花了这么多时间跟我口水比较多有关系,因为每次看到小朋友木讷的眼神我都忍不住“好为人师”的深入讲解下去。
当然,我对万达和猎头在面试环节安排的人不满意,并将此意见表达给猎头,我觉得面试的人的水平和能力远在我之下,甚至不能对技术问题进行有效沟通。朋友戏谑,我在万达只是找了个“乐子”,实非我意,抱歉。
万达的面试是在周一。周二,我赶了两场:京东旗下的“网银在线”和“小米”。
网银在线:
网银在线的面试流程应该是在沈姓 VP 的授意下刻意调整的,沈直言,他对我不是面试,正常面试不是这样的流程,为此我深感荣幸和感激。首先是跟沈沟通我现在的情况,以及可能的工作内容和方式。沈给我的主观的感受是,直白、深刻,当然,经历的不同也让我略感压力,压力在于我对其部分判断不能确定是否“武断”,比如,10 人团队和 30 人团队的管理方式上的确存在差别,我并不觉得我现在 30 人的团队管理是“哥们义气”的方式,当然,我也当面承认,在这 30 人的管理经历里,我非常幸运的是未遇到突发的管理难题(30 人 team 的管理经历我将在后面单独描述)。
同沈沟通的另一个问题是应用运维的方式以及利害。我也是在这个过程中,了解到阿里应用运维在整个业务经营体系中所占的重要性,其承载了开发(coding)和系统环境(软硬件)中间的沟通以及优化的工作,这项工作中有对业务模块、逻辑深入了解的必要,最好有开发的经历,同样需要对系统软硬件的承载能力有所了解深入,对我这种粗浅的理解来讲,就是“为每一双特别的脚定制一双舒适的鞋子”,当然要对这双脚和鞋子都有足够的了解才行。而我的优势在于,在需求分析、产品开发、系统运维、优化上都还蛮有经验,这是做好业务运维的最好的基础。后来我想我从 08 年在 sina 博客时开始,就已经开始从事这样的工作了,只不过 sina 叫产品线架构师。
当然,我也有提到,我在最近这半年系统优化过程中所使用的方法(优化方法和思路后面单独描述),包括简单的对 google 某篇论文中提到如何通过监控解决线上代码运行效率这一问题的理解(见参考1),包含这篇论文引用的 x-strace(见参考2) 的理解。我提到了,在 LAMP 环境中,这些方式和机制几乎是天然存在的。有个难题是,网银在线是 java 的平台,我基本上没有接触过。
网银在线的二面是当前的运维负责人(抱歉我没有问具体的 title),问了一些还算简单的服务器技术,如我在微博上提到的,很给面子。
三面是人力资源,小姑娘言语犀利,我有一说一,坦然面对并坦言自己不足,直言在人生规划中急需解决的问题。但在最后一次的离职原因上,当时我的确没想明白,也没办法说明白,这个问题在所有面试的过程中,一直在困扰着我。
面试结束后,在京东的大 Logo 旁请京东的同学拍了一张照片。赶赴小米。
本来周二下午约了潘少宁在车库咖啡,但是网银在线的同学打电话过来说只有周二他们集中面试,并且 VP 在,无奈,只好委屈潘,把跟潘的会面改到了周三上午。
参考:
https://www.usenix.org/conference/nsdi-07/x-trace-pervasive-network-tracing-framework
【第二部分】
今天叙述小米的面试经历。
小米:
在小米是聊了两个部门的,首先是运维部门,在 @wilbur 井源的热情招待下,吃了顿大餐,抱歉的是我没有带足现金,所以付款时我无法“客气”,改天补请。
wilbur 井源同两位同事与我四人边吃边聊,我简单介绍当前的网站的服务结构以及部分业务的技术设计,比如网站架构的分布情况,分布式文件系统 fastDFS 的使用状况、Redis 和 MySQL 的一些部署结构和技术,其中尤其对监控这件事情我做了详细一些的说明(详见服务可用性监控的一些思考以及实践),中间提到了关于主动监控(主动监控是指通过运维和业务部门指定监控的系统资源、接口、页面、日志等,主动发现问题,警报级别较高)、被控监控的概念(指通过 JSlib 或客户端 lib 对于所有的操作尤其是网络接口的请求进行监控,对异常进行汇报,通过收集日志的方式进行可用性问题的发现)。当然,还有必不可少的是对 haproxy 的运行和优化状况(参见 Haproxy 配置),MySQL 的架构及优化方式(见 MySQL 架构及运维),Redis 常见的性能问题(参见 redis 架构及运维问题),fastDFS 同其他分布式存储 MogileFS、TFS、lusterfs 的在功能、运维成本上的横向比较,多 IDC 图片 cache 的部署以及性能优化(参见多 idc 图片 Cache 部署),Linux 内核参数(参见 Linux 内核配置)和让我特别自豪的是关于网卡 smp affinity/RPF/RFS 的优化效果(参考3/4/5)的一些优化等。当然,这是正经的运维部门,我阐述了我对“运维”工作的理解:60% 的分析整理工作加上 40% 的技能,分析整理能力是做好运维的基础。
井源也询问了几个安全问题,我粗浅的理解是:从 SA 的经历来讲,做好 IT 系统规划,合理区分服务器角色,通过 iptables 是能够阻止大多数接入层非法请求的;对于 web 业务的安全来讲,SQL 注入、CRSF 等攻击是因为对输入输入内容的过滤不严格导致的,在开发的过程中合理使用一些优秀框架或 lib,也能够避免大多数漏洞的产生;有个比较有意思的话题是关于溢出的,现在我已经不会计算溢出地址了,在我当 script boy 的时候研究过一点,忘光光了,惭愧……
井源这边的效率很好,边吃边聊的气氛很放松,不过很多问题都停留在一些思路和效果数据上,没有勾勾画画的太多深入的探讨。
大约 8 点半左右到的电商部门,常规面试的第一轮都是技术,包括细节。面试官是位张姓的 team leader。
在这轮面试的过程中,因为是在会议室,有笔有板,所以我边讲边写。大体上介绍了我对 web 服务架构的理解,我认为,web 服务架构大体上离不开这样几个层面:接入层(负载均衡)、业务服务层、数据层,一般还会有不少的后台辅助程序进行同步、异步的处理各种不适合在业务层融合的服务单元。数据层可以包括 DB、Cache、File 等,数据层还可能会有很多中间件或代理服务器用来做数据层的负载均衡或是 HA,以及 Sharding 等。同面试官详细介绍了当前服务的公司在每一层所采用的技术,分别是:haproxy、nginx+php、twemproxy+redis、MySQL+RedisCache、Varnish+Squid+nginx+fastDFS。
haproxy 的服务器配置是按照 100w 并发的目标进行配置和优化的,计划 100w 客户端连接,考虑每个客户端连接可能产生 1 个内部连接,按照每个连接消耗 4k(此处修正为 17K,haproxy 的官方数据,见参考8,感谢 @GNUer 的修正)内存来算,大约 8G (此处修正为 32G)内存【这里的计算还需要再考虑,我担心 haproxy 的每个连接消耗 17k 内存是包含对内部服务器的连接】,实际上往往比这个数字要大。目前达到的最大连接数目测到过 16w,在接入层的系统优化上分别有:网卡中断优化(参考3/4/5),linux 内核参数优化(见 linux sysctl.conf 配置)。
值得一提的是,我们的 haproxy 服务器都是 64G 内存,实际上远远永不到这么多,图片服务的最外层 cache,即 Varnish,我们也是部署在 haproxy 服务器上的。
在最外层服务器上,我们每天大约 5 亿+(1-1.5 亿+的动态请求、3-4 亿+的图片请求)的请求量,共计使用 7 台 64G 的 Dell R410,目前看负载还很低,从系统的各种资源上看,请求量翻倍应该是没有问题的。
在最外层的服务器配置上,有一个问题值得注意,即 sysctl.conf 的配置中,timestamp 必须为0,这个在 tcp 协议的扩展标准中有提到,否有 nat 环境的客户端连接有可能产生异常,异常的状况可以在 netstat -s 的输出中看到。还需要注意的是 timestamp=0 的情况下,tw_reuse 是不生效的。
要保证服务器能够接收大并发的连接请求是件不难的事情,但需要考虑一个细节,每接收一个请求,haproxy 就需要至少分配一个系统的 tcp 端口请求后面的业务服务器、cache 服务器,系统一个 ip 地址可用的端口数最多为 65535,一般还需要减去 1024。值得考虑的是减小 tw_bucket 的容量,让系统在 tw_bucket 满的状况下,对 tw 状态的连接进行丢弃,以达到快速回收的目的,tw 的默认回收时间的 2 倍的 MSL。还有一个方式就是多配置几个 ip。
还有一个问题,接入层的服务器往往会开启 iptables,内核中 nf 的相关配置也是需要优化的,比如 nf_conntrack_max、nf_conntrack_tcp_timeout_established 等。
在业务层的优化有 nginx+php(fastcgi 连接方式、php-fpm.conf 配置中的优化),我的一个经验是,如果 nginx 同 phpcgi 运行在同一台服务器,采用 unix socket 的方式进行 fastcgi 协议的交互是效果最快的,比 127.0.0.1 的回环地址要快太多。我在 08 年优化过一台服务器(Dell 2960,16G 内存),通过两个步骤,将一台服务器从 900qps,优化到 6000qps 以上,其一是将 fastcgi 协议运行在 unix socket 上,其二是合理配置 spawn-fcgi 的进程数量。现在基本上 phpcgi 都是运行在 php-fpm 中的了,其进程池逻辑是我最赞赏的功能之一。
如果 nginx 和 php-fpm 不在同一台服务器上,可以考虑使用 fastcgi_keepalive 的配置,实现 nginx 同 fastcgi 服务器持久连接,以提高效率。
nginx+php-fpm 提供的运行状态非常有意义,nginx 的 status 模块和 php-fpm 的 status 输出可以告诉我们 nginx 进程的请求处理状况,php-fpm 的 status 输出可以告诉我们 php-fpm 的进程池设置是否合理。我们目前对这两个数据通过 nagios 定期采集,并绘制成图表,很有“观赏价值”。
php-fpm.conf 的配置中还有几个参数对优化比较重要,其一是进程自动重启的条件 pm.max_requests,其二是 php-slow log 的配置,slow log 是优化 php 代码的非常重要的信息。在我目前的环境中,php 的慢执行日志是通过 rsyslog 进行传输并集中分析的,以此反向推进开发对 php 代码的优化。
php 的服务器在高并发的情况下,有可能因为服务器本身可提供的端口数量的限制,无法同 redis 服务器建立大量的连接,这时候可以在 sysctl.conf 中配合 timestamps=1 加上 tw_reuse/tw_recycle 的方式,进行端口快速回收,以便更好的向数据层建立连接,接入层的 haproxy 是不可以这样的。
这一层还涉及到一个安全问题,就是 php 代码被修改并挂马的状况,我的解决方案是,将 php-fpm 的运行用户同 php 代码的属主设置成不同的用户,并且保证 php-fpm 的运行用户不能对 php 代码具有写的权限。
数据层的情况里,MySQL 主从结构以及 MHA+keepalived 的高可用配置,这个基本上是看文档应该就能够理解的。如果是 5.6 的新版 MySQL,其高可用监控可能可以做的更简单,MySQL 官方提供对应的工具,只是我还没有测试。对 MHA 的监控功能,我觉得亮点是 MHA 对切换过程中 MySQL binlog 的获取和执行,在最大程度上避免了数据丢失。但是其缺点也是有的,比如:监控进程在触发切换后就停止了,一旦触发,必须重新启动进程再继续监控。06 年时我在 sina 做过一个叫 Trust DMM 的项目,通过 DNS、MON 加上自己写的插件,监控 MySQL 主从集群的可用性,可以实现,主库、主备自动切换(缺乏 binlog 处理的环节);从库是一组服务器,如果从库发生问题,可以自动下线。只是这套系统部署起来比较麻烦。这个项目曾经获得过 sina 的创新一等奖。
我还提到了我认为的 DBA 日常的工作至少应该包括:审查并执行上线 SQL;定期检查 MySQL 慢日志并分析,将分析结果反馈到开发部门进行调整;定期审查数据库中索引的效率以及可用性,进行优化我反馈。现在做一个一般水平的 DBA 已经相当容易了,对 percona 的工具了解透彻,已经能够解决非常多的数据库问题了。
MySQL 还有一个难缠的问题,numa 架构下,大内存服务器内存使用效率的问题,numactl 对策略进行调整,如果使用 percona 的 MySQL 版本,可以通过 memlock 配置对 MySQL 的 Innodb 引擎进行限制,禁止其使用 swap。
MySQL 常见的架构里,还有一种主从存储引擎不一致的方式,即主库采用 Innodb 引擎,提高并发写入的能力,从库采用 Myisam 引擎,这种方式目前我们也在采用。这样做一是为了获取更好的读性能,另外是,Myisam 引擎的是可以节省内存的。Myisam 在索引数据内存读取,数据内容磁盘读取的状态下,已经可以比较高效的运行了,myisam_use_mmap 的配置项,会让 MySQL 将 myisam 的 data 文件也 mmap 到内存中,这样做既高效,又可以使用 mysiam 引擎的特性。
数据库主库要避免一件事情发生,就是无条件删除和无条件修改,如“delete from table”以及”update table set xxx=yyyy”等无 where 条件语句,原则来讲是应该禁止执行的,这样的权限不应该开放给开发的同学,甚至 DBA 都不能无限制的操作。目前我的解决方案是 sql_safe_updates=1,但这配置是不能够写 my.cnf 中的,只能启动 mysql 后进入 console 进行配置。
当前我们还使用了 Redis 作为 DB,基于主从架构,跨 IDC。目前的问题是,复制连接断开后,Redis 快照重传的问题,从库会在快照替换期间有短暂的性能抖动。 Redis2.8 新版本 psync 的特性应该可以改善这个问题。我们还使用 twemproxy,目前部署在每一台 php 服务器上,并监听 unix socket,php 使用 phpredis 的模块进行连接。有效减少三次握手的时间。temwproxy 还有很多其他的优秀特性,通过一致性 hash 做 cache 集群,可以有效的避免 cache 迁移问题。通过其对后端 redis 的健康监控,可以自动下线有故障的 redis。
还有针对多 IDC 的图片存储和 Cache 部署情况。目前我们自建的图片 CDN 承载网站每天约 4 亿的请求,带宽最高峰值约 1.5G 左右,其结构大体上是中心 IDC 存储图片原图 +SQUID disk cache 存储图片缩略图,在外地 IDC 使用两级缓存,分别为一层 SQUID disk cache(两台,做 HA),另一层为 Varnish cache(最多四台),实际上,如果仅考虑 work around 的状态,squid cache 层基本上也可以不要的。但是,目前这样的结构可以减少 varnish 回中心节点的请求,减少中心机房带宽的压力。这个结构还算简单,varnish 在高并发请求下,有一些资源配置是需要注意的,比如 NFILES / VARNISH_MAX_THREADS / nuke_limit 等。
沟通的技术问题还是非常多的,包括在井源那里提到监控框架的事情,也尤其提到了我对 rsyslog 的优化,优化后的 rsyslog 在可靠性方面是非常值得称赞的(优化思路见参考6)
我有一些将电商三面的运维运维同学的问题综合到这里了,有些话重复的就不再描述。
值得一提的是二面是另一位开发负责人,一看就是个很有独立思考能力的同学,他问了我一个很有意思的问题,大体的意思是,在系统架构方面,有这样的几个层次,从下往上:使用开源、精通开源,优化并修改开源软件,创造开源软件。问我自己评价我是在哪一个层次的。我认真的思考了一下,我应该是在第二个层次,有些精通的,有些修改过的。
电商四面是时间最长的,至少有两个小时以上,结束的时候已经是夜里一点四十了,我觉得电商的老大是应该在支付宝里面给我捐一些钱才好的 ,不知道有没有小米的同学能够转告哈。
。我们应该是谈到了非常多的事情,包括秒杀的解决方案,包括对持续集成和自动化测试的理解、对后台数据业务类型的开发中数据计算错误的理解,时不时能够得到“我们想的很一致”这样的评价。
那时已近半夜,记忆进入低效态,一些太琐碎的事情记不得了,重复的技术方案也不再赘述。下面简单描述一下我对秒杀的解决方案的理解:10w 的数据,从 0 到 10w,不能多卖。目前的问题是,每次到秒杀时分可能同时进入 100w 的请求/连接。如何破?
我的方案是:排除 user、session 等外部依赖服务的前提下,两台 ha 外面抗并发连接(后来想这个无所谓的,不如做成 php 的服务器),三台 PHP 服务器(不要使用任何框架,最朴素的纯粹 PHP 代码),两台 Redis(最初说了一台)。具体优化状况如下:
php 的逻辑可以简单的理解为对 redis 的某一个 key 进行 incr 原子操作,如果返回的当前数值小于等于 10w(两台 redis 的情况下应小于等于 5w),则认为中签。
从我以前看到的数据来讲,redis 的最好状态在 8w qps。nginx+php 在 08 年时已经优化到 6000 qps,目前的服务器设备(双核 16cpu+64G 内存)达到2、3wQps 应该也是不难的事情(这个的最新数据我不知道)。上述配置至少应该能够在 5s 内完成 10w 次 redis 的 incr 操作。加上系统各系统对请求队列的支持,可以几乎做到不报错,短暂延迟。
如果考虑 1 台 redis 请求量会很高,可以考虑分片,每台分 5w。
当然,这是在仅仅思考不到 1 分钟内给出的方案,从现在来看,haproxy 是可以不要,nginx 扛并发连接的能力也不错。所有的细节还需要通过压力测试进行验证。而实际情况加上对其他服务的依赖(我不知到还有哪些,抽丝剥茧去除干扰),方案也会更加复杂一些。据电商老大讲,实际情况是,秒杀的服务用了十几台服务器,秒杀的时候偶尔出现一些故障,小米做秒杀的同学,压力很大哦。
如果你提到要记录中签的用户的 uid 和中签号码,还是 redis 吧。
(突然 wps 的 linux 版崩溃了,只能恢复到这里,后面的部分内容是重写的,可能有点混乱)
针对刚才的问题,我在白板上画了个简单的架构图:haproxy+nginx/php+redis,haproxy 和 nginx/php 都是可线性扩展的,redis 可以通过 sharding 来实现扩展。理论上讲,一个可扩展的架构是可以满足任何性能要求的,更何况如此简单的逻辑,单机性能已经可以做到非常高了。
电商王姓负责人在问我方案时问这个需求会有哪些难点?我看着白板笑笑:目前看,应该不存在难点。如果有问题,应该看日志和服务状态以及服务器状态。
第四面聊得很头机,对方几次想结束时都突然冒出来一个问题,每一个都会讨论比较久,比如后台的一些计算操作是否换成 java 更合适,因为 java 可以更严谨。我说这可能不是语言的问题,而是程序员习惯和素质的问题,如果想换,其实我倒是更愿意尝鲜,比如用 go,还可能可以同时满足性能的问题。
还有突然聊到持续集成,我坦言,我对持续集成的理解停留在用工具实现自动测试和发布这样的层面上,没有实操经验。但我个人的一个粗浅的认知是:持续集成的前提是自动化测试,自动化测试的两个难点:1,自动化测试用例的设计;2,程序员对自动化测试的理解和心理反抗程度。我在目前单位有过短暂的尝试:专业的传统测试人员对测试用例进行设计,程序员接收到的需求应该包括正向逻辑的产品需求和测试用例的需求。开发工作完成的标记是:自己写的测试用例在自己的代码上完全通过,代表自己一项开发工作的完成。
说到这里,对方不禁双手伸出拇指!(哈哈哈哈)
或多或少也还有一些别的话题,我自认为那晚像演讲一样很精彩,只不过时间已过午夜,其他的一些细节不太记得了,如果想起或小米参加面试的同学有提起,我再补充了。
整场小米的面试两个部门加起来共计约 7 个小时,这是我经历过的最长时间的面试了……小米的面试很辛苦,今天码字也很辛苦,现在已经是凌晨 1 点半了,如果你觉得上面的经过对你有所帮助或是有意思,就捧个钱场或人场吧: http://me.alipay.com/chunshengster
参考:
https://www.usenix.org/conference/nsdi-07/x-trace-pervasive-network-tracing-framework