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

Spring cloud實戰 從零開始一個簡單搜索網站(三)

public metrics mys .class tcl net model ons fda

上文已經完成了一個簡單的 瀏覽器 到 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

技術分享圖片

Spring cloud實戰 從零開始一個簡單搜索網站(三)