微服务化四:编写服务

选择了后端服务框架swoft后,接下来需要写一些服务实际感受下如何开发一个自己的服务

服务划分

首先要根据自己系统业务进行服务划分,我暂时规划3个服务,用户,产品,商家。

开发环境目录规划

开发目录:/data/swoft/
后端services: /data/swoft/user, /data/swoft/shop,/data/swoft/goods
前端apps:/data/apps/pc

清理文件

因为swoft create创建的项目很多demo文件,需要自己先删除。既然后端服务,暂时不考虑http端口,而且是从零开始,app下的所有文件都可以先删除掉,config下的 bean/base.php文件里面可以把那些什么db那些全删除,因为如果用了.env,这些都会被覆盖

phpunit

因为是开发后端服务,写的都是service,所以需要保证代码可用,测试必不可少。
执行

composer test

会执行test目录下的所有测试,配置在根目录下的phpunit.xml
当然还有不全部测试:

//测试某个文件
./vendor/bin/phpunit ./test/Cases/IndexControllerTest
//测试某个文件某个方法
./vendor/bin/phpunit --filter 'testNew' ./test/Cases/IndexControllerTest

当然phpunit里面的方法很多,有需要的可以去看文档phpunit中文文档

文件生成

现在可以开始进行撸码了,不过先等等,一个个建文件容易出错不说,显得也不够高逼格,ok,先看看swoft的文件生成命令:命令文档

首先创建个测试表 user:

create table user (
id int primary key auto_increment COMMENT "ID",
account varchar(20)  COMMENT "帐号",
password varchar(20) COMMENT "密码",
username varchar(20) COMMENT "昵称",
reg_time datetime not null COMMENT "注册时间"
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT "用户表";

记得修改.env下的mysql连接和账户密码
然后运行命令创建模型下的实例和DAO

php bin/swoft entity:create -d swoft_user

然后。。。就没有了。。swoft没有生成接口和service的命令,需要手动创建。
首先说明下,通过命令创建的项目,model下有Entity,Dao,Data,Logic 我个人觉得比较多了,我倾向于简单,所以一般只会命令创建Entiry,然后把业务代码写Logic,给servies或者controller调用。

swoft创建接口,是先要定义接口的,所以到app/Lib下定义个UserInterface。
用户接口一般会有些什么功能呢? 注册、登录、用户信息查询(权限暂不涉及)
那么先定义3个接口 register,login,info
然后在model下logic定义个UserLogic处理user逻辑,调用uesrEntiry保存到数据库。

因为后端服务只有rpc服务,所以启动

php user/bin/swoft rpc:start -d

客户端(pc目录下)

修改 .env

//这行很重要,是到consul自动发现服务,如果手动发现需要填写USER_POOL_URI=,这里自动发现为空就行
USER_POOL_USE_PROVIDER=true

CONSUL_ADDRESS=http://172.17.0.2
CONSUL_PORT=8500

然后把服务端的UserInterface拷贝过来即可用了。(需要说明下,后端服务开发完成后,需要把接口放到公共接口库,客户端就可以自行拉取下来。可以用composer 也可以用git管理,甚至是拷贝文件,看自己喜好)

测试中遇到的问题

安装官方文档,很容易的注册服务到consul了,调用花了点时间,
第一:如何编写个服务。从接口,到实现,到Logic的编写,实际走一遍还是花了大半天时间。
第二:客户端调用的时候,服务出问题,老是获取不到,就因为USER_POOL_USE_PROVIDER=true没有设置。还有就是创建的项目里面config里面服务有个 discover发现服务的配置,那个里面的dc(数据中心)的名字需要和你consul的保持一致,或者把这个删了用默认的。。否则会出现奇怪问题,服务连上了,没有服务可用,会报错。
第三:性能问题,用ab测试的时候,发现超过10个并发就会阻塞。。。这还高性能个毛线啊,通过查看swoole问题,调整了些系统参数,参考swoole系统参数优化, 然后调整了下WORKER_NUM,TASK_WORKER_NUM ,USER_POOL_MAX_ACTIVE和USER_POOL_MAX_WAIT等参数,通过半个小时调整测试,能并发到50了。。。因为我的是垃圾测试主机,只分配的1核和2g内存,2次rpc调用写读数据库。这个表现还是可以的

后面附一些目录结构截图和文件

  • 服务端
    1.png

appmodellogicUserLogic.php

<?php
namespace App\Models\Logic;

use App\Models\Entity\User;
use Swoft\Bean\Annotation\Bean;
/**
 * 用户逻辑层
 * 同时可以被controller server task使用
 *
 * @Bean()
 * @uses      UserLogic
 */
class UserLogic
{

    private $passwordSlot = '__jz';

    /**
     * 生成加密字符
     * @param $password
     */
    protected function genPassword($password){
        return md5($password . $this->passwordSlot);
    }

    /**
     * 注册
     * @param array $info
     * @return mixed
     */
    public function register(array $info) {
        $user = new User();
        $info['password'] = $this->genPassword($info['password']);
        $info['reg_time'] = date('Y-m-d H:i:s');
        return $user->fill($info)->save()->getResult();
    }

    /**
     * 登录
     * @param $account
     * @param $password
     * @return mixed
     */
    public function login(string $account, string $password) {
        $cond = [
            'account' => $account,
            'password' => $this->genPassword($password),
        ];
        return User::findOne($cond)->getResult();
    }

    /**
     * 用户信息
     * @param $id
     * @param $cond
     * @return mixed
     */
    public function info(int $id, array $cond) {
        $cond['id'] = $id;
        return User::findOne($cond)->getResult();
    }

}

userSerivce.php

<?php
namespace App\Services;

use App\Lib\UserInterface;
use App\Models\Logic\UserLogic;
use Swoft\Bean\Annotation\Inject;
use Swoft\Bean\Annotation\Strings;
use Swoft\Rpc\Server\Bean\Annotation\Service;
use Swoft\Core\ResultInterface;

/**
 * User servcie
 *
 * @Service()
 * @method ResultInterface deferRegister(array $info)
 * @method ResultInterface deferLogin(string $account, string $password)
 * @method ResultInterface deferInfo(int $id, array $cond)*/
class UserService implements UserInterface
{

    /**
     *
     * @Inject()
     * @var UserLogic
     */
    private $userLogic;

    public function __call($name, $arguments) {
        // TODO: Implement @method ResultInterface deferRegister(array $info)
    }

    /**
     * 注册
     * @param array $info
     *
     * @return mixed
     */
    public function register(array $info) {
        return $this->userLogic->register($info);
    }

    /**
     * 登录
     * @Strings(name="account", min=4, max=32)
     * @Strings(name="password", min=6, max=32)
     *
     * @param $account
     * @param $password
     * @return mixed
     */
    public function login(string $account, string $password) {
        return $this->userLogic->login($account, $password);
    }

    /**
     * 用户信息
     * @param $id
     * @param $cond
     * @return mixed
     */
    public function info(int $id, array $cond) {
        return $this->userLogic->info($id, $cond);
    }
}

.env

# Application
TIME_ZONE=Asia/Shanghai
LOG_ENABLE=true
APP_DEBUG=true

# Server
PFILE=@runtime/swoft.pid
PNAME=php-swoft
TCPABLE=true
CRONABLE=false
AUTO_RELOAD=true
AUTO_REGISTER=true

# WebSocket
WS_ENABLE_HTTP=true

# TCP
TCP_HOST=0.0.0.0
TCP_PORT=20100
TCP_MODE=SWOOLE_PROCESS
TCP_TYPE=SWOOLE_SOCK_TCP
TCP_PACKAGE_MAX_LENGTH=2048
TCP_OPEN_EOF_CHECK=false

# Crontab
CRONTAB_TASK_COUNT=1024
CRONTAB_TASK_QUEUE=2048

# Swoole Settings
WORKER_NUM=4
MAX_REQUEST=100000
DAEMONIZE=0
DISPATCH_MODE=2
TASK_IPC_MODE=1
MESSAGE_QUEUE_KEY=1879052289
TASK_TMPDIR=/tmp/
LOG_FILE=@runtime/logs/swoole.log
TASK_WORKER_NUM=1
PACKAGE_MAX_LENGTH=2048
OPEN_HTTP2_PROTOCOL=false
SSL_CERT_FILE=/path/to/ssl_cert_file
SSL_KEY_FILE=/path/to/ssl_key_file

# Database Master nodes
DB_NAME=dbMaster
DB_URI=192.168.0.179:3306/swoft_user?user=root&password=root&charset=utf8
DB_MIN_ACTIVE=5
DB_MAX_ACTIVE=10
DB_MAX_WAIT=20
DB_MAX_WAIT_TIME=3
DB_MAX_IDLE_TIME=60
DB_TIMEOUT=2

# Consul
CONSUL_ADDRESS=http://172.17.0.2
CONSUL_PORT=8500
CONSUL_REGISTER_NAME=user
CONSUL_REGISTER_ETO=false
CONSUL_REGISTER_SERVICE_ADDRESS=172.17.0.1
CONSUL_REGISTER_SERVICE_PORT=20100
CONSUL_REGISTER_CHECK_NAME=user
CONSUL_REGISTER_CHECK_TCP=172.17.0.1:20100
CONSUL_REGISTER_CHECK_INTERVAL=20
CONSUL_REGISTER_CHECK_TIMEOUT=1
  • 客户端(pc)

2.png

.env配置

# Application
TIME_ZONE=Asia/Shanghai
LOG_ENABLE=true
APP_DEBUG=true

# Server
PFILE=@runtime/swoft.pid
PNAME=php-pc
TCPABLE=true
CRONABLE=false
AUTO_RELOAD=true
AUTO_REGISTER=false

# HTTP
HTTP_HOST=0.0.0.0
HTTP_PORT=80
HTTP_MODE=SWOOLE_PROCESS
HTTP_TYPE=SWOOLE_SOCK_TCP

# WebSocket
WS_ENABLE_HTTP=true

# Crontab
CRONTAB_TASK_COUNT=1024
CRONTAB_TASK_QUEUE=2048

# Swoole Settings
WORKER_NUM=4
MAX_REQUEST=100000
DAEMONIZE=0
DISPATCH_MODE=2
TASK_IPC_MODE=1
MESSAGE_QUEUE_KEY=1879052289
TASK_TMPDIR=/tmp/
LOG_FILE=@runtime/logs/swoole.log
TASK_WORKER_NUM=10
PACKAGE_MAX_LENGTH=2048
OPEN_HTTP2_PROTOCOL=false
SSL_CERT_FILE=/path/to/ssl_cert_file
SSL_KEY_FILE=/path/to/ssl_key_file

# User service (demo service)
USER_POOL_NAME=user
USER_POOL_URI=
USER_POOL_MIN_ACTIVE=5
USER_POOL_MAX_ACTIVE=60
USER_POOL_MAX_WAIT=200
USER_POOL_TIMEOUT=2000
USER_POOL_MAX_WAIT_TIME=3
USER_POOL_MAX_IDLE_TIME=60
USER_POOL_USE_PROVIDER=true
USER_POOL_BALANCER=random
USER_POOL_PROVIDER=consul

# User service breaker (demo service)
USER_BREAKER_FAIL_COUNT = 3
USER_BREAKER_SUCCESS_COUNT = 6
USER_BREAKER_DELAY_TIME = 5000

# Consul
CONSUL_ADDRESS=http://172.17.0.2
CONSUL_PORT=8500

UserServicePool.php

<?php
/**
 * This file is part of Swoft.
 *
 * @link https://swoft.org
 * @document https://doc.swoft.org
 * @contact group@swoft.org
 * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE
 */

namespace App\Pool;

use Swoft\Bean\Annotation\Inject;
use Swoft\Bean\Annotation\Pool;
use App\Pool\Config\UserPoolConfig;
use Swoft\Rpc\Client\Pool\ServicePool;

/**
 * the pool of user service
 *
 * @Pool(name="user")
 */
class UserServicePool extends ServicePool
{
    /**
     * @Inject()
     *
     * @var UserPoolConfig
     */
    protected $poolConfig;
}

IndexController.php

<?php
/**
 * This file is part of Swoft.
 *
 * @link https://swoft.org
 * @document https://doc.swoft.org
 * @contact group@swoft.org
 * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE
 */

namespace App\Controllers;

use App\Lib\UserInterface;
use Swoft\Http\Server\Bean\Annotation\Controller;
use Swoft\Http\Server\Bean\Annotation\RequestMapping;
use Swoft\Rpc\Client\Bean\Annotation\Reference;

/**
 * Class IndexController
 * @Controller()
 */
class IndexController
{
    /**
     * @Reference(name="user", version="1.0.2")
     *
     * @var UserInterface
     */
    private $UserInterface;

    /**
     * @RequestMapping("/")
     * @return mixed
     */
    public function index(): array
    {
        $info = [
            'account' => 'mytest',
            'password' => '123123',
            'username' => '帅哥' . time(),
        ];
        $rs = $this->UserInterface->register($info);
        $rsInfo = $this->UserInterface->info($rs, []);
        return [
            'title' => '注册新用户',
            'add' => $info,
            'result' => $rs,
            'userInfo' => $rsInfo,
        ];
    }

}

标签: 无

发表评论: