Showing posts with label ZooKeeper. Show all posts
Showing posts with label ZooKeeper. Show all posts

Thursday, January 11, 2018

Zookeeper Code



http://www.cnblogs.com/francisYoung/p/5225703.html
http://blog.xiaohansong.com/2016/08/22/zookeeper-watch-async/
初学 Zookeeper 会发现客户端有两种回调方式: Watcher 和 AsyncCallback
zooKeeper.getData(root, new Watcher() {
            public void process(WatchedEvent event) {

            }
        }, new AsyncCallback.DataCallback() {
            public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) {

            }
        }, null);
可以看到,getData方法可以同时设置两个回调:Watcher 和 AsyncCallback,同样是回调,它们的区别是什么呢?要解决这个问题,我们就得从这两个接口的功能入手。
  • Watcher:Watcher是用于监听节点,session 状态的,比如getData对数据节点a设置了watcher,那么当a的数据内容发生改变时,客户端会收到NodeDataChanged通知,然后进行watcher的回调。
  • AsyncCallback:AsyncCallback是在以异步方式使用 ZooKeeper API 时,用于处理返回结果的。例如:getData同步调用的版本是:byte[] getData(String path, boolean watch,Stat stat),异步调用的版本是:void getData(String path,Watcher watcher,AsyncCallback.DataCallback cb,Object ctx),可以看到,前者是直接返回获取的结果,后者是通过AsyncCallback回调处理结果的。
ClientWatchManager中有四种Watcher
  • defaultWatcher:创建Zookeeper连接时传入的Watcher,用于监听 session 状态
  • dataWatches:存放getData传入的Watcher
  • existWatches:存放exists传入的Watcher,如果节点已存在,则Watcher会被添加到dataWatches
  • childWatches:存放getChildren传入的Watcher
从代码上可以发现,监听器是存在HashMap中的,key是节点名称path,value是Set<Watcher>
private final Map<String, Set<Watcher>> dataWatches =
        new HashMap<String, Set<Watcher>>();

ClientWatchManager只有一个方法,那就是materialize,它根据事件类型type和path返回监听该节点的特定类型的Watcher。
每次返回都会从HashMap中移除节点对应的Watcher,例如:addTo(dataWatches.remove(clientPath), result);,这就是为什么Watcher是一次性的原因(defaultWatcher除外)。值得注意的是,由于使用的是HashSet存储Watcher,重复添加同一个实例的Watcher也只会被触发一次。
  • SendThread:负责 IO 操作,包括发送,接受响应,发送 ping 等。
  • EventThread:负责处理事件,执行回调函数。
Zookeeper 客户端中Watcher和AsyncCallback都是异步回调的方式,但它们回调的时机是不一样的,前者是由服务器发送事件触发客户端回调,后者是在执行了请求后得到响应后客户端主动触发的。它们的共同点在于都需要在获取了服务器响应之后,由SendThread写入EventThread的waitingEvents中,然后由EventThread逐个从事件队列中获取并处理。

https://zookeeper.apache.org/doc/r3.1.2/zookeeperProgrammers.html

What ZooKeeper Guarantees about Watches

  • Watches are ordered with respect to other events, other watches, and asynchronous replies. The ZooKeeper client libraries ensures that everything is dispatched in order.
  • A client will see a watch event for a znode it is watching before seeing the new data that corresponds to that znode.

  • The order of watch events from ZooKeeper corresponds to the order of the updates as seen by the ZooKeeper service.


Things to Remember about Watches

  • Watches are one time triggers; if you get a watch event and you want to get notified of future changes, you must set another watch.
  • Because watches are one time triggers and there is latency between getting the event and sending a new request to get a watch you cannot reliably see every change that happens to a node in ZooKeeper. Be prepared to handle the case where the znode changes multiple times between getting the event and setting the watch again. (You may not care, but at least realize it may happen.)
  • A watch object, or function/context pair, will only be triggered once for a given notification. For example, if the same watch object is registered for an exists and a getData call for the same file and that file is then deleted, the watch object would only be invoked once with the deletion notification for the file.
  • When you disconnect from a server (for example, when the server fails), you will not get any watches until the connection is reestablished. For this reason session events are sent to all outstanding watch handlers. Use session events to go into a safe mode: you will not be receiving events while disconnected, so your process should act conservatively in that mode.


