Spring cloud实战 从零开始一个简单搜索网站_Hystrix未封装版动态commandkey(四)

 

上篇用了2个方法来来做断路由,太麻烦了  Spring Cloud集成的是javanica hystrix 虽然集成方便了很多,不过

javanica默认好像不支持 动态修改 commandKey , Hystrix是根据commandKey来分配每个熔断器的  网上也有些解决办法

不过感觉还是太蛋疼了  算了 还是直接用Hystrix好了


先把javanica hystrix的引用换成 hystrix core

            <dependency>
    <groupId>com.netflix.hystrix</groupId>
    <artifactId>hystrix-core</artifactId>
    </dependency>

 

POM配置

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>tsearch_web</groupId>
    <artifactId>springtest-client</artifactId>
    <version>0.0.1</version>
    <packaging>jar</packaging>

    <name>springtest-client</name>
    <description>Note Server catch</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.M3</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        
        <!-- 断路由 -->
            <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
        </dependency>
            <dependency>
    <groupId>com.netflix.hystrix</groupId>
    <artifactId>hystrix-core</artifactId>
    </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Finchley.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>


</project>
View Code

Application 需要把断路由注解去掉 不然会报Caused by: java.lang.NoClassDefFoundError: com/netflix/hystrix/contrib/javanica/aop/aspectj/HystrixCommandAspect

@EnableEurekaClient
@SpringBootApplication
@EnableHystrixDashboard
public class SpringtestClientApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringtestClientApplication.class, args);
    }
    @Bean
    public ServletRegistrationBean getServlet() {
        HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
        ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
        registrationBean.setLoadOnStartup(1);
        registrationBean.addUrlMappings("/hystrix.stream");
        registrationBean.setName("HystrixMetricsStreamServlet");
        return registrationBean;
    }
}
package com.springtest.client;

import org.springframework.web.client.RestTemplate;

public class SearchEntity {
    public String key;
    public String page;
    public String  serviceId;
    RestTemplate restTemplate;
    public String url;
    
    public SearchEntity(String key,String page,String serviceId) {
        this.key = key;
        this.page = page;
        this.serviceId = serviceId;
    }
    
    public SearchEntity url(String url) {
        this.url = url;
        return this;
    }
    public SearchEntity restTemplate(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
        return this;
    }
}
View Code

SearchEntity

 

这里新加了一个 SearchHystrix

public class SearchHystrix extends HystrixCommand<String> {
    private SearchEntity mSearchEntity;
    private SearchMethodListener smListener;

    public SearchHystrix(SearchEntity entity, SearchMethodListener smListener) {
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(entity.serviceId))
                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                        .withCircuitBreakerRequestVolumeThreshold(5).withCircuitBreakerSleepWindowInMilliseconds(10000))
                .andCommandKey(HystrixCommandKey.Factory.asKey(entity.serviceId)));
        this.mSearchEntity = entity;
        this.smListener = smListener;
    }

    @Override
    protected String run() throws Exception {
        // TODO Auto-generated method stub
        if (smListener != null) {
            return smListener.onSearch(mSearchEntity);
        }
        return null;
    }

    public interface SearchMethodListener {
        public String onSearch(SearchEntity entity);
    }

    @Override
    protected String getFallback() {
        // TODO Auto-generated method stub
        return "error:" + mSearchEntity.serviceId;
    }
}

 

Group key 不用管 默认DEMO都自带的 

withCircuitBreakerRequestVolumeThreshold  这个是报错多少次后垄断 为了测试方便 可以改小一点 
用脚本测的话最好弄个sleep(50)不然这个数据会不准
withCircuitBreakerSleepWindowInMilliseconds 这个是垄断多久后恢复, 默认好像是恢复一次  比如你出错了5次 
等10秒后 恢复 又出错了一次 又会垄断
修改下ClientService
@Service
public class ClientService implements SearchMethodListener{
    @Autowired
    RestTemplate restTemplate;
    @Autowired
    private EurekaClient discoveryClient;


    @Value("${serviceIds}")
    public String serviceIds;

    public String search(String key, String page) {
        StringBuffer sb = new StringBuffer();
        if (serviceIds.contains(",")) {
            String[] ids = serviceIds.split(",");
            for(int i=0;i<ids.length;i++) {
                String res = new SearchHystrix(new SearchEntity(key, page, ids[i]),this).execute();
                sb.append(res);
            }
        }
        else {
            String res = new SearchHystrix(new SearchEntity(key, page, serviceIds),this).execute();
            sb.append(res);
        }
        return sb.toString();
    }


