2018年1月

负载均衡,英文名称为 LoadBalance,其意思就是将负载(工作任务)进行平衡,分摊到多个操作单元上进行执行(例如Web服务器、FTP服务器等),实现多个服务器共同完成工作任务的目标。负载均衡建立在现有网络结构之上,它提升了服务器的性能、提高了带宽利用率,增强了网络的灵活性和可靠性。

七层负载均衡的实现

基于URL等应用层信息的负载均衡,Nginx 的 proxy 是它一个很强大的功能,实现了7层负载均衡。

  • 功能强大,性能卓越,运行稳定
  • 配置简单灵活
  • 能够自动剔除工作不正常的后端服务器
  • 上传文件可以使用异步模式
  • 支持多种分配策略,可以分配权重,分配方式灵活

NGINX 拥有两种策略:内置策略扩展策略

内置策略是NGINX安装后内置,开箱即用的均衡策略,而扩展策略反之,需要我们安装特定的模块才能进行使用。

内置策略: IP Hash、加权轮询

扩展策略:fair策略、通用hash、一致性hash。

加权轮询:首先将请求都发给高权的机器,直到该机器的权值降到了比其他机器底,才开始将请求分给下一个高权重的机器。当所有后端机器都 down 掉时,NGINX 会立即将所有机器的标志位清成初始状态,以避免所有的机器都处于 timeout 的状态。

IP Hash: 流程和轮询很类似,只是其中的算法和具体的策略有些变化,IP HASH算法算是一种变相的轮询算法。

fair 策略:根据后端服务器的响应时间判断负载情况,从中选出负载最轻的机器进行分流。

通用hash、一致性hash:比较简单,可以以 NGINX 内置的变量为key进行hash,一致性hash采用了NGINX内置的一致性Hash环,支持 memcache。

http {
    upstream cluster {
        ip hash;
        server serv1;
        server serv2;
        server serv3;
    }
    server {
        listen 80;
        location/ {
            proxy_pass http://cluster;
        }
    }
}

四层负载均衡的实现

通过报文中的目标地址和端口,再加上负载均衡设备设置的服务器选择方式,决定最终选择的内部服务器。

在四层负载均衡上,我们可以使用LVS (Linux Virtual Server),意即Linux 虚拟服务器,是一个虚拟服务器的集群系统。该软件是在1998年5月由章文嵩山博士创建,是国内最早出现的自由软件项目之一,它具有良好的可靠性、可扩展性和可操作性。

实现原理

LVS 是基于 IP 地址的调度方法实现的,是最高效的实现方法,IP 负载均衡是通过 IPVS 内核模块实现的,IPVS 是 LVS 集群系统的核心软件。

也就是说在我们使用 LVS 之前,我们必须要先安装 ipvs 软件。

安装在 Director Server 上,同时在 Director Server 上虚拟出一个 IP(VIP-Virtual IP)地址。在进行域名解析的时候,我们需要将域名解析到 VIP 上。然后根据 VIP 找到 Director Server分发到真实的服务器上。

访问的请求首选in 经过 VIP 到负载调度器,由负载调度器从 Real Server 列表中选取一个节点响应用户的请求。

Real Server 节点返回给用户数据使用过 ipvs 实现的,ipvs 实现负载均衡的机制有三种:NAT(地址转发)DR (直接路由)TUN(隧道模式)

LVS 安装

上文提到过,安装 LVS 需要安装 ipvs,我们需要到其官网进行下载:

http://www.linuxvirtualserver.org/software/index.html

在下载软件时我们需要注意,要下载对应的内核版本号,使用 uname -r 查看当前 Linux 的内核版本。

同时,我们也可以使用 Linux 自带的包管理器进行安装

yum -y install ipsadm

NAT 模式

地址转换技术 DR 只需要将 VIP 配置到 DR 上,将受到的集群服务请求报文目标地址转换成根据算法计算得出的后端主机 IP 地址。

然后后端主主机将相应报文发送至 DR,再由 DR 将原地址转换成 VIP 地址。下面是他的网络拓扑图:

在 LVS(Director)上面需要两双网卡:DIP(内网)和 VIP(外网)

内网的 Real Server 主机的 IP 必须和 DIP 在同一个网络中,并且要求其网关都需要指向 DIP 的地址。

RIP 都是私有 IP地址,仅用于各个节点之间的通信,Director 位于 client 和 Real Server 之间,负责处理所有的进站、出站的通信,支持端口映射。

