首页 > 其他分享 >使用feign打印钉钉告警get请求转post以及stream is close 问题

使用feign打印钉钉告警get请求转post以及stream is close 问题

时间:2022-10-18 19:35:57浏览次数:68  
标签:body feign String stream get request import response

                   在使用feign使用get请求时候,如果是get请求且使用了@RequestBody参数,则接受方会认为是post请求。解决方式是不使用@RequestBody注解。使用@RequestParam

@RequestMapping(path = "{beanThiredApiName}",method = RequestMethod.GET)
<T> BeanTechResponse<T>  getBeanThiredFeign(@PathVariable("beanThiredApiName")String beanThiredApiName, @RequestHeader Map<String, String> headerMap, @RequestParam(required = false) Map<String, String> requestParam);

      之前记录了,在使用feign 的response 时候,使用钉钉打印信息时候,如果使用了 response.body().asInputStream() ,则会出现 stream is close 的问题。这几天这个问题本地一直复现,且一直未能真正解决。

      今天又重新看了github issue的一个回答:(https://github.com/OpenFeign/feign/issues/1187)

 

   

@karantalasila

Short Answer: Use a custom Logger

Long Answer

Our InputStream support on the Response relies on reading the entire stream into memory then providing access to it. Since these network streams do not support marking, they can be read only once. What we provide instead is a method on the Response#isRepeatable. This will let you know that you can read the Response more than once.

With that said, our Logger component has the capability you are looking for. It will log at specific points within the request lifecycle and has support for reading a Response more than once. I recommend taking a look at our documentation and examples in SLF4JLogger

  翻译软件下:  

@卡拉塔拉西拉

 

简短回答:使用自定义记录器

 

冗长的回答

 

我们对Response的InputStream支持依赖于将整个流读入内存,然后提供对它的访问。由于这些网络流不支持标记,因此只能读取一次。相反,我们提供的是响应#上的方法是可重复的。这将让您知道您可以多次阅读响应。

 

也就是说,我们的Logger组件具有您想要的功能。它将在请求生命周期中的特定点登录,并支持多次读取响应。我建议看一下SLF4JLogger中的文档和示例

 

 

也就是说 他们是支持这种的,但是不是默认的。然后看了response的源码。在body的实现有两种: InputStreamBody  和 ByteArrayBody

 

 

 

 

 

  其默认实现是InputStreamBody. (client 初始化的时候)

   两者的不同点就是 isRepeatable()的值,一个是true,一个是false.  看其注释

    True if asInputStream() and asReader() can be called more than once.

    如果可以多次调用asInputStream()和asReader(),则为True。

 

   那么怎么把InputStreamBody 转换为 ByteArrayBody呢?

     因为两个都是一个接口的实现,自己刚开始想的就是使用 属性赋值的方式。 就是new 一个对象,然后把值赋进去。 结果发现有个问题是,返回值会报异常。

 

    今天是重新定义了Response,将response进行了赋值拷贝。并进行了转换。源码如下:

   

package com.gwm.marketing.restfulfeign;

import com.alibaba.fastjson.JSONObject;
import com.gwm.marketing.restfulfeign.alerm.OraRpcDingdingConfiguration;
import feign.Logger;
import feign.Request;
import feign.Response;
import org.apache.commons.io.IOUtils;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;

/**
 * @author fanht
 * @descrpiton
 * @date 2022/7/26 14:09:39
 * @versio 1.0
 */
@Configuration
public class OraFeignLogger extends Logger {

    private static final int PASS_STATUS= 200;
    @Resource
    private OraRpcDingdingConfiguration oraRpcDingdingConfiguration;

    @Override
    protected Response logAndRebufferResponse(String configKey,
                                              Level logLevel,
                                              feign.Response response,
                                              long elapsedTime) throws IOException {
        /**todo 此处传的response 只能读一次,body底层用的是InputStream。里面有个isRepeatable()属性,属性为false.
         * True if asInputStream() and asReader() can be called more than once. 此处用了另外的实现,ByteArrayBody 这个属性默认是true。其实也可以自定义(比如OraResponse),不过已经
         * 有了,就不在重复造轮子了
         *  https://github.com/OpenFeign/feign/issues/1187*/
        Response byteResponse = Response.builder().status(response.status()).reason(response.reason()).request(response.request()).headers(response.headers())
                .body(IOUtils.toString(response.body().asInputStream(),StandardCharsets.UTF_8),StandardCharsets.UTF_8).build();
        if(PASS_STATUS != byteResponse.status()){
            Request request = byteResponse.request();
            String requestMsg = request.httpMethod().name() + " " + request.url() + " HTTP/1.1";
            String bodyMsg = request.body() != null ? new String(request.body()): "";
            String message = "";
            if(byteResponse.body() != null){
                String returnRes = IOUtils.toString(byteResponse.body().asReader(StandardCharsets.UTF_8)).substring(0,3000);
                message = MessageFormat.format("调用三方接口异常告警:项目:{0},环境:{1},IP:{2},接口请求方式以及请求地址:{3},耗时时间:{4}毫秒,请求body入参:{5},三方返回信息:{6}",
                        OraRpcDingdingConfiguration.applicationName, OraRpcDingdingConfiguration.env, OraIpUtil.initIp() , requestMsg, elapsedTime, StringUtils.isEmpty(bodyMsg)?"请求参数为空":bodyMsg,returnRes);
            } else {
                message = MessageFormat.format("调用三方接口异常告警:项目:{0},环境:{1},IP:{2},接口请求方式以及请求地址:{3},耗时时间:{4}毫秒,请求body入参:{5}",
                        OraRpcDingdingConfiguration.applicationName, OraRpcDingdingConfiguration.env, OraIpUtil.initIp() , requestMsg, elapsedTime,StringUtils.isEmpty(bodyMsg)?"请求body参数为空":bodyMsg);
            }
            sendDingdingAlerm(message,
                    oraRpcDingdingConfiguration.getToken().getUrl());

        } else if (elapsedTime > oraRpcDingdingConfiguration.getRpc().getRpcTimeOut()) {
            Request request = byteResponse.request();
            String requestMsg = request.httpMethod().name() + " " + request.url() + " HTTP/1.1";
            String bodyMsg = request.body() != null ? new String(request.body()): "";
            String message = "";
            try {
                String returns = IOUtils.toString(byteResponse.body().asReader(StandardCharsets.UTF_8));
                //todo 不去解析bodyData,因为在debug模式下会出现 stream is close的问题 虽然能解决(参考解决方案:https://github.com/OpenFeign/feign/issues/1208),但是debug模式经常使用 不建议。这样会导致的问题是 钉钉告警不能打印显示三方返回的信息
                message = MessageFormat.format("调用三方接口耗时告警:项目:{0},环境:{1},IP:{2},接口请求方式以及请求地址:{3},耗时时间:{4}毫秒,请求body入参:{5},三方返回信息:{6}",
                        OraRpcDingdingConfiguration.applicationName, OraRpcDingdingConfiguration.env, OraIpUtil.initIp() , requestMsg , elapsedTime, StringUtils.isEmpty(bodyMsg)?"请求body参数为空":bodyMsg,
                        returns);
            } catch (Exception e) {
                e.printStackTrace();
            }
            sendDingdingAlerm(message,
                    oraRpcDingdingConfiguration.getToken().getUrl());
        }
        return byteResponse;
    }

    @Override
    protected void log(String configKey, String format, Object... args) {
    }

    /**
     * 发送钉钉消息
     */
    public static void sendDingdingAlerm(String message, String dingdingTokenUrl) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("msgtype", "text");
        JSONObject content = new JSONObject();
        content.put("content", message);
        jsonObject.put("text", content);
        OraHttpClient oraHttpClient = new OraHttpClient();
        String response = null;
        try {
            String[] dingdingArr = dingdingTokenUrl.split(",");
            if (dingdingArr != null && dingdingArr.length > 0) {
                for (int i = 0; i < dingdingArr.length; i++) {
                    response = oraHttpClient.post(dingdingArr[i], jsonObject.toJSONString());
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


}

 

标签:body,feign,String,stream,get,request,import,response
From: https://www.cnblogs.com/thinkingandworkinghard/p/16803768.html

相关文章