spring cloud利用feign和sentinel进行内部或外部远程调用

时间:2022-06-19
本文章向大家介绍spring cloud利用feign和sentinel进行内部或外部远程调用 ,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

    基于上篇讲解的Sentinel之后,这次讲讲spring cloud环境下最优雅的远程调用方式Feign

    相比于restTemplate来说,feign只需要通过注解和借口就可以实现远程调用,无需关心具体的调用过程,使用起来无感知,和本地调用相同

一、FeignClient注解

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

/*
 * Copyright 2013-2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.cloud.openfeign;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.core.annotation.AliasFor;

/**
 * Annotation for interfaces declaring that a REST client with that interface should be
 * created (e.g. for autowiring into another component). If ribbon is available it will be
 * used to load balance the backend requests, and the load balancer can be configured
 * using a <code>@RibbonClient</code> with the same name (i.e. value) as the feign client.
 *
 * @author Spencer Gibb
 * @author Venil Noronha
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FeignClient {

	/**
	 * The name of the service with optional protocol prefix. Synonym for {@link #name()
	 * name}. A name must be specified for all clients, whether or not a url is provided.
	 * Can be specified as property key, eg: ${propertyKey}.
	 */
	@AliasFor("name")
	String value() default "";

	/**
	 * The service id with optional protocol prefix. Synonym for {@link #value() value}.
	 *
	 * @deprecated use {@link #name() name} instead
	 */
	@Deprecated
	String serviceId() default "";

	/**
	 * The service id with optional protocol prefix. Synonym for {@link #value() value}.
	 */
	@AliasFor("value")
	String name() default "";
	
	/**
	 * Sets the <code>@Qualifier</code> value for the feign client.
	 */
	String qualifier() default "";

	/**
	 * An absolute URL or resolvable hostname (the protocol is optional).
	 */
	String url() default "";

	/**
	 * Whether 404s should be decoded instead of throwing FeignExceptions
	 */
	boolean decode404() default false;

	/**
	 * A custom <code>@Configuration</code> for the feign client. Can contain override
	 * <code>@Bean</code> definition for the pieces that make up the client, for instance
	 * {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.
	 *
	 * @see FeignClientsConfiguration for the defaults
	 */
	Class<?>[] configuration() default {};

	/**
	 * Fallback class for the specified Feign client interface. The fallback class must
	 * implement the interface annotated by this annotation and be a valid spring bean.
	 */
	Class<?> fallback() default void.class;

	/**
	 * Define a fallback factory for the specified Feign client interface. The fallback
	 * factory must produce instances of fallback classes that implement the interface
	 * annotated by {@link FeignClient}. The fallback factory must be a valid spring
	 * bean.
	 *
	 * @see feign.hystrix.FallbackFactory for details.
	 */
	Class<?> fallbackFactory() default void.class;

	/**
	 * Path prefix to be used by all method-level mappings. Can be used with or without
	 * <code>@RibbonClient</code>.
	 */
	String path() default "";

	/**
	 * Whether to mark the feign proxy as a primary bean. Defaults to true.
	 */
	boolean primary() default true;

}

    声明接口之后,在代码中通过@Resource注入之后即可使用。@FeignClient标签的常用属性如下:

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

二:实例

下面指定一个说话的接口,url配置的是同本地的前缀,name是要调用的微服务名称,在erurka启用下生效,此外还指定了失效转移的配置,使用的是Sentinel实现,详情见上篇文章

package cn.chinotan.feign;

import cn.chinotan.SaySomeThing;
import cn.chinotan.config.feign.FeignConfiguration;
import cn.chinotan.feign.fallback.SayServiceFallback;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * @program: test
 * @description: 说接口
 * @author: xingcheng
 * @create: 2019-01-19 18:05
 **/
@FeignClient(name = "sayService", url = "http://localhost:11111", fallback = SayServiceFallback.class, configuration = FeignConfiguration.class)
public interface SayService {

    /**
     * 说
     * @param saySomeThing
     * @return
     */
    @RequestMapping(value = "/feign/hello", method = RequestMethod.POST)
    SaySomeThing saySomeThing(@RequestBody SaySomeThing saySomeThing);
    
}

在使用fallback属性时,需要使用@Component注解

package cn.chinotan.feign.fallback;

import cn.chinotan.SaySomeThing;
import cn.chinotan.feign.SayService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @program: test
 * @description: say快速失败
 * @author: xingcheng
 * @create: 2019-01-19 18:18
 **/
