赞
踩
xml文件读取参考:https://blog.csdn.net/qq_26676207/article/details/53035567
上文讲到dubbo接口测试中点对点的直连方式,需要明确dubbo服务的路径和接口名,如果项目dubbo服务太多的情况下,手动配置显然是下下之策,所以想到有没有什么方式能够把dubbo服务的信息获取到,然后自动生成xml配置文件。方法则是通过zkclient连接到dubbo服务的注册中心zookeeper,然后在zookeeper上面拿到对应服务的相关信息。
我们需要配置zkclient,工程pom文件加上以下内容
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
<version>0.1</version>
</dependency>
首先博主展示在zookeeper里面如何获取服务信息
1、找到zk目录,在bin目录下面输入
sh zkCli.sh
2、进入zk之后,然后再输入指令
ls /dubbo
到这一步之后,我们会看到所有注册到zk上面的工程文件的接口名,整个信息是一个列表,如下
[com.xxx.service1,com.xxxx.service2]
3、然后选择任意接口,输入以下指令
ls /dubbo/com.xxx.service1
到这一步,会看到展示以下内容
[consumers, routers, configurators, providers]
4、输入以下指令
ls /dubbo/com.xxx.service1/providers
到这一步会输出该接口服务的相关信息(urlencode内容,可以在线解码即可),如下所示
[dubbo%3a%2f%2f10.10.10.10%3a8080%2fcom.xxxxx.service%3faccesslog%3dfalse%26anyhost%3dtrue%26application%3ddubbo-rpc%26charset%3dUTF-8%26dubbo%3d3.0.0%26generic%3dfalse%26interface%3dcom.dubbo.service1%26iothreads%3d8%26logger%3dslf4j%26methods%3ddubboFunction%26organization%3dxxx%26owner%3dxxx%26payload%3d883886%26pid%3d6914%26serialization%3dkryo%26server%3dnetty%26side%3dprovider%26threadpool%3dcached%26threads%3d250%26timestamp%3d1527509257826%26version%3d2.1]
urldecode解码之后的内容:
dubbo://10.10.10.10:8080/com.xxxxx.service?accesslog=false&anyhost=true&application=dubbo-rpc&charset=UTF-8&dubbo=3.0.0&generic=false&interface=com.dubbo.service1&iothreads=8&logger=slf4j&methods=dubboFunction&organization=xxx&owner=xxx&payload=883886&pid=6914&serialization=kryo&server=netty&side=provider&threadpool=cached&threads=250×tamp=1527509257826&version=2.1
很明显,我们可以从上面的信息中获取到ip,端口号,接口名称,方法名称(博主暂时没用到),以及版本号(确认版本号信息是在这个字符串的最后面,不用担心跑到中间位置)
直接贴代码
private static List<String> getDubboInfo(String path){
List<String> list = new ArrayList<String>();
String servers = getProperties("zk.servers");
// String roopath = getProperties("zk.rootpath");
ZkClient zkClient = new ZkClient(servers, 5000);
zkClient.subscribeChildChanges(path, new IZkChildListener() {
@Override
public void handleChildChange(String s, List<String> list) throws Exception {
System.out.println("parentPath:"+s+", currentchilds:"+list);
}
});
try{
list = zkClient.getChildren(path);
Thread.sleep(1000);
// return list;
} catch (Exception e){
logger.error(String.valueOf(e));
}
return list;
}
这段代码需要一个path参数作为入参,其实就是”/dubbo”,这个zk里面的根节点,主要是通过获取到配置文件中的zk地址(zk.servers)然后建立连接之后拿到节点下面的服务名形成一个list,这个list包含了所有注册到zk的服务
这一块,博主希望通过一个map建立一组管理,每一组用json描述如下:
{
"10.10.10.10:8888":"com.xxxx.service",
"version":"2.1"
}
当然version并不一定存在,providers节点下面也不一定有信息,这个在代码里面都有做处理,如下:
private static List<Map<String, String>> getInterfaceInfo(){
final List<Map<String, String>> LIST2= new ArrayList<Map<String, String>>();
final String ROOTPATH = getProperties("zk.rootpath");
final List<String> LIST = getDubboInfo(ROOTPATH);
for(int i = 0; i < LIST.size(); i++){
final int J = i;
Runnable task = new Runnable() {
@Override
public void run() {
try{
String path = ROOTPATH + "/"+ LIST.get(J)+"/providers";
List<String> list1 = getDubboInfo(path);
if (list1.size() == 0){
}else {
String url = URLDecoder.decode(list1.get(0));
//检测URl是否为用户所需
if(checkUrlValid(url)){
System.out.println(url);
String host = "dubbo://"+ url.split("\\?")[0].split("dubbo://")[1].split("/")[0];
String inter = url.split("\\?")[0].split("dubbo://")[1].split("/")[1];
//如果接口服务有端口号,需要带上
Map<String ,String> map = new HashMap<String, String>();
if(url.contains("&version=")){
String version = url.split("&version=")[1];
map.put("version", version);
}
map.put(host, inter);
LIST2.add(map);
}
}
}catch (Exception e){
e.printStackTrace();
}
}
};
executor.submit(task);
}
executor.shutdown();
while (true){
if(executor.isTerminated()){
logger.info("所有子线程结束!");
break;
}
}
return LIST2;
}
这里使用了for循环的多线程,主要是为了加速信息处理,不然的话整块代码运行会非常缓慢(启动dubbo连接,进入某个节点,blablabla,流程比较繁琐),博主自己的项目大概需要10分钟,使用多线程之后也就10s不到的样子。
代码中有个checkUrlValid方法,这个是博主有个配置文件,配置了用户想要测试的rpc服务的接口名,减少pom文件添加的”痛苦“,因为如果这里不进行一定的过滤,他会拿到所有注册到zk上面的服务,那么可想而知我们的pom文件在添加依赖的的时候会有多蛋疼,所以写了一个方法进行过滤,就是简单的字符串操作,如下:
private static boolean checkUrlValid(String url){
String services = getProperties("project.tag");
String [] service = services.split(",");
boolean check = false;
for(int i =0;i < service.length;i++){
if (url.contains(service[i])){
check = true;
}
}
return check;
}
博主使用了两个xml文件,结构如下:
base.xml,会引入consumer.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd
">
<dubbo:application name="" owner="Venn"/>
<dubbo:monitor protocol="registry"/>
<import resource="classpath*:entity-consumer/consumer.xml"/>
</beans>
consumer.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd ">
</beans>
consumer.xml我们用来读写,首先新建一个模板文件就如上面的consumer.xml,文件读写如下:
public static void addUser(List<Map<String, String>> list){
/*
* 1. 得到Document
* 2. 得到root元素
* 3. 要把User对象转换成Element元素
* 4. 把user元素插入到root元素中
* 5. 回写document
*/
try{
//1.得到document
// 创建解析器
SAXReader reader = new SAXReader();
//调用读方法,得到document
Document doc = reader.read("src/main/resources/entity-consumer/consumer.xml");
//2.得到root元素
Element root = doc.getRootElement();
String child = "dubbo:reference";
//删除之前的同类型子节点
removeNodes(root, child);
for(int i = 0;i <list.size();i++){
String url = getUrl(list.get(i));
String inter = getInterface(list.get(i));
String id = getId(list.get(i));
String version = getVersion(list.get(i));
// System.out.println(version);
//3.完成添加元素,并返回添加的元素!
Element dubboElement = root.addElement("dubbo:reference");
dubboElement.addAttribute("id",id);
dubboElement.addAttribute("check", "false");
dubboElement.addAttribute("timeout","1000");
dubboElement.addAttribute("url", url);
dubboElement.addAttribute("interface", inter);
if(version.equals("")){
}else {
dubboElement.addAttribute("version", version);
}
}
//3.回写
Writer out = new PrintWriter("src/main/resources/entity-consumer/consumer.xml");
OutputFormat format = new OutputFormat("\t", true);
format.setTrimText(true);//先干掉原来的空白(\t和换行和空格)!
XMLWriter writer = new XMLWriter(out, format);
//把document对象写到out流中
writer.write(doc);
//关闭流
out.close();
writer.close();
}catch (Exception e){
// 把编译异常转换成运行异常!
throw new RuntimeException(e);
}
}
大概逻辑就是,打开xml文件,删除之前的dubbo:reference节点,添加新的节点。generateString是一个随机数方法,主要是避免多个工程有相同的id,毕竟博主的id取的是接口的最后一段字符串,比如:com.xxx.service,那么博主的id就是service,这个容易冲突。
节点删除方法,也贴出来,当然需要注意,使用节点删除的时候一定要配合”回写“,不然无法生效。
public static void removeNodes( Element root, String child){
List nodes = root.selectNodes(child);
for(int i =0;i < nodes.size();i++){
Node node = (Node) nodes.get(i);
node.getParent().remove(node);
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。