Install Laravels and use websockets under Laravel

🚀 LaravelS are out-of-the-box adapters between Swoole and Laravel/Lumen.

Environmental requirements

Rely on instructions
PHP > = 5.5.9 Recommend PHP7 +
Swoole > = 1.7.19 PHP5 is no longer supported as of 2.0.12 Recommend holdings +
Laravel/Lumen > = 5.1 Recommend 5.6 +

The installation

1. Install packagist using Composer. It is possible that version 3.0 cannot be found, solution move to #81.

composer require "Hhxsv5 / laravel - s: ~ 3.7.0" -vvv
# Make sure your composer. Lock file is in version control
Copy the code

2. Register the Service Provider (choose one of the following two steps).

  • Laravel: Modify the file config/app.php, Laravel 5.5+ support package automatic discovery, you should skip this step

    'providers'= > [/ /...
        Hhxsv5\LaravelS\Illuminate\LaravelSServiceProvider::class,
    ],
    Copy the code
  • Lumen: Modify the file bootstrap/app.php

    $app->register(Hhxsv5\LaravelS\Illuminate\LaravelSServiceProvider::class);
    Copy the code

3. Publish the configuration and binary files.

Republish after each upgrade of LaravelS Click on theReleaseUnderstand the change logs for each version.

php artisan laravels publish
# config file: config/laravels
Binaries: bin/laravels bin/fswatch bin/inotify
Copy the code

4. Modify the config/laravels. PHP configuration, such as listening IP addresses and ports.

run

Before running, please read carefully: Precautions (this is very important).

  • Operation command:php bin/laravels {start|stop|restart|reload|info|help}.
The command instructions
start Start LaravelS and show a list of started processes”ps -ef|grep laravels
stop Stop LaravelS and trigger custom processesonStopmethods
restart Restart LaravelS: Smooth them firstStopAnd thenStart; inStartBefore completion, the service isNot available
reload Smoothly restart all Task/Worker/Timer processes that contain your business code and trigger custom processesonReloadMaster/Manger process is not restarted. Modify theconfig/laravels.phpAfter youonlycallrestartTo complete the restart
info Displays component version information
help Display help information
  • Startup options, forstartandrestartCommand.
options instructions
-d|–daemonize Run as a daemon. This option overrideslaravels.phpIn theswoole.daemonizeSet up the
-e|–env Specify the environment to run in, such as--env=testingConfiguration files will be used in preference.env.testing, this feature requiresLaravel 5.2 +
-i|–ignore Ignore checking the PID file of the Master process
-x|–x-version Record the version number (branch) of the current project and save it in$_ENV/$_SERVER, access mode:$_ENV['X_VERSION'] $_SERVER['X_VERSION'] $request->server->get('X_VERSION')
  • The runtimeFile:startWill automatically executephp artisan laravels configAnd generate these files, developers generally do not need to pay attention to them, it is recommended to add them to.gitignoreIn the.
file instructions
storage/laravels.conf The LaravelSThe runtimeThe configuration file
storage/laravels.pid PID file of the Master process
storage/laravels-timer-process.pid Timer Indicates the PID file of the process
storage/laravels-custom-processes.pid PID files for all custom processes

