前言
Swoole Server应该是Swoole工具中最重要也是最核心的一个了,但没有网络服务程序开发基础的童鞋刚接触的时候往往会一头雾水,这里我们返本归元,先聊聊Server在干什么,然后我们再看看Swoole Server是怎样跑起来的~
Server之初
通常,我们会把网络系统根据架构分为B/S架构和C/S架构,而这里笔者想聊的就是这里提到的S,也就是服务(Server)在干什么?
风靡各大高校宿舍的Dota和LOL,主体上可以算是典型的C/S架构的网络系统/软件/应用/程序/What ever
我们先从比较常见的基于PHP的Web网站开始聊起。
这里举例采用的是最基础的Linux + Apache + PHP的开发环境。
当我们打开 http://127.0.0.1:80 并看到Apache的欢迎界面时,我们知道,我们已经成功的完成了一个简单的B/S结构的程序。
虽然这里输出的不是Hello World!虽然目前为止一句PHP也没写。
那么,这个时候,这里我们说B/S中的Server具体指代的是什么呢?以下两个选项哪个是你的想法?
-
运行并保存着我们网站的服务器主机。
-
Apache正在运行的进程。
其实从笔者的角度而言,上述两个选项都是对的,因为Server这个词本身的含义就很丰富,根据特定的语境,它既可以指服务器,也可以指服务程序。
本文中提到的Server如无特别说明,都是指提供服务的应用程序,在当前的场景中,就是Apache。
我们来简单扒一下,在打开这个网页的过程中,Apache作为一个Server,最少要做到哪些工作?
最基础的工作,更深入的问题我们可以一点点讨论。
首先,Apache需要先运行起来,如果Apache没有运行,显然没法向浏览器提供服务(例如,输出Apache的欢迎页面)
传统的Web网站场景中,Server是被动地提供服务的,也就是客户端不请求,Server就不会提供服务,就像一般民事诉讼中的不告不理原则。
再者,浏览器需要有一个可靠的方法找到我们刚刚运行起来的Apache(就像寄快递,要有收件方的地址)
想象一下市民中心的办证大厅,各种各样的窗口,不同的窗口可以办理不同的证件,市民提交的材料就是“输入”,服务台提供的证件就是“输出”,找到正确的窗口,是享受服务的前题。
例如我们在浏览器输入 www.baidu.com ,我们知道会打开百度的首页。
关于域名与DNS的问题,本文就不做探讨了,不了解的童鞋可以简单理解为域名会被解析为IP即可。
当Apache做到了上述两项工作时,我们可以简单认为它具备了作为一个Server的基础。
用物流管理的话说,就是在“正确的时间、正确的地点、正确的货物”。
监听:正确的地点
那么,Apache是怎么做到这两点的呢?相信怎么运行Apache这个问题不必笔者啰嗦,我们主要开始看看第二个问题。
我们怎么去定义这个“正确的地点”呢?最常见的方案,就是TCP/IP协议中的IP协议。
严谨地说,TCP协议是传输层协议;IP是网络层协议。因为两者常常搭配出现,就像LAMP一样,有了TCP/IP协议这个说法。
IP协议帮助客户端在浩瀚的网络中找到正确的主机,例如上文中的 127.0.0.1 主机。
127.0.0.1 是IP协议中定义的一个特殊地址,表示本机,概念上有点像PHP中的$this。
如果我们的主机在局域网中被分配的IP是192.168.1.233,则其他主机也可以通过192.168.1.233这个地址找到我们的主机
IP是“IP协议”给每一台联网设备规定一个地址,便于互相通信和发现。
但一个主机如果只能运行一个服务,就太浪费了,因此如果说IP是用来区分不同的联网设备的,Port在这就是用来区分同一个设备的不同服务的。
最基本的LAMP环境中,SSH需要一个端口(默认22),Apache需要一个端口(默认80),MySql需要一个端口(默认3306)。
一般情况下,端口的编号取值范围是 [1, 65535],一般1000以下的端口都会被一些常用服务默认调用,所以尽可能不要使用。
就像大公司电话系统中的主机与分机一样,IP是主机,全世界通用(没有区号这些东西啦),Port就是分机,仅在自己的主机内通用。
写到这里,前文我们提到的 127.0.0.1:80 的含义就更清晰了,前面的 127.0.0.1 是IP地址,后边的80是端口。而因为80是HTTP服务的默认端口,所以访问一般的常见网站时我们并不需要写成 www.baidu.com:80 。
所以如果是自建HTTP服务的话,默认情况下还是最好提供80端口作为服务端口。
我们把某个服务通过某个端口对外提供服务的行为称为“监听”,形象的说,前文的Apache监听着本机的80端口,如果有客户通过这个端口发来请求,操作系统就会把请求交给Apache,Apache就可以根据请求的具体内容进行处理,并给出响应(例如,欢迎使用Apache!)。
常用“Listen”或者“Bind”这两个动词。
最后,我们可以整理出简单LAMP环境中,基于HTTP协议的Web服务的交互逻辑:
-
客户端(浏览器)将请求提交给指定IP的主机。
-
操作系统根据请求中的PORT,转交给监听了这个PORT的Apache。
-
Apache根据配置找到合适的目录,并获取目录中的PHP脚本。
-
调用Zend对该PHP脚本进行解析,并获得输出结果。
-
将输出结果返回给客户端(浏览器).
而一般情况下,PHP脚本的工作就在第四步中处理具体业务,至于怎么与浏览器保持通信,PHPer一般是不关心的,直到,Swoole重新定义了PHP。
其实还有别的方案,但,俺们的主题是Swoole(写了快一百行才提到Swoole的笔者表示说这句话的时候有点心虚…)
Swoole Server做了什么?
前文我们以Apache作为例子,简单梳理了一下Server在做什么的问题,再回来看Swoole Server,就好理解了,Swoole允许通过PHP构造一个新的Server,提供跟Apache类似的功能,监听请求,作出响应。
其实也回答了群里很常见的一个问题,为什么我运行Swoole Server的时候会跟Apache\Nginx冲突?
因为这里用PHP写的不再只是网页的业务逻辑,同时也包括Server的部分。
那么,我们现在开始第一个简单的Swoole TCP Server的Demo。
<?php //vi swoole_server_demo.php $server = new \swoole_server("127.0.0.1", 8088, SWOOLE_PROCESS, SWOOLE_SOCK_TCP); $server -> start();
后两个参数涉及到了Swoole的运行方式和传输层使用的协议问题,我们以后再聊。
这里我们新建了一个Swoole Server对象,这个对象监听了IP 127.0.0.1,和端口 8088 。然后我们打开浏览器,访问http://127.0.0.1/swoole_server_demo.php,然后我们会惊讶地发现:报错,swoole_server只能运行在cli模式下。
这是很多童鞋第一次接触Swoole会遇到的另一个问题,还记得我前边说的么?Swoole Server已经是一个独立的服务了,它不再依赖于Apache,它本身就是一个完整的网络Server,所以它需要运行在cli模式下,同时也不需要通过浏览器访问的方式执行了。
所以正确的启动方式应该是在shell中执行:
php swoole_server_demo.php > PHP Fatal error: swoole_server::start(): require onReceive/onPacket callback in swoole_server_demo.php
这里的报错又是什么原因呢?因为例子中使用的是TCP Server,我们用打电话的例子来梳理一下,客户给服务打电话,对于服务来说,有这么几个关键事件:
-
OnConnect,建立连接,也就是电话被拨通的时候发生。
-
OnReceive,收到消息,也就是服务听到客户说的话。
-
OnClose,关闭连接,也就是客户\服务其中一方挂掉了电话时发生。
一个完整的TCP服务需要处理这三个过程,而我们访问网页的时候,这些过程都被浏览器\服务程序内部处理掉了,平时写网页的时候只需要考虑对请求作出响应即可。
这种抽象进一步降低了PHP的上手门槛,也让很多人低估了PHP的能力。
而使用Swoole Server的时候,我们需要自己来管理这个过程,同时,也可以在这个基础上做到更多的事情。
所以说学习使用Swoole需要了解更多的是操作系统、计算机网络方面的知识,有相关背景的童鞋学习使用Swoole其实并不困难,了解一下做不同事情需要使用的接口是什么,再了解一下运作机制,就差不多可以上手了。
那我们把这几个回调函数补上,这样就能运行一个完整的Server了:
<?php //vi swoole_server_demo.php $server = new \swoole_server("127.0.0.1",8088,SWOOLE_PROCESS,SWOOLE_SOCK_TCP); $server->on('connect', function ($serv, $fd){ echo "Client:Connect.\n"; }); $server->on('receive', function ($serv, $fd, $from_id, $data) { //打印收到的消息 echo "Receive message: $data"; //关闭连接(当然,也可以不关闭) $serv->close($fd); }); $server->on('close', function ($serv, $fd) { echo "Client: Close.\n"; }); $server -> start();
然后,重新在shell里边执行:
php swoole_server_demo.php >
如果一切顺利的话,我们会看到整个命令行好像卡住了,一个光标在闪动,但没有任何输出?这是什么情况?
笔者第一次遇到的时候思路也没转过弯来,一直以为故障了。
这个时候,其实Server已经启动了,并且正在运行,监听了本机的8088端口,此时Server处于等待的状态,所以没有任何输出。
这个时候如果有客户端访问本机的8088端口,就会触发OnConnect事件了。
我们打开另一个交互窗口(注意别关了Swoole Server正在运行的窗口),用telnet来试试:
# 在第二个Shell窗口 telnet 127.0.0.1 8088 > Trying 127.0.0.1... > Connected to 127.0.0.1. > Escape character is '^]'.
此时,我们再返回第一个窗口,就会看到刚才卡住的光标有输出了:
php swoole_server_demo.php > Client:Connect.
输出的正是我们在OnConnect回调中设置的内容。
想想贝尔第一个打通电话的瞬间。
这时我们可以随便输入一些字符看看:
# 在第二个Shell窗口 telnet 127.0.0.1 8088 > Trying 127.0.0.1... > Connected to 127.0.0.1. > Escape character is '^]'. Hello SwooleServer! > Connection closed by foreign host.
此时再切换回第一个Shell,我们看到输出增加了:
php swoole_server_demo.php > Client:Connect. > Receive message: Hello SwooleServer! > Client: Close.
OK,我们来看看整个过程发生了什么事:
-
运行了一个TCP Server,它监听了 127.0.0.1 主机的 8088 端口。
-
用telnet工具作为客户端,它尝试连接 127.0.0.1 主机的 8088 端口 的服务。
-
Server收到了连接请求,根据TCP的握手机制完成了连接过程,并触发了OnConnect回调,此时Server端输出了字符串“Client:Connect.\n”
-
telnet也获得了连接成功的消息,输出了“Connected to 127.0.0.1.”、“Escape character is ‘^]’.”等消息,此时,相当于电话已经打通了。
-
telnet向Server发送了一个字符串“Hello SwooleServer!”。
-
Server收到了telnet发来的字符串,并触发了OnReceive回调,在该回调中,Server打印了字符串“Receive message: Hello SwooleServer!”,然后将与telnet的连接关闭了。
-
连接关闭后,Server触发OnClose回调,输出了字符串“Client: Close.”。
-
连接关闭后,telnet也输出了字符串“Connection closed by foreign host.”
彪悍的Swoole工具箱
Swoole具备一系列强大的工具,允许我们借助PHP高效开发的特性,写出高性能的Web服务,那么这个工具箱里除了Swoole Server以外还有什么呢?以下引用自官网的手册:
Swoole Server
强大的TCP/UDP Server框架,多线程,EventLoop,事件驱动,异步,Worker进程组,Task异步任务,毫秒定时器,SSL/TLS隧道加密。
- swoole_http_server是swoole_server的子类,内置了Http的支持
- swoole_websocket_server是swoole_http_server的子类,内置了WebSocket的支持
Swoole Client
TCP/UDP客户端,支持同步并发调用,也支持异步事件驱动。
Swoole Event
EventLoop API,让用户可以直接操作底层的事件循环,将socket,stream,管道等Linux文件加入到事件循环中。
eventloop接口仅可用于socket类型的文件描述符,不能用于磁盘文件读写
Swoole Async
异步IO接口,提供了 异步文件系统IO,异步DNS查询,异步MySQL等API。包括2个重要的子模块:
- swoole_timer,异步毫秒定时器,可以实现间隔时间或一次性的定时任务
- file,文件系统操作的异步接口
Swoole Process
进程管理模块,可以方便的创建子进程,进程间通信,进程管理。
Swoole Buffer
强大的内存区管理工具,像C一样进行指针计算,又无需关心内存的申请和释放,而且不用担心内存越界,底层全部做好了。
Swoole Table
基于共享内存和自旋锁实现的超高性能内存表。彻底解决线程,进程间数据共享,加锁同步等问题。
swoole_table的性能可以达到单线程每秒读写50W次
扫码二维码 获取免费视频学习资料
- 本文固定链接: http://phpxs.com/post/7188/
- 转载请注明:转载必须在正文中标注并保留原文链接
- 扫码: 扫上方二维码获取免费视频资料