Spring Cloud框架学习-Spring Cloud OpenFeign

导读:本篇文章讲解 Spring Cloud框架学习-Spring Cloud OpenFeign,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

1.基本使用

Spring Cloud OpenFeign 是声明式的服务调用组件,它整合了 Spring Cloud Ribbon 和Spring Cloud Hystrix,拥有负载均衡和服务容错的功能,并且提供了一种声明式的Web服务客户端定义方式。一开始这个组件不叫这个名字,一开始就叫 Feign,但是 Netflix 中的组件现在已经停止开源工作,OpenFeign 是 Spring Cloud 团队在 Netflix Feign 的基础上开发出来的声明式服务调用组件。

在Spring Cloud OpenFeign 的实现下,我们只需要创建一个接口并用注解的方式来配置它,即可完成对服务提供者的接口绑定,简化了使用Spring Cloud Ribbon时需要自行封装服务调用客户端的开发量。下面简单示例如何使用Spring Cloud OpenFeign 。

1. 创建注册中心

新建Spring Boot项目,添加如下依赖:

 <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

在 application.properties 中添加配置

#当前服务的名字
spring.application.name=eureka
#端口号(Eureka后台管理端端口)
server.port=1111
#默认情况下,Eureka Server也是一个普通的微服务,所以当它还是注册中心时,就有两层身份:
#1.注册中心;2.普通服务。默认当前eureka server自动把自己注册到注册中心中,
# 通过eureka.client.register-with-eureka=false设置不注册
eureka.client.register-with-eureka=false
#表示是否从Eureka Server上获取注册信息
eureka.client.fetch-registry=false

启动类上添加注解

@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {

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

}

2. 创建服务提供者

新建Spring Boot项目,添加如下依赖:

 <dependencies>
        <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-client</artifactId>
        </dependency>
    </dependencies>

在 application.properties 中添加配置

#服务名
spring.application.name=provider
#服务端口号
server.port=1113
#注册中心url地址
eureka.client.service-url.defaultZone=http://localhost:1111/eureka

创建Controller类,提供/hello接口给其他服务调用

@RestController
public class HelloController {
  @Value("${server.port}")
  Integer port;

  @GetMapping("/hello")
  public String hello(){
    return "hello world port:"+port;
  }
 }

3. 创建服务调用者

新建Spring Boot项目,添加如下依赖:

	<dependencies>
		<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-client</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-openfeign</artifactId>
		</dependency>
  </dependencies>

在启动类上添加==注解@EnableFeignClients ==来启用 Feign 的客户端功能

@SpringBootApplication
@EnableFeignClients
public class OpenfeignApplication {

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

在 application.properties 中添加配置,使项目注册到 Eureka 上:

spring.application.name=openfeign
server.port=4000
eureka.client.service-url.defaultZone=http://localhost:1111/eureka

定义接口,使用@FeignClient注解,指定要调用的服务为provider

@FeignClient(value = "provider")
public interface HelloService {
    /**
     * 请求provider的“/hello接口”
     * @return
     */
    @GetMapping("/hello")
    public String hello();
}

创建controller,使用上面的open feign接口

@RestController
public class HelloController {
    @Autowired
    HelloService helloService;

    @GetMapping("/hello")
    public String hello(){
        return helloService.hello();
    }
}

启动Open Feign项目,浏览器访问/hello接口
在这里插入图片描述

2. 参数传递

服务提供者provider提供以下接口:

@RestController
public class HelloController {
  @Value("${server.port}")
  Integer port;

  @GetMapping("/hello")
  public String hello(){
    return "hello world port:"+port;
  }

  @GetMapping("/test_get")
  public String hell02(String name){
    System.out.println(new Date()+"-"+name);
    return "Hello "+name;
  }
  /**
   * 测试post,接收key/value形式的参数
   */
  @PostMapping("/user1")
  public User addUser(User user){
    return user;
  }
  /**
   * 测试post,接收json形式的参数
   */
  @PostMapping("/user2")
  public User addUser2(@RequestBody User user){
    return user;
  }

  /**
   * 测试put,接收key/value形式的参数
   * @param user
   */
  @PutMapping("/user1")
  public void updateUser(User user){
    System.out.println(user);
  }

