zookeeper源碼解析-admin服務

zk版本:3.5.6

1.引入

單機啓動時會創建admin對象

  adminServer = AdminServerFactory.createAdminServer();
  // 設置zookeeper服務
  adminServer.setZooKeeperServer(zkServer);
  // 服務啓動,監聽客戶端請求
  adminServer.start();

通過AdminServerFactory.createAdminServer()可知,最終會創建JettyAdminServer對象,並通過這個對象提供zk的admin服務。所以來看看這個類到底是如何運行的。

2.JettyAdminServer請求和響應

    public JettyAdminServer(String address, int port, int timeout, String commandUrl) {
        ..... // 設置jetty連接

        // jetty admin的處理邏輯是通過CommandServlet實現的,請求地址(/commands/*)
        context.addServlet(new ServletHolder(new CommandServlet()), commandUrl + "/*");
    }

    /**
     * 處理jetty admin的請求
     */
   private class CommandServlet extends HttpServlet {
        private static final long serialVersionUID = 1L;

        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            // 請求命令就是通過指定路徑實現的,比如commands/config,cmd=config
            String cmd = request.getPathInfo();
            // 獲取所有的請求路徑
            if (cmd == null || cmd.equals("/")) {
                // No command specified, print links to all commands instead
                for (String link : commandLinks()) {
                    response.getWriter().println(link);
                    response.getWriter().println("<br/>");
                }
                return;
            }
            cmd = cmd.substring(1);

            // 請求參數
            Map<String, String[]> parameterMap = request.getParameterMap();
            Map<String, String> kwargs = new HashMap<String, String>();
            for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
                kwargs.put(entry.getKey(), entry.getValue()[0]);
            }

            // 請求參數作爲運行命令的參數
            CommandResponse cmdResponse = Commands.runCommand(cmd, zkServer, kwargs);

            // Format and print the output of the command

            // json格式輸出
            CommandOutputter outputter = new JsonOutputter();
            response.setStatus(HttpServletResponse.SC_OK);
            response.setContentType(outputter.getContentType());

            // 響應指定格式的輸出結果
            outputter.output(cmdResponse, response.getWriter());
        }
    }

