NodeJs服务注册与服务发现实现
前言
由于作者才刚开始学习NodeJs,水平实在有限,本文更像是一篇学习笔记,适合同刚开始学习NodeJs的朋友阅读。
服务治理
如果你的团队正在探索微服务的搭建,那么你们可能就在寻找一种机制,这个机制让每个服务能动态的创建地址,同时调用方要能感知到这些服务地址的动态变化。服务注册与服务发现就是这其中一种机制,大概的流程为:
其中:
- 注册中心:zookeeper
- 服务提供者:NodeJs应用服务
- 服务消费者:NodeJs API Gateway
ZooKeeper服务注册中心
ZooKeeper的身份是管理者,它是一个分布式数据一致性的解决方案,分布式任务可以基于它实现数据的发布与订阅、负载均衡、命名服务、分布式协调与通知、集群管理、领导选举、分布式锁、分布式队列等。本文并不会对所有的方面都展开讲解,因为作者也还没涉及到。我们的目标是利用ZooKeeper来实现一个服务的注册中心,如果你感兴趣,可以自己去研究看看,后面我研究了会再来分享的。
1、利用树状模型构建服务地址存储数据结构
zk内部有一个树状的内存模型,类似于文件系统,有若干目录,每个目录又可以有若干文件夹、文件,如下图:
zk有4种节点(会话指客户端连接zk的长连接):
- 持久节点:当会话结束时,节点不会被删除
- 持久顺序节点:当会话结束时,节点不会删除,节点名自带增数后缀
- 临时节点:当会话结束时,节点会被删除
- 临时顺序节点:当会话结束时,节点会被删除,节点名自带增数后缀
只有持久节点才能有子节点。
现在一般是集群部署应用,所以我们来看下集群部署下的服务地址状况。例如,当你拥有应用A,应用A部署在2台机器上,机器IP分别为:127.0.0.1和127.0.0.2,应用服务端口6666,应用A就有这么两个服务地址:127.0.0.1:6666、127.0.0.2:6666
我们指定一个节点来作为所有服务地址的根节点(类似命名空间),所以该节点应该为一个持久节点。我们有n个应用,每个应用下有n台机器,所以应用节点也拥有子节点,也应该是持久节点。每台机器在启动应用服务的时候要向zk注册一个地址,在服务下线的时候要删除zk中的地址,所以使用临时节点特点正好符合这个行为,同时可以使用顺序节点自动帮我们管理节点名称。
因为我们都是使用node操作,所以使用zk的node客户端node-zookeeper-client。
2、app启动前连接zk
本来我是用eggjs插件写的,这里将框架的东西剔除,其他提取出来,这样就不和框架挂钩了。
const { createClient, ACL, CreateMode } = require('node-zookeeper-client');const zkClient = createClient('127.0.0.1:2181');
const promisify = require('util').promisify;zkClient.connect();zkClient.once('connected', () => {registerService();
});// 让zkClient支持promise
const proto = Object.getPrototypeOf(zkClient);
Object.keys(proto).forEach(fnName => {const fn = proto[fnName];if (proto.hasOwnProperty(fnName) && typeof fn === 'function') {zkClient[`${fnName}Async`] = promisify(fn).bind(zkClient);}
});// host和port应该和部署系统结合分配
// serviceName要求唯一
const { serviceName, host, port } = config;async function registerService() {try {// 创建根节点,持久节点const rootNode = await zkClient.existsAsync('/services');if (rootNode == null) {await zkClient.createAsync('/services', null, ACL.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);}// 创建服务节点,持久节点const servicePath = `/services/${serviceName}`;const serviceNode = await zkClient.existsAsync(servicePath);if (serviceNode == null) {await zkClient.createAsync(servicePath, null, ACL.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);}// 创建地址节点,临时顺序节点,这样name就不需要我们去维护了,递增const addressPath = `${servicePath}/address-`;const serviceAddress = `${host}:${port}`;const addressNode = await zkClient.createAsync(addressPath, Buffer.from(serviceAddress), ACL.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);} catch (error) {throw new Error(error);}
}
复制代码
上面的代码其实很简单,就是连接zk后先判断根节点是不是创建,如果没有就创建(第一个应用),然后判断应用节点是否创建,没有就创建(集群第一台机器),最后就是创建机器节点,这里使用临时顺序节点,省去了我们维护唯一name的麻烦,让其递增,注意,host和port作为存储内容,这个需要app部署的时候部署系统提供(如果是使用自动部署系统的话),然后地址转为Buffer存起来。
这样其实服务注册就完成了。
NodeJs API Gateway 网关服务
上面已经在服务启动的时候都注册到zk中了,当前端调用接口访问服务的时候,我们需要知道服务的地址,这就是服务发现过程。
API Gateway如它的字面意思来看,是API的入口,用来路由请求。其实,不单单是路由请求,API Gateway还可以转换协议,整合数据、认证、限速等逻辑。
例如前端有个用户获取的请求,应该这样写:
fetch('/api/user/get', {method: 'POST',body: { id: 1 },headers: {// header的方式指定service'servive-name': 'user'}
})
复制代码
API Gateway本质也是是个服务,使用Eggjs编写,我们的服务发现封装成一个中间件,所以这里只展示中间件的内容,其他的自己看egg的文档。
const proxy = require('koa-proxies');module.exports = (options, app) => {return async (ctx, next) => {const serviceName = ctx.request.headers['servive-name'];if (!serviceName) {ctx.throw(404, 'no service found.');}const servicePath = `/services/${serviceName}`;const addressNodes = await app.zookeeper.getChildrenAsync(servicePath);const size = addressNodes.length;if (size === 0) {ctx.throw(404, 'no service found.');}let addressPath = `${servicePath}/`;if (size === 1) {addressPath += addressNodes[0];} else {// 这里你可以做负载均衡addressPath += addressNodes[parseInt(Math.random() * size)];}const serviceAddress = await app.zookeeper.getDataAsync(addressPath);if (!serviceAddress) {ctx.throw(404, 'no service found.');}await proxy('/', {target: `http://${serviceAddress}/`,})(ctx, next);};
};
复制代码
上面的中间件中根据headers中的service-name去获取到该应用下所有的服务地址,然后根据某个策略选择一个服务,使用代理转发到对应的服务。
总结
以上很简陋地实现了,虽然可以使用,还有很多细节要处理,比如API Gateway中对于已经拿到的服务地址可以缓存起来,然后订阅zk变化;比如在选择服务的时候可以做负载均衡。
NodeJs服务注册与服务发现实现相关推荐
- 什么是微服务架构?什么是服务注册与服务发现?
文章目录 基础名词 分布式 高可用 集群 什么是微服务 服务注册与服务发现 SpringCloud Alibaba 服务关系以及调用关系 服务注册中心 Nacos服务发现的领域模型 Nacos元数据 ...
- 如何理解服务注册和服务发现
服务注册.服务注册表.服务发现 三者的关系是:通过服务注册机制将启动服务的信息上传至服务注册表,服务发现机制通过服务注册表实时获取可用服务的信息. 服务注册的方式包括:自注册和第三方注册.自注册的意思 ...
- 微服务架构-实现技术之三大关键要素1服务治理:服务注册中心+服务发布与注册+服务发现与调用+服务监控
目录 一.服务注册中心:注册中心核心功能+实现策略 1.注册中心核心功能 2.注册中心实现策略 二.服务发布与注册 三.服务发现与调用 四.服务监控 基本思路:日志埋点 基本目标: 基本定位: 基本策 ...
- 【Zookeeper】JAVA通过ZK实现服务注册和服务发现
无意中发现了一个巨牛的人工智能教程,忍不住分享一下给大家.教程不仅是零基础,通俗易懂,而且非常风趣幽默,像看小说一样!觉得太牛了,所以分享给大家.点这里可以跳转到教程. 服务化现在已经是个很成熟的概念 ...
- 小白入门微服务(4) - 服务注册与服务发现
概述 前言 什么是服务注册.服务发现 两种服务注册方式 两种服务发现方式 常见的第三方注册工具 后记 前言 好一阵子没有更新了,有些小伙伴在后台问我有没有更新,看来大家还是挺喜欢看我的文章的嘛.主要是 ...
- 服务注册与服务发现中心
1.基本原理 服务注册和服务发现中心各自有多台机器,保证高可用 服务器注册:就是将周期性将自己的ip和端口号提供给注册中心,注册中心维护了(服务名称,ip和端口号的列表)一个映射关系. 服务发现:调用 ...
- 深入了解服务注册与服务发现
1. 什么是服务注册与发现 我们先来看下什么是服务注册与服务发现? 服务注册,就是将提供某个服务的模块信息(通常是这个服务的ip和端口)注册到1个公共的组件上去(比如: zookeeper\consu ...
- 微服务2——服务的注册,调用(Nacos服务注册中心+服务调用+调用负载均衡)sca-comsumersca-provider
一.Nacos的安装和构建 以及启动 其官网地址如下: Nacos官网 1.安装前提: 第一:确保你电脑已配置JAVA_HOME环境变量(Nacos启动时需要),例如: 第二:确保你的MySQL版本 ...
- Consul 服务注册与服务发现
1. 服务注册 对 Consul 进行服务注册之前,需要先部署一个服务站点,我们可以使用 ASP.NET Core 创建 Web 应用程序,并且部署到 Ubuntu 服务器上. ASP.NET Cor ...
最新文章
- ​Keepalived双机热备
- Windows Xp Oracle 10g的安装
- Android热补丁之Robust(三)坑和解
- qt 中常见错误汇总
- WINDOWS下对音频的处理过程(转)
- apache.camel_Apache Camel 2.19发布–新增功能
- 安装php7的mysql扩展,php7安装mysql扩展的方法是什么
- java开发实战经典
- lora终端连接云服务器_物联网通讯技术三足鼎立形成:NB-IoT、eMTC、LoRa各有千秋...
- (08)Verilog HDL同步复位
- STM32F103单片机输出相位可调PWM波
- c语言———链表的创建
- 搜狗推送工具-搜狗批量提交软件
- 固定偏置放大电路为何不能保证静态工作点的稳定性?
- the owning Session was closed
- 24点小游戏(C语言实现)
- 构建CA证书详解过程步骤
- CDN边缘智能助力5G
- linux服务器移动硬盘挂载,linux挂载命令mount及U盘、移动硬盘的挂载
- 【软件测试】python完成接口测试示例