Blogger Information
Blog 27
fans 15
comment 0
visits 23255
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
教你2种常用的电商高并发处理解决方案
CRMEB
Original
681 people have browsed it

网站架构师面临的最大挑战之一就是并发。自Web服务开始以来,并发水平一直在不断增长,一个主流网站同时服务十万甚至数百万用户,这并不罕见。

就目前应用广泛的电商系统来说,各种营销场景的增加,让电商系统高并发也成为一种必然。

为此,本文将给大家带来2种高并发解决方案,希望能为电商系统实现高并发提供一些灵感。

1、多级缓存

2、Nginx 限流

本文我们将以CRMEB商城为例,了解电商中常见的2种高并发方案

一、多级缓存

我们以CRMEB Pro版首页为例

 图片1.png

在上图中,我们可以看到商品的分类变化不大,我们可以把它存储在缓存中,这样会大大减轻数据库的压力,这种情况我们可以用redis进行缓存,但是有时候电商网站的并发数只靠redis,会使redis的压力太大。这时我们在这里引入一个概念:多级缓存。

1. 什么是多级缓存

为了解决上述问题,我们在redis的基础上增加了另一个nginx缓存。这时候用户访问我们的网站,会先访问nginx缓存。如果nginx缓存不存在,他们会再次访问redis缓存。如果redis也不存在,我们最终会访问MySQL来获取数据。这样一来redis的压力就大大降低了,然后nginx缓存和redis缓存就形成了多级缓存。

2、多级缓存怎么实现

了解了多级缓存的概念,我们该怎么去实现多级缓存呢?

我们可以使用OpenResty实现,这是一个基于Nginx和Lua的高性能Web平台,它将Nginx与大量复杂的Lua库、第三方模块和大多数依赖项集成在一起。Lua是一种轻量级紧凑脚本语言,用标准C语言编写,以源代码的形式开放。它被设计成嵌入在应用程序中,从而为应用程序提供灵活的扩展和定制功能。在实际操作中,我们通常使用Lua脚本来访问Nginx缓存、Redis缓存和MySQL。流程图如下:

图片2.png

图片来自网络,侵权联系删除

但是,在上述过程中,我们会发现一个问题。当Redis缓存已经存在时,数据库中的数据已经更改。此时所有用户都访问缓存的数据,那么如何解决这个问题呢?

3、Redis 缓存同步 MySql 数据

我们可以通过 canal 解决上面的问题。canal 是一个用来监控数据库数据的变化的工具,可以在Mysql 数据更新时获取其更新的数据。

解决方案:当Mysql数据发生变化时,我们可以通过canal微服务或者用RocketMQ或Kafka配置MQ模式来改变redis中的缓存数据,使Redis中的缓存数据与Mysql中的数据保持一致。

二、Nginx 限流

平常情况下,首页的并发量会比较大,即使使用多级缓存,当用户不断刷新页面时,或者大量恶意请求首页,也会对系统造成影响。这时候就需要采用限流方案了。

1、什么是限流

顾名思义,限流就是限制流量。比如你的手机流量包只有1 G的流量,用完了就没了。这时候我们就可以说我们的流量被限制了,这就叫限流。

常见的限流有漏桶算法

 图片3.png

图片来自网络,侵权联系删除

算法思路:

水(请求)从上方倒入水桶,从水桶下面流出(被处理);

来不及流出的水,储存在水桶(缓冲器)里,以固定的速率流出;

水桶满后水溢出(丢弃)。

这个算法的核心是:缓存请求,统一处理,直接丢弃冗余请求。

2、nginx 限流的方式

控制速度

我们先来说第一种,控制速度。Nginx 控制速率主要使用的是漏桶算法,这样能够强行保证请求的实时处理速度不会超过设置的阈值。

配置 OpenResty 中 Nginx 的配置文件,使在访问 ip/test1 地址时进行限流,该地址会访问一个 Lua 脚本

实例

user root root;

worker_processes 1;

events {

    worker_connections 1024;

}

http {

    include mime.types;

    default_type application/octet-stream;

 

    #限流设置

    limit_req_zone $binary_remote_addr zone=contentRateLimit:10m rate=10r/s;

 

    sendfile on;

    #tcp_nopush on;

    #keepalive_timeout 0;

    keepalive_timeout 65;

    #gzip on;

    server {

        listen 80;

        server_name localhost;

 

        location /test1{

            #使用限流配置

            limit_req zone=contentRateLimit;

            content_by_lua_file /root/lua/test1.lua;

        }

    }

}

运行实例 »

点击 "运行实例" 按钮查看在线实例

配置说明:

binary_remote_addr:是一种key,意思是基于 remote_addr(客户端IP) 来做限流,binary_ 的目的是压缩内存占用数。

zone:定义了一个共享内存区域来存储访问信息。 

contentRateLimit:10m 表示一个大小为10M,名字为 contentRateLimit 的内存区域。1M可以存储 16000 IP地址的访问信息,10M可以存储16W IP地址访问信息。

Rate:用于设置最大访问速率,rate=10r/s 表示每秒最多处理10个请求。

Nginx:实际上以毫秒为粒度来跟踪请求信息,因此 10r/s 实际上是限制每100毫秒处理一个请求。这意味着,自上一个请求处理完后,若后续100毫秒内又有请求到达,将拒绝处理该请求。

限流速度为每秒 10 次请求,如果有10次请求同时到达一个空闲的 nginx,他们都能得到执行吗?

