Author: HelloGitHub- Lao Xun

This is the last article in the HelloZooKeeper series. Next, we will talk about how to answer ZooKeeper questions in an interview.

Are you ready? Interview begins ~ “Launch on HelloGitHub official account”

1. Mock interview

Finally came to the main part, this section I will find some on the Internet about ZK interview questions for analysis, and stand on the basis of the interviewer analysis of the test points, I believe that after reading this section, go out to interview ZK related problems you will be able to break through obstacles, invincible!

Let me give you a simulated interview scene:

Interviewer: I see you have used ZK in your resume. Can you tell me something about it? How do you understand ZK’s role?

(If you recite the definition from Baidu Baike to him, I can only say 666. Don’t do that, you will be regarded as a fool.)

I: In my understanding, ZK is a third-party process separated from the application. Similar databases, message queues and Redis all play this role and have certain data storage and query capabilities, which enables us to “transfer” data between distributed applications. Secondly, ZK supports callback notification. The application can sense data changes in some business scenarios and react accordingly. Finally, ZK itself supports cluster deployment and is highly available as a reliable third-party middleware.

Interviewer: Well, you mentioned callback notification. Can you tell me more about how ZK implemented it?

I: Clients in various programming languages abstract this callback notification, usually requiring the developer to declare a callback object. In Java, this interface is Watcher. The ZK server provides several methods, For example, getData, EXISTS or addWatch in the latest version can be used to register callback notification with ZK. The callback notification sent to the server will only tell the server that the current path needs to be notified. The server will record the relationship between the path and the client in memory after knowing it. The client itself also needs to record the relationship between the path and the specific callback. When the subscription path events, all kinds of increase, deletion will be recorded in the memory of a service to check if there is any need to inform client, any request will send a notice to the client, the client, after receipt of the notification will be removed from local records corresponding callback objects to implement callback methods!

(Actually, I don’t think the interviewer will let you keep talking. It’s supposed to be a conversation)

Interviewer: Well, that’s quite detailed. Is there any difference between getData, EXISTS and addWatch?

I: GetData, EXISTS, and getChildren registered notifications are one-time. After the server notifies them once, the record in the memory will be deleted. If the notification is still needed, the client will continue to register, while addWatch registered callback notifications are permanent. You only need to register once to be notified.

Interviewer: Well, you also mentioned that ZK has some data storage capabilities. Can you talk about how ZK stores and organizes data?

Me: ZK’s data comes in two parts.

Interviewer: Oh? Which two parts?

Me: In memory and on disk.

Interviewer: How does ZK store data in memory

I: Logically, the data in ZK’s memory is actually a tree structure, starting at the root of/and descending with /. Each node can have multiple child nodes, similar to the directory structure in Unix, but in reality, ZK uses a HashMap to store the data in the entire tree structure. Key is the corresponding full path string, and value is a node object that contains various information about the node.

Interviewer: Can you tell me why you think it was designed this way?

(I don’t think interviewers usually ask this question, so I guess what I’m going to say)

Me: First, HashMap is a very important data structure in the Java standard library because it is very fast to query, and it is useful in many places. ZK itself does not require sorting or range evaluation, so HashMap is perfectly adequate for query purposes. The logical reason for the tree structure, parent-child nodes, is that it is very similar to Unix file systems in that it is very easy to understand and sort data based on path, and the latest ZK features rely on parent-child recursion (addWatch, for example). If it is a common key-value, it cannot be satisfied.

Interviewer: Ok, so how does ZK store data on a disk?

Me: ZK specifies two file types on disk, one is log file and the other is snapshot. The log file is an incremental record, which is responsible for saving each write request. The snapshot file is a full record, which is a snapshot of memory.

Interviewer: How does ZK ensure that the data in memory is consistent with the data on disk?

Me: Really strong consistency, ZK can’t guarantee. For each write request, ZK records the disk first and then modifies the memory, so it ensures that if something goes wrong, it records the disk first to ensure data integrity as much as possible. If ZK exits normally, disk files will be flushed and snapshot generated to ensure consistency. However, if ZK exits abnormally, some data will be lost in extreme cases.

Interviewer: You also mentioned that ZK itself can be clustered? Can we talk a little bit more?

I: ZK configuration file zoo. CFG can configure the information of other nodes, each node can be distinguished by the myID file in the dataDir directory, different nodes can communicate with each other, the client connected to any node in the cluster can communicate.

Interviewer: How many different roles are there in a ZK cluster? You know what?

I have three roles: Leader, Follower and Observer.

Interviewer: Tell me the difference between them

Me: There can only be one Leader in a cluster. The Leader is responsible for submitting write request transactions of the whole cluster. Before a Leader is elected, the cluster cannot provide services. Both followers and observers can only process read requests. The difference is that followers have the right to vote in the election of the Leader, while the Observer cannot.

Interviewer: Can you tell me something about the information used to elect a Leader?

Me: Each node maintains the three most important information: epoch, ZXID, and myID. Epoch represents the election cycle, which is compared first, and continues to compare the next level if they are the same. Zxid represents the largest transaction ID processed by this node. The larger the zxID is, the more write requests the current node has handled, and the more it knows. The second priority is compared, and if they are the same, the myID will be compared. The winning node is elected Leader.

(To be precise, the epoch and ZXID are one field, one recorded in the upper 32 bits and one in the lower 32 bits.)

Interviewer: How do different nodes communicate with each other? How to conduct an election?

Me: When each ZK node is started, it will set up Socket connection with other nodes by reading cluster information in the configuration file. The communication between clusters is through this Socket. During the election, each node will broadcast the information of the candidate it thinks, and also receive the information of the candidate from other nodes. After comparison, the losing party will change its candidate information and broadcast it again, and the election will be completed until a node has more than half of the votes.

Interviewer: Are there any differences in how the node roles handle read and write requests? Let’s talk about leaders first

I: The Leader, as the boss of the cluster, is responsible for making a PROPOSAL to the received write request and telling other nodes that they have received a write request. After receiving the request, the other nodes will archive it locally, which is actually writing the file output stream. After completion, the Leader will send an ACK to the Leader. After the Leader collects more than half of the ACKS, it sends another COMMIT to the other nodes. After receiving the COMMIT, the other nodes can modify the memory data. Read requests do not need to directly query the data in memory to return.

Interviewer: What about followers or observers?

Me: They receive read request is the same, directly return local memory data can be. However, if the request is written, the current request will be forwarded to the Leader, who will then handle it, just like the previous process.

Interviewer: How does ZK ensure the order of different requests?

Me: The order guarantee is finally implemented in a first-in, first-out queue, the first request to enter the queue will be processed first, so the order can be guaranteed.

Interviewer: How do you ensure the order of requests from different clients? A sends A create node and B sends A query before the request is returned. Will B block the query until A is finished? Or return to the node that cannot be queried directly?

I: B will directly return to check. The order between different clients is not guaranteed by ZK, because at the bottom ZK uses a Map to place requests from different clients. Different clients have different keys, and the Map value is the FIFO queue I just mentioned. Therefore, only requests from the same client can be executed sequentially, and requests from different clients cannot be guaranteed.

Interviewer: Can you tell me what the keys are for different clients? How to guarantee different.

Me: each client is assigned a sessionId after connecting to ZK. This sessionId is a field of type long generated by the current timestamp, the myid of the node, and an increment feature to ensure that it does not repeat.

Interviewer: Speaking of sessions, do you know how ZK’s sessions are maintained?

Me: Are you asking about the session between client and server?

Interviewer: Yes, can you tell me about it?

I: When connecting to ZK, each client will report its own timeout period, plus the sessionId, ZK server will maintain a mapping relationship locally, through calculation can calculate the timeout period of the sessionId, and ZK also has a tickTime configuration. An algorithm can map different client timeout times to the same interval, and then store the relationship between the timeout time and the sessionId.

Interviewer: What are the benefits of mapping to the same point in time?

Me: In this way, after the server is started, there will be a thread in the background. Through this unified interval, it will take out the clients whose session has expired and send them the message of session expiration, which greatly saves the performance.

Interviewer: How does the client update the session timeout?

Me: First, each operation of the client will refresh the timeout period. Second, the client must design a PING operation to actively refresh the session timeout period when the client is idle to prevent the expiration.

Interviewer: Besides the session between client and server, what else?

Me: There is also a heartbeat between the server and the server. The heartbeat of the server is initiated by the Leader and sends a PING request to other nodes. After receiving the PING request, the other nodes need to send the local session information to the Leader.


I can’t make it up. Some of the above questions have my strong subjective bias, and I think most of these questions would not be able to be asked if the interviewer is a jerk. So the point is not how I would answer them, but when you have a clear idea of the principles behind them, it’s natural to block them.

