zookeeper中dubboo节点过多导致dubbo-admin启动失败排查

1、起因

    新需求开发了一个新接口,在联调环境正常注册,到了测试环境,调用方找不到对应接口;

 

   通过dubbo-admin,查看服务注册情况,发现以下情况:
  1、新接口 和 其他旧接口  均未在dubbo管理后台展示;
  2、日志输出显示,服务正在被正常调用;
  3、在dubbo管理后台操作禁用,启用等操作均不生效。

2、排查

2.1、重启

  由于dubbo服务能正常使用,而dubbo-admin却不能及时的更新数据,怀疑是dubbo-admin服务问题,于是尝试重启dubbo-admin服务。

  结果原先还能看到管理界面的dubbo-admin直接罢工,查看日志发现报了“数据包体过大,终止响应”的异常,从而导致dubbo-admin一直未能拉取到注册到zookeeper的dubbo服务信息,进而启动失败;

 

2.2、查看节点数据

 既然是响应包体过大,于是便打算通过zookeeper的客户端直连查看dubbo节点数据都有啥,发现客户端也因为数据包体响应过大而不能连上

 

2.3、jute.maxbuffer

 转换思路,既然是响应体过大,导致连接失败,那就尝试看能不能调整响应包的大小,于是求助度娘,搜索zoookeeper的相关参数,

 发现通过修改“jute.maxbuffer” 的参数大小,可以调整zookeeper的对外响应包体大小。

 

3、尝试

3.1、发现异常节点

通过在启动命令后添加 “-Djute.maxbuffer=41943040” ;

 

果然没有再报响应包过大的异常,但是查看启动日志,却发现打印出了大量的不符合规范的 dubbo服务节点

 

并且最终由于过多的异常节点,导致内存溢出,宣导服务还是启动失败。

 

3.2、删除异常节点

        既然发现了大量的异常节点,那么自然的就是通过遍历将其删除。

        删除规则也相对简单,因为项目注册的服务均是以 com 开始的,因此一旦发现不是以 com开始的就直接将之删除。

PS:链接zookeeper的代码也需要设置参数“”,否则一样会包报“响应包”过大的异常。

 

 

运行代码后,发现在zookeeper中,一旦子节点有数据(官方解释为:不允许删除非叶子节点),是不允许直接对其进行删除动作的。通过递归函数将所有的不符合规范的节点删除。

public class BaseZookeeper implements Watcher {
	private ZooKeeper zookeeper;
	/**
	 * 超时时间
	 */
	private static final int SESSION_TIME_OUT = 20000;
	private CountDownLatch countDownLatch = new CountDownLatch(1);

	@Override
	public void process(WatchedEvent event) {
		if (event.getState() == KeeperState.SyncConnected) {
			System.out.println("Watch received event");
			countDownLatch.countDown();
		}
	}

	/**
	 * 连接zookeeper
	 * 
	 * @param host
	 * @throws Exception
	 */
	public void connectZookeeper(String host) throws Exception {
		zookeeper = new ZooKeeper(host, SESSION_TIME_OUT, this);
		countDownLatch.await();
		System.out.println("zookeeper connection success");
	}