  /**
   * 测试put,接收json格式的参数
   * @param user
   */
  @PutMapping("/user2")
  public void updateUser2(@RequestBody User user){
    System.out.println(user);
  }

  /**
   * 测试delete,接收key/value
   * @param id
   */
  @DeleteMapping("/user1")
  public void deleteUser1(Integer id){
    System.out.println(id);
  }

  /**
   * 测试delete,在路径上直接写参数
   * @param id
   */
  @DeleteMapping("/user2/{id}")
  public void deleteUser2(@PathVariable Integer id){
    System.out.println(id);
  }

  @GetMapping("/user3")
  public void getUserByName(@RequestHeader("name") String name) throws UnsupportedEncodingException {
    System.out.println("get header property of  name:"+ URLDecoder.decode(name,"UTF-8"));
  }
}

使用OpenFeign,方法参数一定要绑定参数名(凡是 key/value 形式的参数,一定要标记参数的名称。),如果通过header来传递参数,中文需要转码。不同参数类型传递示例:

@FeignClient(value = "provider")
public interface HelloService {
    /**
     * 请求provider的“/hello接口”
     * @return
     */
    @GetMapping("/hello")
    public String hello();

    /**
     * 请求带key/value的方法
     * @param name
     * @return
     */
    @GetMapping("/test_get")
    public String hello2(@RequestParam("name") String name);

    /**
     * 请求“/user2”接口,参数为json格式的
     * @param user
     * @return
     */
    @PostMapping("/user2")
    public User insertUser(@RequestBody User user);

    /**
     * 测试“/user2/{id}”
     * 注意@PathVariable("id")
     * @param id
     */
    @DeleteMapping("/user2/{id}")
    public void deleteUserById(@PathVariable("id") Integer id);

    @PutMapping("/user1")
    public void updateUser(@RequestParam("id") Integer id,@RequestParam("username") String username,@RequestParam("password") String password);


    /**
     * @RequestHeader的用法
     * 测试注解在Header中传递参数
     * @param name
     */
    @GetMapping("/user3")
    public void getUserByName(@RequestHeader("name") String name);

}

在HelloController中进行调用测试

@RestController
public class HelloController {
    @Autowired
    HelloService helloService;

    @GetMapping("/hello")
    public String hello(){
        return helloService.hello();
    }

    /**
     * 测试各类请求的远程调用
     */
    @GetMapping("/test_param")
    public void testParam() throws UnsupportedEncodingException {
        User user = new User();
        user.setId(1);
        user.setUsername("zhang2");
        user.setPassword("321");
        User user1 = helloService.insertUser(user);
        helloService.deleteUserById(2);
        helloService.updateUser(2,"Ethan","321");
        helloService.getUserByName(URLEncoder.encode("不会吧","UTF-8"));
        System.out.println(user1);
    }
}

浏览器请求/test_param查看调用结果:
在这里插入图片描述

3. 日志配置

OpenFeign 中,我们可以通过配置日志,来查看整个请求的调用过程。日志级别一共分为四种:

  1. NONE:不开启日志,默认就是这个级别
  2. BASIC:记录请求方法、URL、响应状态码、执行时间
  3. HEADERS:在 BASIC 的基础上,增加记录请求/响应的头信息
  4. FULL:在 HEADERS 基础上,再增加请求和响应的body正文及元数据等。

OpenFeign日志的四种级别,可以通过Bean来配置:

@SpringBootApplication
@EnableFeignClients
public class OpenfeignApplication {

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

