后台开发那些常用技术再次小结(一):前端部分

  回家的路途上看了一下《互联网创业核心技术:构建可伸缩的web应用》这本书,该书是使用开发者头条积分兑换的第二本书,而且出版的时间还比较新。看了之后感觉还是比较有收的,至少感觉有了这个视野之后,再观网上那些所谓的高并发、高性能、高扩展性的网站架构,会有一种豁然开朗的感觉。其实怎么说呢,现在强调的高性能、可扩展系统的各套花样本质上也就那么几种模式,大家都是在实际项目中根据自身的业务规模、业务特点做相应的选型、裁剪、适配、调优而已。
  想想其实也不难理解,毕竟我们已经进入后互联网时代这么久了,大规模互联网系统开发中的各种问题、各种坑点想必大家都遇到过了,解决方案也十分成熟可靠,甚至各大云平台都把你所考虑的东西都帮你做完了,你只需要专注自己的业务开发和运维就可以了!或许真如之前别人所对我所说:这些互联网时代听起来高大上的技术,如果不是作为专家深入研究,而是仅仅当做一个现成的组件使用,那么你所“了解”的这些技术,充其量就是跟别人之间多出来的那几分谈资而已,任何一个程序员在对应的环境下都可以快速掌握的。说说也是,毕竟上面说的那本书俺一天不到的时间就看完了,这么看来,感觉操作系统、数据结构和算法、计算机网络、软件调试和优化等看似不起眼的东西,此时便显得更为地重要!
  不过话又说回来,上面的意思并不是说这类知识不重要,即使你不去做他,但是对他的全局观的理解和把控也会影响到自身的业务设计和实现。于是后面将根据书中内容的主线,总体描述现在互联网系统的开发过程中,为了达到构建高性能、可扩展的架构,从前端层、Web应用层、Web服务层、数据存储层部分,配合缓存、异步这些组件进行设计和实现的相关事项,同时配合其他文献分类别进行整理归纳和扩充,算是做一个自我普及的作用吧。
struct

  首先开篇是与前端相关的。说实话对上面列出的那些内容,后端部分个人还是在不同程度上比较了解和清楚的,但是前端部分确实接触不多,而且前端本身也有流行的开发模式和开发框架,在此就不再深入挖掘了,此处就关于一些零碎的知识点进行总结吧。
  一般来说,前端部分作为应用栈上的最顶层,包含了直接与用户交互的一些组件,他们不应当包含业务逻辑,可以最大限度的使用CDN、缓存进行优化;在这之下通常还会有Web应用层,他们通常用轻量级的动态语言和框架实现最小的业务逻辑,主要用于帮助生成用户界面,而具体业务逻辑应该放置到下面的Web服务层完成,这一方面是因为该部分通常变更会比较频繁,而且最好设计成无状态的,以方便实现弹性伸缩。

一、状态管理

  HTTP最早是被设计成无状态、请求-应答模型的协议用于在客户端和服务端传送数据的,这种模式在网站上用于进行文字、图片资源的传输是合适的,在现代互联网的开发中,越来越多的应用场景也都基于HTTP协议进行传输,主要是因为HTTP具有广泛的跨平台、跨网络、多客户端支持,同时传输中的各项细节也都在协议中进行规范明确了,否则如果直接在socket层面上面进行业务开发,就需要处理太多细节方面的东西,而如果重新设计一套协议也不是一件容易的事情。
  可是,HTTP设计之初主要是用于文件资源类传输的,显然不适合当下的各种应用数据的传输,其中最主要的因素是HTTP是无状态(Stateless)模式的,各个请求之间都相互独立,服务器不会保留相关状态信息,所以每个请求都会被认为是一个新的请求;但是现在应用程序的数据大多都是有状态(Stateful)模式,需要维护一种状态来进行相关的业务处理。为此,解决这个问题的方案有:
  将会话状态存储在cookie中
  Session和Cookie机制是HTTP用于维持状态的主流方式。HTTP的协议是无状态的,所以必须在其之上维护形成一个逻辑上的“有状态”连接,当用户第一次连接到服务端的时候,就会建立一个相关的会话,在会话中可以存储或简单、会复杂的任意的信息。通过Session,服务器就可以识别同一个用户的各个请求,并为这些请求创建一个复杂持久的上下文,以便实现诸如购物车、wizard-style配置等有状态的应用效果了,虽然通信的各个HTTP请求严格上说仍然是无状态的。不过会话有一个时效性的问题,服务端不可能永远维护一个不活跃的会话,否则会严重影响服务端的并发量和处理能力。
  Cookie可以用于处理上面的问题。Cookie是服务端产生的(通过返回头中使用Set-Cookie:添加),可以被浏览器保存,并在之后的所有HTTP请求中浏览器都会自动发送给服务端(通过Cookie头)的数据。cookie是和域名相关联的,在浏览器向服务端传输数据的时候回自动将对应域名中的cookie数据放入HTTP Header中传输给服务端,所以服务端的开发者可以方便的提取这些cookie数据。在了解Cookied额机制后,几乎现在所有的现代服务端程序都会对新连接生成”session ID”字段并将其作为cookie内容传递给浏览器,浏览器保存了该内容之后,及时该会话结束了,下次重连的时候也可以在服务端查找到对应的session ID,从而实现一种有状态的效果。
  在业务开发过程中,服务端就必须要检查每个请求,看其是否具有会话标识;如果存在会话标识,还必须根据业务检查该会话是否过期;在实际的使用中,还需要根据该会话ID调取其管理的相关资源。如果需要存储的数据比较的少,那么所有的状态数据完全都可以放置在Cookie当中,而如果会话的数据比较的多,通常会话数据需要额外存储在外部存储系统中。
  将会话数据存储在外部数据存储系统中
  这种情况更为的常见,因为cookie会在每次请求中传输,如果保存的东西太多显然降低了通信的效率和安全性。该情况下cookie中就只需要保存sessionID、会话令牌等简短的数据当做索引,真正会话数据不需要在请求响应之间传递,而是直接从外部存储(比如内存、数据库、文件系统等)直接加载就可以了,常用的外部存储比如Memcached、Redis、DynamoDB,都是根据key-value读写低延迟的优化存储服务。
  ASP的VIEWSTATE机制
  这个我还不太好归类,主要是之前爬虫的时候,在一些基于Windows/ASP构建的网站中遇到的。在第一次使用GET方式请求网站的时候,在返回的页面中会嵌入一些’VIEWSTATE’的字段,后面的页面都是使用POST方法请求的,每次得到的页面都会更新’VIEWSTATE’字段,而且在下一个页面的请求中需要添加这个不断变化的’VIEWSTATE’。
  既然大家都是做基于Linux平台下的开发的,相比这个VIEWSTATE机制遇到的也比较少,其实还真不知道巨硬公司的这个字段是干吗用的。

  虽然在业务上(而不是协议上)实现有状态逻辑显得比较麻烦,但这反而也是HTTP的优势所在,因为在无状态的情况下客户端可以访问任意事例而不用担心结果会有差异,因此这样的应用具备了强大的可伸缩性。