	/**
	 * 创建节点
	 * 
	 * @param path
	 * @param data
	 * @throws Exception
	 */
	public String createNode(String path, String data) throws Exception {
		return this.zookeeper.create(path, data.getBytes(),
				Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
	}

	/**
	 * 获取路径下所有子节点
	 * 
	 * @param path
	 * @return
	 * @throws KeeperException
	 * @throws InterruptedException
	 */
	public List<String> getChildren(String path) throws KeeperException,
			InterruptedException {
		List<String> children = zookeeper.getChildren(path, false);
		return children;
	}

	/**
	 * 获取节点上面的数据
	 * 
	 * @param path
	 *            路径
	 * @return
	 * @throws KeeperException
	 * @throws InterruptedException
	 */
	public String getData(String path) throws KeeperException,
			InterruptedException {
		byte[] data = zookeeper.getData(path, false, null);
		if (data == null) {
			return "";
		}
		return new String(data);
	}

	/**
	 * 设置节点信息
	 * 
	 * @param path
	 *            路径
	 * @param data
	 *            数据
	 * @return
	 * @throws KeeperException
	 * @throws InterruptedException
	 */
	public Stat setData(String path, String data) throws KeeperException,
			InterruptedException {
		Stat stat = zookeeper.setData(path, data.getBytes(), -1);
		return stat;
	}

	/**
	 * 删除节点
	 * 
	 * @param path
	 * @throws InterruptedException
	 * @throws KeeperException
	 */
	public void deleteNode(String path) throws InterruptedException,
			KeeperException {
		zookeeper.delete(path, -1);
	}

	/**
	 * 获取创建时间
	 * 
	 * @param path
	 * @return
	 * @throws KeeperException
	 * @throws InterruptedException
	 */
	public String getCTime(String path) throws KeeperException,
			InterruptedException {
		Stat stat = zookeeper.exists(path, false);
		return String.valueOf(stat.getCtime());
	}

	/**
	 * 获取某个路径下孩子的数量
	 * 
	 * @param path
	 * @return
	 * @throws KeeperException
	 * @throws InterruptedException
	 */
	public Integer getChildrenNum(String path) throws KeeperException,
			InterruptedException {
		int childenNum = zookeeper.getChildren(path, false).size();
		return childenNum;
	}

	/**
	 * 关闭连接
	 * 
	 * @throws InterruptedException
	 */
	public void closeConnection() throws InterruptedException {
		if (zookeeper != null) {
			zookeeper.close();
		}
	}

}
	public static void main(String[] args) {
		BaseZookeeper zookeeper = new BaseZookeeper();
		try {
			// 链接zookeeper
			zookeeper.connectZookeeper("zookeeper.szy.com:2181");
			String path = "/dubbo";
			// 查询dubbo节点下的所有子节点
	        List<String> children = zookeeper.getChildren(path);
	        
	        // 输出节点总长度
	        System.out.println(children.size());
	        
	        // 遍历dubbo节点下的子节点
	        for (int i = 0; i < children.size(); i++) {
	        	
	        	String childPath = children.get(i);
	        	System.out.println("遍历第"+i+"个");
	        	
	        	// 如果不是符合规范的dubbo节点则直接删除
	        	if(childPath.startsWith("com")){
	        		continue;
	        	}else{
	        		// 删除异常的dubbo子节点
	        		childDel(zookeeper,path+"/"+childPath);
	        	}
			}
	        
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			try {
				// 关闭连接
				zookeeper.closeConnection();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * 方法描述:遍历删除子节点
	 * @param zookeeper
	 * @param childPath
	 * @throws Exception
	 */
	private static void childDel(BaseZookeeper zookeeper,String childPath)throws Exception{
		// 获取当前节点路径下的所有子节点
		List<String> childs = zookeeper.getChildren(childPath);
		
		// 如果还有子节点,则继续遍历这些子节点
		if(childs.size()>0){
			for (String child : childs) {
				childDel(zookeeper, childPath+"/"+child);
			}
		}else{
			// 如果当前子节点(叶子节点),则执行删除动作
    		zookeeper.deleteNode(childPath);
    		System.out.println("删除节点-->"+childPath);	        			
		}
	}

再次启动dubbo-admin项目,恢复正常。

4、关键

4.1、可以通过参数  jute.maxbuffer 来调整 zookeeper的写入和响应包大小

4.2、zookeeper不能直接删除父节点,需要将其子节点均删干净后才能删除父节点

4.3、jute.maxbuffer 的值不能被设置成过大,会影响zookeeper的数据写入和同步性能

 

5、待解决

  由于没能及时打印异常dubbo接口的详细数据,无法准确分析其来源,只能等待后续再次出现类似数据时加以分析解决。

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页