十六、OpenResty 奇门术——使用OpenResty开发Web服务

十六、OpenResty 奇门术——使用OpenResty开发Web服务

本文将介绍如何利用 OpenResty 技术开发高性能的 Web 服务,特别是如何应对高并发访问量、处理复杂的实时数据需求。我们将讨论不同架构的优缺点,以及如何设计一个高效的系统。

1. 架构

本文将介绍的架构包括 OpenResty+JavaEE 技术架构,具体探讨其在应对高访问量和复杂逻辑时的设计和优化策略。

2. 单DB架构

在这里插入图片描述

在早期的单DB架构中,通常使用Nginx作为前端负载均衡,upstream请求转发到后端的Tomcat实例。随着访问量的增长,可以通过增加Tomcat实例来扩展处理能力。这种架构的优点在于简单易用且扩展灵活。

但是,随着请求量的增加,数据库成为瓶颈的风险也随之上升。通常,这种架构的演变过程如下:

  1. Nginx负载均衡与Tomcat扩展:最初的扩展通常是增加Tomcat实例,通过Nginx的负载均衡策略(如轮询、最少连接数等)来均衡请求的分发,进而提高服务的可用性和吞吐量。

  2. 数据库瓶颈问题:当Tomcat实例扩展到一定数量后,后端的单个数据库负载会开始增加,数据库的CPU、IO、内存等资源可能成为性能瓶颈,特别是在高并发场景下。

  3. 读写分离与缓存引入:为了缓解单DB的压力,通常会引入读写分离策略,将数据库的读操作转移到只读节点,从而减轻主库的压力。同时,增加缓存(如Redis或Memcached)来降低数据库的访问频率,提高响应速度。

  4. 进一步优化:在数据库成为瓶颈后,可以进一步考虑使用分库分表、中间件(如ShardingSphere)等技术手段来对数据库进行水平扩展。此外,结合消息队列、微服务架构等方式进行异步化和解耦,也可以提升系统的可扩展性和容错能力。

2.1 DB+Cache/数据库读写分离架构

在这里插入图片描述

在单DB架构下,当数据库压力增大时,单个数据库往往难以承载所有的读写请求。此时可以通过使用数据库读写分离或者增加 Redis 这种缓存机制来支持更大的访问量。

  • 读写分离:通过主数据库(Master)处理写操作,从数据库(Slave)处理读操作,将读写请求分散到不同的数据库实例上。这样可以减轻单个数据库的负担,并提高系统的并发能力和响应速度。主从数据库的同步通常采用半同步复制方式,但这也可能导致数据同步延迟等问题。

  • 使用缓存:缓存(如 Redis)可以用来存储频繁读取的数据,从而减少数据库的读取压力。一般来说,对于读取频率高、变化不频繁的数据,可以优先考虑缓存存储,读取操作直接从缓存中获取。使用缓存的好处是响应速度快、减少数据库负载。但在这种架构下,可能会面临缓存和数据库数据不一致的问题。例如,缓存的数据如果没有及时失效或者更新,将会导致读取到过期的数据。为了避免这种问题,通常会设置缓存的过期时间或者使用缓存淘汰策略。

使用缓存架构时需要注意以下几点:

  1. 数据不一致:缓存与数据库数据不同步会造成数据不一致问题。一般通过设置缓存的过期时间来控制缓存刷新,但有时也可能引发数据不一致情况。

  2. Redis 不可用:当 Redis 缓存服务不可用时,所有请求直接命中数据库,可能导致数据库压力骤增。因此,需要有 Redis 主从备份或者 Redis 集群来增强可用性。

  3. 数据一致性要求:使用缓存的架构通常要求应用对数据一致性的要求不是很高。比如,订单数据这类需要持久化的数据并不适合用 Redis 直接存储,但可以利用缓存来优化订单的读取性能。

  4. 缓存架构的选择:可以考虑 Redis 的主从架构,或者用一致性哈希算法做分片的 Redis 集群。如果对缓存依赖较大,最好使用 Redis 集群架构来避免单点故障,提高系统的可用性和扩展性。

综上,DB+Cache 架构是一种提高系统读写性能的有效方案,适合于读多写少的场景,但对数据一致性要求较高的业务场景则需要谨慎选择。

2.2 OpenResty+Local Redis+MySQL 集群架构

在这里插入图片描述

在这种架构中,OpenResty 作为前端 Web 服务器,通过 Lua 脚本来进行缓存管理。每台 OpenResty 服务器上都会部署一个本地 Redis 实例,用来存储热点数据。请求首先会在 OpenResty 上进行缓存命中检查,如果命中成功,则直接返回缓存数据。若缓存未命中,OpenResty 会将请求转发给后端的 Tomcat 集群,由 Tomcat 再从 MySQL 数据库中读取数据。

  • 本地 Redis 缓存:OpenResty 与 Redis 部署在同一台服务器上,这样设计的优点是减少网络延迟,使得缓存数据的获取速度更快。由于 Redis 只在本地读取,避免了网络 IO 的开销。同时,这种本地化设计可以更好地隔离 Redis 实例,防止缓存宕机对整个集群造成影响。

  • Redis 数据同步:Redis 实例通过主从复制来同步数据,一般采用树状架构。主节点负责写操作,而从节点负责读取操作,通过这种方式提升缓存的读取性能,并保证数据的高可用性。

