OpenFeign学习使用

使用

父依赖 parent-project

<properties>
	<spring-boot.version>2.5.6spring-boot.version>
	<spring-cloud.version>2020.0.4spring-cloud.version>
properties>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-parentartifactId>
            <version>${spring-boot.version}version>
            <type>pomtype>
            <scope>importscope>
        dependency>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-dependenciesartifactId>
            <version>${spring-cloud.version}version>
            <type>pomtype>
            <scope>importscope>
        dependency>
    dependencies>
dependencyManagement>

共同配置

#是否使用okhttp 需要加feign.okhttp jar包
feign.okhttp.enabled=true
#是否使用http 需要加feign.httpclient jar包
feign.httpclient.enabled=false
#feign.httpclient连接池最大连接数,默认200
feign.httpclient.max-connections=500
#feign.httpclient连接超时时间 默认2s
feign.httpclient.connection-timeout=10000

feign.client默认连接超时时间 5s
feign.client.config.default.connect-timeout=5000
#feign.client读超时时间 30s
feign.client.config.default.read-timeout=180000
#feign.client日志等级
feign.client.config.default.logger-level=full

#启用熔断机制
feign.circuitbreaker.enabled=true
##########################使用resilience4j做断路器 配置开始##########################
# 是否向 Actuator 的 HealthIndicator 注册
resilience4j.circuitbreaker.configs.default.registerHealthIndicator=true
#失败请求百分比,超过这个比例,CircuitBreaker就会变成OPEN状态 默认50
resilience4j.circuitbreaker.configs.default.failureRateThreshold=30
#慢调用时间,当一个调用慢于这个时间时,会被记录为慢调用 默认60000[ms]
resilience4j.circuitbreaker.configs.default.slowCallDurationThreshold=60000
#当慢调用达到这个百分比的时候,CircuitBreaker就会变成OPEN状态 默认100
resilience4j.circuitbreaker.configs.default.slowCallRateThreshold=100
#当CircuitBreaker处于HALF_OPEN状态的时候,允许通过的请求数量 默认10
resilience4j.circuitbreaker.configs.default.permittedNumberOfCallsInHalfOpenState=5
#滑动窗口类型,COUNT_BASED代表是基于计数的滑动窗口,TIME_BASED代表是基于计时的滑动窗口 默认COUNT_BASED
resilience4j.circuitbreaker.configs.default.slidingWindowType=TIME_BASED
#滑动窗口大小,如果配置COUNT_BASED默认值100就代表是最近100个请求,如果配置TIME_BASED默认值100就代表是最近100s的请求  默认100
resilience4j.circuitbreaker.configs.default.slidingWindowSize=10
#最小请求个数。只有在滑动窗口内,请求个数达到这个个数,才会触发CircuitBreaker对于是否打开断路器的判断  默认100
resilience4j.circuitbreaker.configs.default.minimumNumberOfCalls=5
#从OPEN状态变成HALF_OPEN状态需要的等待时间   默认60000[ms]
resilience4j.circuitbreaker.configs.default.waitDurationInOpenState=2s
#如果设置为true代表是否自动从OPEN状态变成HALF_OPEN,即使没有请求过来   默认false
resilience4j.circuitbreaker.configs.default.automaticTransitionFromOpenToHalfOpenEnabled=true
#异常名单,指定一个 Exception 的 list,所有这个集合中的异常或者这些异常的子类,在调用的时候被抛出,
#都会被记录为失败。其他异常不会被认为是失败,或者在 ignoreExceptions 中配置的异常也不会被认为是失败。 默认值empty
resilience4j.circuitbreaker.configs.default.recordExceptions[0]=java.lang.Exception
#异常白名单,在这个名单中的所有异常及其子类,都不会认为是请求失败,就算在 recordExceptions 中配置了这些异常也没用。默认empty
#resilience4j.circuitbreaker.configs.default.ignoreExceptions[0]=
#最大线程池大小 默认Runtime.getRuntime().availableProcessors()
resilience4j.thread-pool-bulkhead.configs.default.maxThreadPoolSize=50
#最核心线程池大小 默认Runtime.getRuntime().availableProcessors()-1
resilience4j.thread-pool-bulkhead.configs.default.coreThreadPoolSize=10
#队列大小 默认100
resilience4j.thread-pool-bulkhead.configs.default.queueCapacity=10
##########################使用resilience4j做断路器 配置结束##########################

