• feign 配置使用


    简介:feign是springcloud的一个重要组件,主要功能就是服务调用,既可以用于微服务之间调用,也可以rest调用
     

    启用

    pom文件 添加依赖

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

    启动类上添加注解 @EnableFeignClients

    在这里插入图片描述
     

    微服务调用

    目标:调用微服务里用户服务的获取用户信息接口
    步骤:

    • 新建一个 interface 接口类
    • 注解上填写接口的url
    • 方法的写法类似于controller
    • 配置 application

    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传参

    KEYVALUE
    AuthorizationTOKEN
    @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

    • 配置 configuration 实现 RequestInterceptor
    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"));
        }
    }
    
    • 接口添加 configuration
    @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); } } } }

    打印结果如下
    在这里插入图片描述

  • 相关阅读:
    python tk展示图片
    C/C++ 乘积尾零问题(蓝桥杯)
    termius mac版无需登录注册直接永久使用
    CoreData教程之将核心数据coredata实体拆分到不同的store,实现一个实体与 CloudKit 公共数据库和私有数据库同步
    2022年9月1日:Visual Studio Code 中的 GitHub 简介(未做完)
    YOLOv7改进策略:RIFormerBlock助力检测|CVPR2023 RIFormer:无需TokenMixer也能达成SOTA性能的极简ViT架构
    Spring Boot集成ElasticsearchRepository
    element UI table横向树结合checkbox进行多选,实现各个节点的[全选,半选,不选]状态附带模拟数据
    markdown的学习和使用
    深入理解WPF布局子系统
  • 原文地址:https://blog.csdn.net/weixin_42555971/article/details/126936234