在 windows 应用 docker 环境搭建 hyperf 微服务
创立 hyperf 我的项目环境
docker run -d --name hyperfrpc -v d/dataproject/hyperfrpc:/hyperf-skeleton -p 9501:9501 -it --entrypoint /bin/sh hyperf/hyperf:7.4-alpine-v3.11-swoole
参数解释:
-d 示意在后盾运行
–name 示意给容器取个名字
-v 示意挂载到本机的目录
-p 示意映射到主机的端口,冒号前的是本机,前面是 docker 容器端口
-it 终端
–entrypoint 通过这个来笼罩 dockerfile 里定义的 entrypoint
镜像容器运行后,而后进入容器中,在容器内装置 Composer
# 下载 composer.phar 文件
wget https://github.com/composer/composer/releases/download/1.8.6/composer.phar
#给 composer.phar 文件加可执行权限
chmod u+x composer.phar
#挪动该文件到 /usr/local/bin/composer
mv composer.phar /usr/local/bin/composer
将 Composer 镜像设置为阿里云镜像,减速国内下载速度
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer
通过 Composer 装置 hyperf/hyperf-skeleton 我的项目
composer create-project hyperf/hyperf-skeleton
进入装置好的 Hyperf 我的项目目录,linux 目录
cd hyperf-skeleton
测试 Hyperf 环境是否胜利
php bin/hyperf.php start
在 powshell 关上一个 cli
docker exec -it hyperfrpc /bin/bash
进行测试
curl 127.0.0.1:9501
看到输入
{"method":"GET","message":"Hello Hyperf."}
环境胜利
在 hyperf-skeleton 目录下创立消费者和提供者
删除刚刚创立的 hyperf 我的项目
rm -rf hyperf-skeleton
创立 provider
composer create-project hyperf/hyperf-skeleton hyperf-skeleton/provider
创立时,选项只抉择一个 JSON-PRC with Service Governance,其余都选 n
创立 consumer
cd hyperf-skeleton
复制 provider 为 consumer
cp -r provider consumer
在 windows 应用编辑器编辑 provider
随便应用一个编辑器而后关上我的项目,目录是你创立 docker 容器时挂载的中央,咱们下面挂载了 d /dataproject/hyperfrpc
咱们能够在目录看到存在 provider、consumer
编写 provider
咱们在 app 目录下创立一个文件夹,叫 Rpc,在 Rpc 下创立 CalculatorService 和 CalculatorServiceInterface
服务端口
CalculatorServiceInterface 是服务接口
<?php
namespace AppRpc;
interface CalculatorServiceInterface
{public function add(int $a, int $b);
public function minus(int $a, int $b);
}
服务实现类
CalculatorService 就是咱们服务提供者的服务实现类, 咱们只须要服务实现类上增加注解 @RpcService 就能够将服务公布
name,给服务起个名字
protocol,应用的协定,这里个别应用 jsonrpc-http
server,绑定的 server
<?php
namespace AppRpc;
use HyperfRpcServerAnnotationRpcService;
/**
* Class CalculatorService
* @package AppRpc
* @RpcService(name="CalculatorService",protocol="jsonrpc-http",server="jsonrpc-http")
*/
class CalculatorService implements CalculatorServiceInterface
{
public function add(int $a,int $b)
{return $a+$b;}
public function minus(int $a,int $b)
{return $a-$b;}
}
增加对应 server
接下来,咱们去 config/autoload/server.php 下,增加刚刚在注解 @RpcService 上写的 server=“jsonrpc-http”的这个 server
咱们找到 servers
而后发现曾经存在一个 name 是 http 的 server 了,咱们复制一个 name 是 http 的 server,之后增加在 http 这个 server 的上面,而后批改一下
咱们把 name 批改成 jsonrpc-http
咱们把 port 批改成 9502
咱们把 callbacks 批改一下,把 HyperfHttpServerServer::class 批改成 HyperfJsonRpcHttpServer::class, 如上面
'servers' => [
[
'name' => 'http',
'type' => Server::SERVER_HTTP,
'host' => '0.0.0.0',
'port' => 9501,
'sock_type' => SWOOLE_SOCK_TCP,
'callbacks' => [Event::ON_REQUEST => [HyperfHttpServerServer::class, 'onRequest'],
],
],
[
'name' => 'jsonrpc-http',
'type' => Server::SERVER_HTTP,
'host' => '0.0.0.0',
'port' => 9502,
'sock_type' => SWOOLE_SOCK_TCP,
'callbacks' => [Event::ON_REQUEST => [HyperfJsonRpcHttpServer::class, 'onRequest'],
],
],
编写 consumer
咱们就在 consumer 我的项目的 controller 调用一下刚刚写的 CalculatorService 把。
对应接口
在调用之前咱们须要把服务接口从 provider 哪里复制一份过去
咱们在 consumer 的 app 目录下创立 Rpc 目录,在 Rpc 目录下创立刚刚复制的 CalculatorServiceInterface
<?php
namespace AppRpc;
interface CalculatorServiceInterface
{public function add(int $a, int $b);
public function minus(int $a, int $b);
}
应用 controller 调用
咱们就在 IndexController 外面的 index 办法调用服务, 然而当初还无奈生产胜利,咱们还须要一些配置。
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace AppController;
use HyperfDiAnnotationInject;
class IndexController extends AbstractController
{
/**
* @Inject
* @var AppRpcCalculatorServiceInterface
*/
private $calculatorService;
public function index()
{return $this->calculatorService->add(1,2);
}
}
创立 services 的配置文件
咱们须要去到 /config/autoload 目录下创立 services.php 文件,文件配置如下
<?php
use AppRpcCalculatorServiceInterface;
return [
'consumers'=>[
[
'name'=>'CalculatorService',
'service'=>CalculatorServiceInterface::class,
'nodes'=>[['host'=>'127.0.0.1','port'=>9502],
],
],
],
];
这个服务的 name 和咱们编写注解的 name 是统一的
服务的 service 对应 consumer 的 CalculatorServiceInterface,
因为咱们没有应用注册核心,所以咱们间接应用节点地址,对应下面代码中的 nodes
批改 consumer 的启用端口
因为 consumer 复制过去的,所以咱们须要批改一下端口
在 /config/autoload 下的 server.php 文件下
咱们找到 server,定位到 name 是 http 的 server,将端口批改成 9503
'servers' => [
[
'name' => 'http',
'type' => Server::SERVER_HTTP,
'host' => '0.0.0.0',
'port' => 9503,
'sock_type' => SWOOLE_SOCK_TCP,
'callbacks' => [Event::ON_REQUEST => [HyperfHttpServerServer::class, 'onRequest'],
],
],
],
consumer 编写实现
启动 provider
进入终端
cd hyperf-skeleton/
cd provider/
php bin/hyperf.php start
启动 consumer
cd hyperf-skeleton/
cd consumer/
php bin/hyperf.php start
发动申请
在关上一个终端
curl 127.0.0.1:9503
咱们应该能够看到输入了
3
应用 consul
咱们这里应用的是 consul
咱们在终端 provider 目录试下装置 hyperf 的服务治理
composer hyperf/service-governance
装置 consul 组件
composer require hyperf/consul
公布一下对应的配置文件
php bin/hyperf.php vendor:publish hyperf/consul
咱们能够看到 consul 的配置文件就曾经生成胜利了
能够到 cogfig/autoload 目录下的 consul.php 文件查看
return [
'uri' => 'http://127.0.0.1:8500',
'token' => '',
];
默认是到本地的 8500 端口
下载并启动 consul 服务
# 下载并解压 consul
cd /opt/
mkdir consul
chmod 777 consul
cd consul
wget https://releases.hashicorp.com/consul/1.3.0/consul_1.3.0_linux_amd64.zip
unzip consul_1.3.0_linux_amd64.zip
cp consul /usr/local/bin/
#查看是否装置胜利
consul
consul version
#启动 consul 服务
consul agent -dev -ui -node=consul-dev -client=127.0.0.1
#3. 再开一个终端,输出查看 8500 端口
curl 127.0.0.1:8500
#看见 <a href="/ui/">Moved Permanently</a>. 就胜利了
将 provider 的实现类注册进 consul
在注解上增加 publishTo=”consul”
<?php
namespace AppRpc;
use HyperfRpcServerAnnotationRpcService;
/**
* Class CalculatorService
* @package AppRpc
* @RpcService(name="CalculatorService",protocol="jsonrpc-http",server="jsonrpc-http",publishTo="consul")
*/
class CalculatorService implements CalculatorServiceInterface
{
public function add(int $a,int $b)
{return $a+$b;}
public function minus(int $a,int $b)
{return $a-$b;}
}
将 consumer 的 services 批改
这里的 services 是 config/autoload 下的
<?php
use AppRpcCalculatorServiceInterface;
return [
'consumers'=>[
[
'name'=>'CalculatorService',
'service'=>CalculatorServiceInterface::class,
'registry'=>[
'protocol'=>'consul',
'address'=>'http://127.0.0.1:8500',
],
// 'nodes'=>[// ['host'=>'127.0.0.1','port'=>9502],
// ],
],
],
];
registry 将去 consul 外面取服务,咱们须要先将 nodes 正文
重启 provider
CTRL+C
php bin/hyperf.php start
重启 consumer
CTRL+C
php bin/hyperf.php start
终端测试
curl 127.0.0.1:9503
#能够看到还是输入,咱们曾经把 nodes 正文了
3
服务熔断
为什么要熔断
分布式系统中常常会呈现因为某个根底服务不可用造成整个零碎不可用的状况,这种景象被称为服务雪崩效应。为了应答服务雪崩,一种常见的做法是服务降级。
比方咱们须要到另外服务中查问用户列表,用户列表须要关联很多的表,查问效率较低,但平时并发量不高的时候,响应速度还说得过去。一旦并发量激增,就会导致响应速度变慢,并会使对方服务呈现慢查。
在比方咱们进行一些 Db 查问的时候,须要查问多个表,在这种状况下,平时并发量不高的时候,相应速度还能够说得过去。一旦并发量激增得时候,就可能呈现服务不可用的状况,最初导致整个零碎都不可用,这种景象被称为服务雪崩效应。为了应答服务雪崩,一种常见的做法是服务降级。
降级是指本人的待遇降落了,从 RPC 调用环节来讲,就是去拜访一个本地的伪装者而不是实在的服务。
应用熔断器
熔断器的应用非常简略,只须要退出 HyperfCircuitBreakerAnnotationCircuitBreaker
注解,就能够依据规定策略,进行熔断。
这个时候,咱们只须要配置一下熔断超时工夫 timeout
为 0.05 秒,失败计数 failCounter
超过 1 次后熔断,相应 fallback
为 AppServiceUserService
类的 searchFallback
办法。这样当响应超时并触发熔断后,就不会再申请对端的服务了,而是间接将服务降级从以后我的项目中返回数据,即依据 fallback
指定的办法来进行返回。
创立应用熔断器的 Service
咱们在服务提供者 provider 里的 appService 下创立一个 UserService 来进行应用,
首先,咱们先在 APP 目录下创立目录 Service
而后再 Service 目录下创立 UserService,具体如下:
<?php
namespace AppService;
use HyperfCircuitBreakerAnnotationCircuitBreaker;
class UserService
{
/**
* @return string[]
* @CircuitBreaker(timeout="0.05",failCounter=1,successCounter=1,fallback="AppSerViceUserService::searchFallback")
*/
public function search()
{for ($i=1;$i<=3;$i++){sleep(1);
$a=$i;
print $a."n";
}
return ['message'=>"失常",];
}
public static function searchFallback()
{
return ['message'=>"流量过大,熔断",];
}
}
在咱们须要的服务上加上 @CircuitBreaker 这个注解就能够进行服务熔断了,接下来我阐明一下几个参数
timeout=”0.05″ 熔断超时工夫
failCounter= 1 失败记数,超过次数后熔断
successCounter= 1 胜利记数
fallback=”AppSerViceUserService::searchFallback” 发送熔断后调用的办法
在控制器中应用
咱们编写一个控制器,就叫做 UserController 吧。
咱们在 UserController 中注入刚刚编写的 UserService,我为了不便就没有形象成接口了
而后再 UserController 外面编写一个办法来调用 UserService 的办法
代码如下:
<?php
namespace AppController;
use HyperfCircuitBreakerAnnotationCircuitBreaker;
use HyperfDiAnnotationInject;
use HyperfHttpServerAnnotationAutoController;
use AppServiceUserService;
/**
* Class UserController
* @package AppController
* @AutoController()
*/
class UserController extends AbstractController
{
/**
* @Inject
* @var UserService
*/
private $UserService;
public function search()
{return $this->UserService->search();
}
}
测试
因为咱们之前在创立 docker 绑定了 9501,所以咱们在 windows 上能够用浏览器拜访 9501 端口
咱们关上浏览器
拜访, 多刷新几次就能够看见服务熔断这个返回,或者你关上 2 个拜访窗口
`
127.0.0.1:9501/user/search
`
或者咱们能够在终端上拜访,不过须要屡次拜访
curl curl 127.0.0.1:9501/user/search
curl curl 127.0.0.1:9501/user/search
curl curl 127.0.0.1:9501/user/search
服务限流
限流的目标是通过对并发拜访 / 申请进行限速或者一个工夫窗口内的申请进行限速爱护零碎,一旦达到限度速率就能够拒绝服务(定向到谬误页或告知资源没有了),排队等待(比方秒杀、评论和下单)、降级(返回兜底数据和默认数据,如商品详情页默认有货)。上面咱们将应用到令牌桶限流器。
装置
咱们先进入 provider 目录
`
composer require hyperf/rate-limit
`
公布配置
php bin/hyperf.php vendor:publish hyperf/rate-limit
应用
<?php
namespace AppController;
use HyperfDiAopProceedingJoinPoint;
use HyperfHttpServerAnnotationController;
use HyperfHttpServerAnnotationRequestMapping;
use HyperfRateLimitAnnotationRateLimit;
/**
* @Controller(prefix="rate-limit")
* @RateLimit(limitCallback={RateLimitController::class, "limitCallback"})
*/
class RateLimitController
{
/**
* @RequestMapping(path="test")
* @RateLimit(create=1, capacity=3)
*/
public function test()
{return ["QPS 1, 峰值 3"];
}
public static function limitCallback(float $seconds, ProceedingJoinPoint $proceedingJoinPoint)
{
// $seconds 下次生成 Token 的距离, 单位为秒
// $proceedingJoinPoint 此次申请执行的切入点
// 能够通过调用 `$proceedingJoinPoint->process()` 继续执行或者自行处理
echo "限流中";
return $proceedingJoinPoint->process();}
}
create
1
每秒生成令牌数
consume
1
每次申请耗费令牌数
capacity
2
令牌桶最大容量
limitCallback
[]
触发限流时回调办法
waitTimeout
1
排队超时工夫
参考文档:
https://hyperf.wiki/2.1/#/zh-cn/microservice
https://blog.csdn.net/weixin_40670060/article/details/109582821