应用在较大规模的应用场景中,但 Director 容易成为整个架构的瓶颈。

什么是数据库缓存

MySQL 等一些常见的关系型数据库的数据都存储在硬盘当中,在高并发场景下,业务应用对 MySQL 产生的增、删、改、查的操作造成巨大的I/O开销和查询压力,这无疑对数据库和服务器都是一种巨大的压力,为了解决此类问题,缓存数据的概念应运而生。

使用数据库缓存可以极大的解决我们数据的压力,提高应用数据的响应速度,因为不用再动态查询了,直接将静态数据返回,无论是速度还是效率都要更快一些,节省了,很多查询和计算的时间。

常见的缓存形式:内存缓存,文件缓存

为了避免I/O开销,应该尽量使用内存缓存。

为什么要使用缓存

缓存诗句是为了让客户端很少,甚至是不访问数据库服务器进行数据的查询,高并发下,能最大程度地降低对数据库服务器的访问压力。

当我们默认情况下不使用缓存的情况下,我们的执行顺序如下:

  1. 用户请求
  2. 数据查询
  3. 链接数据库服务器并查询数据
  4. 将数据缓存起来(HTML、内存、JSON、序列化数据)
  5. 显示给客户端

当我们第二次请求或者是新用户访问时候,执行顺序如下:

  1. 用户再次请求或者新用户访问
  2. 数据查询
  3. 直接从缓存中获取数据
  4. 显示给客户端

缓存需要考虑的内容

  • 缓存方式的选择
  • 缓存场景的选择
  • 缓存数据的实时性
  • 缓存数据的稳定性

使用 MySQL查询缓存

启用MySQL查询缓存

query_cache_type

查询缓存类型,有0、1、2三个取值。

  • 0 不是用查询缓存
  • 1 始终使用查询缓存
  • 2 按需查询缓存

query_cache_type为1时,也可以关闭查询缓存

SELECT SQL_NO_CACHE * FROM my_table WHERE condition;

query_cache_type为2时,可以按需使用查询缓存

SELECT SQL_CACHE * FROM my_table WHERE condition;
query_cache_size

默认情况下query_cache_size为0,表示查询缓存预留的内存为0,则无法使用查询缓存。

SET GLOBAL query_cache_size = 134217728;

查询缓存可以看成是 SQL 文本和查询结果的映射。

第二查询的SQL和第二次查询的SQL要完全相同,才会使用缓存。

我们可以通过下面的语句查看缓存的命中次数。

SHOW STATUS LIKE 'Qcache_hits'; 

表的结构发生改变时候,查询缓存中的数据不再有效。

清理缓存如下:

FLUSH QUERY CACHE;  //清理查询缓存内部碎片
RESET QUERY CACHE;  //从查询花村中移出所有查询
FLUSH TABLES;        //关闭所有打开的表,同时该操作将会清空查询缓存中的内容。

使用 Memcache

对于大型的站点,如果没有中间缓存层,当流量打入数据库层的时候,即便有之前的几层为我们挡住了大量流量,但是在大并发的请胯下,还是会有大量请求涌入数据库,这样对于数据库服务器的压力冲击很大,响应速度也会下降,因此添加中间缓存层很有必要。

Memcache 是一套分布式的高速缓存系统, 由 LiveJournal 的Brad Fitzpatrick 开发,但目前被许多网站使用以提升网站的访问速度,尤其对于一些大型的、需要频繁访问数据库的网站访问速度提升效果十分显著。

工作原理

Memcache 是一个高性能的分布式的内存对象缓存系统,通过在内存里维护一个统一的巨大的Hash表,它能够用来存储各种格式的数据,包括图像、视频、文件以及数据库检索出来的结果等。简单的说就是讲数据调用到内存,然后从内存中读取,从而大大提高读取速度。

工作流程

先检查服务端的请求数据是否在 memcached中,如果存在直接把请求数据返回,不再对数据库进行任何操作。如果请求的数据不再memcached中,就去查询数据库, 把从数据库中获取的诗句返回给客户端,同时把数据缓存一份到memcached中。

方法

获取:get(key)

设置:set(key, val, expire)

删除 : delete(key)

通用缓存机制

用查询的方法名 + 参数作为查询时的key value对应中的key值。

使用 Redis

