微服务开发系列:开篇
微服务开发系列:为什么选择 kotlin
微服务开发系列:为什么用 gradle 构建
微服务开发系列:目录结构,保持整洁的文件环境
微服务开发系列:服务发现,nacos 的小补充
微服务开发系列:怎样在框架中选择开源工具
微服务开发系列:数据库 orm 使用
微服务开发系列:如何打印好日志
微服务开发系列:鉴权
微服务开发系列:认识到序列化的重要性
微服务开发系列:设计一个统一的 http 接口内容形式
微服务开发系列:利用异常特性,把异常纳入框架管理之中
微服务开发系列:利用 knife4j,生成最适合微服务的文档
服务发现
框架中的服务发现,使用的是 nacos 提供的。
nacos 提供的域的概念非常好用,能够很方便的将开发环境和本地环境区分来开,有助于接口调试。
但是实现上也有一些缺点,那就是网关无法获取到服务注册的事件,而且当服务启动时,网关会有几秒到十几秒的时间,才能够发现注册的新服务,此时,才能够转发请求。
对于网关无法获取到服务注册事件,Eureka
提供了 EurekalnstanceRegisteredEvent
、EurekalnstanceCanceledEvent
等事件可以判断。
但是我没有在 nacos 下找到类似的方法,对此一开始我的解决方案是,自己做一个检测服务类 cn.gateway.core.InstanceDetect
,将已经注册上的服务保存,之后定时扫描,和保存的服务对比,如果新增就是注册成功,如果不存在就是取消注册。
不过最后,还是找到了相对完美的解决方法,通过 NacosServiceManager
,在 InstanceRegisteredEvent
事件触发后,也就是网关本身注册到 nacos 后,nacosServiceManager.getNamingService(null)
这段代码才能够获取到 namingService
对象。
最后就可以通过 GatewayProperties
获取到所有的服务名称,再进行对应的订阅即可。
能够实现这一点,还是因为 nacos 和 gateway 都是基于 spring.application.name
作为注册服务和转发服务的依据。
@Service
class InstanceDetect(
private val nacosServiceManager: NacosServiceManager,
private val gatewayProperties: GatewayProperties
) {
@EventListener
fun onApplicationEvent(event: InstanceRegisteredEvent<*>) {
val namingService = nacosServiceManager.getNamingService(null)
gatewayProperties.routes.forEach {
val serviceInstanceMap = mutableMapOf<String, Instance>()
namingService.subscribe(it.id) { event ->
if (event !is NamingEvent) {
return@subscribe
}
val serviceInstances = HashSet(serviceInstanceMap.keys)
event.instances.forEach { instance ->
if (serviceInstances.contains(instance.instanceId)) {
serviceInstances.remove(instance.instanceId)
} else {
serviceInstanceMap[instance.instanceId] = instance
log.info(
"instance connected : {}, ip : {}, port : {}",
instance.instanceId, instance.ip, instance.port
)
}
}
for (removedInstance in serviceInstances) {
val remove = serviceInstanceMap.remove(removedInstance)!!
log.info(
"instance disconnected : {}, ip : {}, port : {}",
removedInstance, remove.ip, remove.port
)
}
}
}
}
}
除此之外,客户端也提供了 RegisterEventListener
这个类,当客户端注册到 nacos 成功时,打印注册成功信息。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。