当前位置: 首页 > 工具软件 > LaravelS > 使用案例 >

Laravels配置过程(一)

谢奇略
2023-12-01

Laravels作为websocket服务器

因为公司要做一个学生在线答题 — 老师监控的功能,并且可以实时同步学生的答题状态 所以配置了swoole,当做websocket服务器。我这里用的扩展是laravel-s 来实现一个类似聊天室的功能。

laravel-s文档:https://github.com/hhxsv5/laravel-s/blob/master/README-CN.md

版本信息

  • Laravel 8.0
  • hhxsv5/laravel-s 3.7
  • Swoole扩展 4.5.6
  • PHP 7.3

安装并配置Laravel-s扩展

  1. composer require "hhxsv5/laravel-s:~3.7.0" -vvv

  2. 发布laravel-s的配置文件 php artisan laravels publish

  3. 生成的文件在config/laravels.php中,需要关注的就几项:

    //第24行监听的ip
    'listen_ip' => env('LARAVELS_LISTEN_IP', '127.0.0.1'),
    //第35行监听的端口
    'listen_port' => env('LARAVELS_LISTEN_PORT', 5200),
    
    //第149行 需要修改如下:
    'websocket' => [        
      'enable'  => true,        
      'handler' => \App\Servers\WebSocketService::class, 
    ],
    //第287行 修改 dispatch_mode
    'swoole'         => [
        //...
        // dispatch_mode只能设置为2、4、5,https://wiki.swoole.com/#/server/setting?id=dispatch_mode
        'dispatch_mode' => env('LARAVELS_DISPATCH_MODE', 2),
        //...
    ],
    

    这里laravels需要一个handler类来响应websocket的各种事件,具体的代码 我会放到文末,也可以直接在laravels 文档中获取

配置nginx

不要忘记修改绑定的host

gzip on;
gzip_min_length 1024;
gzip_comp_level 2;
gzip_types text/plain text/css text/javascript application/json application/javascript application/x-javascript application/xml application/x-httpd-php image/jpeg image/gif image/png font/ttf font/otf image/svg+xml;
gzip_vary on;
gzip_disable "msie6";
upstream swoole {
    # 通过 IP:Port 连接 这里的ip和端口 应该config中的配置保持一致
    server 127.0.0.1:5200 weight=5 max_fails=3 fail_timeout=30s;
    # 通过 UnixSocket Stream 连接,小诀窍:将socket文件放在/dev/shm目录下,可获得更好的性能
    #server unix:/yourpath/laravel-s-test/storage/laravels.sock weight=5 max_fails=3 fail_timeout=30s;
    #server 192.168.1.1:5200 weight=3 max_fails=3 fail_timeout=30s;
    #server 192.168.1.2:5200 backup;
    keepalive 16;
}
server {
    listen 80;
    # 别忘了绑Host
    server_name laravels.com;
    root /yourpath/laravel-s-test/public;
    access_log /yourpath/log/nginx/$server_name.access.log  main;
    autoindex off;
    index index.html index.htm;
    # Nginx处理静态资源(建议开启gzip),LaravelS处理动态资源。
    location / {
        try_files $uri @laravels;
    }
    # Http and WebSocket are concomitant, Nginx identifies them by "location"
    # !!! The location of WebSocket is "/ws"
    # Javascript: var ws = new WebSocket("ws://laravels.com/ws");
    location =/ws {
        # proxy_connect_timeout 60s;
        # proxy_send_timeout 60s;
        # proxy_read_timeout: Nginx will close the connection if the proxied server does not send data to Nginx in 60 seconds; At the same time, this close behavior is also affected by heartbeat setting of Swoole.
        # proxy_read_timeout 60s;
        proxy_http_version 1.1;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Real-PORT $remote_port;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header Scheme $scheme;
        proxy_set_header Server-Protocol $server_protocol;
        proxy_set_header Server-Name $server_name;
        proxy_set_header Server-Addr $server_addr;
        proxy_set_header Server-Port $server_port;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_pass http://swoole;
    }
    location @laravels {
        # proxy_connect_timeout 60s;
        # proxy_send_timeout 60s;
        # proxy_read_timeout 120s;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Real-PORT $remote_port;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header Scheme $scheme;
        proxy_set_header Server-Protocol $server_protocol;
        proxy_set_header Server-Name $server_name;
        proxy_set_header Server-Addr $server_addr;
        proxy_set_header Server-Port $server_port;
        # “swoole”是指upstream
        proxy_pass http://swoole;
    }
}

