Swoole Websocket behind Apache mod_ssl proxy

标题里除了那个副词 behind 以外,其他都是技术术语。 充满了技术的高(Low)大(B)上的感觉。

Swoole 是 PHP 语言的一个模块, 就是说 PHP 里要加载这个 模块 (php -m|grep swoole 可以查看是否加载),是做异步,做微服务的性能最好的工具。

Websocket 是什么? wss 就是 Web socket secure,就是前后端需要长连接实时聊天,来回 ping/pong 的协议工具。

Apache 是什么?在这里是 httpd 服务器,就是 Web 服务器。

mod_ssl 就是 Apache 的 https 实现,就是 HTTP 加密需要用的 Apache 模块。

Proxy 的意思,就是代理, 有正向和反向, 在 Web 服务器上, 我们一般都是反向代理, 就是把前端给过来的流量往后端丢。 在这里, 就是 微信小程序客户端请求 wss://wss.yj777.cn/ 连接到 wss.yj777.cn 的 443 端口,我们把数据往 PHP 语言写的基于 Swoole 模块的 WebSocket 的某个端口,例如 9901 丢。
PHP 的片段代码是这样的:

$request 就是取的小程序前端丢过来的 WSS 连接请求数据。从微信小程序端丢过来的请求头是长这样子的:

我们用 curl 写一段测试脚本:

当我们命令行运行这段脚本的时候,会提示如下:

然后就等待聊天的输入,我们按 Ctr-C 退出就好。

为了让 Websocket 服务长期驻留,我们要设置成服务的形式,写一个
/etc/systemd/system/yj777_wss.service
然后 systemctl daemon-reload,然后 systemctl –now enable 就可以启用并启动服务。

当然最核心的,总是留到写到最后的。 那就是 Apache 的代理配置。这是所有出错的关键,遇到的问题,几乎都是在 Apache 的配置里。
首先当然是确认做 ws 反向代理的模块 wstunnel.so 已经加载: httpd -M|grep tunnel:
proxy_wstunnel_module
CentOS 7 安装了 Apache 后,默认是加载 /etc/httpd/conf.modules.d 下的所有 .conf 文件的,这个模块包含在 00-proxy.conf 文件里。

核心的就是 下面的两行:请注意里面的 URL 协议是 wss 不是 https ,关于这个梗,有人写 https 的, 但是需要 rewrite_mod 写 http_upgrade, 就是类似以上 curl 里的头,实际上就是多余的。

  ProxyPass / wss://127.0.0.1:9901/
  ProxyPassReverse / wss://127.0.0.1:9901/

当然其他的几个也非常重要。例如 ProxyPreserveHost

我们每天总是在填坑和挖坑间不断进步!

最后,来一个 Nginx 上配置代理 Websocket 的花絮:
我们直接把 请求转到另外一台服务器公网上暴露的 9981 端口了。(这是不好的)
所以,我们一般需要配一个子域名,做单独的 WS 服务,然后开启证书,然后 wss 就是指向 443 端口, 让 Nginx/Apache 代理往后面的 Swoole 丢。
记住:微服务,每个服务是一个独立的二级域名!

以上 Nginx 配置里, proxy_pass 后面应该是 http://localhost:port/ 即可,用 https 会要求后台的服务器地址开启证书,没有必要。

Ubuntu 1904 上的 Apache + PHP-FPM

在玩 Nginx 之余, 小伙伴们也要玩下 Apache ,索性就在最新版本的 Ubutu 1904 上玩耍一下。

# apt install apache2 php7.2 php7.2-fpm php7.2-mysql libapache2-mod-php7.2

安装中有几句话是这么说的:
NOTICE: Not enabling PHP 7.2 FPM by default.
NOTICE: To enable PHP 7.2 FPM in Apache2 do:
NOTICE: a2enmod proxy_fcgi setenvif
NOTICE: a2enconf php7.2-fpm

看这两个 a2 开头的脚本, ls -al 查看,原来就是软连接指向一个真正的 Perl 脚本 a2enmod 。

a2query 顾名思义是查询的,例如查询 php 模块是否启用:
# a2query -m php7.2

可以看到 Ubuntu 里面的 Apache 配置和 Web 虚拟机是通过 conf-available 和 sites-available 里的文件软连接到 conf-enabled 以及 sites-enabled 来启动的。

Ubuntu 上的 apache2 命令 AH00111 错误

这个错误其实比较简单的。 Ubuntu 上的 Apache Web 服务,命令为 apache2。而 CentOS/RedHat 上为 httpd 。 这个是比较大的区别。

解决错误的办法是运行命令: # source /etc/apache2/envvars

再运行 apache2 -V 就可以。
也就是说, Apache 的所有环境变量实际上是存放在这个 envvars 文件里。

Apache 的控制, 用 apachectl , 当然也可以用 apache2 -k 命令

Nginx 的自带免费监控工具Amplify

Nginx 官网介绍了自带的监控工具 Amplify:
https://www.nginx.com/blog/monitoring-nginx/

安装起来很简单方便。

首先检查 stub_status 模块是否已经编译进 Nginx:
$ nginx -V 2>&1 | grep --color -- --with-http_stub_status_module
再启用 stub_status 模块在 server 块里面添加:

   location /nginx_status {
        stub_status;
    }

重启 Nginx 后,命令行下用 curl 可以检查是否已经生效:

