首页 > 其他分享 >四、SpringCloud alibaba 之 OpenFeign

四、SpringCloud alibaba 之 OpenFeign

时间:2024-02-04 20:24:50浏览次数:19  
标签:feign 调用 name OpenFeign SpringCloud Feign alibaba import public

4.1、调用过程的演化

JAVA 项目中如何实现接口调用?

远程调用工具 描述
HttpClient HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 Http 协议的客户端编程工具包,并且它支持 HTTP 协议最新版本和建议。HttpClient 相比传统 JDK 自带的URLConnection,提升了易用性和灵活性,使客户端发送 HTTP 请求变得容易,提高了开发的效率。
Okhttp 一个处理网络请求的开源项目,是安卓端最火的轻量级框架,由 Square 公司贡献,用于替代HttpUrlConnection 和 Apache HttpClient。OkHttp 拥有简洁的 API、高效的性能,并支持多种协议(HTTP/2 和 SPDY)。
HttpURLConnection HttpURLConnection 是 Java 的标准类,它继承自 URLConnection,可用于向指定网站发送
RestTemplate RestTemplate 是 Spring 提供的用于访问 Rest 服务的客户端,RestTemplate 提供了多种便捷访问远程 HTTP 服务的方法,能够大大提高客户端的编写效率。

上面介绍的是最常见的几种调用接口的方法,我们下面要介绍的方法比上面的更简单、方便,它就是 Feign

4.1.1、点对点直接调用

在最开始,不同服务间调用采用的是http://ip:port/{请求路径}的点对点方式调用

4.1.2、本地负载均衡调用方式

如果被调用的服务可能存在多个实例,此时采用点对点直接调用就不合适,此时引入了nacos注册中心,每个服务实例都注册到nacos里面,此时采取客户端负载均衡的调用方式。

@RequestMapping("/test")
    public String order(){
        // 根据服务Id获取注册到nacos中的服务实例
        List<ServiceInstance> instances = discoveryClient.getInstances("stock-service");
        int index = new Random().nextInt(instances.size());
        ServiceInstance serviceInstance = instances.get(index);
        String host = serviceInstance.getHost();
        int port = serviceInstance.getPort();
        // 拼接调用地址
        String url="http://"+host+":"+port+"/stock/getStock";
        // 远程调用
        String result = restTemplate.getForObject(url, String.class);
        return result;

    }

自己写的调用负载均衡比较简单,此时可以引入Ribbon,可以采取http://{服务名称}/{请求路径}的调用方式。

注意:服务名调用方式需要RestTemplate被代理,也就是在注册RestTemplate时需要加上@LoadBalanced注解

4.1.3、OpenFeign介绍

Feign是Netflix开发的声明式、模板化的HTTP客户端,其灵感来自Retrofit、JAXRS-2.0以及WebSocket。Feign可帮助我们更加便捷、优雅地调用HTTP API。Feign支持多种注解,例如Feign自带的注解或者JAX-RS注解等。

Spring Cloud openfeign对Feign进行了增强,使其支持Spr ing MVC注解,另外还整合了Ribbon和Nacos,从而使得Feign的使用更加方便。

Feign可以做到使用 HTTP 请求远程服务时就像调用本地方法一样的体验,开发者完全感知不到这是远程方法,更感知不到这是个 HTTP 请求。它像 Dubbo 一样,consumer 直接调用接口方法调用 provider,而不需要通过常规的 Http Client 构造请求再解析返回数据。它解决了让开发者调用远程接口就跟调用本地方法一样,无需关注与远程的交互细节,更无需关注分布式环境开发。

4.2、OpenFeign快速使用

4.2.1、引入OpenFeign依赖

OpenFeign依赖于springcloud,在引入openfeign之前,检查一下是否已经引入了SpringCloud的依赖。

<!--openfeign的依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

 

4.2.2、编写OpenFeign接口

在消费者端使用OpenFeign,需要一个@FeignClient注解及参数来定义一个接口。

FeignClient注解被@Target(ElementType.TYPE)修饰,表示FeignClient注解的作用目标在接口上。

@FeignClient标签的常用属性如下:

