经过4、5天的修改调整,把底层架构稍稍改的合理了些,优化了下连接池和代理。 接下来就要完成最重要的工作了,服务注册与发现。
1. 什么是服务注册与发现呢?
网上相关资料还是比较丰富的,毕竟流行了一段时间了,各种文章教程解决方案都非常多。我的理解是,在微服务化的架构下,把各个功能和业务模块拆分得足够小和独立。 模块与模块之间通信的话,通过rpc或者restful接口进行数据调用。 而这些都需要走网络。 既然要走网络,就需要知道接口的 地址和端口。
我们来模拟下没有服务发现和注册的情况。 比如现在有个电商系统,把商品,订单,日志,商家拆分成独立的服务,有个人在网上下了订单, 请求到订单下单接口,这个时候,订单需要读取商品状态和库存,然后下订单,记录操作日志,通知商家。 那么我们就得把这些服务配置成一个数组写在文件里面
$servics = [
'shop' => '192.168.0.1:9001',
'goods' => '192.168.0.2:9002',
'order' => '192.168.0.3:9003'
];
而且这个配置要在每个服务上都要写上。 这个时候如果新加了服务,或者服务更换了地址和端口,或者goods服务压力特别大,新增了一台服务器部署,这个时候问题就来了。你要去所有服务里面更新全部配置。 传统模式下几种解决方法:文件共享、编写shell文件sync同步文件等等方式。维护成本高,而且微服务下,一般现在流行方案是配合docker完成自动部署和扩容。这个就让问题变得更复杂了。
所以为了解决这些问题,出现了服务注册与发现。
每个服务上线的时候,都去注册到同一个地方,然后每个服务定时或者及时同步到本地。 如果有变动则更新。这样就完成了服务的自动上线,下线。就是所谓的服务注册与发现。
经过网上资料整理,目前网上流行的解决工具有consul、zookeeper、etcd等,java的解决方案springcloud,dubbo等。
consul和zookeeper比较熟悉,各有优缺点,consul基于http,更松散和灵活,单实时性差一些,zookeeper需要装php扩展,看了下大部分php类似框架或者工具基本都使用的consul。关于consul的安装和使用可以看《微服务化二:consul》
需求和设计
但是,我想做一个再简单一点的,不依赖第三方的解决方案。然后再通过扩展,可以集成第三方现成工具。该如何下手呢?要实现这个需要解决以下问题:
- 服务如何存储
- 服务如何同步,状态检测,自动上下线
- 分布式服务中心如何处理,
为了使项目足够轻,需要找到最简单的解决方法,当然就意味着会损失一定的功能。但是如果扩展性足够好,可以通过第三方工具弥补,那么需要做一个能满足最简单的服务注册发现即可。按照这个设计思路和约定,大致设想方案如下:
1. 服务存储
存储使用文件存储,将服务存储成数组,结构大致如下
services.php
return [
'path' => [
['ip' => '', 'port' => '']
...
]
...
]
#example
return [
'/news' => [
['ip' => '192.168.0.100', 'port' => '9001'],
['ip' => '192.168.0.101', 'port' => '9001'],
],
'goods' => [
['ip' => '192.168.0.100', 'port' => '9002'],
['ip' => '192.168.0.101', 'port' => '9002'],
]
];
然后用另外一个文件servicesConfig.php 设置连接的数量和限制,manage同步自动这个文件。
2. 文件同步方案
主要通过服务提供的内部接口,进行配置同步,写入本地文件
3. 分布式服务中心
针对应用与不同场景,file存储版本,默认为单注册中心,分布式版本启用consul。
开发方案
- 提供独立注册中心服务,目前对接文件存储,监听内部端口,不开发外部访问。通过扩展可设置consul和zookeeper,代理注册接口,默认manage提供注册中心服务,可以独立部署启动.
- 每个服务提供2个服务端口,一个对外用于manage转发,一个内部(包括manage)配置服务注册中心地址,提供配置更新接口和服务间rpc调用。
- 服务注册:server和manage配置注册中心地址,启动server -> 自动上报server path,ip,端口
- 服务发现:1.注册中心收到上报数据,更新配置->广播通知服务列表更新配置, 2.注册中心定时检测服务列表里面地址的状态,无效的剔除,然后广播通知
- 分布式注册中心:分布式版本启用consul。
开发
ok,以这个方案进行开发,先要搭建注册中心
服务中心开发
主要提供服务上报和通知功能,本来想第一个版本注册中心和manager放一起。实际开发中,发现增加了manager的复杂度,为了保持应用简单,单独开发。
暂且命名为ra:registration authority
根据之前的分析,ra提供以下几个功能:
- 注册服务
- 通知更新
- 服务列表查询
大概的功能就是服务把自己的path,ip,port上报上来,ra找个地方存储起来,有更新,就发送UPDATE_SERVERS命令到所有服务拉取服务列表更新本地服务配置。
经过之前的重构,我们很容易就可以开发出一个带转发功能的服务,只不过这次我们的代理proxy是代理到本地的api文件。
ra还提供一个定时器,实时扫描服务列表,如果检测到服务有失效的,剔除后,通知每个服务拉去服务列表,更新本地列表。
这样我们就完成了一个简易的服务注册与发现的功能。当然聪明的你肯定发现了,这会出现单点问题,当然,这只是针对小规模应用情况下,分布式的注册中心,还是应该选用专业的工具,后期有时间会通过插件扩展支持consul等来完成
server服务发现sdk
server通过sdk,自动拦截请求,更新配置
开发辅助
在使用其他框架的时候,遇到其他接口,调用的时候会有个问题,ide没有提示。 当然也有很多解决方案,比如规范文档,比如加一个interface,需要用接口的地方下载一次interface. 这个在业务经常变更的时候显得非常麻烦。
我在想,服务注册的时候,能不能把对外的接口通过反射机制,把所有信息一起上报上去。然后注册中心生成一个.phpstorm.meta.php 的文件,自动记录所有接口和方法,然后开发模式下,可以通过命令行随时同步这个文件到服务里,这样开发的时候,自然就有了提示。 当然,这只是个初步设想。 后面待验证和实践
至此,我们一个简易的注册中心开发完成,代码详见:https://github.com/shulinqian/summer/tree/dev/suframe/ra
写个client测试一下,模拟服务注册
<?php
$arg = $argv[1] ?? 1;
$client = new swoole_client(SWOOLE_SOCK_TCP);
if (!$client->connect('127.0.0.1', 9500, -1))
{
exit("connect failed. Error: {$client->errCode}\n");
}
switch ($arg){
case 1:
$data = [
'api' => 'server/register',
'args' => [
'path' => '/news',
'ip' => '127.0.0.1',
'port' => rand(9000, 9010),
]
];
break;
case 2:
$data = [
'api' => 'server/get'
];
break;
}
$client->send(json_encode($data));
echo $client->recv(), "\n";
$client->close();
ok,接下来就应该开发服务了。继续努力吧