闽公网安备 35020302035485号









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 就是启动容器的时候指定的那个环境变量:

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

etcdctl get /services/a etcdctl get /services/b也可以通过 --prefix 查询指定前缀的 key 的值:
etcdctl get --prefix /services删除也是可以单个删和指定前缀批量删:
etcdctl del /servcies/a etcdctl del --prefix /services这样的 key-value 用来存储 服务名-链接信息,那就是注册中心,用来存储配置信息,那就是配置中心。
//堆代码 duidaima.com
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 命令行差不多,很容易搞懂。




也收到了通知:
// 保存配置
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);
});
})();
跑起来确实能获得服务的所有节点信息:

这样,我们就实现了服务注册、服务发现功能。有的同学可能问了: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);
});
})();