Use with Nginx (recommended)

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 {
    Connect via IP:Port
    server 127.0.0.1:5200 weight=5 max_fails=3 fail_timeout=30s;
    /dev/ SHM for better performance
    #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;
    5200 # server 192.168.1.2 instead: backup;
    keepalive 16;
}
server {
    listen 80;
    Don't forget to bind 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 handles static resources (gzip is recommended) while LaravelS handles dynamic resources.
    location / {
        try_files $uri @laravels;
    }
    # respond 404 when requesting PHP files to prevent exposure to public/*.php
    #location ~* \.php$ {
    # return 404;
    #}
    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" means upstream
        proxy_passhttp://swoole; }}Copy the code

Enable the WebSocket server

The WebSocket server listens on the same IP and port as the Http server.

1. Create the WebSocket Handler class, and implement interface WebSocketHandlerInterface. Start will automatically instantiate, do not need to manually create an instance.


      
/** * Created by PhpStorm. * User: wxiangqian * Date: 2020-10-28 * Time: 15:23 */

namespace App\Services;
use Hhxsv5\LaravelS\Swoole\WebSocketHandlerInterface;
use Illuminate\Support\Facades\Log;
use Swoole\Http\Request;
use Swoole\WebSocket\Frame;
use Swoole\WebSocket\Server;

/ * * *@see https://wiki.swoole.com/#/start/start_ws_server
 */
class WebSocketService implements WebSocketHandlerInterface
{
    // Declare a constructor with no arguments
    public function __construct()
    {}public function onOpen(Server $server, Request $request)
    {
        // The HTTP request to set up WebSocket is routed through Laravel before the onOpen event is triggered.
        // So Laravel's Request, Auth, etc., are readable. Session is read and write, but only in onOpen events.
        // \Log::info('New WebSocket connection', [$request->fd, request()->all(), session()->getId(), session('xxx'), session(['yyy' => time()])]);
        Log::info('WebSocket connection setup ');
        $server->push($request->fd, 'Welcome to LaravelS');
        // throw new \Exception('an exception'); // The raised exception will be ignored by the upper layer and recorded in a Swoole log, requiring the developer to try/catch it
    }
    public function onMessage(Server $server, Frame $frame)
    {
        // \Log::info('Received message', [$fram // \Log::info('Received message', [$frame->fd, $frame->data, $frame->opcode, $frame->finish]);
        //
        // $server->push($frame->fd, $frame->data); e->fd, $frame->data, $frame->opcode, $frame->finish]);

        $server->push($frame->fd, $frame->data);
// $arr = [
// 'time' => date('Y-m-d H:i:s')
/ /];
// $server->push($frame->fd, json_encode($arr));
        // throw new \Exception('an exception'); // The raised exception will be ignored by the upper layer and recorded in a Swoole log, requiring the developer to try/catch it
    }
    public function onClose(Server $server.$fd.$reactorId)
    {
        Log::info('WebSocket connection closed ');
        // throw new \Exception('an exception'); // The raised exception will be ignored by the upper layer and recorded in a Swoole log, requiring the developer to try/catch it}}Copy the code

2. Change the config/laravels.

// ...
'websocket'= > ['enable'= >true.// This is true
    'handler' => \App\Services\WebSocketService::class,
],
'swoole'= > [/ /...
    // Dispatch_mode can be set to 2, 4, 5, or https://wiki.swoole.com/#/server/setting?id=dispatch_mode
    'dispatch_mode'= >2./ /...].// ...
Copy the code

3. Use SwooleTable to bind FD to UserId, optional, SwooleTable example. You can also use other global storage services, such as Redis/Memcached/MySQL, but be aware that FDS may clash with multiple Swoole Server instances.

4. Use with Nginx (recommended)

See WebSocket proxy

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}
upstream swoole {
    Connect via IP:Port
    server 127.0.0.1:5200 weight=5 max_fails=3 fail_timeout=30s;
    /dev/ SHM for better performance
    #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;
    5200 # server 192.168.1.2 instead: backup;
    keepalive 16;
}
server {
    listen 80;
    Don't forget to bind 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 handles static resources (gzip is recommended) while LaravelS handles dynamic resources.
    location / {
        try_files $uri @laravels;
    }
    # respond 404 when requesting PHP files to prevent exposure to public/*.php
    #location ~* \.php$ {
    # return 404;
    #}
    # Http and WebSocket coexist, Nginx distinguishes between them via location
    #!!!!!! The WebSocket connection path is /ws
    # Javascript: var ws = new WebSocket("ws://laravels.com/ws");
    location =/ws {
        # proxy_connect_timeout 60s;
        # proxy_send_timeout 60s;
        # proxy_read_timeout: If the proxied server does not respond to Nginx within 60 seconds, then Nginx will close the current connection; Swoole's heartbeat Settings also affect the closure of the connection
        # 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 60s;
        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;
        proxy_passhttp://swoole; }}Copy the code

5. Configure the heartbeat

  • Swoole heartbeat configuration

    // config/laravels.php
    'swoole'= > [/ /...
        // Traverses every 60 seconds. If a connection does not send any data to the server within 600 seconds, it will be forcibly closed
        'heartbeat_idle_time'= >600.'heartbeat_check_interval'= >60./ /...].Copy the code
  • Nginx reads the proxy server timeout configuration

    If the proxied server does not respond to Nginx within 60 seconds, then Nginx will close the current connection
    proxy_read_timeout 60s;
    Copy the code

HTML field code

GitHub complete source code

<! DOCTYPEhtml>
<html>
<head>
    <meta charset="UTF-8">
    <title>Chat Client</title>
</head>
<body>
<div style="width:600px; margin:0 auto; border:1px solid #ccc;">
    <div id="content" style="overflow-y:auto; height:300px;"></div>
    <hr />
    <div style="height:40px; background:white;">
        <input type="text" class="form-control" id="message"  placeholder="Please enter the content">
        <button type="button" class="btn btn-primary" onclick="sendMessage()">Primary</button>
    </div>
</div>

<script type="text/javascript">
    if(window.WebSocket){
        // Do not write the correct port and IP address
        var webSocket = new WebSocket(Ws: / / 127.0.0.1:5200 "");
        webSocket.onopen = function (event) {
            console.log('webSocket connection successful ');
        };
        // Triggered when the connection is closed
        webSocket.onclose = function (event) {
            console.log("WebSocket closes the connection");
        }

        // Received a callback from the server
        webSocket.onmessage = function (event) {
            var content = document.getElementById('content');
            content.innerHTML = content.innerHTML.concat('

'

+event.data+'</p>'); console.log(event.data) } var sendMessage = function(){ var data = document.getElementById('message').value; webSocket.send(data); }}else{ console.log("Your browser does not support WebSocket"); }
</script> </body> </html> Copy the code

Actual screenshots

conclusion

Hopefully this article has helped you learn how to use websocket. 👍

Related articles

Install Laravels and use websockets under Laravel

How to use hhXSV5 / LARavel-S asynchronous task queue