Swoole从入门到入土(9)——TCP服务器[协程风格]
上一篇,我们一起初步接触了协程。我相信只有一节的讨论,很多小伙伴对于“协程”与“线程”的区分可能还有点模糊。我们这里以两者的比较作为本篇开头,进行一番比较。
首先,“协程”与“线程”的任务调度机制不一样。“协程”重在“协调”,“线程”重在“抢占”。
举个例子:现在有一个任务,需要5个“子协程”同时进行处理,那么执行顺序是:子协程1执行->子协程1暂停->子协程2执行->子协程2暂停……顺序执行,直到结束。所以,协程的特点是某个协程让出时间片后,下一个协程会接上;如果这个协程还在阻塞中,会再次移交给下一个协程。这样往复执行,直到结束。
同样的,如果现在有一个任务,需要5个“线程”同时执行,那么执行顺序是,主线程同时开辟启动5个线程,这5个线程哪一个先执行,当先执行的线程让出时间片后,接下来由哪一个线程接上,这些完全听天由命。所以线程的执行才叫做“抢占式”,直到任务结束。
其次,“协程”与“线程”解决的事情不一样。“协程”解决的的是“并发”问题,“线程”解决的是“并行”问题。
举个栗子,我们的服务器需要支撑500人同时访问,以大名鼎鼎的TOMCAT为例,正常情况下会开辟300个线程进行处理。某个线程一旦接收了请求就会进行阻塞,在处理完成之前就不会再接收第二个请求,这就决定了这时TOMCAT只能“并行”支持300人同时访问,另外的200人只能收到500的报错。但是这里存在一个问题,500人的请求业务并不是全都是阻塞行为(访问数据库、读写文件等),所以我们完全可以在一个线程中开辟N个协程,一个线程同时接受N个请求,对于有阻塞的业务请求进行等待直到业务完成,对于没有阻塞的请求,我们就可以立即响应,这样就可以将“并发”从300,提高到300*N(N>=2)。
当然,从上面这个栗子,我们可以看出“协程”针对的是IO密集型的业务(多阻塞),而线程针对的是计算密集型的业务。但我们不能简单认为IO密集型业务,协程开得越多越好。因为对于IO的处理也需要IO资源,如果同时接受太多的需要IO请求,服务器对于IO的处理在超时时间范围内无法处理完,同样会出现超时错误。
有了以上的比较,我们对于“协程”与“线程”的比较应该是更加明晰了。接下来,我们就一起进入协程风格的TCP服务器话题。先看以下这段代码:
//多进程管理模块
$pool = new Swoole\Process\Pool(2);
//让每个OnWorkerStart回调都自动创建一个协程
$pool->set(['enable_coroutine' => true]);
$pool->on('workerStart', function ($pool, $id) {
//每个进程都监听9501端口
$server = new Swoole\Coroutine\Server('127.0.0.1', '9501' , false, true); //收到15信号关闭服务
Swoole\Process::signal(SIGTERM, function () use ($server) {
$server->shutdown();
}); //接收到新的连接请求 并自动创建一个协程
$server->handle(function (Swoole\Coroutine\Server\Connection $conn) {
while (true) {
//接收数据
$data = $conn->recv();
if (empty($data)) {
$conn->close();
break;
} //发送数据
$conn->send('hello'); \Co::sleep(1);
}
}); //开始监听端口
$server->start();
});
$pool->start();
这是一段完整的协程风格TCP服务器的代码,对于这段代码涉及到的技术点进行以下讨论。
1、关于Swoole\Process\Pool
对于协程代码,我们一般需要一个“协程容器”,来保证一组协程的完整性。在swoole中,创建“协程容器”的方法有两种(编者注:官网写的是三种,但这里不考虑将异步风格转为协程风格的方式):
1) 调用管理模块 Process 和 Process\Pool 的 start 方法,指定构造函数的 enable_coroutine 参数,此种启动方式会在进程启动的时候创建协程容器。
2) 手动创建协程容器,即调用Co\run() 函数,或者使用Coroutine\Scheduler(这两者是一样的,前者是后者的简易调用)。
对于TCP服务器,我们需要并发,需要多个进程进行管理,所以选择了Swoole\Process\Pool(这个进程池的使用,后续我们会详细讨论)。
对于Pool初使化后,在子进程创建时(onWorkerStart事件触发),内部实例化了协程化的TCP服务器。
当$pool->start()调用后,这时会创建出2个子进程(Pool构造函数中传入的子进程数量),所以创建后共有3个进程。主进程进入wait状态,对子进程进行管理,一旦子进程退出,就会再进行启动。这时我们可以把onWorkerStart事件的处理函数看成是一个大的协程容器。
2、Swoole\Coroutine\Server->__construct
Swoole\Coroutine\Server->__construct(string $host, int $port = 0, bool $ssl = false, bool $reuse_port = false);
$host:监听的地址,有三种格式:0.0.0.0/127.0.0.1: IPv4 地址 || ::/::1: IPv6 地址 || unix:/tmp/test.sock: UnixSocket 地址
$port:监听的端口【如果为 0 将由操作系统随机分配一个端口】
$ssl:是否开启 SSL 加密
$reuse_port:是否开启端口重用
Swoole\Coroutine\Server 是一个完全协程化的类,用于创建协程 TCP 服务器,支持 TCP 和 unixSocket 类型。它与 Server 模块不同之处在于以下两点:
1) 动态创建销毁,在运行时可以动态监听端口,也可以动态关闭服务器;
2) 处理连接的过程是完全同步的,程序可以顺序处理 Connect、Receive、Close 事件
3、Swoole\Coroutine\Server->handle:设置连接处理函数,必须在 start() 之前设置处理函数
Swoole\Coroutine\Server->handle(callable $fn);
$fn:设置连接处理函数
注意:
-服务器在 Accept(建立连接) 成功后,会自动创建协程并执行 $fn
-$fn 是在新的子协程空间内执行,因此在函数内无需再次创建协程
-$fn 接受一个参数,类型为 Swoole\Coroutine\Server\Connection 对象
-可以使用 exportSocket() 得到当前连接的 Socket 对象
示例:
$server->handle(function (Swoole\Coroutine\Server\Connection $conn) {
while (true) {
$data = $conn->recv();
}
});
4、关于粘包处理Swoole\Coroutine\Server->set
Swoole\Coroutine\Server->set(array $options);
用法与异步风格的TCP服务器完全一致,如果已经遗忘的小伙伴,请点击这里
示例:
$server->set([
'open_eof_split' => true,
'package_eof' => "abc"
]);
如果客户端发送,1234abc5678abc,则服务端就会recv到两条数据,分别是1234abc和5678abc。
关于协程风格的TCP服务器就介绍到这里。如果想要了解协程风格的类Swoole\Coroutine\Server、Swoole\Coroutine\Server\Connection、Swoole\Process\Pool请阅读官网,这部分非常直观没有什么太多难点。
--------------------------- 我是可爱的分割线 ----------------------------
最后博主借地宣传一下,漳州编程小组招新了,这是一个面向漳州青少年信息学/软件设计的学习小组,有意向的同学点击链接,联系我吧。
Swoole从入门到入土(9)——TCP服务器[协程风格]的更多相关文章
- 【Swoole】简单安装与创建TCP服务器
pecl install swoole PHP的异步.并行.高性能网络通信引擎,使用纯C语言编写,提供了php语言的异步多线程服务器,异步TCP/UDP网络客户端,异步MySQL,异步Redis,数据 ...
- php的异步非阻塞swoole模块使用(一)实现简易tcp服务器--服务端
绑定tcp服务器的地址 $swserver = new swoole_server("127.0.0.1",9501); 设置tcp服务器装机容量(太危言耸听了-其实就是设置属性) ...
- Swoole 中使用通道(Channel)实现协程间通讯(消息队列)
通道 Coroutine\Channel 使用本地内存,不同的进程之间内存是隔离的. 只能在同一进程的不同协程内进行 push 和 pop 操作. Co::set(['hook_flags'=> ...
- flask服务器 + 协程 + 爬虫 + ui自动化
公司有个爬取的需求,要求持续性爬取,需要永久性地挂载到目标网站上,每天爬一次里面的数据.数据有下载表格的,我通过ui自动化点击拿到数据:还有一部分数据是几乎所有的图片信息,信息量近百万,这部分用scr ...
- HTTP从入门到入土(3)——TCP三次握手
TCP三次握手 客户端与服务器之间互相发送HTTP请求响应之前需要先进行TCP连接,因为HTTP是一个无连接.无状态协议,不存在连接的概念,只有请求和响应的概念.而请求和响应实际上只是数据包,他们需要 ...
- php的异步非阻塞swoole模块使用(一)实现简易tcp服务器--客户端
//实例化一个swoole客户端 $swclient = new swoole_client(SWOOLE_SOCK_TCP); //建立连接---如果连接无效则退出 )){ echo "连 ...
- Web服务器-并发服务器-协程 (3.4.2)
@ 目录 1.分析 2.代码 关于作者 1.分析 随着网站的用户量越来愈多,通过多进程多线程的会力不从心 使用协程可以缓解这一问题 只要使用gevent实现 2.代码 from socket impo ...
- Swoole 中使用 TCP 异步服务器、TCP 协程服务器、TCP 同步客户端、TCP 协程客户端
TCP 异步风格服务器 异步风格服务器通过监听事件的方式来编写程序.当对应的事件发生时底层会主动回调指定的函数. 由于默认开启协程化,在回调函数内部会自动创建协程,遇到 IO 会产生协程调度,异步风格 ...
- Swoole 中使用 WebSocket 异步服务器、WebSocket 协程服务器
WebSocket 异步风格服务器 WebSocket\Server 继承自 Http\Server,所以 Http\Server 提供的所有 API 和配置项都可以使用. # ws_server.p ...
- Swoole 中使用 HTTP 异步服务器、HTTP 协程服务器
HTTP 异步风格服务器 # http_server.php $http = new Swoole\Http\Server("0.0.0.0", 9501); // 设置服务器运行 ...
随机推荐
- 【C++】const 常类型
常引用 格式:const 类型说明符 &引用名 注意:常引用所引用的对象不能修改 常对象 格式:类名 const 对象名 或 const 类名 对象名 注意:常对象其数据成员在生存期内不能修改 ...
- 浏览器兼容 : IE 5 到 IE 9
<!--[if IE]> <link href="ie.css" rel="stylesheet"> <![endif]--> ...
- [转帖]什么是内存屏障? Why Memory Barriers ?
要了解如何使用memory barrier,最好的方法是明白它为什么存在.CPU硬件设计为了提高指令的执行速度,增设了两个缓冲区(store buffer, invalidate queue).这个两 ...
- [转帖]tiup cluster restart
https://docs.pingcap.com/zh/tidb/stable/tiup-component-cluster-restart 注意 重启过程中会有一段时间服务不可用. 语法 tiu ...
- [转帖]jmeter压力测试
使用jmeter 进行并发压力测试. 首先需要安装好jmeter,下面以widows操作平台为例: 1.确保电脑安装并配置好java环境:具体怎么下载和配置请自行百度: 2.登录jmeter官网htt ...
- python批量上传文件到七牛云
导航 引子 棘手的需求 化繁为简 实战案例 结语 参考 本文首发于智客工坊-<python批量上传文件到七牛云>,感谢您的阅读,预计阅读时长3min. 古之立大事者,不惟有超世之才,亦必有 ...
- 程序调试利器——GDB使用指南
作者:京东科技 孙晓军 # 1\. GDB介绍 GDB是GNU Debugger的简称,其作用是可以在程序运行时,检测程序正在做些什么.GDB程序自身是使用C和C++程序编写的,但可以支持除C和C++ ...
- ABP-VNext 用户权限管理系统实战05----扩展授权类型(单点登录)
一.适合场景: 1.我方系统在集成到别人的集成本台时一般是拿别的平台的用户名,在我方系统进行登录 2.我方系统是前后端分离,前端要拿到token 二.解决方案:自定义授权类型 我们知道Identity ...
- ffmpeg修改文件格式
http://ffmpeg.org/ 官网下载windows版本 进这个文件夹 随便找一个格式的文件我这里以mp4 放在这个文件夹里面 然后状态栏输入cmd 输入下方命令代码 ffmpeg -i 66 ...
- 13.2 外部DirectX绘制实现
在前一节中我们简单介绍了D3D绘制窗体所具备的基本要素,本节将继续探索外部绘制技术的实现细节,并以此实现一些简单的图形绘制功能,首先外部绘制的核心原理是通过动态创建一个新的窗口并设置该窗口属性为透明无 ...