服务提供者 sys-project

依赖
<parent>
    <groupId>com.projectgroupId>
    <artifactId>parent-projectartifactId>
    <version>1.0version>
    
parent>

<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-openfeignartifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-netflix-ribbonartifactId>
        exclusion>
    exclusions>
dependency>
<dependency>
    <groupId>io.github.openfeign.formgroupId>
    <artifactId>feign-form-springartifactId>
dependency>
<dependency>
    
    <groupId>io.github.openfeigngroupId>
    <artifactId>feign-okhttpartifactId>
dependency>
<dependency>
    
    <groupId>io.github.openfeigngroupId>
    <artifactId>feign-httpclientartifactId>
dependency>


<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-circuitbreaker-resilience4jartifactId>
dependency>
<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-circuitbreaker-spring-retryartifactId>
dependency>
<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-circuitbreaker-resilience4jartifactId>
dependency>
<dependency>
    <groupId>io.github.resilience4jgroupId>
    <artifactId>resilience4j-spring-cloud2artifactId>
dependency>
<dependency>
    <groupId>io.github.resilience4jgroupId>
    <artifactId>resilience4j-feignartifactId>
dependency>
<dependency>
    
    <groupId>io.github.resilience4jgroupId>
    <artifactId>resilience4j-circuitbreakerartifactId>
dependency>
<dependency>
    
    <groupId>io.github.resilience4jgroupId>
    <artifactId>resilience4j-ratelimiterartifactId>
dependency>
<dependency>
    
    <groupId>io.github.resilience4jgroupId>
    <artifactId>resilience4j-bulkheadartifactId>
dependency>
<dependency>
    
    <groupId>io.github.resilience4jgroupId>
    <artifactId>resilience4j-retryartifactId>
dependency>
<dependency>
    
    <groupId>io.github.resilience4jgroupId>
    <artifactId>resilience4j-cacheartifactId>
dependency>
<dependency>
    
    <groupId>io.github.resilience4jgroupId>
    <artifactId>resilience4j-timelimiterartifactId>
dependency>

启动类添加@EnableFeignClients
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class SysProjectApplication extends SpringBootServletInitializer {
	
    public static void main(String[] args) {
    	SpringApplication.run(SysProjectApplication.class, args);
    }
    
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        // 注意这里要指向原先用main方法执行的Application启动类
        return builder.sources(SysProjectApplication.class);
    }
}
定义接口
@Api(value="角色管理接口集",tags={"角色管理接口集"})
@RestController
@RequestMapping("/_api/role")
public class _RoleController {
    @Autowired
    private IRoleService roleService;

    @ApiOperation(value="查询拥有指定资源的用户", httpMethod="GET")
    @ApiImplicitParam(name="resourceCode", value="指定的资源编号", required=true)
    @RequestMapping(value="/find/by_rsCode", method= RequestMethod.GET)
    public ApiResultDTO<List<SysDataSimpleDTO>> findUserByRsCode(@RequestParam String resourceCode, HttpServletRequest hreq) {
        return RestAPITemplate.restapi(new IMyLogic<List<SysDataSimpleDTO>>() {
            @Override
            public List<SysDataSimpleDTO> logic() {
                AccessTokenUser user=new AccessTokenUserAssembler().getAccessTokenUserFromReq(hreq);
                return roleService.findUserByRsCode(user,resourceCode);
            }
        });
    }

}

服务消费者 xfxt-project

依赖
<parent>
    <groupId>com.xysdgroupId>
    <artifactId>xysd-parentartifactId>
    <version>1.0version>
    
