Feign如何使用protobuf的類作為參數(shù)調(diào)用
這兩天在把原來的項目遷移到spring cloud上,微服務之間的數(shù)據(jù)傳輸使用protobuf。
碼了幾天的代碼。重要準備上線測試下微服務之間的接口調(diào)用功能,但是在用feign調(diào)用數(shù)據(jù)庫代理接口時,總是報一個錯誤,在這記錄下。
feign客戶端接口定義如下
@FeignClient(name = "db-proxy") public interface RemoteRestFull { ? ? @RequestMapping(value = "/getUser") ? ? User.UserInfoRsp getUserInfo(@RequestBody User.UserInfoReq req); }
服務端接口實現(xiàn)如下
@RestController public class UserRestFull { ? ? @RequestMapping("/getUser") ? ? public User.UserInfoRsp getUserInfo(@RequestBody User.UserInfoReq req) { ? ? ? ? ....... ? ? } }
服務端具體代碼實現(xiàn)就省略了。
服務啟動后,使用測試代碼測試連接,發(fā)送消息。
在feign接口調(diào)用時
出現(xiàn)下面的錯誤:
因為涉及到具體的類報錯,省略了一些信息。。。
[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轉(zhuǎn)換的問題,感謝度娘。讓我找到了下面的解決方法。
這個問題主要是因為,feign的參數(shù)使用protobuf的類,在轉(zhuǎn)換的時候,找不到Converter,如果是使用基本的數(shù)據(jù)類型是沒有任何問題的。
所以,我們需要配置一個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>
這樣就可以正常調(diào)用了。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
手寫redis@Cacheable注解?參數(shù)java對象作為key值詳解
這篇文章主要介紹了手寫redis@Cacheable注解?參數(shù)java對象作為key值詳解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01springboot快速整合Mybatis組件的方法(推薦)
Spring Boot是由Pivotal團隊提供的全新框架,其設(shè)計目的是用來簡化新Spring應用的初始搭建以及開發(fā)過程。這篇文章主要介紹了springboot快速整合Mybatis組件的方法,需要的朋友可以參考下2019-11-11詳解Java的Hibernat框架中的Map映射與SortedMap映射
這篇文章主要介紹了Java的Hibernat框架中的Map映射與SortedMap映射,Hibernat是Java的SSH三大web開發(fā)框架之一,需要的朋友可以參考下2015-12-12Java基于ServletContextListener實現(xiàn)UDP監(jiān)聽
這篇文章主要介紹了Java基于ServletContextListener實現(xiàn)UDP監(jiān)聽,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2019-12-12