浏览了大量关于php 的rpc,但是多数为原生php实现rpc的demo。虽然很多人都知道rpc 的原理,但是对于编写代码并没有一个直观的感受,所以这次手写一个自定义rpc框架,基于使用tp6来完成rpc框架。这次以代码为主,中间使用的一些技术分享,理论性的东西不在一一概括,用最简单的话来解释其中很多技术!(如果对rpc不太了解的话,请翻上一篇文章)
概括
简述:client端调用server端 如果server端的代码为本地则是本地调用,如果server端的代码在另外一台机器就需要远程调用(Rpc协议)
本地调用(伪代码)
<?php
#如果代码在本地(同一台机器,同一个项目中) 我们就可以通过new(实例化)的方式来进行调用
class Client
{
public function A()
{
return (new Server())->B();
}
}
class Server
{
public function B(){
return '我是服务端,代码在本地';
}
}
远程调用
假设:client端 在 ip 192.168.1.2的机器上
server端 在 ip 192.168.1.3的机器上
如果我们还是想通过new这种方式去进行调用的话,那么就需要远程调用
远程调用伪代码
#client ip 192.168.1.2
<?php
class Client {
public function A()
{
#最终我们要完成的也是实例化RpcClient去调用Server服务的B方法
return (new RpcClient())->Service('Server')->B();
}
}
#server ip 192.168.1.3
<?php
class Server
{
public function B()
{
return '我是服务端,代码在远程';
}
}
RpcClient即为Rpc框架的客户端
原理
下图为我们要完成的框架需要做的步骤,以及远程调用的步骤(根据序号):
在client端发起一个远程伪代码中(new RpcClient())->Service('Server')->B();
-
通过实例化框架RpcClient将发起代理服务
-
将Server和B方法,以及B方法可能带有的参数序列化
-
通过swoole把序列化的消息发送给服务端
-
服务端接收消息并反序列化
-
通过反射调用调用服务端的Server类下的B方法
-
服务端Server类B方法返回的结果序列化
-
将返回的序列化结果通过swoole发送给客户端
-
客户端通过反序列化得到结果
在上述的过程中省略了服务注册。保留了最真实的序列化和通信过程。
所以们要完成的功能就是
通信、序列化、调用这三个最主要的逻辑,接下来我们一一实现。
准备阶段
-
php版本>= 7.4、安装swoole拓展(php 7.4 默认安装4.4以上版本的拓展)
-
需要安装composer
-
准备两个tp6项目分别命名为
tp6-client
和tp6-server
-
需要开通
9008
端口(端口自定义,本使用的是9008) -
可以通过http也可以访问到
目标
-
实现swoole通信
-
序列化和反序列化
-
反射调用并返回结果
目录结构
/tianyu (自定义目录名)
├── src
│ ├── command (自定义命令)
│ │ └── Server.php (根据tp6命令行自行创建的命令启动文件,启动swoole)
│ ├── common
│ │ ├── Buffer.php 缓冲器
│ │ ├── Packer.php 定义包头
│ │ ├── Paser.php 解析协议
│ │ └── Protocol.php 协议封装
│ ├── reflection
│ │ └──Contaoner.php 自定义容器
│ ├── register.php 注册中心文件
│ ├── RpcClient.php 客户端
│ ├── RpcServer.php 服务端
│ └── Server.php tp6命令行注册文件
└── composer.json compsoer文件
一、安装tp6框架
composer create-project topthink/think tp-client
composer create-project top
二、使用composer开发自定义框架,自定义composer.json
为什么使用composer开发?
ThinkPHP6.0遵循PSR-2命名规范和PSR-4自动加载规范,并且注意如下规范:
目录和文件
- 目录使用小写+下划线;
- 类库、函数文件统一以`.php`为后缀;
- 类的文件名均以命名空间定义,并且命名空间的路径和类库文件所在路径一致;
- 类(包含接口和Trait)文件采用驼峰法命名(首字母大写),其它文件采用小写+下划线命名;
- 类名(包括接口和Trait)和文件名保持一致,统一采用驼峰法命名(首字母大写);
函数和类、属性命名
- 类的命名采用驼峰法(首字母大写),例如 `User`、`UserType`;
- 函数的命名使用小写字母和下划线(小写字母开头)的方式,例如 `get_client_ip`;
- 方法的命名使用驼峰法(首字母小写),例如 `getUserName`;
- 属性的命名使用驼峰法(首字母小写),例如 `tableName`、`instance`;
- 特例:以双下划线`__`打头的函数或方法作为魔术方法,例如 `__call` 和 `__autoload`;
composer开发一般情况下使用composer命令,不过这次先使用手动更改目录文件的形式将自定义框架加载到composer中
根据上述开发规范,应该将自定义框架目录放在vendor目录下,在composer.json中配置自己的命名空间
composer.json文件如下:
{
"name": "tianyu-rpc/tianyu-rpc",
"description": "description",
"minimum-stability": "stable",
"license": "proprietary",
"authors": [
{
"name": "45576",
"email": "email@example.com"
}
],
"require": {
"php": ">=7.4",
"ext-json": "*",
"swoole/ide-helper": "^4.3"
},
"autoload": {
"psr-4": {
"tianyu\\": "src"
}
}
}
最重要的是autoload
下psr-4
,定义自己的命名空间
然后为了将自己的命名空间加入到composer包管理中还需要如下的几个步骤:
1、在 根目录/vendor/composer
目录下找到autoload_psr4.php
(注意以下注释内容)
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'think\\trace\\' => array($vendorDir . '/topthink/think-trace/src'),
'think\\' => array($vendorDir . '/topthink/think-helper/src', $vendorDir . '/topthink/think-orm/src', $vendorDir . '/topthink/framework/src/think'),
'app\\' => array($baseDir . '/app'),
'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'),
'Symfony\\Polyfill\\Php72\\' => array($vendorDir . '/symfony/polyfill-php72'),
'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
'Symfony\\Component\\VarDumper\\' => array($vendorDir . '/symfony/var-dumper'),
'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'),
'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
'Psr\\Cache\\' => array($vendorDir . '/psr/cache/src'),
'League\\MimeTypeDetection\\' => array($vendorDir . '/league/mime-type-detection/src'),
'League\\Flysystem\\Cached\\' => array($vendorDir . '/league/flysystem-cached-adapter/src'),
'League\\Flysystem\\' => array($vendorDir . '/league/flysystem/src'),
//只需要加入我们自己的命名空间和目录,其余不要无脑复制
'tianyu\\' => array($vendorDir . '/tianyu/src'),
);
2、在 根目录/vendor/composer
目录下找到autoload_static.php
(注意以下注释内容)
需要修改两个地方$prefixLengthsPsr4
、$prefixLengthsPsr4
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInit6e727dc751020a5cd17c7dc2b4c706c7
{
public static $files = array (