属性名 作用
name 指定FeignClient的名称。name有两个作用:1、作为Bean的名称注册到spring容器中。2、如果项目使用了Ribbon,name属性会作为微服务的名称,用于服务发现。如果使用了url属性,此时name的第二个作用就失效了。
url url一般用于调试,可以手动指定@FeignClient调用的地址
decode404 当发生http 404错误时,如果该字段位true,会调用decoder进行解码,否则抛出FeignException
configuration Feign配置类,可以自定义Feign的Encoder、Decoder、LogLevel、Contract
fallback 定义容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑,fallback指定的类必须实现@FeignClient标记的接口
fallbackFactory 工厂类,用于生成fallback类示例,通过这个属性我们可以实现每个接口通用的容错逻辑,减少重复的代码
path 定义当前FeignClient的统一前缀

 

一般 建议在消费者项目中创建一个feign的包,用于存放当前项目中调用别的服务的Feign接口

在Feign接口中,声明当前项目要调用的服务接口。接口的命名一般建议{要调用的服务名}FeignService

package com.java.coder.feign.feign;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
/**
 * name: 指定FeignClient的名称,如果项目使用了Ribbon,name属性会作为微服务的名称,用于服务发现。如果使用了url属性,此时name属性就失效了。
 * path: 定义当前FeignClient的统一前缀, 一般是生产者类上@RequestMapping的value值
 */
@FeignClient(name = "stock-service",path = "/stock")
public interface StockFeignService {
     /**
     * openfeign中对注解的使用更加严格,PathVariable注解中的id必须写
     * @param id
     * @return
     */
   	@RequestMapping("/getStock/{id}")
    String addOrder(@PathVariable("id") Integer id);
    /**
     * 入参和出参都是json的格式
     * @param dto
     * @return
     */
    @RequestMapping("/qryStockById")
    @ResponseBody
    StockRespDto qryStockById(@RequestBody StockReqDto dto);
}

 

注意在Feign接口中声明的方法,必须与对应的服务提供方提供的方法声明一样。且在OpenFeign中注解的使用更加严格,比如:@PathVariable,在普通的springboot应用中,如果参数的名字与路径中的名字一样,那么@PathVariable中的name可以不用写,但是在OpenFeign中必须写

4.2.3、启动类添加注解

package com.java.coder.feign;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
// 启动Feign接口客户端
@EnableFeignClients
public class FeignApplication {

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

4.2.4、Feign调用项目内服务

package com.java.coder.feign.controller;

import com.java.coder.feign.feign.StockFeignService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/feign")
@RestController
public class FeignController {

    // 引入之前声明的Feign接口
    @Autowired
    private StockFeignService stockFeignService;

    @RequestMapping("/add")
    public String addOrder(){
        // 直接通过Feign接口调用服务提供方
         String forObject = stockFeignService.addOrder(1100);
        return forObject;
    }
    