事实上,漏桶漏出请求是匀速的。10r/s是怎样匀速的呢?每100ms漏出一个请求。在这个配置中,桶是空的,所有不能实时漏出的请求,会被拒绝掉。所以如果10次请求同时到达,那么只有一个请求能够得到执行,其它的,都会被拒绝。这不太友好,大部分业务场景下我们希望这10个请求都能得到执行。

处理突发流量

上述情况称为突发流量,上面的例子仅限于10r/s,如果正常流量突然增加,超出的请求会被拒绝,无法处理突发流量,可以结合 burst 参数使用来解决该问题。

修改上文中的 Nginx 配置文件

实例

server {

    listen 80;

    server_name localhost;

    location /test1 {

        limit_req zone=contentRateLimit burst=20 nodelay;

        content_by_lua_file /root/lua/test1.lua;

    }  

}

运行实例 »

点击 "运行实例" 按钮查看在线实例

burst 译为突发、爆发,表示在超过设定的处理速率后能额外处理的请求数,当 rate=10r/s 时,将1s拆成10份,即每100ms可处理1个请求。

此处,burst=12,若同时有12个请求到达,Nginx 会处理第一个请求,剩余11个请求将放入队列,然后每隔100ms从队列中获取一个请求进行处理。若请求数大于12,将拒绝处理多余的请求,直接返回 503。 

不过,单独使用 burst 参数并不实用。假设 burst=50 ,rate依然为10r/s,排队中的50个请求虽然每100ms会处理一个,但第50个请求却需要等待 50 * 100ms即 5s,这么长的处理时间自然难以接受。

因此,burst 往往结合 nodelay 一起使用。

继续修改上文中的 Nginx 配置文件

实例

server {

    listen 80;

    server_name localhost;

    location /test1 {

        limit_req zone=contentRateLimit burst=20 nodelay;

        content_by_lua_file /root/lua/test1.lua;

    }  

}

运行实例 »

点击 "运行实例" 按钮查看在线实例

平均每秒允许不超过10个请求,突发不超过20个请求,并且处理突发20个请求的时候,没有延迟,等到完成之后,按照正常的速率处理。

如上两种配置结合就达到了速率稳定,但突然流量也能正常处理的效果。

控制并发量(连接数)

nginx 还提供了利用连接数限制某一个用户的ip连接的数量来控制流量。一种是限制固定连接数,第二种是限制每个客户端IP与服务器的连接数,同时限制与服务器的连接总数。

注意:并非所有连接都被计算在内,只有当服务器正在处理请求并且已经读取了整个请求头时,才会计算有效连接。

配置限制固定连接数:

实例

http {

    include mime.types;

 

    default_type application/octet-stream;

 

    #限流设置

    limit_req_zone $binary_remote_addr zone=contentRateLimit:10m rate=10r/s;

 

    #根据IP地址来限制,存储内存大小10M

    limit_conn_zone $binary_remote_addr zone=addr:1m;

 

    sendfile on;

    #tcp_nopush on;

    #keepalive_timeout 0;

    keepalive_timeout 65;

 

    #gzip on;

 

    server {

        listen 80;

        server_name localhost;

           

        location /test2 {

            limit_conn addr 2;

            content_by_lua_file /root/lua/test2.lua;

        }                                                        

        location /test1 {

            limit_req zone=contentRateLimit burst=20 nodelay;

            content_by_lua_file /root/lua/test1.lua;

        }    

    }

}

运行实例 »

点击 "运行实例" 按钮查看在线实例

limit_conn_zone $binary_remote_addr zone=addr:10m; 表示限制根据用户的IP地址来显示,设置存储地址为的内存大小10M

limit_conn addr 2; 表示同一个地址只允许连接2次。

限制每个客户端IP与服务器的连接数,同时限制与服务器的连接总数:

实例

http {

    include mime.types;

 

    default_type application/octet-stream;

 

    #限流设置

    limit_req_zone $binary_remote_addr zone=contentRateLimit:10m rate=10r/s;

 

    #存储个人请求IP的限流配置

    limit_conn_zone $binary_remote_addr zone=perip:10m;

    #整个location对应的请求并发容量配置

    limit_conn_zone $server_name zone=perserver:100m;

 

    sendfile on;

    #tcp_nopush on;

    #keepalive_timeout 0;

    keepalive_timeout 65;

 

    #gzip on;

 

    server {

        listen 80;

        server_name localhost;

           

        location /test2 {

            limit_conn perip 10;#单个客户端ip与服务器的连接数"10"

            limit_conn perserver 100; #限制与服务器的总连接数"100"

            content_by_lua_file /root/lua/test2.lua;

        }                                                        

        location /test1 {

            limit_req zone=contentRateLimit burst=20 nodelay;

            content_by_lua_file /root/lua/test1.lua;

        }    

    }

}

运行实例 »

点击 "运行实例" 按钮查看在线实例

总结:我们在redis的基础上增加了另一个nginx缓存就能实现多级缓存,使用漏桶算法可以实现限流的效果,再结合 burst 参数使用来解决突发流量的处理。这几种方法相结合,就能实现电商系统高并发。如果还有不明白之处可以在下方留言,大家一起学习讨论。


Statement of this Website
The copyright of this blog article belongs to the blogger. Please specify the address when reprinting! If there is any infringement or violation of the law, please contact admin@php.cn Report processing!
All comments Speak rationally on civilized internet, please comply with News Comment Service Agreement
0 comments