0%

WEB 服务器探究

介绍 web 服务器基本概念,和常见的服务端演替路线。

几种不同名字的服务器有什么区别?

WEB服务器、应用程序服务器、HTTP服务器

具体的内容可以看 这篇文章 和它的 翻译版

我只说结论:

  • HTTP服务器本质上也是一种应用程序
  • Web服务器的基本功能就是提供Web信息浏览服务。它只需支持HTTP协议、HTML文档格式及URL。与客户端的网络浏览器配合。因为Web服务器主要支持的协议就是HTTP,所以通常情况下HTTP服务器和WEB服务器是相等的,说的是一回事。
  • 应用服务器可能包含PC机上运行的GUI进程,web服务器,甚至其他的app服务器。app服务器和客户端之间的通信并不局限于简单的显示标记,而是可以由程序逻辑,比如数据表单、方法调用,而非静态的HTML。这样,客户端程序就可以按需去用。
  • 举个例子,QQ 的后台是应用程序服务器,QQ 空间的后台是WEB服务器。

tomcat 与 nginx,apache的区别是什么?

知乎有一个关于这个问题的答案 tomcat 与 nginx,apache的区别是什么?
还有一个 Nginx 和 Apache 的对比 三大Web服务器对比分析

总的来说:

  • 应用程序服务器

    • Tomcat能够动态的生成资源并返回到客户端。
      • Java Servlet技术以及衍生的Java Server Pages技术可以让Java程序也具有处理HTTP请求并且返回内容。
      • 它有直接部署 java 程序的能力
    • Tomcat运行在JVM之上,它和HTTP服务器一样,绑定IP地址并监听TCP端口
      • 管理Servlet程序的生命周期
      • 将URL映射到指定的Servlet进行处理
      • 与Servlet程序合作处理HTTP请求
  • web 服务器

    • Apache和Nginx都能够将某一个静态资源文件的内容通过HTTP协议返回到客户端。
      • 无论何时、任何人访问它得到的内容都是完全相同的
    • Apache和Nginx可以通过其他模块来支持动态资源
      • 通过Shell、PHP、Python脚本程序来动态生成内容
      • 其他语言可以通过 CGI、WSGI 等协议接入 web 服务器。
    • Apache vs Nginx
      • Apache 的 rewrite 更强大,社区模块非常多,bug极少
      • nginx 轻量,用 epoll可以扛并发,可以反向代理

各种语言的简易web服务器

这个地方的 web 服务器应该理解为只能访问静态资源的 web 服务器。

换句话说,在硬盘某个地方放一些文件,搭起服务器后,可以在浏览器里通过网络协议来访问这些资源。

C++

Golang

Python

Nodejs

  • 推荐 light-server

PHP

正经网站的结构

这部分内容的结构来源于【大型网站技术架构】,然后加上我自己的理解和常见的工具、现代化 MVP 处理方式。

正经网站基本上都需要需要存储结构化数据,有不同身份用户交互的,需要存储音乐图片视频等媒体文件的,然后是用网络协议调用外部服务。

  • 在书里说是 xxx 服务器,但更准确的说法是 xxx 服务,比如缓存,我们很多时候可以和应用程序使用同一个服务器硬件。
  • 更现代的方式应该是监控各种服务的资源占用情况,然后根据资源占用情况混合部署,充分利用 CPU、内存、硬盘 IO 性能等等
  • 这里可以把 xxx 服务器理解为抽象概念的服务器,哪怕调用 memcache 这样的插件也是走网络协议 socket 或者 http 协议
  • 各个部件用不同的协议和端口,所以抽象层逻辑上的单机和多机区别不大,集群带来的效果更多关注总的 CPU 算力、总内存、总硬盘 IO 量等等。

初始阶段的网站架构

