1. 程式人生 > >Spring cloud實戰 從零開始一個簡單搜尋網站(三)

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