http://sel-fish.net/2016/11/29/zk-len-err/

1. 根据报错查找对应的源码


1
2
3
4
5
6
7
ack 'Len error'
src/java/main/org/apache/zookeeper/server/NettyServerCnxn.java
392: throw new IOException("Len error " + len);
src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java
540: throw new IOException("Len error " + len);

想要的信息在NIOServerCnxn.java中:

1
2
3
4
5
vim src/java/main/org/apache/zookeeper/server/NIOServerCnxn.java +540
539 if (len < 0 || len > BinaryInputArchive.maxBuffer) {
540 throw new IOException("Len error " + len);
541 }

继续dig maxBuffer:

1
2
3
4
5
6
7
8
9
10
ack maxBuffer
src/java/main/org/apache/jute/BinaryInputArchive.java
87: static public final int maxBuffer = Integer.getInteger("jute.maxbuffer", 0xfffff);
122: // Since this is a rough sanity check, add some padding to maxBuffer to
126: if (len < 0 || len > maxBuffer + 1024) {
src/java/main/org/apache/zookeeper/ClientCnxn.java
107: * jute.maxBuffer value. To avoid this we instead split the watch
110: * with respect to the server's 1MB default for jute.maxBuffer.

可以看到maxBuffer和jute.maxbuffer这个环境变量相关,默认值是1048575,那么现在可以确认是客户端发出来的包太大,那么为什么会这样大呢?如果你足够幸运的话,会发现ack maxBuffer的第二个匹配,内容为:

1
2
3
4
5
6
7
8
9
104 /* ZOOKEEPER-706: If a session has a large number of watches set then
105 * attempting to re-establish those watches after a connection loss may
106 * fail due to the SetWatches request exceeding the server's configured
107 * jute.maxBuffer value. To avoid this we instead split the watch
108 * re-establishement across multiple SetWatches calls. This constant
109 * controls the size of each call. It is set to 128kB to be conservative
110 * with respect to the server's 1MB default for jute.maxBuffer.
111 */
112 private static final int SET_WATCHES_MAX_LENGTH = 128 * 1024;

这时候,如果足够幸运去Jira ZOOKEEPER-706看一看描述,会发现报错完全一致,剩下的就是重现、确认修复版本了。
其实经历过的很多问题排查从事后看都走了或多或少的弯路,重要的是需要尽量多地使用工具尽快地把自己从弯路上掰过来。

1. 怀疑业务方代码问题

服务端是一个公共集群,服务于多个业务,重启之后只有一个业务报错,其它业务都没有问题,所以怀疑业务方代码有问题。从事后来看,这个是因为出错的业务方watch的path太多导致的,和数据有关系,而不是代码的原因。当然,看了许久,业务代码是没问题的。

2. 怀疑业务方使用的版本问题

还是之前的思路,代码没问题就怀疑是版本的问题,找了一个很牵强的点,Curator 2.8.0里面指定依赖的是ZooKeeper 3.4.5,而业务方使用的是3.4.6。于是试图沿着这个方向去重现。结果显而易见,无法重现问题,卡壳了。

3. tcpdump抓包分析问题

由于后续又重启了Server导致问题在线上复现,有几台预发的业务机器未重启,提供了错误现场。使用tcpdump抓包分析后,可以看到
从内容看,第41行发的请求包,type为101(按照zk的协议,除了ConnectRequest,每个请求包的包头都会有type来标志请求包的类型),根据src/java/main/org/apache/zookeeper/ZooDefs.java中的代码显示,是setWatches请求。
这时候大概有些眉目了,应该是setWatches请求包太大导致的错误,于是转入了上述“正确的姿势”。
  • Q:为什么重启的时候不会有这个问题,而重连的时候会有这个问题?
  • A:重新启动的逻辑和重连的逻辑不一样。重启的时候会首先调用getChildren2去获取path的子节点,然后逐一去setWatches。重连时会把之前的watches批量set一遍。
  1. 尽量先翻代码。先Google固然也是好习惯,但是容易陷入寻找类似问题的泥沼中,特别是在关键词挑选和组织得不是很好的情况下。
  2. 维护的开源产品还是需要多看看upstream的更新。



Tuesday, September 13, 2016

Zookeeper Practice



https://community.hortonworks.com/questions/12942/how-to-clean-up-files-in-zookeeper-directory.html

Ongoing Data Directory Cleanup

The ZooKeeper Data Directory contains files which are a persistent copy of the znodes stored by a particular serving ensemble. These are the snapshot and transactional log files. As changes are made to the znodes these changes are appended to a transaction log, occasionally, when a log grows large, a snapshot of the current state of all znodes will be written to the filesystem. This snapshot supercedes all previous logs.
A ZooKeeper server will not remove old snapshots and log files, this is the responsibility of the operator. Every serving environment is different and therefore the requirements of managing these files may differ from install to install (backup for example).
The PurgeTxnLog utility implements a simple retention policy that administrators can use. The API docs contains details on calling conventions (arguments, etc...).
In the following example the last count snapshots and their corresponding logs are retained and the others are deleted. The value of <count> should typically be greater than 3 (although not required, this provides 3 backups in the unlikely event a recent log has become corrupted). This can be run as a cron job on the ZooKeeper server machines to clean up the logs daily.

 java -cp zookeeper.jar:log4j.jar:conf org.apache.zookeeper.server.PurgeTxnLog <dataDir> <snapDir> -n <count>

java -cp "*" org.apache.zookeeper.server.PurgeTxnLog  ~/src/apache/solr/solr-5.3.0/example/cloud/node1/solr/zoo_data/ ~/src/apache/solr/solr-5.3.0/example/cloud/node1/solr/zoo_data/ -n 3

https://community.hortonworks.com/questions/12942/how-to-clean-up-files-in-zookeeper-directory.html


The ZooKeeper server continually saves znode snapshot files and, optionally, transactional logs in a Data Directory to enable you to recover data. It's a good idea to back up the ZooKeeper Data Directory periodically. Although ZooKeeper is highly reliable because a persistent copy is replicated on each server, recovering from backups may be necessary if a catastrophic failure or user error occurs.
When you use the default configuration, the ZooKeeper server does not remove the snapshots and log files, so they will accumulate over time. You will need to clean up this directory occasionally, taking into account on your backup schedules and processes. To automate the cleanup, a zkCleanup.sh script is provided in the bin directory of thezookeeper base package. Modify this script as necessary for your situation. In general, you want to run this as a cron task based on your backup schedule.
The data directory is specified by the dataDir parameter in the ZooKeeper configuration file, and the data log directory is specified by the dataLogDir parameter.

The ZooKeeper API is built around a ZooKeeper handle that is passed to every API call. This handle represents a session with ZooKeeper.
An object we need to create that will receive session events.

In this algorithm, all potential masters try to create the /master znode, but only one will succeed. The process that succeeds becomes the master.
We need two things to create /master. First, we need the initial data for the znode. Usually we put some information about the process that becomes the master in this initial data
void checkMaster() {
    zk.getData("/master", false, masterCheckCallback, null);
}

public void runForMaster() {
    LOG.info("Running for master");
    zk.create("/master",
            serverId.getBytes(),
            Ids.OPEN_ACL_UNSAFE,
            CreateMode.EPHEMERAL,
            masterCreateCallback,
            null);
}

zk.create("/master", serverId.getBytes(),
OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);

DataCallback masterCheckCallback = new DataCallback() {
  void processResult(int rc, String path, Object ctx, byte[] data,
                     Stat stat) {
      switch(Code.get(rc)) {
      case CONNECTIONLOSS:
          checkMaster();
          return;
      case NONODE:
          runForMaster();
          return;
      }
  }
}

void checkMaster() {
  zk.getData("/master", false, masterCheckCallback, null);
}


void submitTask(String task, TaskObject taskCtx){
    taskCtx.setTask(task);
    zk.create("/tasks/task-",
            task.getBytes(),
            Ids.OPEN_ACL_UNSAFE,
            CreateMode.PERSISTENT_SEQUENTIAL,
            createTaskCallback,
            taskCtx);
}
First, the sequence number will indicate the order in which the tasks were queued. Second, the sequence number will create unique paths for tasks with minimal work.

ZooKeeper helps organize distributed state and provides a framework for handling failures; it doesn’t make failures go away

The primary mechanism ZooKeeper provides to deal with changes is watches. With watches, a client registers its request to receive a one-time notification of a change to a given znode. For example, we can have the primary master create an ephemeral znode representing the master lock, and the backup masters register a watch for the existence of the master lock. Once the backup masters receive their notifications, they can start a new master election by trying to create a new ephemeral znode to represent the master lock

Watches and notifications form a general mechanism that enables clients to observe changes by other clients without having to continually poll ZooKeeper.

The notification comes in the form of a callback to the application client.
Missing events is typically not a problem because any changes that have occurred during the period between receiving a notification and registering a new watch can be captured by reading the state of ZooKeeper directly.

having one notification amortized across multiple events is a positive aspect. It makes the notification mechanism much more lightweight than sending a notification for every event for applications that have a high rate of updates.

There are two types of watches: data watches and child watches. Creating, deleting, or setting the data of a znode successfully triggers a data watch. Both exists and getData set data watches. Only getChildren sets child watches, which are triggered when a child znode is either created or deleted.

The Stat structure contains information about the znode, such as the timestamp of the last change (zxid) that changed this znode and the number of children in the znode.

it is possible for the /master znode to be deleted between the execution of the create callback and the execution of the exists operation. Consequently, we need to check whether stat is null whenever the response from exists is positive (return code is OK). stat is null when the node does not exist.


Curator implements recipes for primitives such as locks, barriers, and caches. For ZooKeeper operations like create, delete, getData, etc., it streamlines programming by allowing us to chain calls, a programming style often called fluent. It also provides namespaces, automatic reconnection, and other facilities that make applications more robust.

zkc.create().withMode(CreateMode.PERSISTENT).forPath("/mypath", new byte[0]);
zkc.create().inBackground().withMode(CreateMode.PERSISTENT).forPath("/mypath",
    new byte[0]);
zkc.getData().inBackground().watched().forPath("/mypath");

client = CuratorFrameworkFactory.newClient(hostPort, retryPolicy);
client.getCuratorListenable().addListener(masterListener);

client.getUnhandledErrorListenable().addListener(errorsListener);

Curator exposes a different set of states than ZooKeeper. It has, for example, a SUSPENDED state, and it uses LOST to represent session expiration.

When dealing with state changes, our recommendation is in general to halt all operations of the master because we do not know if the ZooKeeper client will be able to reconnect before the session expires, and even if it does, the client might not be the primary master any more. It is safer to play conservatively in the case of a disconnection.

Sequential znodes
CreateBuilder provides a withProtection call that tells the Curator client to prefix the sequential znode with a unique identifier. If the create fails, the client retries the operation, and as part of retrying it verifies whether there is already a znode with the unique identifier.

the DeleteBuilder interface defines a guaranteed call.
http://www.voidcn.com/blog/kiss_the_sun/article/p-4913080.html
Curator中选举分为两种: Leader Latch和Leader Election
Leader Latch
很简单的选举算法。随机从候选者中选择一台作为leader, 选中后除非leader自己 调用close()释放leadership,否则其他的候选者不能成为leader

释放leadership
只能通过close()释放leadership, 只有leader将leadership释放时,其他的候选者才有机会被选为leader

leaderLatch.close();
错误处理
LeaderLatch通过增加了一个ConnectionStateListener监听连接问题。如果出现SUSPENDED或者LOST,leader会报告自己不再是leader(直到重新建立连接,否则不会有leader)。如果LOST的连接被重新建立即RECONNECTED,leaderLatch会删除先前的zNode并重新建立zNode。
强烈建议LeaderLatch使用时注册一个ConnectionStateListener。

leaderLatch.start();
一旦启动,LeaderLatch会与其他有相同latchpath的候选者协商,从中随机选中一个作为leader. 可以调用实例的hasLeadership()判断该实例是否为leader。


通过LeaderSelectorListener可以对领导权进行控制, 在适当的时候释放领导权,这样每个节点都有可能获得领导权。 而LeaderLatch一根筋到死, 除非调用close方法,否则它不会释放领导权。
leaderSelector.start();
一旦启动,如果获取了leadership的话,takeLeadership()会被调用,只有当leader释放了leadership的时候,takeLeadership()才会返回。

释放
调用close()释放 leadership

leaderSelector.close();
错误处理
LeaderSelectorListener类继承了ConnectionStateListener。一旦LeaderSelector启动,它会向curator客户端添加监听器。 使用LeaderSelector必须时刻注意连接的变化。一旦出现连接问题如SUSPENDED,curator实例必须确保它可能不再是leader,直至它重新收到RECONNECTED。如果LOST出现,curator实例不再是leader并且其takeLeadership()应该直接退出。
建议重要:推荐的做法是,如果发生SUSPENDED或者LOST连接问题,最好直接抛CancelLeadershipException,此时,leaderSelector实例会尝试中断并且取消正在执行takeLeadership()方法的线程。 建议扩展LeaderSelectorListenerAdapter, LeaderSelectorListenerAdapter中已经提供了推荐的处理方式 。

http://curator.apache.org/curator-recipes/distributed-id-queue.html
https://github.com/yiming187/curator-example/blob/master/src/main/java/com/ctrip/zk/curator/example/DistributedDelayQueueExample.java
https://github.com/yiming187/curator-example/blob/master/src/main/java/com/ctrip/zk/curator/example/LeaderSelectorExample.java

http://www.cnblogs.com/francisYoung/p/5458615.html
原声的ZooKeeper 的CRUD API有同步和异步之分,对于异步API,需要传递AsyncCallback回调。对于getData,getChildren,exists这三个API,还可以设置Watcher。这些功能在Curator中是如何实现的?
在Curator中,可以通过如下三种方式来异步获取结果:
 1.inBackground()+CuratorListener
 2.inBackground(new BackgroundCallback(){ public void processResult(CuratorFramework client,CuratorEvent event){}})
 3.inBackground(newBackgroundCallback(){},Executor)

以inBackground()+CuratorListener这种方式来使用异步API,如下:
复制代码
             client.getCuratorListenable().addListener(new CuratorListener(){

                @Override
                public void eventReceived(CuratorFramework client, CuratorEvent event) throws Exception {
                     // TODO Auto-generated method stub
                    if(event.getType()==CuratorEventType.CREATE){
                        System.out.println("create path="+event.getPath()+",code="+event.getResultCode());
                    }else if(event.getType()==CuratorEventType.GET_DATA){
                        System.out.println("get path="+event.getPath()+",data="+new String(event.getData()));
                    }else if(event.getType()==CuratorEventType.SET_DATA){
                        
                        System.out.println("set path="+event.getPath()+",data="+new String(client.getData().forPath(event.getPath()))/*+",data="+new String(event.getData())*/);
                    }else if(event.getType()==CuratorEventType.DELETE){
                        System.out.println("delete path="+event.getPath());
                    }
                    
    
                }});
复制代码

接下来client的所有以inBackground()方式使用的API,其异步处理结果都是通过这个CuratorListener来处理。

在第二中方式inBackground(BackgroundCallback)中,如下:
复制代码
client.create()
            .creatingParentsIfNeeded()
            .withProtection()
            .withMode(CreateMode.EPHEMERAL)
            .inBackground(new BackgroundCallback(){

                @Override
                public void processResult(CuratorFramework client, CuratorEvent event) throws Exception {
                    // TODO Auto-generated method stub
                  if(event.getType()==CuratorEventType.CREATE){
                      System.out.println("code:"+event.getResultCode()+"path:"+event.getPath()); 
                      
                      //client.getData().inBackground().forPath(event.getPath());
                  }
                }  
              } )
            .forPath("/francis/tmp/a","wbs".getBytes());
          



https://github.com/Netflix/curator/issues/297
Exception in thread "main" java.lang.NoSuchMethodError: org.apache.zookeeper.server.quorum.flexible.QuorumMaj.<init>(Ljava/util/Map;)V
    at org.apache.curator.framework.imps.EnsembleTracker.<init>(EnsembleTracker.java:54)
    at org.apache.curator.framework.imps.CuratorFrameworkImpl.<init>(CuratorFrameworkImpl.java:156)
    at org.apache.curator.framework.CuratorFrameworkFactory$Builder.build(CuratorFrameworkFactory.java:136)
    at org.miaohong.jbfs.store.server.controller.Test.main(Test.java:47)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
For this particular issue, you're probably using Curator 3.0 which requires ZK 3.5.x. Use Curator 2.9.x instead.


https://github.com/apache/curator/tree/master/curator-examples/src/main/java/framework
http://www.cnblogs.com/zhwbqd/p/3969161.html
正确设置java max heap size , 对于防止swaping很重要, 频繁的进行磁盘交换将会大幅影响性能, 由于Zk是有序的, 如果一个request hit到磁盘, 那么其他后续的一定也是到磁盘
防止swapping, 尝试设置heap zise到物理内存大小, 留给OS和cache一点空间, 最好的做法是进行性能测试, 如果不行的话, 建议是4G的机器, 3G的heap size, 大约3/4左右
http://www.xuehuile.com/blog/aec50c9a085b42f5a97144fd90551170.html

http://www.xuehuile.com/blog/aec50c9a085b42f5a97144fd90551170.html
http://blog.5ibc.net/p/15486.html
257                List<String> children = curator.getChildren().usingWatcher(new ZKWatcher(parentPath,path)).forPath(path);
258                if(children != null && children.size() > 0) {
259                    for(String child : children) {
260                        String childPath = parentPath + "/" + child;
261                        byte[] b = curator.getData().usingWatcher(new ZKWatcher(parentPath,childPath))
262                                .forPath(childPath);
263                        String value = new String(b,"utf-8");
264                        if(StringUtils.isNotBlank(value)) {
265                            cacheMap.put(childPath, value);
266                        }
267                    }
268                }
269                
curator.delete().inBackground().forPath(path);


http://qindongliang.iteye.com/blog/2122764
  1.         zkclient.setData().forPath(path,content.getBytes());      
  1.         zkclient.delete().guaranteed().deletingChildrenIfNeeded().forPath(path);  
  1.         if(zkclient.checkExists().forPath(path)==null){  
https://cyberroadie.wordpress.com/2011/11/24/implementing-leader-election-with-zookeeper/

http://www.cnblogs.com/good-temper/p/5656866.html
目前分布式系统已经很流行了,一些开源框架也被广泛应用,如dubbo、Motan等。对于一个分布式服务,最基本的一项功能就是服务的注册和发现,而利用zk的EPHEMERAL节点则可以很方便的实现该功能。EPHEMERAL节点正如其名,是临时性的,其生命周期是和客户端会话绑定的,当会话连接断开时,节点也会被删除。下边我们就来实现一个简单的分布式server:
server:
  1. 服务启动时,创建zk连接,并在go_servers节点下创建一个新节点,节点名为"ip:port",完成服务注册
  2. 服务结束时,由于连接断开,创建的节点会被删除,这样client就不会连到该节点
client:
  1. 先从zk获取go_servers节点下所有子节点,这样就拿到了所有注册的server
  2. 从server列表中选中一个节点(这里只是随机选取,实际服务一般会提供多种策略),创建连接进行通信

Labels

Review (572) System Design (334) System Design - Review (198) Java (189) Coding (75) Interview-System Design (65) Interview (63) Book Notes (59) Coding - Review (59) to-do (45) Linux (43) Knowledge (39) Interview-Java (35) Knowledge - Review (32) Database (31) Design Patterns (31) Big Data (29) Product Architecture (28) MultiThread (27) Soft Skills (27) Concurrency (26) Cracking Code Interview (26) Miscs (25) Distributed (24) OOD Design (24) Google (23) Career (22) Interview - Review (21) Java - Code (21) Operating System (21) Interview Q&A (20) System Design - Practice (20) Tips (19) Algorithm (17) Company - Facebook (17) Security (17) How to Ace Interview (16) Brain Teaser (14) Linux - Shell (14) Redis (14) Testing (14) Tools (14) Code Quality (13) Search (13) Spark (13) Spring (13) Company - LinkedIn (12) How to (12) Interview-Database (12) Interview-Operating System (12) Solr (12) Architecture Principles (11) Resource (10) Amazon (9) Cache (9) Git (9) Interview - MultiThread (9) Scalability (9) Trouble Shooting (9) Web Dev (9) Architecture Model (8) Better Programmer (8) Cassandra (8) Company - Uber (8) Java67 (8) Math (8) OO Design principles (8) SOLID (8) Design (7) Interview Corner (7) JVM (7) Java Basics (7) Kafka (7) Mac (7) Machine Learning (7) NoSQL (7) C++ (6) Chrome (6) File System (6) Highscalability (6) How to Better (6) Network (6) Restful (6) CareerCup (5) Code Review (5) Hash (5) How to Interview (5) JDK Source Code (5) JavaScript (5) Leetcode (5) Must Known (5) Python (5)

Popular Posts