public class SayServiceFallback implements SayService {
    
    Logger logger = LoggerFactory.getLogger(SayServiceFallback.class);
    
    @Override
    public SaySomeThing saySomeThing(SaySomeThing saySomeThing) {
        SaySomeThing saySomeThingFail = new SaySomeThing();
        saySomeThingFail.setName("错误");
        saySomeThingFail.setWords("错误的话");
        logger.error("调用失败");
        return saySomeThingFail;
    }
}

接下是配置feign的配置信息

package cn.chinotan.config.feign;

import cn.chinotan.feign.fallback.EchoServiceFallback;
import cn.chinotan.feign.fallback.SayServiceFallback;
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @program: test
 * @description: FeignConfiguration
 * @author: xingcheng
 * @create: 2019-01-12 18:44
 **/
@Configuration
public class FeignConfiguration {
    @Bean
    public SayServiceFallback sayServiceFallback() {
        return new SayServiceFallback();
    }
    
    @Bean
    public Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }
}

两个bean指定降级类和日志级别打印,并注入到spring上下文

每一个被创建的Feign客户端都会有一个logger。该logger默认的名称为Feign客户端对应的接口的全限定名。Feign日志记录只能响应DEBUG日志级别。

例如

# feign日志
logging.level.cn.chinotan.feign: DEBUG

针对每一个Feign客户端,可以配置一个Logger.Level对象,通过该对象控制日志输出内容。

Logger.Level有如下几种选择:

NONE, 不记录日志 (默认)。

BASIC, 只记录请求方法和URL以及响应状态代码和执行时间。

HEADERS, 记录请求和应答的头的基本信息。

FULL, 记录请求和响应的头信息,正文和元数据。

之后配置异常报警信息,主要通过实现ErrorDecoder接口实现

package cn.chinotan.config.feign;

import feign.FeignException;
import feign.Response;
import feign.codec.ErrorDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;

import static feign.FeignException.errorStatus;

/**
 * @program: test
 * @description: feign调用异常统一处理
 * @author: xingcheng
 * @create: 2019-01-19 19:47
 **/
@Configuration
public class MyErrorDecoder implements ErrorDecoder {
    
    Logger logger = LoggerFactory.getLogger(MyErrorDecoder.class);
    
    @Override
    public Exception decode(String methodKey, Response response) {
        FeignException exception = errorStatus(methodKey, response);
        // 报警
        logger.error("methodKey: {}, reason is {}", methodKey, response.toString());
        return exception;
    }
    
}

三:运行

配置一个请求controller

package cn.chinotan.controller;

import cn.chinotan.SaySomeThing;
import cn.chinotan.feign.SayService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @program: test
 * @description: 最简单的使用 Sentinel 的例子
 * @author: xingcheng
 * @create: 2019-01-20 18:01
 **/
@RestController
@RequestMapping("/feign")
public class FeignController {

    @Autowired
    SayService sayService;

    @PostMapping(value = "/hello")
    public SaySomeThing hello(@RequestBody SaySomeThing saySomeThing) {
//        throw new RuntimeException("我是异常");
        return saySomeThing;
    }

    @PostMapping(value = "/say")
    public SaySomeThing echoFeign(@RequestBody SaySomeThing saySomeThing) {
        return sayService.saySomeThing(saySomeThing);
    }
}

运行结果:

日志打印:

INFO: log base dir is: /Users/xingcheng/logs/csp/
INFO: log name use pid is: false
2019-01-19 20:11:45.916 DEBUG 2932 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] ---> POST http://localhost:11111/feign/hello HTTP/1.1
2019-01-19 20:11:45.916 DEBUG 2932 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] Content-Type: application/json;charset=UTF-8
2019-01-19 20:11:45.916 DEBUG 2932 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] Content-Length: 36
2019-01-19 20:11:45.917 DEBUG 2932 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] 
2019-01-19 20:11:45.917 DEBUG 2932 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] {"name":"chinotan","words":"世界"}
2019-01-19 20:11:45.917 DEBUG 2932 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] ---> END HTTP (36-byte body)
2019-01-19 20:11:45.944 DEBUG 2932 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] <--- HTTP/1.1 200 (27ms)
2019-01-19 20:11:45.944 DEBUG 2932 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] cache-control: no-cache, no-store, max-age=0, must-revalidate
2019-01-19 20:11:45.944 DEBUG 2932 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] content-type: application/json;charset=UTF-8
2019-01-19 20:11:45.944 DEBUG 2932 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] date: Sat, 19 Jan 2019 12:11:45 GMT
2019-01-19 20:11:45.944 DEBUG 2932 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] expires: 0
2019-01-19 20:11:45.944 DEBUG 2932 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] pragma: no-cache
2019-01-19 20:11:45.944 DEBUG 2932 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] transfer-encoding: chunked
2019-01-19 20:11:45.944 DEBUG 2932 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] x-content-type-options: nosniff
2019-01-19 20:11:45.945 DEBUG 2932 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] x-frame-options: DENY
2019-01-19 20:11:45.945 DEBUG 2932 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] x-xss-protection: 1; mode=block
2019-01-19 20:11:45.945 DEBUG 2932 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] 
2019-01-19 20:11:45.951 DEBUG 2932 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] {"name":"chinotan","words":"世界"}
2019-01-19 20:11:45.951 DEBUG 2932 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] <--- END HTTP (36-byte body)
2019-01-19 20:37:41.552 DEBUG 2932 --- [io-11111-exec-7] cn.chinotan.feign.SayService             : [SayService#saySomeThing] ---> POST http://localhost:11111/feign/hello HTTP/1.1
2019-01-19 20:37:41.552 DEBUG 2932 --- [io-11111-exec-7] cn.chinotan.feign.SayService             : [SayService#saySomeThing] Content-Type: application/json;charset=UTF-8
2019-01-19 20:37:41.552 DEBUG 2932 --- [io-11111-exec-7] cn.chinotan.feign.SayService             : [SayService#saySomeThing] Content-Length: 36
2019-01-19 20:37:41.552 DEBUG 2932 --- [io-11111-exec-7] cn.chinotan.feign.SayService             : [SayService#saySomeThing] 
2019-01-19 20:37:41.552 DEBUG 2932 --- [io-11111-exec-7] cn.chinotan.feign.SayService             : [SayService#saySomeThing] {"name":"chinotan","words":"世界"}
2019-01-19 20:37:41.552 DEBUG 2932 --- [io-11111-exec-7] cn.chinotan.feign.SayService             : [SayService#saySomeThing] ---> END HTTP (36-byte body)
2019-01-19 20:37:41.559 DEBUG 2932 --- [io-11111-exec-7] cn.chinotan.feign.SayService             : [SayService#saySomeThing] <--- HTTP/1.1 200 (7ms)
2019-01-19 20:37:41.559 DEBUG 2932 --- [io-11111-exec-7] cn.chinotan.feign.SayService             : [SayService#saySomeThing] cache-control: no-cache, no-store, max-age=0, must-revalidate
2019-01-19 20:37:41.560 DEBUG 2932 --- [io-11111-exec-7] cn.chinotan.feign.SayService             : [SayService#saySomeThing] content-type: application/json;charset=UTF-8
2019-01-19 20:37:41.560 DEBUG 2932 --- [io-11111-exec-7] cn.chinotan.feign.SayService             : [SayService#saySomeThing] date: Sat, 19 Jan 2019 12:37:41 GMT
2019-01-19 20:37:41.560 DEBUG 2932 --- [io-11111-exec-7] cn.chinotan.feign.SayService             : [SayService#saySomeThing] expires: 0
2019-01-19 20:37:41.560 DEBUG 2932 --- [io-11111-exec-7] cn.chinotan.feign.SayService             : [SayService#saySomeThing] pragma: no-cache
2019-01-19 20:37:41.560 DEBUG 2932 --- [io-11111-exec-7] cn.chinotan.feign.SayService             : [SayService#saySomeThing] transfer-encoding: chunked
2019-01-19 20:37:41.560 DEBUG 2932 --- [io-11111-exec-7] cn.chinotan.feign.SayService             : [SayService#saySomeThing] x-content-type-options: nosniff
2019-01-19 20:37:41.560 DEBUG 2932 --- [io-11111-exec-7] cn.chinotan.feign.SayService             : [SayService#saySomeThing] x-frame-options: DENY
2019-01-19 20:37:41.560 DEBUG 2932 --- [io-11111-exec-7] cn.chinotan.feign.SayService             : [SayService#saySomeThing] x-xss-protection: 1; mode=block
2019-01-19 20:37:41.560 DEBUG 2932 --- [io-11111-exec-7] cn.chinotan.feign.SayService             : [SayService#saySomeThing] 
2019-01-19 20:37:41.560 DEBUG 2932 --- [io-11111-exec-7] cn.chinotan.feign.SayService             : [SayService#saySomeThing] {"name":"chinotan","words":"世界"}
2019-01-19 20:37:41.560 DEBUG 2932 --- [io-11111-exec-7] cn.chinotan.feign.SayService             : [SayService#saySomeThing] <--- END HTTP (36-byte body)

可以看到请求地址,入参和出参,headers信息都打印出来了

接下来试试异常情况,修改controller:

@PostMapping(value = "/hello")
    public SaySomeThing hello(@RequestBody SaySomeThing saySomeThing) {
        throw new RuntimeException("我是异常");
//        return saySomeThing;
    }

运行结果:

日志打印:

INFO: log base dir is: /Users/xingcheng/logs/csp/
INFO: log name use pid is: false
2019-01-19 20:40:18.049 DEBUG 3137 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] ---> POST http://localhost:11111/feign/hello HTTP/1.1
2019-01-19 20:40:18.049 DEBUG 3137 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] Content-Type: application/json;charset=UTF-8
2019-01-19 20:40:18.049 DEBUG 3137 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] Content-Length: 36
2019-01-19 20:40:18.049 DEBUG 3137 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] 
2019-01-19 20:40:18.049 DEBUG 3137 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] {"name":"chinotan","words":"世界"}
2019-01-19 20:40:18.049 DEBUG 3137 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] ---> END HTTP (36-byte body)
2019-01-19 20:40:18.083 ERROR 3137 --- [io-11111-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: 我是异常] with root cause

java.lang.RuntimeException: 我是异常
	at cn.chinotan.controller.FeignController.hello(FeignController.java:26) ~[classes/:na]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_162]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_162]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_162]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_162]
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:209) ~[spring-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136) ~[spring-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102) ~[spring-webmvc-5.0.7.RELEASE.jar:5.0.7.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:877) ~[spring-webmvc-5.0.7.RELEASE.jar:5.0.7.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:783) ~[spring-webmvc-5.0.7.RELEASE.jar:5.0.7.RELEASE]
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.0.7.RELEASE.jar:5.0.7.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991) ~[spring-webmvc-5.0.7.RELEASE.jar:5.0.7.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925) ~[spring-webmvc-5.0.7.RELEASE.jar:5.0.7.RELEASE]
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974) ~[spring-webmvc-5.0.7.RELEASE.jar:5.0.7.RELEASE]
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:877) ~[spring-webmvc-5.0.7.RELEASE.jar:5.0.7.RELEASE]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:661) ~[tomcat-embed-core-8.5.31.jar:8.5.31]
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851) ~[spring-webmvc-5.0.7.RELEASE.jar:5.0.7.RELEASE]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) ~[tomcat-embed-core-8.5.31.jar:8.5.31]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-8.5.31.jar:8.5.31]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.31.jar:8.5.31]
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) ~[tomcat-embed-websocket-8.5.31.jar:8.5.31]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.31.jar:8.5.31]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.31.jar:8.5.31]
	at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.filterAndRecordMetrics(WebMvcMetricsFilter.java:158) ~[spring-boot-actuator-2.0.3.RELEASE.jar:2.0.3.RELEASE]
	at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.filterAndRecordMetrics(WebMvcMetricsFilter.java:126) ~[spring-boot-actuator-2.0.3.RELEASE.jar:2.0.3.RELEASE]
	at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:111) ~[spring-boot-actuator-2.0.3.RELEASE.jar:2.0.3.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.31.jar:8.5.31]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.31.jar:8.5.31]
	at org.springframework.boot.actuate.web.trace.servlet.HttpTraceFilter.doFilterInternal(HttpTraceFilter.java:90) ~[spring-boot-actuator-2.0.3.RELEASE.jar:2.0.3.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.31.jar:8.5.31]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.31.jar:8.5.31]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:320) ~[spring-security-web-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127) ~[spring-security-web-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91) ~[spring-security-web-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) ~[spring-security-web-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137) ~[spring-security-web-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111) ~[spring-security-web-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170) ~[spring-security-web-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) ~[spring-security-web-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilterInternal(BasicAuthenticationFilter.java:158) ~[spring-security-web-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) ~[spring-security-web-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:66) ~[spring-security-web-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) ~[spring-security-web-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) ~[spring-security-web-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215) ~[spring-security-web-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178) ~[spring-security-web-5.0.6.RELEASE.jar:5.0.6.RELEASE]
	at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357) ~[spring-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
	at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270) ~[spring-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.31.jar:8.5.31]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.31.jar:8.5.31]
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) ~[spring-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.31.jar:8.5.31]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.31.jar:8.5.31]
	at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109) ~[spring-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.31.jar:8.5.31]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.31.jar:8.5.31]
	at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93) ~[spring-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.31.jar:8.5.31]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.31.jar:8.5.31]
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) ~[spring-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.31.jar:8.5.31]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.31.jar:8.5.31]
	at com.alibaba.csp.sentinel.adapter.servlet.CommonFilter.doFilter(CommonFilter.java:89) ~[sentinel-web-servlet-1.4.0.jar:na]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.31.jar:8.5.31]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.31.jar:8.5.31]
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) ~[tomcat-embed-core-8.5.31.jar:8.5.31]
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-8.5.31.jar:8.5.31]
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496) [tomcat-embed-core-8.5.31.jar:8.5.31]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) [tomcat-embed-core-8.5.31.jar:8.5.31]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81) [tomcat-embed-core-8.5.31.jar:8.5.31]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [tomcat-embed-core-8.5.31.jar:8.5.31]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) [tomcat-embed-core-8.5.31.jar:8.5.31]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803) [tomcat-embed-core-8.5.31.jar:8.5.31]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.31.jar:8.5.31]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790) [tomcat-embed-core-8.5.31.jar:8.5.31]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1468) [tomcat-embed-core-8.5.31.jar:8.5.31]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.31.jar:8.5.31]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_162]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_162]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.31.jar:8.5.31]
	at java.lang.Thread.run(Thread.java:748) [na:1.8.0_162]