    @RequestMapping("/qryStockById")
    public String qryStockById(){
        StockReqDto stockReqDto=new StockReqDto();
        stockReqDto.setStockId(10000000);
        // 通过OpenFeign调用复杂参数接口
        StockRespDto respDto = stockFeignService.qryStockById(stockReqDto);
        log.info("respDto:{}",respDto);
        return "调用成功";
    }
}

4.2.5、Feign调用第三方服务

下面的代码用于调用github上的接口,此时调用的服务并非自己项目里面的服务,而是第三方的服务。

@FeignClient(name="github-client",url = "https://api.github.com")
public interface ThirdFeignService {
    @RequestMapping(value = "/search/repositories", method = RequestMethod.GET)
    String searchRepo(@RequestParam("q") String queryStr);
}

 

4.3、OpenFeign扩展配置

Feign 提供了很多的扩展机制,让用户可以更加灵活的使用。包括日志、超时时间、拦截器等。

4.3.1、日志配置

有时候我们遇到 Bug,比如接口调用失败、参数没收到等问题,或者想看看调用性能,就需要配置 Feign 的日志了,以此让 Feign 把请求信息输出来。

OpenFeign的日志等级有 4 种,分别是:

日志级别 备注
NONE 性能最佳,适用于生产,不记录任何日志(默认值)。
BASIC 适用于生产环境追踪问题,仅记录请求方法、URL、响应状态代码以及执行时间。
HEADERS 记录BASIC级别的基础上,记录请求和响应的header。
FULL 比较适用于开发及测试环境定位问题,记录请求和响应的header、body和元数据。

 

(1)全局日志配置

全局日志配置就是此配置对当前项目中所有的远程微服务调用都生效,只需要在@CompontScan注解可以扫描到的地方设置一个配置类,且配置类被@Configuration注解标注。

package com.java.coder.feign.config;

import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 全局配置: 当使用@Configuration 会将配置作用所有的服务提供方
* 局部配置: 如果只想针对某一个服务进行配置,不要加@Configuration
*/
@Configuration
public class OpenFeignConfig {

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

此处的Logger是feign.Logger

(2)局部配置

局部日志配置有两种形式:

1、配置类上不加@Configuration注解,在@FeignClient注解上指定配置类

@FeignClient(name = "stock-service",path = "/stock", configuration= OpenFeignConfig.class)
public interface StockFeignService {
    @RequestMapping("/getStock")
    public String addOrder();
}

 

此时OpenFeignConfig配置类中的相关配置,只有在调用StockFeignService中声明的服务时才生效。

@FeignClient(name = "stock-service",path = "/stock", configuration= OpenFeignConfig.class)

2、在配置文件中指定配置,且对应的属性配置类是FeignClientProperties.FeignClientConfiguration

org.springframework.cloud.openfeign.FeignClientProperties.FeignClientConfiguration.FeignClientConfiguration

feign:
  client:
    config:
      stock-service: #服务的名称
        loggerLevel: FULL

在yml配置文件中执行 Client 的日志级别才能正常输出日志,格式是"logging.level.feign接口包路径=debug

logging:
  level:
	com.tuling.mall.feigndemo.feign: debug

如果采用properties文件的话,配置的内容是

logging.level.com.java.coder.order.feign=debug

 日志配置完后的运行结果如下

4.3.2、契约配置

Spring Cloud 在 Feign 的基础上做了扩展,使用 Spring MVC 的注解来完成Feign的功能。原生的 Feign 是不支持 Spring MVC 注解的,如果你想在 Spring Cloud 中使用原生的注解方式来定义客户端也是可以的,通过配置契约来改变这个配置,Spring Cloud 中默认的是 SpringMvcContract。

Spring Cloud 1 早期版本就是用的原生Fegin. 随着netflix的停更替换成了Open feign

1)修改契约配置,支持Feign原生的注解

 package com.java.coder.feign.config;
import feign.Contract;
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class OpenFeignConfig {

    /**
     * 配置openfeign的日志级别
     * @return
     */
    @Bean
    public Logger.Level feignLoggerLevel(){
        return Logger.Level.FULL;
    }

    /**
     * 修改契约配置,支持feign的原生注解
     * @return
     */
    @Bean
    public Contract feignContract(){
        return new Contract.Default();
    }
}

注意:修改契约配置后,OrderFeignService 不再支持springmvc的注解,需要使用Feign原生的注解也可以通过配置文件配置。

 

4.3.3、自定义拦截器

定义拦截器

首先在消费者端定义一个OpenFeign的拦截器,在拦截器中加入特定的请求头,可以加上微服务的名称。

package com.java.coder.feign.interceptor;

import feign.RequestInterceptor;
import feign.RequestTemplate;

public class FeignInterceptor implements RequestInterceptor {

    @Override
    public void apply(RequestTemplate requestTemplate) {
        requestTemplate.header("service-name", "order-service");
        // 可以设置一些token值
        requestTemplate.header("service-code", "001");
    }
}

让拦截器生效

1、通过配置类配置

package com.java.coder.feign.config;

import com.java.coder.feign.interceptor.FeignInterceptor;
import feign.Contract;
import feign.Logger;
import feign.Request;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class OpenFeignConfig {

    /**
     * 配置openfeign的日志级别
     * @return
     */
    @Bean
    public Logger.Level feignLoggerLevel(){
        return Logger.Level.FULL;
    }

    /**
     * 修改契约配置,支持feign的原生注解
     * @return
     */
//    @Bean
//    public Contract feignContract(){
//        return new Contract.Default();
//    }

    /**
     * 超时时间配置
     * @return
     */
//    @Bean
//    public Request.Options options() {
//        return new Request.Options(5000,10000);
//    }

    /**
     * 拦截器配置
     * @return
     */
    @Bean
    public FeignInterceptor customFeignInterceptor(){
        return new FeignInterceptor();
    }


}

2、通过配置文件配置

feign:
  client:
    config:
      stock-service: #服务的名称
        loggerLevel: FULL
        requestInterceptors[0]: com.java.coder.feign.interceptor.FeignInterceptor

在服务提供方验证

1、定义一个普通的拦截器

package com.java.coder.stock.interceptor;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Slf4j
public class LogInterceptor implements HandlerInterceptor {
    private String TRACE_ID = "TRACE_ID";

