热门标签 | HotTags
当前位置:  开发笔记 > 后端 > 正文

如何基于springcloud模拟RPC调用(Feign)

这篇文章主要介绍了如何基于springcloud模拟RPC调用(Feign),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

Feign简介

Feign是一个声明式的Web Service客户端,它能够让Web Service客户端的编写变得更加容易(你只需创建一个接口,并在接口上添加相应注解即可)。除了Feign自带的注解外它还支持JAX-RS注解,SpringCloud又为Feign增加了对SpringMVC注解的支持,同时为了能够使用和Spring Web中默认使用的相同的httpMessageConverter,SpringCloud集成了Ribbon和Eureka,用来在使用Feign时能够为其提供一个负载均衡的HTTP客户端。

总起来说,Feign具有如下特性:

1.可插拔的注解支持,包括Feign注解和JAX-RS注解;

2.支持可插拔的HTTP编码器和解码器;

3.支持Hystrix和它的Fallback;

4.支持Ribbon的负载均衡;

5.支持HTTP请求和响应的压缩。

接下来我们将通过对上一章《客户端负载均衡(Ribbon)》中的 message-center 项目进行改造,演示如何使用Feign。

message-center改造

引入Feign依赖

由于Feign依赖中默认包含了Ribbon,所以只需要在 pom.xml 文件中引入Feign依赖即可,Ribbon依赖无需重复引入:


    org.springframework.boot
    spring-boot-starter-parent
    2.0.6.RELEASE
  
 
  
    Finchley.SR2
  
 
  
    
      org.springframework.boot
      spring-boot-starter-web
    
    
    
      org.springframework.cloud
      spring-cloud-starter-netflix-eureka-client
    
    
    
      org.springframework.cloud
      spring-cloud-starter-openfeign
    
  
 
  
    
      
      
        org.springframework.cloud
        spring-cloud-dependencies
        ${spring-cloud.version}
        pom
        import
      
    
  

修改启动类

在MessageCenterApplication启动类上增加@EnableFeignClients注解:

import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
public class MessageCenterApplication {
  public static void main(String[] args) {
    new SpringApplicationBuilder(MessageCenterApplication.class).web(WebApplicationType.SERVLET).run(args);
  }
}

这里我们在启动类中增加了@EnableFeignClients注解,用来开启Feign客户端发现功能。

如果你的Feign客户端类文件不在Spring的包扫描路径之中,可以在@EnableFeignClients注解中对Feign客户端的包路径进行指定。

@SpringBootApplication
@EnableFeignClients(basePackages = "com.pengjunlee.client.**")
public class MessageCenterApplication {
  public static void main(String[] args) {
    new SpringApplicationBuilder(MessageCenterApplication.class).web(WebApplicationType.SERVLET).run(args);
  }
}

创建Feign客户端

对外提供服务的HTTP接口定义在MessageController

@RestController
@RequestMapping("/api/v1/msg")
public class MessageController {
 
  @Value("${server.port}")
  private String port;
 
  /**
   * 返回一条消息
   */
  @GetMapping("/get")
  public String getMsg() {
    return "This message is sent from port: " + port;
  }
}

接下来,我们在消费端message-center中为它创建一个Feign客户端。新建一个接口取名MessageServiceClient,并在上面添加@FeignClient注解,完整代码如下:

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
 
@FeignClient(name = "message-service")
public interface MessageServiceClient {
 
  @GetMapping("/api/v1/msg/get")
  public String getMsg();
}

说明:此处@FeignClient注解的name属性应与message-service应用的spring.application.name属性相同,表示为message-service服务创建一个Feign客户端。接口的映射地址路径以及接口入参都必须与MessageController中的方法完全相同。

调用Feign客户端

接下来,我们来看一看如何在消费端使用创建好的Feign客户端对message-service服务进行调用,示例代码如下:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.pengjunlee.service.MessageServiceClient;
@RestController
@RequestMapping("/api/v1/center")
public class MessageCenterController {
  @Autowired
  private MessageServiceClient messageService;
  @GetMapping("/msg/get")
  public Object getMsg() {
    String msg = messageService.getMsg();
    return msg;
  }
}

