SAE上使用自定义环境运行异步任务

      lazy 新浪云计算

      在实际的业务场景中,总有很多的任务需要实时的长时间的执行,例如:

  •       需要使用tcp/http长连接去外部获取数据;

  •       需要实时监控一些数据;

  •       任务队列的处理,发短信、发报警、发邮件等等。


      在新浪云的云应用环境,可能大家熟知的有三种处理方式:

  •       通过定时任务处理,因为定时任务可以执行30分钟,普通的http请求最长只能处理300秒。

  •       通过taskqueue任务队列处理,接口程序将一个大任务打散成小的任务,然后通过taskqueue将任务的参数通过http的GET参数通过taskqueue的异步回调到另外的接口处理,taskqueue天生有并发度,可以并行处理多个请求。

  •       通过新浪云提供的后台进程服务实现,后台进程帮助你启动一个PHP的进程完成响应的处理,大多的后台进程都需要这样的死循环实现:

while (1) {     // 任务的执行逻辑   sleep(1); }

      本文将为大家介绍一个更通俗、接地气的进程处理方式,使用新浪云提供的自定义运行环境实现可控的进程管理,以PHP为例进行说明。

      实现的流程图


      为了达成我们的目的,主要有这么几个问题需要解决:
  •       运行环境的创建

  •       数据怎么获取

  •       代码怎么更新

  •       进程怎么启动、重启、管理

  •       日志的处理

      为了说清楚这几个问题,我们绘制了一张示意图:

      下面我们将逐一阐述以上问题:

      运行环境的制作

      自定义的运行环境当前提供的都是一个空的操作系统,目前提供的分类有centos、ubuntu、debian、opensuse、alpine、fedora,每个操作系统的分类都支持几个主流的版本,你可以在创建应用的时候选择具体的分类。

      本文假定你已经注册好新浪云账号并完成实名认证,且账户有余额,如果以上的基本要求没达到,请从

      https://www.sinacloud.com 注册账户并完成以上步骤。

      进入云应用的创建应用页面,选择开发语言选择『自定义』,部署方式选择『手工部署』,选定操作系统和版本(你最熟悉的即可),本文以centos的7.5.1804为例,如图:

      创建完成后几秒种后,属于你的一个空的容器应用就完成了,进入容器管理页面:

      可以看到容器已经在运行中了,点击上图标示的终端就可以进入到terminal,就可以开始运行环境的制作了。

      安装PHP7的环境

      安装PHP7需要使用remi的仓库,请参考以下步骤(如果不是centos的环境请自行解决):

yum -C install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm http://rpms.remirepo.net/enterprise/remi-release-7.rpm   rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7 /etc/pki/rpm-gpg/RPM-GPG-KEY-remi /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7   yum -y install yum-utils   yum-config-manager --enable remi,remi-php70   yum install -y vim git svn php php-gd php-intl php-tidy php-pdo php-cli php-process php-xml php-mysql php-mbstring php-bcmath php-pecl-swoole php-pecl-redis php-pecl-imagick php-pecl-zip php-pecl-xdebug which --skip-broken   echo 'date.timezone=Asia/Shanghai' > /etc/php.d/00-docker-php-date-timezone.ini   curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin  

      为了简便,也可以快速执行:

curl 'http://cdn.sinacloud.net/opensource/php7/centos_init_php7.sh'|bash  

      安装是一个比较漫长的过程,因为安装的软件包比较多,需要耐心等待。以上步骤执行完成后,PHP7及常用扩展、SVN、Git、Vim等软件就都安装完成了,验证一下PHP的版本及安装的扩展,看到下面这个输出就安装完成了:

Complete!   All settings correct for using Composer   Downloading...   Composer (version 1.6.5) successfully installed to: /usr/bin/composer.phar   Use it: php /usr/bin/composer.phar  

      验证一下PHP的版本:

[root@demo1 /]# php -v PHP 7.0.31 (cli) (built: Jul 17 2018 15:30:29) ( NTS )   Copyright (c) 1997-2017 The PHP Group   Zend Engine v3.0.0, Copyright (c) 1998-2017 Zend Technologies      with Xdebug v2.6.0, Copyright (c) 2002-2018, by Derick Rethans

      到此,运行环境就已经安装完成了。

      如果你需要安装其他的扩展,你可以编译安装或者直接使用yum安装,如果使用yum安装,类似执行即可:

yum install -y php-pecl-swoole   # 提示,这是示例。这个swoole上面已经安装了,不用重复安装了

      数据怎么获取

      容器中可以使用同一个账户下的其他应用的数据(共享型数据库)及账户级服务的服务,例如redis、rds等。

      代码怎么更新

      以上在安装软件时,已经帮你装好了SVN和GIT客户端,你可以在容器上直接svn checkout或者git clone其他标准环境应用的仓库,以SVN为例:

mkdir -p /data1/code/   cd /data1/code   svn checkout "https://svn.sinacloud.com/gequ/2/" .   # 这里的https://svn.sinacloud.com/gequ/2/ 要换成你自己的代码仓库

      如上图的步骤,就下载了应用gequ的版本2代码到容器的本地了。

      注意:你提交带仓库的代码不会自动更新到容器上,每次修改后需要手工更新。

      进程启动、重启、管理

      写一个简单的进程示例代码process.php:

<?php   if (!function_exists('get')) {      function get($url, $timeout = 5, $headers = false)    {        $s = curl_init($url);        curl_setopt($s, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);        curl_setopt($s, CURLOPT_RETURNTRANSFER, true);        curl_setopt($s, CURLINFO_HEADER_OUT, true);        if ($headers) {            curl_setopt($s, CURLOPT_HTTPHEADER, $headers);        }        curl_setopt($s, CURLOPT_TIMEOUT, $timeout);        $ret = curl_exec($s);        $info = curl_getinfo($s);        curl_close($s);        if ($info['http_code'] > 0) {            return $ret;        }        return false;    } } while (1) {      $url = 'http://ip.cn';    $ip = get($url, 5, array('User-Agent: curl/7.29.0'));    $time_now = date('Y-m-d H:i:s', time());    echo(sprintf("Now: %s\n", $time_now));    echo(sprintf("%s\n", $ip));    sleep(10); }

      以上程序每10秒钟请求一个外部的地址获取本机的出口ip,然后输出,直接执行 php process.php

root@demo1 /data1/code/process]# php process.php Now: 2018-08-03 10:35:23   当前 IP:123.125.23.213 来自:北京市 联通 Now: 2018-08-03 10:35:34   当前 IP:123.125.23.213 来自:北京市 联通

      可以看到每十秒钟就输出了一段IP信息,还可以看到通过php process.php就可以启动一个PHP的进程,去完成需求,那么也不可能一直挂在这个页面上一直执行着,因此需要一个进程管理器去管理这个进程,supervisor横空出世。关于supervisor的介绍请参考 http://www.supervisord.org官网。

      安装supervisor