与Memcache的区别

  • 性能相差不大
  • Redis 在2.0版本后增加了自己的 VM 特性,突破了物理内存的限制
  • Memcache 可以修改最大可用内存,采用 LRU(缓存淘汰) 算法
  • Redis 依赖客户端来实现分布式读写
  • Memcache 本身没有数据冗余机制
  • Redis 支持(快照、AOF),依赖快照进行持久化,AOF 增强了可靠性的同时,对性能也有所影响
  • Memcache 不支持持久化,通常做缓存,提升性能
  • Memcache 在并发场景下,用 cas 保证一致性,Redis 事务支持比较弱,只能保证事务中的每个操作的连续执行
  • Redis 支持多种数据类型
  • Redis 用于数据量较小的高性能操作和运算上
  • Memcache 用于动态系统中减少数据库负载,提升性能;适合做缓存,提高性能

独立图片服务器的必要性

我们知道,无论对于Apache还是Nginx,图片始终是最消耗系统资源的,如果将图片服务和应用服务放在同一个服务器的话,应用服务器很容易会因为图片的 高I/O负载而崩溃,因此对于有些大型网站项目,我们有必要将图片服务器和应用服务器分离。

部署独立的图片服务器(甚至是服务器集群)是大型网站图片存储解决方案中最基础的,因为有了独立的图片服务器后,我们才能对图片服务器做更有针对性的性能优化,为图片服务器设置针对性的缓存方案,减少带宽成本,提高访问速度。

从硬件角度说,图片服务器可以配置高端的硬盘,7200转的换成15000转的,而CPU只需要使用一般的CPU就可以了。

从软件角度说,可以为图片服务器配置特殊的文件系统来满足对图片的I/O请求,如淘宝 的TFS,就很好地解决了大规模小图片文件带来的I/O噩梦,同时,我们也可以采用Nginx、squid来代理图片请求,通过增加图片服务器,提高图片吞吐能力。

分担 Web 服务器的 I/O 负载-将耗资源的图片服务分离出来,提高服务器的性能、稳定性和扩展性。

# 采用独立域名

注意,这里是指独立域名,不是子域哦,比如yahoo.com图片服务器用了yimg.com的域名,而不是用二级域名img.yahoo.com。

同一域名下浏览器的并发链接数有限制,突破浏览器链接数的限制,通常情况下浏览器的并发连接数是2到6个。

这样,我们如果给图片服务器配置独立的域名,那么在一个页面中加载图片时,就可以突破浏览器连接数的限制,理论上,增加一个独立域名,并发连接数加倍。

另外还有由于 Cookie 的原因,大部分 Web Cache都只缓存不带 Cookie 的请求,导致每次的图片请求都不能命中 Cache。而仍旧要去原始服务器获取图片,导致图片缓存意义不大。所以,还是给单独搞一个图片独立域名吧,当然,不只是图片,CSS和JavaScript文件也可以参照这个思路来搞。

# 如何进行图片上传和图片同步?

## NFS 共享方式

NFS是Network  File System(网络文件系统)。主要功能是通过网络让不同的服务器之间可以共享文件或者目录。

NFS客户端一般是应用服务器(比如web,负载均衡等),可以通过挂载的方式将NFS服务器端共享的目录挂载到NFS客户端本地的目录下。    

NFS在文件传送过程中依赖与RPC(远程过程调用)协议。NFS本身是没有提供信息传送的协议和功能的,但是能够用过网络进行图片,视频,附件等分享功能。只要用到NFS的地方都需要启动RPC服务,不论是NFS的服务端还是客户端。    

NFS和RPC的关系:可以理解为NFS是一个网络文件系统(比喻为租房的房主),而RPC是负责信息的传输(中介),客户端(相当于租房的租客)

了解 NFS 可以看我的另外一篇博文《LInux 典型应用:》

## 利用 FTP 同步

用户上传完图片后是利用 FTP 同步到各个图片服务器的,PHP、Java、Asp.net基本上都能操作 FTP。这样的话 每个图片服务器就都保存一份图片的副本,也起到了备份的作用。但是缺点是将图片ftp到服务器比较耗时,如果异步去同步的话又会有延时,不过一般的小图片 文件也还好了。

什么是动态语言静态化

将现有 PHP 等动态语言的逻辑代码生成为静态HTML 文件,用户访问动态脚本重定向到静态 HTML 文件的过程。

如果页面中的的数据一直都在变化,那么不建议使用静态化。

为什么要静态化

动态脚本通常会做逻辑运算和数据查询,访问量越大,服务器压力越大。