Redis 主从复制

如上图所示,Redis 主从复制的叶子节点可以启用 AOF(Append-Only File)持久化策略,这样在主 Redis 节点不可用时,数据依然可以从从节点恢复。AOF 可以确保每条操作日志都被保存下来,能够更好地保障数据的持久性和一致性。

  • 多主 Redis 架构:如果业务对 Redis 的依赖性非常高,则可以考虑采用多主 Redis 架构(Multi-Master Redis),而不是单主架构,以防止单主 Redis 出现不可用情况导致的数据不一致以及对后端 Tomcat 集群的流量击穿。多主架构还可以进一步提高 Redis 的扩展能力和数据的读写性能。

  • 架构的缺点

    • Redis 实例数据量限制:由于 Redis 是一个内存数据库,所以每个 Redis 实例能存储的数据量相对较小。为了解决单机内存不足的问题,可以根据 Redis 键的尾号来分配存储位置。例如,尾号为 1 的键存储在 A 服务器,尾号为 2 的键存储在 B 服务器。虽然这种方式能有效分散负载,但会增加系统的复杂性。
    • 运维复杂,扩展性差:这种架构要求每台 OpenResty 服务器都安装 Redis,并且 Redis 实例之间需要有主从复制关系或树状架构。在扩展新服务器时,需要同步调整缓存架构和数据分布规则,增加了运维的复杂性。

总结来说,OpenResty + Local Redis + MySQL 集群架构适用于数据量适中、缓存命中率高的业务场景,可以显著降低数据库的读负载并加快数据响应速度。但是其复杂的运维和扩展成本,要求运维团队具备较高的技能水平和资源管理能力。

2.3 OpenResty+Redis 集群+MySQL 集群架构

OpenResty+Redis 集群+MySQL 集群架构

在这个架构中,使用了一致性哈希算法来实现 Redis 集群,而不是每台服务器部署本地 Redis。这种方式可以确保当其中一台 Redis 实例不可用时,仅有少量数据会丢失,从而防止大量请求直接打到数据库,减轻数据库的压力。该架构通过一致性哈希算法来进行 Redis 分片,使得 Redis 集群可以水平扩展,增加容量和处理能力。

  • Redis 集群分片:可以使用 Twemproxy 作为中间件来实现 Redis 的分片。Twemproxy 是一个轻量级的 Redis 代理,它负责客户端和 Redis 服务器之间的请求转发。通过 Twemproxy,我们可以实现对 Redis 集群的分片管理,保证数据均匀分布在多个 Redis 实例上,同时避免单点故障。

Twemproxy与Redis交互

如上图所示,Twemproxy 与 Redis 之间通过单链接交互,Twemproxy 负责对 Redis 集群的请求进行分片管理。通过这种方式,我们可以水平扩展更多的 Twemproxy 实例,以增加 Redis 的连接数和吞吐量。

  • 链接数瓶颈:随着 Tomcat 实例数量的增加,Redis 和 MySQL 的连接数可能会成为瓶颈,因为大多数 Redis 和 MySQL 客户端都是通过连接池来实现的。为了应对这种情况,可以使用 Twemproxy 这样的中间件来减少每个应用程序实例的连接数,从而优化性能。

  • 负载均衡与高可用:当 Twemproxy 实例众多时,应用的维护和配置会变得复杂,需要在其之上做负载均衡。例如,可以使用 LVS(Linux Virtual Server)或 HaProxy 实现 VIP(虚拟 IP),从而对应用程序做到透明切换和故障自动转移。负载均衡还可以通过实现内网 DNS 来完成。

LVS/HaProxy 负载均衡

如上图所示,通过使用 LVSHaProxy 进行负载均衡,可以提高整个架构的容错能力和稳定性,避免单点故障带来的影响。对于这些中间件的具体配置和负载均衡策略,可以参考相关的深入资料。

  • 架构优缺点
    • 优点
      • Redis 和 MySQL 集群架构大大增强了系统的高可用性和扩展性。
      • 一致性哈希算法确保了数据的分布均匀,即使部分节点宕机也只会影响少量数据。
      • Twemproxy 等中间件有效减少了 Redis 和 MySQL 的连接数问题,降低了客户端的复杂性。
    • 缺点
      • 维护和配置 Twemproxy 实例数量多时较为困难,系统扩展性虽好但也增加了运维成本。
      • 需要额外的负载均衡机制,如 LVSHaProxy 等,增加了架构的复杂度。

