如果 用 webman 实现 yar 服务端呢?
搜索
webman 实现 YAR 服务端 31 个结果
在 Webman 里“实现一个 YAR 服务端”= 让 Webman 能看懂 YAR 协议,把请求解包→路由到类→把结果按 YAR 格式回给客户端。
核心只有三步:
核心只有三步:
- 识别 YAR 协议头(
X-YAR-*); - 用 msgpack/json 把 body 解成 [method, params];
- 把返回值再按同样格式打包返回,并带上
X-YAR-*响应头。
下面给出一份「零依赖、开箱即用」的 Webman 路由级实现,不装 yar 扩展也能跑。
- 新建路由文件
route/yar.php
php
复制
use Webman\Route;
use app\yar\YarServer; // 后面会创建
# 只接管 POST,GET 留给 info 页面
Route::post('/yar', [YarServer::class, 'handle']);
Route::get ('/yar', [YarServer::class, 'info']);- 实现协议解析器
app/yar/YarProtocol.php
php
复制
<?php
namespace app\yar;
/**
* 把 YAR 请求/响应打包、解包
* 只实现最常用 msgpack 格式,json 同理
*/
class YarProtocol
{
const HEADER_SIZE = 80; // yar_header_t 固定 80 B
const MAGIC_NUM = 0x80dfec60; // 网络字节序
/* 解析一次请求,返回 ['method'=>xx,'params'=>[...]] */
public static function unpack(string $raw): array
{
if (strlen($raw) < self::HEADER_SIZE) {
throw new \Exception('bad yar header');
}
$header = unpack('Nmagic/Nid/Nversion/Nbody_len/Npackager/Nreserved', substr($raw, 0, 24));
if ($header['magic'] !== self::MAGIC_NUM) {
throw new \Exception('magic number mismatch');
}
$body = substr($raw, self::HEADER_SIZE, $header['body_len']);
$data = match ($header['packager']) {
0 => msgpack_unpack($body), // msgpack
1 => json_decode($body, true),// json
default => throw new \Exception('unsupported packager'),
};
return $data; // ['method'=>'add','params'=>[1,2]]
}
/* 把返回值打包成 YAR 响应体 */
public static function pack($ret, string $packager = 'msgpack'): string
{
$body = match ($packager) {
'msgpack' => msgpack_pack(['ret'=>$ret]),
'json' => json_encode(['ret'=>$ret]),
};
$head = pack('NNNNNN',
self::MAGIC_NUM,
mt_rand(), // id
0, // version
strlen($body),
$packager==='msgpack' ? 0 : 1,
0
);
return $head . str_pad('', self::HEADER_SIZE - strlen($head), "\0") . $body;
}
}- 服务端入口
app/yar/YarServer.php
php
复制
<?php
namespace app\yar;
use Webman\Http\Request;
use Webman\Http\Response;
class YarServer
{
protected static $executor; // 要暴露的类实例
/* 注册业务对象(Webman 启动时调用一次) */
public static function setExecutor(object $obj): void
{
self::$executor = $obj;
}
/** 真正处理 RPC 调用 */
public static function handle(Request $request): Response
{
if (!$request->isPost()) {
return response('only POST', 405);
}
$packager = $request->header('x-yar-packager', 'msgpack');
try {
$raw = $request->rawBody();
$data = YarProtocol::unpack($raw);
$ret = call_user_func_array([self::$executor, $data['method']], $data['params']);
$body = YarProtocol::pack($ret, $packager);
return response($body, 200, [
'Content-Type' => 'application/octet-stream',
'X-YAR-Packager' => $packager,
]);
} catch (\Throwable $e) {
return response(YarProtocol::pack(['error'=>$e->getMessage()], $packager),
200, ['Content-Type'=>'application/octet-stream']);
}
}
/** 浏览器 GET 访问时,返回 HTML API 列表 */
public static function info(Request $request): Response
{
$ref = new \ReflectionObject(self::$executor);
$html = '<h1>'.get_class(self::$executor).'</h1><ul>';
foreach ($ref->getMethods(\ReflectionMethod::IS_PUBLIC) as $m) {
if ($m->name[0] !== '_') {
$html .= '<li>'.$m->name.'('.implode(',', $m->getParameters()).')</li>';
}
}
return response($html.'</ul>');
}
}- 注册业务类(在
process/bootstrap.php或自定义启动文件里)
php
复制
use app\yar\YarServer;
use app\service\CalcService; // 你自己的业务类
YarServer::setExecutor(new CalcService());- 业务类示例
app/service/CalcService.php
php
复制
<?php
namespace app\service;
class CalcService
{
public function add($a, $b) { return $a + $b; }
public function mul($a, $b) { return $a * $b; }
}- 客户端调用(任意 yar-client / 上一问的纯 PHP 客户端均可)
php
复制
$cli = new Yar_Client('http://127.0.0.1:8787/yar');
echo $cli->add(3, 5); // 8要点回顾
- Webman 本身不需要装 yar 扩展,只要按协议收包/发包即可;
- 路由里把
/yar的 POST 流量交给YarServer::handle,其余流量正常走业务; - 打包格式只支持 msgpack/json 就足够与官方 yar-client 互通;
- 如果想暴露 TCP 版,把上面协议解析搬到
workerman/tcp-server的onMessage里即可,思路完全一样。