parent>

<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>

<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-openfeignartifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-netflix-ribbonartifactId>
        exclusion>
    exclusions>
dependency>
<dependency>
    <groupId>io.github.openfeign.formgroupId>
    <artifactId>feign-form-springartifactId>
dependency>
<dependency>
    
    <groupId>io.github.openfeigngroupId>
    <artifactId>feign-okhttpartifactId>
dependency>
<dependency>
    
    <groupId>io.github.openfeigngroupId>
    <artifactId>feign-httpclientartifactId>
dependency>


<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-circuitbreaker-resilience4jartifactId>
dependency>
<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-circuitbreaker-spring-retryartifactId>
dependency>
<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-circuitbreaker-resilience4jartifactId>
dependency>
<dependency>
    <groupId>io.github.resilience4jgroupId>
    <artifactId>resilience4j-spring-cloud2artifactId>
dependency>
<dependency>
    <groupId>io.github.resilience4jgroupId>
    <artifactId>resilience4j-feignartifactId>
dependency>
<dependency>
    
    <groupId>io.github.resilience4jgroupId>
    <artifactId>resilience4j-circuitbreakerartifactId>
dependency>
<dependency>
    
    <groupId>io.github.resilience4jgroupId>
    <artifactId>resilience4j-ratelimiterartifactId>
dependency>
<dependency>
    
    <groupId>io.github.resilience4jgroupId>
    <artifactId>resilience4j-bulkheadartifactId>
dependency>
<dependency>
    
    <groupId>io.github.resilience4jgroupId>
    <artifactId>resilience4j-retryartifactId>
dependency>
<dependency>
    
    <groupId>io.github.resilience4jgroupId>
    <artifactId>resilience4j-cacheartifactId>
dependency>
<dependency>
    
    <groupId>io.github.resilience4jgroupId>
    <artifactId>resilience4j-timelimiterartifactId>
dependency>

启动类添加@EnableFeignClients
@EnableFeignClients
@EnableDiscoveryClient
@EnableTransactionManagement
@SpringBootApplication
public class XfxtProjectApplication extends SpringBootServletInitializer {
    
    public static void main(String[] args) {
        System.setProperty("spring.devtools.restart.enabled", "false");
        SpringApplication.run(XfxtProjectApplication.class, args);
    }
    
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        
        // 注意这里要指向原先用main方法执行的Application启动类
        return builder.sources(XfxtProjectApplication.class);
    }

    @Bean
    public MultipartConfigElement multipartConfigElement() {
        MultipartConfigFactory factory = new MultipartConfigFactory();
        //允许上传的文件最大值
        factory.setMaxFileSize(DataSize.parse("50MB")); //KB,MB
        /// 设置总上传数据总大小
        factory.setMaxRequestSize(DataSize.parse("50MB"));
        return factory.createMultipartConfig();
    }

}
定义Feign接口
@FeignClient(
    name="sys-project",
    contextId="ISysProjectGatewayServiceFeign",
    path="/sys-project/_api",
    configuration= FeignClientConfig.class
)
public interface ISysProjectGatewayService {
	/**
     * 查询拥有指定资源的用户
     * @param resourceCode
     * @return
     */
    @GetMapping("/role/find/by_rsCode")
    ApiResultDTO<List<SysDataSimpleDTO>> findUserByRsCode(@RequestParam("resourceCode") String resourceCode);
}
定义Feign配置文件
@Configuration
@AutoConfigureBefore({FeignClientsConfiguration.class})
public class FeignClientConfig{
	@Autowired
	private IBeansFactoryService beansFactoryService;
	
	//解决 post 的url编码,和mutipart/from-data文件上传问题
	@Bean
    @Primary
    public Encoder multipartFormEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {
        return new SpringFormEncoder(new SpringEncoder(messageConverters));
    }
    @Bean
    @Scope("prototype")
    public Feign.Builder feignBuilder(Encoder encoder) {
        return Feign.builder()
        		.encoder(encoder)//编码
        		;
    }
    
	//增加请求头
	@Bean
    public RequestInterceptor headerInterceptor() {
        return new RequestInterceptor() {
            @Override
            public void apply(RequestTemplate template) {
            	String url=template.feignTarget().url();
    			if(StringUtils.isBlank(url)) return;
    			
    			//logger.error("--------------------------FeignClient["+url+"]开始--------------------------");
    			AccessTokenUser user = null;
    			ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    			String access_token=null;
    			if(attributes!=null) {
    				HttpServletRequest hreq = attributes.getRequest();
    		        user=new AccessTokenUserAssembler().getAccessTokenUserFromReq(hreq);
    			}
    			if(user!=null) access_token=user.getAccessToken();
    			if(StringUtils.isNotBlank(access_token)) {
    				template.header("access_token", access_token);
    			}
    			
    			//api接口
    			boolean api=url.indexOf("/api")>0;
    			if(!api) api=template.url().startsWith("/api/")||template.url().startsWith("api/");
    			if(api) {
    				//logger.error("--------------------------FeignClient["+url+"]access_token["+access_token+"]--------------------------");
    				return;
    			}
    			
    			//已设置过访问令牌 以访问令牌为准 返回
    			if(StringUtils.isNotBlank(access_token)) {
    				//logger.error("--------------------------FeignClient["+url+"]access_token["+access_token+"]--------------------------");
    				return;
    			}
    			//_api接口
    			boolean _api=url.indexOf("/_api")>0;
    			if(!_api) api=template.url().startsWith("/_api/")||template.url().startsWith("_api/");
    			if(!_api) return;//非_api接口 返回 不处理
    			
    			if(user==null) user=AccessTokenUser.createSystemUser();//不存在 令牌用户 创建系统用户
    			//为_api接口 创建 内部接口访问令牌
    			AccessTokenUser _user=user;
    			//BUG:为了避免循环依赖不允许通过autowired注入,这里直接通过静态方法获取ICachedTokenService
    			ICachedTokenService tokenService = beansFactoryService.getBeanOfType(ICachedTokenService.class);
    			if(tokenService!=null) {
    				String innerToken=ThreadLocalCache.fetchAPIData(null,()->{
    					return tokenService.createInnerToken(_user);
    				},"生成内部访问令牌错误,");
    				
    		        template.header("inner_token", innerToken);
    			}
            }
        };
    }
	
}

简介

Spring Cloud OpenFeign是一个声明式的REST客户端,使得编写Web服务客户端更加简单。它基于Feign,一个Spring Cloud组件,是一个轻量级的RESTful HTTP客户端。OpenFeign支持可插拔的编码器和解码器,并集成了Ribbon,用于客户端负载均衡,可以调用服务注册中心的服务。OpenFeign还支持Spring MVC注解,如@RequestMapping等,并利用Feign的高扩展性,使用标准Spring Web MVC来声明客户端Java接口。使用OpenFeign时,只需定义服务接口,然后在上面添加注解。
注意: 高版本OpenFeign底层不使用Ribbon做负载均衡。(SpringCloud 2020.0.x版本开始之后的版本)

OpenFeign与Feign的区别

Feign

Feign是Spring Cloud组件中的一个轻量级RESTful的HTTP服务客户端,Feign内置了Ribbon,用来做客户端负载均衡,去调用服务注册中心的服务。Feign的使用方式是:使用Feign的注解定义接口,调用这个接口,就可以调用服务注册中心的服务。

<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-feignartifactId>
dependency>	

OpenFeign

OpenFeign是Spring Cloud 在Feign的基础上支持了SpringMVC的注解,如@RequesMapping等等。OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。

<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-openfeignartifactId>
dependency>

总结

Feign是在2019就已经不再更新了,通过maven网站就可以看出来,随之取代的是OpenFeign,从名字上就可以知道,他是Feign的升级版。