总结来说,这种架构适用于业务访问量大、数据一致性要求较高的场景,通过 Redis 集群、MySQL 集群和高效的负载均衡机制,实现了高并发和高可用的系统架构设计。

3. 实现

在这一部分,我们将搭建一种架构来处理广告词缓存和展示。

在这里插入图片描述

该架构的主要流程如下:

  1. Nginx:处理客户端请求,首先从缓存读取数据,如果缓存中没有,则负载到后端 Tomcat 处理,同时更新缓存。
  2. Twemproxy:作为 Redis 的代理,提高 Redis 集群的性能和可用性。
  3. Redis:作为缓存层,存储热点数据以提高读取效率。
  4. 后端 Tomcat:处理业务逻辑,如果 Redis 中没有数据,则从数据库中读取数据,并更新 Redis 缓存。
  5. Atlas :是一个类似于 Twemproxy 的 MySQL 中间件,由 Qihoo 360 开发。它支持分库、分表、读写分离等功能,但不支持跨库分表功能。可以选择在客户端实现分库逻辑。
  6. MySQL:作为数据库存储持久数据。

以下是步骤和详细配置说明。

3.1 后台逻辑
  1. 商家登录后台
    商家通过后台系统管理广告词。后台系统需要支持商家登录并进行数据管理。

  2. 分页查询商家数据
    根据商家的需求,后台系统需支持分页查询,可能需要整合商品系统的搜索功能,例如通过Solr或ElasticSearch实现复杂的查询需求。

  3. 广告词的增删改查

    • :将新广告词加入数据库并更新Redis缓存。
    • :从数据库中删除广告词并更新Redis缓存。
    • :更新数据库中的广告词,并同步更新Redis缓存。
    • 对于增删改操作,建议直接更新Redis缓存,或者仅删除缓存,下一次查询时再更新缓存。
3.2 前台逻辑
  1. Nginx与Redis缓存
    Nginx通过Lua脚本查询Redis缓存,确保广告词的快速获取。

  2. 回源到Tomcat
    如果Redis缓存中没有数据,则回源到Tomcat。Tomcat从数据库中读取数据,并异步更新Redis缓存。为防止Tomcat压力过大,设置适当的降级开关,例如当回源请求达到一定阈值时,直接返回空广告词以防止Tomcat雪崩。

3.3 项目搭建

项目部署目录结构

/usr/chapter6
    ├── redis_6660.conf
    ├── redis_6661.conf
    ├── nginx_chapter6.conf
    ├── nutcracker.yml
    ├── nutcracker.init
    └── webapp
        ├── WEB-INF
        ├── lib
        ├── classes
        └── web.xml
3.4 Redis+Twemproxy 配置

Redis 配置

  1. 安装 Redis

  2. Redis 配置文件 (redis_6660.confredis_6661.conf):

    # Redis实例6660配置
    port 6660  # Redis 监听的端口号
    pidfile "/var/run/redis_6660.pid"  # PID 文件路径
    maxmemory 20mb  # 最大内存限制
    maxmemory-policy volatile-lru  # 内存淘汰策略:LRU(最近最少使用)策略
    maxmemory-samples 10  # LRU 算法的采样数
    save ""  # 关闭 RDB 持久化
    appendonly no  # 关闭 AOF 持久化
    
    # Redis实例6661配置
    port 6661
    pidfile "/var/run/redis_6661.pid"
    maxmemory 20mb
    maxmemory-policy volatile-lru
    maxmemory-samples 10
    save ""
    appendonly no
    
  3. Twemproxy 配置 (nutcracker.yml):

    server1:
      listen: 127.0.0.1:1111  # 代理监听地址和端口
      hash: fnv1a_64  # 哈希算法
      distribution: ketama  # ketama 一致性哈希分布
      redis: true  # 启用 Redis 模式
      timeout: 1000  # 超时时间(毫秒)
      servers:
        - 127.0.0.1:6660:1  # Redis 实例 6660
        - 127.0.0.1:6661:1  # Redis 实例 6661
    

    nutcracker.yml 配置文件复制到 /usr/chapter6 目录,并修改配置文件路径为 /usr/chapter6/nutcracker.yml

  4. 启动 Redis 和 Twemproxy

    nohup /usr/servers/redis-2.8.19/redis-server /usr/chapter6/redis_6660.conf &
    nohup /usr/servers/redis-2.8.19/redis-server /usr/chapter6/redis_6661.conf &
    /usr/chapter6/nutcracker.init start
    

    使用 ps -aux | grep -e redis -e nutcracker 命令检查 Redis 和 Twemproxy 的进程是否成功启动。

3.5 MySQL+Atlas 配置

Atlas 是一个类似于 Twemproxy 的 MySQL 中间件,由 Qihoo 360 开发。它支持分库、分表、读写分离等功能,但不支持跨库分表功能。可以选择在客户端实现分库逻辑。以下是 Atlas 的配置和使用步骤:

1. MySQL 初始化

为测试创建一个数据库和表:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我心向阳iu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值