专栏首页程序员的碎碎念简明入门讲义——如何实现可扩展的 Web 服务

简明入门讲义——如何实现可扩展的 Web 服务

一. 服务器

可扩展的应用服务器(Application Server)集群藏身于负载均衡器(Load balance,LB)背后,LB 将负载(即用户请求)平均地分配到各个组或集群的应用服务器上,此时负载均衡器可能运行在 TCP 层(Layer 4),分配请求的方式默认是简单的轮询(Round-Robin),即假设有服务器 A-D,请求依次从 A 分配到 D,列表循环。

现在,小明向你的 Web 服务发起请求,第一个请求可能被分配到服务器 A,第二个请求可能被分配到服务器 C,要求小明每次请求总能获得相同的返回结果,无论请求最终落到哪个服务器上

不改变设计,可能出现下面的情况,小明发起第一次请求,填完密码登录,提示 3 天内直接进入无需填写密码。结果再次登录时,路由到另一个服务器,又一次提示小明登录!原因很简单,这台服务器上面没有小明的 Session。

这个示例引出了扩展性的第一个黄金法则:每个服务器都包含完全相同的代码库,不在本地磁盘或内存上存储任何与用户相关的数据,例如会话(Session)或个人资料。

怎么实现会话保持(Sticky Session) 是水平扩展服务器中的常见问题。

假设请求随机到任一服务器,则必须有一个中心化的存储服务用来保存 Session,并且所有应用服务器都可以访问。这项存储服务独立于应用服务器之外,可以是持久化的数据库或者缓存

如果没有额外的存储服务怎么办,假设现在只有负载均衡器和应用服务器?

我们可以让负载均衡器监听 HTTP 层(Layer 7,应用层) 的请求,当小明第一次请求分配到服务器 A 时,产生一个随机数 r,将它写到 Cookie 中随请求返回。当小明再次请求时,负载均衡器层通过一个哈希函数,计算 Cookie 中的随机数 r,请求即可再次路由到服务器 A。

这个方案节省了存储空间,但引入一个问题,服务器 A 挂掉之后,小明还需重新登录,所幸这不是非常关键的数据,还可以接受。但独立存储也存在自己的问题,最明显的,怎么解决单点问题(Single Point Of Failure)?这个后文再谈。

现在你的关键问题是,如何使多个应用服务器发布时都存有同一份代码?可以借助 capistrano 这个开源项目。

将用户数据移出应用服务器,并解决完全相同代码库问题后,就可以打包为服务器镜像进行统一部署了。

二. 数据库

完成第一步用户数据中心化隔离、代码库同步后,不费多大力气就可以添加多个应用服务器,使你的 Web 服务处理大量并发请求。但 Web 服务还是会变慢甚至挂掉,原因就在中心化的数据库上!

最好从一开始就走反范式的数据设计方式,数据库只做简单的写入和查询操作,其他复杂的操作、约束都通过代码解决。这样你的数据库会更容易进行水平扩展,更方便做迁移,单个数据库实例也不需要很大。

否则,数据量一大,迁移、修改等操作,还是会由于数据库外键约束,导致长时间锁表等问题,或者比反范式设计的数据库消耗更多的内存和 CPU,成本与日俱增。

在进行数据库复制(Replication)时,一般使用主从模式(Master-Slave)。这里的主从采用读写分离,主库负责写,从库定时同步主库的数据,接收读请求。当主库挂掉的时候,从库也不能提升为主库。

为了解决这个问题,在主库上引入双主(Master-Master)或者待命(Standby)模式双主即两个主库(或者两个集群)都可以接收写请求,无论哪一方收到写请求,另一方会立刻同步。待命模式则同一时刻只有一个接收写请求,另一方只做同步。当主库宕机,待命方将自己升为主库,继续提供服务

当你引入了多个数据库(集群)时,最好不要通过硬编码(Hard-code)来解决故障重连问题,开发同学没必要了解你的架构拓扑,而且在你扩展或者收缩集群的时候,开发同学可不想跟着你加班发布。

这时同样可以引入负载均衡器来解决扩展问题。如果你还需要根据用户名分区操作,比如小明分到了新手区 Z,小红分配到新手区 X,那么负载均衡器可能解决不了,因为 MySQL 请求内容是二进制的,对 LB 是透明的

你可以引入分库分表的中间件,在代码层面解决。对于业务开发团队而言,这个中间件的处理过程同样是透明的。

但这个时候请求也只是“可用”,还不够快,是时候考虑引入缓存了。

三. 缓存

相同配置下,以 Redis 为例,缓存在读取和写入上要远胜于 MySQL 这样的关系型数据库。建议只使用 Redis 或者 Memcached 这类基于内存的缓存服务,不要使用基于文件的缓存,这会使数据迁移和复制(水平扩展)变得复杂。

保存缓存数据一般有两种方式

请注意,这里不是在讨论缓存更新的模式,如果感兴趣,可以阅读 缓存更新的套路。

