Eureka服务发现和注册_服务容错hystrix

Eureka Server: 服务注册中心,负责服务列表的注册、维护和查询等功能;

Service Provider: 服务提供方,同时也是一个Eureka Client,负责将所提供的服务向Eureka Server进行注册、续约和注销等操作。

  注册时所提供的主要数据包括:服务名、机器ip、端口号、域名等,从而能够使服务消费方能够找到。

Service Consumer: 服务消费方,同时也是一个Eureka Client,同样也会向Eureka Server注册本身所提供的服务。

  在本示例工程中更多的是从Eureka Server中获取相应的服务列表,以便能够发起服务调用。

Service Provider(服务提供方)和Service Consumer(服务消费方)并不是一个严格的概念,往往服务消费方也是一个服务提供方,同时服务提供方也可能会调用其它服务方所提供的服务。

  当然在我们进行微服务构建时还是需要遵守业务层级之间的划分,尽量避免服务之间的循环依赖。

断路器熔断机制:当Hystrix Command请求后端服务失败数量超过一定比例(默认50%), 断路器会切换到开路状态(Open). 这时所有请求会直接失败而不会发送到后端服务.

  断路器保持在开路状态一段时间后(默认5秒), 自动切换到半开路状态(HALF-OPEN). 这时会判断下一次请求的返回情况, 如果请求成功, 断路器切回闭路状态(CLOSED), 否则重新切换到开路状态(OPEN).

 

第一部分:创建服务注册cloud-eureka-server 工程

1,创建一个基础的Spring Boot工程,名字叫cloud-eureka-server,并在pom.xml中引入需要的依赖内容,如下:

<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>com.abc.cloud</groupId>
  <artifactId>cloud-eureka-server</artifactId>
  <version>1.0.0</version>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.4.3.RELEASE</version>
    <relativePath />
  </parent>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Brixton.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>
</project>

注意:这里的版本号需要对应,随便写版本号可能会报错,导致注解不能引入。

2,创建一个启动类AppEurekaServer

@EnableEurekaServer
@SpringBootApplication
public class AppEurekaServer {
    
    public static void main(String[] args) {
        SpringApplication.run(AppEurekaServer.class, args);
    }

}

通过@EnableEurekaServer注解启动一个服务注册中心提供给其他应用进行对话。

3,创建一个配置文件 application.properties,注意不要出现空格,否启动报错。

server.port=8000

# if register self to eureka server ? default value is true. 是否注册自己到eureka server,因为自己就是eureka server,所以设为false.

eureka.client.register-with-eureka=false

# if get register info from other eureka server ? default=true, single eureka server set false. 是否从其他eureka server获取注册信息?

eureka.client.fetch-registry=false

security.basic.enabled=true

security.user.name=admin

security.user.password=123

# set exchange address, search and register sevice all need this address. 服务提供者或消费者 用来注册或发现服务的url地址。

eureka.client.serviceUrl.defaultZone=http://admin:123@localhost:8000/eureka/

4,编译打包,查看注册中心:

    http://127.0.0.1:8000/   输入用户admin 密码123 进入管理界面。

 

第二部分:创服务提供者cloud-service-provider工程

1,pom文件和cloud-eureka-server 工程的pom文件的区别。

  a,cloud-starter-eureka依赖有区别:(没有-server)

     <dependency>
        <groupId>org.springframework.cloud</groupId>
           <artifactId>spring-cloud-starter-eureka</artifactId>
    </dependency> 
 b,需要把 spring-boot-starter-security 这个依赖删掉,否则访问服务时,总是弹出“需要进行身份验证”的小窗口,而且输入用户和密码后,小窗口还不消失。

2,application.properties文件配置:

spring.application.name=eureka.client.provider.test

server.port=8762

eureka.client.serviceUrl.defaultZone=http://admin:123@localhost:8000/eureka/

3,写服务接口类:

public interface TestIntf {

   @RequestMapping(value = "/testHello", method = RequestMethod.GET)

   public String testHello(@RequestParam("name") String name);

    @RequestMapping(value = "/testRibbon", method = RequestMethod.GET)
    public String testRibbon(@RequestParam("version") String version, @RequestParam("date") Date date);
    
    @RequestMapping(value = "/testRibbon2", method = RequestMethod.POST)
    public String testRibbon2(@RequestParam("version") String version);

}

4,写服务接口实现类:

@RestController

public class TestImpl implements TestIntf {

   public String testHello(String name) {

      return "Hello world,"+name;

   }

