简介:feign是springcloud的一个重要组件,主要功能就是服务调用,既可以用于微服务之间调用,也可以rest调用
pom文件 添加依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
启动类上添加注解 @EnableFeignClients
目标:调用微服务里用户服务的获取用户信息接口
步骤:
IUserService 接口
import com.njc.java.entity.base.ResponseEntity;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@FeignClient(value = "user-service", url = "${api.user}")
public interface IUserService {
@RequestMapping(value = "/user/info", method = RequestMethod.GET)
ResponseEntity<RespDTO> getUserInfo();
}
application.yml
api:
user: user-service:8888
rest 调用类似,把 application.yml 修改 url 即可
api:
user: http://192.168.188.1:8888
Header传参
KEY | VALUE |
---|---|
Authorization | TOKEN |
@RequestMapping(value = "/user", method = RequestMethod.GET)
ResponseEntity<RespDTO> userInfo(@RequestHeader(name = "Authorization") String token);
URL传参
@RequestMapping(value = "/user/{pernr}", method = RequestMethod.GET)
NjcResponseEntity<UserInfoRespDTO> userInfo( @PathVariable("pernr") String pernr);
Params传参
@GetMapping(value = "/user")
NjcResponseEntity<String> userInfo(@SpringQueryMap ReqDTO reqDTO);
Body传参
@GetMapping(value = "/user")
NjcResponseEntity<String> userInfo(@RequestBody ReqDTO reqDTO);
场景举例:IUserService 在调用时,所有接口都需要在 Header 加 token
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
public class FeignConfig implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
//添加token
requestTemplate.header("Authorization", request.getHeader("Authorization"));
}
}
@FeignClient(url = "${api.user.url}", name = "user-service", configuration = FeignConfig.class)
public interface IUserService {
@GetMapping(value = "/user/user/{userId}")
ResponseEntity<UserSimpleInfoRespDTO> getUserInfo(@PathVariable(value = "userId") Long userId);
}
设置 config
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FeignConfig {
@Bean
public Logger.Level feignLogLevel(){
/* 设置feign客户端的日志打印级别为FULL */
return Logger.Level.BASIC;
}
@Bean
Logger feignLogger() {
return new NjcFeignLogger();
}
}
import cn.hutool.core.io.FileUtil;
import com.alibaba.fastjson.JSON;
import feign.*;
import feign.Response;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import java.io.*;
import java.util.*;
import static feign.Util.decodeOrDefault;
import static java.nio.charset.StandardCharsets.UTF_8;
/**
* Feign请求日志处理类
* 所有通过Feign请求的外部接口都会被监控
*/
@Slf4j
public class NjcFeignLogger extends Logger {
private static final ThreadLocal<Map<String, String>> logContext = new ThreadLocal<>();
private static final String PATH = "path";
private static final String METHOD = "method";
private static final String REQUEST_BODY = "body";
private static final String HEADER = "header";
private static final String TIME = "time";
/**
* 构建headers字符串
*/
private String builderHeaders(Map<String, Collection<String>> headersMap) {
StringBuilder headers = new StringBuilder();
Iterator<Map.Entry<String, Collection<String>>> iterator = headersMap.entrySet().stream().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Collection<String>> next = iterator.next();
ArrayList<String> values = new ArrayList<>(next.getValue());
headers.append(next.getKey())
.append(":")
.append(values.size() == 1 ? values.get(0) : JSON.toJSONString(values))
.append(iterator.hasNext() ? "|" : "");
}
return headers.toString();
}
/**
* 请求拦截
*/
@Override
protected void logRequest(String configKey, Level logLevel, Request request) {
Map<String, String> map = new HashMap<>(3);
map.put(PATH, request.url());
map.put(TIME, System.currentTimeMillis() + "");
map.put(METHOD, request.httpMethod().name());
map.put(HEADER, builderHeaders(request.headers()));
String body = request.body() == null ? null : request.charset() == null ? null : new String(request.body(), request.charset());
//文件上传不打印请求日志
if (StringUtils.contains(request.url(), "/helper/common/file/upload")) {
body = null;
}
map.put(REQUEST_BODY, body);
logContext.set(map);
}
/**
* 响应拦截
*/
@Override
protected feign.Response logAndRebufferResponse(
String configKey, Level logLevel, Response response, long elapsedTime) throws IOException {
Map<String, String> request = logContext.get();
logContext.remove();
// 返回参数
byte[] bodyData = streamToByteArray(response.body().asInputStream());
if (null != bodyData && bodyData.length > 0) {
String responseBody = decodeOrDefault(bodyData, UTF_8, "Binary data");
log(request, response.status(), responseBody.replaceAll("\\s*|\t|\r|\n", ""));
return response.toBuilder().body(bodyData).build();
}
log(request, response.status(), null);
return response;
}
/**
* 异常拦截
*/
@Override
protected IOException logIOException(String configKey, Level logLevel, IOException ioe, long elapsedTime) {
Map<String, String> request = logContext.get();
log.info("Feign-Error -> \npath -> {}\nmethod -> {}\nheaders -> {}\nrequest -> {}\n",
request.get(PATH), request.get(METHOD), request.get(HEADER), request.get(REQUEST_BODY)
);
logContext.remove();
return ioe;
}
/**
* 日志打印
*
* @param request
* @param responseStatus
* @param responseBody
*/
private void log(Map<String, String> request, Integer responseStatus, String responseBody) {
log.info("\n" +
"\npath -> {}" +
"\ntime -> {}" +
"\nmethod -> {}" +
"\nstatus -> {}" +
"\nheaders -> {}" +
"\nrequest -> {}" +
"\nresponse -> {}",
request.get(PATH),
(System.currentTimeMillis() - Long.parseLong(request.get(TIME))) + "ms",
request.get(METHOD),
responseStatus,
request.get(HEADER),
request.get(REQUEST_BODY),
responseBody
);
}
@Override
protected void log(String configKey, String format, Object... args) {
if (log.isInfoEnabled()) {
log.info(String.format(methodTag(configKey) + format, args));
}
}
/**
* 输入流转byte[]
*
* @param inStream 文件流内容
* @return
*/
public static byte[] streamToByteArray(InputStream inStream) {
if (inStream == null) {
return null;
}
byte[] in2b = null;
BufferedInputStream in = new BufferedInputStream(inStream);
ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
int rc = 0;
try {
while ((rc = in.read()) != -1) {
swapStream.write(rc);
}
in2b = swapStream.toByteArray();
} catch (IOException e) {
log.warn("streamToByteArray exception inStream:[{}]", inStream, e);
} finally {
closeIo(inStream, in, swapStream);
}
return in2b;
}
/**
* 关闭流
*/
public static void closeIo(Closeable... closeable) {
if (null == closeable || closeable.length <= 0) {
return;
}
for (Closeable cb : closeable) {
try {
if (null == cb) {
continue;
}
cb.close();
} catch (IOException e) {
throw new RuntimeException(
FileUtil.class.getName(), e);
}
}
}
}
打印结果如下