上文已经完成了一个简单的 浏览器 到 Client 到CSDN端的通路
我们的架构是每个博客网址为一个单独的组件, 这里为了方便直接先用CSDN 那个组件复制下
我这里改成 SDN 修改下 application.properties 端口记得改
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
spring.application.name=sdnserver.port=8983
下面是TOMCAT 和 eureka server运行的截图
好了 有两个搜索端了,我这里把CSDN SDN 叫做搜索端了 , 下面修改下 Client端 让他能分别调用2个组件
我修改了下github上的配置 把我们新建的serviceID加入进去
我们会回到Client项目的 ClientService 多了个SDN,需要修改下代码,能让程序自动加载多个HOST(这里只是简单介绍,如果搜索端好几个肯定得改进)
@Servicepublic class ClientService { @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;imap = new HashMap<>(); map.put("key", key); map.put("page", page); String str= restTemplate.getForObject(serviceUrl(serviceId)+"/search?key={key}&page={page}",String.class,map); return str; } @Bean RestTemplate restTemplate() { return new RestTemplate(); } public String serviceUrl(String serviceId) { InstanceInfo instance = discoveryClient.getNextServerFromEureka(serviceId, false); return instance.getHomePageUrl(); }}
因为数据比较多 我把两个搜索端直接改成返回一个字符串了
@RestControllerpublic 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> result; try {// result = SearchUtil.search(key, "blog", page);// return gson.toJson(result); return "i am csdn 1"; } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; }}
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> result; try {// result = SearchUtil.search(key, "blog", page);// return gson.toJson(result); return "i am sdn 1"; } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; }}
这下有两个端了,搜索时间也延长了2倍, 但万一个端有异常 另外一个端良好 怎么办 这时候就需要断路由 hystrix
在client pom里面导入下
org.springframework.cloud spring-cloud-starter-netflix-hystrix
POM 代码
4.0.0 tsearch_web springtest-client 0.0.1 jar springtest-client Note Server catch org.springframework.boot spring-boot-starter-parent 2.1.1.RELEASE UTF-8 UTF-8 1.8 Greenwich.M3 org.springframework.boot spring-boot-starter-freemarker org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-netflix-eureka-server org.springframework.cloud spring-cloud-starter-config org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-starter-netflix-hystrix org.springframework.cloud spring-cloud-dependencies Finchley.RELEASE pom import org.springframework.boot spring-boot-maven-plugin spring-milestones Spring Milestones https://repo.spring.io/milestone false
修改下application
@EnableEurekaClient@SpringBootApplication@EnableCircuitBreakerpublic class SpringtestClientApplication { public static void main(String[] args) { SpringApplication.run(SpringtestClientApplication.class, args); }}
新增个SearchUtil 类
@Componentpublic class SearchUtil { @Autowired RestTemplate restTemplate; @Autowired private EurekaClient discoveryClient; @HystrixCommand(groupKey = "searchDetail1", commandKey = "searchDetail1",fallbackMethod = "stubMyService") public String searchDetail1(String key, String page, String serviceId) { System.out.println("come="+serviceId); HashMapmap = new HashMap<>(); map.put("key", key); map.put("page", page); String str = restTemplate.getForObject(serviceUrl(serviceId) + "/search?key={key}&page={page}", String.class, map); return str; } @HystrixCommand(groupKey = "searchDetail2", commandKey = "searchDetail2",fallbackMethod = "stubMyService") public String searchDetail2(String key, String page, String serviceId) { System.out.println("come="+serviceId); HashMap map = new HashMap<>(); map.put("key", key); map.put("page", page); String str = restTemplate.getForObject(serviceUrl(serviceId) + "/search?key={key}&page={page}", String.class, map); return str; } public String stubMyService(String key, String page, String serviceId) { return "error"; } @Bean RestTemplate restTemplate() { return new RestTemplate(); } public String serviceUrl(String serviceId) { InstanceInfo instance = discoveryClient.getNextServerFromEureka(serviceId, false); return instance.getHomePageUrl(); }}
修改下 ClientService
@Servicepublic class ClientService { @Autowired SearchUtil sc; @Value("${serviceIds}") public String serviceIds; public String search(String key, String page) { StringBuffer sb = new StringBuffer(); if (serviceIds.contains(",")) { String[] ids = serviceIds.split(","); sb.append(sc.searchDetail1(key, page, ids[0])); sb.append(sc.searchDetail2(key, page, ids[1])); } else { sb.append(sc.searchDetail1(key, page, serviceIds)); } return sb.toString(); }}
不断刷新 浏览器 好像没啥区别
给CSDN的Client加个超载
@RestControllerpublic 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> result; try {// result = SearchUtil.search(key, "blog", page);// return gson.toJson(result); Thread.sleep(5000); return "i am csdn 1"; } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; }}
继续刷新
然后看下控制台 不是说好的垄断吗 为啥 还是进来了 虽然时间减少了
用Hystrix的时候要注意一点 Hystrix的 超时时间一点要大于resttemplete 或者你用 ribbon
Hystrix 的默认值全在 com.netflix.hystrix.HystrixCommandProperties
这个就是出错次数 private static final Integer default_circuitBreakerRequestVolumeThreshold = 20;// default => statisticalWindowVolumeThreshold: 20 requests in 10 seconds must occur before statistics matter
我们改下 SearchUtil
@Componentpublic class SearchUtil { @Autowired RestTemplate restTemplate; @Autowired private EurekaClient discoveryClient; @HystrixCommand(groupKey = "searchDetail1", commandKey = "searchDetail1",fallbackMethod = "stubMyService", commandProperties = { @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "5") }) public String searchDetail1(String key, String page, String serviceId) { System.out.println("come="+serviceId); HashMapmap = new HashMap<>(); map.put("key", key); map.put("page", page); String str = restTemplate.getForObject(serviceUrl(serviceId) + "/search?key={key}&page={page}", String.class, map); return str; } @HystrixCommand(groupKey = "searchDetail2", commandKey = "searchDetail2",fallbackMethod = "stubMyService") public String searchDetail2(String key, String page, String serviceId) { System.out.println("come="+serviceId); HashMap map = new HashMap<>(); map.put("key", key); map.put("page", page); String str = restTemplate.getForObject(serviceUrl(serviceId) + "/search?key={key}&page={page}", String.class, map); return str; } public String stubMyService(String key, String page, String serviceId) { return "error"; } @Bean RestTemplate restTemplate() { return new RestTemplate(); } public String serviceUrl(String serviceId) { InstanceInfo instance = discoveryClient.getNextServerFromEureka(serviceId, false); return instance.getHomePageUrl(); }}
重启下 不断刷新浏览器 发现 searchDetail1方法不执行了 好了 垄断完成
我们需要知道组件的当前状态 这时候就需要hystrix的dashbox
POM 中引入
org.springframework.cloud spring-cloud-starter-netflix-hystrix-dashboard
修改下ClientApplication 加入dashboard注释
@EnableEurekaClient@SpringBootApplication@EnableCircuitBreaker@EnableHystrixDashboardpublic 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; }}
在浏览器输入http://localhost:8881/hystrix 就能看到界面
按上面的填写好 点击Monitor Stream就能进入统计页面
写个函数拼命跑 会发现数字多有变化 鼠标放上去可以看到具体说明
public class TestHystic { public static void main(String[] args) { long time1=System.currentTimeMillis(); String b =""; System.out.println(b.length()); 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(); }}