yum install supervisor -y  写supervisor的配置文件

      supervisor的默认配置文件是

      /etc/supervisord.conf,其中配置的加载配置设置为

[include] files = supervisord.d/*.ini

      从配置可以看出,只要是

      /etc/supervisord.d/ 目录下所有以ini结束的文件都会额外加载。

      将示例的进程写到配置文件

      /etc/supervisord.d/process.ini

[root@demo1 /data1/code/process]# cat /etc/supervisord.d/process.ini [program:process] command=php /data1/code/process/process.php   stdout_logfile=/tmp/process.log   autostart=true   autorestart=true   startsecs=5   priority=3   stopasgroup=true   killasgroup=true

      启动supervisord

[root@demo1 /data1/code/process]# supervisord /usr/lib/python2.7/site-packages/supervisor/options.py:296: UserWarning: Supervisord is running as root and it is searching for its configuration file in default locations (including its current working directo ry); you probably want to specify a "-c" argument specifying an absolute path to a configuration file for improved security.    'Supervisord is running as root and it is searching '

      看进程列表和状态

[root@demo1 /data1/code/process]# supervisorctl status process                         RUNNING   pid 426, uptime 0:00:08  

      可以看到进程已经启动。

      查看输出日志

      如supervisor的配置文件中设置,将日志定位到了 /tmp/process.log

[root@demo1 /data1/code/process]# tail -f /tmp/process.log 当前 IP:123.125.23.213 来自:北京市 联通 Now: 2018-08-03 10:45:35   当前 IP:123.125.23.213 来自:北京市 联通 Now: 2018-08-03 10:45:45   当前 IP:123.125.23.213 来自:北京市 联通 Now: 2018-08-03 10:46:00   Now: 2018-08-03 10:46:11   当前 IP:123.125.23.213 来自:北京市 联通

      可以看到,日志确实是输出了。

      注意:容器的本地文件系统大小是有限制的,你可以将日志的输出写到stdout,这样新浪云的容器管理进程会接管你输出的日志,这样从日志中心就可以查看。

      稍微修改一下supervisor的配置

      /etc/supervisord.d/process.ini

[root@demo1 /data1/code/process]# cat /etc/supervisord.d/process.ini [program:process] command=php /data1/code/process/process.php   stdout_logfile=/proc/1/fd/1   stderr_logfile=/proc/1/fd/2   stdout_logfile_maxbytes=0   stderr_logfile_maxbytes=0   autostart=true   autorestart=true   startsecs=5   priority=3   stopasgroup=true   killasgroup=true  

      重启supervisor

supervisorctl reload  

      注意:修改了配置supervisor配置文件需要使用reload重启supervisord进程,如果是修改了PHP代码,使用

      supervisorctl restart process即可,如果重启所有进程,使用supervisorctl restart all将日志写到 /proc/1/fd/1 这个路径即可。为什么需要配置stdoutlogfilemaxbytes,

      请参考 

      https://stackoverflow.com/questions/25195880/supervisord-redirect-process-stdout-to-console。

      现在从管理面板的日志中心就可以看到日志了,这样日志文件就不会占用容器的磁盘,如图:


      MySQL has gone away的问题

      在从写PHP的接口到写PHP的进程时基本所有人都会遇到这个错误:

MySQL error 2006: mysql server has gone away  

      这个是一个很常见的问题,而且不算是系统的bug,因为MySQL的服务器为了确保能及时回收无用的连接,设定了这个参数,即客户端连上MySQL后过了N秒没有任何活动,MySQL就主动断开连接,客户端再用这个连接去查询数据,就会报这个错误,在新浪云的共享型MySQL上,这个N是30秒,且不可修改。

      因此客户端需要自己处理这个错误,怎么处理呢?其实很简单,就是执行完sql后判断下错误码,如果是2006,就重新连接MySQL,然后再执行一下sql即可。

      以MySQL扩容为例,伪代码是这样的:

$link = mysql_connect(主机:端口,用户名,密码); mysql_select_db(数据库名, $link);   $ret = mysql_query("SQL语句", $link); if (!$ret && mysql_errno($link) == 2006) {      // 重新连接    $link = mysql_connect(主机:端口,用户名,密码);    mysql_select_db(数据库名, $link);    $ret = mysql_query("SQL语句", $link); }

      注意:以上是伪代码描述,不能直接使用,实际使用中请结合你的MySQL操作封装修改逻辑。