Swoole入门篇(上)

入门篇的服务对象是那些没有看过官方文档、没有接触过Swoole框架的同学。

通过入门篇的学习,你将会了解Swoole的一些基本入门知识。由于本小册的项目仅仅使用到了Swoole中的部分技术,所以入门篇也只会为这些技术做一个Demo示范,尽量以更少的文字介绍更多的知识,有兴趣的同学也可以学习完本小册后,通过官方文档进行更深入地学习。根据学习金字塔来说,实践才是更高效的学习方式,对于一些技术的实际用法就请大家在小册的正式篇章中进行实战学习了。

编译安装

源码地址:github.com/swoole/swoo…

下载源码后,进入swoole-src文件夹,并依次运行以下命令:

  • phpize
  • ./configure
  • make
  • make install

其中运行configure的时候可以会报出一个错误:configure: error: Cannot find php-config. Please use --with-php-config=PATH。这是因为无法找到php-config文件,解决方法就是在后面使用参数的形式添加文件路径。

  • ./configure —with-php-config=/usr/local/php/bin/php-config

执行完毕后就会看到以下提示:

  1. [root@localhost swoole-src]# make install
  2. Installing shared extensions: /usr/local/php/lib/php/extensions/no-debug-non-zts-20170718/
  3. Installing header files: /usr/local/php/include/php/
  4. [root@localhost swoole-src]# ls /usr/local/php/lib/php/extensions/no-debug-non-zts-20170718/
  5. opcache.a opcache.so redis.so swoole.so yaconf.so

可以看到,swoole.so文件已经编译出来了,下一步就是要引用这个文件作为我们php的扩展。

编辑php.ini文件,在其中加入一句代码:

  • extension=swoole

运行php -m

  1. [root@localhost swoole-src]# php -m
  2. [PHP Modules]
  3. ...
  4. swoole
  5. ...

只要显示出swoole就代表安装成功了。

HTTP服务器

Swoole HTTP Server :wiki.swoole.com/wiki/page/3…

Swoole创建一个HTTP Server非常简单,只需要新建一个swoole_http_server对象,并监听request事件就能够完成普通的HTTP请求了,我们来实验一下。

新建一个http_server.php文件,并在其中编写以下代码:

  1. $http = new swoole_http_server('0.0.0.0', 9501);
  2. $http->on('request', function ($request, $response) {
  3. $response->end("<h1>Hello Swoole. #".rand(1000, 9999)."</h1>");
  4. });
  5. $http->start();

上面代码就是创建了一个HTTP服务器,并监听了9501端口,当有请求进来的时候就会输出Hello Swoole

CentOS环境下运行命令:

  • php http_server.php

18 Swoole入门篇(上) - 图1

看到这个界面就代表运行成功。

但是一般的接口请求都是带有一些参数的,在swoole_http_server这里要怎么获取请求参数呢?童鞋们注意到request回调函数中带有一个$request变量了吗?没错,所有关于请求的数据都在这里,我们打印一下这个变量。

  1. $http->on('request', function ($request, $response) {
  2. var_dump($request);
  3. $response->end("<h1>Hello Swoole. #".rand(1000, 9999)."</h1>");
  4. });

重启服务器后,在访问的URL中增加请求参数?action=test_action

  1. object(Swoole\Http\Request)#6 (10) {
  2. ...
  3. ["get"]=>
  4. array(1) {
  5. ["action"]=>
  6. string(11) "test_action"
  7. }
  8. ...
  9. }

$request对象的get属性中,保存我们传入的参数action和值test_action

使用面向对象创建HTTP服务器

我们上面创建的HTTP服务器是使用面向过程的形式来编写的,使用面向对象会更符合现代的Web开发。

做题时间

  1. 使用面向对象来编写一个HTTP服务器。

http_server.php

  1. class Server
  2. {
  3. private $server;
  4. public function __construct()
  5. {
  6. $this->server = new swoole_http_server('0.0.0.0', 9501);
  7. $this->server->on('request', [$this, 'onRequest']);
  8. $this->server->start();
  9. }
  10. public function onRequest($request, $response)
  11. {
  12. $response->end("<h1>Hello Swoole. #" . rand(1000, 9999) . "</h1>");
  13. }
  14. }
  15. new Server();

只需要把swoole_http_server创建出来的对象作为Server类的成员属性,那就封装完成了。

WebSocket服务器

Swoole WebSocket Server:wiki.swoole.com/wiki/page/3…

Swoole中内置了WebSocket服务器,可以通过简单的代码来监听某个端口的WebSocket连接。

