Spring Cloud——声明式服务调用:Spring Cloud Feign

Spring Cloud Feign整合了Spring Cloud Ribbon与Spring Cloud Hystrix,除了提供这两者的强大功能之外,还提供了一种声明式的Web服务客户端定义方式。同时,Spring Cloud Feign具备可插拔的注解支持,包括Feign注解和JAX-RS注解。

(一)服务注册中心

创建一个Spring Boot工程,命名为cloud_eureka_server,并在pom.xml中引入必要的模块:



    4.0.0

    iwhale
    cloud_eureka_server
    0.0.1-SNAPSHOT
    jar

    cloud_eureka_server
    Demo project for Spring Boot

    
        org.springframework.boot
        spring-boot-starter-parent
        1.5.13.RELEASE
         
    

    
        UTF-8
        UTF-8
        1.8
    

    
        
            org.springframework.boot
            spring-boot-starter
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
        

        
            org.springframework.cloud
            spring-cloud-starter-eureka-server
        
    

    
    
        
            
                org.springframework.cloud
                spring-cloud-dependencies
                Edgware.SR3
                pom
                import
            
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    

在应用中添加注解@EnableEurekaServer

@EnableEurekaServer
@SpringBootApplication
public class CloudEurekaServerApplication {

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

在配置文件application.yml添加一下配置:

server:
  port: 8888

eureka:
  instance:
    hostname: localhost
    lease-expiration-duration-in-seconds: 30  #表示eureka server至上一次收到client的心跳后,等下一次心跳的超时时间,在这个时间内若没收到下一次心跳,则将移除该instance,默认90s。
    lease-renewal-interval-in-seconds: 10 #服务续约间隔时间
  server:
    enable-self-preservation: false #关闭保护机制,以确保注册中心可以将不可用的实例正确剔除
  client:
    register-with-eureka: false #不注册自己
    fetch-registry: false
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

(二)服务提供者

创建一个Spring Boot工程,命名为cloud_service,pom.xml内容与注册中心一样

在应用中添加注解@EnableDiscoveryClient,获得服务发现的能力

@EnableDiscoveryClient
@SpringBootApplication
public class CloudServiceApplication {

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

在配置文件application.yml指定服务命名cloudservice,指定服务注册中心地址,添加一下配置:

server:
  port: 9881
#  context-path: /${spring.application.name}
#restTemplate.getForEntity("http://cloudservice/hello",String.class).getBody();    这样访问时需注释掉context-path

#############################spring配置#############################
spring:
  application:
    name: cloudservice

#############################eureka配置#############################
eureka:
  client:
    serviceUrl:
      defaultZone:  http://127.0.0.1:8888/eureka/
    eureka-server-read-timeout-seconds: 180

编写带函数请求处理接口,其中User对象包含name、age,以及setter\getter方法,注意必须也包含User的默认构造函数。不然,Feign根据Json字符串转换User对象时会抛出异常。

@RestController
public class HelloController {

    @Autowired
    private DiscoveryClient client;

    @RequestMapping(value = "hello1",method = RequestMethod.GET)
    public String hello(@RequestParam String name){
        ServiceInstance instance=client.getLocalServiceInstance();
        return "ServiceId:"+instance.getServiceId()+
                ", port:"+instance.getPort()+",  参数为:"+name;
    }

    @RequestMapping(value = "hello2",method = RequestMethod.GET)
    public User hello(@RequestHeader String name, @RequestHeader Integer age){
        return new User(name,age);
    }

    @RequestMapping(value = "hello3",method = RequestMethod.POST)
    public String hello(@RequestBody User user){
        return "Hello "+user.getName()+", "+user.getAge();
    }
}

(三)服务消费者

创建一个Spring Boot工程,命名为cloud-web,并在pom.xml中引入必要的模块:



    4.0.0

    iwhale
    cloud-web
    0.0.1-SNAPSHOT
    jar
    cloud-web
    Demo project for Spring Boot

    
        org.springframework.boot
        spring-boot-starter-parent
        1.5.13.RELEASE
         
    

    
        UTF-8
        UTF-8
        1.8
    

    
        
        
            org.springframework.boot
            spring-boot-starter-web
            
            
        

        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        

        
            org.springframework.cloud
            spring-cloud-starter-eureka-server
        

        
            org.springframework.cloud
            spring-cloud-starter-feign
        
    

    
    
        
            