	@Bean
	Logger.Level loggerLevel(){
		return Logger.Level.FULL;
	}

}

最后,需要在applicaiton.properties中开启日志级别:

logging.level.top.javahai.openfeign=debug

其中top.javahai.openfeign是包名,重启OpenFeign测试是否打印。
在这里插入图片描述

4. 数据压缩配置

OpenFeign请求的数据压缩配置

# 开启请求的数据压缩,开启GZIP配置
feign.compression.request.enabled=true
# 开启响应的数据压缩,开启GZIP配置
feign.compression.response.enabled=true
# 压缩的数据类型,默认值为text/html,application/xml,application/json
feign.compression.request.mime-types=text/html,application/xml,application/json
# 压缩的数据下限,2048 表示当要传输的数据大于 2048 时,才会进行数据压缩,默认值为2048
feign.compression.request.min-request-size=2048

5. 服务降级配置

在Open Feign中可以使用Hystrix实现服务容错和降级的功能。首先在application.properties中配置开启Hystrix

#开启Hystric
feign.hystrix.enabled=true

5.1 配置fallback属性

首先定义服务降级的方法,实现HelloService。

@Component
@RequestMapping("/error")
public class HelloServiceFallback implements HelloService {
    @Override
    public String hello() {
        return "error";
    }

    @Override
    public String hello2(String name) {
        return "error2";
    }

    @Override
    public User insertUser(User user) {
        return new User();
    }

    @Override
    public void deleteUserById(Integer id) {
        System.out.println("error");
    }

    @Override
    public void updateUser(Integer id, String username, String password) {
        System.out.println("error");
    }

    @Override
    public void getUserByName(String name) {
        System.out.println("error");
    }
}

在HelloService中配置服务降级类

@FeignClient(value = "provider",fallback = HelloServiceFallback.class)
public interface HelloService {
}

5.2 配置FallbackFactory属性

或者可以通过配置FallbackFactory来实现服务降级

@Component
public class HelloServiceFallbackFactory implements FallbackFactory<HelloService> {
    @Override
    public HelloService create(Throwable throwable) {
        return new HelloService() {
            @Override
            public String hello() {
                return "error";
            }

            @Override
            public String hello2(String name) {
                return "error2";
            }

            @Override
            public User insertUser(User user) {
                return null;
            }

            @Override
            public void deleteUserById(Integer id) {

            }

            @Override
            public void updateUser(Integer id, String username, String password) {

            }

            @Override
            public void getUserByName(String name) {

            }
        };
    }
}

然后HelloService 中进行配置:

@FeignClient(value = "provider",fallbackFactory = HelloServiceFallbackFactory.class)
public interface HelloService {
}

5.3 Hystrix全局配置

常用全局配置如下:

hystrix:
  command:#用于控制HystrixCommand的行为
    default:
      execution:
        isolation:
          strategy:THREAD#控制HystrixCommand的隔离策略,THREAD->线程池隔离策略(默认),SEMAPHORE->信号量隔离策略
          thread:
            timeoutInMilliseconds:1000#配置HystrixCommand执行的超时时间,执行超过该时间会进行服务降级处理
            interruptOnTimeout:true#配置HystrixCommand执行超时的时候是否要中断
            interruptOnCancel:true#配置HystrixCommand执行被取消的时候是否要中断
          timeout:
            enabled:true#配置HystrixCommand的执行是否启用超时时间
          semaphore:
            maxConcurrentRequests:10#当使用信号量隔离策略时,用来控制并发量的大小,超过该并发量的请求会被拒绝
      fallback:
        enabled:true#用于控制是否启用服务降级
      circuitBreaker:#用于控制HystrixCircuitBreaker的行为
        enabled:true#用于控制断路器是否跟踪健康状况以及熔断请求
        requestVolumeThreshold:20#超过该请求数的请求会被拒绝
        forceOpen:false#强制打开断路器,拒绝所有请求
        forceClosed:false#强制关闭断路器,接收所有请求
      requestCache:
        enabled:true#用于控制是否开启请求缓存
  collapser:#用于控制HystrixCollapser的执行行为
    default:
      maxRequestsInBatch:100#控制一次合并请求合并的最大请求数
      timerDelayinMilliseconds:10#控制多少毫秒内的请求会被合并成一个
      requestCache:
        enabled:true#控制合并请求是否开启缓存
  threadpool:#用于控制HystrixCommand执行所在线程池的行为
    default:
      coreSize:10#线程池的核心线程数
      maximumSize:10#线程池的最大线程数,超过该线程数的请求会被拒绝
      maxQueueSize:-1#用于设置线程池的最大队列大小,-1采用SynchronousQueue,其他正数采用LinkedBlockingQueue
      queueSizeRejectionThreshold:5#用于设置线程池队列的拒绝阀值,由于LinkedBlockingQueue不能动态改版大小,使用时需要用该参数来控制线程数

实例配置只需要将全局配置中的default换成与之对应的key即可。

hystrix:
  command:
    HystrixComandKey:#将default换成HystrixComrnandKey
      execution:
        isolation:
          strategy:THREAD
  collapser:
    HystrixCollapserKey:#将default换成HystrixCollapserKey
      maxRequestsInBatch:100
  threadpool:
    HystrixThreadPoolKey:#将default换成HystrixThreadPoolKey
      coreSize:10

配置文件中相关key的说明

