Cómo implementa Node.js el centro de configuración y el centro de registro de microservicios

Un sistema con una arquitectura de microservicios tendrá un centro de configuración y un centro de registro.

¿por qué?

Por ejemplo, el centro de configuración:

Habrá muchos microservicios en el sistema y tendrán cierta información de configuración, como variables de entorno, información de conexión a la base de datos, etc.

Esta información de configuración está dispersa en varios servicios y existe en forma de archivos de configuración.

De esta forma, si modifica la misma configuración, debe cambiar el archivo de configuración en cada servicio y luego reiniciar el servicio.

Es muy problemático.

¿Qué pasa si hay un servicio dedicado a administrar de forma centralizada la información de configuración?

De esta manera, cada microservicio obtiene la configuración de aquí, que puede modificarse de manera uniforme, y cada microservicio será notificado después de los cambios de configuración.

Este servicio de gestión centralizada de la información de configuración se denomina centro de configuración.

Luego está el centro de registro:

Los microservicios dependerán unos de otros para completar juntos el procesamiento de la lógica empresarial.

Si un microservicio deja de funcionar, todos los servicios que dependen de él no funcionarán.

Para evitar esta situación, implementaremos varios nodos para cada microservicio a través de la implementación del clúster, y algunos nodos también se pueden agregar dinámicamente.

Así que aquí viene la pregunta:

El microservicio A depende del microservicio B. Cuando se escribió el código, B solo tenía 3 nodos, pero después de ejecutarse, un nodo murió y se agregaron varios nodos nuevos del microservicio B.

En este momento, ¿cómo sabe el microservicio A qué nodos están disponibles para el microservicio B?

La respuesta también requiere un servicio separado para administrar, este servicio es el centro de registro:

Cuando se inicia el microservicio, se registra en el centro de registro, cuando se destruye, se desconecta del centro de registro y envía paquetes de latidos regularmente para informar su estado.

Cuando busque otros microservicios, vaya al centro de registro para verificar toda la información del nodo de este servicio y luego elija uno para usar. Esto se llama detección de servicios.

De esta forma, los microservicios pueden agregar o eliminar nodos dinámicamente sin afectar a otros microservicios.

Estos dos servicios existirán en el sistema de back-end de la arquitectura de microservicio.

Los siguientes son los diagramas de arquitectura de varios sistemas de microservicios que encontré en línea:

Como puede ver, el centro de configuración y el centro de registro son componentes esenciales.

Sin embargo, aunque se trata de dos servicios, las funciones son similares y se pueden implementar en un solo servicio.

Hay bastantes middleware que se pueden usar como centros de configuración y centros de registro, como nacos, apollo, etcd, etc.

Hoy aprenderemos cómo etcd implementa el centro de registro y el centro de configuración.

En realidad, es un servicio de almacenamiento de valores clave.

k8s se utiliza como centro de registro y centro de configuración:

Lo ejecutamos a través de Docker.

如果你本地没装 docker,可以去 docker.com 下载个 docker desktop:

它可以可视化管理镜像、容器等:

搜索 etcd,点击 run:

输入容器名,映射 2379 端口到容器内的 2379 端口,设置 ETCD_ROOT_PASSWORD 环境变量,也就是指定 root 的密码。

然后就可以看到 etcd server 的 docker 镜像成功跑起来了:

它带了一个 etcdctl 的命令行工具,可以作为客户端和 etcd server 交互。

常用的命令有这么几个:

etcdctl put key value
etcdctl get key
etcdctl del key
etcdctl watch key

就是对 key value 的增删改查和 watch 变动,还是比较容易理解的。

但是现在执行命令要加上 --user、--password 的参数才可以:

etcdctl get --user=root --password=guang key

如果不想每次都指定用户名密码,可以设置环境变量:

export ETCDCTL_USER=root
export ETCDCTL_PASSWORD=guang

这里的 password 就是启动容器的时候指定的那个环境变量:

我们设置几个 key:

etcdctl put /services/a xxxx
etcdctl put /services/b yyyy