一般来讲,大型网站都是从小型网站发展而来,一开始的架构都比较简单,随着业务复杂和用户量的激增,才开始做很多架构上的改进。当它还是小型网站的时候,没有太多访客,一般来讲只需要一台服务器就够了,这时应用程序、数据库、文件等所有资源都在一台服务器上,网站架构如下图所示:
web-server-1
HINT:

  • 这个级别基本上属于本科生大作业级别
  • 直接在应用程序里面写东西去连接 client,操作文件,操作数据库
  • 几乎没有抗风险能力,一挂全挂
  • 面向自己的程序这么写是没问题的,开发起来很快,也不需要多余的轮子
  • 比较好的实践是自己封装一些lib,用来隔离各种不相关操作

应用服务和数据服务分离

随着网站业务的发展和用户量的增加,一台服务器就无法再满足需求了。大量用户访问导致访问速度越来越慢,而逐渐增加的数据也会导致存储空间不足。这时就需要将应用和数据分离,应用和数据分离后整个网站使用 3 台服务器:应用服务器、文件服务器和数据库服务器。这 3 台服务器对硬件资源的要求各不相同:应用服务器业务逻辑,需要强大的CPU数据库服务器对磁盘读写操作很多,需要更快的磁盘和更大的内存文件服务器存储用户上传的文件,因此需要更大的磁盘空间此时,网站系统的架构如下图所示:
web-server-2
HINT:

  • 这个阶段一般关注两件事:数据库索引、缓存系统
  • 做一个好的数据库设计,让并让绝大多数查询打在索引上,避免掉不必要的多级join 、limit 很大数字 等速度极慢的艹库行为。
  • 内存 io 实力比硬盘高很多数量级,设计好精巧的缓存能把网站抗压能力提高一到三个数量级。因为很多场景,82原理可以用两次(80%的流量打在20%的页面和数据库上,64%的流量打在4%的页面和数据库上)
  • 绝大多数时候,认真设计数据模型+靠谱的缓存就能解决大部分并发,比加机器更有效

使用缓存改善网站性能

随着用户再增加,网站又会一次面临挑战:数据库压力太大导致整站访问效率再此下降,用户体验受到影响。一个网站,往往 80% 的业务访问集中在 20% 的数据上,比如微博请求量最多的肯定是那些千万级粉丝的大 V 的微博,而几乎没有人关注的你的首页,除了自己想起来之外根本不会被打开。既然大部分业务访问集中在一小部分数据上,那就把这一小部分数据先提前缓存在内存中,而不是每次都去数据库读取,这样就可以减少数据库的访问压力,从而提高整个网站的访问速度。 网站使用的缓存一般分为缓存到应用服务器或者缓存在专门的分布式缓存服务器。缓存到应用服务器自己的访问速度快很多,但是受自身内存限制,往往不太适用。远程分布式缓存使用一个集群专门负责缓存服务,当内存不够还可以轻松得动态扩容。
web-server-3
HINT:

  • 缓存是个可以分很多层级的事情
    • 全局级别。比如全局的通知,这类东西全局缓存一份就可以。
    • 页面级别。每个页面缓存一定时间,挡掉别人的连续刷新打库。浏览器也有类似机制。
    • 模块级别。比如某些资源的计数器,不是强一致性要求的功能一般可以这么做。
    • 变量级别。最常见的缓存,推荐一篇文章缓存更新的套路
  • 常用的两个Redis与Memcached
    • redis 内存内核是单线程的,且支持事务,支持丰富的数据结构,不仅仅是全内存、有 swap 空间、支持 CoW 持久化数据。
    • memcached 更充分发挥 cpu 等硬件的潜力,有 cas 支持,吞吐量更大。
    • 分布式集群方案
      • 同时支持 redis & memcached:twitter 的方案 twemproxy,NetFlix 的方案 Dynomite
      • memcached 方案:facebook 的 mcrouter,已经过恐怖的线上流量验证,FB大部分数据直接扔里面
      • redis 方案:豌豆荚的Codis,国内用的人比较多。

使用应用服务器集群改善网站的并发处理能力