    private String SERVICE_NAME = "service-name";
    private String SERVICE_CODE = "service-code";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {

        log.info("service name:{}", request.getHeader(SERVICE_NAME));
        log.info("service code:{}", request.getHeader(SERVICE_CODE));
        return true;

    }

}

 

2、把拦截器加入到拦截器链

package com.java.coder.stock.config;

import com.java.coder.stock.interceptor.LogInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {
    /**
     * 配置拦截器
     *
     * @param registry 相当于拦截器的注册中心
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
       //下面这句代码相当于添加一个拦截器   添加的拦截器就是我们刚刚创建的
        registry.addInterceptor(new LogInterceptor())
        //配置我们要拦截哪些路径 addPathPatterns("/**")表示拦截所有请求,包括我们的静态资源
                .addPathPatterns("/**")
//       excludePathPatterns()表示我们要放行哪些(表示不用经过拦截器)
//       excludePathPatterns("/","/login")表示放行“/”与“/login”请求
//       如果有静态资源的时候可以在这个地方放行
                .excludePathPatterns("/", "/login");
    }

}

容错配置

1、开启容错配置

feign:
	hystrix:
		enabled: true

 

2、定义容错类

@Component
public class StockFeignServiceImpl implements StockFeignService {

    @Override
    public String getStock() {
        System.out.println("调用失败");
        return "调用失败";
    }
}

 

3、在Feign接口上配置容错类

/**
 * name: 指定FeignClient的名称,如果项目使用了Ribbon,name属性会作为微服务的名称,用于服务发现。如果使用了url属性,此时name属性就失效了。
 * path: 定义当前FeignClient的统一前缀, 一般是生产者类上@RequestMapping的value值
 */
@FeignClient(name = "stock-service",path = "/stock",fallback = StockFeignServiceImpl.class)
public interface StockFeignService {
    /**
     * openfeign中对注解的使用更加严格,PathVariable注解中的id必须写
     * @return
     */
    @RequestMapping("/getStock")
    String getStock();


}

或者采取fallbackFactory配置也可以

 

带请求头

1、在@RequestMapping中的headers属性中添加

@FeignClient(name="custorm",fallback=Hysitx.class)
public interface IRemoteCallService {
	@RequestMapping(value="/custorm/getTest",
                    method = RequestMethod.POST,
                    headers = {"Content-Type=application/json;charset=UTF-8"})
    List<String> test(@RequestParam("names") String[] names);
}

 

2、在方法参数中添加


@FeignClient(name="custorm",fallback=Hysitx.class)
public interface IRemoteCallService {
    
	@RequestMapping(value="/custorm/getTest",method = RequestMethod.POST,
		headers = {"Content-Type=application/json;charset=UTF-8"})
    List<String> test(@RequestParam("names") String[] names, 
                      @RequestHeader MultiValueMap<String, String> headers);

3、使用@Header注解

@FeignClient(name="custorm",fallback=Hysitx.class)
public interface IRemoteCallService {
    
	@RequestMapping(value="/custorm/getTest",method = RequestMethod.POST)
	@Headers({"Content-Type: application/json;charset=UTF-8"})
    List<String> test(@RequestParam("names") String[] names);
}

 

4、实现RequestInterceptor接口

@Configuration
public class FeignRequestInterceptor implements RequestInterceptor {
 
    @Override
    public void apply(RequestTemplate temp) {
        temp.header(HttpHeaders.AUTHORIZATION, "XXXXX");
    }
 
}

 

 

 

标签:feign,调用,name,OpenFeign,SpringCloud,Feign,alibaba,import,public
From: https://www.cnblogs.com/cplinux/p/18006920

相关文章

  • 面试官:SpringCloudGateway过滤器类型有哪些?
    在SpringCloudGateway中,过滤器是在请求到达目标服务之前或之后,执行某些特定操作的一种机制。例如,它可以实现对传入的请求进行验证、修改、日志记录、身份验证、流量控制等各种功能。在SpringCloudGateway中,过滤器总共分为以下两大类:局部过滤器:只作用于某一个路由(route)。全......
  • 阿里巴巴 Alibaba
    这一道题目与费用提前计算的那类题目很像,但是由于每个宝藏每秒钟不会损失价值,所以不是费用提前计算,但是还是可以按照类似的状态进行推导这个区间DP滚动数组好好理解一下,其实就是我们只需要用到前一阶段的量,由于阶段是长度,所以实际处理中一般按照如下处理然后这道题还告诉我们......
  • 面试官:SpringCloudGateway过滤器类型有哪些?
    在SpringCloudGateway中,过滤器是在请求到达目标服务之前或之后,执行某些特定操作的一种机制。例如,它可以实现对传入的请求进行验证、修改、日志记录、身份验证、流量控制等各种功能。在SpringCloudGateway中,过滤器总共分为以下两大类:局部过滤器:只作用于某一个路由(route......
  • SpringBoot、SpringCloud相关技术
    一、三层架构 1.1统一业务处理1.2解决三层架构中的结偶,使用了Ioc(控制反转)和DI(依赖注入) 1.3切面编程、事务处理、全局异常处理、Cookie、Session、JWT令牌、阿里云OSS、Mybatis处理数据库二、技术分类说明  2.1SSM三、Web总结四、相关第三方插件https://www......
  • 详解SpringCloud之远程方法调用神器Fegin
    第1章:引言咱们作为Java程序员,在微服务领域里,SpringCloud可谓是个耳熟能详的大名。它提供了一套完整的微服务解决方案,其中就包括了服务间的通信。在这个微服务中,有一个成员特别引人注意,它就是Feign。那Feign到底是什么呢?简单来说,Feign是一个声明式的Web服务客户端,它让编写Web服......
  • Springcloud学习笔记61---Spring MVC的拦截器HandlerInterceptor
    1. HandlerMethod介绍HandlerMethod它作为SpringMVC的非公开API,可能绝大多数小伙伴都对它比较陌生,但我相信你对它又不是那么的生疏,因为你可能没用过但肯定见过。比如SpringMVC的拦截器HandlerInterceptor的拦截方法的第三个入参Objecthandler,虽然它是Object类型,但其实绝大部......
  • 三、SpringCloud Alibaba之Ribbon
    3.1、负载均衡介绍单台服务器不管好好坏总是会遇到性能瓶颈,而解决单台服务器瓶颈方式就是采用集群。高性能集群的本质很简单,通过增加更多的服务器来提升系统整体的计算能力。在一个集群中,每一台服务器提供的功能是一样,但是每一台服务器的类型可能是不一样的(性能、配置、价格等等),......
  • 解决 fatal: unable to access 'https://github.com/alibaba/nacos.git/': Failed to
    直接打开这个网站:https://sites.ipaddress.com/github.com/。找到网站中的IP地址复制出里面的IPAddress并粘贴到hosts里面。以下是macos上使用命令行打开方式,也可以直接在硬盘上找到这个文件打开sudovim/private/etc/hosts在最后一行添加如下代码140.82.113.4githu......
  • 重写SpringCloudGateway路由查找算法,性能提升100倍!
    如果你也在做SpringCloudGateway网关开发,希望这篇文章能给你带来一些启发背景先说背景,某油项目,通过SpringCloudGateway配置了1.6万个路由规则,实际接口调用过程中,会偶现部分接口从发起请求到业务应用处理间隔了大概5秒的时间,经排查后发现是SpringCloudGateway底层在查找对应的R......
  • SpringCloudAliBaBa Seata学习
    SpringCloudAliBaBa学习服务的注册与发现分布式事务使用场景同一个事务对两个数据库操作,事务肯定是会失效的因为一个事务就是一个数据库连接,不同的连接就是不同的事务Seata是什么?Seata是一款分布式事务解决方案,用AT模式是阿里推荐的事务模式:TCC、SAGA、XA常见分布式事......