                org.springframework.cloud
                spring-cloud-dependencies
                Edgware.SR3
                pom
                import
            
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    

application.yml配置如下:

server:
  port: 9884
  context-path: /${spring.application.name}

#############################spring配置#############################
spring:
  application:
    name: cloud-web

############################################################
# spring-boot-starter-actuator,监控管理,查看端点信息,部分端点的访问需要鉴权,可将安全校验关闭,生产环境下需设为true
management:
  security:
    enabled: true
  context-path: /actuator #为了端点安全,增加前缀

endpoints:
  health:
    path: /checkHealth #修改/health端点的原始路径

#############################eureka配置#############################
eureka:
  client:
    serviceUrl:
      defaultZone:  http://127.0.0.1:8888/eureka/
    eureka-server-read-timeout-seconds: 180
  instance:
    health-check-url-path: /${endpoints.health.path} #修改/health端点的原始路径

在应用主类添加注解@EnableDiscoveryClient,@EnableFeignClients

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

在HelloServer接口中,利用注解@FeignClient指定服务提供者的服务名,绑定参数时,利用@RequestParam、@RequestHeader、@RequestBody指定参数名称,value值不能少,也不能空。

@FeignClient("cloudservice")  //指定服务名来绑定服务
public interface HelloService {

    @RequestMapping(value = "hello1",method = RequestMethod.GET)
    String hello(@RequestParam("name") String name);

    @RequestMapping(value = "hello2",method = RequestMethod.GET)
    User hello(@RequestHeader("name") String name, @RequestHeader("age") Integer age);

    @RequestMapping(value = "hello3",method = RequestMethod.POST)
    String hello(@RequestBody User user);

}

创建一个类HelloController对上面接口进行调用

@RestController
public class HelloController {

    @Autowired
    private HelloService helloService;

    @RequestMapping(value = "/helloFang",method = RequestMethod.GET)
    public String helloFang(){
        StringBuilder sb=new StringBuilder();
        sb.append(helloService.hello("hjy")).append("\n");
        sb.append(helloService.hello("hjy",33)).append("\n");
        sb.append(helloService.hello(new User("hjy",33))).append("\n");
        return sb.toString();
    }
}

测试

启动服务注册中心、两个cloudservice服务以及服务消费者cloud-web,访问http://localhost:9884/cloud-web/helloFang时我们发现会以轮询的方式访问9881和9882端口

(四)Ribbon配置

Spring Cloudn Feign的客户端负载均衡是通过Spring Cloud Ribbon实现的,在application.yml中添加对于cloudservice服务的重试策略

########重试策略########
cloudservice:
  ribbon:
    ConnectTimeout: 500  #请求连接的超时时间
    ReadTimeout: 2000  #请求处理的超时时间
    okToRetryOnAllOperations: true  #对所有操作都进行重试
    MaxAutoRetriesNextServer: 2 #切换实例的重试次数
    MaxAutoRetries: 1  #对当前实例的重试次数

将cloudservice服务的/hello1接口改造如下,然后启动9881端口为改造后的(即可能超时的),9882端口为改造前的。

@RequestMapping(value = "hello1",method = RequestMethod.GET)
    public String hello(@RequestParam String name) throws Exception{
        ServiceInstance instance=client.getLocalServiceInstance();
        //测试超时
        int sleepTime=new Random().nextInt(3000);
        Thread.sleep(sleepTime);
        System.out.println(sleepTime);
        return "ServiceId:"+instance.getServiceId()+
                ", port:"+instance.getPort()+",  参数为:"+name;
    }

测试发现当超时2s后,会再次访问9881端口一次,再超时时就会去访问9882的端口。

(五)Hystrix配置

在对Hystrix进行配置时,需要确认feign.hystrix.enables参数没有被设置为false,在application.yml配置熔断器全局超时时间(也可针对某个服务设置)

############熔断器配置##############
feign:
  hystrix:
    enable: true #开启熔断器
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 8000  #熔断器超时时间应大于ribbon的超时时间,不然不会触发重试

feign.hystrix.enables=false可以全局关闭Hystrix功能,但如果只想关闭某个服务客户端时,需要使用@Scope("prototype")注解为指定的客户端配置Feign.Builder实例。

@Configuration
public class DisableHystrixConfiguration {
    @Bean
    @Scope("prototype")
    public Feign.Builder feignBuilder(){
        return Feign.builder();
    }
}

在HelloService的@FeignClient注解中,通过configuration参数引入上面的配置

@FeignClient(value = "cloudservice" ,configuration = DisableHystrixConfiguration.class)  //指定服务名来绑定服务
public interface HelloService {
    ......
}

Hystrix提供的服务降级是服务容错的重要功能,下面我们对服务消费者进行改造,为HelloService接口实现一个服务降级类HelloServiceFallback,其中每个重写方法的实现逻辑都可以用来定义相应的服务降级逻辑。

@Component
public class HelloServiceFallback implements HelloService{

    @Override
    public String hello(String name) {
        return "error";
    }

    @Override
    public User hello(String name, Integer age) {
        return new User("未知",0);
    }

    @Override
    public String hello(User user) {
        return "error";
    }
}

通过@FeignClient注解的fallback属性来指定对应的服务降级实现类

@FeignClient(value = "cloudservice",fallback = HelloServiceFallback.class)  //指定服务名来绑定服务
public interface HelloService {
    ......
}

启动服务注册中心和服务消费者,但不启动服务提供者,测试发现服务降级没生效,百度了很多解决方案,仍不行,什么鬼!!!

你可能感兴趣的:(Web开发,Java)