之后可以 get 来查询他们的值:

etcdctl get /services/a
etcdctl get /services/b

也可以通过 --prefix 查询指定前缀的 key 的值:

etcdctl get --prefix /services 

删除也是可以单个删和指定前缀批量删:

etcdctl del /servcies/a
etcdctl del --prefix /services

这样的 key-value 用来存储 服务名-链接信息,那就是注册中心,用来存储配置信息,那就是配置中心。

我们在 node 里面链接下 etcd 服务:

使用 etcd 官方提供的 npm 包 etcd3:

const { Etcd3 } = require('etcd3');
const client = new Etcd3({
    hosts: 'http://localhost:2379',
    auth: {
        username: 'root',
        password: 'guang'
    }
});
 
(async () => { 
  const services = await client.get('/services/a').string();
  console.log('service A:', services);

  const allServices = await client.getAll().prefix('/services').keys();
  console.log('all services:', allServices);
 
  const watcher = await client.watch().key('/services/a').create();
  watcher.on('put', (req) => {
    console.log('put', req.value.toString())
  })
  watcher.on('delete', (req) => {
    console.log('delete')
  })
})();

get、getAll、watch 这些 api 和 ectdctl 命令行差不多,很容易搞懂。

我们再 put 几个 key:

然后执行上面的 node 脚本:

确实取到了 etcd server 中的值。

然后在 etcdctl 里 put 修改下 /services/a 的值:

在 node 脚本这里收到了通知:

再 del 试下:

也收到了通知:

这样,在 node 里操作 etcd server 就跑通了。

然后我们封装下配置中心和注册中心的工具函数:

配置中心的实现比较简单,就是直接 put、get、del 对应的 key:

// 保存配置
async function saveConfig(key, value) {
    await client.put(key).value(value);
}

// 读取配置
async function getConfig(key) {
    return await client.get(key).string();
}

// 删除配置
async function deleteConfig(key) {
    await client.delete().key(key);
}

使用起来也很简单;

(async function main() {
    await saveConfig('config-key', 'config-value');
    const configValue = await getConfig('config-key');
    console.log('Config value:', configValue);
})();

你可以在这里存各种数据库连接信息、环境变量等各种配置。

然后是注册中心:

服务注册:

// 服务注册
async function registerService(serviceName, instanceId, metadata) {
    const key = `/services/${serviceName}/${instanceId}`;
    const lease = client.lease(10);
    await lease.put(key).value(JSON.stringify(metadata));
    lease.on('lost', async () => {
        console.log('租约过期,重新注册...');
        await registerService(serviceName, instanceId, metadata);
    });
}

注册的时候我们按照 /services/服务名/实例id 的格式来指定 key。

也就是一个微服务可以有多个实例。

设置了租约 10s,这个就是过期时间的意思,然后过期会自动删除。

我们可以监听 lost 事件,在过期后自动续租。

当不再续租的时候,就代表这个服务挂掉了。

然后是服务发现:

// 服务发现
async function discoverService(serviceName) {
    const instances = await client.getAll().prefix(`/services/${serviceName}`).strings();
    return Object.entries(instances).map(([key, value]) => JSON.parse(value));
}

服务发现就是查询 /services/服务名 下的所有实例,返回它的信息。

// 监听服务变更
async function watchService(serviceName, callback) {
    const watcher = await client.watch().prefix(`/services/${serviceName}`).create();
    watcher .on('put', async event => {
        console.log('新的服务节点添加:', event.key.toString());
        callback(await discoverService(serviceName));
    }).on('delete', async event => {
        console.log('服务节点删除:', event.key.toString());
        callback(await discoverService(serviceName));
    });
}

通过 watch 监听 /services/服务名下所有实例的变动,包括添加节点、删除节点等,返回现在的可用节点。

我们来测试下:

