Spring cloud实战 从零开始一个简单搜索网站
Posted dikeboy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring cloud实战 从零开始一个简单搜索网站相关的知识,希望对你有一定的参考价值。
上篇用了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
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;Spring cloud实战 从零开始一个简单搜索网站让你从零开始搭建Spring Cloud Alibaba!这份Alibaba内部笔记太牛了
手把手教你,从零开始搭建Spring Cloud Alibaba!这份笔记太牛了
手把手教你,从零开始搭建Spring Cloud Alibaba!这份笔记太牛了