Docker中的LaravelS

Ⅰ.序

此篇是接着这篇写滴:https://www.jianshu.com/p/6c9ad29c552b;
目的是一个搭小聊天室。

Ⅱ.安装swoole扩展

再上一遍的基础上我先安装了sockets calendar exif pcntl shmop sysvmsg sysvsem sysvshm wddx这些扩展,文件laravel/docker/Dockerfile,在上一步安装php扩展的下方添加,感觉只有sockets是需要的,谁知道呢...都给整上:

RUN set -xe \
    && docker-php-ext-configure calendar --enable-calendar \
    && docker-php-ext-configure exif --enable-exif \
    && docker-php-ext-configure pcntl --enable-pcntl \
    && docker-php-ext-configure shmop --enable-shmop \
    && docker-php-ext-configure sysvmsg --enable-sysvmsg \
    && docker-php-ext-configure sysvsem --enable-sysvsem \
    && docker-php-ext-configure sysvshm --enable-sysvshm \
    && docker-php-ext-configure wddx --enable-wddx \
    && docker-php-ext-configure sockets --enable-sockets \
    && docker-php-ext-install sockets  calendar exif pcntl shmop sysvmsg sysvsem sysvshm wddx

安装swoole扩展

  • 文件laravel/docker/Dockerfile添加:
RUN set -xe \
    && pecl install -o -f swoole \
    && docker-php-ext-enable swoole
  • 报错,大致是这样说的(忘记截图了):错误:加载公共库libstdc++.so.6 文件找不到或者不存在...swoole.so...找不到或者不存在。但其实swoole.so是有的,只是加载出来要libstdc++库。所以:
RUN set -xe \
    && apk add --no-cache --virtual .persistent-deps \
        libstdc++

安装LaravelS

参考

composer require hhxsv5/laravel-s
php artisan laravels publish

配置 .env

LARAVELS_LISTEN_IP=workspace
LARAVELS_DAEMONIZE=true

实现 WebSocket 服务器

参考

agents = array(
            self::AGENT_PC_WEB,
            self::AGENT_PHONE_WEB,
            self::AGENT_PC_CLIENT,
            self::AGENT_PHONE_CLIENT,
        );
    }

    // 连接建立时触发
    public function onOpen(Server $server, Request $request)
    {
        // 在触发 WebSocket 连接建立事件之前,Laravel 应用初始化的生命周期已经结束,你可以在这里获取 Laravel 请求和会话数据
        // 调用 push 方法向客户端推送数据,fd 是客户端连接标识字段
        Log::info('WebSocket 连接建立');
        // $server->push($request->fd, 'Welcome to WebSocket Server built on LaravelS');
    }

    // 收到消息时触发
    public function onMessage(Server $server, Frame $frame)
    {
        // 调用 push 方法向客户端推送数据
//        $server->push($frame->fd, 'This is a message sent from WebSocket Server at ' . date('Y-m-d H:i:s'));
        echo "received message: {$frame->data}\n";
        $msg = json_decode($frame->data, true);
        if ($msg['msg_type'] == self::MSG_TYPE_JOIN) {
            // TODO:创建房间。参考:https://github.com/nineyang/chat/blob/master/app/Console/Commands/Swoole.php
            Redis::zadd("room:{$msg['room_id']}", intval($msg['msg_from']), $frame->fd);
            Redis::hset('room', $frame->fd, $msg['room_id']);
            $memberInfo = [
                'online' => Redis::zcard("room:{$msg['room_id']}"),
                'all'    => Redis::zrange("room:{$msg['room_id']}", 0, -1)
            ];
            $message = [
                'member_info' => $memberInfo,
                'msg'         => $msg['msg_body']
            ];
            $this->sendAll($server, $msg['room_id'], $msg['msg_from'], $message, self::MSG_TYPE_JOIN);
        } else {
            $this->sendAll($server, $msg['room_id'], $msg['msg_from'], $frame->data, self::MSG_TYPE_TEXT);
            // event(new Chat($msg['msg_from'], $frame->data));
        }
    }

    // 关闭连接时触发
    public function onClose(Server $server, $fd, $reactorId)
    {
        echo "connection close: {$fd}\n";
        $room_id = Redis::hget('room', $fd);
        Redis::hdel('room', $fd);
        $user_id = intval(Redis::zscore("room:{$room_id}", $fd));
        Redis::zrem("room:{$room_id}", $fd);
        $balance = Redis::zcard("room:{$room_id}");
        if ($balance == 0) {
            Redis::del("room:{$room_id}");
            return;
        }
        $memberInfo = [
            'online' => $balance,
            'all'    => Redis::zrange("room:{$room_id}", 0, -1)
        ];
        $message = [
            'member_info' => $memberInfo,
            'msg'         => $user_id . '离开了群聊'
        ];
        $this->sendAll($server, $room_id, $user_id, $message, self::MSG_TYPE_LEAVE);
    }

    /**
     * @param Server $ws
     * @param $room_id
     * @param null $user_id
     * @param null $message
     * @param int $type
     */
    private function sendAll($ws, $room_id, $user_id = null, $message = null, $type = self::MSG_TYPE_TEXT)
    {
//        $user = $this->user->find($user_id, ['id', 'name']);
//        if (!$user) {
//            return;
//        }
        if ($type != self::MSG_TYPE_TEXT)
            $message = json_encode([
                'msg_body' => is_string($message) ? nl2br($message) : $message,
                'msg_from' => $user_id,
                'msg_type' => $type
            ]);
        $members = Redis::zrange("room:{$room_id}", 0, -1);
        foreach ($members as $fd) {
            $ws->push($fd, $message);
        }
    }
}