二、智能DNS、CDN和负载均衡

  前端优化主要有智能DNS、CDN和负载均衡扩充的方式。

2.1 智能DNS和CDN

  在国内,电信行业的历史渊源造就了对骨干网南电信、北联通、其他运营商忽略的格局,虽然电信和联通的网络高速互联不会有什么技术上的问题,但实际的效果确实互访的延时很大,于是在买虚拟主机和服务器的时候,双线网络都会作为一个卖点列出来。大家会发现当访问一些老网站、某些下载资源的网站时候,那时候智能DNS解析还没有普及开来,所以网站会给出电信、联通的链接让你去点击,以便让用户有较好的网络体验,不过以我们现在的情商看来,这种让用户手动选择线路的体验真是太差了。
  智能DNS就是把上面手动选择的操作智能化,因为域名的访问最终都是转化成IP地址才能直接使用的,所以智能DNS就会根据用户所在的网络返回适宜的IP地址。因为朋友的信任,自己手头有好几天服务器,前段时间还折腾过cloudxns的智能解析的,可以细分到各个省份的各个线路解析成什么IP地址,不过国内网络环境复杂,到底某个地区的某个线路访问自己的某个主机比较快,测试起来比较麻烦,最后也作罢了。在书中提的较多的是GeoDNS,其号称是一种基于客户地理位置进行域名解析的DNS服务,其实按照上面的运维方式手动设置各个地区各个线路的DNS记录的话会累趴,如果能做到给出所有目的主机IP列表,服务商自动给予最优化将会是多么美好的事情!书中还提到了Amazon的Route 53解析服务,其解析的结果不是基于地理位置,而是根据响应延迟来决断的,可见这算是非常先进的智能
DNS解析了。
  现在的机房也讲求BGP(边界网关协议),其实际是实现线路的智能路由的功能,其实现成本较高。不过这都是机房和运营商相互合作的,对于我们普通用户可玩性不大。
  CDN从本质上来说就是智能DNS+缓存结合的产物。CDN对网站的优化效果十分显著,当访问静态内容的时候不仅可以加快用户的响应时间,而且可以大大减少后端服务器的压力和带宽/流量消耗(CDN提供商的带宽费用肯定比自己服务器带宽费用便宜很多),一定程度上还能起到抗攻击的效果。不过CDN的只对静态资源加速效果较好,动态内容很多还是需要访问原始服务器才可以,实践的时候可以根据业务特点将某些动态资源静态化,以此可以享受CDN带来的好处。

