基于上篇讲解的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 @RibbonClient
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 @Qualifier
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 @Configuration
for the feign client. Can contain override
* @Bean
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
* @RibbonClient
.
*/
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可以轻松实现流量控制和降级等