微服务治理-注册中心
一、介绍
1.1 什么是注册中心
注册中心可以理解为:
整个微服务系统的“通讯录”或“服务目录”。
在单体应用中:
Web
│
▼
Controller
│
▼
Service
│
▼
Repository
所有模块都运行在同一个进程中,方法之间直接调用: $user = $this->userService->getUser($id);
开发者无需关心:
- 服务在哪里?
- IP 是多少?
- 端口是多少?
因为它们都在同一个应用中。
而在微服务架构中:
User Service
10.0.0.10
│
Order Service ---------------- Product Service
10.0.0.11 10.0.0.12
现在 Order Service 想调用 User Service。
问题来了:Order Service 怎么知道 User Service 在哪里?
如果直接写 IP:虽然可以运行,但会产生很多问题。
1.2 为什么不能写死 IP?
假设:今天:User Service 10.0.0.10
后面因为扩容:User Service 10.0.0.20 或者增加服务器:10.0.0.xxx
那么:
所有调用 User Service 的项目:都要修改配置。
到底调用哪一个?
于是:
需要一个统一管理所有服务信息的地方。
这个地方就是:
注册中心(Registry)
1.4 注册中心解决什么问题?
1.统一管理服务地址
所有服务启动后都会向注册中心注册:
user-service
↓
10.0.0.5:9501
10.0.0.6:9501
10.0.0.7:9501
其他服务无需关心具体 IP,只需要知道服务名: user-service
2.动态更新
新增实例:10.0.0.8
自动加入注册中心。
无需修改业务代码。
3. 自动下线
服务器宕机:10.0.0.6
注册中心:
自动删除。
调用方不会再访问已经失效的实例。
4.为其它治理能力提供基础
很多微服务能力都依赖注册中心。
二、原理
2.1 注册中心整体架构
典型架构如下:
+----------------------+
| Service Registry |
| (Nacos/Consul) |
+----------+-----------+
▲
Register │ Register
│
+----------------+ │ +----------------+
| User Service |-----+-----| Order Service |
+----------------+ +----------------+
▲ │
│ │ Discover
│ ▼
Payment Service
流程:
- 服务启动。
- 向注册中心注册自己。
- 注册中心保存服务信息。
- 调用方从注册中心获取服务地址。
2.2 服务注册流程
以 User Service 为例:
User Service 启动
│
▼
读取配置
│
▼
获取本机 IP、端口
│
▼
发送 Register 请求
│
▼
Registry 保存:
user-service
10.0.0.5:9501
以后:
所有服务都能找到它。
2.3 注册信息包含什么?
通常包括:
| 字段 | 说明 |
|---|---|
| ServiceName | 服务名称 |
| InstanceId | 实例 ID |
| IP | 服务地址 |
| Port | 服务端口 |
| Version | 服务版本 |
| Weight | 权重 |
| Metadata | 元数据 |
| Status | 服务状态 |
例如:
{
"service": "user-service",
"ip": "10.0.0.5",
"port": 9501,
"version": "1.0.0",
"weight": 100,
"status": "UP"
}
2.4 注册中心如何保存数据?
可以理解为一个服务目录:
user-service
├── 10.0.0.5:9501
├── 10.0.0.6:9501
└── 10.0.0.7:9501
order-service
├── 10.0.0.10:9502
└── 10.0.0.11:9502
调用方查询:user-service
返回:
[ 10.0.0.5, 10.0.0.6, 10.0.0.7 ]
然后再由负载均衡选择一个实例。
2.5 服务生命周期
一个服务实例通常会经历:
启动
│
▼
注册(Register)
│
▼
心跳(Heartbeat)
│
▼
正常运行
│
▼
优雅下线(Deregister)
如果服务异常退出:
心跳停止
↓
注册中心检测超时
↓
自动移除实例
因此,心跳机制是注册中心的重要组成部分。
三、实现
3.1 最简单的注册中心(PHP 示例)
假设我们不用 Nacos,而是自己实现一个极简版注册中心。
服务注册:
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
class RegistryController
{
public function register(Request $request)
{
$service = $request->input('service');
$instance = [
'ip' => $request->input('ip'),
'port' => $request->input('port'),
'time' => time(),
];
$instances = Cache::get($service, []);
$instances[] = $instance;
Cache::put($service, $instances, now()->addMinutes(5));
return response()->json(['success' => true]);
}
}
这里只是演示原理,实际生产环境通常不会直接使用缓存存储服务目录。
3.2 主流注册中心对比
| 产品 | 语言生态 | 一致性协议 | 特点 |
|---|---|---|---|
| Nacos | Java、Spring、PHP 等 | AP(支持 CP 模式) | 注册中心 + 配置中心,国内应用广泛 |
| Consul | 多语言 | Raft(CP) | 服务发现、KV 配置、健康检查能力完善 |
| Eureka | Java | AP | Spring Cloud Netflix 经典方案,现已较少用于新项目 |
| Etcd | Go、Kubernetes | Raft(CP) | Kubernetes 默认存储,适合基础设施场景 |
对于 PHP 微服务来说,Nacos 和 Consul 是更常见的选择。
四、实践
1. 服务名称保持唯一
建议采用统一命名,例如:
user-service
order-service
inventory-service
payment-service
2. 不要使用固定 IP 调用
正确方式:
先通过注册中心获取可用实例,再进行调用。
3. 服务下线要优雅
实例准备下线时:
- 从注册中心注销(Deregister)。
- 等待当前请求处理完成。
- 再关闭进程。
避免请求打到即将退出的实例。
4. 注册信息不要过于复杂
注册中心保存的是服务元数据,而不是业务数据。
例如:
- 服务名
- IP
- 端口
- 权重
- 标签
- 版本
不要把用户信息、订单数据等业务内容放入注册中心。