Feign如何使用protobuf的类作为参数调用

 更新时间:2022年03月17日 09:58:00   作者:宝贝等等我  
这篇文章主要介绍了Feign如何使用protobuf的类作为参数调用,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

这两天在把原来的项目迁移到spring cloud上,微服务之间的数据传输使用protobuf。

码了几天的代码。重要准备上线测试下微服务之间的接口调用功能,但是在用feign调用数据库代理接口时,总是报一个错误,在这记录下。

feign客户端接口定义如下

@FeignClient(name = "db-proxy")
public interface RemoteRestFull {
    @RequestMapping(value = "/getUser")
    User.UserInfoRsp getUserInfo(@RequestBody User.UserInfoReq req);
}

服务端接口实现如下

@RestController
public class UserRestFull {
    @RequestMapping("/getUser")
    public User.UserInfoRsp getUserInfo(@RequestBody User.UserInfoReq req) {
        .......
    }
}

服务端具体代码实现就省略了。

服务启动后,使用测试代码测试连接,发送消息。

在feign接口调用时

出现下面的错误:

因为涉及到具体的类报错,省略了一些信息。。。

[WARN ][2021-01-04 14:10:38][DefaultChannelPipeline.java:1152]:onUnhandledInboundException - An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exception.
feign.codec.EncodeException: Error converting request body
        at org.springframework.cloud.openfeign.support.SpringEncoder.encode(SpringEncoder.java:119) ~[spring-cloud-openfeign-core-2.2.5.RELEASE.jar!/:2.2.5.RELEASE]
        at org.springframework.cloud.openfeign.support.PageableSpringEncoder.encode(PageableSpringEncoder.java:101) ~[spring-cloud-openfeign-core-2.2.5.RELEASE.jar!/:2.2.5.RELEASE]
        at feign.ReflectiveFeign$BuildEncodedTemplateFromArgs.resolve(ReflectiveFeign.java:380) ~[feign-core-10.10.1.jar!/:?]
        at feign.ReflectiveFeign$BuildTemplateByResolvingArgs.create(ReflectiveFeign.java:232) ~[feign-core-10.10.1.jar!/:?]
        at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:84) ~[feign-core-10.10.1.jar!/:?]
        at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:100) ~[feign-core-10.10.1.jar!/:?]
        at com.sun.proxy.$Proxy93.getUser(Unknown Source) ~[?:?]
        ......................
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) [netty-all-4.1.54.Final.jar!/:4.1.54.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) [netty-all-4.1.54.Final.jar!/:4.1.54.Final]
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) [netty-all-4.1.54.Final.jar!/:4.1.54.Final]
        at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:324) [netty-all-4.1.54.Final.jar!/:4.1.54.Final]
        at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:296) [netty-all-4.1.54.Final.jar!/:4.1.54.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) [netty-all-4.1.54.Final.jar!/:4.1.54.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) [netty-all-4.1.54.Final.jar!/:4.1.54.Final]
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) [netty-all-4.1.54.Final.jar!/:4.1.54.Final]
        at io.netty.handler.logging.LoggingHandler.channelRead(LoggingHandler.java:271) [netty-all-4.1.54.Final.jar!/:4.1.54.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) [netty-all-4.1.54.Final.jar!/:4.1.54.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) [netty-all-4.1.54.Final.jar!/:4.1.54.Final]
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) [netty-all-4.1.54.Final.jar!/:4.1.54.Final]
        at io.netty.handler.codec.ByteToMessageDecoder.handlerRemoved(ByteToMessageDecoder.java:253) [netty-all-4.1.54.Final.jar!/:4.1.54.Final]
        at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:508) [netty-all-4.1.54.Final.jar!/:4.1.54.Final]
        at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:440) [netty-all-4.1.54.Final.jar!/:4.1.54.Final]
        at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:276) [netty-all-4.1.54.Final.jar!/:4.1.54.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) [netty-all-4.1.54.Final.jar!/:4.1.54.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) [netty-all-4.1.54.Final.jar!/:4.1.54.Final]
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) [netty-all-4.1.54.Final.jar!/:4.1.54.Final]
        at io.netty.handler.logging.LoggingHandler.channelRead(LoggingHandler.java:271) [netty-all-4.1.54.Final.jar!/:4.1.54.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) [netty-all-4.1.54.Final.jar!/:4.1.54.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) [netty-all-4.1.54.Final.jar!/:4.1.54.Final]
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) [netty-all-4.1.54.Final.jar!/:4.1.54.Final]
        at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) [netty-all-4.1.54.Final.jar!/:4.1.54.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) [netty-all-4.1.54.Final.jar!/:4.1.54.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) [netty-all-4.1.54.Final.jar!/:4.1.54.Final]
        at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) [netty-all-4.1.54.Final.jar!/:4.1.54.Final]
        at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166) [netty-all-4.1.54.Final.jar!/:4.1.54.Final]
        at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:719) [netty-all-4.1.54.Final.jar!/:4.1.54.Final]
        at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:655) [netty-all-4.1.54.Final.jar!/:4.1.54.Final]
        at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:581) [netty-all-4.1.54.Final.jar!/:4.1.54.Final]
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493) [netty-all-4.1.54.Final.jar!/:4.1.54.Final]
        at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989) [netty-all-4.1.54.Final.jar!/:4.1.54.Final]
        at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) [netty-all-4.1.54.Final.jar!/:4.1.54.Final]
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) [netty-all-4.1.54.Final.jar!/:4.1.54.Final]
        at java.lang.Thread.run(Thread.java:748) [?:1.8.0_231]