(async function main() {
    const serviceName = 'my_service';
    
    await registerService(serviceName, 'instance_1', { host: 'localhost', port:3000 });
    await registerService(serviceName, 'instance_2', { host: 'localhost', port:3002 });

    const instances = await discoverService(serviceName);
    console.log('所有服务节点:', instances);

    watchService(serviceName, updatedInstances => {
        console.log('服务节点有变动:', updatedInstances);
    });
})();

跑起来确实能获得服务的所有节点信息:

当在 etcdctl 里 del 一个服务节点的时候,这里也能收到通知:

这样,我们就实现了服务注册、服务发现功能。

有的同学可能问了:redis 不也是 key-value 存储的么?为什么不用 redis 做配置中心和注册中心?

因为 redis 没法监听不存在的 key 的变化,而 etcd 可以,而配置信息很多都是动态添加的。

当然,还有很多别的原因,毕竟 redis 只是为了缓存设计的,不是专门的配置中心、注册中心的中间件。

专业的事情还是交给专业的中间件来干。

全部代码如下:

const { Etcd3 } = require('etcd3');
const client = new Etcd3({
    hosts: 'http://localhost:2379',
    auth: {
        username: 'root',
        password: 'guang'
    }
});

// 保存配置
async function saveConfig(key, value) {
    await client.put(key).value(value);
}

// 读取配置
async function getConfig(key) {
    return await client.get(key).string();
}

// 删除配置
async function deleteConfig(key) {
    await client.delete().key(key);
}
   
// 服务注册
async function registerService(serviceName, instanceId, metadata) {
    const key = `/services/${serviceName}/${instanceId}`;
    const lease = client.lease(10);
    await lease.put(key).value(JSON.stringify(metadata));
    lease.on('lost', async () => {
        console.log('租约过期,重新注册...');
        await registerService(serviceName, instanceId, metadata);
    });
}

// 服务发现
async function discoverService(serviceName) {
    const instances = await client.getAll().prefix(`/services/${serviceName}`).strings();
    return Object.entries(instances).map(([key, value]) => JSON.parse(value));
}

// 监听服务变更
async function watchService(serviceName, callback) {
    const watcher = await client.watch().prefix(`/services/${serviceName}`).create();
    watcher .on('put', async event => {
        console.log('新的服务节点添加:', event.key.toString());
        callback(await discoverService(serviceName));
    }).on('delete', async event => {
        console.log('服务节点删除:', event.key.toString());
        callback(await discoverService(serviceName));
    });
}

// (async function main() {
//     await saveConfig('config-key', 'config-value');
//     const configValue = await getConfig('config-key');
//     console.log('Config value:', configValue);
// })();

(async function main() {
    const serviceName = 'my_service';
    
    await registerService(serviceName, 'instance_1', { host: 'localhost', port:3000 });
    await registerService(serviceName, 'instance_2', { host: 'localhost', port:3002 });

    const instances = await discoverService(serviceName);
    console.log('所有服务节点:', instances);

    watchService(serviceName, updatedInstances => {
        console.log('服务节点有变动:', updatedInstances);
    });
})();

总结

微服务架构的系统中少不了配置中心和注册中心。

不同服务的配置需要统一管理,并且在更新后通知所有的服务,所以需要配置中心。

微服务的节点可能动态的增加或者删除,依赖他的服务在调用之前需要知道有哪些实例可用,所以需要注册中心。

服务启动的时候注册到注册中心,并定时续租期,调用别的服务的时候,可以查一下有哪些服务实例可用,也就是服务注册、服务发现功能。

注册中心和配置中心可以用 etcd 来做,它就是一个专业做这件事的中间件,k8s 就是用的它来做的配置和服务注册中心。

我们用 docker 跑了 etcd server,它内置了命令行工具 etcdctl 可以用来和 server 交互。

常用的命令有 put、get、del、watch 等。

在 node 里可以通过 etcd3 这个包来操作 etcd server。

稍微封装一下就可以实现配置管理和服务注册、发现的功能。

在微服务架构的后端系统中,配置中心、注册中心是必不可少的组件,不管是 java、go 还是 Node.js。

Supongo que te gusta

Origin juejin.im/post/7234060695254990909
Recomendado
Clasificación