hyperf笔记
hyperf笔记
hyperf环境搭建
使用docker配置环境
docker run --name hyperf \
-v ~/pythonschool/hyperf:/data/project \
-p 9501:9501 -it \
--privileged -u root \
--entrypoint /bin/sh \
hyperf/hyperf:7.4-alpine-v3.11-swoole
cd /data
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer
composer create-project hyperf/hyperf-skeleton project
本地安装php-redis
wget http://pecl.php.net/get/redis-5.3.4.tgz
tar xf redis-5.3.4.tgz
cd redis-5.3.4
phpize
./configure
make
sudo make install
php -m | grep redis
vim /etc/php.ini
extension=redis.so
安装hyperf
用composer自动创建项目和执行脚本
composer config repo.packagist composer https://mirrors.aliyun.com/composer/
composer create-project hyperf/hyperf-skeleton
手动处理
echo {}>composer.json
composer config repo.packagist composer https://mirrors.aliyun.com/composer/
composer require hyperf/hyperf-skeleton
cp -R ./vendor/hyperf/hyperf-skeleton/ .
composer config repo.packagist composer https://mirrors.aliyun.com/composer/
composer update
// 一路回车,默认配置安装
cp .env.example .env
git add .
git commit -am "add hyperf"
如果端口被占用则修改端口,/config/autoload/server.php => port => 9502
// localhost:9502
php bin/hyperf.php start
hyperf调试
因为hyperf使用了swoole,使用常驻内存的方法,会导致代码修改好不能立即生效.
在本地调试的时候,可以更改一些配置.
/config/autoload/server.php => OPTION_MAX_REQUEST => 1
'max_request' => 1, // 设置worker进程的最大任务数
这样hyperf执行完一个任务后,会新建一个worker,代码就会被自动加载.
hyperf生命周期
Self-calling自调用函数闭包
当函数被创建的时候就会自动执行
(function($s) {
echo $s;
})('Hello Moments');
__invoke
当尝试以调用函数的方式调用一个对象时,__invoke()方法会被自动调用.
class Invoke
{
public function __invoke()
{
echo 'Hello Moments';
}
}
$invoke = new Invoke();
$invoke(); // 没有__invoke方法则会报错
AOP切面编程
通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术.
php bin/hyperf.php 命令来生成所有代理类
然后再更改config/config.php的SCAN_CACHEABLE为true
可以加快启动速度,减少内存消耗
自定义注释功能,注解
composer require doctrine/annotations
AnnotationRegistry::registerLoader('class_exists');
hyperf执行过程
// 加载composer组件包
require BASE_PATH . '/vendor/autoload.php';
// 切面编程代理类路径
$proxyFileDirPath = BASE_PATH . '/runtime/container/proxy/';
// 配置文件路径
$configDir = BASE_PATH . '/config/';
// 返回所有已注册的 __autoload() 函数
$loaders = spl_autoload_functions();
// 注册注解功能
AnnotationRegistry::registerLoader(function ($class) use ($composerClassLoader) {
return (bool) $composerClassLoader->findFile($class);
});
// 将composer的类加载改成hyperf
$loader[0] = new static($composerClassLoader, $proxyFileDirPath, $configDir);
// .env环境变量加载处理
$this->loadDotenv();
// 处理配置文件,可以使用class_map替换相应的类
// config/autoload/annotations.php => scan => class_map
$config = ScanConfig::instance($configDir);
$classLoader->addClassMap($config->getClassMap());
// 创建容器
$container = require BASE_PATH . '/config/container.php';
// ApplicationFactory进行注册然后返回Symfony\Component\Console\Application
$application = $container->get(Hyperf\Contract\ApplicationInterface::class);
$application->run();
debug模式下,控制台输出的信息
$container->get(Hyperf\Contract\ApplicationInterface::class);
Hyperf\Di\Container::class->make(Hyperf\Contract\ApplicationInterface::class);
Hyperf\Di\Resolver\FactoryResolver::class->resolve();
Hyperf\Framework\ApplicationFactory::class->__invoke($container);
$eventDispatcher->dispatch(new BootApplication());
Hyperf\Event\EventDispatcher::class->dispatch();
$this->dump($listener, $event);
$this->logger->debug(sprintf('Event %s handled by %s listener.', $eventName, $listenerName));
- 获取StartServer类的配置
// Hyperf\Di\ClassLoader::class->init()
$loader[0] = new static($composerClassLoader, $proxyFileDirPath, $configDir);
// Hyperf\Di\ClassLoader::class->__construct($composerClassLoader, $proxyFileDirPath, $configDir)
// Hyperf\Di\Annotation\ScanConfig
$config = ScanConfig::instance($configDir);
[$config, $serverDependencies, $cacheable] = static::initConfigByFile($configDir);
$configFromProviders = ProviderConfig::load();
// Hyperf\Config\ProviderConfig
static::$providerConfigs = static::loadProviders($providers);
$providerConfigs[] = (new $provider())();
// Hyperf\Server\ConfigProvider
'commands' => [
StartServer::class,
],
- 然后执行指定的命令类
$application = $container->get(Hyperf\Contract\ApplicationInterface::class);
return $this->resolvedEntries[$name] = $this->make($name);
// Hyperf\Di\Resolver\FactoryResolver
resolve()
$object = call($callable, [$this->container]);
// Hyperf\Framework\ApplicationFactory
__invoke()
$application = new Application();
// 到这里时已经把本地命令行添加到Symfony\Component\Console中
$application->add($container->get($command));
// bin/hyperf.php start
$application->run();
// Symfony\Component\Console::class->run();
// Symfony\Component\Console\Input\ArgvInput类对参数做了访问控制(私有),所以看不到
// $_SERVER['argv'][1]取出start命令
php bin/hyperf.php
框架初始化
php bin/hyperf.php start
框架初始化后,执行start
命令类StartServer
hyperf路由
基本上都延用了laravel的规范,虽然精简了很多,直接用就好.
Router::post('/post', 'App\Controller\IndexController::post');
自定义路由
config/autoload/annotations.php
将\Hyperf\HttpServer\Router\DispatcherFactory::class复制到class_map文件夹下即可.
'class_map' => [
\Hyperf\HttpServer\Router\DispatcherFactory::class => BASE_PATH . '/class_map/Hyperf/HttpServer/Router/DispatcherFactory.php'
]
使用注解定义路由
关闭config/config.php里的参数SCAN_CACHEABLE = false
新建的类更新一下composer以防找不到,composer dump
php bin/hyperf.php gen:controller AnnotateController
use Hyperf\HttpServer\Annotation\AutoController;
/**
* @AutoController()
*/
class AnnotateController
/**
* @Controller()
*/
class AnnotateController
/**
* @RequestMapping(path="index", methods="get,post")
*/
public function index(RequestInterface $request, ResponseInterface $response)
hyperf注意事项
尽量不要用全局变量,因为要自己手动释放.
使用上下文来进行逻辑处理.
使用注解一定要重启服务!
服务调用jsonrpc-http
基础实现
分成两个主体,服务提供者(TestService:9504),服务消费者(TestServiceConsumer:9502).
通过服务契约来定义和约束接口的调用(TestServiceInterface).
服务提供者:TestServiceInterface,TestService,config/autoload/server.php
服务消费者:TestServiceInterface,TestServiceConsumer(自动生成可省),config/autoload/services.php
路由指定节点信息(nodes),路由负载均衡(registry)
服务提供者
composer require hyperf/json-rpc
composer require hyperf/rpc-server
mkdir app/JsonRpc
// TestServiceInterface.php
// TestService.php
// 添加server:9504
@RpcService(name="TestService",protocol="jsonrpc-http",server="jsonrpc-http")
服务消费者
composer require hyperf/json-rpc
composer require hyperf/rpc-client
services.php->consumers->service(自动生成要指定接口)
服务网关
// http://localhost:9502/rpc
Router::get('/rpc', function(){
$client = \Hyperf\Utils\ApplicationContext::getContainer()->get(\App\JsonRpc\TestServiceInterface::class);
$result = $client->sum(11,22);
return 'rpc' . $result;
});
手动生成服务消费者
// TestServiceConsumer
// dependencies.php注入一下对象
config/autoload/services.php
'consumers' => [
[
'name' => 'TestService',
'nodes' => [
['host' => '127.0.0.1', 'port' => 9504]
]
]
]
路由负载均衡
服务提供者(publishTo)->服务注册(consul)
服务消费者(registry)->负载均衡(consul)
服务调用grpc
需要安装如下软件.
- php-grpc扩展
- php-protobuf扩展
- protoc命令行工具
- grpc_php_plugin模板生成工具
- swoole开启openssl,http2
protoc生成规范
根目录下新建模板文件,grpc.proto
vim grpc.proto
syntax = "proto3";
package grpc;
service Hi {
rpc sayHello (HiUser) returns (HiReply) {}
}
message HiUser {
string name = 1;
}
message HiReply {
string message = 1;
}
// 根目录添加目录,并加入命名空间
mkdir grpc
// 只生成接口规范
protoc --php_out=grpc/ grpc.proto
// 生成php类模板
protoc -I=. grpc.proto --proto_path=grpc/ --php_out=grpc/ --grpc_out=grpc/ --plugin=protoc-gen-grpc=../grpc_php_plugin
grpc目录结构
grpc
├── GPBMetadata
│ └── Grpc.php
└── Grpc
├── HiClient.php // 由grpc_php_plugin生成
├── HiReply.php
└── HiUser.php
添加命名空间
"autoload": {
"psr-4": {
"App\\": "app/",
"GPBMetadata\\": "grpc/GPBMetadata",
"Grpc\\": "grpc/Grpc"
},
修改grpc/Grpc/HiClient.php
的基类为Hyperf\GrpcClient\BaseClient;
class HiClient extends \Grpc\BaseStub
==>
class HiClient extends BaseClient
服务提供者
composer require hyperf/grpc-server
添加服务器配置:9505
[
'name' => 'grpc',
'type' => Server::SERVER_HTTP,
'host' => '0.0.0.0',
'port' => 9505,
'sock_type' => SWOOLE_SOCK_TCP,
'callbacks' => [
Event::ON_REQUEST => [Hyperf\GrpcServer\Server::class, 'onRequest'],
],
]
配置控制器
php bin/hyperf.php gen:controller HiController
app/Controller/HiController.php
use Grpc\HiReply;
use Grpc\HiUser;
class HiController
{
public function sayHello(HiUser $user)
{
$message = new HiReply();
$message->setMessage(sprintf('Hello %s(%s)', $user->getName(), time()));
return $message;
}
}
配置路由,要注意大小写,以protoc生成的接口文档为准.
在HiClient中,如'/grpc.Hi/sayHello'.
Router::addServer('grpc', function () {
Router::addGroup('/grpc.Hi', function () {
Router::post('/sayHello', [App\Controller\HiController::class, 'sayHello']);
});
});
服务消费者
composer require hyperf/grpc-client
直接在路由里测试,
Router::get('/grpc', function () {
$client = new \Grpc\HiClient('127.0.0.1:9505', ['credentials' => null]);
$request = new \Grpc\HiUser();
$request->setName('hyperf');
/**
* @var \Grpc\HiReply $reply
*/
list($reply, $state) = $client->sayHello($request);
return $reply->getMessage();
});
测试
php bin/hyperf.php s
服务中心
必须要先了解微服务再去看hyperf框架,毕竟是国内开源的可以参考了解一下,辅助学习,看一下此框架如何整合微服务的组件.
不要光看文档,文档不会面面俱到,先看原理,多搜索,借用框架实际操作一下.
安装服务中心
brew tap hashicorp/tap
brew install hashicorp/tap/consul
consul -v
运行服务中心
consul agent -http-port=8500 -dev
http://localhost:8500/
配置服务中心
composer require hyperf/service-governance
composer require hyperf/consul
composer require hyperf/guzzle
config/autoload/consul.php
php bin/hyperf.php vendor:publish hyperf/consul
return [
'uri' => 'http://127.0.0.1:8500',
];
发布服务到服务中心publishTo="consul"
/**
* @RpcService(name="TestService",protocol="jsonrpc-http",server="jsonrpc-http",publishTo="consul")
*/
class TestService implements TestServiceInterface
config/autoload/services.php
'registry' => [
'protocol' => 'consul',
'address' => 'http://127.0.0.1:8500',
],
测试地址 http://127.0.0.1:9502/consul
consul键值对存储
Router::get('/consul', function(){
$container = \Hyperf\Utils\ApplicationContext::getContainer();
$clientFactory = $container->get(\Hyperf\Guzzle\ClientFactory::class);
$consulServer = 'http://127.0.0.1:8500';
// consul键值对存储
$kv = new \Hyperf\Consul\KV(function () use ($clientFactory, $consulServer) {
return $clientFactory->create([
'base_uri' => $consulServer,
]);
});
$kv->put('consul', 'Hello World!');
$result = $kv->get('consul')->getBody()->read(1024);
$kv->delete('consul');
var_dump(base64_decode(json_decode($result)[0]->Value));
return 'consule ' . $result;
});
代理操作
$agent = new \Hyperf\Consul\Agent(function () use ($clientFactory, $consulServer) {
return $clientFactory->create([
'base_uri' => $consulServer,
]);
});
var_dump($agent->services()->getBody()->read(1024));
return $agent->services()->getBody();
{
"TestService-0": {
"ID": "TestService-0",
"Service": "TestService",
"Tags": [],
"Meta": {
"Protocol": "jsonrpc-http"
},
"Port": 9504,
"Address": "192.168.1.5",
"SocketPath": "",
"TaggedAddresses": {
"lan_ipv4": {
"Address": "192.168.1.5",
"Port": 9504
},
"wan_ipv4": {
"Address": "192.168.1.5",
"Port": 9504
}
},
"Weights": {
"Passing": 1,
"Warning": 1
},
"EnableTagOverride": false,
"Datacenter": "dc1"
}
}