第 4 章 Hyperf 微服务信创全栈部署(麒麟 + 达梦端到端)
4.1 为什么选 Hyperf(而不是 Laravel/ThinkPHP)
大白话:信创项目的三个特点决定了框架选型:
- 要求高并发(政务大厅、金融交易系统),单机要扛住 5 万+ QPS;
- 要求微服务化(模块间解耦,方便不同供应商分包),需要服务注册发现、熔断降级;
- 要求全国产适配(数据库、缓存、消息队列都是国产),框架的数据库抽象层要能快速对接。
Hyperf 满足这三点:基于 Swoole 协程(高并发)、内置服务治理(微服务)、DB 层支持自定义驱动(适配达梦)。
4.2 准备工作:装 Composer 和创建项目
# 1. 装 Composer(国内用阿里云镜像)cd/usr/local/srcwgethttps://mirrors.aliyun.com/composer/composer.pharsudomvcomposer.phar /usr/local/bin/composersudochmod+x /usr/local/bin/composercomposerconfig-grepo.packagistcomposerhttps://mirrors.aliyun.com/composer/# 2. 创建 Hyperf 项目cd/var/wwwcomposercreate-project hyperf/hyperf-skeleton hyperf-democdhyperf-demo# 3. 检查依赖(Swoole 必须 >= 5.0)/usr/local/php8/bin/php--riswoole|grepVersion# 若低于 5.0,回到第 3 章升级# 4. 初次启动测试/usr/local/php8/bin/php bin/hyperf.php start# 期望输出:Hyperf HTTP Server started at http://0.0.0.0:9501# 访问:curl http://127.0.0.1:9501 应返回 "Hello Hyperf"4.3 对接达梦数据库(最大的坑在这)
4.3.1 达梦和 MySQL 的差异速查表
| 差异点 | MySQL 5.7/8.0 | 达梦 DM8 | Hyperf 代码改动 |
|---|---|---|---|
| 表名大小写 | 不敏感(Linux 上可配) | 默认敏感 | 建表全用大写或全小写 |
| 自增主键 | AUTO_INCREMENT | IDENTITY(1,1) | 迁移文件要改 |
| 分页 | LIMIT 10 OFFSET 20 | LIMIT 20, 10 | 查询构造器要判断驱动 |
| 字符串拼接 | CONCAT() | ` | |
| 日期函数 | NOW() | SYSDATE | ORM 层统一 |
| JSON 类型 | 原生支持 | 8.1.2+ 才有 | 老版本用TEXT |
| 事务隔离级别 | RR(可重复读) | RC(读已提交) | 显式设置 |
4.3.2 安装达梦 PDO 驱动(最折腾的一步)
达梦官方提供的是PDO_ODBC驱动,不是直接的 PDO_DM。步骤如下:
# 1. 先装 unixODBC(麒麟 V10)sudoyuminstall-yunixODBC unixODBC-devel# 统信 UOS 用:# sudo apt install -y unixodbc unixodbc-dev# 2. 下载达梦客户端(需要去达梦官网注册账号下载 DM8 客户端)# 假设下载到 /tmp/dm8_client_linux_x86_64.tar.gz(海光)# ARM64 的下载文件名是 dm8_client_linux_aarch64.tar.gzcd/usr/local/srctar-zxf/tmp/dm8_client_linux_*.tar.gzsudomvdm8_client /usr/local/dm8# 3. 配置 ODBC 数据源sudotee/etc/odbc.ini>/dev/null<<'EOF' [DM8] Driver = /usr/local/dm8/drivers/libdodbc.so Description = DaMeng ODBC Driver SERVER = 192.168.1.100 # 达梦服务器 IP PORT = 5236 DATABASE = SYSDBA # 默认数据库 UID = SYSDBA PWD = SYSDBA EOF# 4. 测试 ODBC 连接isql-vDM8# 若能进入 SQL> 提示符,输入 SELECT * FROM V$VERSION; 能返回版本号,说明通了# 5. 确认 PHP 已编译 PDO_ODBC(第 1 章编译时如果没加 --with-pdo-odbc,要重编)/usr/local/php8/bin/php-m|grep-ipdo_odbc# 若没有,回到源码目录重新 configure:# ./configure --with-pdo-odbc=unixODBC,/usr ... (其他参数同第 1 章)4.3.3 Hyperf 配置达梦连接
编辑config/autoload/databases.php:
<?phpreturn['default'=>env('DB_DRIVER','dm8'),// 改为 dm8'connections'=>['dm8'=>['driver'=>'pdo',// 注意:不是 'dm',达梦走 PDO_ODBC'dsn'=>'odbc:DM8',// 对应 /etc/odbc.ini 里的 [DM8]'username'=>env('DB_USERNAME','SYSDBA'),'password'=>env('DB_PASSWORD','SYSDBA'),'charset'=>'utf8','collation'=>'','prefix'=>'','options'=>[// 达梦的事务隔离级别默认是 RC,业务需要 RR 的话要显式设置\PDO::ATTR_ERRMODE=>\PDO::ERRMODE_EXCEPTION,\PDO::ATTR_CASE=>\PDO::CASE_NATURAL,// 保持原始大小写\PDO::ATTR_ORACLE_NULLS=>\PDO::NULL_NATURAL,],'pool'=>['min_connections'=>1,'max_connections'=>20,'connect_timeout'=>10.0,'wait_timeout'=>3.0,'heartbeat'=>60,// 达梦默认空闲超时 10 分钟,这里 60 秒心跳保活'max_idle_time'=>60.0,],],// 保留 MySQL 配置,方便本地开发切换'mysql'=>['driver'=>'mysql','host'=>env('DB_HOST','127.0.0.1'),'port'=>env('DB_PORT',3306),'database'=>env('DB_DATABASE','hyperf'),'username'=>env('DB_USERNAME','root'),'password'=>env('DB_PASSWORD',''),'charset'=>'utf8mb4','collation'=>'utf8mb4_unicode_ci','prefix'=>'','pool'=>['min_connections'=>1,'max_connections'=>10,'connect_timeout'=>10.0,'wait_timeout'=>3.0,'heartbeat'=>60,'max_idle_time'=>60.0,],],],];4.3.4 迁移文件适配达梦
Hyperf 用的是类似 Laravel 的 migration,但语法要改:
<?php// database/migrations/2024_01_01_000000_create_users_table.phpuseHyperf\Database\Schema\Schema;useHyperf\Database\Schema\Blueprint;useHyperf\Database\Migrations\Migration;classCreateUsersTableextendsMigration{publicfunctionup():void{// 获取当前连接驱动$driver=config('databases.default');Schema::create('users',function(Blueprint$table)use($driver){if($driver==='dm8'){// 达梦的自增语法$table->bigInteger('ID')->comment('用户ID');// 注意:大写$table->string('USERNAME',50)->unique()->comment('用户名');$table->string('PASSWORD',255)->comment('密码');$table->timestamp('CREATED_AT')->useCurrent()->comment('创建时间');$table->timestamp('UPDATED_AT')->useCurrent()->comment('更新时间');$table->primary('ID');// 手动加自增(达梦不支持 $table->id())DB::statement('ALTER TABLE "USERS" MODIFY "ID" IDENTITY(1,1)');}else{// MySQL 标准写法$table->id();$table->string('username',50)->unique();$table->string('password');$table->timestamps();}});}publicfunctiondown():void{Schema::dropIfExists('users');}}执行迁移:
/usr/local/php8/bin/php bin/hyperf.php migrate# 若报错 "table or view does not exist: MIGRATIONS",说明达梦里还没有迁移表,正常,继续# 若报错 "ORA-00001: unique constraint violated",说明表已存在,先 migrate:rollback4.3.5 查询构造器的兼容性处理
问题:Hyperf 的DB::table()->paginate()生成的 SQL 在达梦上语法错误。
解决:自定义一个DmQueryBuilder:
<?php// app/Kernel/Db/DmQueryBuilder.phpnamespaceApp\Kernel\Db;useHyperf\Database\Query\Builder;classDmQueryBuilderextendsBuilder{/** * 重写分页方法,适配达梦的 LIMIT 语法(与 MySQL 顺序相反) */publicfunctionforPage($page,$perPage=15){$offset=($page-1)*$perPage;// 达梦的语法:LIMIT offset, limit(和 MySQL 反着)return$this->offset($offset)->limit($perPage);}}在config/autoload/databases.php里注册:
'dm8'=>[// ...'query_builder'=>\App\Kernel\Db\DmQueryBuilder::class,],4.4 Hyperf 微服务组件配置(服务注册与发现)
信创项目常用Consul或Nacos做服务注册中心。这里以 Consul 为例(Nacos 是 Java 栈,信创环境可能要换国产的,如"分布式协调服务 TongLINK")。
4.4.1 安装 Consul(在管理节点上)
# 下载(ARM64 用 consul_*_linux_arm64.zip)cd/usr/local/srcwgethttps://releases.hashicorp.com/consul/1.17.0/consul_1.17.0_linux_amd64.zipunzipconsul_1.17.0_linux_amd64.zipsudomvconsul /usr/local/bin/# 启动(单机 dev 模式,生产要搭集群)consul agent-dev-bind=0.0.0.0-client=0.0.0.0&# 检查consul members# 期望输出:Node Address Status Type4.4.2 Hyperf 配置服务注册
安装依赖:
cd/var/www/hyperf-democomposerrequire hyperf/consul hyperf/service-governance编辑config/autoload/consul.php:
<?phpreturn['uri'=>'http://192.168.1.200:8500',// Consul 服务器地址];编辑config/autoload/services.php(配置要注册的服务):
<?phpreturn['enable'=>['discovery'=>true,// 启用服务发现'register'=>true,// 启用服务注册],'consumers'=>[],// 消费的外部服务(后面配)'providers'=>[['name'=>'user-service',// 服务名'service'=>\App\JsonRpc\UserServiceInterface::class,// 接口'protocol'=>'jsonrpc-http',// 协议'registry'=>['protocol'=>'consul',// 注册到 Consul'address'=>'http://192.168.1.200:8500',],],],'drivers'=>['consul'=>['uri'=>'http://192.168.1.200:8500','token'=>'','check'=>['deregister_critical_service_after'=>'90m','interval'=>'10s',// 健康检查间隔],],],];4.4.3 定义 JSON-RPC 服务(微服务间通信)
创建接口:
<?php// app/JsonRpc/UserServiceInterface.phpnamespaceApp\JsonRpc;interfaceUserServiceInterface{publicfunctiongetUserById(int$id):array;publicfunctioncreateUser(array$data):int;}实现服务:
<?php// app/JsonRpc/UserService.phpnamespaceApp\JsonRpc;useHyperf\RpcServer\Annotation\RpcService;#[RpcService(name:"user-service",protocol:"jsonrpc-http",server:"jsonrpc-http")]classUserServiceimplementsUserServiceInterface{publicfunctiongetUserById(int$id):array{// 实际业务从达梦查数据$user=\Hyperf\DbConnection\Db::connection('dm8')->table('USERS')->where('ID',$id)->first();return$user?(array)$user:[];}publicfunctioncreateUser(array$data):int{return\Hyperf\DbConnection\Db::connection('dm8')->table('USERS')->insertGetId(['USERNAME'=>$data['username'],'PASSWORD'=>password_hash($data['password'],PASSWORD_BCRYPT),'CREATED_AT'=>date('Y-m-d H:i:s'),'UPDATED_AT'=>date('Y-m-d H:i:s'),]);}}启动服务:
/usr/local/php8/bin/php bin/hyperf.php start# 查看 Consul UI:http://192.168.1.200:8500/ui/dc1/services# 应能看到 "user-service" 已注册,健康检查为绿色4.4.4 其他服务调用(服务发现)
在另一个 Hyperf 项目里(如 order-service),配置消费者:
<?php// config/autoload/services.phpreturn['consumers'=>[['name'=>'user-service','service'=>\App\JsonRpc\UserServiceInterface::class,// 接口要一致'protocol'=>'jsonrpc-http','load_balancer'=>'random',// 负载均衡策略'registry'=>['protocol'=>'consul','address'=>'http://192.168.1.200:8500',],],],];在控制器里调用:
<?phpnamespaceApp\Controller;useApp\JsonRpc\UserServiceInterface;useHyperf\Di\Annotation\Inject;classOrderController{#[Inject]privateUserServiceInterface$userService;publicfunctioncreate(){// 跨服务调用$user=$this->userService->getUserById(1);if(empty($user)){return['code'=>404,'message'=>'User not found'];}// 创建订单逻辑...return['code'=>0,'data'=>['order_id'=>12345]];}}4.5 容器化与部署(systemd 方式)
生产环境不能直接php bin/hyperf.php start(前台运行,SSH 断了就挂),要用 systemd 托管。
sudotee/etc/systemd/system/hyperf-user-service.service>/dev/null<<'EOF' [Unit] Description=Hyperf User Service After=network.target consul.service dm8.service [Service] Type=simple User=www Group=www WorkingDirectory=/var/www/hyperf-demo ExecStart=/usr/local/php8/bin/php bin/hyperf.php start ExecReload=/bin/kill -USR1 $MAINPID Restart=on-failure RestartSec=5s LimitNOFILE=65535 StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target EOFsudosystemctl daemon-reloadsudosystemctlenablehyperf-user-servicesudosystemctl start hyperf-user-servicesudosystemctl status hyperf-user-service查看日志:
sudojournalctl-uhyperf-user-service-f