转载于童年swoft+thinkphp6搭建一套可落地的微服务架构
经常听大家说微服务,微服务,但是没有正儿八经的搭建或者落地过一套完整的微服务。
那么今天就分享一篇可以落地的微服务吧!
重点!这个教程花了我0.5大洋!!!如果对你有帮助记得打赏!抱拳了!老铁!
安装环境 【直接宝塔】省时省力
lnmp环境一键安装 其实装个mysql和php就可以了
拉下swoft的代码
虽然经历了一些七七八八的东西,但是swoft一出来我就找了个课程学习过,所以相对还是比较熟悉的,hyperf不太熟悉,需要花时间学习一下,所以这里就用swoft吧
swoft
开始架构
正常来说微服务,微服务,重点就是服务,把我们一个整体的项目拆分为多个,比如一个商城可能有
- 用户
- 商品
- 订单
- 支付
- 物流
- 评论
- 等等各种服务
我们这里就用用户和商品两个服务做个示范
- 将swoft 复制一份 改名为 user 和 goods
- 安装consul 作为服务注册中心
https://developer.hashicorp.com/consul/downloads 官网
# centos/rhel
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
sudo yum -y install consul
# 源码安装
unzip consul_1.10.3_linux_amd64.zip
##将解压好的consul可执行命令移动到/usr/local/bin目录下
mv consul /usr/local/bin
# 安装完输入 consul 可查看consul是否安装
# 查看consul安装路径:whereis consul
# 启动 consul agent -dev -ui -client=0.0.0.0
# 就可以访问了 通过浏览器访问地址,端口号是 8500
# 查看consul中的所有节点成员:consul members
. 启动consul 开放8500端口
浏览器输入 ip:8500
. 修改user服务的bean.php
bean.php
<?php declare(strict_types=1);
/**
* This file is part of Swoft.
*
* @link https://swoft.org
* @document https://swoft.org/docs
* @contact group@swoft.org
* @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE
*/
use Swoft\Db\Database;
use Swoft\Db\Pool;
use Swoft\Http\Server\HttpServer;
use Swoft\Http\Server\Swoole\RequestListener;
use Swoft\Redis\RedisDb;
use Swoft\Rpc\Client\Client as ServiceClient;
use Swoft\Rpc\Client\Pool as ServicePool;
use Swoft\Rpc\Server\ServiceServer;
use Swoft\Server\SwooleEvent;
use Swoft\Task\Swoole\FinishListener;
use Swoft\Task\Swoole\TaskListener;
use Swoft\WebSocket\Server\WebSocketServer;
return [
'noticeHandler' => [
'logFile' => '@runtime/logs/notice-%d{Y-m-d-H}.log',
],
'applicationHandler' => [
'logFile' => '@runtime/logs/error-%d{Y-m-d}.log',
],
'logger' => [
'flushRequest' => false,
'enable' => false,
'json' => false,
],
'httpServer' => [
'class' => HttpServer::class,
'port' => 18506,
'listener' => [
'rpc' => bean('rpcServer'),
// 'tcp' => bean('tcpServer'),
],
'process' => [
// 'monitor' => bean(\App\Process\MonitorProcess::class)
// 'crontab' => bean(CrontabProcess::class)
],
'on' => [
// SwooleEvent::TASK => bean(SyncTaskListener::class), // Enable sync task
SwooleEvent::TASK => bean(TaskListener::class), // Enable task must task and finish event
SwooleEvent::FINISH => bean(FinishListener::class)
],
/* @see HttpServer::$setting */
'setting' => [
'task_worker_num' => 12,
'task_enable_coroutine' => true,
'worker_num' => 6,
// static handle
// 'enable_static_handler' => true,
// 'document_root' => dirname(__DIR__) . '/public',
]
],
'httpDispatcher' => [
// Add global http middleware
'middlewares' => [
\App\Http\Middleware\FavIconMiddleware::class,
\Swoft\Http\Session\SessionMiddleware::class,
// \Swoft\Whoops\WhoopsMiddleware::class,
// Allow use @View tag
\Swoft\View\Middleware\ViewMiddleware::class,
],
'afterMiddlewares' => [
\Swoft\Http\Server\Middleware\ValidatorMiddleware::class
]
],
'db' => [
'class' => Database::class,
'dsn' => 'mysql:dbname=ceshi;host=127.0.0.1',
'username' => 'ceshi',
'password' => 'ki7HC5X6Ep5M4HbF',
'charset' => 'utf8mb4',
],
'db2' => [
'class' => Database::class,
'dsn' => 'mysql:dbname=test2;host=127.0.0.1',
'username' => 'root',
'password' => 'swoft123456',
'charset' => 'utf8mb4',
// 'dbSelector' => bean(DbSelector::class)
],
'db2.pool' => [
'class' => Pool::class,
'database' => bean('db2'),
],
'db3' => [
'class' => Database::class,
'dsn' => 'mysql:dbname=test2;host=127.0.0.1',
'username' => 'root',
'password' => 'swoft123456',
'charset' => 'utf8mb4',
],
'db3.pool' => [
'class' => Pool::class,
'database' => bean('db3')
],
'migrationManager' => [
'migrationPath' => '@database/Migration',
],
'redis' => [
'class' => RedisDb::class,
'host' => '127.0.0.1',
'port' => 6379,
'database' => 0,
'option' => [
'prefix' => 'swoft:'
]
],
'user' => [
'class' => ServiceClient::class,
'host' => '127.0.0.1',
'port' => 18307,
'setting' => [
'timeout' => 0.5,
'connect_timeout' => 1.0,
'write_timeout' => 10.0,
'read_timeout' => 0.5,
],
'packet' => bean('rpcClientPacket')
],
'user.pool' => [
'class' => ServicePool::class,
'client' => bean('user'),
],
'rpcServer' => [
'class' => ServiceServer::class,
'post' => 18408
// 'listener' => [
// 'http' => bean('httpServer'),
// ]
],
'wsServer' => [
'class' => WebSocketServer::class,
'port' => 18308,
'listener' => [
'rpc' => bean('rpcServer'),
// 'tcp' => bean('tcpServer'),
],
'on' => [
// Enable http handle
SwooleEvent::REQUEST => bean(RequestListener::class),
// Enable task must add task and finish event
SwooleEvent::TASK => bean(TaskListener::class),
SwooleEvent::FINISH => bean(FinishListener::class)
],
'debug' => 1,
// 'debug' => env('SWOFT_DEBUG', 0),
/* @see WebSocketServer::$setting */
'setting' => [
'task_worker_num' => 6,
'task_enable_coroutine' => true,
'worker_num' => 6,
'log_file' => alias('@runtime/swoole.log'),
// 'open_websocket_close_frame' => true,
],
],
// 'wsConnectionManager' => [
// 'storage' => bean('wsConnectionStorage')
// ],
// 'wsConnectionStorage' => [
// 'class' => \Swoft\Session\SwooleStorage::class,
// ],
/** @see \Swoft\WebSocket\Server\WsMessageDispatcher */
'wsMsgDispatcher' => [
'middlewares' => [
\App\WebSocket\Middleware\GlobalWsMiddleware::class
],
],
/** @see \Swoft\Tcp\Server\TcpServer */
'tcpServer' => [
'port' => 18309,
'debug' => 1,
],
/** @see \Swoft\Tcp\Protocol */
'tcpServerProtocol' => [
// 'type' => \Swoft\Tcp\Packer\JsonPacker::TYPE,
'type' => \Swoft\Tcp\Packer\SimpleTokenPacker::TYPE,
// 'openLengthCheck' => true,
],
/** @see \Swoft\Tcp\Server\TcpDispatcher */
'tcpDispatcher' => [
'middlewares' => [
\App\Tcp\Middleware\GlobalTcpMiddleware::class
],
],
'cliRouter' => [// 'disabledGroups' => ['demo', 'test'],
],
'consul' => [
'host'=>'127.0.0.1',
'post'=>8500
],
];
修改 user服务的 app\listerner\RegisterServiceListener
<?php declare(strict_types=1);
/**
* This file is part of Swoft.
*
* @link https://swoft.org
* @document https://swoft.org/docs
* @contact group@swoft.org
* @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE
*/
namespace App\Listener;
use Swoft\Bean\Annotation\Mapping\Inject;
use Swoft\Consul\Agent;
use Swoft\Event\Annotation\Mapping\Listener;
use Swoft\Event\EventHandlerInterface;
use Swoft\Event\EventInterface;
use Swoft\Http\Server\HttpServer;
use Swoft\Server\SwooleEvent;
/**
* Class RegisterServiceListener
*
* @since 2.0
*
* @Listener(event=SwooleEvent::START)
*/
class RegisterServiceListener implements EventHandlerInterface
{
/**
* @Inject()
*
* @var Agent
*/
private $agent;
/**
* @param EventInterface $event
*/
public function handle(EventInterface $event): void
{
/** @var HttpServer $httpServer */
$httpServer = $event->getTarget();
$service = [
'ID' => 'swoft-user',
'Name' => 'swoft-user',
'Tags' => [
'http'
],
'Address' => '127.0.0.1',
'Port' => 18408,
'Meta' => [
'version' => '1.0'
],
'EnableTagOverride' => false,
'Weights' => [
'Passing' => 10,
'Warning' => 1
],
'checks'=> [
[
'name'=>'prod-check-user',
'http'=>'http://127.0.0.1:18506/consul/health',
'interval'=>'10s',
'timeout'=>'5s'
]
]
];
// Register
$this->agent->registerService($service);
// CLog::info('Swoft http register service success by consul!');
}
}
. 修改goods服务的 bean.php
bean.php
<?php declare(strict_types=1);
/**
* This file is part of Swoft.
*
* @link https://swoft.org
* @document https://swoft.org/docs
* @contact group@swoft.org
* @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE
*/
use Swoft\Db\Database;
use Swoft\Db\Pool;
use Swoft\Http\Server\HttpServer;
use Swoft\Http\Server\Swoole\RequestListener;
use Swoft\Redis\RedisDb;
use Swoft\Rpc\Client\Client as ServiceClient;
use Swoft\Rpc\Client\Pool as ServicePool;
use Swoft\Rpc\Server\ServiceServer;
use Swoft\Server\SwooleEvent;
use Swoft\Task\Swoole\FinishListener;
use Swoft\Task\Swoole\TaskListener;
use Swoft\WebSocket\Server\WebSocketServer;
return [
'noticeHandler' => [
'logFile' => '@runtime/logs/notice-%d{Y-m-d-H}.log',
],
'applicationHandler' => [
'logFile' => '@runtime/logs/error-%d{Y-m-d}.log',
],
'logger' => [
'flushRequest' => false,
'enable' => false,
'json' => false,
],
'httpServer' => [
'class' => HttpServer::class,
'port' => 18507,
'listener' => [
'rpc' => bean('rpcServer'),
// 'tcp' => bean('tcpServer'),
],
'process' => [
// 'monitor' => bean(\App\Process\MonitorProcess::class)
// 'crontab' => bean(CrontabProcess::class)
],
'on' => [
// SwooleEvent::TASK => bean(SyncTaskListener::class), // Enable sync task
SwooleEvent::TASK => bean(TaskListener::class), // Enable task must task and finish event
SwooleEvent::FINISH => bean(FinishListener::class)
],
/* @see HttpServer::$setting */
'setting' => [
'task_worker_num' => 12,
'task_enable_coroutine' => true,
'worker_num' => 6,
// static handle
// 'enable_static_handler' => true,
// 'document_root' => dirname(__DIR__) . '/public',
]
],
'httpDispatcher' => [
// Add global http middleware
'middlewares' => [
\App\Http\Middleware\FavIconMiddleware::class,
\Swoft\Http\Session\SessionMiddleware::class,
// \Swoft\Whoops\WhoopsMiddleware::class,
// Allow use @View tag
\Swoft\View\Middleware\ViewMiddleware::class,
],
'afterMiddlewares' => [
\Swoft\Http\Server\Middleware\ValidatorMiddleware::class
]
],
'db' => [
'class' => Database::class,
'dsn' => 'mysql:dbname=ceshi;host=127.0.0.1',
'username' => 'ceshi',
'password' => 'ki7HC5X6Ep5M4HbF',
'charset' => 'utf8mb4',
],
'db2' => [
'class' => Database::class,
'dsn' => 'mysql:dbname=test2;host=127.0.0.1',
'username' => 'root',
'password' => 'swoft123456',
'charset' => 'utf8mb4',
// 'dbSelector' => bean(DbSelector::class)
],
'db2.pool' => [
'class' => Pool::class,
'database' => bean('db2'),
],
'db3' => [
'class' => Database::class,
'dsn' => 'mysql:dbname=test2;host=127.0.0.1',
'username' => 'root',
'password' => 'swoft123456',
'charset' => 'utf8mb4',
],
'db3.pool' => [
'class' => Pool::class,
'database' => bean('db3')
],
'migrationManager' => [
'migrationPath' => '@database/Migration',
],
'redis' => [
'class' => RedisDb::class,
'host' => '127.0.0.1',
'port' => 6379,
'database' => 0,
'option' => [
'prefix' => 'swoft:'
]
],
'user' => [
'class' => ServiceClient::class,
'host' => '127.0.0.1',
'port' => 18307,
'setting' => [
'timeout' => 0.5,
'connect_timeout' => 1.0,
'write_timeout' => 10.0,
'read_timeout' => 0.5,
],
'packet' => bean('rpcClientPacket')
],
'user.pool' => [
'class' => ServicePool::class,
'client' => bean('user'),
],
'rpcServer' => [
'class' => ServiceServer::class,
'port' => 18409
// 'listener' => [
// 'http' => bean('httpServer'),
// ]
],
'wsServer' => [
'class' => WebSocketServer::class,
'port' => 18308,
'listener' => [
'rpc' => bean('rpcServer'),
// 'tcp' => bean('tcpServer'),
],
'on' => [
// Enable http handle
SwooleEvent::REQUEST => bean(RequestListener::class),
// Enable task must add task and finish event
SwooleEvent::TASK => bean(TaskListener::class),
SwooleEvent::FINISH => bean(FinishListener::class)
],
'debug' => 1,
// 'debug' => env('SWOFT_DEBUG', 0),
/* @see WebSocketServer::$setting */
'setting' => [
'task_worker_num' => 6,
'task_enable_coroutine' => true,
'worker_num' => 6,
'log_file' => alias('@runtime/swoole.log'),
// 'open_websocket_close_frame' => true,
],
],
// 'wsConnectionManager' => [
// 'storage' => bean('wsConnectionStorage')
// ],
// 'wsConnectionStorage' => [
// 'class' => \Swoft\Session\SwooleStorage::class,
// ],
/** @see \Swoft\WebSocket\Server\WsMessageDispatcher */
'wsMsgDispatcher' => [
'middlewares' => [
\App\WebSocket\Middleware\GlobalWsMiddleware::class
],
],
/** @see \Swoft\Tcp\Server\TcpServer */
'tcpServer' => [
'port' => 18309,
'debug' => 1,
],
/** @see \Swoft\Tcp\Protocol */
'tcpServerProtocol' => [
// 'type' => \Swoft\Tcp\Packer\JsonPacker::TYPE,
'type' => \Swoft\Tcp\Packer\SimpleTokenPacker::TYPE,
// 'openLengthCheck' => true,
],
/** @see \Swoft\Tcp\Server\TcpDispatcher */
'tcpDispatcher' => [
'middlewares' => [
\App\Tcp\Middleware\GlobalTcpMiddleware::class
],
],
'cliRouter' => [// 'disabledGroups' => ['demo', 'test'],
],
'consul' => [
'host'=>'127.0.0.1',
'post'=>8500
],
];
修改 goods服务的 app\Listerner\RegisterServiceListener
<?php declare(strict_types=1);
/**
* This file is part of Swoft.
*
* @link https://swoft.org
* @document https://swoft.org/docs
* @contact group@swoft.org
* @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE
*/
namespace App\Listener;
use Swoft\Bean\Annotation\Mapping\Inject;
use Swoft\Consul\Agent;
use Swoft\Event\Annotation\Mapping\Listener;
use Swoft\Event\EventHandlerInterface;
use Swoft\Event\EventInterface;
use Swoft\Http\Server\HttpServer;
use Swoft\Server\SwooleEvent;
/**
* Class RegisterServiceListener
*
* @since 2.0
*
* @Listener(event=SwooleEvent::START)
*/
class RegisterServiceListener implements EventHandlerInterface
{
/**
* @Inject()
*
* @var Agent
*/
private $agent;
/**
* @param EventInterface $event
*/
public function handle(EventInterface $event): void
{
/** @var HttpServer $httpServer */
$httpServer = $event->getTarget();
$service = [
'ID' => 'swoft-goods',
'Name' => 'swoft-goods',
'Tags' => [
'http'
],
'Address' => '127.0.0.1',
'Port' => 18409,
'Meta' => [
'version' => '1.0'
],
'EnableTagOverride' => false,
'Weights' => [
'Passing' => 10,
'Warning' => 1
],
'checks'=> [
[
'name'=>'prod-check-user',
'http'=>'http://127.0.0.1:18507/consul/health',
'interval'=>'10s',
'timeout'=>'5s'
]
]
];
// Register
$this->agent->registerService($service);
// CLog::info('Swoft http register service success by consul!');
}
}
分别在user 服务和 goods服务的 app\Http\Controller 新建一个ConsulController.php ,内容如下
<?php declare(strict_types=1);
/**
* This file is part of Swoft.
*
* @link https://swoft.org
* @document https://swoft.org/docs
* @contact group@swoft.org
* @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE
*/
namespace App\Http\Controller;
use Exception;
use Swoft\Co;
use Swoft\Http\Server\Annotation\Mapping\Controller;
use Swoft\Http\Server\Annotation\Mapping\RequestMapping;
use Swoft\Rpc\Client\Annotation\Mapping\Reference;
/**
* Class RpcController
*
* @since 2.0
*
* @Controller()
*/
class ConsulController
{
/**
* @RequestMapping("health")
*
* @return array
*/
public function health(): array
{
return ['status'=>'ok'];
}
}
- 启动consul 分别启动 user 和 goods服务
consul agent -dev -ui -client=0.0.0.0
cd user
php ./bin/swoft http:start
cd goods
php ./bin/swoft http:start
可以看到服务已经注册进注册中心了,注册中心会检查我们的服务是否正常,这样就不担心服务挂掉
-
用thinkphp6+vue新建一个web服务
启动 网站 -
安装kong和konga 前置条件要安装pgsql 【后面才发现宝塔集成了】当然也可以自己搭建
-
安装pgsql【自己搭建】
# 安装postgresql
# 下载
yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm
# 安装
yum install -y postgresql15-server
# 初始化数据库
sudo /usr/pgsql-15/bin/postgresql-15-setup initdb
# 设置开机启动
sudo systemctl enable postgresql-15
# 关闭防火墙【宝塔直接打开5432端口就可以】
# 修改监听地址为任意地址,即修改postgresql.conf文件。
#打开配置文件
vi /var/lib/pgsql/15/data/postgresql.conf
#打开监听注释,监听地址改为*
listen_addresses = '*'
# port 也要打开注释、
#保存退出 esc
# 允许所有IP访问,即修改 pg_hba.conf 文件
#打开配置文件
vi /var/lib/pgsql/15/data/pg_hba.conf
#新增一行,若连接不上则将 scram-sha-256修改为trust 信任模式
host all all 0.0.0.0/0 scram-sha-256
# 重启服务
sudo systemctl restart postgresql-15
#如果登录不上,就改下密码
#切换到postgres用户
su postgres
#切换SQL模式
psql
# 修改密码 【默认密码是postgres】
alter user postgres with password 'postgres123';
#正常到这一步就可以了 不需要创建test用户
#创建test用户
create user test with password 'test';
#授权
grant all privileges on database mydb to test;
#退出
- kong 和 konga安装
# 官网
https://docs.konghq.com/gateway
# 安装kong
curl -Lo kong-enterprise-edition-3.3.0.0.rpm $( rpm --eval "https://download.konghq.com/gateway-3.x-rhel-%{rhel}/Packages/k/kong-enterprise-edition-3.3.0.0.rhel%{rhel}.amd64.rpm")
sudo yum install kong-enterprise-edition-3.3.0.0.rpm
# 查看是否成功
kong version
#复制配置文件
cp /etc/kong/kong.conf.default /etc/kong/kong.conf
# 修改配置文件信息
# 直接打开配置文件的副本
vim /etc/kong/kong.conf
# 找到下面这些配置(打开注释)
# gp_host可以使用127.0.0.1(前提是和你的postgreSQL部署在同一台服务器上)
database = postgres
pg_host = 127.0.0.1 # Host of the Postgres server.
pg_port = 5432 # Port of the Postgres server.
pg_timeout = 5000 # Defines the timeout (in ms), for connecting,
pg_user = postgres # Postgres user.
# 密码需要自己进入编辑模式输入(按i进入编辑模式)
pg_password = postgres # Postgres user's password.
pg_database = postgres # The database name to connect to.
# 这个配置是对于你后面需要集成其他的服务的配置信息(本文以consul注册中心为例)配置consul的IP consul 8500是http端口,但是dns是8600
dns_resolver = 127.0.0.1:8600 # Comma separated list of nameservers, each
# 配置管理员访问IP,改成0.0.0.0方便其他服务访问(有其他需要可以自行修改),可以对kong进行各种配置
admin_listen = 0.0.0.0:8001 reuseport backlog=16384, 0.0.0.0:8444 http2 ssl reuseport backlog=16384
# 配置用户访问IP 默认是8000
proxy_listen = 0.0.0.0:80 reuseport backlog=16384, 0.0.0.0:8443 http2 ssl reuseport backlog=16384
# 启动 kong
kong start -c /etc/kong/kong.conf
# 重启
kong restart -c /etc/kong/kong.conf
# 出现的问题 Kong 网关 authentication exchange unsuccessful 报错
#上面有一步修改postgresql的密码可以解决,网上说的改md5解决不了
# Database needs bootstrapping or is older than Kong 1.0.
#直接运行命令 kong migrations bootstrap
# konga
git clone https://github.com/pantsel/konga.git
cd konga
npm install 或者 npm install --unsafe-perm=true --allow-root
npm start
#放行端口 1337
#注册管理员账号 admin admin888即可
- 启动kong 运行konga 进入界面