在这个系列里,我们自己动手搭建一个可以向APN发送请求的Provider。为什么要这样做呢?一来,这算是构建App的整个环节中,应该了解的一个技术细节;二来,尽管有不少线上推送的服务,但它们的API各有区别,无论使用哪家,都没有充分的代表性。

所以,还是自己撸一个吧。

服务器环境

首先,从要搭建的环境说起。我们这个最简单的Provider包含三个部分,分别是Nginx, MySQL和PHP。其中:

  • Nginx用于接收App上传的Token以及设备信息;
  • MySQL用于存储设备数据;
  • PHP用于为每一个iOS设备生成APN需要的推送请求数据,并给APN发送请求;

把它们与iOS设备和APN之间的关系以及各自的功能用一张图表示,就是这样的:

为什么没有用Vapor呢

看到这你可能会想,等等,为什么服务端程序选择了PHP而没有用Vapor呢?答案很简单,因为向APN发送通知请求的时候,我们要使用HTTP /2的长连接特性,以保证每台设备的推送数据都通过同一个TCP连接发送。否则,每发送一台设备的推送请求,就要新建一个TCP连接,Apple会认为这是某种形式的DoS攻击,进而阻止我们继续向APN发送消息。

不过遗憾的是,目前Foundation并没有对HTTP /2的长连接支持,而Vapor在底层使用的Swift NIO http2也还处在beta阶段。为了上线的时候确保这个服务可用,我们暂时没有使用Vapor,而是选择了更为成熟的Laravel来实现这部分的功能。

使用Docker搭建环境

虽然在技术上没有选择Vapor,不过我们的环境构建过程,和之前构建Vapor环境是非常类似的。因此,我们索性就把构建这种环境的过程一般化一些,让它变成一个可以部署Vapor/PHP这两种环境的脚本。大家可以在GitHub上获取完整的构建脚本。

目录结构

接下来,我们就从目录结构开始。把它用一张图表示,就是这样的:

其中:

  • 根目录的docker-compose.yml是部署MySQL和Nginx容器的脚本,而docker-compose.php.ymldocker-compose.vapor.yml则分别是部署PHP和Vapor容器的脚本,稍后,我们会介绍如何把这些脚本拼接起来构建不同的环境;
  • dbs用于存放MySQL数据库的文件,稍后我们会把这个目录作为一个Volume映射到MySQL容器,避免不慎删掉了容器,数据库就丢了;
  • mysql / nginx / php / echo-server / redis / vapor子目录中,包含的则是构建各自容器的脚本。可以看到,除了Dockerfile之外,这里还包含了一些用到的辅助脚本。例如:
    • nginx目录中的php-default / vapor-default分别是在PHP和Vapor环境中的站点设置文件;
    • php目录中的install_composer.sh用于在PHP容器中安装composer工具,xdebug.ini用于支持在Host上通过PhpStorm进行调试;
    • echo-server包含了服务器端推送的配置文件;
  • projects子目录中是我们在这个环境中要运行的各种项目;
  • 最后,在根目录中还有一个隐藏文件:.env.demo文件,它是这个docker环境的配置文件模板,我们可以基于它复制出多份配置,例如我就创建了apn.envbx.env。然后,只要在根目录中创建一个.env的符号链接,让它指向不同的配置,就可以方便地切换项目环境了;

现在,大家只要对这些东西有一个印象就行,稍后,我们会在用到的时候,逐一详细地进行解释。

使用.env定义环境

了解了环境的整体结构之后,我们从配置文件模板说起。对于即将构建的APN provider来说,它的配置文件是这样的:

HOST_ROOT=./projects/push-notifications
CONTAINER_ROOT=/var/www/push-notifications/current

HOST_STORAGE_ROOT=./projects/push-notifications/storage
CONTAINER_STORAGE_ROOT=/var/www/push-notifications/current/storage

HOST_DB_ROOT=./dbs/apn
MYSQL_ROOT_PASSWORD=apns
DB_NAME=apn
DB_USER=homestead
DB_PASSWORD=secret

HOST_HTTP_PORT=80
HOST_DB_PORT=33060
HOST_REDIS_PORT=6379
HOST_ECHO_PORT=6001

XDEBUG_CONFIG="remote_host=docker.for.mac.host.internal remote_port=9001"
PHP_IDE_CONFIG="serverName=APN-demo"

CURRENT_PHP_IMG=boxue/php:0.1.0
CURRENT_DB_IMG=boxue/mysql:0.1.1
CURRENT_NGINX_IMG=boxue/nginx:0.1.6
CURRENT_VAPOR_IMG=boxue/vapor:0.1.1
CURRENT_REDIS_IMG=boxue/redis:0.1.0
CURRENT_ECHO_IMG=boxue/echo:0.1.0

在这个配置文件里,有两类值。一类是和项目相关的,例如:

  • HOST_ROOT表示host上项目文件所在的目录;
  • CONTAINER_ROOT表示项目目录映射到容器中的目录;
  • MYSQL_ROOT_PASSWORD表示MySQL容器数据库的root密码;
  • DB_NAME / DB_USER / DB_PASSWORD表示项目使用的数据库名称以及访问账号;

另一类,是容器使用的镜像版本信息。当我们修改了容器的构建方式重新build的时候,可以在这里,基于修改的幅度,调整这些用CURRENT_开头的版本号。

至于其中我们还没有提到的配置,则和具体的容器应用相关,我们等用到的时候再专门解释。

容器的配置细节

了解了.env的内容之后,我们来看容器自身的配置。这里的绝大多数内容,在基于docker的Vapor开发环境这个系列中已经说过了,因此就不再重复了。如果你还不太熟悉Docker,那么应该先去看一下对应的内容。这里,着重说一些我们会用到的配置细节。

Nginx

首先,是Nginx容器的部分,在nginx/php-default中可以找到下面的配置:

server_name apn.boxue.io;

location / {
  try_files $uri $uri/ /index.php$is_args$query_string;
}

这里,我们的Provider域名是apn.boxue.io,把它设置成server_name。另外,try_files中,访问/index.php的部分一定要带上$is_args$query_string这两个参数。否则,Nginx就无法处理URL中形如?key=value这样的参数了。

接下来,为了让Nginx把请求转发到PHP容器,我们还要在php-default配置文件中添加下面的内容。其中最关键的,就是fastcgi_pass php:9000;,这里的php是我们在docker-compose.php.yml中,为PHP容器定义的名字:

location ~ \.php$ {
  fastcgi_pass php:9000;
  fastcgi_index index.php;
  fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
  include fastcgi_params;
}

PHP

其次,是PHP容器的部分,在php/Dockerfile里,主要是对两个PHP默认配置文件的修改。一个是/etc/php/7.2/fpm/pool.d/www.conf。这里,把PHP-FPM的监听方式从文件改成网络:

RUN sed -i '/listen = \/run\/php\/php7\.2-fpm\.sock/d' \
  /etc/php/7.2/fpm/pool.d/www.conf
RUN echo 'listen = php:9000' >> /etc/php/7.2/fpm/pool.d/www.conf

可以看到,listen的地址,和刚在在Nginx中配置的是一样的。

另外一处要修改的配置文件是/etc/php/7.2/fpm/php.ini,我们设置了时区,并把PHP-FPM的错误日志重定向到控制台:

RUN sed -i '/^;date\.timezone =/d' /etc/php/7.2/fpm/php.ini
RUN sed -i '/^;display_errors = stderr/d' /etc/php/7.2/fpm/php.ini

RUN echo 'date.timezone = Asia/Shanghai' >> /etc/php/7.2/fpm/php.ini
RUN echo 'display_errors = stderr' >> /etc/php/7.2/fpm/php.ini

这样,当前我们会使用的容器配置部分,就说完了。

在Host上安装Laravel

最后一个准备工作,是在Host上创建Laravel应用。如果之前你还没装过Laravel安装工具,可以在这里找到具体的安装方法,我们就不重复了。

接下来,在projects子目录,执行laravel new push-notifications创建项目模板。完成后,进入push-notifications目录,执行:

composer install -vvv
npm install

安装依赖关系。这样,所有的准备工作就结束了。

启动容器环境

接下来,我们启动这个容器环境试一下。一开始的时候我们说过,在环境的根目录中,我们把docker composer的配置文件拆成了docker-compose.ymldocker-compose.php.yml,如何把这两个文件中的配置当成同一个环境启动呢?

其实很简单,在执行docker-compose命令的时候,通过-f参数依次指定它们就好了。例如,构建镜像的时候,可以这样:

docker-compose -f docker-compose.yml -f docker-compose.php.yml build

构建完成后,执行下面的命令启动这个环境:

docker-compose -f docker-compose.yml -f docker-compose.php.yml up

简单来说,就是一旦使用了多个-f配置,在使用docker-compose命令的时候,就要始终带着这些配置文件。否则,得到的结果,就有可能丢失信息了。如果一切正常,在容器全部启动完后,先编辑下Host上的/etc/hosts文件,添加一条下面的记录:

127.0.0.1 apn.boxue.io

然后,在浏览器中访问apn.boxue.io,如果可以看到下面的结果,一切就都准备就绪了:

What's next?

搞定了这个基础的环境之后,为了方便接下来的开发,下段视频里,我们介绍一个在PhpStorm和VScode中接入XDebug的方法。这样,就可以方便地单步调试代码,而不用反复的var_dump输出结果了。

所有订阅均支持 12 期免息分期

¥ 59

按月订阅

一个月,观看并下载所有视频内容。初来泊学,这可能是个最好的开始。

开始订阅

¥ 512

按年订阅

一年的时间,让我们一起疯狂地狩猎知识吧。比按月订阅优惠 28%

开始订阅

¥ 1280

泊学终身会员

永久观看和下载所有泊学网站视频,并赠送 100 元商店优惠券。

我要加入
如需帮助,欢迎通过以下方式联系我们