通过supervisord守护进程

  1. 添加supervisord配置文件 配置文件目录一般在 /etc/supervisor/conf.d
[program:laravel-s-test]
directory=/www/laravel
command=/usr/bin/php bin/laravels start -i
numprocs=1
autostart=true
autorestart=true
startretries=3
user=www-data
redirect_stderr=true
stdout_logfile=/var/log/supervisor/%(program_name)s.log

directory 设置为laravel项目根目录即可。

command中需要正确定义 php可执行命令的地址。

stdout_logfile是输出的日志文件,需要保证user配置项中的用户有可写权限。

  1. 载入配置并重启supervisord
> sudo supervisorctl update
> sudo supervisorctl start laravel-s-test
laravel-s-test: started

查看一下端口的监听状态

> lsof -i tcp:5200
COMMAND   PID     USER   FD   TYPE     DEVICE SIZE/OFF NODE NAME
php     10932 www-data    5u  IPv4 1174359495      0t0  TCP localhost:5200 (LISTEN)

这样websocket服务器就算正式启动了

业务代码

websocketService监听器类的代码很简单 只有三种事件,

onOpen 建立连接的事件,onMessage服务器收到推送消息的事件,onClose连接关闭事件。

代码一定要包裹在try catch之中,监听器中的异常只会记录到swoole的异常日志,不会抛出

namespace App\Services;
use Hhxsv5\LaravelS\Swoole\WebSocketHandlerInterface;
use Swoole\Http\Request;
use Swoole\Http\Response;
use Swoole\WebSocket\Frame;
use Swoole\WebSocket\Server;
/**
 * @see https://wiki.swoole.com/#/start/start_ws_server
 */
class WebSocketService implements WebSocketHandlerInterface
{
    // 声明没有参数的构造函数
    public function __construct()
    {
    }
    // public function onHandShake(Request $request, Response $response)
    // {
           // 自定义握手:https://wiki.swoole.com/#/websocket_server?id=onhandshake
           // 成功握手之后会自动触发onOpen事件
    // }
    public function onOpen(Server $server, Request $request)
    {
        // 在触发onOpen事件之前,建立WebSocket的HTTP请求已经经过了Laravel的路由,
        // 所以Laravel的Request、Auth等信息是可读的,Session是可读写的,但仅限在onOpen事件中。
        // \Log::info('New WebSocket connection', [$request->fd, request()->all(), session()->getId(), session('xxx'), session(['yyy' => time()])]);
        // 此处抛出的异常会被上层捕获并记录到Swoole日志,开发者需要手动try/catch
        $server->push($request->fd, 'Welcome to LaravelS');
    }
    public function onMessage(Server $server, Frame $frame)
    {
        // \Log::info('Received message', [$frame->fd, $frame->data, $frame->opcode, $frame->finish]);
        // 此处抛出的异常会被上层捕获并记录到Swoole日志,开发者需要手动try/catch
        $server->push($frame->fd, date('Y-m-d H:i:s'));
    }
    public function onClose(Server $server, $fd, $reactorId)
    {
        // 此处抛出的异常会被上层捕获并记录到Swoole日志,开发者需要手动try/catch
    }
}

如果在其他类中想推送数据给前端 可以这样写

//这里获取的是 Swoole\WebSocket\Server 实例
app('swoole')->push($fd, $data);

尾巴

这篇主要记录一下配置之类的,免得自己忘记,其实可以直接看laravels的配置文档。

下一篇会记录 WebSocketService类中对websock事件的响应的业务代码

  • Laravels 中文文档:https://github.com/hhxsv5/laravel-s/blob/master/README-CN.md
  • Laravel 文档:https://learnku.com/docs/laravel/8.x
 类似资料: