Spring cloud實戰 從零開始一個簡單搜尋網站(三)
上文已經完成了一個簡單的 瀏覽器 到 Client 到CSDN端的通路
我們的架構是每個部落格網址為一個單獨的元件, 這裡為了方便直接先用CSDN 那個元件複製下
我這裡改成 SDN 修改下 application.properties 埠記得改
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
spring.application.name=sdn
server.port=8983
下面是TOMCAT 和 eureka server執行的截圖
好了 有兩個搜尋端了,我這裡把CSDN SDN 叫做搜尋端了 , 下面修改下 Client端 讓他能分別呼叫2個元件
我修改了下github上的配置 把我們新建的serviceID加入進去
我們會回到Client專案的 ClientService 多了個SDN,需要修改下程式碼,能讓程式自動載入多個HOST(這裡只是簡單介紹,如果搜尋端好幾個肯定得改進)
@Service public 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;i<ids.length;i++) { sb.append(searchDetail(key, page, ids[i])); } } else { sb.append(searchDetail(key, page,serviceIds)); } return sb.toString(); } public String searchDetail(String key,String page,String serviceId) { HashMap<String, String> 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; } @Bean RestTemplate restTemplate() { return new RestTemplate(); } public String serviceUrl(String serviceId) { InstanceInfo instance = discoveryClient.getNextServerFromEureka(serviceId, false); return instance.getHomePageUrl(); } }
因為資料比較多 我把兩個搜尋端直接改成返回一個字串了
@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, "blog", page); // return gson.toJson(result); return "i am csdn 1"; } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } }View Code
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, "blog", page); // return gson.toJson(result); return "i am sdn 1"; } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } }View Code
這下有兩個端了,搜尋時間也延長了2倍, 但萬一個端有異常 另外一個端良好 怎麼辦 這時候就需要斷路由 hystrix
在client pom裡面匯入下
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</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</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
@EnableEurekaClient
@SpringBootApplication
@EnableCircuitBreaker
public class SpringtestClientApplication {
public static void main(String[] args) {
SpringApplication.run(SpringtestClientApplication.class, args);
}
}
新增個SearchUtil 類
@Component public 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); HashMap<String, String> 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; } @HystrixCommand(groupKey = "searchDetail2", commandKey = "searchDetail2",fallbackMethod = "stubMyService") public String searchDetail2(String key, String page, String serviceId) { System.out.println("come="+serviceId); HashMap<String, String> 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(); } }View Code
修改下 ClientService
@Service
public 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加個超載
@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, "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; } }View Code
繼續重新整理
然後看下控制檯 不是說好的壟斷嗎 為啥 還是進來了 雖然時間減少了
用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
@Component public 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); HashMap<String, String> 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; } @HystrixCommand(groupKey = "searchDetail2", commandKey = "searchDetail2",fallbackMethod = "stubMyService") public String searchDetail2(String key, String page, String serviceId) { System.out.println("come="+serviceId); HashMap<String, String> 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(); } }View Code
重啟下 不斷重新整理瀏覽器 發現 searchDetail1方法不執行了 好了 壟斷完成
我們需要知道元件的當前狀態 這時候就需要hystrix的dashbox
POM 中引入
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> </dependency>View Code
修改下ClientApplication 加入dashboard註釋
@EnableEurekaClient
@SpringBootApplication
@EnableCircuitBreaker
@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;
}
}
在瀏覽器輸入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(); } }View Code