其一是基于数据库查询(SQL-Based)来缓存,不难理解,就是把数据库的查询结果保存到缓存中,键名(Key)可以是查询的 SQL 语句哈希,简单粗暴。但这会存在问题,例如前面我们已经用了反范式的设计,尽量避免使用 JOIN 查询,一个语句有时候解决不了查询,怎么办?

这就有第二种方式,直接缓存对象(Object-Based)。一个请求(多次)查询后的数据在代码中“组装”(Assemble)完毕后。例如一个嵌套的数据结构,查询一个小明的个人信息和他的订单,其中订单数组中是一个个独立的订单对象。可以在代码中将数据组装完毕后,直接缓存整个对象。

想想看如果是第一种,你还需要分开缓存多个查询,下次读缓存还要读两次,再组装数据返回给用户,太麻烦了,用户可等不及!

四. 异步

做完了上面的三个步骤,用户可能还在抱怨我不想等!Web 服务的设计可不能像排队买所谓的网红奶茶一样,让一排用户在原地死等。

想象一下你到一个面包店买蛋糕,有这样的情况:

1.你要的蛋糕已经提前做好了,店员直接给你,交易完成2.你要的蛋糕卖完了,新一批晚上才出炉3.你要的蛋糕有,但你是给小明祝寿的,上面要有小明寿比南山的字。

情形一对应 Web 服务中的第一种异步模式,提前把内容生产好,等用户消费。典型的场景是个人博客、新闻网站等,提前将 HTML 渲染完毕,通过自动的定时任务或者手动执行脚本将内容上传到服务器,必要的时候配合 CDN(Content Delivery Network)进行加速,用户就可以快速的访问你的网页内容了。

情形二、三你肯定不想在蛋糕店干等,而是希望制作完成的时候,蛋糕店通知你来取就可以了。面包店有自己的订单队列,有些订单可能是加急(加钱)订单,面包店还需要优先处理。这对应 Web 服务中第二种异步方式,将用户的请求转化为异步任务在后台排队处理,用户注册一个监听器,处理完成后就会收到通知了。

相关的服务例如 RabbitMQ、Celery 等等。一旦你发现 Web 服务中有需要等的动作,务必将它异步处理。

参考文献

1.Lecture 9 Scalability Harvard Web Development David Malan(视频) (https://www.youtube.com/watch?v=-W9F__D3oY4)2.Scalability for Dummies(https://www.lecloud.net/tagged/scalability/chrono)

本文分享自微信公众号 - 程序员的碎碎念(gh_53e607dd4782),作者:FesonX

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2021-05-05

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 简明入门讲义——如何实现可扩展的 Web 服务

    可扩展的应用服务器(Application Server)集群藏身于负载均衡器(Load balance,LB)背后,LB 将负载(即用户请求)平均地分配到各个...

    FesonX
  • 理论联系实际:从零理解WebSocket的通信原理、协议格式、安全性

    WebSocket的出现,使得浏览器具备了实时双向通信的能力。本文由浅入深,介绍了WebSocket如何建立连接、交换数据的细节,以及数据帧的格式。此外,还简要...

    JackJiang
  • WebSocket从入门到精通,半小时就够!

    本文原题“WebSocket:5分钟从入门到精通”,作者“程序猿小卡_casper”,原文链接见文末参考资料部分。本次收录时有改动。

    JackJiang
  • 进阶Java架构师必看的15本书

    1、大型网站技术架构:核心原理与案例分析 本书通过梳理大型网站技术发展历程,剖析大型网站技术架构模式,深入讲述大型互联网架构设计的核心原理,并通过一组典型网站技...

    Java技术栈
  • 渗透测试入门指南与路线规划(全文版)

    我本非安全大牛,水平有限,所以自然亲民,和许多渗透测试的初学者打得火热。这其中大部分是大学生,还有工作多年,但一直对网络安全热情不减的热血之人。许多同学一直在努...

    用户1631416
  • SOA概览3-SOA的基本特征

    SOA的实施具有几个鲜明的基本特征。实施SOA的关键目标是实现企业IT资产的最大化重用。 1.可从企业外部访问

    py3study
  • 第 431 期 Python 周刊

    本文讨论一个很酷的项目 - 在 Linux 内核中运行的完整 Python 解释器。

    爱写bug
  • JavaWeb类

    本书全面介绍了Tomcat的架构、各组件的实现方案以及使用方式。包括Tomcat的基础组件架构以及工作原理,Tomcat各组件的实现方案、使用方式以及详细配置说...

    用户5224393
  • 取舍于得失之间:权衡Java EE 5.0 & Seam & Spring & Yourself

    Java EE 5.0明显比它之前的版本都更容易使用,也更加强大。与Web应用程序开发人员最相关的Java EE 5.0平台的两种规范是JSF和EJB...

    阿敏总司令

扫码关注云+社区

领取腾讯云代金券