访问量大的时候可能造成 CPU 负载过高,数据服务器压力过大,静态化可以减轻逻辑处理能力,降低数据库服务器的查询压力。

静态化的实现方式

使用模板引擎

可以使用 Smarty 的缓存机制生成静态 HTML 缓存文件。

$smarty->cache_dir = $ROOT.'/cache'; //缓存目录
$smarty->caching = true;                //是否开启缓存
$smarty->cache_lifetime = '3600';    //缓存时间 
$smarty->display(strign template [,string cache_id[, strign compile_id]]);

如果开启了缓存,Smarty 会自动的生成 HTML 缓存文件。

$smarty->clear_all_cache();            //清除所有缓存
$smarty->clear_cache('file.html');    //清除指定缓存
$smarty->clear_cache('article,html', $cache_id) //清除一同模板下的指定缓存号的缓存。

利用 OB 系列函数

  • ob_start():打开输出控制缓冲
  • ob_get_contents():返回输出缓冲区的内容
  • ob_clean():清空输出缓冲区
  • ob_end_flush():冲刷出(送出)输出缓冲区内容并且关闭缓冲
ob_start();
输出到页面的 HTML 代码
ob_get_contents();
ob_end_flush();
fopen()写入

可以使用 filectime 函数 判断文件的 inode 修改时间,判断是否过期。

<?php
$cache_name = md5(__FILE__).'html';
$cache_lifetime = 3600;

if ( filectime(__FILE__) <= filectime($cache_name) file_exists($cache_name) && filectime($cache_name) + $cache_lifetime > time()) {
    //判断 PHP 文件修改时间
    //判断是否存在缓存
    //判断是否过期
    include $cache_name;
    exit;
}
ob_start();
?>
<b> This is my Script</b>
<?php
$content = ob_get_contents();    
$ob_end_flush();
$handle = fopen($cache_name, 'w');
fwrite($handle, $content);
fclose($handle);
?>

什么是进程、线程、协程

进程

进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。

进程的三态模型

进程的三态模型:多道程序系统中,进程在处理器上交替运行,状态不断地发生变化

运行:当一个进程在处理机上运行时,则称该进程处于运行状态。处于此状态的进程的数目小于等于处理器的数目,对于单机处理系统,处于运行状态的进程只有一个。没有其他进程可以执行时(如所有进程都在阻塞状态),通常会自动执行系统的空闲进程。

就绪:当一个程序获得了除处理机意外的一切所需资源,一旦得到处理机即可运行,则称此进程出于就绪状态。就绪进程可以按多个优先级来划分队列。例如,当一个进程处于时间片用完而进入就绪状态时,排入低优先级队列;当前进程由 I/O 操作完成而进入就绪状态时,排入高优先队列。

阻塞:也称之为等待或者睡眠状态,一个进程正在等待某一事件发生(例如请求 I/O而等待 I/O 完成等)而暂时停止运行,这时即使把处理机分配给进程也无法运行,故称该进程出于阻塞状态。

进程的五态模型

进程的五态模型:对于一个实际的系统,进程的状态及其转换更为复杂分为新建态、活跃就绪/静止就绪、运行、活跃阻塞/静止阻塞、终止态,可见下图。

新建态:对应于进程刚刚被创建时没有被提交的状态,并等待系统完成创建进程的所有必要信息。

活跃就绪:是指进程在主内存并且可被调度的状态。

静止就绪(挂起就绪):是指进程被对换到辅存时的就绪状态,是不能被直接调度的状态,只有当主存中没有活跃就绪态进程,或者是挂起就绪态进程具有更高的优先级,系统将被挂起就绪态进程调回主存并转化为活跃就绪。

活跃阻塞:是指进程已在主存,一旦等待事件产生便进入活跃就绪状态

静止阻塞:进程对换到辅存时的阻塞状态,一旦等地啊的事件产生便进入静止就绪状态。

终止态:进程已经结束运行,回收除进程控制块之外的其他资源,并让其他进程从进程控制块中有关信息。

线程

由于我们用户的并发请求,为每一个请求都创建一个进程显然是行不通的,从系统资源开销方面或是响应用户请求的效率方面来看。因此系统中线程概念便被引进了。

线程,有时被称之为轻量级的进程(Lightweight Process, LWP),是程序执行流的最小单元。

线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属的一个进程的其他线程共享进程所拥有的全部资源。

一个线程可以创建和撤销另一个线程,统一进程的多个线程之间可以并发执行。