使用缓存后,数据访问压力得到了缓解,但是单一应用服务器能够处理的请求连接有限,在网站访问高峰期,应用服务器就成了整个网站的效率瓶颈。使用分布式集群是网站解决高并发、海量数据问题的常用手段。当一台服务器的处理能力和存储空间不足时,不要尝试去更换更强大的服务器,对大型网站而言,多么强大的服务器,都满足不了网站持续增长的业务需求。这种情况下,更恰当的做法是增加一台服务器分担原有服务器的访问及存储压力。 对网站架构而言,只要能通过增加一台服务器的方式改善负载压力,就可以以同样的方式持续增加服务器不断改善系统性能,从而实现系统的可伸缩性。通过负载均衡调度服务器,可以将来自用户浏览器的访问请求分发到应用服务器集群中的任何一台服务器上,如果有更多用户,就在集群中加入更多的应用服务器,使应用服务器的压力不再成为整个网站的瓶颈。应用服务器实现集群是网站可伸缩架构设计中较为简单成熟的一种,如下图所示:
web-server-4

  • 不同层面有不同做法
    • LVS在操作系统层,构建虚拟服务器集群
      • 负责TCP/IP层的转发
      • 实现负载均衡,与业务无关
    • nginx 在应用层面
      • nginx 支持反向代理,吞吐量极大,接近物理极限
      • 提供页面级缓存,通过修改正则式和if-modified-since等 header 可以精准的控制过期策略
      • 可以当文件服务器,通过修改X-Accel-Redirect等 header 可以完成身份认证
    • docker 容器、微内核层面
      • OpenStack 可以自建集群
      • Paas 平台可以直接用

数据库读写分离

网站在使用缓存后,使对大部分数据读操作访问都可以不通过数据库就能完成,但是仍有一部分读操作(缓存访问不命中、缓存过期)和全部的写操作都需要访问数据库,在网站的用户达到一定规模后,数据库因为负载压力过高而成为网站的瓶颈。 目前大部分的主流数据库都提供主从热备功能,通过配置两台数据库主从关系,可以将一台数据库服务器的数据更新同步到另一台服务器上。网站利用数据库的这一功能,实现数据库读写分离,从而改善数据库负载压力。如下图所示:应用服务器在写数据的时候,访问主数据库,主数据库通过主从复制机制将数据更新同步到从数据库,这样当应用服务器读数据的时候,就可以通过从数据库获得数据。为了便于应用程序访问读写分离后的数据库,通常在应用服务器端使用专门的数据访问模块,使数据库读写分离对应用透明。
web-server-5
HINT:

  • 最开始的场景是对单数据库的热备份,后来逐步发展成把读请求全部发到从库上
  • 不适合强一致性数据,数据从主库复制到从库需要时间。如果强行等待,反而降低了响应时间。
  • 主从只负责各自的读和写,一般场景是读多写少,通过多加读的从库增加读的性能。
  • 综上,作者的观点我不认同。主从架构更多是是一种用于数据容错的高可用性解决方案,而不是一种处理高并发压力的解决方案。解决并发只能多数据库分表分库或者newSQL(用paxos 或者两段式提交)。

使用反向代理和 CDN 加速网站响应

随着网站业务不断发展,用户规模越来越大,由于中国复杂的网络环境,不同地区的用户访问网站时,速度差别也极大。有研究表明,网站访问延迟和用户流失率正相关,网站访问越慢,用户越容易失去耐心而离开。为了提供更好的用户体验,留住用户,网站需要加速网站访问速度。主要手段有使用 CDN 和反向代理。如下图所示:
web-server-6
HINT:

  • 反向代理主要两个作用:安全+加速
    • 外部连接是接到反向代理服务器,这样对应用服务器更加安全。
    • 对于可以缓存的东西,反向代理层可以增加自动化页面级别缓存
  • CDN 内容分发网络
    • 根据网络流量和各节点的状况把用户请求导向离用户最近的服务节点上,让用户可就近取得所需内容
    • CDN 的分发能力很强很快,但是由于内容冗余和缓存,默认姿势是写入之后不可变,因此上 cdn 的东西一般要加一个摘要算法判断版本。

使用分布式文件系统和分布式数据库系统

