HyanCat's Blog

懒拭宝剑锋,流光不加少

我是小明


Swoole 从入坑到放弃

一直想用 swoole,却一直没有真正地去了解它。

前些天看到群里有人讨论相关的技术问题,于是下定决心试一试。但是真正搞定它前后折腾了近一个星期。

起步

试用一下 swoole,很简单,官网的 swoole server 例子

本地环境 ab 测一下:

ab

简直惊叹!当然官方的压测数据更详细和准确: 压力测试

Laravel 结合

Swoole 虽强,但是还是要能和项目结合好才算能用。

调研了几个 laravel 的 swoole 库之后,选择了 huang-yi/laravel-swoole-http,他对 swoole 的封装实现比较好。当然他只实现了 http server,目前也是足够了。

如果项目比较简单,基于 Laravel 并且没有太多的修改,那么可能 1 个小时就能立竿见影了。但是项目复杂了就折腾起来没完了。

研究了下实现的源码,原理还比较简单,和 artisan serve 差不多。

  1. 管理 swoole start stop 等几个命令;
  2. 启动 Laravel Application;
  3. 监听 swoole http server 的 request 等几个事件;
  4. 将数据传给 Laravel 的 request,并将 Laravel 的 response 传出来。

Docker 结合

自从用了 docker,什么东西都想 docker 化。项目也一直运行在 docker 环境下好几年了。所以必须把 swoole 在 docker 里跑起来。

定制一个 swoole 的 docker 容器倒也简单,Dockerfile
把该装的装上。

docker-compose.yml 里多一条 command:

swoole:
build: ./swoole
# ...
command: php /docker/app/someproject/artisan swoole:http start

稍麻烦的是 Nginx 的配置,不再是 fastcgi 的方式,而是反向代理到 swoole 的 http server,简单配置示例如下:

# laravel swoole config example.
server {
listen 80;
server_name swoole.app;

access_log /var/log/nginx/host.access.log main;

root /docker/app/someproject/public;
index index.php;

location / {
try_files $uri $uri/ @swoole;
}

# 由于 http server 支持不完善,需要传递 header
proxy_set_header HOST $host;
proxy_set_header SERVER_PORT $server_port;
proxy_set_header REMOTE_ADDR $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;

# 这里注意:如果是无路径的 URL,需要加 /
location = /index.php {
proxy_pass http://swoole:1215/;
}

location @swoole {
proxy_pass http://swoole:1215;
}

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

  1. Docker 端口容易混乱

    这个坑算自己的失误,要管理的容器太多了,端口映射就乱了。项目里配置的端口,Dockerfile 里开放的端口,docker-compose 里映射的端口,以及 Nginx 里反向代理的端口,都要对应得上(不需要一样),很容易混乱,导致调了很久还是 502。

  2. Nginx 配置

    Nginx 的配置需要揣摩每一项的意思,并思考还缺少什么,分析请求到达 Nginx 之后是如何转给 swoole 的。很多次访问静态文件是可以,但访问 php 页面就 502 了。这里调了很久很久。

  3. require_once 大坑货

    Nginx 配置好了,不再 502 了,却一直是 404 了。开始找了很久都没找到原因,一直以为是缺少了什么,导致请求没有找到目标。但是后来一想,Laravel 的 Application 也启动了,那 404 肯定就是 Laravel 内部抛出来的。然后就一点点 log 调试(好吧,当时好像不知道为什么不能看错误栈),终于发现是 route 丢了。

    什么,丢了?

    对,就是本来所有的 route 都加载进去了,但是又丢了。就是在 swoole onRequest 的时候,会 reset 并重新加载一些 ServiceProvider (待考究)。

    PHP 对于引入代码文件一般用 require_once 避免重复 require,比如我的 RouteServiceProvider 中需要 require 路由文件:

    class RouteServiceProvider extends ServiceProvider
    {
    /**
    * `map()` called by parent's `loadRoutes()`.
    */
    public function map()
    {
    require_once base_path('route/web.php');
    require_once base_path('route/passport.php');
    require_once base_path('route/api_v3.php');
    }
    }

    就在这里,路由文件是 require_once 的,加载一次后就不再被加载了,swoole 启动后实际的路由被清空了,改成 require 就好了。。。

  4. 上线后才发现的一个大坑:共享单例

    之前知道会有这个问题,但一时忽略了,没有重视,上线后才发现并意识到,赶紧回滚。。

    Laravel 的容器里有很多单例,比如 Auth、Session、View 等。使用 phpfpm 等 fastcgi 方式的话,任何两个请求互不相干,单例的生命周期只在response 之后就结束。而使用 swoole 之后,应用程序是先加载到内存中,单例是一直活跃的,前后两个请求用到的单例对象就是同一个。会出现 session 串了的问题。解决办法是 onRequest 的时候 reset 这些单例,重新 new 一个出来。

    好吧,我试图改了很多地方,还是有问题。

    于是,暂时放弃。慢慢研究。🤪

最近的文章

Hybrid App 混合开发之自定义字体

前言自从开始了 Hybrid App,就一发不可收拾了。以后有空会记录一些 Hybrid 在多端开发中的问题。 问题由于某个地方的文字显示需要用到特殊字体,在 iOS 和 Android 中加载自定义的字体也都是很容易的,但在 webview 中自定义字体却没那么简单。 解决 各端按照系统支持的方案 …

于  Android, Hybrid, iOS 继续阅读
更早的文章

Effective Objective-C 2.0 知识点梳理

复习了《Effective Objective-C 2.0》这本书,每小节简单地做点笔记。总体来说知识点都比较基础,大部分在平时写代码时也都有涉及到,这里就当是总结吧,对新人可能会有一定的帮助。 …

于  继续阅读
comments powered by Disqus