线程是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指运行中的程序的调度单位。

在单个程序中同事运行多个线程完成不同的工作,称之为多线程。

每一个程序都只要有一个线程,若程序只有一个线程,那就是程序本身。

线程的状态:就绪阻塞运行

就绪状态:线程具备运行的所有条件,逻辑上可以运行,在等待处理机。

阻塞状态:线程在等待一个事件(如某个信号量),逻辑上不可执行。

运行状态:线程占有处理机正在运行。

协程

协程是一种用户态的轻量级线程,协程的调度完全由用户控制。协程已拥有自己的寄存器上下文和栈,协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈基本没有内核切换开销,可以不加锁访问全局变量,所以上下文的切换非常快。

如果想要深入的了解协程的实现,可以读鸟哥有关于协程的博文,里面详细介绍了PHP协程 的实现——传送门

线程与进程的区别

  1. 线程是进程内的一个执行单元,进程内至少有一个线程,它们共享进程的地址空间,而进程有自己独立的地址空间。
  2. 进程是资源分配和拥有的单元,同一个进程内的线程共享进程的资源。
  3. 线城市处理器调度的基本单位,进程不是。
  4. 两者均可并发执行
  5. 每个独立的线程有一个程序运行的入口,顺序执行序列和程序的出口,但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。

线程和协程的区别

  1. 一个线程可以有多个协程,一个协程也可以单独拥有多个协程
  2. 线程进程都是同步机制,而协程这是异步
  3. 协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态。

什么是多进程,多线程

多进程是指同一时间里,统一计算机系统中如果允许两个或者两个以上的进程处于运行状态,就是多进程。多开一个进程,多分配一份资源,进程间通信不方便。

多线程就是把一个进程分为很多片,每一片都可以是一个独立的流程与多进程的却别是只会使用一个进程的资源,线程间可以直接通信。

同步阻塞模型

在最早的服务器端程序透视通过多进程、多线程来解决并发IO的问题。

一个请求创建一个进程,然后子进程进入循环同步阻塞地与客户端进行交互,收发处理数据。

多线程模式实现非常简单,线程可以直接向某一个客户端连接发送数据。

步骤:

  1. 创建一个 socket,绑定服务器端口(bind),监听端口(listen),在PHP中用stream_socket_server一个函数就能完成上面3个步骤,当然也可以使用更底层的sockets扩展分别实现。
  2. 进入while循环,阻塞在accept操作上,等待客户端连接进入。此时程序会进入睡眠状态,直到有新的客户端发起connect到服务器,操作系统会唤醒此进程。accept函数返回客户端连接的socket
  3. 主进程在多进程模型下通过fork(PHP: pcntl_fork)创建子进程,多线程模型下使用pthread_create(PHP: new Thread)创建子线程。下文如无特殊声明将使用进程同时表示进程/线程。
  4. 子进程创建成功后进入while循环,阻塞在recv(PHP: fread)调用上,等待客户端向服务器发送数据。收到数据后服务器程序进行处理然后使用send(PHP: fwrite)向客户端发送响应。长连接的服务会持续与客户端交互,而短连接服务一般收到响应就会close。
  5. 当客户端连接关闭时,子进程/线程退出并销毁所有资源。主进程/线程会回收掉此子进程/线程。

缺点:

  • 这种模型严重依赖进程的数量解决并发问题,一个客户端连接就需要占用一个进程,工作进程的数量有多少,并发处理能力就有多少。操作系统可以创建的进程数量是有限的。
  • 启动大量进程会带来额外的进程调度消耗。数百个进程时可能进程上下文切换调度消耗占CPU不到1%可以忽略不计,如果启动数千甚至数万个进程,消耗就会直线上升。调度消耗可能占到 CPU 的百分之几十甚至 100%。

另外有一些场景多进程模型无法解决,比如即时聊天程序(IM),一台服务器要同时维持上万甚至几十万上百万的连接(经典的C10K问题),多进程模型就力不从心了。

还有一种场景也是多进程模型的软肋。通常Web服务器启动100个进程,如果一个请求消耗100 ms,100个进程可以提供1000 QPS,这样的处理能力还是不错的。但是如果请求内要调用外网HTTP接口,像QQ、微博登录,耗时会很长,一个请求需要10s。那一个进程1秒只能处理0.1个请求,100个进程只能达到QPS,这样的处理能力就太差了。