  • HystrixComandKey对应@HystrixCommand中的commandKey属性;
  • HystrixCollapserKey对应@HystrixCollapser注解中的collapserKey属性;
  • HystrixThreadPoolKey对应@HystrixCommand中的threadPoolKey属性。

6. Ribbon配置

6.1 全局配置

Open Feign 整合了 Ribbon 实现负载均衡,在Open Feign 中可以直接使用 Ribbon 的配置。

#服务请求连接超时时间(毫秒)
ribbon.ConnectTimeout=1000
#服务请求处理超时时间(毫秒)
ribbon.ReadTimeout=3000
#对超时请求启用重试机制
ribbon.OkToRetryOnAllOperations=true
#切换重试实例的最大个数
ribbon.MaxAutoRetriesNextServer=1
# 切换实例后重试最大次数
ribbon.MaxAutoRetries=1
#修改负载均衡策略
ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule

在重试策略的配置中,如果MaxAutoRetries设置为1的话,Feign客户端会先尝试访问首选实例一次,失败后才更换实例访问,而更换实例访问的次数由MaxAutoRetriesNextServer参数决定,如果MaxAutoRetriesNextServer设置为2,则会尝试更换两次实例进行重试。

需要注意的是,Ribbon的超时和Hystrix的超时是两个概念。我们需要设置Hystrix的超时时间大于Ribbon的超时时间,否则Hystrix命令超时时,该命令直接熔断,则重试机制的配置就失去意义了。

6.2 负载均衡策略配置

最后一点负载均衡策略配置的选择,Ribbon提供了如下几种负载均衡策略:

  • com.netflix.loadbalancer.RandomRule:从提供服务的实例中以随机的方式;
  • com.netflix.loadbalancer.RoundRobinRule:以线性轮询的方式,就是维护一个计数器,从提供服务的实例中按顺序选取,第一次选第一个,第二次选第二个,以此类推,到最后一个以后再从头来过;
  • com.netflix.loadbalancer.RetryRule:在RoundRobinRule的基础上添加重试机制,即在指定的重试时间内,反复使用线性轮询策略来选择可用实例;
  • com.netflix.loadbalancer.WeightedResponseTimeRule:对RoundRobinRule的扩展,响应速度越快的实例选择权重越大,越容易被选择;
  • com.netflix.loadbalancer.BestAvailableRule:选择并发较小的实例;
  • com.netflix.loadbalancer.AvailabilityFilteringRule:先过滤掉故障实例,再选择并发较小的实例;
  • com.netflix.loadbalancer.ZoneAwareLoadBalancer:采用双重过滤,同时过滤不是同一区域的实例和故障实例,选择并发较小的实例。

6.3 指定服务配置

我们可以采用< client >.ribbon.key=value的格式进行设置各个服务的配置,其中client就是在FeignClient的name属性的名称,例如指定对provider服务提供的接口单独配置:

#服务请求连接超时时间(毫秒)
provider.ribbon.ConnectTimeout=1000

在定义Feign客户端的时候,我们使用了@FeignClient注解,在初始化过程中Spring Cloud Feign会根据该注解的name属性或value属性指定的服务名,自动创建一个同名的Ribbon客户端,所以我们就可以使用@FeignClient注解的name或value属性值来设置对应的Ribbon参数了。

参考:
1.《Spring Cloud微服务实战》
2.Spring Cloud OpenFeign:基于 Ribbon 和 Hystrix 的声明式服务调用

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由半码博客整理,本文链接:https://www.bmabk.com/index.php/post/44269.html

(0)

相关推荐

半码博客——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!