做文件上传功能时,dubbo对MultipartFile文件传输时,一个bug:Fail to decode request due to: RpcInvocation
三月 22, 2019 2:37:27 下午 org.apache.catalina.core.StandardWrapperValve invoke 严重: Servlet.service() for servlet [taotao-manager-web] in context with path [] threw exception [Request processing failed; nested exception is com.alibaba.dubbo.rpc.RpcException: Failed to invoke the method uploadPicture in the service com.taotao.manager.service.PictureService. Tried 3 times of the providers [192.168.138.1:20880] (1/1) from the registry 192.168.138.128:2181 on the consumer 192.168.138.1 using the dubbo version 2.5.3. Last error is: Failed to invoke remote method: uploadPicture, provider: dubbo://192.168.138.1:20880/com.taotao.manager.service.PictureService?anyhost=true&application=taotao-manager-web&check=false&dubbo=2.5.3&interface=com.taotao.manager.service.PictureService&methods=uploadPicture&pid=20660&revision=1.0-SNAPSHOT&side=consumer&timeout=1000000×tamp=1553236601190, cause: Fail to decode request due to: RpcInvocation [methodName=uploadPicture, parameterTypes=null, arguments=null, attachments={path=com.taotao.manager.service.PictureService, input=883, dubbo=2.5.3, version=0.0.0}]] with root cause com.alibaba.dubbo.remoting.RemotingException: Fail to decode request due to: RpcInvocation [methodName=uploadPicture, parameterTypes=null, arguments=null, attachments={path=com.taotao.manager.service.PictureService, input=883, dubbo=2.5.3, version=0.0.0}] at com.alibaba.dubbo.remoting.exchange.support.DefaultFuture.returnFromResponse(DefaultFuture.java:190) at com.alibaba.dubbo.remoting.exchange.support.DefaultFuture.get(DefaultFuture.java:110) at com.alibaba.dubbo.remoting.exchange.support.DefaultFuture.get(DefaultFuture.java:84) at com.alibaba.dubbo.rpc.protocol.dubbo.DubboInvoker.doInvoke(DubboInvoker.java:96) at com.alibaba.dubbo.rpc.protocol.AbstractInvoker.invoke(AbstractInvoker.java:144) at com.alibaba.dubbo.rpc.listener.ListenerInvokerWrapper.invoke(ListenerInvokerWrapper.java:74) at com.alibaba.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:75) at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) at com.alibaba.dubbo.rpc.protocol.dubbo.filter.FutureFilter.invoke(FutureFilter.java:53) at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) at com.alibaba.dubbo.rpc.filter.ConsumerContextFilter.invoke(ConsumerContextFilter.java:48) at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) at com.alibaba.dubbo.rpc.protocol.InvokerWrapper.invoke(InvokerWrapper.java:53) at com.alibaba.dubbo.rpc.cluster.support.FailoverClusterInvoker.doInvoke(FailoverClusterInvoker.java:77) at com.alibaba.dubbo.rpc.cluster.support.AbstractClusterInvoker.invoke(AbstractClusterInvoker.java:227) at com.alibaba.dubbo.rpc.cluster.support.wrapper.MockClusterInvoker.invoke(MockClusterInvoker.java:72) at com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler.invoke(InvokerInvocationHandler.java:52) at com.alibaba.dubbo.common.bytecode.proxy2.uploadPicture(proxy2.java) at com.taotao.manager.controller.PictureController.uploadPicture(PictureController.java:25) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:222) at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137) at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:814) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:737) at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:969) at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:871) at javax.servlet.http.HttpServlet.service(HttpServlet.java:647) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:845) at javax.servlet.http.HttpServlet.service(HttpServlet.java:728) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:121) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100) at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408) at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1041) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:603) at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748)
问题解决:
1、通过删代码试验得出的结论是:采用dubbo对MultipartFile文件进行传输时,会报invoke method 错误,是因为dubbo并不能跨系统传递这个对象,可以采用Hessian
<dependency> <groupId>com.caucho</groupId> <artifactId>hessian</artifactId> <version>4.0.7</version> </dependency> <dependency> <groupId>org.mortbay.jetty</groupId> <artifactId>jetty</artifactId> <version>6.1.26</version> <exclusions> <exclusion> <groupId>org.mortbay.jetty</groupId> <artifactId>servlet-api</artifactId> </exclusion> </exclusions> </dependency>
要注意,jetty包中要排除servlet-api包的依赖,否则会报错,Tomcat不能正常启动。
2、在spring配置文件里面配置文件上传
<!-- 多部分文件上传 文件上传解析器 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="104857600" />
<property name="maxInMemorySize" value="4096" />
<property name="defaultEncoding" value="UTF-8"></property>
</bean>
3、在service层就是我们的provider里面将需要的服务注册成Hessian协议的就可以了。
我的spring-service.xml
<!-- 配置dubbo --> <!-- 提供方应用信息,用于计算依赖关系 --> <dubbo:application name="taotao-manager-service" /> <!-- 使用multicast广播注册中心暴露服务地址 --> <dubbo:registry protocol="zookeeper" address="192.168.138.128:2181" /> <!-- 用dubbo协议在20880端口暴露服务 --> <dubbo:protocol name="dubbo" port="20880"/> <dubbo:protocol name="hessian" port="20887"/> <!-- 声明需要暴露的服务接口 --> <dubbo:service interface="com.taotao.manager.service.TestService" ref="testServiceImpl" /> <dubbo:service interface="com.taotao.manager.service.ItemService" ref="itemServiceImpl" /> <dubbo:service interface="com.taotao.manager.service.ItemCatService" ref="itemCatServiceImpl" /> <dubbo:service protocol="hessian" interface="com.taotao.manager.service.PictureService" ref="pictureServiceImpl" />
4、consumer消费者那儿注册一下
我的spring-mvc.xml
<!-- 配置dubbo服务 --> <dubbo:application name="taotao-manager-web" /> <!-- 使用广播 --> <dubbo:registry protocol="zookeeper" address="192.168.138.128:2181" /> <!-- 声明要调用的服务,timeout是设置连接超时最长时间,如果不设置,超时时间默认是3秒 --> <dubbo:reference interface="com.taotao.manager.service.TestService" id="testService" timeout="1000000" /> <dubbo:reference interface="com.taotao.manager.service.ItemService" id="itemService" timeout="1000000" /> <dubbo:reference interface="com.taotao.manager.service.ItemCatService" id="itemCatService" timeout="1000000" /> <dubbo:reference protocol="hessian" interface="com.taotao.manager.service.PictureService" id="pictureService" timeout="1000000" />
5、发现能传递这个对象了,但是最后发现MultipartFile.getInputStream() 拿不到InputStream了,没办法只能用byte数组来传递这个InputStream了。
- 先把上传的文件转换成二进制数组,并传递到service层
@RequestMapping(value = "/pic/upload") @ResponseBody public String uploadPicture(MultipartFile uploadFile) throws IOException { byte[] pic = uploadFile.getBytes(); PictureResult pictureResult = pictureService.uploadPicture(pic, uploadFile); String result = JsonUtils.objectToJson(pictureResult); return result; }
2.
@Override public PictureResult uploadPicture(byte[] pic, MultipartFile uploadFile) { PictureResult pictureResult = new PictureResult(); if (pic == null || pic.length == 0) { pictureResult.setError(1); pictureResult.setMessage("空文件"); return pictureResult; } //拿到上传文件的二进制数组inputStream InputStream inputStream = new ByteArrayInputStream(pic); //上传文件的名称 String oldName = uploadFile.getOriginalFilename(); //使用IDUtils工具类生成新名称 String newName = IDUtils.genImageName(); //新名称加上旧名称扩展名 newName = newName + oldName.substring(oldName.lastIndexOf(".")); //创建一个以日期命名的文件目录"/2019/3/21" String imagePath = new DateTime().toString("/yyyy/mm/dd"); //上传文件到图片服务器 boolean result = FtpUtil.uploadFile(FTP_ADRESS, FTP_PORT, FTP_USERNAME, FTP_PASSWORD, FTP_BASE_PATH, imagePath, newName, inputStream); //返回格式参考KindEditor String url = IMAGE_BASE_PATH + imagePath + "/" + newName; if (!result) { pictureResult.setError(1); pictureResult.setMessage("文件上传失败"); return pictureResult; } else { pictureResult.setError(0); pictureResult.setUrl(url); return pictureResult; } }