lumen为速度而生的 Laravel 框架官网的介绍很简洁,而且 lumen 确实也很简单,我在调研了 lumen 相关组件(比如缓存,队列,校验,路由,中间件和最重要的容器)之后认为已经能够满足我目前这个微服务的需求了。任务目标因为业务需求,需要在内网服务B中获取到公网服务A中的数据,但是B服务并不能直接对接公网,于是需要开发一个relay 中转机来完成数据转存和交互。任务列表环境准备 【done】RSA数据加密 【done】guzzle请求封装 【done】添加monolog日志RocketMQ java请求转发程序数据库migrateEvent和Listener的业务应用Scheduler计划任务(基于crontab)Jobs和Queue业务应用使用supervisor守护queue进程和java进程添加sentry来获取服务日志信息和实现邮件报警jwt用户身份校验.env 文件的配置可能的扩展 K8S docker性能并发测试环境准备机器是centos6.8, 使用work用户, 安装 php(^7),mysql,nginx,redisyum 安装的同学可以试试 https://www.softwarecollectio…安装composerhttps://getcomposer.org/downl…# 注意php的环境变量php -r “copy(‘https://getcomposer.org/installer', ‘composer-setup.php’);“php -r “if (hash_file(‘sha384’, ‘composer-setup.php’) === ‘93b54496392c062774670ac18b134c3b3a95e5a5e5c8f1a9f115f203b75bf9a129d5daa8ba6a13e2cc8a1da0806388a8’) { echo ‘Installer verified’; } else { echo ‘Installer corrupt’; unlink(‘composer-setup.php’); } echo PHP_EOL;“php composer-setup.phpphp -r “unlink(‘composer-setup.php’);“mv composer.phar /usr/local/bin/composer安装lumencomposer global require “laravel/lumen-installer"composer create-project –prefer-dist laravel/lumen YOURPROJECT配置 .env配置Lumen 框架所有的配置信息都是存在 .env 文件中。一旦 Lumen 成功安装,你同时也要 配置本地环境。应用程序密钥在你安装完 Lumen 后,首先需要做的事情是设置一个随机字符串到应用程序密钥。通常这个密钥会有 32 字符长。 这个密钥可以被设置在 .env 配置文件中。如果你还没将 .env.example 文件重命名为 .env,那么你现在应该去设置下。如果应用程序密钥没有被设置的话,你的用户 Session 和其它的加密数据都是不安全的!配置nginx 和 php-fpm配置nginx的serverserver { listen 8080; server_name localhost; index index.php index.html index.htm; root /home/work/YOURPROJECT/public; error_page 404 /404.html; location / { try_files $uri $uri/ /index.php?$query_string; } location ~ .php$ { root /home/work/YOURPROJECT/public; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; #include fastcgi.conf; }}php-fpm的监听端口推荐一篇文章:Nginx+Php-fpm运行原理详解lumen 基础介绍lumen的入口文件是 public/index.php,在nginx配置文件中已有体现初始化核心容器是 bootstrap/app.php 它做了几件非常重要的事情加载了 composer的 autoload 自动加载创建容器并可以选择开启 Facades 和 Eloquent (建议都开启,非常方便)Register Container Bindings:注册容器绑定 ExceptionHandler(后面monolog和sentry日志收集用到了) 和 ConsoleKernel(执行计划任务)Register Middleware:注册中间件,例如auth验证: $app->routeMiddleware([‘auth’ => AppHttpMiddlewareAuthenticate::class,]);注册Service Providers$app->register(App\Providers\AppServiceProvider::class);$app->register(App\Providers\AuthServiceProvider::class);$app->register(App\Providers\EventServiceProvider::class);在AppServiceProvider 里还能一起注册多个provider// JWT$this->app->register(\Tymon\JWTAuth\Providers\LumenServiceProvider::class);// redis$this->app->register(\Illuminate\Redis\RedisServiceProvider::class);// 方便IDE追踪代码的Helper,因为laravel使用了大量的魔术方法和call方法以至于,对IDE的支持并不友好,强烈推荐开发环境安装$this->app->register(\Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class);// sentry$this->app->register(\Sentry\SentryLaravel\SentryLumenServiceProvider::class);加载route文件 routes/web.php//localhost:8080/test 调用app/Http/Controllers/Controller.php的 test方法$router->get("/test”, [‘uses’ => “Controller@test”]);// 使用中间件进行用户校验$router->group([‘middleware’ => ‘auth:api’], function () use ($router) { $router->get(’/auth/show’, ‘AuthController@getUser’);});还可以添加其他初始化控制的handler,比如说这个 monolog日志等级和格式,以及集成sentry的config$app->configureMonologUsing(function(Monolog\Logger $monoLog) use ($app){ // 设置processor的extra日志信息等级为WARNING以上,并且不展示Facade类的相关信息 $monoLog->pushProcessor(new \Monolog\Processor\IntrospectionProcessor(Monolog\Logger::WARNING, [‘Facade’])); // monolog 日志发送到sentry $client = new Raven_Client(env(‘SENTRY_LARAVEL_DSN’)); $handler = new Monolog\Handler\RavenHandler($client); $handler->setFormatter(new Monolog\Formatter\LineFormatter(null, null, true, true)); $monoLog->pushHandler($handler); // 设置monolog 的日志处理handler return $monoLog->pushHandler( (new Monolog\Handler\RotatingFileHandler( env(‘APP_LOG_PATH’) ?: storage_path(’logs/lumen.log’), 90, env(‘APP_LOG_LEVEL’) ?: Monolog\Logger::DEBUG) )->setFormatter(new \Monolog\Formatter\LineFormatter(null, null, true, true)) );});配置文件 config/ 和 .env 文件其他目录文件用到时再具体说明RSA数据加密因为业务中包含部分敏感数据,所以,数据在传输过程中需要加密传输。选用了RSA非对称加密。借鉴了 PHP 使用非对称加密算法(RSA)但由于传输数据量较大,加密时会报错,所以采用了分段加密连接和分段解密php使用openssl进行Rsa长数据加密(117)解密(128)如果选择密钥是1024bit长的(openssl genrsa -out rsa_private_key.pem 1024),那么支持加密的明文长度字节最多只能是1024/8=128byte;如果加密的padding填充方式选择的是OPENSSL_PKCS1_PADDING(这个要占用11个字节),那么明文长度最多只能就是128-11=117字节。如果超出,那么这些openssl加解密函数会返回false。分享一个我的完成版的工具类openssl genrsa -out rsa_private_key.pem 1024//生成原始 RSA私钥文件openssl pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt -out private_key.pem//将原始 RSA私钥转换为 pkcs8格式openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem<?phpnamespace App\Lib\Oscar;class Rsa{ private static $PRIVATE_KEY = ‘—–BEGIN RSA PRIVATE KEY—–xxxxxxxxxxxxx完整复制过来xxxxxxxxxxxxxxxxxxx—–END RSA PRIVATE KEY—–’; private static $PUBLIC_KEY = ‘—–BEGIN PUBLIC KEY—–xxxxxxxxxxxxx完整复制过来xxxxxxxxxxxxxxxxxxx—–END PUBLIC KEY—–’; /** * 获取私钥 * @return bool|resource / private static function getPrivateKey() { $privateKey = self::$PRIVATE_KEY; return openssl_pkey_get_private($privateKey); } /* * 获取公钥 * @return bool|resource / private static function getPublicKey() { $publicKey = self::$PUBLIC_KEY; return openssl_pkey_get_public($publicKey); } /* * 私钥加密 * @param string $data * @return null|string / public static function privateEncrypt($data = ‘’) { if (!is_string($data)) { return null; } $EncryptStr = ‘’; foreach (str_split($data, 117) as $chunk) { openssl_private_encrypt($chunk, $encryptData, self::getPrivateKey()); $EncryptStr .= $encryptData; } return base64_encode($EncryptStr); } /* * 公钥加密 * @param string $data * @return null|string / public static function publicEncrypt($data = ‘’) { if (!is_string($data)) { return null; } return openssl_public_encrypt($data,$encrypted,self::getPublicKey()) ? base64_encode($encrypted) : null; } /* * 私钥解密 * @param string $encrypted * @return null / public static function privateDecrypt($encrypted = ‘’) { $DecryptStr = ‘’; foreach (str_split(base64_decode($encrypted), 128) as $chunk) { openssl_private_decrypt($chunk, $decryptData, self::getPrivateKey()); $DecryptStr .= $decryptData; } return $DecryptStr; } /* * 公钥解密 * @param string $encrypted * @return null */ public static function publicDecrypt($encrypted = ‘’) { if (!is_string($encrypted)) { return null; } return (openssl_public_decrypt(base64_decode($encrypted), $decrypted, self::getPublicKey())) ? $decrypted : null; }}使用tip// 私钥加密则公钥解密,反之亦然$data = \GuzzleHttp\json_encode($data);$EncryptData = Rsa::privateEncrypt($data);$data = Rsa::publicDecrypt($EncryptData);guzzle使用安装超简单 composer require guzzlehttp/guzzle:~6.0guzzle 支持PSR-7 http://docs.guzzlephp.org/en/…官网的示例也很简单,发个post自定义参数的例子use GuzzleHttp\Client;$client = new Client();// 发送 post 请求$response = $client->request( ‘POST’, $this->queryUrl, [ ‘form_params’ => [ ‘req’ => $EncryptData ]]);$callback = $response->getBody()->getContents();$callback = json_decode($callback, true);guzzle支持 异步请求// Send an asynchronous request.$request = new \GuzzleHttp\Psr7\Request(‘GET’, ‘http://httpbin.org’);$promise = $client->sendAsync($request)->then(function ($response) { echo ‘I completed! ’ . $response->getBody();});$promise->wait();值的注意的是github上有一个很好玩的项目 https://github.com/kitetail/zttp它在guzzle的基础上做了封装,采用链式调用$response = Zttp::withHeaders([‘Fancy’ => ‘Pants’])->post($url, [ ‘foo’ => ‘bar’, ‘baz’ => ‘qux’,]);$response->json();// => [// ‘whatever’ => ‘was returned’,// ];$response->status();// int$response->isOk();// true / false#如果是guzzle 则需要更多的代码$client = new Client();$response = $client->request(‘POST’, $url, [ ‘headers’ => [ ‘Fancy’ => ‘Pants’, ], ‘form_params’ => [ ‘foo’ => ‘bar’, ‘baz’ => ‘qux’, ]]);json_decode($response->getBody());高可用问题思考数据传输量过大可能导致的问题RSA加密失败请求超时数据库存储并发列队失败重试和堵塞数据操作日志监控和到达率监控未完待续…..
...