admin服務創建和處理流程:

  1. 創建jetty server
  2. 監聽請求路徑commands/*,設置admin請求處理器CommandServlet
  3. 根據請求路徑和請求參數,執行對象的命令(Commands保存所有的命令執行邏輯)
  4. 響應指定格式的輸出結果,這裏指定json格式輸出

3.admin命令執行

在Commands類中保存所有的命令的定義和執行,jettyAdminServer中使用到了Commands.runCommand,我們直接從這個方法介紹:

Commands.java
----------------

 public static CommandResponse runCommand(String cmdName, ZooKeeperServer zkServer, Map<String, String> kwargs) {
     
        ....
        // 運行命令
        return commands.get(cmdName).run(zkServer, kwargs);
    }

commands在Commands類靜態初始化的時候就會添加所有可以使用的命令集合。這裏我們以其中一個admin請求說明問題:

http://localhost:8080/commands/configuration

此時: cmdName=configuration,通過反推很容易得到commands.get(cmdName)對應的就是org.apache.zookeeper.server.admin.Commands.ConfCommand對象。
所以看看這個類是如果處理的:

Commands.java
--------------

    public static class ConfCommand extends CommandBase {
        public ConfCommand() {
              // 通過CommandBase構造器可知,CommandBase=configuration,names=conf,config super(Arrays.asList("configuration", "conf", "config"));
        }

        @Override
        public CommandResponse run(ZooKeeperServer zkServer, Map<String, String> kwargs) {
           // 初始化響應對象
            CommandResponse response = initializeResponse();
            // zkServer的配置 response.putAll(zkServer.getConf().toMap());
            return response;
        }
    }

可以看出:commands/configure請求最終獲取到的是zkServer的配置信息。

最終響應的結果示例:

{
  "client_port" : 2181,
  "data_dir" : "D:\\tmp\\zookeeper\\version-2",
  "data_log_dir" : "D:\\tmp\\zookeeper\\version-2",
  "tick_time" : 2000,
  "max_client_cnxns" : 60,
  "min_session_timeout" : 4000,
  "max_session_timeout" : 40000,
  "server_id" : 0,
  "command" : "configuration",
  "error" : null
}

4.所有命令示例

commands/connection_stat_reset 
{
  "command" : "connection_stat_reset",
  "error" : null
}

commands/connections
{
  "connections" : [ ],
  "secure_connections" : [ ],
  "command" : "connections",
  "error" : null
}

commands/dirs
{
  "datadir_size" : 67109730,
  "logdir_size" : 67109730,
  "command" : "dirs",
  "error" : null
}

commands/dump
{
  "expiry_time_to_session_ids" : { },
  "session_id_to_ephemeral_paths" : { },
  "command" : "dump",
  "error" : null
}

commands/get_trace_mask
{
  "tracemask" : 306,
  "command" : "get_trace_mask",
  "error" : null
}

commands/is_read_only
{
  "read_only" : false,
  "command" : "is_read_only",
  "error" : null
}

commands/monitor
{
  "version" : "3.5.6-1, built on 2019-11-11",
  "avg_latency" : 0,
  "max_latency" : 0,
  "min_latency" : 0,
  "packets_received" : 0,
  "packets_sent" : 0,
  "num_alive_connections" : 0,
  "outstanding_requests" : 0,
  "server_state" : "standalone",
  "znode_count" : 5,
  "watch_count" : 0,
  "ephemerals_count" : 0,
  "approximate_data_size" : 44,
  "open_file_descriptor_count" : -1,
  "max_file_descriptor_count" : -1,
  "last_client_response_size" : -1,
  "max_client_response_size" : -1,
  "min_client_response_size" : -1,
  "command" : "monitor",
  "error" : null
}

commands/ruok 
{
  "command" : "ruok",
  "error" : null
}

commands/server_stats
{
  "version" : "3.5.6-1, built on 2019-11-11",
  "read_only" : false,
  "server_stats" : {
    "packets_sent" : 0,
    "packets_received" : 0,
    "max_latency" : 0,
    "min_latency" : 0,
    "fsync_threshold_exceed_count" : 0,
    "client_response_stats" : {
      "last_buffer_size" : -1,
      "min_buffer_size" : -1,
      "max_buffer_size" : -1
    },
    "provider_null" : false,
    "avg_latency" : 0,
    "server_state" : "standalone",
    "log_dir_size" : 67109730,
    "data_dir_size" : 67109730,
    "num_alive_client_connections" : 0,
    "last_processed_zxid" : 8,
    "outstanding_requests" : 0
  },
  "client_response" : {
    "last_buffer_size" : -1,
    "min_buffer_size" : -1,
    "max_buffer_size" : -1
  },
  "node_count" : 5,
  "command" : "server_stats",
  "error" : null
}

commands/set_trace_mask
{
  "error" : "setTraceMask requires long traceMask argument",
  "command" : "set_trace_mask"
}

commands/stat_reset
{
  "command" : "stat_reset",
  "error" : null
}

commands/stats
{
  "version" : "3.5.6-1, built on 2019-11-11",
  "read_only" : false,
  "server_stats" : {
    "packets_sent" : 0,
    "packets_received" : 0,
    "max_latency" : 0,
    "min_latency" : 0,
    "fsync_threshold_exceed_count" : 0,
    "client_response_stats" : {
      "last_buffer_size" : -1,
      "min_buffer_size" : -1,
      "max_buffer_size" : -1
    },
    "provider_null" : false,
    "avg_latency" : 0,
    "server_state" : "standalone",
    "log_dir_size" : 67109730,
    "data_dir_size" : 67109730,
    "num_alive_client_connections" : 0,
    "last_processed_zxid" : 8,
    "outstanding_requests" : 0
  },
  "client_response" : {
    "last_buffer_size" : -1,
    "min_buffer_size" : -1,
    "max_buffer_size" : -1
  },
  "node_count" : 5,
  "connections" : [ ],
  "command" : "stats",
  "error" : null
}

commands/watch_summary
{
  "num_connections" : 0,
  "num_paths" : 0,
  "num_total_watches" : 0,
  "command" : "watch_summary",
  "error" : null
}

commands/watches
{
  "session_id_to_watched_paths" : { },
  "command" : "watches",
  "error" : null
}

commands/watches_by_path
{
  "path_to_session_ids" : { },
  "command" : "watches_by_path",
  "error" : null
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章