Caused by: org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class com.google.protobuf.UnknownFieldSet$Parser]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class com.google.protobuf.UnknownFieldSet$Parser and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: cn.nodetec.easychat.proto.IMUser$IMUserLogin["unknownFields"]->com.google.protobuf.UnknownFieldSet["parserForType"])
        at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:348) ~[spring-web-5.2.10.RELEASE.jar!/:5.2.10.RELEASE]
        at org.springframework.http.converter.AbstractGenericHttpMessageConverter.write(AbstractGenericHttpMessageConverter.java:104) ~[spring-web-5.2.10.RELEASE.jar!/:5.2.10.RELEASE]
        at org.springframework.cloud.openfeign.support.SpringEncoder.checkAndWrite(SpringEncoder.java:176) ~[spring-cloud-openfeign-core-2.2.5.RELEASE.jar!/:2.2.5.RELEASE]
        at org.springframework.cloud.openfeign.support.SpringEncoder.encode(SpringEncoder.java:109) ~[spring-cloud-openfeign-core-2.2.5.RELEASE.jar!/:2.2.5.RELEASE]
        ... 46 more
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class com.google.protobuf.UnknownFieldSet$Parser and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: cn.nodetec.easychat.proto.IMUser$IMUserLogin["unknownFields"]->com.google.protobuf.UnknownFieldSet["parserForType"])
        at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:77) ~[jackson-databind-2.11.3.jar!/:2.11.3]
        at com.fasterxml.jackson.databind.SerializerProvider.reportBadDefinition(SerializerProvider.java:1277) ~[jackson-databind-2.11.3.jar!/:2.11.3]
        at com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:400) ~[jackson-databind-2.11.3.jar!/:2.11.3]
        at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:71) ~[jackson-databind-2.11.3.jar!/:2.11.3]
        at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.serialize(UnknownSerializer.java:33) ~[jackson-databind-2.11.3.jar!/:2.11.3]
        at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728) ~[jackson-databind-2.11.3.jar!/:2.11.3]
        at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:755) ~[jackson-databind-2.11.3.jar!/:2.11.3]
        at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178) ~[jackson-databind-2.11.3.jar!/:2.11.3]
        at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728) ~[jackson-databind-2.11.3.jar!/:2.11.3]
        at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:755) ~[jackson-databind-2.11.3.jar!/:2.11.3]
        at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178) ~[jackson-databind-2.11.3.jar!/:2.11.3]
        at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480) ~[jackson-databind-2.11.3.jar!/:2.11.3]
        at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:319) ~[jackson-databind-2.11.3.jar!/:2.11.3]
        at com.fasterxml.jackson.databind.ObjectWriter$Prefetch.serialize(ObjectWriter.java:1516) ~[jackson-databind-2.11.3.jar!/:2.11.3]
        at com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:1006) ~[jackson-databind-2.11.3.jar!/:2.11.3]
        at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:342) ~[spring-web-5.2.10.RELEASE.jar!/:5.2.10.RELEASE]
        at org.springframework.http.converter.AbstractGenericHttpMessageConverter.write(AbstractGenericHttpMessageConverter.java:104) ~[spring-web-5.2.10.RELEASE.jar!/:5.2.10.RELEASE]
        at org.springframework.cloud.openfeign.support.SpringEncoder.checkAndWrite(SpringEncoder.java:176) ~[spring-cloud-openfeign-core-2.2.5.RELEASE.jar!/:2.2.5.RELEASE]
        at org.springframework.cloud.openfeign.support.SpringEncoder.encode(SpringEncoder.java:109) ~[spring-cloud-openfeign-core-2.2.5.RELEASE.jar!/:2.2.5.RELEASE]
        ... 46 more