启动应用,再次请求 http://localhost:8781/api/v1/center/msg/get ,返回如下结果表明服务调用成功:

关于传参

Feign除了支持自带的注解和JAX-RS注解外,还支持 SpringMVC注解,常用的有:@RequestParam 、@PathVariable、@RequestBody 等。

例如,服务端提供如下两个服务接口:

/**
 * 获取消息详情
 */
@GetMapping("/api/v1/msg/detail/{id}")
public MessageEntity getDetail(@PathVariable(name = "id") Long id) {
  return messageService.getById(id);
}
 
/**
 * 新建一条消息
 */
@PostMapping("/api/v1/msg/save")
public MessageEntity save(@RequestBody MessageEntity message) {
  return messageService.save(message);
}

相应的,在Feign客户端中可以进行如下定义:

/**
 * 获取消息详情
 */
@GetMapping("/api/v1/msg/detail/{id}")
public MessageEntity getDetail(@PathVariable(name = "id") Long id) ;
 
/**
 * 新建一条消息
 */
@PostMapping("/api/v1/msg/save")
public MessageEntity save(@RequestBody MessageEntity message) ;

重写Feign的默认配置

在Spring Cloud对Feign的支持实现中,一个核心的概念就是客户端命名,每一个Feign客户端都是整个组件系统的一部分,它们相互协同一起工作来按照需求与远程服务器取得联系。并且它们每一个都有自己的名字,应用程序开发人员可以使用@feignclient来给它取名。Spring Cloud按照自己的需要又使用FeignClientsConfiguration为每一个已命名的客户端创建了一个ApplicationContext,额外包含了一个feign.Decoder、一个 feign.Encoder 和一个 feign.Contract。你可以通过指定@FeignClient注解的contextId 属性来设置ApplicationContext的名字。

SpringCloud还允许你在FeignClientsConfiguration的基础之上使用@FeignClient声明一些额外的配置,从而实现对Feign客户端的完全控制,如下例所示:

@FeignClient(name = "message-service", cOnfiguration= MessageConfiguration.class)
public interface MessageServiceClient {
  //..
}

在这个例子中,这个Feign客户端将由FeignClientsConfiguration 和MessageConfiguration中的组件一起组成(后者会覆盖前者的配置)。

注意:本例中,MessageConfiguration不必用@Configuration注解进行标注,如果确实要加上@Configuration注解,你需要注意把MessageConfiguration排除在@ComponentScan和@SpringBootApplication扫描的包路径之外,否则它将成为feign.Decoder、feign.Encoder 和 feign.Contract 等的默认值。

下表列出了 Spring Cloud Netflix 缺省为Feign提供的所有 Bean(Bean类型 Bean名称:Bean实现):

Decoder feignDecoder: ResponseEntityDecoder (包装了一个 SpringDecoder)
Encoder feignEncoder: SpringEncoder
Logger feignLogger: Slf4jLogger
Contract feignContract: SpringMvcContract
Feign.Builder feignBuilder: HystrixFeign.Builder
Client feignClient: 启用 Ribbon 时是 LoadBalancerFeignClient,否则使用 feign.Client.Default。

你可以使用 OkHttpClient 或者 ApacheHttpClient 的Feign客户端,只需要将 feign.okhttp.enabled 或者 feign.httpclient.enabled 设置为 true ,并将相应类添加到项目的CLASSPATH即可。你也可以使用自定义的HTTP 客户端,使用 Apache 时提供一个ClosableHttpClient 类型Bean或者使用OK HTTP时提供一个OkHttpClient 类型Bean。

默认情况下,Spring Cloud Netflix 并没有为Feign提供下列的Bean,但依然会从Spring容器中查找这些类型的Bean用来创建Feign客户端。