Let me mention a few features that I think are important:

  • Callback notification, ZK other principles can not understand, but how to use callback is certainly to know.
  • Election, one of the most characteristic attributes of ZK, will basically ask about it.
  • Persistence, to clarify the difference between the two types of files.
  • Conversation, the concept of conversation, and how to maintain it.

Finally, through a simulated interview answered the ZK I think there are more characteristic interview questions, if you have any questions about the interview questions remember to leave a message to me oh ~ must be arranged for you!

Second, the online real exam

I searched most of the questions directly on the Internet, the website is here ZooKeeper interview questions (2020 latest edition), but filtered some too low questions.

4. How does ZooKeeper synchronize the status of the primary and secondary nodes?

As I said above, after receiving the write request, the Leader will initiate a proposal and wait for ACK from other nodes. This ACK requires more than half of the nodes to pass before it can continue. Therefore, receiving more than half of the ACKS means that more than half of the cluster has completed archiving the local disk. Naturally, data synchronization between master and slave is ensured.


5. Four types of data nodes zNodes

As I mentioned in my previous article, there are 7 node types in ZK now. I haven’t talked about the principle of the new node, so you can give him an official answer if he asks:

  • Persistent node
  • Persistent order node
  • Temporary node
  • Temporary sequence node

He will then ask the difference between the two behind the general and temporary node will automatically deleted with the client session is disconnected, principle is when create temporary node, the server will maintain a path sessionId and its corresponding temporary node list, when closing session, take out your path in this list are removed one by one. The difference between sequential nodes is that ZK automatically suffixes the path with a number, and that’s it.


How do sequential nodes ensure that suffix numbers are unique when created concurrently?

ZK requests are queued to be processed one by one, so there is no such thing as concurrency. After the first request is processed, the next request is processed to ensure that the suffix number is unique.


10. ACL permission control mechanism

ZK divides permissions into two categories, which can be further subdivided:

  • Client role permissions
    • IP
    • User Name Password
    • World, the broadest permission, which means no permission
    • Super, special user name password, equivalent to administrator rights
  • Data permission of a node
    • Create, Create
    • Delete, Delete
    • Read, Read
    • Write, Write
    • ACL: Read and write permission

11. Chroot feature

Chroot is designed by ZK to isolate the namespace of clients. It acts as the root node of different clients and is maintained by clients. In general, before sending requests, the chroot path is joined together and then requests the server. Chroot is completely transparent to the server.


15. Data synchronization

Synchronizing data between Learner and Leader is a long and complicated process. Generally speaking, it can be roughly divided into the following steps:

  • Learner reports his information to the Leader
  • The Leader decides what synchronization method to use based on the Learner information
    • DIFF, which recovers data from the last 500 proposals by sending them directly
    • TRUNC, which usually appears when Learner is the former Leader, needs to degrade his data to be consistent with the Leader
    • SNAP: the Leader directly sends the entire memory snapshot to the followers
  • The Leader and Learner start to synchronize
  • After the synchronization is complete, external services are provided

Three, complete configuration

Thanks to all of you, I have browsed the source code of ZK and found at least 99% of the configuration options. ZK configuration can be roughly divided into three kinds:

  • Starts the arguments passed in from the command line
  • zoo.cfgParameters in the configuration file
  • Parameters in the current environment variable

3.1 Cli Parameters

The command line has few parameters and no corresponding configuration name. Here I briefly introduce:

The standalone version supports only two forms of command line parameter transmission

  • Client listening port plus data directory, the previous section source debugging is used in this form, for example:2181 /your/zk/data/path
  • Or just pass one argument,zoo.cfgPath to, for example:/your/zoocfg/path

The cluster version is simpler and supports only the zoo. CFG path as a parameter

3.2 Configuration in zoo. CFG file

When I carefully look at the source code, I found that some configuration needs to be calculated or used in multiple places, so it is difficult to speak clearly in one step, so the following introduction is for reference only, configuration item plus asterisk (*) is my future plan to explain at the beginning.