2019-01-19 20:40:18.112 DEBUG 3137 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] <--- HTTP/1.1 500 (62ms)
2019-01-19 20:40:18.113 DEBUG 3137 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] cache-control: no-cache, no-store, max-age=0, must-revalidate
2019-01-19 20:40:18.113 DEBUG 3137 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] connection: close
2019-01-19 20:40:18.113 DEBUG 3137 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] content-type: application/json;charset=UTF-8
2019-01-19 20:40:18.113 DEBUG 3137 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] date: Sat, 19 Jan 2019 12:40:18 GMT
2019-01-19 20:40:18.113 DEBUG 3137 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] expires: 0
2019-01-19 20:40:18.113 DEBUG 3137 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] pragma: no-cache
2019-01-19 20:40:18.113 DEBUG 3137 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] transfer-encoding: chunked
2019-01-19 20:40:18.113 DEBUG 3137 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] x-content-type-options: nosniff
2019-01-19 20:40:18.113 DEBUG 3137 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] x-frame-options: DENY
2019-01-19 20:40:18.113 DEBUG 3137 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] x-xss-protection: 1; mode=block
2019-01-19 20:40:18.113 DEBUG 3137 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] 
2019-01-19 20:40:18.114 DEBUG 3137 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] {"timestamp":"2019-01-19T12:40:18.094+0000","status":500,"error":"Internal Server Error","message":"我是异常","path":"/feign/hello"}
2019-01-19 20:40:18.114 DEBUG 3137 --- [io-11111-exec-2] cn.chinotan.feign.SayService             : [SayService#saySomeThing] <--- END HTTP (136-byte body)
2019-01-19 20:40:18.116 ERROR 3137 --- [io-11111-exec-2] cn.chinotan.config.feign.MyErrorDecoder  : methodKey: SayService#saySomeThing(SaySomeThing), reason is HTTP/1.1 500
cache-control: no-cache, no-store, max-age=0, must-revalidate
connection: close
content-type: application/json;charset=UTF-8
date: Sat, 19 Jan 2019 12:40:18 GMT
expires: 0
pragma: no-cache
transfer-encoding: chunked
x-content-type-options: nosniff
x-frame-options: DENY
x-xss-protection: 1; mode=block

{"timestamp":"2019-01-19T12:40:18.094+0000","status":500,"error":"Internal Server Error","message":"我是异常","path":"/feign/hello"}
2019-01-19 20:40:18.116 ERROR 3137 --- [io-11111-exec-2] c.c.feign.fallback.SayServiceFallback    : 调用失败

可见利用feign组件可以轻松的实现远程接口的调用,监控和日志,配合Sentinel可以轻松实现流量控制和降级等

(adsbygoogle = window.adsbygoogle || []).push({});