//创建scoket监听
$scokserv = stream_scoket_server('tcp://0.0.0.0:8880', $errno, $errstr);
for ($i = 0; $i < 5; $i++)
{
    if(pcntl_fork() == 0) {
        while (true) {
            $conn = stream_scoket_accept($sockserv);
            if ($conn == false) {
                continue;
            }
            $request = fread($conn, 9000);
            $response = 'hello';
            fwrite($conn, $response);
            fclose($conn);
        }
        exit();
    } 
}

异步非阻塞模型

现在各种高并发异步IO的服务器程序都是基于 epoll 实现的。

在早期Linux就提供了select,可以在一个进程内维持1024个连接,后来加入了poll,可以维持任意数量个连接。

但是poll需要循环检测是否有事件,如果服务器当前有100 W个连接,但是某一个时间内只有一条连接向服务器发送数据,这样系统就会循环100 W次,对于CPU是一种浪费。

Linux在2.6时提供了epoll,可以在系统内维持无数个连接,而且无需轮训。

IO复用异步非阻塞程序使用经典的 Reactor 模型,Reactor 顾名思义,就是反应堆的意思,它本身不处理任何数据收发,只是可以监视一个socket句柄的事件变化。

Reactor模型:

  • Add: 添加一个 SOCKET到 Reactor
  • Set: 修改 SOCKET 对应的事件,如可读可写
  • Del: 从 Reactor 中移除
  • Callback: 事件发生后回调指定的函数

PHP 并发编程实践

PHP的Swoole扩展

PHP的异步、并行、高性能的网络通信引擎,使用纯C语言编写,提供了PHP语言的异步多线程服务器,异步TCP/UDP 网络客户端,异步 MySQL,异步 Redis,数据库连接池,AsyncTask,消息队列,毫秒定时器,异步文件读写,异步 DNS 查询。

除了异步IO的支持之外,Swoole 为 PHP 多进程的模式设计了多个并发数据结构和 IPC 通信机制,可以大大简化多进程并发编程的工作。

Swoole 2.0支持了类似Go语言的协程,可以使用完全同步的代码实现异步程序。

#### Swoole 的异步MySQL实现

$db = new swoole_mysql;
$server = array(
    'host' => '192.168.56.102',
    'port' => 3306,
    'user' => 'test',
    'password' => 'test',
    'database' => 'test',
    'charset' => 'utf8', //指定字符集
    'timeout' => 2,  // 可选:连接超时时间(非查询超时时间),默认为SW_MYSQL_CONNECT_TIMEOUT(1.0)
);

$db->connect($server, function ($db, $r) {
    if ($r === false) {
        var_dump($db->connect_errno, $db->connect_error);
        die;
    }
    $sql = 'show tables';
    $db->query($sql, function(swoole_mysql $db, $r) {
        if ($r === false)
        {
            var_dump($db->error, $db->errno);
        }
        elseif ($r === true )
        {
            var_dump($db->affected_rows, $db->insert_id);
        }
        var_dump($r);
        $db->close();
    });
});

消息队列

用户注册

场景说明:当用户注册后,需要发注册邮件和注册短信。

串行方式:将注册信息写入数据库成功以后,发送注册邮件,再发送注册短信。

并行方式:将注册写入数据库成功后,发送注册邮件的同事,发送注册短信。

消息队列方式:将注册信息写入数据库成功后,将成功信息写入队列,此时直接返回成功给用户,写入队列的时间非常短,可以忽略不计,然后异步发送邮件和短信。

解耦操作

场景说明:用户下单后,订单系统需要通知库存系统。假如库存系统无法访问,则订单减库存将失败,从而导致订单失败。此时需要进行解耦。

引入队列:

  1. 用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户下单成功。
  2. 库存系统订阅下单的消息,采用拉/推得方式,获取下单信息,库存系统根据下单信息,进行库存操作。

流量削峰

应用场景:秒杀活动,流量瞬间激增,服务器压力大

用户发起请求,服务器接收后,先写入消息队列,假如消息队列长度超过最大值,则直接报错或提示用户。

后续程序读取消息队列,在进行处理。

日志处理

应用场景:解决大量日志的传输

日志采集程序可以将日志写入消息队列,然后通过日志处理程序的订阅消费日志。

消息通讯

应用场景: 聊天室

多个客户端订阅同一主题,进行消息发布和接收。

常见的消息队列产品

kafka、ActiveMQ、ZeroMQ, RabbitMQ、Redis等等。

接口的并发请求

curl_multi系列函数。