Skip to content

Commit

Permalink
Fix to keep zk connection alive while reading 300K child nodes.
Browse files Browse the repository at this point in the history
  • Loading branch information
gitorko committed Jul 3, 2015
1 parent 71e8ec3 commit 36788ef
Show file tree
Hide file tree
Showing 10 changed files with 156 additions and 64 deletions.
3 changes: 3 additions & 0 deletions config.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ jdbcPwd=manager
loginMessage=Please login using admin/manager or appconfig/appconfig.
#session timeout 5 mins/300 secs.
sessionTimeout=300
#Default 5 seconds to keep short lived zk sessions. If you have large data then the read will take more than 30 seconds so increase this accordingly.
#A bigger zkSessionTimeout means the connection will be held longer and resource consumption will be high.
zkSessionTimeout=5
#Block PWD exposure over rest call.
blockPwdOverRest=false
#ignore rest of the props below if https=false.
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/deem/zkui/controller/Export.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t
String zkPath = request.getParameter("zkPath");
StringBuilder output = new StringBuilder();
output.append("#App Config Dashboard (ACD) dump created on :").append(new Date()).append("\n");
Set<LeafBean> leaves = ZooKeeperUtil.INSTANCE.exportTree(zkPath, ServletUtil.INSTANCE.getZookeeper(request, response, zkServerLst[0], globalProps.getProperty("defaultAcl")), authRole);
Set<LeafBean> leaves = ZooKeeperUtil.INSTANCE.exportTree(zkPath, ServletUtil.INSTANCE.getZookeeper(request, response, zkServerLst[0], globalProps), authRole);
for (LeafBean leaf : leaves) {
output.append(leaf.getPath()).append('=').append(leaf.getName()).append('=').append(ServletUtil.INSTANCE.externalizeNodeValue(leaf.getValue())).append('\n');
}// for all leaves
Expand Down
33 changes: 18 additions & 15 deletions src/main/java/com/deem/zkui/controller/Home.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
*/
package com.deem.zkui.controller;

import com.deem.zkui.dao.Dao;
import com.deem.zkui.utils.ServletUtil;
import com.deem.zkui.utils.ZooKeeperUtil;
import com.deem.zkui.vo.LeafBean;
import com.deem.zkui.vo.ZKNode;
import freemarker.template.TemplateException;
import java.io.IOException;
import java.util.Arrays;
Expand All @@ -32,10 +37,6 @@
import javax.servlet.http.HttpServletResponse;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooKeeper;
import com.deem.zkui.dao.Dao;
import com.deem.zkui.utils.ServletUtil;
import com.deem.zkui.utils.ZooKeeperUtil;
import com.deem.zkui.vo.LeafBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -56,7 +57,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t
Map<String, Object> templateParam = new HashMap<>();
String zkPath = request.getParameter("zkPath");
String navigate = request.getParameter("navigate");
ZooKeeper zk = ServletUtil.INSTANCE.getZookeeper(request, response, zkServerLst[0], globalProps.getProperty("defaultAcl"));
ZooKeeper zk = ServletUtil.INSTANCE.getZookeeper(request, response, zkServerLst[0], globalProps);
List<String> nodeLst;
List<LeafBean> leafLst;
String currentPath, parentPath, displayPath;
Expand All @@ -67,15 +68,17 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t

