文章首发于公众号:松花皮蛋的黑板报
作者就职于京东,在稳定性保障、敏捷开发、高级JAVA、微服务架构有深入的理解
以一个经典问题抛砖引玉,当用户在浏览器中输入一个URL到底发生了什么?
常见的URL格式是http://www.liangsonghua.me,由协议+域名+端口号组成,这里涉及到一个不可轻视的知识点,就是跨域,浏览器有一个同源策略限制,协议、域名、端口号有一个不同就会发生跨域冲突,从而保证了其他站点不能非法操作正常站点的cookie和修改dom元素,重要性不言而喻。当不得已冲突时,可以通过JSONP请求、添加允许跨域响应头、使用代理转发的方式获取资源。不过请记住,尽量不要使用代理转发的方式,因为它违背了环境标准化准则,我们应该保证扩容新服务器时能取得正确、最新的配置,比如服务日记输出路径应该形成一种共识规范,这种称为”约定大于配置”,它的好处是,除了简化配置工作外,还可以提高沟通效率,另外标准先行是持续交付和架构改造技术实施的前提条件
WEB系统早已从久远的单系统发展为多系统,面对众多的系统,用户不可能一个一个登录然后又一个一个地注销,况且每个系统都重复实现登录业务和管理用户实在上浪费成本,统一登录系统应运而生,假设是plogin.liangsonghua.me,随着业务的发展,某些业务方想保留自己独有的域名,假设是plogin.lsh.com,我们当然应该也必须理解和接受这样的需求,毕竟再小的个体也有自己的品牌,但是同源策略限制了无法下发登录态cookie,不过可以通过别名CNAME解析处理,也可以将登录态附到域名的URL上,然后业务方自行适配,比如plogin.lsh.com?sso_ticket=utm_site_www_liangsonghua_me。当客户端禁用cookie时,也只能选择后者了
客户端请求头部信息一般是固定的,代理服务器一般会将其进行缓存,有缓存的地方就有缓存大小控制的江湖,缓存容量不足时可以进行页面置换或者直接抛弃,代理服务器选用的是后者,缓冲区大小默认为4*128K,当URI过大或者Htttp Header过大时直接向上抛出400或401错误
为了规范化管理,通常会使用不同的根域名区分业务,比如CDN服务类、对外展示类、服务间调用类,或者又比如泰国站、印尼站,或者又比如公网、内网、预发,那么此时你就需要注意是否可以直接复用统一登录,是否支持在内网环境下通过手机端联调
或许你刚出于好奇逛了下www.liangsonghua.me,那么浏览器是怎么知道去哪获取资源的呢?实际上客户端必须先通过分布式DNS系统进行本地解析或者权威服务递归解析获取真实IP,然后才能建立连接和获取响应。为了提高可靠性和吞吐量,一般采用多机房多台容灾部署,统称多个集群,避免单点故障,那么上线时就需要采用一定机制的发布策略比如滚动发布,减小上线影响范围,发布时先摘取流量,发布成功后利用本地DNS缓存也就是在/etc/hosts指定映射,进行服务验证,当无异常时再恢复流量。通常域名证书不会直接配置在后端实例上,所以在绑定hosts的时候无法使用HTTPS访问,你可能会发现访问时自动跳转到HTTPS请求,然后报错了,背后其实是浏览器的HSTS协议,它代表的是HTTPS严格传输协议,它是一个网络安全政策机制,当第一次通过HTTPS请求时,服务器响应strict-transport-security: max-age=7776000rn头,其含义是浏览器在max-age到期前只能通过安全的HTTPS连接与网站交互,避免中间人劫持
域名解析类型常见的有A记录、AAAA记录、CNAME、TXT记录,A记录又称IP指向,可以设置子域名并指定到目标主机地址上,AAAA记录用于IPv6解析,IPv6的设计目的是取代IPv4,从而解决IPv4地址枯竭问题,同时它也在其他方面对于IPv4有许多改进,正常网络请求下,客户端会向DNS服务器发起域名A记录和AAAA记录的解析请求,获取到结果后,会同时使用IPv4和IPv6两种链路尝试建立连接,默认优先使用IPv6链路完成后续的网络请求,当网络时延比较大时才会使用IPv4地址,也就是说客户端是处在双栈环境。另外一个TXT记录,它一般配合在域名前加上_dnsauth,完成HTTPS验证
前面说到流量集群,也就是一个域名解析到多台主机上,随之而来的麻烦是如何避免数据倾斜,常见的做法是简单轮询、加权轮询、粘性Session、最少连接、随机算法,将用户流量转发到后端,假设域名www.liangsonghua.me对应有三台主机,分别是A、B、C,权重分别为1、2、3,加权轮询得到的序列顺序则为A、B、B、C、C、C,一般用于后端主机配置有较大差异的场景。粘性Session也称为一致性哈希算法,它将Key和实例标识的值Hash后映射到同一个圆环上,Key所对应的值为顺时针查找的第一个Value值,从而保证了相同来源的请求始终会到达相同的后端实例,当有新实例节点增加或者删除时也只会影响极小部分的请求映射。流量集群带来的第二个麻烦是如何避免DNS缓存,这里先直接给出解决方案,域名解析到一个主机上,然后通过该主机再转发到真实的后端实例,每次变更实例状态或者数量时,只更新转发映射路由表即可,这个主机称为Virtual IP Address,简称VIP
VIP是一个负载均衡器节点,有硬件和软件之分,硬件的成本比较高,使用率比较低。负载均衡器又分为四层和七层负载均衡器,顾名思义,四层的工作在TCPIP协议栈上,通过修改请求报文的(源/目的)地址和(源/目的)端口来转发,比如LVS,一个VIP主机对应一个域名,适用于每秒QPS超过一万的业务。七层的工作在应用层,将HTTP请求数据转发到具体的服务器上,比如Nginx。LVS常见的两种工作模式是NAT地址转换和DR直接路由,NAT地址转换模式下响应会经过VIP,而DR直接路由则不会,另外一种软件实现Nginx的响应返回也不经过VIP。可以根据成熟度选择不同的方案,尽量减少VIP的压力,一般VIP的数量会远小于后端真实实例,它本身也会出现性能瓶颈
NAT网络地址转换常用于将公网和内网进行隔离后进行映射,也就是说在内网线上调用外网服务如微信接口、银行接口,都是借助的NAT技术,它不是一种安全机制,只是降低了对公网地址的依赖,不过从客观评价的角度来看NAT技术一定程度下拖慢了IPv6的发展。一般NAT出公网节点数量都比较少,内网线上在没有申请权限的前提下无法访问外网是正常的,另外也不推荐在线上访问公司内部的公网服务
不少公司都是数据驱动型的,首当其冲要解决的其实是大规模数据的存储问题,幸运的是,我们可以将多个容量较小、相对廉价的磁盘进行有机组合,从而以较低的成本获得与昂贵大容量磁盘相当的容量、性能、可靠性,这种技术称为RAID独立磁盘冗余阵列,可以看作是一种垂直伸缩。常用的RAID技术等级有RAID-0、RAID-1、RAID-10、RAID-5、RAID-6,RAID-0是数据在从内存缓冲区写入磁盘时,并发写入N块磁盘,显然具有极快的数据读写速度,但是数据备份能力极差,只要有一块磁盘损坏,数据完整性就得不到保证了。RAID-1则是将一份数据同时写入到两块磁盘中,RAID-10充分结合了RAID-0和RAID-1的特点,它将所有磁盘平均分成两份,数据同时在两份磁盘中写入,相当于一半磁盘充当备份。RAID-5和RAID-6则分别可以允许损坏一块、两块磁盘,当磁盘损坏时可以通过其他磁盘上的数据和校验数据进行恢复。总结一下RAID技术等级的区别,访问速度:RAID-0>RAID-5>RAID-6>RAID-10>RAID-1,数据可靠性:RAID-1>RAID-10>RAID-6>RAID-5>RAID-0,磁盘利用率:RAID-0>RAID-1>=RAID-0>RAID-5>RAID-6
然而RAID技术不是银弹,磁盘故障导致服务实例不可用的概率远比实例本身崩溃死亡的要高,此时人工进行流量切换操作的话相当繁琐,VIP应该充当一个自动探活的角色,每间隔一段时间进行健康检查,也就是熔断,类似家里的电路保险器,当服务连通器异常时将实例标记为不再接收新请求,等待已发送的请求处理完成或者超时后,开始标记为下线,当服务恢复时并且一段时间内都是健康的才重新自动接入流量,实际上业界的负载均衡器都支持这个特性
前面用了很大的篇幅描述了请求的过程,我们将探讨点转移到持续交付的始端,代码是如何部署到线上的?发布系统一般需要有什么样的功能?
一个好用的发布系统必须具备易用、快速、稳定、容错力强,必要时可迅速回滚等特点,变更流程可以抽象为:(1)、通知下游调用方自己现在正在上线;(2)、分发新的版本构建物到服务器上,只修改last版本的软链指向;(3)、运行命令reload变更重启服务;(4)、验证服务的健康状况 ;(5)、通知下游调用方上线已完成。另外,发布系统最好能提供集群分组管理、(实例、主机、分组、全局)静态配置管理、构建缓存、一键重启服务、按比例分批部署、预热功能,完善而不失精简。随着Docker容器的日益盛行,镜像发布慢慢开始流行起来,研发将代码仓库地址、配置、环境、CPU参数、内存参数、磁盘大小参数封装成独立版本的镜像,然后指定实例数直接发布即可,无须手动申请机器。每一次变更都是一次发布,每一次发布都是一个独立的镜像启动,这种称为不可变模型,它能让我们在快速灾备、快速恢复系统、增强系统健壮性等场景下收益。不过需要注意镜像层不能过大,镜像制作应该易上手,重新发布和异常拉起时容器IP地址无变化
上下游应用关联信息一般由CMDB功能模块负责。在标准化建设时,需要对关键的对象进行拓扑关系识别,形成标准后进行固化到某个信息管理平台中,也就是CMDB。信息固化不是目的,也没有价值,只有对信息动态流转起来才有价值,我们可以基于应用这一层做弹性扩缩容、稳定性平台、成本控制等等。其他工具也可以借鉴CMDB理念,比如归属权限管理、用户反馈信息定位到具体业务然后定位到具体的人、测试数据模块化管理
发布结束并不代表一个交付的结束,发布往往是事故的开端,所以需要对发布质量进行监控,确保所做的更改不会产生负面影响,不要等到用户来反馈错误,可以结合以下五大类监控快速发现发布应用的异常表现,包括:(1)、用户侧监控,关注的是用户真正感受到的访问速度和性能;(2)、业务监控,关注的是核心业务指标的波动;(3)、应用监控,即服务调用的监控; (4)、系统监控,即基础设施、虚拟机及操作系统的监控;(5)、网络监控,即CDN与核心网络的监控
服务上线或者应对大促运营活动时,都需要考虑容量规划,容量规划离不开压力测试。压力测试就是对不同场景进行模拟,然后验证系统容量和性能是否可以满足某些极端情况,验证系统性能优化的有效性。压力测试从场景角度可划分为六个纬度,分别为一般测试、负载测试、压力测试、大数据量测试、稳定性测试、配置测试、可恢复性测试。一般测试就是验证在正常情况下,是否能满足性能指标要求,比如持续30分钟不失败并且耗时在一定范围内。负载测试就是利用日常统计数据然后不断按比例新增用户操作,直至系统性能出现拐点,此时长时间运行,观察系统是否正常。压力测试就是在系统负载运行的情况下,继续增加压力,观察系统是否出现内存泄漏或者Core Dump。大数据量测试主要是针对数据库有特殊要求的系统进行测试,检查累积或者瞬间大量数据时,系统是否能稳定运行,实时计算模块是否能正常运转。稳定性测试,则是系统在满足性能指标的要求下,进行长时间的运行,观察是否能一直正常工作。配置测试,则是在不同的软硬件配置环境下,进行测试以找到最优分配原则。可恢复性测试,则是关闭、重启服务,观察过程中是否有失败逻辑,还可以验证服务是否具备幂等性。压力测试从落地角度又可分为单机单应用压力测试、单链路压力测试、全链路压力测试、线上流量回放、线上流量引流、流量模拟、数据读写施压。容量压测的技术方案整体来说还是比较复杂的,施压时还需要关联监控,避免过多的错误异常,对性能结果造成干扰
压测过程中我们会关注基础监控,比如TCP重传、CPU使用率、平均负载、内存、SWAP、网络流量。主机报文重传是TCP最基本的错误恢复功能,它的目的是防止报文丢失,报文丢失的可能因素有很多种,包括但不限于网络设备或线路异常、数据路径上的流量突发导致链路拥塞、网卡故障、服务端性能下降、代理节点或者VIP性能下降。如果你压测的场景是大数据,请勿必留意该监控项和网络流量监控项,重传发生的次数和时间周期很小时影响才微乎其微,网络流量入出速率不超过50Ms时也不需要过于在意。CPU使用率是单位时间内CPU使用情况的统计,以百分比的方式展示,它表示除了空闲时间外的其他时间占总CPU时间的百分比,日常运行中CPU使用率保持在25%左右,大促运营活动时保持在30%左右会比较好。平均负载描述的是系统的平均活跃进程数,理想情况下它等于逻辑CPU个数,这表示每个CPU都恰好被充分利用。目前主流的技术栈是Java,程序由JVM虚拟机进行翻译,JVM通常会预先分配好内存,所以在监控平台看到的内存使用率一般是固定的、偏高,我们可以同时关注下Swap交换分区,它的作用可简单描述为:当系统的物理内存不够用的时候,就需要将物理内存中的一部分空间释放出来,以供当前运行的程序使用。那些被释放的空间可能来自一些很长时间没有什么操作的程序,这些空间被临时保存到Swap空间中,等到那些程序要运行时,再从Swap中恢复保存的数据到内存中
文章来源:www.liangsonghua.me
作者介绍:京东资深工程师-梁松华,长期关注稳定性保障、敏捷开发、JAVA高级、微服务架构
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。