任何强大的单一服务器都满足不了大型网站持续增长的业务需求。数据库经过读写分离后,从一台服务器拆分成两台服务器,但是随着网站业务的发展依然不能满足需求,这时需要使用分布式数据库。文件系统也一样,需要使用分布式文件系统。分布式数据库是网站数据库拆分的最后手段,只有在单表数据规模非常庞大的时候才使用。不到不得已时,网站更常用的数据库拆分手段是业务分库,将不同业务的数据部署在不同的物理服务器上。如下图所示:
web-server-7
HINT:

  • 分布式文件系统
  • 分布式数据库
    • 优先注意 CAP 原理,按照自己需要选择
    • 这篇文章里面有一张图,大致说明了各个数据库如何取舍 cap 分布式系统之CAP理论
    • Spanner,TiDB,PhxSQL,Oceanbase 这些 newSQL 在数据查询上和 MySQL 几乎没有区别,cap 上有一个比较不错的均衡点,它们基本上都可以解决95% 的 OLTP 场景和 70% 的 OLAP 场景。

使用 NoSQL 和搜索引擎

随着网站业务越来越复杂,对数据存储和检索的需求也越来越复杂,网站需要采用一些非关系数据库技术如 NoSQL 和非数据库查询技术如搜索引擎。
NoSQL 和搜索引擎都是源自互联网的技术手段,对可伸缩的分布式特性具有更好的支持。应用服务器则通过一个统一数据访问模块访问各种数据,减轻应用程序管理诸多数据源的麻烦。如下图所示:
web-server-8
HINT:

  • 绝大多数场景和情况,MySQL 加上 Memcache/Redis 已经够用,但是有一些场景用 noSQL 会有更好的效果。
  • 业内用得比较多的 noSQL 基本介绍
  • 我说说我用过的和仔细看过介绍的的几款
    • MongoDB。非常火的文档数据库,360、github、几乎所有的大前端团队都用 mongo 来存数据。本身结构无 schema,可以快速 restful,支持基本的索引。
    • Redis。数据结构数据库。独特的性质决定用法多样,从队列到计数器,到秒杀系统,各种地方都有它的身影。
    • memcached。经典缓存框架,成熟的大公司要么自己就用 memcache,要么实现了一套类似的 kv 内存存储数据库,吞吐量极高。
    • Neo4j。图数据库市场上的唯一选择。做复杂的 join 操作极快。本身提供的cypher就是一种图结构的抽象。赤兔的关系链用的这个数据库。
    • HDFS/HBase。适合存海量的文件/行数据。几乎是 Apache 生态的基石,Zoomkeeper,MapReduce,kafka 等很多框架存储数据都是用的 hbase。
  • 搜索和推荐
    • Elasticsearch应该是业内用得最多的搜索开源框架,基于Lucene工具包建全文索引,自带分布式、近实时索引、Schema-Free、RESTful api、面向文档设计等。
    • 开源推荐系统相对要多一些。我只用过Mahout 里的 itemBase 推荐算法,已经实现好并留下了接口,只需要去覆盖几个函数就能运行。
    • 机器学习框架,TensorFlow,Torchnet。这俩没玩过,之后有机会仔细研究一下。

业务拆分

大型网站为了应对日益复杂的业务场景,通过使用分而治之的手段将整个网站业务分成不同的产品线。如大型购物交易网站都会将首页、商铺、订单、买家、卖家等拆分成不同的产品线,分归不同的业务团队负责。具体到技术上,也会根据产品线划分,将一个网站拆分成许多不同的应用,每个应用独立部署。应用之间可以通过一个超链接建立关系(在首页上的导航链接每个都指向不同的应用地址),也可以通过消息队列进行数据分发,当然最多的还是通过访问同一个数据存储系统来构成一个关联的完整系统,如下图所示:
web-server-9
HINT:

  • 上面的架构图里出现的最重要的一个东西就是消息队列,之前数据状态变化必须写数据库,有了队列就可以有新的设计方法。八字真言:削峰解耦,提速广播。
    • 削峰。消息队列可以帮助抹平峰值,给架构带来巨大弹性
    • 解耦。复杂任务、耗时任务可以用更好的方式拆解,对应可以为架构带来非常多的可能性
    • 提速。同样配置服务器下,大部分消息队列的吞吐量都大于 MySQL。像 kafka 这种自带内存 buffer 的队列速度更快。
    • 广播。可以由队列在协议层完成广播,比自己轮转通知效率高很多。
  • 常见队列介绍常用消息队列介绍与对比
    • 我用过 RabbitMQ 和 kafka,两个都是可持久化队列。RabbitMQ是一个很重也很全的分布式队列,当年 OJ 用它来分发判题任务,跨语言实时队列用这个不错。kafka主要场景还是离在线混合任务,比如线上有了访问记录,可以用 kafka 来驱动一个计算函数更新某些数值。然后就是听说 nsq 不错,但是我没用过。
    • QPS 1W以下用什么队列都一个样,如果慢肯定是用搓了,高于这个了就得关注关注各家特点了。

