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接口的详细数据,无法准确分析其来源,只能等待后续再次出现类似数据时加以分析解决。