本文Demo已上传到Github:
链接: hyperf-rpc-demo
为什么是RPC?而不是HTTP
1、简单来说,RPC没有那么多HTTP的Request 和Response Body 字段,比如:content-type、user-agent、accept-language、content-encoding、expires等字段。往往这些字段就占了报文的70%内容,如果请求次数过于频繁,可想而知对于性能和网络开销有多大。
2、而在内部服务器间相互通讯,我们完全可以减少握手次数
- 使用HTTP2.0,HTTP2.0可以在一个连接上发送多次数据,相比目前的http1.1模式,它还是非阻塞的
- 使用基于TCP的RPC协议,长连接传输数据
基于以上,微服务间使用HTTPv1.1的服务器间通讯并不是最好的方案,这就是为什么很多PHP框架基于PHP-FPM不适合做微服务的原因,因为PHP-FPM每个请求都会产生一个新的Woker进程,如果每个Worker都跑去与微服务建立HTTP1.1连接,那其实没有意义
为什么需要Consul?
consul是什么?为什么需要服务发现?看图就知道了
我在Server1部署了一套HTTP服务(服务消费者)对外提供接口,但是它同时也依赖Server2的服务(服务提供者),Server1不需要知道Server2的地址,只需要知道Consul这个服务者中心就行了,它会帮我注册所有的服务
这在现实中举个例子就是,一个大公司的Team A在做HTTP业务核心功能,Team B做了很多服务轮子,那Team A总不能每次开发都跑去Team B问:“你们的地址是多少啊?端口是多少啊? 服务名叫什么啊?告诉我好调用啊”。那如果要调用100个服务,岂不是Team A的人每次都要为了配置IP和端口忙死?
一、在Server 1搭建Consul
1、下载consul for linux,解压后就是一个可执行文件,直接mv到/usr/bin/consul
2、新建一个consul文件夹,在里面新建一个data文件夹和etc文件夹,data文件夹用于存放consul的数据文件,etc用于存放配置文件
3、切换到etc文件夹,新建一个web.json文件,内容如下:
{
"service": {
//这里写服务的ID,必须唯一
"id": "CalculatorService",
//这里写服务名称,一般也是ID名,非唯一
"name": "CalculatorService",
//注册服务,服务在哪台服务器上,就填写那台服务器IP,这里我填写server2的ip,因为是server2提供服务给server1调用
"address": "server2.ip.server2.ip",
//随便写
"tags": [
"webapi"
],
//同上,服务也有端口,就填写那台服务器提供的端口,这里填写server2的端口
"port": 9502
}
}
4、回到consul目录,启动consul,其中-config-dir指定刚刚的配置目录,-data-dir指定存放数据的目录
consul agent -dev -ui -config-dir=./etc -data-dir=./data -client=0.0.0.0
client=0.0.0.0这里需要注意下,网上一般的都是127.0.0.1,如果写成127.0.0.1同样可以运行,但是没办法在外网访问consul的UI界面,所以这里改成0.0.0.0
5、在浏览器输入IP:8500,就可以看到consul安装成功
二、在Server 2安装并设置hyperf服务提供者
1、在app目录下新建RPC文件夹,先新建一个CalculatorServiceInterface接口文件,文件名CalculatorServiceInterface.php
<?php
namespace App\Rpc;
interface CalculatorServiceInterface
{
public function add(int $a, int $b): int;
}
2、基于这个Interface,在该目录下添加一个实现方式的CalculatorService,文件名:CalculatorService.php
<?php
namespace App\Rpc;
use Hyperf\RpcServer\Annotation\RpcService;
/**
* 注意,如希望通过服务中心来管理服务,需在注解内增加 publishTo 属性
* @RpcService(name="CalculatorService", protocol="jsonrpc-http", server="jsonrpc-http" ,publishTo="consul")
*/
class CalculatorService implements CalculatorServiceInterface
{
public function add(int $a, int $b) : int
{
return $a +$b;
}
}
在这里使用了注解方式提供服务,name是服务名称,protocol指定使用了jsonrpc-http协议,这里加了publishTo选项,指定一个服务注册中心,因为之前已经搭建好了consul
3、打开config/autoload/server.php,在servers数组下,添加一个数组:
[
'name' => 'jsonrpc-http',
'type' => Server::SERVER_HTTP,
'host' => '0.0.0.0',
'port' => 9502,
'sock_type' => SWOOLE_SOCK_TCP,
'callbacks' => [
SwooleEvent::ON_REQUEST => [\Hyperf\JsonRpc\HttpServer::class, 'onRequest'],
],
],
这样hyperf就会把代码里的服务,以9502端口发布出去并运行,这里的端口,其实就与上面的consul配置文件web.json文件对应起来,从而使到consul能发现9502这个服务
三、在Server 1安装并设置hyperf服务消费者
1、我这里新建一个项目来表示消费者consumer服务模,同样在app目录下新建RPC文件夹,先新建一个CalculatorServiceInterface接口文件,文件名CalculatorServiceInterface.php
<?php
namespace App\Rpc;
interface CalculatorServiceInterface
{
public function add(int $a, int $b): int;
}
2、打开app/Controllers/IndexController.php
<?php
namespace App\Controller;
use App\Rpc\CalculatorServiceInterface;
use Hyperf\Di\Annotation\Inject;
use Hyperf\HttpServer\Annotation\AutoController;
/**
* Class IndexController
* @package App\Controller
* @AutoController()
*/
class IndexController extends AbstractController
{
/**
* //在这里我们注入了一个接口类,并直接调用接口的add方法,但在本项目并没有add
方法的实现,真正的add方法在服务提供者里已经实现了,hyperf会帮我们找到相应服务并使用
* @Inject()
* @var CalculatorServiceInterface
*/
private $calculatorService;
public function index()
{
return $this->calculatorService->add(1,2);
}
}
2、在config/autoload下新建services.php
<?php
return [
'consumers' => [
[
// name 需与服务提供者的 name 属性相同
'name' => 'CalculatorService',
// 服务接口名,可选,默认值等于 name 配置的值,如果 name 直接定义为接口类则可忽略此行配置,如 name 为字符串则需要配置 service 对应到接口类
'service' => \App\Rpc\CalculatorServiceInterface::class,
// 这个消费者要从哪个服务中心获取节点信息,如不配置则不会从服务中心获取节点信息
'registry' => [
'protocol' => 'consul',
//这里的address,表示consul搭建在那台服务器上,就填写哪台服务器的IP
'address' => 'http://127.0.0.1:8500',
],
]
],
];
这样当请求进来的时候,IndexController就会注入Service,hyperf就会告知这个service应该要去services.php去找到这个服务(也就是consul)
3、把config/autoload/server.php里的port改成9503,待会通过9503访问index控制器,这里只是演示,现实中你可以随意更改成你想访问的端口
'servers' => [
[
'name' => 'http',
'type' => Server::SERVER_HTTP,
'host' => '0.0.0.0',
'port' => 9503,
'sock_type' => SWOOLE_SOCK_TCP,
'callbacks' => [
SwooleEvent::ON_REQUEST => [Hyperf\HttpServer\Server::class, 'onRequest'],
],
],
],
四、跑起来
1、重新启动consul,如果之前启动过,先kill -9 consul的PID
consul agent -dev -ui -config-dir=./etc -data-dir=./data -client=0.0.0.0
2、切换到Server2的hyperf-provider,启动
php bin/hyperf.php start
3、切换到Server1的hyperf-consumer,启动
php bin/hyperf.php start
4、在浏览器打开Server1的HTTP 8500端口,查看consul界面,可以看到可以找到Server2下的hyperf的provider
5、在浏览访问一下Servier1的服务
可以看到Server1下的9503服务是正常的,它会访问Server1下的consul-8500中心,然后consul-8500中心会去访问Server2下的9502服务,再交还给Server1的9503
那这里就把整个简单的微服务体系完成了,写得不好大佬们见谅哈~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。