分布式服务

随着业务拆分越来越小,存储系统越来越庞大,应用系统的整体复杂度呈指数级增加,部署维护越来越困难。由于所有应用要和所有数据库系统连接,在数万台服务器规模的网站中,这些连接的数目是服务器规模的平方,导致数据库连接资源不足,拒绝服务。既然每一个应用系统都需要执行许多相同的业务操作,比如用户管理、商品管理等,那么可以将这些共用的业务提取出来,独立部署。由这些可复用的业务连接数据库,提供共用业务服务,而应用系统只需要管理用户界面,通过分布式服务调用共用业务服务完成具体业务操作。大型网站的架构演化到这里,基本上大多数的技术问题都可以得以解决了。
如下图所示:
web-server-10
HINT:

  • 架构演化到这个地方,最重要的事情是 RPC 和服务治理
    • RPC生态一般要有序列化反序列化工具、数据交换协议、跨语言的自动生成工具等
    • 服务治理一般要有监控系统、自动化上线回滚系统、负载均衡、版本容错等等
  • 我只做过序列化,没有做过服务治理,大致看过一些资料,欢迎pr这几节。
    • gRPC: 用 google 的 protobuf 序列化,grpc服务治理,包括企鹅在内的很多公司在用protobuf,然后 rpc 框架用自研的。
    • dubbo:阿里和阿里浪在用的 rpc 框架,当你负责人去过北邮校赛讲座,当年没觉得这哥们多牛逼,现在觉得真特么碉堡了。
    • thrift:Facebook 在用的 rpc 框架,应该是目前多语言支持最好的框架。某头条也在用这个。
    • avro:Apache 系的东西,很多数据分析的人喜欢这个玩意,不明觉厉。

若干个人观点

  • 只有系统复杂度到了这后面两三步了才有必要开始用 docker 这些 LXC 工具,初期十来二十台服务器直接脚本化就可以解决大部分问题。
  • 如果基于云的 SaaS 做架构,很多事情会简单很多,甚至一步到位。国外三四十个人撑起十几亿美金估值靠的就是这种结构。运维的压力和系统的风险指数级减少。
  • 架构师不会失业,各个部件需要多少容量,这个事情接下来很多年还是需要有经验的工程师来做,每家公司也都会有需求。但是大部分 DBA 类的职位危险了。
  • 搞存储之类的事情,将来只有大厂和云服务商有需求。剩下的工程师会逐步转变成全栈工程师,以业务为基石来调用各种平台的 API,不需要专门一个后台/后端工程师来帮助产后端接口。
  • FB 鼓励刚毕业的人做”全栈”工程师,足够了解整个栈且技术素养发展到一定阶段再去选择深入的方向。
    • 同样的一个功能在不同技术栈上由同一个人完成,沟通的成本比七八个人同时工作要低
    • 设计师的工作范围变宽,各种设计工具React-SketchApp帮助生成对程序员更友好的设计稿
    • 运维架构后端工程师来管理诸如数据库 Schema、自动化流程、开发工具等基础性工具和规范。
    • 中间的部分归全栈工程师管:自己定数据库 schema,自己定表单验证方式,自己定前端交互数据流,自己定在哪个位置渲染,连接设计师的各种细节。
  • 安卓和 iOS 如果使用 react-native 或者 weex 可以进一步加速研发流程。
  • 最后来个鸡汤:如何成为一名优秀的全栈工程师?