华尔街见闻的运营方上海阿牛信息科技有限公司是全球金融信息服务提供商,每天全平台为近200万用户提供资讯、数据、研究等服务。旗舰产品华尔街见闻 APP 长期位居各应用市场财经资讯类客户端第1位。由于将重大事件、市场的基本面变化和100多种全球资产价格紧密关联,在金融领域具有极高渗透率。首创的7x24快讯模式已经成为在中文世界理解全球市场的最快来源。也因此,该产品有技术架构复杂,需要高并发承载能力等特性。
PHP monolithic
架构,功能按模块划分,日积月累,最后模块达到60+个,新人接手项目会拿到一整个系统的代码,以及需要整个系统的配置,而他可能只需要专注开发不到1/10的业务。PHP
单体应用在生产环境服务的时候,所有业务都跑在一个程序里,增加了系统的脆弱性,一个隐藏的性能问题,能在服务量激增的时候成为压垮骆驼的一根稻草。每台服务器部署相同的服务端PHP代码,由PHP-fpm解释执行,通过Nginx进行反向代理。
因此,在2016年11月至2017年3月,我们采用微服务架构启动重构,尝试解决一部分上述问题,在伸缩性上能以服务为单位进行拓容,同时,这一设计会在某些方面增大我们的开发成本和运维成本。
微服务架构图
每台服务器上均衡地部署服务,LB 接受用户的请求,将请求转发到API gateway
,API gateway
向服务发现查询具体服务的IP
和端口,服务执行完业务逻辑后向上返回数据。
我们选择golang
作为我们的后端开发语言。
golang
在性能和开发效率上有很好的平衡,语法上很简单,并发编程简单高效,基础库健全。pprof
包可以 profile 当前程序的 CPU 消耗、内存占用、锁状态、channel
阻塞等,非常便利我们定位问题。golang
在华尔街见闻已经有过比较多的应用,工程师使用golang
开发几乎0学习成本。
拆分的原则是通过服务功能划分,尽量避免双向依赖。经过拆分我们目前有13个服务,包括用户、内容、实时新闻、评论、搜索、商城、支付、三方代理等服务。
服务间使用protobuf
协议对数据进行编码,使用UDP
作为传输协议。
Etcd 搭建多节点高可用的服务发现。
我们选择Hystrix作为服务保护以及服务降级的方案。
每个服务开发者,需要定义自己服务接口的并发量、超时时间以及 fallback 方法。
我们选择了Kubernetes
。
Docker 1.12
之后已经将Swarm功能集成到Docker Engine
,能以最少的配置启动Docker
集群。经过简化和设计的控制台API,方便地管理集群、调整服务如控制服务的数量、CPU、内存限制等。往集群内加入机器非常简单,只需要运行一条命令即可。使用manager-worker架构,manager作为调度节点,支持高可用。 但遇到了非常致命的问题,比如频繁更新服务的时候会出现服务访问不到,某服务的负载均衡后挂载的服务IP是其它服务的,服务之间的通信有几率出现超时问题,归根结底,还是社区正在不断完善 swarm,有很多不稳定的地方,网络方面没有进行优化。Docker
,相比Docker Swarm
来说,它的概念更多,分层更细。功能方面多于Docker Swarm
,支持一些高级功能如秘钥管理、配置管理、自动拓容等。在生产环境的应用比较广泛,稳定性更高。Docker Swarm
和Kubernetes
都没有成功,我们直接裸机部署。裸机部署的需要解决单机端口冲突,如果一个服务在一个服务器上最多只部署一个,那么可以通过写脚本,并划分服务器角色的方式进行部署,利用ansible
可以定义user
服务集群、content
服务集群、comment
服务集群等,通过分发二进制文件的方式让服务启动,这样的方案要考虑到服务更新、服务重启、服务删除等逻辑,同一时间只有部分节点更新,在服务未更新成功的时候流量暂时不能打到正在更新的节点。
由于之前使用github
开发人员的代码提交在有访问外国网站工具的帮助下速度依然不是很理想,我们自建了Gitlab
仓库,自此开发过上了幸福的生活。
swarm
和kubernetes
是基于docker
快速创建删除服务,通过增加容器为服务拓容,缩减容器为服务缩小规模,所以所有项目必须要构建docker
镜像。按项目类型划分,我们遇到3种镜像打包情况。
后端服务90%是golang
项目,针对golang
的镜像,我们采取将golang
项目编译成可执行文件,基于最小的alpine
镜像打包入docker
,这里遇到过一个问题,就是alpine
里缺少openssl
的证书,无法支持https
,我们自定义了新的基础镜像,不仅将证书文件打入镜像,同时为了线上调试方便,增加了tcpdump
、strace
、bash
等工具,在初期调试容器间通信问题时发挥重要的作用。
见闻的后台以及m站基于Vue
,编译后生成的静态文件打入镜像,通过nginx
访问。
为了支持HTTP2,我们打入nginx
镜像缺少的证书。
主站PC站基于nodejs
、Vue
实现服务端渲染,所以不仅需要依赖nodejs
,而且需要利用pm2
进行nodejs
生命周期的管理。为了加速线上镜像构建的速度,我们利用taobao源https://registry.npm.taobao.org
进行加速, 并且将一些常见的npm
依赖打入了基础镜像,避免每次都需要重新下载,镜像打包从开始的3分钟缩减到1.5分钟。
三类镜像结构
我们利用Gitlab CI
配置了测试、镜像构建、镜像发布、自动部署等流程,后端服务从提交代码到测试分支到测试环境自动部署完成花费1.5分钟,前端服务平均为2.5分钟。
CI任务中的test->build->docker->deploy流程
最终我们选择了腾讯云的容器服务,主要基于以下几点考虑:
我们将我们的应用重构成微服务的架构,每个微服务部署成腾讯云容器服务上的一个服务,前端接入通过一个负载均衡。后端服务间可互相访问。
通过VPC进行网络隔离,将网络划分为生产环境、测试环境,在生产环境中又划分backend子网和data子网,设定子网之间的访问规则,增加服务端的安全性。
利用locust模拟线上请求的比例,利用2台16核的压测机在内网对10台16C32G的机器上的服务进行压测,达到1w/s QPS以上,并且服务的负载并没达到极限,这已经是之前PHP
生产环境20+台16C32G服务器能达到的QPS。
通过追踪API调用链的流向与耗时,我们可以找出性能的瓶颈。我们通过zipkin实际优化了几种情况:
由从外部系统表征到内部日志,我们将监控分为API健康,程序错误报警,以及服务器/容器负载。
排查问题的流程一般有两种情况,一种是用户发现问题,申报问题,开发人员跟进问题;一种是我们的监控优先发现问题,开发人员在用户反馈前跟进并修复。在报警方面,我们通过为监控系统谨慎设置报警阈值,当触发报警时,开发人员会收到邮件。
这里我们在报警的定义上有过思考,即什么样的报警算是有意义的?我们遇到过每天10几条重复的报警,通常开发人员开始时会对报警非常重视,当重复的报警一再出现,渐渐失去了对报警的关注。所以我们有解除一些不必要的报警,并且对剩余一些报警进行调查,甚至有些警报是因为监控工具本身的不准确引起的。
我们设置默认的时间区间是5分钟
后端程序内接入Sentry
日志报警系统,golang
程序捕获panic
日志以及error
日志,并发送报警邮件。
通过在服务器上运行telegraf daemon
进程,收集服务器metrics并发送给influxdb
,使用Grafana
作为前端面板,对服务器负载以及容器的平均 CPU 、内存占用率进行监控。
本文介绍了华尔街见闻微服务的实践情况。经过几个月的开发测试,已经完成了线上服务从PHP
到Golang
的转型。
在服务的稳定性上经历了考验,支撑了几次重大新闻的高流量。
在开发流程上,搭建了完善的自动化工具,减少了人工操作的重复性和误操作概率。
在运维方面,由于监控系统对系统完整的监控,与Kubernetes
健全的上线、下线、回滚、拓容功能配合,能以极快的速度处理线上问题。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。