修改配置文件

  • 配置文件 config/laravels.php
'websocket' => [
    'enable' => true,
    'handler' => \App\Services\WebSocketService::class,
],
...
// 可选
'swoole' => [
    ...
    
    // 每隔 60s 检测一次所有连接,如果某个连接在 600s 内都没有发送任何数据,则关闭该连接
    'heartbeat_idle_time'      => 600,
    'heartbeat_check_interval' => 60,
    
    ...
],
  • 配置.env
LARAVELS_LISTEN_IP=app   // 这里的 IP 需要和 nginx upstream 中配置的监听 IP 保持一致,即php-fpm的服务名
LARAVELS_DAEMONIZE=true
  • 配置Nginx配置文件,注意mapupstream配置与server是平级的
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

upstream laravels{
    # Connect IP:Port
#            server workspace:5200 weight=5 max_fails=3 fail_timeout=30s;
#            keepalive 16;
    server app:5200 weight=5 max_fails=3 fail_timeout=30s;
    keepalive 16;
}

server {
        listen 80;
        server_name .purchase.com;

        set $root_path '/usr/share/nginx/html';
        set $php_path '/var/www/html/public';

        client_max_body_size 100m;

        charset utf-8;

        location ~ (\.html|\.css|\.js|\.jpg|\.jpeg|\.png|\.gif|\.ico|\.ttf|\.woff|\.woff2|\.xls|\.xlsx|\.docx|\.pdf|\.mpga|\.mp3|\.txt) {
             root $root_path;
       }

        location / {
             index   index.php index.html index.htm;
             try_files $uri $uri/ /index.php?$args;
        }

        # WebSocket 通信
        location =/ws {
            proxy_connect_timeout 3600;
            proxy_send_timeout   3600;
            proxy_read_timeout   3600;
            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://laravels;
        }

        location ~ \.php(.*)$ {
            root $php_path;
            fastcgi_pass app:9000;
            fastcgi_index index.php;
            fastcgi_buffering off;
            fastcgi_split_path_info ^((?U).+\.php)(/?.+)$;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            fastcgi_param PATH_INFO $fastcgi_path_info;
            fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
            include fastcgi_params;
            fastcgi_intercept_errors on;
        }

        location ~ /\.ht {
            deny all;
        }
}

Websocket的前端实现

直接上代码吧,简陋滴很。(要一个jquery文件)




    
    

    Laravel
    
    
    
    
    


启动

在php源代码容器中,项目根目录下执行:

php bin/laravels start

访问下:http://purchase.com/test/chatRoom/91609929b9699631e84fdef3385f3721(本地的host文件配置好域名)

  • 出现问题,这个问题参考的网站下的评论里也出现了,但是没有解决:
    php源代码容器中,[ERROR] worker[5] error: exitCode=255, signal=0

解决问题

在nginx容器中查看app:5200是否能连上:

  • 安装telnet,参考,下面这样是正常的
/usr/share/nginx/html # apk update
/usr/share/nginx/html # apk add busybox-extras
/usr/share/nginx/html # busybox-extras telnet app:5200
Connected to app:5200
  • 如果连不上,可能是5200端口没有给到位,那么,文件docker/docker-compose.override.yml,暴露或者映射下端口
    app:
        build:
            context: ../laravel/
            dockerfile: docker/Dockerfile
        image: "laravel-php:latest"
        ports:
            - "5200:5200"
        environment:
            ## Application Configuration ##
            APP_ENV: "dev"
            APP_DEBUG: "true"
        volumes:
            - ../laravel:/var/www/html

php源代码容器中,[ERROR] worker[5] error: exitCode=255, signal=0

  • 参考,composer.json文件加入 "app/Services",
"autoload": {
"psr-4": {
"App\": "app/"
},
"classmap": [
"database/seeds",
"app/Services",
"database/factories"
]
},
  • 项目根目录下运行命令
composer dump-autoload

至此,模子就差不多了

image.png

你可能感兴趣的:(Docker中的LaravelS)