使用Docker、Laravel、Redis和Laravel Echo实现实时通信
首先
在Google上搜索时,我找到了很多关于使用Laravel和Redis进行WebSocket通信的官方文档和其他文章,但是我尝试了很多方法却一直不太成功,所以我决定将这些过程记录在这篇文章中作为备忘。由于篇幅原因,我将省略各个工具的说明。我认为搜索会有很多更易理解的文章出现…。
环境
Docker Desktop for Mac 3.6.0
PHP 8.0.9版本
Laravel 8.40版本
redis-server 6.2.5版本
Laravel-echo-server 1.6.2版本
node 14.16.0版本
npm 7.8.0版本
composer 1.10.19版本
使用Docker构建容器。
这次我们在以下文章的基础上添加了所需内容的Docker环境。非常感谢 @ucan-lab 先生。
【超入门】使用Docker快速构建20分钟内的Laravel开发环境 Hands-on教程。
项目组成
.
├── infra
│ ├── echo-server
│ │ └──Dockerfile
│ ├── mysql
│ │ ├── Dockerfile
│ │ └── my.cnf
│ ├── nginx
│ │ └── default.conf
│ ├── php
│ │ ├── Dockerfile (この名前にするとファイル名の指定を省略できる)
│ │ └── php.ini
│ └── redis
│ └── data(ディレクトリです)
├── docker-compose.yml (この名前にするとファイル名の指定を省略できる)
└── backend
└── Laravelをインストールするディレクトリ
各个文件的内容
version: "3.9"
services:
app:
build: ./infra/php
volumes:
- ./backend:/work
web:
image: nginx:1.20-alpine
ports:
- 8080:80
volumes:
- ./backend:/work
- ./infra/nginx/default.conf:/etc/nginx/conf.d/default.conf
working_dir: /work
db:
build: ./infra/mysql
ports:
- 33060:3306
volumes:
- db-store:/var/lib/mysql
echo-server:
image: broadcast-echo-server
build: ./infra/echo-server
ports:
- "6001:6001"
command: laravel-echo-server start
volumes:
- ./backend:/work
working_dir: /work
redis:
image: redis:latest
ports:
- 6379:6379
volumes:
- "./infra/redis/data:/data"
volumes:
db-store:
FROM node:13.8-alpine
RUN npm install -g laravel-echo-server
WORKDIR /work
CMD ["laravel-echo-server", "start"]
FROM mysql/mysql-server:8.0
ENV MYSQL_DATABASE=laravel_local \
MYSQL_USER=phper \
MYSQL_PASSWORD=secret \
MYSQL_ROOT_PASSWORD=secret \
TZ=Asia/Tokyo
COPY ./my.cnf /etc/my.cnf
RUN chmod 644 /etc/my.cnf
[mysqld]
# default
skip-host-cache
skip-name-resolve
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
secure-file-priv=/var/lib/mysql-files
user=mysql
pid-file=/var/run/mysqld/mysqld.pid
# character set / collation
character_set_server = utf8mb4
collation_server = utf8mb4_ja_0900_as_cs_ks
# timezone
default-time-zone = SYSTEM
log_timestamps = SYSTEM
# Error Log
log-error = mysql-error.log
# Slow Query Log
slow_query_log = 1
slow_query_log_file = mysql-slow.log
long_query_time = 1.0
log_queries_not_using_indexes = 0
# General Log
general_log = 1
general_log_file = mysql-general.log
[mysql]
default-character-set = utf8mb4
[client]
default-character-set = utf8mb4
server {
listen 80;
server_name example.com;
root /work/public;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
index index.php;
charset utf-8;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
error_page 404 /index.php;
location ~ \.php$ {
fastcgi_pass app:9000;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
FROM php:8.0-fpm-buster
SHELL ["/bin/bash", "-oeux", "pipefail", "-c"]
ENV COMPOSER_ALLOW_SUPERUSER=1 \
COMPOSER_HOME=/composer
COPY --from=composer:2.0 /usr/bin/composer /usr/bin/composer
RUN apt-get update && \
apt-get -y install git unzip libzip-dev libicu-dev libonig-dev && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* && \
docker-php-ext-install intl pdo_mysql zip bcmath
COPY ./php.ini /usr/local/etc/php/php.ini
RUN curl -sL https://deb.nodesource.com/setup_14.x | bash -
RUN apt-get install -y nodejs
RUN npm install npm@latest -g
WORKDIR /work
zend.exception_ignore_args = off
expose_php = on
max_execution_time = 30
max_input_vars = 1000
upload_max_filesize = 64M
post_max_size = 128M
memory_limit = 256M
error_reporting = E_ALL
display_errors = on
display_startup_errors = on
log_errors = on
error_log = /dev/stderr
default_charset = UTF-8
[Date]
date.timezone = Asia/Tokyo
[mysqlnd]
mysqlnd.collect_memory_statistics = on
[Assertion]
zend.assertions = 1
[mbstring]
mbstring.language = Japanese
安装Laravel
在启动Docker容器之后,安装Laravel。从Laravel的安装到环境配置,可以参考下方链接的文章,采用相同的步骤进行操作。
请参考【超入门】Docker手动教程:在20分钟内快速构建Laravel开发环境。
Laravel-echo-server的配置
在初次启动容器时,由于Laravel-echo-server的初始设置尚未完成,会导致错误发生,因此需要进行初始设置。
$ docker compose run echo-server laravel-echo-server init
? Do you want to run this server in development mode? Yes
? Which port would you like to serve from? 6001
? Which database would you like to use to store presence channel members? redis
? Enter the host of your Laravel authentication server. https://localhost
? Will you be serving on http or https? http
? Do you want to generate a client ID/Key for HTTP API? No
? Do you want to setup cross domain access to the API? No
? What do you want this config to be saved as? laravel-echo-server.json
Configuration file saved. Run laravel-echo-server start to run server.
当执行上述操作后,会在Laravel 项目根目录下创建一个名为 laravel-echo-server.json 的文件,随后按照以下方式进行修改。
{
"authHost": "http://localhost",
"authEndpoint": "/broadcasting/auth",
"clients": [],
"database": "redis",
"databaseConfig": {
"redis": {
"host": "redis",
"port": 6379
},
"sqlite": {
"databasePath": "/database/laravel-echo-server.sqlite"
}
},
"devMode": true,
"host": null,
"port": "6001",
"protocol": "http",
"socketio": {},
"secureOptions": 67108864,
"sslCertPath": "",
"sslKeyPath": "",
"sslCertChainPath": "",
"sslPassphrase": "",
"subscribers": {
"http": true,
"redis": true
},
"apiOriginAllow": {
"allowCors": false,
"allowOrigin": "",
"allowMethods": "",
"allowHeaders": ""
}
}
添加所需的库
"require": {
// 他省略
"predis/predis": "^1.1"
}
"dependencies": {
// 他省略
"laravel-echo": "^1.11.1",
"laravel-echo-server": "^1.6.2",
"socket.io": "^2.4.0",
"socket.io-client": "^2.4.0",
}
所有的设置
配置提供商
取消app/config/app.php文件中以下部分的注释。
App\Providers\BroadcastServiceProvider::class,
Redis的配置
'redis' => [
'client' => env('REDIS_CLIENT', 'predis'),
'default' => [
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => 0,
],
'cache' => [
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => 1,
],
'options' => [
'prefix' => env('REDIS_PREFIX', ''),
],
],
更改.env文件
BROADCAST_DRIVER=redis
QUEUE_CONNECTION=redis
REDIS_HOST=redis
REDIS_CLIENT=predis
REDIS_PASSWORD=null
REDIS_PORT=6379
REDIS_PREFIX=""
在这一点上,我们已经能够通过WebSocket进行实时通信。
亲自试试看
使用Laravel的事件功能,可以将数据实时发送到客户端。
服务器端的准备工作
创建活动
php artisan make:event MessageRecieved
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class MessageReceived implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new Channel('test');
}
public function broadcastWith()
{
return [
'data' => 'test',
];
}
}
活动准备引发
当我们访问该路径时,将触发一个事件。
Route::get('/', function () {
event(new \App\Events\MessageReceived());
return view('welcome');
});
工作人员的启动
由于使用Redis进行处理,因此需要启动工作进程来执行任务。
php artisan queue:work
客户端准备工作
准备视图
以下是在路由中显示的视图方式:
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>Laravel</title>
<link rel="stylesheet" href="{{ mix('css/app.css') }}">
</head>
<body>
<div id="app">
test
</div>
<script src="{{ mix('js/app.js') }}"></script>
<script src="http://{{ Request::getHost() }}:6001/socket.io/socket.io.js"></script>
</body>
</html>
脚本准备完毕
在 bootstrap.js 中添加以下内容。
import Echo from "laravel-echo";
window.io = require("socket.io-client");
window.Echo = new Echo({
broadcaster: "socket.io",
host: window.location.hostname + ":6001",
});
window.Echo.channel("test").listen("MessageReceived", e => {
console.log("メッセージ受け取り成功!!!");
});
编译
npm install && npm run dev
进行
動かないとき
如果重新启动Docker可以解决问题,那么可以尝试这个方法。
如果问题仍然存在,则应检查Docker的日志信息。
docker compose logs
最后
这次为了方便起见我选择直接粘贴代码作为备忘录。顺便一提,如果使用Pusher这个服务,实现实时通信非常简单。这在Laravel的官方文档中也有说明,如果你感兴趣的话一定要查看一下。