SpringBoot项目整合OpenFeign、实现动态IP+URL请求、自定义(编码器\解码器)

基础操作

pom依赖

OpenFeign是Spring Cloud在Feign的基础上支持了SpringMVC的注解,如@RequestMapping等等。OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中.

<dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency> 

配置application.yml

#====《跨服务HTTP接口请求工具》====
feign:
  client:
    config:
      #可指定需要配置的:服务名称
      # manage-client:
      #或 默认配置:全局
      
      default:
        #日志打印级别
        loggerLevel: basic
        #跨服务接口请求超时
        readTimeout: 5000
        #跨服务请求连接超时
        connectTimeout: 5000

服务启动类

启动类加上注解:@EnableDiscoveryClient

@SpringBootApplication
@EnableDiscoveryClient
public class OpenFeignServer {
        public static void main(String[] args) {
            SpringApplication.run(OpenFeignServer.class, args);
    }
}

基础跨服务调用

/**
 * @Author Owen
 * @Date 2020/8/18
 * @Description Admin后台管理服务
 */
@FeignClient("serverName")
public interface AdminServiceClient {


    @RequestMapping(value = "/XXX/XXXX/XXXX", method = RequestMethod.GET, produces = {"application/json"}, consumes = {"application/glue+json"})
    <T> T doGet(@RequestParam("params") String params);


    @RequestMapping(value = "/XXX/XXXX/XXXX", method = RequestMethod.PUT, produces = {"application/json"}, consumes = {"application/communication+json"})
    <T> T doPut(@RequestBody Object params);


    @RequestMapping(value = "/XXX/XXXX/XXXX", method = RequestMethod.POST, produces = {"application/json"}, consumes = {"application/communication+json"})
    <T> T doPost(@RequestBody Object params);

}

动态IP+URL请求 + 动态编码器\解码器

自定义跨服务请求,Encoder编译器

import com.details.utils.ConvertUtils;
import com.details.utils.JsonUtils;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import feign.RequestTemplate;
import feign.Util;
import feign.codec.EncodeException;
import feign.codec.Encoder;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;

import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Map;

/**
 * @author Owen
 * @date 2022/10/22 21:10
 * @Description:自定义Feign跨服务请求,编译器
 */
@Slf4j
public class FeignEncoder implements Encoder {
    ObjectMapper mapper = new ObjectMapper();

    @SneakyThrows
    @Override
    public void encode(Object object, Type bodyType, RequestTemplate template) {
        log.info("Feign requset encode details: {}", object);
        try {
            //获取请求头详情
            Map<String, Collection<String>> headers = template.headers();
            //查看请求方式
            Collection<String> contentType = headers.get("Content-Type");

            //Json请求方式
            if (CollectionUtils.isNotEmpty(contentType) && contentType.contains("application/json") && !(object instanceof String)) {
                template.body(JsonUtils.object2Json(object));
            }
            //字符串
            else if (object instanceof String) {
                template.body(object.toString());
            } else {
                JavaType javaType = this.mapper.getTypeFactory().constructType(bodyType);
                template.body(ConvertUtils.writeAsBytes(javaType, object), Util.UTF_8);
            }
        } catch (Exception e) {
            throw new EncodeException(String.format("%s is not a type supported by this encoder.", object.getClass()));
        }
    }
}

自定义跨服务响应,Decoder 解码器

import feign.Response;
import feign.codec.Decoder;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Type;

/**
 * @author Owen
 * @date 2022/10/22 21:10
 * @Description:自定义Feign跨服务响应,解码器
 */
@Slf4j
public class FeignDecoder implements Decoder {
    @Override
    public Object decode(Response response, Type type) {
        log.info("URL:[{}] response result: {}", response.request().url(), response);
        return response.body();
    }
}

方式1:FeignFactory.class

动态URL

import feign.*;
import java.net.URI;

/**
 * @Author: Owen
 * @Date: 2022/10/22
 * @Description:Feign公共接口(自定义请求API,内部请求)
 */
public interface FeignFactory {

    /**
     * @param host 接口主机地址,如:http://localhost:8080,该参数是实现动态URL的关键
     * @param path 接口路径,如:/test/hello
     * @param body 请求消息体对象
     * @Author: Owen
     * @Date: 2022/10/22
     * @Description:通用POST请求,请求消息体格式为JSON
     */
    @RequestLine("POST {path}")
    @Headers({"Content-Type: application/json", "Accept: application/json"})
    <T> T doPost(URI host,@Param("path") String path, Object body);

    /**
     * @param host 接口主机地址
     * @param path 接口路径,如:/test/hello
     * @Author: Owen
     * @Date: 2022/10/22
     * @Description:通用Get请求
     */
    @RequestLine("POST {path}")
    <T> T doGet(URI host, @Param("path") String path);

}

工具类: FeignUtils.class

import com.alibaba.nacos.api.naming.pojo.Instance;
import com.details.nacos.NacosFactory;
import com.details.utils.CacheUtils;
import com.details.utils.ExceptionUtils;
import com.details.utils.IocUtils;
import com.details.utils.TcpIpUtils;
import feign.Feign;
import feign.Logger;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.slf4j.Slf4jLogger;
import lombok.extern.slf4j.Slf4j;
import java.net.URI;
import java.util.*;

/**
 * @author: Owen
 * @date: 2021/3/24
 * @description:跨服务API请求工具 集成Spring openfeign各个功能
 * 动态设置 编译器 解码器 熔断器
 * 跨服务器 动态API请求 自定义构建 IP(单机\集群)+服务名
 */
