首先说说业务需求场景,在公司产品不断演进和迭代的过程中,由初始的轻量级一体化小服务迅速发展,在业务体量的推进中整个产品也很快变得臃肿起来,给日常迭代和维护带来了很大负担。
再之后产品为了更好的服务于业务,演变为微服务体系,于是原本糅杂于一体的服务模块按照业务或者功能规则开始拆分,因此应用数量井喷式增加,几百个应用分工合作,看似和谐的提供服务。
接下来由于服务数量太多,开发以及运维人员发现由于服务之间相互依赖等原因,日常发布也还能勉强,一旦有机房迁移的时候,根本理不清服务之间复杂的依赖关系,这时候就需要有一个过程来梳理服务之间的依赖,然后分析归纳,进行基础服务下沉等骚操作,规划出后台、中台、前台等,
今天要说的就是梳理服务依赖时数据采集的过程,以下是我自己做这些的大概思路和代码,仅供有需要的同学参考:
核心代码如下:
package com.dubbocheck;
import com.alibaba.fastjson.JSON;
import com.common.constants.ZtstConstants;
import com.common.utils.ZtstUtils;
import com.dubbocheck.dto.DcHostDto;
import com.dubbocheck.dto.DcProviderAllDto;
import com.dubbocheck.dto.DcProviderBaseDto;
import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.ZkConnection;
import org.mortbay.util.MultiMap;
import org.mortbay.util.UrlEncoded;
import java.io.*;
import java.net.URLDecoder;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author yanwei.shen
* @title: ZKClientBase
* @projectName howbuy-ztst
* @description: TODO
* @date 2019/10/2415:51
*/
public class ZKClientBase {
private static Pattern pattern;
private static Matcher matcher;
/**
* zookeeper地址
*/
//static final String CONNECT_ADDR = "192.168.XXX.XXX:2181";//zk地址选择自己需要的,多个zk以逗号分隔,"192.168.1.XXX:2181,192.168.1.XXX:2181,192.168.1.XXX:2181"
/**
* session超时时间
*/
static final int SESSION_OUTTIME = 10000;//ms
public static String getString(String str) {
int index = -1;
char[] chars = str.toCharArray();
if (chars != null) {
for (int i = 0; i < chars.length; i++) {
if ((chars[i] >= 'A') && (chars[i] <= 'Z')) {
index = i;
break;
}
}
}
return str.substring(0, index - 1);
}
/**
* 解析dubbo中的provider集合
*
* @param zkClient
* @param allDto
*/
public static void parseProvider(ZkClient zkClient, DcProviderAllDto allDto) {
if (zkClient != null && allDto != null) {
String service = allDto.getService();
List<DcProviderBaseDto> baseList = allDto.getList();
Map<String, List<DcProviderBaseDto>> serviceMap = allDto.getServiceMap();
if (service != null && !"".equals(service)) {
//构建providers的zk路径
String path = ZtstConstants.DUBBO_ROOT + "/" + service + ZtstConstants.DUBBO_PROVIDERS;
//读取providers列表
List<String> providerList = zkClient.getChildren(path);
//循环解析
DcProviderBaseDto baseDto;
//定义临时变量
DcHostDto hostDto;
String host;
Integer port;
String hostStr;
List<DcProviderBaseDto> hostList;
for (String providerStr : providerList) {
//把provider的字符串转化成baseDto
System.out.println("provider:" + providerStr);
baseDto = parseProviderBase(providerStr);
if (baseDto != null) {
//添加至完整的list
if (baseList == null) {
//创建新对象时,allDto需要重新指向新对象 zt@2019-08-12
baseList = new ArrayList<>();
allDto.setList(baseList);
}
baseList.add(baseDto);
//如果有host,则添加一个host的map对象(其实肯定有host)
hostDto = baseDto.getDcHostDto();
if (hostDto != null) {
host = hostDto.getHost();
port = hostDto.getPort();
hostStr = host + ":" + port;
//填充map
if (serviceMap == null) {
//创建新对象时,allDto需要重新指向新对象 zt@2019-08-12
serviceMap = new HashMap<>();
allDto.setServiceMap(serviceMap);
hostList = new ArrayList<>();
serviceMap.put(hostStr, hostList);
} else {
hostList = serviceMap.get(hostStr);
if (hostList == null) {
hostList = new ArrayList<>();
serviceMap.put(hostStr, hostList);
}
}
hostList.add(baseDto);
} //hostDto
} //baseDto
} //for
} //service
} //null
}
/**
* 解析dubbo中的consumer集合
*
* @param zkClient
* @param allDto
*/
public static void parseConsumers(ZkClient zkClient, DcProviderAllDto allDto) {
if (zkClient != null && allDto != null) {
String service = allDto.getService();
List<DcProviderBaseDto> baseList = allDto.getList();
Map<String, List<DcProviderBaseDto>> serviceMap = allDto.getServiceMap();
if (service != null && !"".equals(service)) {
//构建providers的zk路径
String path = ZtstConstants.DUBBO_ROOT + "/" + service + ZtstConstants.DUBBO_CONSUMERS;
//读取providers列表
List<String> providerList = zkClient.getChildren(path);
//循环解析
DcProviderBaseDto baseDto;
//定义临时变量
DcHostDto hostDto;
String host;
Integer port;
String hostStr;
List<DcProviderBaseDto> hostList;
for (String providerStr : providerList) {
//把provider的字符串转化成baseDto
System.out.println("consumer:" + providerStr);
baseDto = parseConsumersBase(providerStr);
if (baseDto != null) {
//添加至完整的list
if (baseList == null) {
//创建新对象时,allDto需要重新指向新对象 zt@2019-08-12
baseList = new ArrayList<>();
allDto.setList(baseList);
}
baseList.add(baseDto);
//如果有host,则添加一个host的map对象(其实肯定有host)
hostDto = baseDto.getDcHostDto();
if (hostDto != null) {
host = hostDto.getHost();
port = hostDto.getPort();
hostStr = host + ":" + port;
//填充map
if (serviceMap == null) {
//创建新对象时,allDto需要重新指向新对象 zt@2019-08-12
serviceMap = new HashMap<>();
allDto.setServiceMap(serviceMap);
hostList = new ArrayList<>();
serviceMap.put(hostStr, hostList);
} else {
hostList = serviceMap.get(hostStr);
if (hostList == null) {
hostList = new ArrayList<>();
serviceMap.put(hostStr, hostList);
}
}
hostList.add(baseDto);
} //hostDto
} //baseDto
} //for
} //service
} //null
}
/**
* 把provider字符串转化成对象
*
* @param providerStr
* @return
*/
public static DcProviderBaseDto parseProviderBase(String providerStr) {
DcProviderBaseDto baseDto = null;
try {
MultiMap multiMap = new MultiMap();
String dubboUrl = URLDecoder.decode(providerStr, "UTF-8");
int idx = dubboUrl.indexOf("?");
String hostUrl = dubboUrl.substring(0, idx);
String paraUrl = dubboUrl.substring(idx + 1);
UrlEncoded.decodeTo(paraUrl, multiMap, "UTF-8");
//手动解析base对象
baseDto = JSON.parseObject(ZtstUtils.toJsonStr(multiMap), DcProviderBaseDto.class);
//1、构建host和service
if (hostUrl != null && !"".equals(hostUrl)) {
String regex = "^dubbo://(.+):(.+)/(.+)$";
pattern = Pattern.compile(regex);
matcher = pattern.matcher(hostUrl);
if (matcher.find()) {
String host = matcher.group(1);
String portStr = matcher.group(2);
String service = matcher.group(3);
int port = Integer.parseInt(portStr);
DcHostDto hostDto = new DcHostDto();
hostDto.setHost(host);
hostDto.setPort(port);
if (baseDto == null) {
baseDto = new DcProviderBaseDto();
}
baseDto.setDcHostDto(hostDto);
baseDto.setService(service);
}
}
if (baseDto != null) {
//2、解析method
String methods = baseDto.getMethods();
if (methods != null && !"".equals(methods)) {
List<String> methodList = Arrays.asList(methods.split(","));
baseDto.setMethodList(methodList);
}
//3、时间解析
Long timestamp = baseDto.getTimestamp();
if (timestamp != null) {
Calendar c = Calendar.getInstance();
c.setTimeInMillis(timestamp);
Date date = c.getTime();
baseDto.setDate(date);
}
}
} catch (UnsupportedEncodingException e) {
//log.error(">>>>dubbo url编码异常!", e);
}
return baseDto;
}
/**
* 把provider字符串转化成对象
*
* @param providerStr
* @return
*/
public static DcProviderBaseDto parseConsumersBase(String providerStr) {
DcProviderBaseDto baseDto = null;
try {
MultiMap multiMap = new MultiMap();
String dubboUrl = URLDecoder.decode(providerStr, "UTF-8");
int idx = dubboUrl.indexOf("?");
String hostUrl = dubboUrl.substring(0, idx);
String paraUrl = dubboUrl.substring(idx + 1);
UrlEncoded.decodeTo(paraUrl, multiMap, "UTF-8");
//手动解析base对象
baseDto = JSON.parseObject(ZtstUtils.toJsonStr(multiMap), DcProviderBaseDto.class);
//1、构建host和service
if (hostUrl != null && !"".equals(hostUrl)) {
String regex = "^consumer://(.+)/(.+)$";
pattern = Pattern.compile(regex);
matcher = pattern.matcher(hostUrl);
if (matcher.find()) {
String host = matcher.group(1);
// String portStr = matcher.group(2);
String service = matcher.group(2);
//int port = Integer.parseInt(portStr);
DcHostDto hostDto = new DcHostDto();
hostDto.setHost(host);
//hostDto.setPort(port);
if (baseDto == null) {
baseDto = new DcProviderBaseDto();
}
baseDto.setDcHostDto(hostDto);
baseDto.setService(service);
}
}
if (baseDto != null) {
//2、解析method
String methods = baseDto.getMethods();
if (methods != null && !"".equals(methods)) {
List<String> methodList = Arrays.asList(methods.split(","));
baseDto.setMethodList(methodList);
}
//3、时间解析
Long timestamp = baseDto.getTimestamp();
if (timestamp != null) {
Calendar c = Calendar.getInstance();
c.setTimeInMillis(timestamp);
Date date = c.getTime();
baseDto.setDate(date);
}
}
} catch (UnsupportedEncodingException e) {
//log.error(">>>>dubbo url编码异常!", e);
}
return baseDto;
}
public static void main(String[] args) throws Exception {
ZkClient zkc = new ZkClient(new ZkConnection(args[0]), SESSION_OUTTIME);
PrintWriter out = null;
int ss=0;
try {
out = new PrintWriter(new BufferedWriter(new FileWriter("packageNumber44.csv")));
out.println("type,appName,facade,meaths");
DcProviderAllDto allDto;
if (zkc != null) {
List<String> serviceList = zkc.getChildren(ZtstConstants.DUBBO_ROOT);
allDto = new DcProviderAllDto();
for (String service : serviceList) {
allDto.setService(service);
parseProvider(zkc, allDto);
}
for (DcProviderBaseDto dcProviderBaseDto : allDto.getList()) {
out.println("provider," + dcProviderBaseDto.getApplication() + "," + dcProviderBaseDto.getService() + "," + dcProviderBaseDto.getMethods().replaceAll(",", "|"));
}
}
if (zkc != null) {
List<String> serviceList = zkc.getChildren(ZtstConstants.DUBBO_ROOT);
allDto = new DcProviderAllDto();
for (String service : serviceList) {
allDto.setService(service);
parseConsumers(zkc, allDto);
}
for (DcProviderBaseDto dcProviderBaseDto : allDto.getList()) {
if(dcProviderBaseDto.getMethods()!=null){
out.println("consumer," + dcProviderBaseDto.getApplication() + "," + dcProviderBaseDto.getService() + "," + dcProviderBaseDto.getMethods().replaceAll(",", "|"));
}else{
ss++;
out.println("consumer," + dcProviderBaseDto.getApplication() + "," + dcProviderBaseDto.getService());
}
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
System.out.println(ss);
if (out != null) {
out.close();
}
}
}
}
导出后的数据落库大概是这样:
最后展示到页面大概是这样: