一、关于Nacos
什么是Nacos?
官方介绍
https://nacos.io/zh-cn/docs/what-is-nacos.html
Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。
Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 Nacos 是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。
简单概括一下
Nacos可以做两方面用途:
配置服务中心
服务注册中心
nacos调用的完整流程
整个nacos服务链路上分三种角色:nacos服务器,服务提供方,服务消费方
服务提供者告诉nacos服务器,我可以提供服务。
消费方通过访问nacos服务器,根据服务名查询有哪些服务提供方。
消费方获取到具体的服务提供方列表,然后从中选取一个服务提供者调用具体服务。
搭建Nacos服务中心
https://nacos.io/zh-cn/docs/quick-start.html
您可以在Nacos的release notes及博客中找到每个版本支持的功能的介绍,当前推荐的稳定版本为1.3.1。
创建服务提供方NodeJS应用
初始化环境:
nacos服务地址:http://10.22.5.14:32572/
安装 Node LST (8.x.x) 环境: https://nodejs.org/zh-cn;我本地安装的Node 版本是 v14.17.4
nacos关于nodejs的sdk:https://github.com/nacos-group/nacos-sdk-nodejs
创建一个ndoe项目:
1.执行:
npm init npm install nacos ip
2.创建config-service和service-discovery文件:
3.注册配置文件
config-service.js:
/* * @Descripttion: * @version: * @Author: Jason chen * @Date: 2021-08-27 10:05:04 * @LastEditors: Jason chen * @LastEditTime: 2021-08-27 14:03:06 */ // 注册配置,获取配置 const NacosConfigClient = require('nacos').NacosConfigClient; // nacos服务地址 const nacosServerAddress = '10.22.5.14:32572'; // namespace: 名称空间必须在服务器上存在 const providerNamespase = 'edsp-domain'; // 名称空间下的Group const group = 'DEFAULT_GROUP' // 命名空间下的Data Id const dataId = 'test1' // for direct mode const configClient = new NacosConfigClient({ serverAddr: nacosServerAddress, namespace: providerNamespase, }); // 获取nacos配置 (async () => { const content1 = await configClient.getConfig(dataId, group); console.log('[Nacos] 获取配置: ', content1); })() // 监听远程nacos配置变化 configClient.subscribe({ dataId: dataId, group: group, }, content => { console.log('[Nacos] 监听远程nacos配置:', content); });
3.1 执行:
node config-servce.js
3.2 执行结果: 终端输出结果:
在nacos中查看配置:
3.3 在nacos中修改配置:
终端监听到配置修改:
4.service-discovery.js:
/* * @Descripttion: * @version: * @Author: Jason chen * @Date: 2021-08-27 11:22:32 * @LastEditors: Jason chen * @LastEditTime: 2021-08-27 14:09:32 */ // 注册服务到Nacos服务器 const { NacosNamingClient, } = require('nacos'); const { address, } = require('ip'); const logger = console // 动态获取本机 IP 地址 const ipAddr = address(); // 我们当前app1应用的端口号 const port = 3001 // 服务名称,后面消费方调用的时候通过这个服务名进行服务查询。 const providerServiceName = 'edsp-component-app1'; // nacos服务地址 const nacosServerAddress = '10.22.5.14:32572'; // namespace: 命名空间必须在服务器上存在 const providerNamespase = 'edsp-domain'; const client = new NacosNamingClient({ logger, serverList: nacosServerAddress, namespace: providerNamespase, }); console.log('[Nacos] 注册Nacos服务',); (async () => { const allinstance = await client.getAllInstances() console.log('[Nacos]----allinstance----', allinstance) }); (async () => { try { await client.ready(); // 注册服务和实例 await client.registerInstance(providerServiceName, { ip: ipAddr, port }); // 这里也可以传入group,不传默认就是 DEFAULT_GROUP // const groupName = 'nodejs'; // await client.registerInstance(providerServiceName, { // ip: ipAddr, // port // }, groupName); console.log(`[Nacos] Nacos服务注册实例成功: ${ipAddr}:${port}`); } catch (err) { console.log('[Nacos] Nacos服务注册实例失败: ' + err.toString()); } })();
4.1 终端执行:
node service-discovery.js
终端输出结果:
在nacos中查看配置:
二、基于Node+Nacos 开始搭建服务关系网:
以下nacos相关步骤我们主要使用Nacos的服务注册中心功能:整体设计如下图:
我们准备三个项目分别是app1,app2,app3;
第一步:
我们将app1和app2项目进行打包,并在根目录起一个node服务,
node服务的作用:
1.app1服务和app2服务开放deploy目录,供外界访问; 2.分别注册到nacos中(将app1和app2通过webpack模块联邦打包出来的静态资源地址保存在meta中, 如 address=10.18.31.46:9900/app1.js, 其中app1.js就是webpack模块联邦打包出来的文件,当消费方拿到这个文件即可加载app1通过webpack模块联邦暴露出来的模块
app1项目目录(app2目录类似):
app.js
const express = require('express'); const app = express(); // nacos相关 const { NacosNamingClient, } = require('nacos'); const { address } = require('ip'); // 动态获取本机 IP 地址 const ipAddr = address(); // 我们当前app1应用的端口号 const port = 9900 const logger = console // 服务名称,后面消费方调用的时候通过这个服务名进行服务查询。 const providerServiceName = 'edsp-component-app1'; // nacos服务地址 const nacosServerAddress = '10.22.5.14:32572'; // namespace: 命名空间必须在服务器上存在 const providerNamespase = 'edsp-domain'; app.use(express.static('../output')); app.listen(port, () => { console.log(`启动成功:localhost:${port}`); }); // 注册服务到Nacos服务器 const client = new NacosNamingClient({ logger, serverList: nacosServerAddress, namespace: providerNamespase, }); (async () => { try { await client.ready(); // 注册服务和实例 await client.registerInstance(providerServiceName, { ip: ipAddr, port, metadata: { componentName: 'vue-app1', address: `${ipAddr}:${port}/app1.js` } }); console.log(`[Nacos] Nacos服务实例注册成功: ${ipAddr}:${port}`); } catch (err) { console.log('[Nacos] Nacos服务实例注册失败: ' + err.toString()); } })();
启动app1和app2的node服务:
分别到app1和app2中web-serve目录下执行:
node app.js
当app1和app2的实例注册到nacos成功后
第二步:
我们在app3根目录起一个node服务,
app3项目目录:
app.js
const express = require('express'); const app = express(); // nacos相关 const { NacosNamingClient, } = require('nacos'); // 我们当前应用的端口号 const port = 9090 const logger = console // 服务名称,后面消费方调用的时候通过这个服务名进行服务查询。 const providerServiceName = 'edsp-component-app1'; // nacos服务地址 const nacosServerAddress = '10.22.5.14:32572'; // namespace: 命名空间必须在服务器上存在 const providerNamespase = 'edsp-domain'; app.use(express.static('../deploy')); app.listen(port, () => { console.log(`启动成功:localhost:${port}`); }); // 注册服务到Nacos服务器 const client = new NacosNamingClient({ logger, serverList: nacosServerAddress, namespace: providerNamespase, }); // app3前端页面发起查询实例列表请求 app.get('/nacos/getAllInstances', async (req, res) => { let allInstances = await client.getAllInstances(providerServiceName, 'DEFAULT_GROUP', 'DEFAULT', false) res.send(allInstances); });
启动app3的node服务:到app3中web-serve目录下执行:
node app.js
目的: 1.与nacos建立连接,拿到nacos中的实例列表 ; 2.部署app3前端项目
说明:app3的前端页面发起 ‘/nacos/getAllInstances’ 这个请求会走到app3的node服务,通过node服务查询到nacos的实例列表后返回给前端,前端再拿到app1和app2的资源地址;
场景1:消费方因为网络或等其他原因获取不到远端提供方的资源时;(如当远端的应用掉线了或消费方这边发起的查询消费方服务的请求出错)
解决方案:
第一步:将提供方的静态资源放到消费方的目录中:
第二步:当消费方获取提供方资源时,直接获取本地目录中关于提供方经过模块联邦打包后的的js文件(app1.js和app2.js)即可;
第三步效果:
我们从此处可以看到虽然getAllInstance这个接口报错了(获取不到远端提供方的静态资源地址);但我们可选择加载本地准备好的的资源进行渲染;
场景2:消费方无法正常获取提供方的字体图标资源:
解决方案1:消费方的字体图标库需要包含提供方的字体图标库;(即提供方中字体图标的类名在消费方的环境中(iconfont.css)中是可以匹配到的)
解决方案2:
第一步:提供方通过模块联邦暴露iconfont.js文件(此方法需要提供方使用svg的形式来渲染字体图标) 1.1参考iconfont官方提供的 symbol 引用方式 :
1.2 提供方字体元素使用svg ;
1.3 提供方并暴露iconfont.js文件:
第二步:消费方加载提供方的iconfont.js文件即可