$ curl http://127.0.0.1/nginx_status
Active connections: 1
server accepts handled requests
813 813 1675
Reading: 0 Writing: 1 Waiting: 0

下载和安装 Amplify 很简单:
curl -sS -L -O \ https://github.com/nginxinc/nginx-amplify-agent/raw/master/packages/install.sh && \ API_KEY=’YOUR_API_KEY’ sh ./install.sh

systemctl status amplify-agent 看下是否正常启动

和 MySQL 集成的话, 只要在数据库上创建一个用户
CREATE USER ‘amplify-agent’@’localhost’ IDENTIFIED BY ‘YOUR_PASSWORD_HERE’
然后修改配置: /etc/amplify-agent/agent.conf:
设置 mysql = true , 把账号的用户名和密码设置好。

和 PHP-FPM 的集成:

使用限制:

隐藏 PHP 以及隐藏 Nginx 的版本号

隐藏 PHP 是修改 php.ini 的 expose_php
$ grep ^expose $(php –ini|awk ‘/^Loaded/ {print $NF}’)
expose_php = Off

隐藏 Nginx 版本号是修改两个地方:
1. /etc/nginx/nginx.conf 里面的 http 节里,添加 server_tokens off;
2. /etc/nginx/fastcgi.conf 里面的
fastcgi_param SERVER_SOFTWARE nginx;

当然隐藏 Apache 版本号是

ServerTokens  Prod
ServerSignature  off


hostname 带下划线引起的 400 错误

根据 RFC952 hostname 只允许字母,数字,短划线和小数点,并且以字母开头。
当我配置了一个 abc_demo 这样的本地主机名, 并配置在 Apache 的 VirtualHost 上时,尝试访问这个主机名下的资源都返回 400 错误。

那问题是怎么发现的呢? 我们在 Apache 的虚拟主机上配置不同的日志文件, 当尝试用“坏”的主机名去访问的时候,发现访问日志其实是被 log 在默认的日志文件上了,这就怀疑主机名有问题。当我们去掉下划线,或者用短横线代替后,问题迎刃而解。

Mask 不能启动的服务

在一台很久不用Web 的 Ubuntu 上设置 Apache, 看到题示的错误,只要运行
systemctl unmask apache 即可,实际上主要原因是因为系统安装的 Web 是 Nginx 不是 Apache。

mask 的概念:
We saw in the service management section how to stop or disable a service, but systemd also has the ability to mark a unit as completely unstartable, automatically or manually, by linking it to /dev/null. This is called masking the unit, and is possible with the mask command:
翻译过来就是 mask 的服务, 不允许自动或者手工启动。

systemctl list-units –all
systemctl list-unit-files
systemctl list-units –all –state=inactive

systemctl cat atd.service
systemctl list-dependencies sshd.service
systemctl show sshd.service
systemctl show sshd.service -p Conflicts

编辑服务单元文件:
systemctl edit nginx.service
systemctl edit –full nginx.service
rm -r /etc/systemd/system/nginx.service.d
rm /etc/systemd/system/nginx.service
查看有问题的服务:
systemctl –failed
reset 有错的服务: systemctl reset–failed
每次编辑或者删除服务文件需要做 daemon-reload:
systemctl daemon-reload

修改运行级:
systemctl get-default
systemctl set-default graphical.target
systemctl list-unit-files –type=target
systemctl list-units –type=target
隔离目标:
systemctl list-dependencies multi-user.target
systemctl isolate multi-user.target

设置重要事件:
放到 rescue/halt/poweroff 模式:
systemctl rescue
systemctl halt
systemctl poweroff
systemctl reboot

阿里云 CentOS7 初始镜像上安装 LAMP

  1. 更新主机名: # hostnamectl set-hostname newname
  2. 首先更新所有现有系统包:yum update -y ; reboot
  3. 安装 Apache # yum install httpd mod_ssl ; systemctl enable httpd; systemctl start httpd
  4. 安装 MariaDB # yum install mariadb-server; systemctl enable mariadb; systemctl start mariadb; mysql_secure_installation
  5. 安装 PHP7.2
    1. # yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
    2. # yum install http://rpms.remirepo.net/enterprise/remi-release-7.rpm
    3. # yum install yum-utils
    4. # yum-config-manager –enable remi-php72
    5. # yum install php72-php php72-php-mysqlnd php72-php-xml php72-php-mbstring php72-php-gd
    6. # ln -s /opt/remi/php72/root/bin/php /usr/bin

安装其他需要的工具:

  1. host 命令: # yum install bind-utils
  2. 安装 Certbot: # yum install augeas augeas-devel libffi-devel python-devel; pip install –upgrade pip; pip install certbot certbot-apache
  3. 安装 git: # yum install git

lighttpd 的 Alias 和 cgi-bin 配置

把原来用 PHP写的后台代码, 用 Go 语言的 fast cgi 来替代是很有趣的一件事情。

仿照 php-fpm 和 lighttpd 的配置,我们的 lighttpd 包含的 fafstcgi.conf 配置如下:

fastcgi.server += (

“.php” => ((
“socket” => “/var/run/php-fpm.sock”,
“broken-scriptfilename” => “enable”
)),

“/cgi-bin/” => ((
“socket” => “/var/run/yongjie.sock”,
“broken-scriptfilename” => “enable”
))

)
而 alias 的设置,我们只要把 modules.conf 里的 mod_alias 打开, 并写上:

alias.url = ( “/cgi-bin/” => “/fast-cgi-binary-path/” )