 public String testRibbon(@RequestParam("version") String version, @RequestParam("date") Date date) {
        return "test ribbon ok. version = "+version + ", date = "+date;
    }

    public String testRibbon2(String version) {
        return "test ribbon2 ok. version = "+version;
    }

}

5,写工程启动类:

如果选用的注册中心是eureka,那么就推荐@EnableEurekaClient,如果是其他的注册中心,那么推荐使用@EnableDiscoveryClient。

//@EnableDiscoveryClient
@EnableEurekaClient
@EnableAutoConfiguration
@SpringBootApplication
public class AppServiceProvider {
    public static void main(String[] args) {
        new SpringApplicationBuilder(AppServiceProvider.class).web(true).run(args);
    }
}

6,启动cloud-service-provider 工程

会在http://127.0.0.1:8000/ 管理界面 看到注册了新的服务:EUREKA.CLIENT.TEST01

然后用http://127.0.0.1:8762/testHello?name=longe 测试,结果如下:

Hello world,longe

 

第三部分:创建一个cloud-service-consumer工程

在调用cloud远程服务时必须使用HTTP客户端,这一部分采用Spring的RestTemplate的方式来实现,但是,用起来最方便、最优雅的还是要属Feign了。

关联web前端(与web, android, ios交互)的服务消费者工程。

1,pom文件    <properties>

        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <!--以下两项需要如果不配置,解析themleaft 会有问题-->
        <thymeleaf.version>3.0.2.RELEASE</thymeleaf.version>
        <thymeleaf-layout-dialect.version>2.0.5</thymeleaf-layout-dialect.version>
    </properties>
    <dependencies>
        <!-- spring boot -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </dependency>
        <!-- spring cloud -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>        
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
        </dependency>

  <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
  </dependency>

    </dependencies>

说明:加了spring-cloud-starter-ribbon依赖,该子项目提供客户端负载均衡功能。

引入 spring-cloud-starter-hystrix 依赖,提供服务容错功能,Hystrix 默认超时时间为 2000 毫秒。 

2,编写启动类:

@EnableEurekaClient

@EnableCircuitBreaker
@SpringBootApplication
public class AppServiceConsumer {
    
    @Bean
    @LoadBalanced
    RestTemplate restTemplate() {
        return new RestTemplate();
    }

    public static void main(String[] args) {
        SpringApplication.run(AppServiceConsumer.class, args);
    }

}

说明:@EnableCircuitBreaker 注解开启断路器功能,Hystrix 默认超时时间为 2000 毫秒。

3,编写服务调用类:

/** @RestController注解相当于@ResponseBody + @Controller合在一起的作用。 */

@Controller
@RequestMapping("/")
public class HelloController {
    
    @Autowired
    private RestTemplate restTemplate;

@Autowired
HelloService helloService;

    @RequestMapping(value="/index", method=RequestMethod.GET)  
    public String index(HttpServletRequest request) {
        return "index";
    }

    @ResponseBody
    @RequestMapping(value = "/helloRibbon", method = RequestMethod.GET)
    public String helloRibbon(@RequestParam("version") String version) {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("version", version);
        map.put("date", new Date());
        return restTemplate.getForEntity("http://eureka.client.provider.test/testRibbon?version={version}&date={date}",
                String.class, map).getBody();
    }

  // 测试服务容错保护hystrix
  @ResponseBody
  @RequestMapping(value = "/helloRibbon2", method = RequestMethod.GET)
  public String helloRibbon2(@RequestParam("version") String version) {
    Map<String, Object> map = new HashMap<String, Object>();
    map.put("version", version);
    map.put("date", new Date());
    return helloService.helloService(map);
  }

    
    @ResponseBody
    @RequestMapping(value = "/postForObject_test", method = RequestMethod.POST)
    public String postForObject_test(@RequestParam("version") String version) {
        MultiValueMap<String, String> requestParam = new LinkedMultiValueMap<>();
        requestParam.set("version", version);
        return restTemplate.postForObject("http://eureka.client.provider.test/testRibbon2", requestParam, String.class);
    }

}

32,新建HelloService类:用来测试服务容错保护hystrix

@Service
public class HelloService {

@Autowired
RestTemplate restTemplate;

@HystrixCommand(fallbackMethod = "helloFallback")
public String helloService(Map<String, Object> map) {
  return restTemplate.getForEntity("http://eureka.client.provider.test/testRibbon?version={version}&date={date}", String.class, map).getBody();
}

// 如果方法helloFallback和helloService方法参数不同,则会报错误:allback method wasn't found: helloFallback([interface java.util.Map])

public String helloFallback(Map<String, Object> map) {
  return "error hhl.";
}

}

4,application.properties文件配置:

server.port=8763
spring.application.name=eureka.client.consumer.test
eureka.client.service-url.defaultZone=http://admin:123@localhost:8000/eureka

5,测试:

  51,测试GET方法:

    在浏览器中输入:http://192.168.20.211:8763/helloRibbon?version="3.0.4"  会得到结果:

    test ribbon ok. version = "3.0.4", date = Tue Jan 30 07:36:33 CST 2018

  52,测试POST方法:

    新增一个页面,然后页面用POST方法请求,如:

    <form action="/postForObject_test" method="POST">
            <label for="version">version:</label> <input type="text" name="version" size="50"/><br />
            <input type="submit" value="submit"/>  
         </form>

    然后在页面输入version字段的字符串,如:1.32  ,点提交,这时页面会变成:

    test ribbon2 ok. version = 1.32

 

至此,spring cloud的服务注册中心,服务提供者注册服务,服务消费者用restful方式调用远程服务 都已实现,并测试OK。

 

第四部分:创建一个cloud-consumer-feign工程:采用feign的方式来实现cloud的远程服务调用

第三部分是采用Spring的RestTemplate的方式来实现,但是,用起来最方便、最优雅的还是要属Feign了。

1,在pom.xml文件引入feign依赖的jar包:

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-feign</artifactId>
</dependency>

2,application.yml  配置文件如下:

eureka:
  client:
    serviceUrl:
      defaultZone: http://admin:123@localhost:8000/eureka/
server:
  port: 8764
spring:
  application:
    name: cloud-consumer-feign-test01

feign:
  hystrix:
    enabled: true

3,应用程序启动类AppConsumerFeign

/**
* Feign 采用的是基于接口的注解,Feign 整合了ribbon,默认实现了负载均衡
* Feign能干Ribbon和Hystrix的事情
*/
@EnableEurekaClient
@SpringBootApplication
@EnableFeignClients
public class AppConsumerFeign {
  public static void main(String[] args) {
    SpringApplication.run(AppConsumerFeign.class, args);
  }

}

4,和web界面交互的controller类:

@RestController
public class HiController {

@Autowired
HiService hiService;

@RequestMapping(value = "/testHello",method = RequestMethod.GET)
public String testHello(@RequestParam String name){
  return hiService.testHello(name);
}

}

5,调用cloud远程服务的service类:

// 下面的FeignClient value是服务提供者的spring.application.name,调用的对应方法是testHello

//@FeignClient(value = "eureka.client.provider.test")
// 添加指定fallback类,在服务熔断的时候返回fallback类中的内容。
@FeignClient(name = "eureka.client.provider.test", fallback = HiServiceHystrix.class)

public interface HiService {
  @RequestMapping(value = "/testHello",method = RequestMethod.GET)
  String testHello(@RequestParam(value = "name") String name);

}

6,启动项目,测试

报错:Spring Boot ::        (v1.4.3.RELEASE)       FeignClient] must be declared as an @AliasFor [serviceId], not [name].

原因是spring cloud的版本和spring boot的版本造成的,cloud Brixton版本的feign使用的是spring boot 1.3.x的版本。

所以,我们在选择Spring Boot与Spring Cloud版本的时候,还是需要尽可能的按照Spring Cloud官方版本依赖关系来使用:

cloud Brixton版本对应Spring Boot 1.3.x       cloud  Camden版本对应Spring Boot 1.4.x

因为我们上面用的cloud Brixton版本,所以需要把feign的pom.xml 改为如下:

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>1.3.8.RELEASE</version>
</parent>

测试:在浏览器中输入:http://192.168.20.211:8764/testHello?name="dragon",在界面会输出:

Hello world,"dragon"

测试成功。

7,写一个类用Hystrix来对feign的接口类HiService实现熔断保护:

// 用Hystrix 对HiService接口类的方法做熔断保护,上面的HiService接口类也会标记用此类来做熔断保护。
@Component
public class HiServiceHystrix implements HiService {

@Override
public String testHello(String name) {
  return "hello" +name+", this messge send failed.";
}

}

测试:停掉cloud provider服务,然后在浏览器中输入:http://192.168.20.211:8764/testHello?name="dragon2",在界面会输出:

hello"dragon2", this messge send failed.

测试成功。

 

posted @ 2018-01-27 19:28  涓水之子  阅读(116)  评论(0)    收藏  举报