Logger.Level
Retryer
ErrorDecoder
Request.Options
Collection
SetterFactory

创建这些类型的一个Bean 并将它写到 @FeignClient 声明的配置中,这样你就能够对这些Bean中的每一个进行重写。例如下面的 MessageConfiguration 利用feign.Contract.Default替代了SpringMvcContract 并向RequestInterceptor 集合中添加了一个RequestInterceptor 。

@Configuration
public class MessageConfiguration {
  @Bean
  public Contract feignContract() {
    return new feign.Contract.Default();
  }
 
  @Bean
  public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
    return new BasicAuthRequestInterceptor("user", "password");
  }
}

当然,@FeignClient 也支持通过配置文件进行配置。

feign:
client:
config:
message-service:
connectTimeout: 5000
readTimeout: 5000
loggerLevel: full
errorDecoder: com.pengjunlee.SimpleErrorDecoder
retryer: com.pengjunlee.SimpleRetryer
requestInterceptors:
- com.pengjunlee.FooRequestInterceptor
- com.pengjunlee.BarRequestInterceptor
decode404: false
encoder: com.pengjunlee.SimpleEncoder
decoder: com.pengjunlee.SimpleDecoder
contract: com.pengjunlee.SimpleContract

默认配置可以通过@EnableFeignClients的defaultConfiguration属性进行指定,然后会被应用到所有的Feign客户端。如果你希望使用配置文件对所有的@FeignClient进行配置,可以使用 default 作为Feign客户端的名称。

feign:
 client:
  config:
   default:
    connectTimeout: 5000
    readTimeout: 5000
    loggerLevel: basic

如果我们同时使用了@Configuration Bean和文件配置,文件配置会优先生效。如果你希望优先使用 @Configuration Bean中的配置,可以将 feign.client.default-to-properties 设置为 false 。

如果你需要在RequestInterceptor中使用ThreadLocal 变量,你需要将Hystrix 的线程隔离策略设置为 SEMAPHORE 或者直接禁用Hystrix 。

# To disable Hystrix in Feign
feign:
 hystrix:
  enabled: false
 
# To set thread isolation to SEMAPHORE
hystrix:
 command:
  default:
   execution:
    isolation:
     strategy: SEMAPHORE 

关于超时

在启用Ribbon的情况下,Feign客户端是一个LoadBalancerFeignClient Bean,其内部有一个 execute() 方法用来发送一个HTTP请求并获取响应, 本质上其实还是使用的Ribbon做负载均衡,并使用RestTemplate发送的请求。execute() 接口声明如下:

public Response execute(Request request, Request.Options options) throws IOException;

其中,Request 用来封装HTTP请求的详细信息。

/**
 *
 * An immutable request to an http server.
 *
 */
public final class Request {
  private final String method;
  private final String url;
  private final Map> headers;
  private final byte[] body;
  private final Charset charset;
  // ...
}

Options 则封装了一些请求控制参数:

public static class Options {
 
  private final int connectTimeoutMillis;
  private final int readTimeoutMillis;
  private final boolean followRedirects;
  public Options(int connectTimeoutMillis, int readTimeoutMillis) {
    this(connectTimeoutMillis, readTimeoutMillis, true);
  }
  public Options() {
    this(10 * 1000, 60 * 1000);
  } 
  // ...
}

从Options 源码来看,Feign客户端默认的读取超时时间为60秒。若同时使用了Hystrix,由于Hystrix 默认的读取超时时间为1秒,会导致Feign客户端默认的读取超时时间设置无效,即超过1秒即为读取超时。可使用如下配置同时对Feign客户端和Hystrix 的超时配置进行重写。