2.2 负载均衡

  通过DNS轮训机制本身就自带负载均衡的效果,比如我们每次PING百度的IP地址都不一样。不过基于DNS的负载均衡问题比较多,比如DNS是一个分布式的系统,在本地主机到各层次的服务器都有缓存效应,这使得服务器的管理和变更时效性很差,所以实践中使用的较少。负载均衡还可以通过硬件实现,这里复杂均衡器性能较好,但是专业性太强,而且售价很高。
  其实,现在各大云计算厂商也提供负载均衡的服务,我查阅了qcloud的CLB服务,其实很多都跟Nginx的负载均衡功能类似,说明负载均衡已经是一项十分成熟的技术了。负载均衡现在比较有意思的东西是和弹性按需计算相结合,比如CLB可以根据业务的负载进行自动的横向扩展,自动的创建和释放CVM实例,而Amazon的ELB也有这样的功能,可以自动增加和删除EC2运算实例。这种方式的确可以自动优化资源利用从而节省成本,但是这样的弹性运算实例不能再上面存储任何数据,至少保证数据是一次性的(比如缓存),而且删除和销毁实例的时候要保证运算实例是真正空闲的才可以;其次这种弹性机制存在着“预热”的问题,对于网站流量爆发的情况,自动伸缩往往不能立刻响应,从而影响服务的可用性。
  一般的用户使用的都是软件负载均衡,其中最著名的就是Nginx和HAProxy两个开源软件,他们都支持三层负载均衡和七层负载均衡,HAProxy本人没有接触过所以不太了解,Nginx本身就是一个HTTP服务器,所以可以预见对HTTP的七层代理支持会比较完善。负载均衡通常都能实现很大的并发量和很快的数据包处理,但是单机也会有性能极限的时候,水平扩展负载均衡通常采用DNS轮训的方式,因为负载均衡器不会做业务处理,通常服务比较稳定,同时也不会频繁的增加删除负载均衡器实例,而且负载均衡器本身也是无状态的,可以进行随意互换的。
  负载均衡的好处
  a. 安全性
  负载均衡器像是个卫士一样挡在服务的最前线,所有的流量都会先经过他们,所以可以隐藏后端服务器的IP地址、网络和服务结构等信息。同时负载均衡本身还可以设置各种过滤和规则,对用户的访问进行控制,以对后端整个服务起到保护作用。
  b. 服务器维护
  无论是维护原因还是失效的原因,服务器都可以从负载均衡的转发对象中方便的移除。负载均衡器本身就有健康度检查机制,当请求失败率达到一定程度就认定该服务器失效并停止流量转发;同时正常工作的服务器可以配置负载均衡让其不在接收新的请求,正在服务的连接结束后服务器就可以安全下线了,对于不间断服务滚动更新等各项需求都很容易实现。
  c. 无缝伸缩处理能力:
  和上面说的一样,负载均衡器结合弹性计算,可以优化资源的利用率和节省成本。
  d. 高效的资源管理:
  负载均衡的一大功能就是SSL终结(SSL卸载),在负载均衡器上处理所有的SSL加密解密工作,而在服务和数据中心全部采用非加密传输方式。SSL是在外网不可信环境下进行数据保护的操作,而经过负载均衡之后就是内网环境,尽早卸载SSL可以最大程度地降低对性能的影响。

三、安全

  在HTTP的开发中,会有很多安全性相关的问题,这一方面主要是之前的HTTP协议是明文传输的,可以说上个世纪的协议在当下暴露出各种各样的安全性问题。
  同源策略和会话劫持
  同源策略(Same-origin policy)是保证同一站点的资源处于一个可信的范围内,他们之间可以相互访问而不受限制,但是会阻止不同站点对文档、资源的访问和操作(同源策略涉及到的是访问内容,比如Cookie、文档内容嵌入等,而连接可以随便插入不受此限制)。同源中相同站点的三要素是:相同的协议、相同的主机名、相同的端口号。
  同源策略是增强了安全性,但是也给开发带来了麻烦,因为在现代互联网开发扩展中,资源和服务拆分到不同的主机上面十分常见,那么根据同源的三要素,这些主机之间资源将不能相互操作。不过当然针对这种情况都有相应的解决方式,对于最常见的Cookie访问和AJAX请求,可以:
  Cookie访问:在相同一级域名下的Cookie共享十分简单,只需要在调用Set-Cookie的时候添加domain=.example.com进行制定就可以,后面其子域名就可以共享Cookie了;
  AJAX请求:同源策略规定AJAX请求只能发给同源的网址,否则就会报错。现在解决这个的方法最常见的是跨源资源共享CORS(Cross-Origin Resource Sharing),该方式需要浏览器和服务器端同时支持,当支持的浏览器发现AJAX请求跨源后,会自动通过添加Origin附加头信息;服务端通过检查这个字段,判断是否同意这次请求,如果不允许就返回不带Access-Control-Allow-Origin头字段的响应,这时候浏览器就知道出错了,否则返回一些额外的头部信息(Access-Control-XXX)并附带正常响应结果。

  跨站脚步攻击(XSS)
  当允许用户输入的HTML和javascript在网站上显示的时候,极容易遭受这种攻击。
  如果服务端对用户输入的内容不做检查和无害处理,这些内容嵌入到网页中进行显示的时候,就会现实和执行这些HTML和javascript代码,后果将会是不可估计的。
  除了对用户的输入进行检查和无害处理外,还建议使用安全的输入格式,比如Markdown格式以提供丰富的输入格式并保证安全,这一点v2ex做的就比较好。

本文完!

参考