if (zkPath == null || zkPath.equals("/")) {
templateParam.put("zkpath", "/");
nodeLst = ZooKeeperUtil.INSTANCE.listFolders(zk, "/");
leafLst = ZooKeeperUtil.INSTANCE.listLeaves(zk, "/", authRole);
ZKNode zkNode = ZooKeeperUtil.INSTANCE.listNodeEntries(zk, "/", authRole);
nodeLst = zkNode.getNodeLst();
leafLst = zkNode.getLeafBeanLSt();
currentPath = "/";
displayPath = "/";
parentPath = "/";
} else {
templateParam.put("zkPath", zkPath);
nodeLst = ZooKeeperUtil.INSTANCE.listFolders(zk, zkPath);
leafLst = ZooKeeperUtil.INSTANCE.listLeaves(zk, zkPath, authRole);
ZKNode zkNode = ZooKeeperUtil.INSTANCE.listNodeEntries(zk, zkPath, authRole);
nodeLst = zkNode.getNodeLst();
leafLst = zkNode.getLeafBeanLSt();
currentPath = zkPath + "/";
displayPath = zkPath;
parentPath = zkPath.substring(0, zkPath.lastIndexOf("/"));
Expand Down Expand Up @@ -130,7 +133,7 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response)
case "Save Node":
if (!newNode.equals("") && !currentPath.equals("") && authRole.equals(ZooKeeperUtil.ROLE_ADMIN)) {
//Save the new node.
ZooKeeperUtil.INSTANCE.createFolder(currentPath + newNode, "foo", "bar", ServletUtil.INSTANCE.getZookeeper(request, response, zkServerLst[0], globalProps.getProperty("defaultAcl")));
ZooKeeperUtil.INSTANCE.createFolder(currentPath + newNode, "foo", "bar", ServletUtil.INSTANCE.getZookeeper(request, response, zkServerLst[0], globalProps));
request.getSession().setAttribute("flashMsg", "Node created!");
dao.insertHistory((String) request.getSession().getAttribute("authName"), request.getRemoteAddr(), "Creating node: " + currentPath + newNode);
}
Expand All @@ -139,7 +142,7 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response)
case "Save Property":
if (!newProperty.equals("") && !currentPath.equals("") && authRole.equals(ZooKeeperUtil.ROLE_ADMIN)) {
//Save the new node.
ZooKeeperUtil.INSTANCE.createNode(currentPath, newProperty, newValue, ServletUtil.INSTANCE.getZookeeper(request, response, zkServerLst[0], globalProps.getProperty("defaultAcl")));
ZooKeeperUtil.INSTANCE.createNode(currentPath, newProperty, newValue, ServletUtil.INSTANCE.getZookeeper(request, response, zkServerLst[0], globalProps));
request.getSession().setAttribute("flashMsg", "Property Saved!");
if (ZooKeeperUtil.INSTANCE.checkIfPwdField(newProperty)) {
newValue = ZooKeeperUtil.INSTANCE.SOPA_PIPA;
Expand All @@ -151,7 +154,7 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response)
case "Update Property":
if (!newProperty.equals("") && !currentPath.equals("") && authRole.equals(ZooKeeperUtil.ROLE_ADMIN)) {
//Save the new node.
ZooKeeperUtil.INSTANCE.setPropertyValue(currentPath, newProperty, newValue, ServletUtil.INSTANCE.getZookeeper(request, response, zkServerLst[0], globalProps.getProperty("defaultAcl")));
ZooKeeperUtil.INSTANCE.setPropertyValue(currentPath, newProperty, newValue, ServletUtil.INSTANCE.getZookeeper(request, response, zkServerLst[0], globalProps));
request.getSession().setAttribute("flashMsg", "Property Updated!");
if (ZooKeeperUtil.INSTANCE.checkIfPwdField(newProperty)) {
newValue = ZooKeeperUtil.INSTANCE.SOPA_PIPA;
Expand All @@ -161,7 +164,7 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response)
response.sendRedirect("/home?zkPath=" + displayPath);
break;
case "Search":
Set<LeafBean> searchResult = ZooKeeperUtil.INSTANCE.searchTree(searchStr, ServletUtil.INSTANCE.getZookeeper(request, response, zkServerLst[0], globalProps.getProperty("defaultAcl")), authRole);
Set<LeafBean> searchResult = ZooKeeperUtil.INSTANCE.searchTree(searchStr, ServletUtil.INSTANCE.getZookeeper(request, response, zkServerLst[0], globalProps), authRole);
templateParam.put("searchResult", searchResult);
ServletUtil.INSTANCE.renderHtml(request, response, templateParam, "search.ftl.html");
break;
Expand All @@ -171,15 +174,15 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response)
if (propChkGroup != null) {
for (String prop : propChkGroup) {
List delPropLst = Arrays.asList(prop);
ZooKeeperUtil.INSTANCE.deleteLeaves(delPropLst, ServletUtil.INSTANCE.getZookeeper(request, response, zkServerLst[0], globalProps.getProperty("defaultAcl")));
ZooKeeperUtil.INSTANCE.deleteLeaves(delPropLst, ServletUtil.INSTANCE.getZookeeper(request, response, zkServerLst[0], globalProps));
request.getSession().setAttribute("flashMsg", "Delete Completed!");
dao.insertHistory((String) request.getSession().getAttribute("authName"), request.getRemoteAddr(), "Deleting Property: " + delPropLst.toString());
}
}
if (nodeChkGroup != null) {
for (String node : nodeChkGroup) {
List delNodeLst = Arrays.asList(node);
ZooKeeperUtil.INSTANCE.deleteFolders(delNodeLst, ServletUtil.INSTANCE.getZookeeper(request, response, zkServerLst[0], globalProps.getProperty("defaultAcl")));
ZooKeeperUtil.INSTANCE.deleteFolders(delNodeLst, ServletUtil.INSTANCE.getZookeeper(request, response, zkServerLst[0], globalProps));
request.getSession().setAttribute("flashMsg", "Delete Completed!");
dao.insertHistory((String) request.getSession().getAttribute("authName"), request.getRemoteAddr(), "Deleting Nodes: " + delNodeLst.toString());
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/deem/zkui/controller/Import.java
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response)
}
br.close();

ZooKeeperUtil.INSTANCE.importData(importFile, Boolean.valueOf(scmOverwrite), ServletUtil.INSTANCE.getZookeeper(request, response, zkServerLst[0], globalProps.getProperty("defaultAcl")));
ZooKeeperUtil.INSTANCE.importData(importFile, Boolean.valueOf(scmOverwrite), ServletUtil.INSTANCE.getZookeeper(request, response, zkServerLst[0], globalProps));
for (String line : importFile) {
if (line.startsWith("-")) {
dao.insertHistory((String) request.getSession().getAttribute("authName"), request.getRemoteAddr(), "File: " + uploadFileName + ", Deleting Entry: " + line);
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/deem/zkui/controller/Login.java
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response)
}
}
if (authenticated) {
logger.info("Login successfull: " + username);
logger.info("Login successful: " + username);
session.setAttribute("authName", username);
session.setAttribute("authRole", role);
response.sendRedirect("/home");
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/deem/zkui/controller/Logout.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t
Properties globalProps = (Properties) getServletContext().getAttribute("globalProps");
String zkServer = globalProps.getProperty("zkServer");
String[] zkServerLst = zkServer.split(",");
ZooKeeper zk = ServletUtil.INSTANCE.getZookeeper(request, response, zkServerLst[0],globalProps.getProperty("defaultAcl"));
ZooKeeper zk = ServletUtil.INSTANCE.getZookeeper(request, response, zkServerLst[0],globalProps);
request.getSession().invalidate();
zk.close();
response.sendRedirect("/login");
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/deem/zkui/controller/RestAccess.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t
if (hostName == null) {
hostName = ServletUtil.INSTANCE.getRemoteAddr(request);
}
zk = ServletUtil.INSTANCE.getZookeeper(request, response, zkServerLst[0], globalProps.getProperty("defaultAcl"));
zk = ServletUtil.INSTANCE.getZookeeper(request, response, zkServerLst[0], globalProps);
//get the path of the hosts entry.
LeafBean hostsNode = null;
//If app name is mentioned then lookup path is appended with it.
Expand Down
69 changes: 34 additions & 35 deletions src/main/java/com/deem/zkui/utils/ServletUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,30 @@
*/
package com.deem.zkui.utils;

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.zookeeper.ZooKeeper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.Arrays;
import java.util.HashMap;

public enum ServletUtil {

INSTANCE;
private final static Logger logger = LoggerFactory.getLogger(ServletUtil.class);

public void renderHtml(HttpServletRequest request, HttpServletResponse response, Map<String, Object> templateParam, String view) throws IOException, TemplateException {

if (request != null && response != null && templateParam != null) {
//There is no way to access session info in freemarker template.
//Hence all view rendering happens via this function which adds session info to attribute for each request.
Expand All @@ -53,24 +54,22 @@ public void renderHtml(HttpServletRequest request, HttpServletResponse response,
}
templateParam.put("authName", session.getAttribute("authName"));
templateParam.put("authRole", session.getAttribute("authRole"));

response.setContentType("text/html");
Template template = null;

long startTime = System.currentTimeMillis();
Configuration config = new Configuration();
config.setClassForTemplateLoading(request.getServletContext().getClass(), "/");
template = config.getTemplate("/webapp/template/" + view);

try (Writer out = new OutputStreamWriter(response.getOutputStream())) {
template.process(templateParam, out);
out.flush();
}

template.process(templateParam, response.getWriter());
long estimatedTime = System.currentTimeMillis() - startTime;
logger.trace("Elapsed Time in Secs for Rendering: " + estimatedTime / 1000);

}
}

}

public void renderError(HttpServletRequest request, HttpServletResponse response, String error) {
try {
logger.error("Error :" + error);
Expand All @@ -81,47 +80,47 @@ public void renderError(HttpServletRequest request, HttpServletResponse response
config.setClassForTemplateLoading(request.getServletContext().getClass(), "/");
template = config.getTemplate("/webapp/template/error.ftl.html");
templateParam.put("error", error);
try (Writer out = new OutputStreamWriter(response.getOutputStream())) {
template.process(templateParam, out);
out.flush();
}
template.process(templateParam, response.getWriter());
} catch (TemplateException | IOException ex) {
logger.error(Arrays.toString(ex.getStackTrace()));
}

}
public ZooKeeper getZookeeper(HttpServletRequest request, HttpServletResponse response, String zkServer,String acl) {

public ZooKeeper getZookeeper(HttpServletRequest request, HttpServletResponse response, String zkServer, Properties globalProps) {
try {

HttpSession session = request.getSession();
ZooKeeper zk = (ZooKeeper) session.getAttribute("zk");
if (zk == null || zk.getState() != ZooKeeper.States.CONNECTED) {
zk = ZooKeeperUtil.INSTANCE.createZKConnection(zkServer);
ZooKeeperUtil.INSTANCE.setDefaultAcl(acl);
Integer zkSessionTimeout = Integer.parseInt(globalProps.getProperty("zkSessionTimeout"));
//Converting seconds to ms.
zkSessionTimeout = zkSessionTimeout * 1000;
zk = ZooKeeperUtil.INSTANCE.createZKConnection(zkServer, zkSessionTimeout);
ZooKeeperUtil.INSTANCE.setDefaultAcl(globalProps.getProperty("defaultAcl"));
if (zk.getState() != ZooKeeper.States.CONNECTED) {
session.setAttribute("zk", null);
} else {
session.setAttribute("zk", zk);
}

}
return zk;
} catch (IOException | InterruptedException ex) {
logger.error(Arrays.toString(ex.getStackTrace()));
}
return null;
}

public void closeZookeeper(ZooKeeper zk) {
try {
zk.close();
} catch (Exception ex) {
logger.error("Error in closing zk,will cause problem in zk! " + ex.getMessage());
}

}

public String externalizeNodeValue(byte[] value) {
return value == null ? "" : new String(value).replaceAll("\\n", "\\\\n").replaceAll("\\r", "");
// We might want to BASE64 encode it
Expand All @@ -135,5 +134,5 @@ public String getRemoteAddr(HttpServletRequest request) {
}
return remoteAddr;
}

}
Loading

0 comments on commit 36788ef

Please sign in to comment.