feign:
 client:
  config:
   default:
    connectTimeout: 5000
    readTimeout: 5000

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • SpringMVC工作流程概述
    SpringMVC工作流程概述 ... [详细]
  • Servlet多用户登录时HttpSession会话信息覆盖问题的解决方案
    本文讨论了在Servlet多用户登录时可能出现的HttpSession会话信息覆盖问题,并提供了解决方案。通过分析JSESSIONID的作用机制和编码方式,我们可以得出每个HttpSession对象都是通过客户端发送的唯一JSESSIONID来识别的,因此无需担心会话信息被覆盖的问题。需要注意的是,本文讨论的是多个客户端级别上的多用户登录,而非同一个浏览器级别上的多用户登录。 ... [详细]
  • 本文讨论了在shiro java配置中加入Shiro listener后启动失败的问题。作者引入了一系列jar包,并在web.xml中配置了相关内容,但启动后却无法正常运行。文章提供了具体引入的jar包和web.xml的配置内容,并指出可能的错误原因。该问题可能与jar包版本不兼容、web.xml配置错误等有关。 ... [详细]
  • [翻译]微服务设计模式5. 服务发现服务端服务发现
    服务之间需要互相调用,在单体架构中,服务之间的互相调用直接通过编程语言层面的方法调用就搞定了。在传统的分布式应用的部署中,服务地 ... [详细]
  • ZooKeeper 学习
    前言相信大家对ZooKeeper应该不算陌生。但是你真的了解ZooKeeper是个什么东西吗?如果别人面试官让你给他讲讲ZooKeeper是个什么东西, ... [详细]
  • LVS-DR直接路由实现负载均衡示例
    nsitionalENhttp:www.w3.orgTRxhtml1DTDxhtml1-transitional.dtd ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • 本文介绍了禅道作为一款国产开源免费的测试管理工具的特点和功能,并提供了禅道的搭建和调试方法。禅道是一款B/S结构的项目管理工具,可以实现组织管理、后台管理、产品管理、项目管理和测试管理等功能。同时,本文还介绍了其他软件测试相关工具,如功能自动化工具和性能自动化工具,以及白盒测试工具的使用。通过本文的阅读,读者可以了解禅道的基本使用方法和优势,从而更好地进行测试管理工作。 ... [详细]
  • PHP组合工具以及开发所需的工具
    本文介绍了PHP开发中常用的组合工具和开发所需的工具。对于数据分析软件,包括Excel、hihidata、SPSS、SAS、MARLAB、Eview以及各种BI与报表工具等。同时还介绍了PHP开发所需的PHP MySQL Apache集成环境,包括推荐的AppServ等版本。 ... [详细]
  • 本文总结了初学者在使用dubbo设计架构过程中遇到的问题,并提供了相应的解决方法。问题包括传输字节流限制、分布式事务、序列化、多点部署、zk端口冲突、服务失败请求3次机制以及启动时检查。通过解决这些问题,初学者能够更好地理解和应用dubbo设计架构。 ... [详细]
  • 在单位的一台4cpu的服务器上部署了esxserver,挂载了6个虚拟机,目前运行正常。在安装部署过程中,得到了cnvz.net论坛精华区 ... [详细]
  • k8s+springboot+Eureka如何平滑上下线服务
    k8s+springboot+Eureka如何平滑上下线服务目录服务平滑上下线-k8s版本目录“上篇介绍了springboot+Euraka服务平滑上下线的方式,有部分小伙伴反馈k ... [详细]
  • 抖音服务器带宽有多大,才能供上亿人同时刷?
    最近看到一个有意思的提问:抖音服务器带宽有多大,为什么能够供那么多人同时刷?今天来给大家科普一下。 ... [详细]
  • pm2常用的命令用法介绍pm2是一个带有负载均衡功能的Node应用的进程管理器.当你要把你的独立代码利用全部的服务器上的所有CPU,并保证进程永远都活着,0秒的重载, ... [详细]
  • 域名解析系统DNS
    文章目录前言一、域名系统概述二、因特网的域名结构三、域名服务器1.根域名服务器2.顶级域名服务器(TLD,top-leveldomain)3.权威(Authoritative)域名 ... [详细]
author-avatar
zhuyiming69751
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有