本文先提供一个没有采用的方式--采用事务加select for update的形式
这么做呢就有个非常严重的问题,--- 同一时刻只有一个有效服务
如果A系统拿到了数据,开始了事务但是没提交,那么B系统同样的条件也会查到同一批还没处理好提交的数据,此时B系统该查询线程就会阻塞等待A提交事务这么看问题就来了,这里虽然保障了同一时刻只有一个服务可以拿到并处理一批数据,但是也导致了效率特别低,而且后面 无论扩展多少服务应用都没啥用
步骤解释:
我这里只写了大致的方案,一些redis高可用以及数据幂等性自己考虑去
这种方式是 我觉得最好的方案 了,完全保障了每个服务每次 处理 mysql的数据都是 互不相同的数据 ,完全 避免了竞争 问题
但是我们目前没有用这种方案,原因是目前我们redis内存只申请到一个比较小的内存,而 zset采用的跳跃表结构虽然保障了数据查询非常快速,但是也非常占用内存 ,预估了一下按照我们的数据量起码要存储300万数据,用到的内存量是 3~4G 之间,好家伙直接把我们所有内存都用了,其他服务还用个屁而且这玩意为了保障数据安全,不进行数据淘汰起码还要留个1G空闲安全空间那肯定就用不了了
如果你们的服务 数据量够小 或者 内存够大 ,redis又做到了 高可用,高可靠 ,那么我还是 十分推荐 用这种方案,毕竟很多服务都是 性能为王!
所谓并发服务器就是在同一个时刻可以处理来自多个客户端的请求;循环服务器是指服务器在同一时刻只可以响应一个客户端的请求。而且对于TCP和UDP套接字,这两种服务器的实现方式也有不同的特点。1、TCP循环服务器:
首先TCP服务器接受一个客户端的连接请求,处理连接请求,在完成这个客户端的所有请求后断开连接,然后再接受下一个客户端的请求。创建TCP循环服务器的算法如下:
复制代码 代码如下:
socket(……); //创建一个TCP套接字
bind(……); //邦定公认的端口号
listen(……); //倾听客户端连接
while(1) //开始循环接收客户端连接
{
accept(……);//接收当前客户端的连接
while(1)
{ //处理当前客户端的请求
read(……);
process(……);
write(……);
}
close(……); //关闭当前客户端的连接,准备接收下一个客户端连接
}
TCP循环服务器一次只处理一个客户端的请求,如果有一个客户端占用服务器不放时,其它的客户机连接请求都得不到及时的响应。因此,TCP服务器一般很少用循环服务器模型的。
2、TCP并发服务器:
并发服务器的思想是每一个客户端的请求并不由服务器的主进程直接处理,而是服务器主进程创建一个子进程来处理。创建TCP并发服务器的算法如下:
复制代码 代码如下:
socket(……); //创建一个TCP套接字
bind(……); //邦定公认的端口号
listen(……);//倾听客户端连接
while(1) //开始循环接收客户端的接收
{
accept(……);//接收一个客户端的连接
if(fork(……)==0) //创建子进程
{
while(1)
{ //子进程处理某个客户端的连接
read(……);
process(……);
write(……);
}
close(……); //关闭子进程处理的客户端连接
exit(……) ;//终止该子进程
}
close(……); //父进程关闭连接套接字描述符,准备接收下一个客户端连接
}
TCP并发服务器可以解决TCP循环服务器客户端独占服务器的情况。但同时也带来了一个不小的问题,即响应客户机的请求,服务器要创建子进程来处理,而创建子进程是一种非常消耗资源的 *** 作。
3、UDP循环服务器:
UDP服务器每次从套接字上读取一个客户端的数据报请求,处理接收到的UDP数据报,然后将结果返回给客户机。创建UDP循环服务器的算法如下:
1 socket(……); //创建一个数据报类型的套接字 2 bind(……); //邦定公认的短口号 3 while(1) //开始接收客户端的连接 4 { //接收和处理客户端的UDP数据报 5 recvfrom(……); 6 process(……); 7 sendto(……);//准备接收下一个客户机的数据报 8 }
消除行号
因为UDP是非面向连接的,没有一个客户端可以独占服务器。只要处理过程不是死循环,服务器对于每一个客户机的请求总是能够处理的。
UDP循环服务器在数据报流量过大时由于处理任务繁重可能造成客户技数据报丢失,但是因为UDP协议本身不保证数据报可靠到达,所以UDP协议是允许丢失数据报的。
鉴于以上两点,一般的UDP服务器采用循环方式4、UDP并发服务器把并发的概念应用UDP就得到了并发UDP服务器,和并发TCP服务器模型一样是创建子进程来处理的。
创建UDP并发服务器的算法如下:
复制代码 代码如下:
socket(……); //创建一个数据报类型的套接字
bind(……); //邦定公认的短口号
while(1) //开始接收客户端的连接
{ //接收和处理客户端的UDP数据报
recvfrom(……);
if(fork(……)==0) //创建子进程
{
rocess(……);
sendto(……);
}
}
除非服务器在处理客户端的请求所用的时间比较长以外,人们实际上很少用这种UDP并发服务器模型的。
4、多路复用I/O并发服务器:
创建子进程会带来系统资源的大量消耗,为了解决这个问题,采用多路复用I/O模型的并发服务器。采用select函数创建多路复用I/O模型的并发服务器的算法如下:
初始化(socket,bind,listen);
复制代码 代码如下:
while(1)
{
设置监听读写文件描述符(FD_);
调用select;
如果是倾听套接字就绪,说明一个新的连接请求建立
{
建立连接(accept);
加入到监听文件描述符中去;
}
否则说明是一个已经连接过的描述符
{
进行 *** 作(read或者write);
}
多路复用I/O可以解决资源限制问题,此模型实际上是将UDP循环模型用在了TCP上面。这也会带了一些问题,如由于服务器依次处理客户的请求,所以可能导致友的客户会等待很久。产生进程的开销要比线程的开销更大。如果你的服务器连接的客户端的数量比较少,那么进程和线程在效率方面的差别感觉并不大。如果数量很大,比如1000,甚至更多,如果你用进程,那么响应完1000+的客户端连接就会变得很慢,因为你要把资源复制1000多份;但是用线程,它们共享同一个进程里的资源,就不需要花那么大的开销去响应客户端的连接。消息服务器使用socket,为避免服务器过载,单台只允许500个socket连接,当一台不够的时候,扩充消息服务器是必然,问题来了,如何让链接在不同消息服务器上的用户可以实现消息发送呢?
要实现消息互通就必须要让这些消息服务器本身能互通,想了两个方式,一种是消息服务器之间交叉链接,另一种是增加一个特殊的消息服务器,这个消息服务器不对外开放,只负责消息转发和推送。
下列测试不考虑防火墙等。仅测试可行性和效率。
消息服务器
转发服务器
公共缓存
软件环境
client1 可向 client2 或者其他 client 发送消息,并接收其他 client 发送的消息
Redis 中保存 client 连接的信息,给每个用户分配唯一的 key ,包括链接的哪台服务器,转发服务器定时检测消息服务器,如消息服务器挂掉,由转发服务器清理掉Redis已经挂掉的所有链接。
1 Client1 给 Client2 发送一条消息
2 Socket1 接收到消息,根据 key从Redis 取出 Client2 的连接信息,连接在本机,直接推送给 Client2 ,流程结束。
3如果连接不在本机,把消息推送到转发服务器,由转发服务器把该消息推送给连接所在消息服务器,消息服务器接收消息,推送给 Client2 。
服务器上创建一个serverphp,内容如下:
上只需把ip变更一下即可。1921680201变更为1921680202
在转发服务器上建立脚本proxyphp,内容如下:
注意开启顺序
1开启转发服务器php proxyphp
2分别开启socket服务器php serverphp
可以在转发服务器上看到两个消息服务器已经连接
3开始测试,分别打开两个telnet,连接两个消息服务器,发送消息测试:
登陆
基于强大的 swoole 扩展,让php高效的实现这些成为可能,目前消息服务器到转发服务器是长连接,转发服务器到消息服务器是短连接,存在性能瓶颈,也浪费了连接资源。下一步改造成长连接,消息服务器的client使用异步。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)