@Slf4j
public class FeignUtils {
    private static  Feign.Builder  feignClient = null; 
    
    /**
     * @param ip   对应服务器公网IP
     * @param path API接口地址
     * @Author: Owen
     * @Date: 2022/10/22
     * @Description:通用Get请求
     */
    public static <T> T doGet(String ip, String path) {
        T result = null;
        ExceptionUtils.isBlank(ip, "Params :[ip] is null !");
        ExceptionUtils.isBlank(ip, "Params :[path] is null !");
        ExceptionUtils.isTrue(!ip.contains(":"), "Ip port not null!");
        ExceptionUtils.isTrue(!ip.contains("http://"), "Ip formal error, lack \"http://\"");

        try {
            //构建Feign基础实例
            Feign.Builder client = feigenClient();
            ExceptionUtils.isNull(client, "Feign client is null !");
            //自定义配置
            client.encoder(new FeignEncoder()); //API请求 编译器
            client.decoder(new FeignDecoder()); //API响应 解码器
            client.logger(new Slf4jLogger());   //日志
            client.logLevel(Logger.Level.FULL); //日志级别

            //构建工厂
            //注意:这里的url参数不能为空字符串,但是可以设置为任意字符串值,在这里设置为“EMPTY”
            FeignFactory factory = client.target(FeignFactory.class, "EMPTY");
            //跨服务请求
            result = factory.doGet(URI.create(ip), path);
        } catch (Exception e) {
            log.error("HTTP get requset faild: {}", e.getMessage());
        }
        return result;
    }

    /**
     * @param ip   对应服务器公网IP
     * @param path API接口地址
     * @param body Json数据体
     * @Author: Owen
     * @Date: 2022/10/22
     * @Description:跨服务POST请求接口,类型:application/json
     */
    public static <T> T doPost(String ip, String path, Object body) {
        T result = null;
        ExceptionUtils.isBlank(ip, "Params :[ip] is null !");
        ExceptionUtils.isBlank(ip, "Params :[path] is null !");
        ExceptionUtils.isNull(body, "Params :[body] is null !");
        ExceptionUtils.isTrue(!ip.contains(":"), "ip port not null!");
        ExceptionUtils.isTrue(!ip.contains("http://"), "Ip formal error, lack \"http://\"");

        try {
            //构建Feign基础实例
            Feign.Builder client = feigenClient();
            ExceptionUtils.isNull(client, "Feign client is null !");
            //自定义配置
            client.encoder(new FeignEncoder()); //API请求 编译器
            client.decoder(new FeignDecoder()); //API响应 解码器
            client.logger(new Slf4jLogger());   //日志
            client.logLevel(Logger.Level.FULL); //日志级别

            FeignFactory factory = client.target(FeignFactory.class, ip);
            //跨服务请求
            result = factory.doPost(URI.create(ip), path, body);
        } catch (Exception e) {
            log.error("HTTP post requset faild: {}", e.getMessage());
        }
        return result;
    }



    /**
     * @author: Owen
     * @date: 2020/12/4
     * @description:获取Feign实例(单例模式)
     */
    private static Feign.Builder feigenClient() {
        try {
            //feign客户端(http跨服务请求 单例模式)
            if (Objects.isNull(feigenClient)) {//第一次判空
                //保证线程安全
                synchronized (Feign.Builder.class) {
                    //第二次判空,保证单例对象的唯一性,防止第一次有多个线程进入第一个if判断
                    if (Objects.isNull(feigenClient)) {
                        try {
                            feigenClient = Feign.builder();
                        } catch (Exception e) {
                            log.error("Feigen client initialize fail: " +e.getMessage());
                        }
                    }
                }
            }
        } catch (Exception e) {
            log.error("Feigen client get faild: {}", e.getMessage());
        }
        return feigenClient;
    }
}

方式2:FeignApiFactory.class

import feign.*;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.slf4j.Slf4jLogger;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.context.annotation.Bean;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import java.net.URI;

/**
 * @Author: Owen
 * @Date: 2022/10/22
 * @Description:Feign公共接口(走网关转发)
 */
@FeignClient(value = "server-gateway", url = "EMPTY", configuration = FeignApiFactory.CallbackConfiguration.class)
public interface FeignApiFactory {
    /**
     * @param host 接口主机地址,如:http://localhost:8080
     * @param path 接口路径,如:/test/hello
     * @param body 请求消息体对象
     * @Author: Owen
     * @Date: 2022/10/22
     * @Description: 统一回调接口方法,请求消息体格式为JSON,响应消息体格式也为JSON
     */
    @RequestMapping(value = "{path}", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    <T> T doPost(URI host, @PathVariable("path") String path, @RequestBody Object body);

/*
==========《Deme》==========:
    String uri = "http://localhost:8080";
    Map<String, Object> queryMap = new HashMap<>(0);
    queryMap.put("k","v");
    Map<String,Object> body = new hashMap<String,Object>();
    body.put("params1","value");
    Object result = this.callbackAPI.callback(URI.create(uri), "/test/apiTest", queryMap, body);
*/

    //自定义回调接口配置
    @Component
    class CallbackConfiguration {
        @Bean
        public Encoder feignEncoder() {
            return new FeignEncoder();
        }

        @Bean
        public Decoder feignDecoder() {
            return new FeignDecoder();
        }

        @Bean
        public Retryer feignRetryer() {
            return new Retryer.Default();
        }

        @Bean
        public Logger feignLogger() {
            return new Slf4jLogger();
        }

        @Bean
        public Logger.Level feignLoggerLevel() {
            return Logger.Level.FULL;
        }
    }
}
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_Owen__

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值