Configuration items Default value (unit) introduce
dataDir /tmp/zookeeper Path for storing snapshot and myid files
clientPort 2181 Listen to the client request port
tickTime 2000 (ms) The session check interval on clients and heartbeat interval between servers are affected
syncLimit 5 TickTime * syncLimit determines the server heartbeat timeout
initLimit 10 TickTime * initLimit determines the timeout for ACK
dataLogDir And dataDir Path for storing log files
minSessionTimeout tickTime * 2 Minimum client timeout period
maxSessionTimeout tickTime * 20 Maximum client timeout duration
electionAlg 3 Election algorithm (1,2 deprecated)
localSessionsEnabled* false Enabling a local session
localSessionsUpgradingEnabled* false A local session can be upgraded to a global session
clientPortAddress The host of the client requires that any request sent to 2181 be accepted if the host is not specified
secureClientPort SSL security port number
secureClientPortAddress SSL security host
observerMasterPort* Enable the Observer to use followers to learn about the election situation in the cluster
clientPortListenBacklog 50 The maximum length of the queue used by the TCP server to temporarily store requests that have completed the three-way handshake
maxClientCnxns 60 Maximum number of client connections
connectToLearnerMasterLimit 0 Determines the timeout period for the Follower to connect to the Leader
quorumListenOnAllIPs false Whether the server accepts requests from arbitrary IP addresses
peerType The options Observer/Participant determine the node role
syncEnabled true Whether Learner requires a local persistence file
dynamicConfigFile* Dynamically Configuring paths
autopurge.snapRetainCount 3 How many latest Snapshot files to keep
autopurge.purgeInterval 0 (hours) How often to clean snapshot
standaloneEnabled true Whether to allow single-machine mode to start
reconfigEnabled* false Whether to allow dynamic configuration
sslQuorum false Whether to use SSL for communication between clusters
portUnification false Whether an insecure connection is allowed
sslQuorumReloadCertFiles false Enable automatic loading of key update
quorum.auth.enableSasl false Enable SASL authentication between clusters
quorum.auth.serverRequireSasl false
quorum.auth.learnerRequireSasl false
quorum.auth.learner.saslLoginContext QuorumLearner
quorum.auth.server.saslLoginContext QuorumServer
quorum.auth.kerberos.servicePrincipal zkquorum/localhost
quorum.cnxn.threads.size 20 Maximum number of threads in the thread pool that asynchronously establishes connections between clusters
jvm.pause.info-threshold.ms 1000 (ms) INFO Indicates the threshold for suspending statistics
jvm.pause.warn-threshold.ms 10000 (ms) WARN Output pause statistics threshold
jvm.pause.sleep.time.ms 500 (ms) JVM pauses count thread sleep intervals
jvm.pause.monitor* false Whether to enable JVM pause statistics
metricsProvider.className* DefaultMetricsProvider (full path) Statistics implementation classpath
multiAddress.enabled false
multiAddress.reachabilityCheckTimeoutMs 1000 (ms)
multiAddress.reachabilityCheckEnabled true
(Starts with) Server.* The cluster configuration
(Begins) Group * Group configuration
(begins) weight* The weight
MetricsProvider.* User-defined statistics configuration

These are all the official zoo.cfg configuration options in 3.6.2

3.3 Configuring Environment Variables

Java programs can specify environment variables in two ways:

  • You just need to add it at boot time-DpropertyKey=propertyValueCan be
  • ZK also supports an easy way to do thiszoo.cfg(No need to write when specifyingzookeeper.“). As long as they are not in the configuration items defined by ZK in 2.2 above, ZK reads these configuration items at startup and automatically adds them for themzookeeper.Prefix and add to the current environment variable

If the configuration is follower.nodelay, the environment variable can only be added in the first way.

Let’s also take a look at what environment variable configurations ZK defines itself