从log看,是AbstractJackson2HttpMessageConverter转换的问题,感谢度娘。让我找到了下面的解决方法。

这个问题主要是因为,feign的参数使用protobuf的类,在转换的时候,找不到Converter,如果是使用基本的数据类型是没有任何问题的。

所以,我们需要配置一个protobuf的HttpMessageConverter。

feign客户端增加一个配置如下

import feign.codec.Decoder;
import feign.codec.Encoder;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.support.ResponseEntityDecoder;
import org.springframework.cloud.openfeign.support.SpringDecoder;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter;
 
@Configuration
public class ProtoFeignConfig {
    @Autowired
    private ObjectFactory<HttpMessageConverters> messageConverterObjectFactory;
 
    @Bean
    public ProtobufHttpMessageConverter protobufHttpMessageConverter() {
        return new ProtobufHttpMessageConverter();
    }
 
    @Bean
    public Encoder springEncoder() {
        return new SpringEncoder(this.messageConverterObjectFactory);
    }
 
    @Bean
    public Decoder springDecoder() {
        return new ResponseEntityDecoder(new SpringDecoder(this.messageConverterObjectFactory));
    }
}

服务端增加一个配置如下

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter;
 
@Configuration
public class ProtoFeignConfig {
    @Bean
    public ProtobufHttpMessageConverter protobufHttpMessageConverter() {
        return new ProtobufHttpMessageConverter();
    }
}

当然,pom中使用的是openfeign,依赖如下,注意

不要加入版本

否则编译报错:

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

这样就可以正常调用了。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Mybatis控制台打印Sql语句的实现代码

    Mybatis控制台打印Sql语句的实现代码

    MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架,下面给大家介绍Mybatis控制台打印Sql语句的实现代码,非常不错,感兴趣的朋友一起看下吧
    2016-07-07
  • Java数据结构及算法实例:汉诺塔问题 Hanoi

    Java数据结构及算法实例:汉诺塔问题 Hanoi

    这篇文章主要介绍了Java数据结构及算法实例:汉诺塔问题 Hanoi,本文直接给出实现代码,代码中包含大量注释,需要的朋友可以参考下
    2015-06-06
  • 简单了解SpringBoot HATEOAS使用方法

    简单了解SpringBoot HATEOAS使用方法

    这篇文章主要介绍了简单了解SpringBoot HATEOAS使用方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-10-10
  • java中删除 数组中的指定元素方法

    java中删除 数组中的指定元素方法

    下面小编就为大家带来一篇java中删除 数组中的指定元素方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-01-01
  • 原因分析IDEA导入Spring-kafka项目Gradle编译失败

    原因分析IDEA导入Spring-kafka项目Gradle编译失败

    这篇文章主要为大家介绍分析了IDEA导入Spring-kafka项目Gradle中编译失败原因及解决,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步
    2022-02-02
  • 记一次springboot服务凌晨无故宕机问题的解决

    记一次springboot服务凌晨无故宕机问题的解决

    这篇文章主要介绍了记一次springboot服务凌晨无故宕机问题的解决,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-09-09
  • 设计模式之中介者模式_动力节点Java学院整理

    设计模式之中介者模式_动力节点Java学院整理

    这篇文章主要为大家详细介绍了设计模式之中介者模式的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-08-08
  • Java死锁的产生原因及解决方法总结

    Java死锁的产生原因及解决方法总结

    Java中的死锁是指多个线程同时占用一些共享资源且彼此相互等待,从而导致所有的线程都被阻塞,不能继续执行程序的情况,本文小编给大家介绍了Java死锁的产生原因及解决方法总结,需要的朋友可以参考下
    2023-11-11
  • java实现字符串like和not like的使用示例

    java实现字符串like和not like的使用示例

    在Java中,我们经常需要对字符串进行模式匹配操作,字符串的模式匹配通常使用like和not like这两个运算符进行,本文就来介绍一下如何实现,感兴趣的可以了解一下
    2023-09-09
  • MyBatis-Plus之@TableField的用法解读

    MyBatis-Plus之@TableField的用法解读

    这篇文章主要介绍了MyBatis-Plus之@TableField的用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11

最新评论