Introducción a Zookeeper
El modelo de datos de zookeeper es similar al sistema de archivos. Cada nodo se llama znode, que es la unidad de datos más pequeña en zookeeper. Cada znode puede informar datos y montar nodos secundarios para formar una estructura de atributos jerárquica.
Zookeeper puede crear los siguientes tipos de nodos
Tipo de nodo | Explicación |
---|---|
Nodo persistente | Cree un nodo como un nodo persistente, y los datos siempre se almacenarán en el servidor de zookeeper. Incluso si la sesión entre el cliente y el servidor que creó el nodo está cerrada, el nodo aún no se eliminará |
Nodo de secuencia persistente | Sobre la base de los nodos persistentes, se agrega el orden de los nodos |
Nodo temporal | Cree el nodo como un nodo temporal, los datos no siempre se almacenarán en el servidor de zookeeper, cuando se cierre la sesión del cliente que creó el nodo temporal, el nodo se eliminará en el servidor de zookeeper correspondiente |
Nodo de secuencia temporal | Sobre la base de los nodos temporales, se agrega el orden de los nodos |
Demuestre brevemente los comandos de uso común
create [-s] [-e] path data acl
-s: crear nodos secuenciales
-e: crear nodos temporales
ruta: ruta
datos: datos
acl: permisos
Crear crea un nodo persistente de forma predeterminada
create /level-1 123
create /level-1/level-1-2 456
get /level-1(获取节点level-1的值,输出123)
ls /level-1 (获取节点level-1的子节点,输出[level-1-2])
// 创建一个顺序节点
create -s /nodes 123(输出nodes0000000003)
create -s /nodes 456(输出nodes0000000004)
Después de ejecutar el comando anterior, la estructura de datos es la siguiente
Aquí hay una breve charla sobre las características de los nodos secuenciales. Cada vez que se crea un nodo secuencial, zk agregará automáticamente un número de 10 dígitos (contador) después de la ruta, como <ruta> 0000000001, <ruta> 0000000002, ... Se puede garantizar que este contador sea único bajo el mismo nodo padre. Se utiliza un entero de 4 bytes con signo dentro de zk para representar este contador, lo que significa que cuando el tamaño del contador excede 2147483647, se producirá un desbordamiento. Cada vez que se crea un nodo temporal bajo el nodo principal, el tamaño aumenta en 1 .3 a 4 en la imagen de arriba
Uso de curador
Curator es un conjunto de cliente zookeeper de código abierto de la empresa netflix. Puede ayudarnos a simplificar la operación del guardián del zoológico. Se resolvieron muchos problemas subyacentes, como la reconexión, el registro repetido de Watcher y NodeExistsException, etc.
Además, Curator también proporciona varios escenarios de aplicación de Zookeeper: Receta, servicio de bloqueo compartido, mecanismo de elección maestra y contadores distribuidos, etc.
Curator se divide en los siguientes módulos. Los
más utilizados son curator-framework (encapsulación de la api de zookeeper, como agregar administración de conexiones, mecanismo de reintento, etc.) y curator-recetas (implementación de escenarios de aplicación típicos de zookeeper), curator -cliente (paquete de cliente zookeeper)
Cuando lo usamos, solo necesitamos agregar las siguientes dependencias
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.0.1</version>
</dependency>
Demuestre el uso básico de Curator Api
@Slf4j
public class ApiDemo {
private CuratorFramework client;
/**
* RetryPolicy 是重试策略接口
* https://www.cnblogs.com/qingyunzong/p/8666288.html
*/
@Test
@Before
public void connect() {
String connectString = "myhost:2181";
// 重试3次,每次间隔1000ms
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
client = CuratorFrameworkFactory.newClient(connectString, retryPolicy);
client.start();
}
/**
* 创建一个持久节点
*/
@Test
public void createPersistent() throws Exception {
// 创建一个内容为空的节点
client.create().forPath("/persistent");
// 创建包含内容的节点
client.create().forPath("/persistentContent", "我是内容".getBytes());
}
/**
* 创建临时节点
*/
@Test
public void createEphemeral() throws Exception {
// 创建一个内容为空的节点
client.create().withMode(CreateMode.EPHEMERAL).forPath("Ephemeral");
// 创建包含内容的节点
client.create().withMode(CreateMode.EPHEMERAL).forPath("/ephemeralContent", "我是内容".getBytes());
}
/**
* 获取值
*/
@Test
public void getData() throws Exception {
client.getData().forPath("/persistentContent");
}
/**
* 更新值
*/
@Test
public void setData() throws Exception {
client.setData().forPath("/persistentContent", "新内容".getBytes());
}
/**
* 删除
*/
@Test
public void delete() throws Exception {
client.delete().forPath("/persistent");
}
}
Evento detallado
Zookeeper proporciona publicación / suscripción de datos distribuidos, lo que permite al cliente registrar un observador en el servidor.Cuando algunos eventos específicos en el servidor activan al observador, se enviará una notificación de evento al cliente especificado para realizar la función de notificación distribuida.
Zookeeper define la interfaz Watcher para representar un controlador de eventos estándar y contiene
dos clases de enumeración KeeperState y EventType para representar el estado de notificación y el tipo de evento.
La relación entre KeeperState y EventType es la siguiente
: Monitorear eventos en el servidor es una tarea importante para que el cliente opere el servidor. En la api de curator, hay dos modos de monitoreo de eventos
- El modo de observación estándar solo se puede monitorear una vez (es decir, la interfaz Watcher en Zookeeper. Para lanzar una excepción, el curador redefine una interfaz CuratorWatcher)
- Modo de monitor de caché, puede monitorizar varias veces
El modo de monitoreo de caché puede entenderse como el proceso de comparación entre la vista de caché local y la vista de guardián del zoológico remoto.Cuando se detecta el cambio de estado de znode del clúster zk, se activarán eventos y los oyentes registrados procesarán estos eventos.
Hay tres tipos de monitoreo de caché
Tipo de monitor | Explicación |
---|---|
Caché de nodo | Monitorear el nodo ZNode |
Caché de ruta | Supervisar los nodos secundarios de ZNode |
Caché de árbol | Supervisar el nodo ZNode y sus hijos |
Sigue yendo directamente al ejemplo
@Test
public void watcher() throws Exception {
Watcher watcher = new Watcher() {
@Override
public void process(WatchedEvent event) {
// 只输出一次
// /watchDemo SyncConnected NodeDataChanged
System.out.println(event.getPath() + " " + event.getState() + " " + event.getType());
}
};
String path = "/watchDemo";
if (client.checkExists().forPath(path) == null) {
client.create().forPath(path);
}
client.getData().usingWatcher(watcher).forPath(path);
client.setData().forPath(path, "第一个变更的内容".getBytes());
client.setData().forPath(path, "第二个变更的内容".getBytes());
TimeUnit.SECONDS.sleep(3);
}
@Test
public void curatorWatcher() throws Exception {
CuratorWatcher watcher = new CuratorWatcher() {
@Override
public void process(WatchedEvent event) throws Exception {
// 只输出一次
// /watchDemo SyncConnected NodeDataChanged
System.out.println(event.getPath() + " " + event.getState() + " " + event.getType());
}
};
String path = "/watchDemo";
if (client.checkExists().forPath(path) == null) {
client.create().forPath(path);
}
client.getData().usingWatcher(watcher).forPath(path);
client.setData().forPath(path, "第一个变更的内容".getBytes());
client.setData().forPath(path, "第二个变更的内容".getBytes());
TimeUnit.SECONDS.sleep(3);
}
Puede ver que ya sea la interfaz de Watcher en zookeeper o la interfaz de CuratorWatcher en curator, el evento solo se puede monitorear una vez
@Test
public void treeCacheListener() throws Exception {
String bossPath = "/treeCache";
String workerPath = "/treeCache/id-";
if (client.checkExists().forPath(bossPath) == null) {
client.create().forPath(bossPath);
}
TreeCache treeCache = new TreeCache(client, bossPath);
TreeCacheListener listener = ((CuratorFramework client, TreeCacheEvent event) -> {
String path = null;
String content = null;
switch (event.getType()) {
case NODE_ADDED:
log.info("节点增加");
path = event.getData().getPath();
content = new String(event.getData().getData());
break;
case NODE_UPDATED:
log.info("节点更新");
path = event.getData().getPath();
content = new String(event.getData().getData());
break;
case NODE_REMOVED:
log.info("节点移除");
path = event.getData().getPath();
content = new String(event.getData().getData());
break;
default:
break;
}
// 事件类型为: NODE_ADDED, 路径为: /treeCache, 内容为: 192.168.97.69
// 事件类型为: INITIALIZED, 路径为: null, 内容为: null
// 事件类型为: NODE_ADDED, 路径为: /treeCache/id-0, 内容为: 0
// 事件类型为: NODE_ADDED, 路径为: /treeCache/id-1, 内容为: 1
// 事件类型为: NODE_REMOVED, 路径为: /treeCache/id-0, 内容为: 0
// 事件类型为: NODE_REMOVED, 路径为: /treeCache/id-1, 内容为: 1
// 事件类型为: NODE_REMOVED, 路径为: /treeCache, 内容为: 192.168.97.69
log.info("事件类型为: {}, 路径为: {}, 内容为: {}", event.getType(), path, content);
});
treeCache.getListenable().addListener(listener);
treeCache.start();
// 创建2个子节点
for (int i = 0; i < 2; i++) {
client.create().forPath(workerPath + i, String.valueOf(i).getBytes());
}
// 删除2个子节点
for (int i = 0; i < 2; i++) {
client.delete().forPath(workerPath + i);
}
// 删除当前节点
client.delete().forPath(bossPath);
TimeUnit.SECONDS.sleep(3);
}
Como puede ver, el modo de monitoreo de caché se puede registrar repetidamente
El resto del contenido de zookeeper no se introducirá mucho.Después de comprender la api de zookeeper y curator, no hay gran problema al mirar el código relacionado con zookeeper en el módulo de registro de Dubbo.
Blog de referencia
[1] https://www.cnblogs.com/crazymakercircle/p/10228385.html