上篇用了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>
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; } }
SearchEntity
这里新加了一个 SearchHystrix
package com.springtest.client;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.HystrixCommandProperties;
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(); } }
现在就算配置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(); } }
从等待时间 可以看到 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; } }
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; } }
这里还是有区别的 一个是讨论 一个是博客
修改下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(); } }
修改下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>
前端代码不具体说了 没啥特别的
访问地址http://localhost:8881/
点击搜索 往下滑会自动分页
基础功能完成了
原文地址:https://www.cnblogs.com/dikeboy/p/10066846.html