    @Override
    public String onSearch(SearchEntity entity) {
        // TODO Auto-generated method stub
        HashMap<String, String> map = new HashMap<>();
        map.put("key", entity.key);
        map.put("page", entity.page);
        String str = restTemplate.getForObject(serviceUrl(entity.serviceId) + "/search?key={key}&page={page}", String.class,
                map);
        return str;
    }
    
    public String serviceUrl(String serviceId) {
        InstanceInfo instance = discoveryClient.getNextServerFromEureka(serviceId, false);
        return instance.getHomePageUrl();
    }

    @Bean
    RestTemplate restTemplate() {
        return new RestTemplate();
    }

}
View Code

现在就算配置100个搜索端也不用改代码了    因为resttemplete默认是实例化当前类的   所以我把他放在这里做了个回调

两个搜索端还是一样  CSDN 端弄个sleep 5 秒   SDN 正常 重启下Client

测试脚本

package com.lin.search;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

public class TestHystic {
    public static void main(String[]  args) {
        long time1=System.currentTimeMillis();
        String b ="";

        for(int i=0;i<100;i++) {
            try {
                getData();
                System.out.println("usertime="+(System.currentTimeMillis()-time1));
                time1=System.currentTimeMillis();
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    
    public static  void  getData() throws Exception {
        URL serverUrl = new URL("http://localhost:8881/search?key=spring&page=1");
        HttpURLConnection conn = (HttpURLConnection) serverUrl.openConnection();
        InputStream in = conn.getInputStream();
        BufferedReader br =new BufferedReader(new InputStreamReader(in, "UTF-8"));
       StringBuffer sb= new StringBuffer();
       String line;
        while((line=br.readLine())!=null) {
            sb.append(line);
        }
        System.out.println(sb.toString());
        in.close();
    }
}
View Code

 

从等待时间 可以看到 CSDN 方法执行了6次后 后面开始垄断 不执行了
hystrix dashboard不用改 
http://localhost:8881/hystrix/monitor?stream=http%3A%2F%2Flocalhost%3A8881%2Fhystrix.stream


 

 

 断路由这里差不多结束了 ,把2个CSDN  和SDN 改成正常返回数据

SDN

@RestController
public class CsdnController {
    Gson gson = new Gson();

    @RequestMapping(value = "/search")
    public String search(@RequestParam("key") String key, @RequestParam("page") String page) {
        System.out.println("search");
        ArrayList<HashMap<String, String>> result;
        try {
            result = SearchUtil.search(key, "discuss", page);
            return gson.toJson(result);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }
}
View Code

CSDN

@RestController
public class CsdnController {
    Gson gson = new Gson();

    @RequestMapping(value = "/search")
    public String search(@RequestParam("key") String key, @RequestParam("page") String page) {
        System.out.println("search");
        ArrayList<HashMap<String, String>> result;
        try {
            key = key.trim().replaceAll("\\s+" , "+");
            result = SearchUtil.search(key, "blog", page);
            return gson.toJson(result);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }
}
View Code

 

这里还是有区别的  一个是讨论 一个是博客

修改下ClientService 两个JSON 做下拼接

@Service
public class ClientService implements SearchMethodListener{
    @Autowired
    RestTemplate restTemplate;
    @Autowired
    private EurekaClient discoveryClient;


    @Value("${serviceIds}")
    public String serviceIds;

    public String search(String key, String page) {
        StringBuffer sb = new StringBuffer();
        if (serviceIds.contains(",")) {
            String[] ids = serviceIds.split(",");
            for(int i=0;i<ids.length;i++) {
                String res = new SearchHystrix(new SearchEntity(key, page, ids[i]),this).execute();
                int l=sb.length();
                sb.append(res);
                if(l>0) {
                    sb.replace(l-1, l+1, ",");
                }
            }
        }
        else {
            String res = new SearchHystrix(new SearchEntity(key, page, serviceIds),this).execute();
            sb.append(res);
        }
        System.out.println(sb.toString());
        return sb.toString();
    }


    @Override
    public String onSearch(SearchEntity entity) {
        // TODO Auto-generated method stub
        HashMap<String, String> map = new HashMap<>();
        map.put("key", entity.key);
        map.put("page", entity.page);
        String str = restTemplate.getForObject(serviceUrl(entity.serviceId) + "/search?key={key}&page={page}", String.class,
                map);
        return str;
    }
    
    public String serviceUrl(String serviceId) {
        InstanceInfo instance = discoveryClient.getNextServerFromEureka(serviceId, false);
        return instance.getHomePageUrl();
    }

    @Bean
    RestTemplate restTemplate() {
        return new RestTemplate();
    }

}
View Code

 

修改下Client项目下的 index.ftl 这里加了个自动分页是不是很高大上

 

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style>
            .searchBtn{
                background-color:#38f;
                color:#fff;
                font-size: 16px;
                padding-top: 0px;
                width: 80px;
                height: 34px;
                vertical-align: middle;
                padding: 0;
                border: 0;
            }
        .searchBtn:hover {
            background-color: #3171f3;
        }
        .searchBtn:active {
            background-color: #2964bb;
        }
            .searchSpan{
                padding-left: 10px;
                padding-right: 10px;
                margin-top: 0px;
                margin-bottom: 0px;
                border-color: #b8b8b8;
                width: 40%;
                vertical-align: middle;
                display: inline-block;
                height: 34px;
                border: 1px solid #b6b6b6;
            }
                .searchText{
                font-size: 16px;
                width: 100%;
                margin-top: 5px;
                    outline: 0;
                border: 0;
            }

        dt{
            margin: 0px;
            padding: 0px;
            font-size: 16px;
            color: #303030;
            line-height: 24px;
            margin-top: 20px;
        }
        dd{
            margin: 0px;
            padding: 0px;
            font-size: 14px;
            line-height: 22px;
            color: #999999;
        
        }
        a{
            text-decoration: none;
        }
    .contentDiv{
            width: 800px;
            text-align: left;
            padding-bottom: 30px;
            
        }
        .contentDiv em{
            color: #CA0C16;
            font-style:normal;
        }
        .nextdiv{
      width: 50px;
      height: 50px;
          position: relative;
  }
 .next a:visited {
    text-decoration: none;
    color: #9B8878;
}
        </style>
        <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.8.0.js"></script>
<script >
String.prototype.replaceAll = function(s1,s2){ 
    return this.replace(new RegExp(s1,"gm"),s2); 
}
$(document).ready(function(){
    var width =$(window).width();
    if(width<900){
        $(".searchSpan").css("width","50%")
        $(".searchBtn").css("width","20%")
        $("#contentDiv").css("width","80%")
            
    }
    else{
        $(".searchSpan").css("width","600px")
        $("#contentDiv").css("width","800px")
        $(".searchBtn").css("width","80px")
        $(".searchSpan").css("margin-left","-85px")
    }
    
    $('.searchText').bind('keydown',function(event){
        if(event.keyCode == "13") {
            openSearch(0);
        }
    });  
});

    function openSearch(state){
        if(state!=null){
               $("#contentDiv").html("");
        }
        isLoad = true;
        $.ajax({
             type: "GET",
             url: "search",
             data: {"key":$("#searchText").val(),
                 "page":p},
             dataType: "json",
             success: function(data){
                var str = "";
                   $.each(data, function(i, item){
                         str = str +"<dl><a target='_blank' href='"+item.link;
                         str = str+"'><dt>";
                        str= str + item.title;
                    str=str+"</dt>";
                          str = str +"<dd>";
                        str= str + item.content;
                    str=str+"</dd></a></dl>";
                   });
                  isLoad = false;
                $("#contentDiv").append(str);
             }
         });
    }
    var p=1;
    var isLoad = false;
    $(window).scroll(function(){
             if($(window).scrollTop()> $(document).height() - $(window).height()-10){
                   if(!isLoad){
                       p++;
                       openSearch();
                   }
             }
    });

</script>
    </head>
    <body >
    <div align="center" style="margin-top: 30px;font-size: 24px;margin-left: -50px;"  >博客搜</div>
        <div align="center" style="margin-top: 20px;"  >
            <span class="searchSpan">
                <input type="text" id="searchText"
             value="spring cloud"
                class="searchText" /></span>

        <input type="submit" value="Search" id="su" class="searchBtn"
            onclick="openSearch(0)"
            ></input>
    </div>
    <div align="center">
    <div id="contentDiv" class="contentDiv" ></div>
    </div>

    </body>
</html>
View Code

 

 

 

前端代码不具体说了  没啥特别的

访问地址http://localhost:8881/

点击搜索 往下滑会自动分页

基础功能完成了

 

posted @ 2018-12-04 21:01  dikeboyR  阅读(383)  评论(0编辑  收藏  举报