选择了后端服务框架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调用写读数据库。这个表现还是可以的
后面附一些目录结构截图和文件
- 服务端
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)
.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,
];
}
}