# 我用easyswoole v2做了一个http服务

在我看来,swoole的优点是性能高,可定制性强,应用场景广(支持http服务,socket服务,或者自己手撸个tcp服务)。但对于日常使用TP或laravel这类框架的开发者来说说,使用原生的swoole框架来做开发并不是很方便,于是产生了很多以swoole为核心的swoole框架,如easyswoole,Swoft ,SwooleDistributed 等,具体介绍可以看swoole的衍生开源项目。这次使用easyswoole来做一个http服务。

easyswoole目前有三个版本:v1, v2, v3。如果你不需要用到协程,建议用v1,否则建议用v3,这次我用v2。easyswoole的安装,路由配置就不多介绍了,请看官方文档:EasySwoole V2 中文手册

# env环境配置

在日常开发中,最少都会有两个环境:开发环境和生产环境,常见的方法是使用不同配置文件来区分这两个环境(如数据库的连接配置,Redis的连接配置等)。easyswoole v2默认没这个功能(v3有),为了实现这个功能,我使用vlucas/phpdotenv来做。

1.安装phpdotenv

composer require vlucas/phpdotenv

2.在根目录下添加Config目录,在该目录下添加database.php文件,并将数据库的配置信息移到该文件中,内容参考如下:

<?php

return [
    'database' => [
        'driver'    => 'mysql',
        'host'      => env('DB_HOST','127.0.0.1'),
        'database'  => env('DB_DATABASE','park_new'),
        'username'  => env('DB_USERNAME','root'),
        'password'  => env('DB_PASSWORD','123456'),
        'charset'   => 'utf8',
        'collation' => 'utf8_general_ci',
        'prefix'    => ''
    ],
    'redis' => [
        'master' => [
            'host' => env('REDIS_MASTER_HOST','127.0.0.1'),
            'port' => env('REDIS_MASTER_PORT',6379),
            'password' => env('REDIS_MASTER_PASSWORD',''),
        ],
        'slave' => [
            'host' => env('REDIS_SLAVE_HOST','127.0.0.1'),
            'port' => env('REDIS_SLAVE_PORT',6379),
            'password' => env('REDIS_SLAVE_PASSWORD',''),
        ],
    ],
];

当然不是一定要放在这里,只需保证在swoole启动时能加载到.env配置,并在后面mysql,redis等读取配置时使用到它即可。

3.在根目录增加.env配置

和laravel的.env配置一样,比如

REDIS_MASTER_HOST=172.16.88.101
REDIS_MASTER_PASSWORD=888999
REDIS_MASTER_PORT=6379

REDIS_SLAVE_HOST=172.16.88.102
REDIS_SLAVE_PASSWORD=888999
REDIS_SLAVE_PORT=6379

4.在EasySwooleEvent的frameInitialize中加载配置,内容如下

// 载入项目 Conf 文件夹中所有的配置文件
// 载入env配置
$dotenv = new \Dotenv\Dotenv('./');
$dotenv->load();

$files = File::scanDir('./Config');
if($files){
    foreach ($files as $file) {
        $fileNameArr= explode('.',$file);
        $fileSuffix = end($fileNameArr);
        // 目前只支持php结尾的配置文件
        if($fileSuffix !='php'){
            continue;
        }
        if (!is_file($file)) {
            continue;
        }
        $confData = require_once $file;
        if (is_array($confData) && !empty($confData)) {
            foreach ($confData as $key=>$datum){
                Config::getInstance()->setConf($key,$datum);
            }
        }
    }
}

5.tips

假如使用git开发,一般都会排除vendor目录,但在使用easyswoole的过程中,可能会需要去修改下源码。此时我们需要保留vendor中的easyswoole目录,排除其他目录,可在.gitignore中做如下配置:

vendor/*
!vendor/easyswoole/

# ORM封装

之前使用laravel比较多,习惯了illuminate/database。 1.安装illuminate/database

composer require illuminate/database

2.加载illuminate/database 在 \EasySwoole\EasySwooleEvent 的 框架初始化完成 事件中初始化数据库类配置

use Illuminate\Database\Capsule\Manager as Capsule;//如果你不喜欢这个名称,as DB;就好 
// 初始化完成
function static frameInitialize()
{
    // 初始化数据库
    $dbConf = Config::getInstance()->getConf('database');
    $capsule = new Capsule;
    // 创建链接
    $capsule->addConnection($dbConf);
    // 设置全局静态可访问
    $capsule->setAsGlobal(); 
    // 启动Eloquent
    $capsule->bootEloquent();
}

3.增加Model配置

在App目录下增加一个Models目录,用于model层,定义下表这些,方便使用。比如增加一个订单信息表OrderInfo.php,内容如下:

<?php

namespace App\Models;


use  Illuminate\Database\Eloquent\Model;

class OrderInfo extends Model
{
    protected $table = 'order_info';
}

4.愉快的使用

做好这些工作后,就可以方便的操作数据库了,如增加一个订单:

use App\Models\OrderInfo;

$order = new OrderInfo();
$order->order_sn = date("YmdHis", time()) . $car->user_id . rand(000, 999);
$order->user_id = $car->user_id;
$order->car_number = $data['carID'];
$order->save();

# redis封装

本次使用predis/predis

1.安装

composer require predis/predis

2.在App目录下创建Libs\Redis目录,在里面增加redis封装RedisTool.php,内容如下:

namespace App\Libs\Redis;

use EasySwoole\Config;

class RedisTool
{
    const REDIS_MAIN = 'master';
    const REDIS_SLAVE = 'slave';
    private static $ins = null;

    public static function getInstance($redisType=self::REDIS_MAIN, $extras=[])
    {
        // 获取配置
        $redisConf = Config::getInstance()->getConf('redis');
        $redisConf = array_merge($redisConf[$redisType], $extras);

        if(is_null(self::$ins)){
            self::$ins = new self($redisConf);
        }
        return self::$ins;
    }

    /*
     *  构造函数
     *
     */
    private function __construct($config)
    {
        $redis = new \Predis\Client($config);
        $this->ser = $redis;
    }

    /*
     *  结束时关闭连接
     */
    private function __destruct()
    {
        $this->ser->disconnect();
    }

    public function produce($key, array $data)
    {
        return $this->ser->lpush($key, $data);
    }

    public function lpush($key, $data)
    {
        if (is_string($data)) {
            $data = (array)$data;
        }
        return $this->ser->lpush($key, $data);
    }

3.使用redis

use App\Libs\Redis\RedisTool;

RedisTool::getInstance()->lpush('CAR_OUT',$carOut->order_sn);

# 增加基础工具

一般项目中都会有些小函数,小方法,这里我统一把它们放到App\Libs\Base目录下(默认没这个目录,需手动创建)。如BaseService.php:

class BaseService
{
    public static function withResult($state, $message = '', $data = null)
    {
        return [
            'state' => $state,
            'message' => $message,
            'data' => $data
        ];
    }

    public static function withJson(string $retuslt,Response $response)
    {
        $response->withHeader('Content-type','application/json; charset=utf-8');
        $response->write($retuslt);
        $response->end();
    }

    public static function withHtml(string $retuslt,Response $response)
    {
        $response->withHeader('Content-type','text/html; charset=utf-8');
        $response->write($retuslt);
        $response->end();
    }
}

这些准备完成后,就可以愉快的撸代码了。最终我的整个项目规划如下:

APP
  |---- HttpController:默认的http控制器,里面写路由等
  |---- Libs:基础方法的封装,自定义的SDK等
  |---- Models:model层
  |---- Services:各种服务的封装,业务逻辑的处理等
  |---- Tasks:easyswoole的异步任务(很好用,很方便)
  |---- Utility:easyswoole的封装,如redis连接池,mysql连接池