上篇用了 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
- 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();
- }
- }
- 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://localhost:8881/hystrix.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 = $(Windows).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;
- $(Windows).scroll(function() {
- if ($(Windows).scrollTop() > $(document).height() - $(Windows).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/
点击搜索 往下滑会自动分页
基础功能完成了
来源: http://www.bubuko.com/infodetail-2872769.html