Configuration items The default value configuration
zookeeper.server.realm* Client Configuration
zookeeper.clientCnxnSocket ClientCnxnSocketNIO (full path) Client configuration, communication implementation class
zookeeper.client.secure true Client Configuration
zookeeper.request.timeout 0 Client configuration, asynchronous API timeout
zookeeper.server.principal Client Configuration
zookeeper.sasl.client.username zookeeper Client Configuration
zookeeper.sasl.client.canonicalize.hostname true Client Configuration
zookeeper.disableAutoWatchReset false Client configuration, session timeout automatically clears watcher
zookeeper.sasl.clientconfig
zookeeper.sasl.client true Enable SASL
zookeeper.ssl(.quorum).authProvider x509 SSL implementation class, quorum is the server configuration, same as below
zookeeper.ssl(.quorum).protocol TLSv1.2
zookeeper.ssl(.quorum).enabledProtocols
zookeeper.ssl(.quorum).ciphersuites Depending on the JVM version
zookeeper.ssl(.quorum).keyStore.location
zookeeper.ssl(.quorum).keyStore.password
zookeeper.ssl(.quorum).keyStore.type
zookeeper.ssl(.quorum).trustStore.location
zookeeper.ssl(.quorum).trustStore.password
zookeeper.ssl(.quorum).trustStore.type
zookeeper.ssl(.quorum).context.supplier.class
zookeeper.ssl(.quorum).hostnameVerification true
zookeeper.ssl(.quorum).crl false
zookeeper.ssl(.quorum).ocsp false
zookeeper.ssl(.quorum).clientAuth
zookeeper.ssl(.quorum).handshakeDetectionTimeoutMillis 5000 (ms)
zookeeper.kinit /usr/bin/kinit
zookeeper.jmx.log4j.disable false Disable JMX log4j
zookeeper.admin.enableServer* true Whether to enable the Admin Server
zookeeper.admin.serverAddress* 0.0.0.0
zookeeper.admin.serverPort* 8080
zookeeper.admin.idleTimeout* 30000
zookeeper.admin.commandURL* /commands
zookeeper.admin.httpVersion* 11
zookeeper.admin.portUnification false
zookeeper.DigestAuthenticationProvider.superDigest Administrator Account password
zookeeper.ensembleAuthName
zookeeper.requireKerberosConfig
zookeeper.security.auth_to_local DEFAULT
(Beginning) Zookeeper.authProvider.* User-defined Scheme verification rules
zookeeper.letAnySaslUserDoX
zookeeper.SASLAuthenticationProvider.superPassword
zookeeper.kerberos.removeHostFromPrincipal
zookeeper.kerberos.removeRealmFromPrincipal
zookeeper.X509AuthenticationProvider.superUser
zookeeper.4lw.commands.whitelist* Four word command whitelist
zookeeper.preAllocSize 65536 * 1024
zookeeper.forceSync yes
zookeeper.fsync.warningthresholdms 1000 (ms) Fsync Alarm threshold
zookeeper.txnLogSizeLimitInKb 1 (KB) Log file size
zookeeper.datadir.autocreate true The data directory is automatically created
zookeeper.db.autocreate true
zookeeper.snapshot.trust.empty false Do not trust empty snapshot files
zookeeper.snapshot.compression.method An empty string Snapshot file compression implementation
zookeeper.commitProcessor.numWorkerThreads Number of CPU core
zookeeper.commitProcessor.shutdownTimeout 5000 (ms)
zookeeper.commitProcessor.maxReadBatchSize – 1
zookeeper.commitProcessor.maxCommitBatchSize 1
zookeeper.fastleader.minNotificationInterval 200 (ms) Timeout for ballot collection (initial)
zookeeper.fastleader.maxNotificationInterval 60000 (ms) Timeout for ballot collection (maximum)
zookeeper.leader.maxTimeToWaitForEpoch – 1
zookeeper.leader.ackLoggingFrequency 1000
zookeeper.testingonly.initialZxid Initialize zxID for testing only!
zookeeper.leaderConnectDelayDuringRetryMs 100 Timeout duration of Leaner connection to the Leader
follower.nodelay true Example Set TCP no delay
zookeeper.forceSnapshotSync false Learner forces synchronization with the Leader using snapshot
zookeeper.leader.maxConcurrentSnapSyncs 10
zookeeper.leader.maxConcurrentDiffSyncs 100
zookeeper.observer.reconnectDelayMs 0 (ms) The Observer delayed reconnecting to the Leader
zookeeper.observer.election.DelayMs 200 (ms) The Observer delayed the commencement of the election
zookeeper.observerMaster.sizeLimit 32 * 1024 * 1024
zookeeper.electionPortBindRetry 3 Number of port connection retries
zookeeper.tcpKeepAlive false Socket keep alive setting
zookeeper.cnxTimeout 5000 (ms) Socket timeout duration
zookeeper.multiAddress.enabled false
zookeeper.multiAddress.reachabilityCheckTimeoutMs 1000 (ms)
zookeeper.multiAddress.reachabilityCheckEnabled true
zookeeper.quorumCnxnTimeoutMs – 1
zookeeper.observer.syncEnabled true Whether the Observer requires local archiving
zookeeper.bitHashCacheSize 10 Bitmap initial cache size
zookeeper.messageTracker.BufferSize 10
zookeeper.messageTracker.Enabled false
zookeeper.pathStats.slotCapacity 60
zookeeper.pathStats.slotDuration 15
zookeeper.pathStats.maxDepth 6
zookeeper.pathStats.sampleRate 0.1
zookeeper.pathStats.initialDelay 5
zookeeper.pathStats.delay 5
zookeeper.pathStats.topPathMax 20
zookeeper.pathStats.enabled false
zookeeper.watcherCleanThreshold 1000
zookeeper.watcherCleanIntervalInSeconds 600
zookeeper.watcherCleanThreadsNum 2
zookeeper.maxInProcessingDeadWatchers – 1
zookeeper.watchManagerName WatchManager (full path)
zookeeper.connection_throttle_tokens 0
zookeeper.connection_throttle_fill_time 1
zookeeper.connection_throttle_fill_count 1
zookeeper.connection_throttle_freeze_time – 1
zookeeper.connection_throttle_drop_increase 0.02
zookeeper.connection_throttle_drop_decrease 0.002
zookeeper.connection_throttle_decrease_ratio 0
zookeeper.connection_throttle_weight_enabled false
zookeeper.connection_throttle_global_session_weight 3
zookeeper.connection_throttle_local_session_weight 1
zookeeper.connection_throttle_renew_session_weight 2
zookeeper.extendedTypesEnabled* false Whether to enable the TTL node type
zookeeper.emulate353TTLNodes* false Whether it is compatible with TTL of 3.5.3
zookeeper.client.portUnification false
zookeeper.netty.server.outstandingHandshake.limit – 1
zookeeper.netty.advancedFlowControl.enabled false
zookeeper.nio.sessionlessCnxnTimeout 10000 (ms)
zookeeper.nio.numSelectorThreads Number of CPU cores / 2 then take the square root
zookeeper.nio.numWorkerThreads Number of CPU cores x 2
zookeeper.nio.directBufferBytes 64 x 1024 (bytes)
zookeeper.nio.shutdownTimeout 5000 (ms)
zookeeper.request_stale_connection_check true
zookeeper.request_stale_latency_check false
zookeeper.request_throttler.shutdownTimeout 10000 (ms)
zookeeper.request_throttle_max_requests 0
zookeeper.request_throttle_stall_time 100
zookeeper.request_throttle_drop_stale true
zookeeper.serverCnxnFactory NIOServerCnxnFactory (full path)
zookeeper.maxCnxns 0
zookeeper.snapshotSizeFactor 0.33
zookeeper.commitLogCount 500
zookeeper.sasl.serverconfig Server
zookeeper.globalOutstandingLimit 1000
zookeeper.enableEagerACLCheck false
zookeeper.skipACL no
zookeeper.allowSaslFailedClients false
zookeeper.sessionRequireClientSASLAuth false
zookeeper.digest.enabled true
zookeeper.closeSessionTxn.enabled true
zookeeper.flushDelay 0
zookeeper.maxWriteQueuePollTime zookeeper.flushDelay / 3
zookeeper.maxBatchSize 1000
zookeeper.intBufferStartingSizeBytes 1024
zookeeper.maxResponseCacheSize 400
zookeeper.maxGetChildrenResponseCacheSize 400
zookeeper.snapCount 100000
zookeeper.snapSizeLimitInKb 4194304 (kilobytes)
zookeeper.largeRequestMaxBytes 100 * 1024 * 1024
zookeeper.largeRequestThreshold – 1
zookeeper.superUser
zookeeper.audit.enable false Whether to enable audit logs
zookeeper.audit.impl.class Log4jAuditLogger (full path) Audit log function implementation class