做题时间

  1. 参考官方文档,使用面向对象的方式创建一个WebSocket服务器。

    <?php class WebSocketServer {

    1. const HOST = '0.0.0.0';
    2. const PORT = 9501;
    3. private $server;
    4. public function __construct()
    5. {
    6. $this->server = new swoole_websocket_server(self::HOST, self::PORT);
    7. $this->server->set(array(
    8. 'worker_num' => 4
    9. ));
    10. $this->server->on('start', [$this, 'onStart']);
    11. $this->server->on('workerStart', [$this, 'onWorkerStart']);
    12. $this->server->on('open', [$this, 'onOpen']);
    13. $this->server->on('message', [$this, 'onMessage']);
    14. $this->server->on('close', [$this, 'onClose']);
    15. $this->server->start();
    16. }
    17. public function onStart($server)
    18. {
    19. swoole_set_process_name('websocket-server');
    20. echo sprintf(
    21. "master start (listening on %s:%d)\n",
    22. self::HOST,
    23. self::PORT
    24. );
    25. }
    26. public function onWorkerStart($server, $workerId)
    27. {
    28. echo "server: onWorkStart,worker_id:{$server->worker_id}\n";
    29. }
    30. public function onOpen($server, $request)
    31. {
    32. echo "server: handshake success with fd{$request->fd}\n";
    33. }
    34. public function onMessage($server, $request)
    35. {
    36. echo "receive from {$request->fd}:{$request->data},opcode:{$request->opcode},fin:{$request->finish}\n";
    37. $server->push($request->fd, "this is server");
    38. }
    39. public function onClose($server, $fd)
    40. {
    41. echo "client {$fd} closed\n";
    42. }

    }

    new WebSocketServer();

创建WebSocket Server时调用了set()方法,并且绑定了五个回调方法。当接收到客户端消息时,将会发送一条this is server消息。

Server配置

可以通过调用set()方法,为我们创建的WebSocket Server进行参数配置,如以上代码则表示开启了4worker

有童鞋想问worker有什么用呢?简单来说worker就是用到接受请求并且进行逻辑运算的一个进程,详情可以查看文档:wiki.swoole.com/wiki/page/1…

回调函数

  • onStart:启动后在**主进程(master)**的主线程回调此函数。如果想要修改服务器后台进程名,就需要在这个回调函数中进行处理。
  • onWorkerStart:此事件在Worker进程/Task进程启动时发生。这里创建的对象可以在进程生命周期内使用。当设置了nworker的时候,这个函数将会被回调n次。在默认的dispatch_mode模式下,每一个新连接都会随机分配一个worker进程,并且在此连接断开前每一次的message将会发送到同一个worker进程。如果我们想发送消息到客户端,就可以使用此回调函数的第一个参数$server对象。
  • onOpen:当WebSocket客户端与服务器建立连接并完成握手后会回调此函数。
  • onMessage:当服务器收到来自客户端的数据帧时会回调此函数。第一个参数$serveronWorkerStart()方法中的参数$server是同一个对象。第二个参数$request实际是一个swoole_websocket_frame对象,里面包含了4个属性,其中比较重要的是fddatafd表示当前发送消息的客户端socket id,想要给客户端发送消息就必须要通过这个fddata则保存了客户端发来的数据。
  • onClose:客户端连接关闭后,在worker进程中回调此函数,参数中的$fd就是客户端的连接fd

下面我们来添加一个前端的WebSocket客户端,测试一下Swoole WebSocket服务器是否正常。

新建一个index.html文件,在里面输入以下代码:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <script>
  9. var wsServer = 'ws://192.168.3.41:9501';
  10. var websocket = new WebSocket(wsServer);
  11. websocket.onopen = function (evt) {
  12. websocket.send("this is client");
  13. console.log("Connected to WebSocket server.");
  14. };
  15. websocket.onclose = function (evt) {
  16. console.log("Disconnected");
  17. };
  18. websocket.onmessage = function (evt) {
  19. console.log('Retrieved data from server: ' + evt.data);
  20. };
  21. websocket.onerror = function (evt, e) {
  22. console.log('Error occured: ' + evt.data);
  23. };
  24. </script>
  25. </body>
  26. </html>

前端代码很简单,仅仅是连接了服务端,在连接成功后会回调到onopen()方法,发送this is client字符串到服务端。

打开页面并在chrome浏览器中打开network标签。

18 Swoole入门篇(上) - 图2

客户端成功发送this is client,同时也成功接收到了服务端发来的this is server

  1. [root@localhost PHPDemo]# php websocket_server.php
  2. master start (listening on 0.0.0.0:9501)
  3. server: onWorkStart,worker_id:0
  4. server: onWorkStart,worker_id:2
  5. server: onWorkStart,worker_id:3
  6. server: onWorkStart,worker_id:1
  7. server: handshake success with fd1
  8. receive from 1:this is client,opcode:1,fin:1

服务端中也成功输出了客户端发来的消息。

小结

本章我们学习了Swoole扩展的安装,一个简单的HTTP Server的创建,使用面向对象的代码来封装一个Server,一个简单的WebSocket ServerWebSocket Client。相信童鞋们应该对WebSocket编程有了一点点体会了,下一章我们将会介绍更多Swoole的基础功能。