1. Análisis macro de la estructura fuente de ZooKeeper
Código fuente del análisis macro de ZooKeeper, como se muestra a continuación:
Para analizar el código fuente, primero debe analizar toda la macro de estructura de ZooKeeper. Debe saber que ZooKeeper se divide en dos partes: el clúster de servidores y el cliente.
El servidor:
- Cada servidor ZooKeeper tiene tres estados: inicialización, ejecución y apagado. Por lo tanto, cuando todos los servidores se ejecutan para formar un clúster de zookeeper, pueden proporcionar servicios externos (también se puede ejecutar una sola máquina);
- Después de que el servidor inicia el servicio, se inicializa para formar un clúster disponible;
Para el cliente:
- El cliente encapsula la capa de operación de la API, de modo que cualquier acceso se base en la misma API;
- La API del cliente debe seguir un cierto protocolo para encapsular el protocolo del mensaje;
- La comunicación de red debe realizar la serialización, deserialización y establecimiento de conexión;
Por supuesto, esta parte de la encapsulación del protocolo, la serialización / deserialización y el establecimiento de la conexión proporcionada por el cliente también debe ser proporcionada por el servidor. Podemos interceptar la solicitud escribiendo el pseudo servidor para ver, el código es el siguiente:
public class SoecktLister { public static void main (String [] args) lanza Exception { ServerSocket serverSocket = new ServerSocket (2181 ); Socket accept = serverSocket.accept (); byte [] resultado = nuevo byte [2048 ]; accept.getInputStream (). read (resultado); ByteBuffer bb = ByteBuffer.wrap (resultado); ByteBufferInputStream bbis = nuevo ByteBufferInputStream (bb); BinaryInputArchive bia =BinaryInputArchive.getArchive (bbis); RequestHeader header2 = new RequestHeader (); header2.deserialize (bia, "encabezado" ); System.out.println (encabezado2); bbis.close (); } }
Luego acceda a través del cliente:
clase pública ZooKeeperTest { zooKeeper privado zooKeeper; public ZooKeeperTest () { try { zooKeeper = new ZooKeeper ("localhost: 2181" , 5000 , null , false ); } catch (IOException e) { e.printStackTrace (); } } public void add (ruta de cadena, datos de cadena) { try { String newPath =zooKeeper.create (ruta, data.getBytes (), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } catch (KeeperException e) { e.printStackTrace (); } catch (InterruptedException e) { e.printStackTrace (); } } public static void main (String [] args) { ZooKeeperTest zooKeeperTest = new ZooKeeperTest (); zooKeeperTest.add ( "/ monkey2", "2019" ); } }
Para que el servidor pueda recibir la solicitud:
RequestHeader {protocolVersion = 45, lastZxidSeen = 0, timeOut = 0, sessionId = 21474836480000, passwd = []}
De hecho, estos contenidos son el paquete de protocolo de un mensaje de solicitud simple.
En segundo lugar, el análisis del código fuente del servidor
1. Inicialización del servidor
De acuerdo con el script de inicio de ZooKeeper./zkServer.sh start -server ip: port, abra el script para ver la entrada de inicio del servidor: org.apache.zookeeper.server.quorum.QuorumPeerMain.
Nota: La estructura de almacenamiento de datos del servidor es: org.apache.zookeeper.server.DataTree, dataTree se coloca en ZKDataBasse.
Después de que se inicie el servidor, cargará el archivo de configuración zoo.cfg, la carga de datos, el establecimiento de comunicación y la elección del líder a su vez. El código es el siguiente:
@ Override pública la sincronizada vacío Inicio () { SI (! {La getView () containsKey (MyID).) De banda nueva nuevo una RuntimeException ( "mi ID" + + MyID "El no va a utilizar la lista de pares" ); } loadDataBase (); // Cargar Carga de datos znode de datos: lea el archivo de instantánea del disco duro (en el directorio de datos) startServerCnxnFactory (); // Establecimiento de comunicación de red intente { adminServer.start (); } catch (AdminServerException e) { LOG.warn ( "Problema al iniciar AdminServer" , e ); System.out.println (e); } startLeaderElection (); // elecciones startJvmPauseMonitor (); Súper .start (); // ahora llamando al método de ejecución hilo }
Nota: La configuración se ha cargado antes de llamar a esto, como el código:
public void runFromConfig (configuración de QuorumPeerConfig) lanza IOException, AdminServerException { try { ManagedUtil.registerLog4jMBeans (); } catch (JMException e) { LOG.warn ( "No se puede registrar el control log4j JMX" , e); } LOG.info ( "Inicio del quórum par" ); MetricsProvider metricsProvider; pruebe { metricsProvider = MetricsProviderBootstrap.startMetricsProvider ( config.getMetricsProviderClassName (), config.getMetricsProviderConfiguration ()); }catch (error de MetricsProviderLifeCycleException) { lanzar una nueva IOException ("No se puede iniciar MetricsProvider" + config.getMetricsProviderClassName (), error); } pruebe { ServerMetrics.metricsProviderInitialized (metricsProvider); ServerCnxnFactory cnxnFactory = nulo ; ServerCnxnFactory secureCnxnFactory = nulo ; if (config.getClientPortAddress ()! = null ) { cnxnFactory = ServerCnxnFactory.createFactory (); cnxnFactory.configure (config.getClientPortAddress (), config.getMaxClientCnxns (), config.getClientPortListenBacklog (), falso ); } if (config.getSecureClientPortAddress ()! = null ) { secureCnxnFactory = ServerCnxnFactory.createFactory (); secureCnxnFactory.configure (config.getSecureClientPortAddress (), config.getMaxClientCnxns (), config.getClientPortListenBacklog (), verdadero ); } quorumPeer = getQuorumPeer (); quorumPeer.setTxnFactory ( nuevo FileTxnSnapLog (config.getDataLogDir (), config.getDataDir ())); quorumPeer.enableLocalSessions (config.areLocalSessionsEnabled ()); quorumPeer.enableLocalSessionsUpgrading (config.isLocalSessionsUpgradingEnabled ()); // quorumPeer.setQuorumPeers (config.getAllMembers ()); quorumPeer.setElectionType (config.getElectionAlg ()); quorumPeer.setMyid (config.getServerId ()); quorumPeer.setTickTime (config.getTickTime ()); quorumPeer.setMinSessionTimeout (config.getMinSessionTimeout ()); quorumPeer.setMaxSessionTimeout (config.getMaxSessionTimeout ()); quorumPeer.setInitLimit (config.getInitLimit ()); quorumPeer.setSyncLimit (config.getSyncLimit ()); quorumPeer.setConnectToLearnerMasterLimit (config.getConnectToLearnerMasterLimit ()); quorumPeer.setObserverMasterPort (config.getObserverMasterPort ()); quorumPeer.setConfigFileName (config.getConfigFilename ()); quorumPeer.setClientPortListenBacklog (config.getClientPortListenBacklog ()); quorumPeer.setZKDatabase ( Nueva ZKDatabase (quorumPeer.getTxnFactory ())); quorumPeer.setQuorumVerifier (config.getQuorumVerifier (), falsa ); si (config.getLastSeenQuorumVerifier () =! nula ) { quorumPeer.setLastSeenQuorumVerifier (config.getLastSeenQuorumVerifier (), falsa ); } QuorumPeer.initConfigInZKDatabase (); quorumPeer.setCnxnFactory (cnxnFactory); quorumPeer.setSecureCnxnFactory (secureCnxnFactory); quorumPeer.setSslQuorum (config.isSslQuorum ()); quorumPeer.setUsePortUnification (config.shouldUsePortUnification ()); quorumPeer.setLearnerType (config.getPeerType ()); quorumPeer.setSyncEnabled (config.getSyncEnabled ()); quorumPeer.setQuorumListenOnAllIPs (config.getQuorumListenOnAllIPs ()); if (config.sslQuorumReloadCertFiles) { quorumPeer.getX509Util (). enableCertFileReloading (); } // establece las configuraciones de autenticación sasl de quórum quorumPeer.setQuorumSaslEnabled (config.quorumEnableSasl); if (quorumPeer.isQuorumSaslAuthEnabled ()) { quorumPeer.setQuorumServerSaslRequired (config.quorumServerRequireSasl); quorumPeer.setQuorumLearnerSaslRequired (config.quorumLearnerRequireSasl); quorumPeer.setQuorumServicePrincipal (config.quorumServicePrincipal); quorumPeer.setQuorumServerLoginContext (config.quorumServerLoginContext); quorumPeer.setQuorumLearnerLoginContext (config.quorumLearnerLoginContext); } quorumPeer.setQuorumCnxnThreadsSize (config.quorumCnxnThreadsSize); quorumPeer.initialize (); if (config.jvmPauseMonitorToRun) { quorumPeer.setJvmPauseMonitor ( nuevo JvmPauseMonitor (config)); } quorumPeer.start (); // Por el momento, es para llamar al método de inicio de quorumPeer, no para iniciar el hilo quorumPeer, el hilo real se inicia en el método de inicio de super.start () quorumPeer.join (); // Espere a que el servidor se inicialice ) catch (InterruptedException e) { // warn, pero generalmente está bien LOG.warn ("Quorum Peer interrupted" , e); } finalmente { if (metricsProvider! = null ) { try { metricsProvider.stop (); } catch ( Error de lanzamiento ) { LOG.warn ( "Error al detener las métricas" , error); } } } }
El proceso detallado de inicio del servidor se muestra en la siguiente figura:
2. Respuesta de solicitud del servidor
Luego, el servidor proporciona la solicitud de respuesta de servicio externamente, como se muestra en la siguiente figura (operación de escritura de respuesta):
El proceso anterior cumple con el protocolo de coherencia Zab de ZooKeeper. El nombre completo del protocolo Zab es Zookeeper Atomic Broadcast (transmisión atómica Zookeeper). Zookeeper utiliza el protocolo Zab para garantizar la coherencia final de las transacciones distribuidas.
Para obtener detalles sobre el acuerdo de Zab y las reglas electorales, consulte:
Tres, análisis de código fuente del cliente
1. Inicialización del cliente
El proceso de inicio del cliente es el siguiente:
Al principio, el cliente realizará un análisis de clúster e inicialización de red (objeto ClientCncx). Al mismo tiempo, el objeto ClientCncx creará dos subprocesos, SendThread y EventThread, para gestionar la solicitud / respuesta y los eventos del observador. El código es el siguiente:
público ClientCnxn ( Cadena chrootPath, HostProvider hostProvider, int sessionTimeout, ZooKeeper ZOOKEEPER, ClientWatchManager observador, ClientCnxnSocket clientCnxnSocket, largo sessionId, byte [] sessionPasswd, boolean canBeReadOnly) { este .zooKeeper = ZOOKEEPER; this .watcher = vigilante; this .sessionId = sessionId ; this .sessionPasswd = sessionPasswd; this .sessionTimeout = sessionTimeout ; esta.hostProvider = hostProvider; this .chrootPath = chrootPath; connectTimeout = sessionTimeout / hostProvider.size (); readTimeout = sessionTimeout * 2/3 ; readOnly = canBeReadOnly; sendThread = nuevo SendThread (clientCnxnSocket); eventThread = new EventThread (); this .clientConfig = zooKeeper.getClientConfig (); initRequestTimeout (); } public void start () { sendThread.start (); eventThread.start (); }
2. Gestión de solicitudes del cliente
El proceso para que el cliente acceda al servidor es el siguiente:
Se puede ver en la figura anterior que ClientCncx ha iniciado dos hilos: SendThread y EventThread. Estos dos hilos manejan la respuesta a la solicitud del servidor, y el otro maneja el evento de escucha.
Estos dos subprocesos se basan en la cola para la gestión de solicitudes, outGoingQueue se usa para procesar la cola para enviar solicitudes de solicitud, PendingQueue se usa para almacenar las solicitudes que se han enviado en espera de las respuestas del servicio, de modo que cuando se recibe la solicitud, se puede realizar el procesamiento de la respuesta, y waitingEventsQueue se usa para el almacenamiento temporal El objeto que necesita ser activado, por lo que la aplicación de la cola se da cuenta del alto rendimiento de ZooKeeper.
Por lo tanto, el cliente utiliza principalmente estas tecnologías: la gestión de solicitudes subyacente es cola> hilo para procesamiento de cola> método de comunicación predeterminado NIO> bloqueo sincronizado (utilizado en la cola).
Tanto el cliente como el servidor utilizan el componente de serialización de Jute y su propio protocolo de comunicación. Para obtener más información, consulte:
Cuatro, operación y mantenimiento de ZooKeeper
Uso diario en Linux: comando echo zk | comando nc ip port para la operación y mantenimiento diario de ZooKeeper, como: echo mntr | nc 192.168.0.31 2181.
El comando nc en Linux es una poderosa herramienta de red, el nombre completo es netcat, instalación en línea: yum install -y nc. Los comandos zk comunes son los siguientes:
Por supuesto, también puede usar su propio código para implementar la operación y el mantenimiento de la interfaz.