改造断路器集群监控Hystrix Turbine实现自动注册消费者、实时监控多个服务
在上一篇文章中,我们搭建了hystrix dashoard,对指定接口进行监控。但是只能对一个接口进行监听,功能比较局限;
turbine:汇总系统内多个服务的数据并显示到 hystrix dashboard 上。虽然可以实现对消费者的聚合监控,但是如果新增了消费者,也需要对其进行监控,就需要重新启动项目,这是非常不合理的。
改造后的turbine项目:本文将进行对turbine的改造,在原有的功能上实现登录认证、实时监控的功能,使turbine项目在不用重启的情况下都可以获取最新的、完整的服务消费情况。
准备项目:
1. eureka-service:eureka注册中心,端口:8761,用于服务注册与发现;
2. eureka-provider_1:服务提供者1,端口:8071,用于提供服务;
3. eureka-provider_2:服务提供者2,端口:8072,用于提供服务;
4. ribbon-consumer-hystrix-1:服务消费者1,端口:9001,用于消费服务;
5. ribbon-consumer-hystrix-2:服务消费者2,店口:9002,用于消费服务;
6. readlocalproperties:用于读取本地文件,端口:9999;
7. turbine-dashboard:断后改造后的hystrix dashboard客户端项目,端口:7979。
1-3个项目可参考自行创建,或者从下文附件中下载。我们详细讲下ribbon-consumer-hystrix-1、ribbon-consumer-hystrix-2、readlocalproperties项目、turbine-dashboard项目的构建;
重要注释在代码中,就不另外叙述了。
一、搭建ribbon-consumer-hystrix-1项目:
1. 在pom.xml中添加如下依赖:
<!-- 客户端负载均衡 --> <dependency> <groupid>org.springframework.cloud</groupid> <artifactid>spring-cloud-starter-ribbon</artifactid> </dependency> <!-- eureka客户端 --> <dependency> <groupid>org.springframework.cloud</groupid> <artifactid>spring-cloud-starter-eureka</artifactid> </dependency> <dependency> <groupid>org.springframework.cloud</groupid> <artifactid>spring-cloud-starter-hystrix</artifactid> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-test</artifactid> <scope>test</scope> </dependency> <dependency> <groupid>com.google.code.gson</groupid> <artifactid>gson</artifactid> <version>2.5</version> </dependency> <dependency> <groupid>org.slf4j</groupid> <artifactid>slf4j-api</artifactid> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-actuator</artifactid> </dependency>
2.配置文件:
eureka: client: serviceurl: defaultzone: http://localhost:8761/eureka/ instance: leaseexpirationdurationinseconds: 4 leaserenewalintervalinseconds: 1 spring: application: name: ribbon-consumer-hystrix server: port: 9001 #用于存入本地文件,供readlocalproperties项目读取并调用 url: http://localhost:9001/sendmsg
3.启动类:
1 @enablehystrix 2 @enablediscoveryclient 3 @springbootapplication 4 public class ribbonconsumerapplication_1 { 5 6 @loadbalanced 7 @bean 8 resttemplate resttemplate() { 9 return new resttemplate(); 10 } 11 12 public static void main(string[] args) { 13 springapplication.run(ribbonconsumerapplication_1.class, args); 14 writeconfig(); 15 } 16 //读取调用该jar包的项目中配置文件的url,存入指定文件,供readlocalproperties项目读取 17 public static void writeconfig (){ 18 properties prop=new properties(); 19 try { 20 prop.load(config.class.getresourceasstream("/application.yml")); 21 } catch (ioexception e2) { 22 // todo auto-generated catch block 23 e2.printstacktrace(); 24 } 25 string url=""; 26 27 url=prop.getproperty("url"); 28 29 system.out.println("url---"+url); 30 //读取文件中已有的url 31 properties propp=new properties(); 32 string oldurl=""; 33 try { 34 inputstream ins = new fileinputstream(new file("f:\\config.properties")); 35 propp.load(ins); 36 37 oldurl=propp.getproperty("url"); 38 system.out.println("oldurl---"+oldurl); 39 ins.close(); 40 } catch (filenotfoundexception e1) { 41 // todo auto-generated catch block 42 e1.printstacktrace(); 43 44 } catch (ioexception e1) { 45 // todo auto-generated catch block 46 e1.printstacktrace(); 47 } 48 49 //拼接原来的url和新的url 50 try { 51 // 调用 hashtable 的方法 put,使用 getproperty 方法提供并行性。 52 // 强制要求为属性的键和值使用字符串。返回值是 hashtable 调用 put 的结果。 53 fileoutputstream fos = new fileoutputstream("f:\\config.properties",false); 54 system.out.println("fos---"+fos); 55 properties pro = new properties(); 56 57 int ifhas=0; 58 if(com.google.common.base.strings.isnullorempty(oldurl)) { 59 oldurl= ","; 60 } 61 string[] strs=oldurl.split(","); 62 for(int i=0;i<strs.length;i++) { 63 if(url.equals(strs[i])) { 64 ifhas=1; 65 } 66 } 67 //如果配置文件中未存在该接口 68 string newurl=""; 69 if(ifhas==0) { 70 // 存储 71 newurl=oldurl+","+url;//拼接已存在的urls和新的url 72 }else { 73 newurl=oldurl; 74 } 75 pro.setproperty("url", newurl); 76 // 以适合使用 load 方法加载到 properties 表中的格式, 77 // 将此 properties 表中的属性列表(键和元素对)写入输出流 78 pro.store(fos, newurl); 79 fos.close(); 80 } catch (ioexception e) { 81 system.err.println("属性文件更新错误"); 82 } 83 84 } 85 }
4. 控制层:编写接口(此处以/hello为例)消费服务,编写接口用于组装监控面板监控对象,实现自动注册到turbine聚合监控面板上的功能。
监控面板监控对象,包括以下信息:
name:监控详情页显示的hystrix stream名称
stream:需要监控的服务接口
auth:作者
delay:获取监控数据间隔时间
@restcontroller public class consumercontroller { @autowired private resttemplate resttemplate; //创建了熔断器的功能 ,并指定了defaultstores熔断方法 //@hystrixcommand 表明该方法为hystrix包裹, //可以对依赖服务进行隔离、降级、快速失败、快速重试等等hystrix相关功能 //fallbackmethod 降级方法 //commandproperties 普通配置属性,可以配置hystrixcommand对应属性,例如采用线程池还是信号量隔离、熔断器熔断规则等等 //ignoreexceptions 忽略的异常,默认hystrixbadrequestexception不计入失败 //groupkey() 组名称,默认使用类名称 //commandkey 命令名称,默认使用方法名 @hystrixcommand(fallbackmethod = "defaultstores") @getmapping(value = "/hello") public string hello(throwable throwable) { return resttemplate.getforentity("http://eureka-provider/", string.class).getbody(); } //熔断方法直接返回了一个字符串, "feign + hystrix ,提供者服务挂了" public string defaultstores(throwable throwable) { return "ribbon + hystrix ,提供者服务挂了"; } //用于组装监控面板监控对象,实现自动注册到turbine聚合监控面板上的功能。 @requestmapping(value = "/sendmsg") public string sendmessage(httpservletrequest request) { jsonobject lan=new jsonobject(); lan.addproperty("name", "9001"); lan.addproperty("stream", "http://127.0.0.1:9001/hystrix.stream"); lan.addproperty("auth", "test"); lan.addproperty("delay", "2000"); system.out.println("jaon-lan------"+lan); return "callback("+lan+")"; } }
二、搭建ribbon-consumer-hystrix-2项目:
做法同一,配置文件端口和sendmessage方法中端口改成9002。
三、搭建readlocalproperties项目:
1. 在pom.xml中添加如下依赖:
<dependency> <groupid>org.springframework.cloud</groupid> <artifactid>spring-cloud-starter-eureka</artifactid> </dependency>
2.配置文件:
eureka: client: serviceurl: defaultzone: http://localhost:8761/eureka/ instance: leaseexpirationdurationinseconds: 4 leaserenewalintervalinseconds: 1 spring: application: name: ribbon-consumer-hystrix-dashbord server: port: 9999
3.启动类:
@enablediscoveryclient @springbootapplication public class readapplication { public static void main(string[] args) { springapplication.run(readapplication.class, args); } }
4.控制层:编写接口实现从本地文件中读取接口列表。用于获取消费者注册信息,供turbine项目调用。
@restcontroller public class consumercontroller { @requestmapping(value = "/read") public string read(httpservletrequest request) { // 读取文件中已有的url properties propp = new properties(); string oldurl = ""; try { inputstream ins = new fileinputstream(new file("f:\\config.properties")); propp.load(ins); oldurl = propp.getproperty("url"); system.out.println("oldurl---" + oldurl); ins.close(); } catch (filenotfoundexception e1) { // todo auto-generated catch block e1.printstacktrace(); } catch (ioexception e1) { // todo auto-generated catch block e1.printstacktrace(); } return "readcallback(\"" + oldurl + "\")"; } }
四、重点改造搭建turbine-dashboard项目:
项目首先导入turbine项目,然后我们对其前端进行改造,通过ajax跨域请求readlocalproperties项目,读取本地文件f:\\config.properties中的urls,然后在监控面板中显示。
还记得读取本地文件f:\\config.properties中的urls是什么时候添加的吗?忘记的同学可以往上翻,懒得翻的同学我告诉你,就是在消费者项目启动时,通过writeconfig()方法将配置文件中的url添加到本地文件。
那配置文件中url的是什么呢?忘记的同学可以往上翻,懒得翻的同学我告诉你,配置文件中url是该项目封装的监控对象信息。当turbine-dashboard项目启动后,会从本地文件中读取这些url,然后获取一个个需要监控的对象,从而显示在监控面板上。
1.项目结构:(这里的项目结构是我改造后的)
2.登录页面:
后端:修改/hystrix_dashboard路径的路由,使之路由到login.html,进行登录认证。
前端:新建login.html页面,进行登录认证,并将登录信息存入cookie。
1 <html> 2 <head> 3 <meta http-equiv="content-type" content="text/html; charset=utf-8"> 4 <title>登录</title> 5 </head> 6 <script type="text/javascript"> 7 function validatelogin() { 8 var username = document.getelementbyid('username').value; 9 var password = document.getelementbyid('password').value; 10 if (username == "" || password == "") { 11 alert("请输入用户名密码!"); 12 return false; 13 } else if (username != 'admin' || password != "admin") { 14 alert("用户名密码不正确!"); 15 return false; 16 } else { 17 setcookie('username',username,10);//cookie保存10分钟 18 setcookie('password',password,10);//cookie保存10分钟 19 //校验成功进入监控面板主页 20 location.href = "/hystrix-dashboard/main"; 21 /* location.href = "/hystrix-dashboard/main?username=" + username 22 + "&&password=" + password; */ 23 } 24 } 25 /* 创建和存储 cookie */ 26 function setcookie(c_name, value, expire) { 27 var exdate = new date() 28 exdate.settime(exdate.gettime()+expire*60*1000); //设置date为当前时间分钟 29 // exdate.setdate(exdate.gethour() + expiredays) 30 document.cookie = c_name 31 + "=" 32 + escape(value) 33 + ((expire == null) ? "" : ";expires=" 34 + exdate.togmtstring()) 35 } 36 /* 检查是否已设置 cookie */ 37 function getcookie(c_name) 38 { 39 if (document.cookie.length>0) 40 { 41 c_start=document.cookie.indexof(c_name + "=") 42 if (c_start!=-1) 43 { 44 c_start=c_start + c_name.length+1 45 c_end=document.cookie.indexof(";",c_start) 46 if (c_end==-1) c_end=document.cookie.length 47 return unescape(document.cookie.substring(c_start,c_end)) 48 } 49 } 50 return "" 51 } 52 53 </script> 54 <body> 55 <div style="width: 800px; margin: 0 auto;"> 56 <center> 57 <img width="264" height="233" src="images/hystrix-logo.png"> 58 <br> <br> 59 <br> <br> 60 <h2>hystrix dashboard</h2> 61 </center> 62 </div> 63 <center> 64 <form id="form" action="/hystrix-dashboard/test" method="post"></form> 65 <table> 66 <tr> 67 <td>username:</td> 68 <td><input type="text" name="username" id="username" size="20" 69 maxlength="20" /></td> 70 </tr> 71 <tr> 72 <td>password:</td> 73 <td><input type="password" name="password" id="password" 74 size="20" maxlength="20" /></td> 75 </tr> 76 77 <tr> 78 <td><input type="submit" name="login" value="login" 79 style="margin-top: 50%; margin-left: 150%" 80 onclick="return validatelogin()" /></td><!-- 进行登录校验 --> 81 </tr> 82 83 </table> 84 </form> 85 </center> 86 87 </body> 88 </html>
3.改造项目中的监控面板主页、监控详情页
后端:对/main(监控面板主页)、/monitor(监控详情页)路径进行cookie认证
前端:获取需要监控的对象
1 $(function(){ 2 $('#streams').html('<table id="hystrix_list"></table>'); 3 }) 4 //跨域请求read接口,获取本地文件中存储的urls 5 $.getjson("http://127.0.0.1:9999/read?jsoncallback=?"); 6 function readcallback(url){ 7 alert("read.url--"+url); 8 var strs= new array(); 9 strs=url.split(","); 10 for (i=0;i<strs.length ;i++ ) 11 { 12 if(strs[i]!="null"){ 13 $.getjson(strs[i]+"?jsoncallback=?"); //$.getjson("http://localhost:9000/sendmessage?jsoncallback=?"); 14 }else{ 15 continue; 16 } 17 } 18 } 19 //跨域请求本地文件中存储的urls,获取需要进行监控的对象 20 function callback(data){ 21 c={ 22 name:data.name, 23 stream: data.stream, 24 auth: data.auth, 25 delay: data.delay 26 }; 27 streams.push(c); 28 /* $('#streams').html('<table>' + _.reduce(streams, function(html, c) { 29 return html + '<tr><td>' + c.name + '</td><td>' + c.stream + '</td> <td><a href="#" onclick="removestream(this);">remove</a></td> </tr>'; 30 }, '') + '</table>'); */ 31 32 $("#hystrix_list").append('<tr><td>' + c.name + '</td><td>' + c.stream + '</td> <td><a href="#" onclick="removestream(this);">remove</a></td> </tr>'); 33 } 34 //添加需要监控的stream 35 function addstream () { 36 if ($('#stream').val().length > 0) { 37 var s = { 38 name: $('#title').val(), 39 stream: $('#stream').val(), 40 auth: $('#authorization').val(), 41 delay: $('#delay').val() 42 }; 43 streams.push(s); 44 /* $('#streams').html('<table>' + _.reduce(streams, function(html, s) { 45 return html + '<tr><td>' + s.name + '</td><td>' + s.stream + '</td> <td><a href="#" onclick="removestream(this);">remove</a></td> </tr>'; 46 }, '') + '</table>'); */ 47 $("#hystrix_list").append('<tr><td>' + s.name + '</td><td>' + s.stream + '</td> <td><a href="#" onclick="removestream(this);">remove</a></td> </tr>'); 48 $('#title').val(""); 49 $('#stream').val(""); 50 $('#authorization').val(""); 51 $('#delay').val(""); 52 $('#message').html(""); 53 } else { 54 $('#message').html("the 'stream' value is required."); 55 } 56 }
五、启动步骤:
1. 依次启动eureka-service、eureka-provider_1、eureka-provider_2、ribbon-consumer-hystrix-1、ribbon-consumer-hystrix-2、readlocalproperties项目 :
可以看见eureka监控面板都注册上了这五个服务:
打开本地配置文件f:\\config.properties,可以看见两个消费者项目的获取监控对象的url已经保存在本地文件了:
2.启动turbine-dashboard项目:
由于我们在浏览器输入的地址是http://localhost:7979/hystrix-dashboard/,经上文我们改造过后,路由到了登录页面。
接下来我们输入代码中设置好的用户名密码,admin/admin,再点击login,结果进入到了监控主页:啊!又是这只恶狠狠的刺猬熊呢!
并且,我们可以看到在主页下方已经自动添加了两个监控对象,这就是我们之前在启动消费者对象的时候,保存的对象,当我们打开监控面板时获取到页面上了。
当然你也可以在此页面上手动添加和删除,然后直接点击“monitor stream”就可以进入监控详情页。
下图为未改造前的监控面板主页,需要手动添加监控对象。
点击“monitor stream”就可以进入监控详情页:
可以发现turbine dashboard项目实现了集群监控。
还没结束,为了测试是否实现消费者自动注册到turbine,我们在turbine项目启动的情况下,直接启动端口为9005的消费者项目,随后返回turbine监控主页面进行刷新,刷新后页面如下:
监控主页添加了一个新的消费者对象。
进入监控详情页,开始监控所有消费者对象。
虽然是在原来项目上已经做了改造,但是实际用的话还需要优化,比如登录用户密码、本地文件存储地址等可以写在配置文件中,从而可以根据不同环境进行打包等。
总之。。。有待改进
最后,附上本文用到的所有项目:newturbine.zip
链接:https://pan.baidu.com/s/1mamx5d77towvphzbcbeqsg
提取码:xxdz
下一篇: 银灰色的浴帽