@FeignClient

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface FeignClient {

	/**
	 *作用同name属性 服务的名称(被调用的服务)
	 */
	@AliasFor("name")
	String value() default "";

	/**
	 * 如果指定这个属性,则bean的名称则取这个值,否则取name属性值
	 */
	String contextId() default "";

	/**
	 *作用同value属性
	 */
	@AliasFor("value")
	String name() default "";

	/**
	 * 给当前bean指定一个名称
	 */
	@Deprecated
	String qualifier() default "";

	/**
	 * 给当前bean指定多个名称
	 */
	String[] qualifiers() default {};

	/**
	 *指定被调用服务的url 绝对路径  一般用于调试
	 */
	String url() default "";

	/**
	 * 是否启用404解码。默认为 false。
	 * 如果设置为 true,当请求返回404时,会抛出异常而不是返回null。
	 */
	boolean decode404() default false;

	/**
	 *指定自定义配置文件
	 */
	Class<?>[] configuration() default {};

	/**
	 * 指定一个 fallback bean,当远程调用失败时,会调用这个 bean。这通常用于实现断路器、重试等高级特性。
	 * 指定的bean 必须要要实现当前接口
	 */
	Class<?> fallback() default void.class;

	/**
	 *指定一个 fallback factory bean,用于创建 fallback 实例。这通常与 fallback 属性一起使用,用于提供更复杂的 fallback 逻辑。
	 */
	Class<?> fallbackFactory() default void.class;

	/**
	 * 指定被调用服务返回地址前缀
	 */
	String path() default "";

	/**
	 * 存在多个相同类型的服务时,指定主要第一使用的服务
	 */
	boolean primary() default true;

}

@EnableFeignClients

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {

	/**
	 * 作用同basePackages 指定包扫描路径
	 */
	String[] value() default {};

	/**
	 *  指定包扫描路径
	 */
	String[] basePackages() default {};

	/**
	 * 指定要扫描的那些类
	 */
	Class<?>[] basePackageClasses() default {};

	/**
	 *自定义Feign配置文件
	 */
	Class<?>[] defaultConfiguration() default {};

	/**
	 * 指定有@FeignClient注解的类
	 */
	Class<?>[] clients() default {};

}

AutoConfigureBefore

@AutoConfigureBefore 是 Spring Boot 的一个注解,用于指定一个配置类应该在其他配置类之前执行。这个注解可以用于自定义自动配置类,以便在 Spring Boot 自动配置之前执行一些自定义的配置。

FeignClientsConfiguration

	//依赖引入CircuitBreaker 所以这里的配置类使用这个
	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(CircuitBreaker.class)
	@ConditionalOnProperty("feign.circuitbreaker.enabled")
	protected static class CircuitBreakerPresentFeignBuilderConfiguration {

		@Bean
		@Scope("prototype")
		@ConditionalOnMissingBean({ Feign.Builder.class, CircuitBreakerFactory.class })
		public Feign.Builder defaultFeignBuilder(Retryer retryer) {
			return Feign.builder().retryer(retryer);
		}

		@Bean
		@Scope("prototype")
		@ConditionalOnMissingBean
		@ConditionalOnBean(CircuitBreakerFactory.class)
		public Feign.Builder circuitBreakerFeignBuilder() {
			return FeignCircuitBreaker.builder();
		}

	}

生成接口代理类

public class ReflectiveFeign extends Feign {
	 public <T> T newInstance(Target<T> target) {
    Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
    Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
    List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();

    for (Method method : target.type().getMethods()) {
      if (method.getDeclaringClass() == Object.class) {
        continue;
      } else if (Util.isDefault(method)) {
        DefaultMethodHandler handler = new DefaultMethodHandler(method);
        defaultMethodHandlers.add(handler);
        methodToHandler.put(method, handler);
      } else {
        methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
      }
    }
    InvocationHandler handler = factory.create(target, methodToHandler);
    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
        new Class<?>[] {target.type()}, handler);

    for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
      defaultMethodHandler.bindTo(proxy);
    }
    return proxy;
  }
}

你可能感兴趣的:(项目,java)