ZK configuration is still a lot of, some of my TODO here, later have the opportunity to introduce you in detail ~ and quite a part of the configuration of ZK official documentation has been given the explanation, you can check the ZK 3.6.2 configuration document.

Some configurations in ZK use true or false, some use yes or no, obviously developed by two people. Shouldn’t there be a unification? Yes or no is really much more than…

Iv. Conclusion of the series

Thank you for being here and accompanying the series from the beginning to the present! It has been more than three months since I had the idea to establish the project, then communicated with the balls, then started writing, and finally I wrote this conclusion (it should be later when you see it). Now looking back at what I wrote before, I feel quite impressed.

(Screenshot from the video of Student He at -B station)

If I had my own self-control, this thing would have gotten cold by myself. Thank you for your help and encouragement. About ZK research over a period of time before I do, but in today’s view, the research also is actually skin-deep (may still), is a lot of things when I finish this time now to learn, harvest very much, is the most intuitive feelings, I go out after the interview will not be afraid of ZK related problem.

Thank you for your company for the last 3 months. This series is over! If there are any other open source frameworks and technologies you’d like to learn, let us know in the comments and we’ll continue to arrange a free dry tutorial.

And finally, give it a big thumbs up!


Follow the HelloGitHub public account to receive the first updates.

There are more open source projects and treasure projects waiting to be discovered.