改造断路器集群监控Hystrix Turbine实现自动注册…

2019-04-25 06:57:51来源:博客园 阅读 ()

新老客户大回馈,云服务器低至5折

在上一篇文章中,我们搭建了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个项目可参考https://www.souyunku.com/categories/自行创建,或者从下文附件中下载。我们详细讲下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>
pom.xml

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
application.yml

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 }
RibbonConsumerApplication_1

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+")";
    }
}
ConsumerController

二、搭建ribbon-consumer-hystrix-2项目:

       做法同一,配置文件端口和sendMessage方法中端口改成9002。

三、搭建ReadLocalProperties项目:

        1. 在pom.xml中添加如下依赖: 

<dependency>
    <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-eureka</artifactId>
</dependency> 
pom.xml

         2.配置文件:

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
  instance:
    leaseExpirationDurationInSeconds: 4  
    leaseRenewalIntervalInSeconds: 1  
spring:
  application:
    name: ribbon-consumer-hystrix-dashbord

server:
  port: 9999
application.yml

        3.启动类: 

@EnableDiscoveryClient
@SpringBootApplication
public class ReadApplication {
    public static void main(String[] args) {
        SpringApplication.run(ReadApplication.class, args);
    }
}
ReadApplication

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 + "\")";
    }
}
ConsumerController

 四、重点改造搭建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>
login.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         }
main.html

 五、启动步骤:

       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 

 


原文链接:https://www.cnblogs.com/ValeryHome/p/10760605.html
如有疑问请与原作者联系

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:String字符串类总结

下一篇:Eclipse4JavaEE安装Gradle,并导入我们的Gradle项目