Spring cloud实战 从零开始一个简单搜索网站

Posted dikeboy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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

 

以上是关于Spring cloud实战 从零开始一个简单搜索网站的主要内容,如果未能解决你的问题,请参考以下文章

Spring cloud实战 从零开始一个简单搜索网站

让你从零开始搭建Spring Cloud Alibaba!这份Alibaba内部笔记太牛了

手把手教你,从零开始搭建Spring Cloud Alibaba!这份笔记太牛了

手把手教你,从零开始搭建Spring Cloud Alibaba!这份笔记太牛了

手把手教你,从零开始搭建Spring Cloud Alibaba!这份笔记太牛了

深入浅出Spring原理及实战「原理分析专题」从零开始教你SpringEL表达式使用和功能分析讲解指南(上篇)