28.扩展支持HTTP协议
关于dubbo对http协议的支持,要分几个维度说:
- 仅仅传输协议使用http,报文的组织方式没有要求,那么dubbo这个版本(2.5.3)原生提供的http协议就能满足。适当配置即可。此处报文指http请求体与响应体的报文。http协议本身规定了其报文规范,诸如method、url、content-type,header等等,但是其请求体与响应体中内容你可以随意放置。
- 传输协议使用http,报文组织希望使用json/xml,那么可以使用当当的dubbox,dubbo在2.6.0之后合入了。使用入门文档。这种形式还分三种:
- 非dubbo的消费端调用dubbo的REST服务(non-dubbo –> dubbo)
- dubbo消费端调用dubbo的REST服务 (dubbo –> dubbo)
- dubbo的消费端调用非dubbo的REST服务 (dubbo –> non-dubbo)
- 其他的一些玩法:基于spring mvc的玩法 基于RestExpress的玩法 基于Servlet的玩法
dubbo原生支持的http协议
case配置
在META-INF目录下放置名为com.alibaba.dubbo.rpc.Protocol的文件,里面写上
http=com.alibaba.dubbo.rpc.protocol.http.HttpProtocol
配置文件中声明协议使用http
<dubbo:protocol name="http" port="10880"/>
pom中增加spring-web 3.x和jetty等依赖,如果用spring-web4.x的话会有个别类不能发现。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>3.2.18.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty</artifactId>
<version>6.1.26</version>
</dependency>
简单分析其实现
默认的http协议支持,仅仅是传输走http协议的方式,报文的组织方式是用的spring-web中的java序列化的方式。
实现主要依赖spring-web
3.x版本加jetty
实现。
用wireshark抓到的请求报文如下:
POST /com.code260.ss.dubbo.demov.facade.service.HelloService HTTP/1.1
Content-Type: application/x-java-serialized-object
Accept-Encoding: gzip
User-Agent: Java/1.8.0_121
Host: 172.17.0.224:10880
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive
Content-Length: 343
....sr.5org.springframework.remoting.support.RemoteInvocation_l...
.
...[. argumentst..[Ljava/lang/Object;L.
attributest..Ljava/util/Map;L.
methodNamet..Ljava/lang/String;[..parameterTypest..[Ljava/lang/Class;xpur..[Ljava.lang.Object;..X..s)l...xp....t..Simonpt..sayHellour..[Ljava.lang.Class;......Z....xp....vr..java.lang.String...8z;.B...xpHTTP/1.1 200 OK
Content-Type: application/x-java-serialized-object
Transfer-Encoding: chunked
Server: Jetty(6.1.26)
....sr.;org.springframework.remoting.support.RemoteInvocationResult.....IJm...L. exceptiont..Ljava/lang/Throwable;L..valuet..Ljava/lang/Object;xppp
provider端解码请求的堆栈:
HttpInvokerServiceExporter(RemoteInvocationSerializingExporter).doReadRemoteInvocation(ObjectInputStream) line: 142
HttpInvokerServiceExporter.readRemoteInvocation(HttpServletRequest, InputStream) line: 121
HttpInvokerServiceExporter.readRemoteInvocation(HttpServletRequest) line: 100
HttpInvokerServiceExporter.handleRequest(HttpServletRequest, HttpServletResponse) line: 77
HttpProtocol$InternalHandler.handle(HttpServletRequest, HttpServletResponse) line: 81
DispatcherServlet.service(HttpServletRequest, HttpServletResponse) line: 64
DispatcherServlet(HttpServlet).service(ServletRequest, ServletResponse) line: 770
ServletHolder.handle(ServletRequest, ServletResponse) line: 511
ServletHandler.handle(String, HttpServletRequest, HttpServletResponse, int) line: 401
Server(HandlerWrapper).handle(String, HttpServletRequest, HttpServletResponse, int) line: 152
Server.handle(HttpConnection) line: 322
HttpConnection.handleRequest() line: 542
HttpConnection$RequestHandler.content(Buffer) line: 945
HttpParser.parseNext() line: 756
HttpParser.parseAvailable() line: 218
HttpConnection.handle() line: 404
SelectChannelConnector$ConnectorEndPoint(SelectChannelEndPoint).run() line: 410
QueuedThreadPool$PoolThread.run() line: 582
解码的代码由spring-web完成,代码如下:
protected RemoteInvocation doReadRemoteInvocation(ObjectInputStream ois)
throws IOException, ClassNotFoundException {
141 Object obj = ois.readObject();
142 if (!(obj instanceof RemoteInvocation)) {
143 throw new RemoteException("Deserialized object needs to be assignable to type [" +
RemoteInvocation.class.getName() + "]: " + obj);
}
return (RemoteInvocation) obj;
}
141行从流中读出来的这个obj(反序列化出来)就是RemoteInvocation。这个RemoteInvocation中有方法名、参数类型、参数值等,调试其看到其信息如下:
[Simon]
sayHello
class java.lang.String
141行的ois是org.springframework.remoting.rmi.CodebaseAwareObjectInputStream。
dubbo合入dubbox之后http rest的支持
case配置
dubbo2.5.3在此处用不了了,我们换成2.6.8。
pom依赖
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.code260.ss</groupId>
<artifactId>dubbox-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<dependencies>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-client</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>3.2.18.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.2.18.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>3.2.18.RELEASE</version>
</dependency>
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty</artifactId>
<version>6.1.26</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>3.0.7.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-client</artifactId>
<version>3.0.7.Final</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.0.0.GA</version>
</dependency>
<!-- 如果要使用json序列化 -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson-provider</artifactId>
<version>3.0.7.Final</version>
</dependency>
<!-- 如果要使用xml序列化 -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxb-provider</artifactId>
<version>3.0.7.Final</version>
</dependency>
<!-- 如果要使用netty server -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-netty</artifactId>
<version>3.0.7.Final</version>
</dependency>
<!-- 如果要使用Sun HTTP server -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jdk-http</artifactId>
<version>3.0.7.Final</version>
</dependency>
<!-- 如果要使用tomcat server -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>8.0.11</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-logging-juli</artifactId>
<version>8.0.11</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.3</version>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
<configuration>
<delimiters>
<delimiter>@</delimiter>
</delimiters>
<useDefaultDelimiters>false</useDefaultDelimiters>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
<modules>
<module>dubbox-demo-v-facade</module>
<module>dubbox-demo-v-server</module>
<module>dubbox-demo-v-client</module>
</modules>
</project>
接口配置
@Path("hello")
public interface HelloService
{
@POST
@Path("sayHello")
@Consumes({MediaType.APPLICATION_JSON})
public void sayHello(HelloReq req);
}
协议配置
<dubbo:protocol name="rest" port="8090"/>
简单分析其实现
wireshark抓到的报文
POST /hello/sayHello HTTP/1.1
Accept-Encoding: gzip, deflate
Content-Type: application/json
Content-Length: 16
Host: 172.17.0.224:8090
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.5.3 (Java/1.8.0_121)
{"name":"simon"}HTTP/1.1 204 No Content
Server: Jetty(6.1.26)
入口协议:
rest=com.alibaba.dubbo.rpc.protocol.rest.RestProtocol
handler: com.alibaba.dubbo.rpc.protocol.rest.DubboHttpServer$RestHandler
实现了dubbo的com.alibaba.dubbo.remoting.http.HttpHandler
接口约定行为如下:
void handle(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException;
到handler的调用堆栈
com.alibaba.dubbo.rpc.protocol.rest.DubboHttpServer$RestHandler.handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 88
com.alibaba.dubbo.remoting.http.servlet.DispatcherServlet.service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 61
com.alibaba.dubbo.remoting.http.servlet.DispatcherServlet(javax.servlet.http.HttpServlet).service(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 770
org.mortbay.jetty.servlet.ServletHolder.handle(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 511
org.mortbay.jetty.servlet.ServletHandler.handle(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int) line: 401
org.mortbay.jetty.servlet.SessionHandler.handle(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int) line: 182
org.mortbay.jetty.servlet.Context(org.mortbay.jetty.handler.ContextHandler).handle(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int) line: 766
org.mortbay.jetty.Server(org.mortbay.jetty.handler.HandlerWrapper).handle(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int) line: 152
org.mortbay.jetty.Server.handle(org.mortbay.jetty.HttpConnection) line: 326
org.mortbay.jetty.HttpConnection.handleRequest() line: 542
org.mortbay.jetty.HttpConnection$RequestHandler.content(org.mortbay.io.Buffer) line: 945
org.mortbay.jetty.HttpParser.parseNext() line: 756
org.mortbay.jetty.HttpParser.parseAvailable() line: 218
org.mortbay.jetty.HttpConnection.handle() line: 404
org.mortbay.jetty.nio.SelectChannelConnector$ConnectorEndPoint(org.mortbay.io.nio.SelectChannelEndPoint).run() line: 410
org.mortbay.thread.QueuedThreadPool$PoolThread.run() line: 582
com.alibaba.dubbo.rpc.protocol.rest.DubboHttpServer$RestHandler 中的HttpServletDispatcher
org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher@6e62b305
调用到业务方法的堆栈:
com.code260.ss.dubbo.demov.server.service.impl.HelloServiceImpl.sayHello(com.code260.ss.dubbo.demov.facade.bean.HelloReq) line: 37
com.alibaba.dubbo.common.bytecode.Wrapper1.invokeMethod(java.lang.Object, java.lang.String, java.lang.Class[], java.lang.Object[]) line: not available
com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory$1.doInvoke(T, java.lang.String, java.lang.Class<?>[], java.lang.Object[]) line: 47
com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory$1(com.alibaba.dubbo.rpc.proxy.AbstractProxyInvoker<T>).invoke(com.alibaba.dubbo.rpc.Invocation) line: 76
com.alibaba.dubbo.config.invoker.DelegateProviderMetaDataInvoker<T>.invoke(com.alibaba.dubbo.rpc.Invocation) line: 52
com.alibaba.dubbo.registry.integration.RegistryProtocol$InvokerDelegete<T>(com.alibaba.dubbo.rpc.protocol.InvokerWrapper<T>).invoke(com.alibaba.dubbo.rpc.Invocation) line: 56
com.alibaba.dubbo.rpc.filter.ExceptionFilter.invoke(com.alibaba.dubbo.rpc.Invoker<?>, com.alibaba.dubbo.rpc.Invocation) line: 62
com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(com.alibaba.dubbo.rpc.Invocation) line: 72
// ......
com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(com.alibaba.dubbo.rpc.Invocation) line: 72
com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler.invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[]) line: 52
com.alibaba.dubbo.common.bytecode.proxy0.sayHello(com.code260.ss.dubbo.demov.facade.bean.HelloReq) line: not available
sun.reflect.NativeMethodAccessorImpl.invoke0(java.lang.reflect.Method, java.lang.Object, java.lang.Object[]) line: not available [native method]
sun.reflect.NativeMethodAccessorImpl.invoke(java.lang.Object, java.lang.Object[]) line: 62
sun.reflect.DelegatingMethodAccessorImpl.invoke(java.lang.Object, java.lang.Object[]) line: 43
java.lang.reflect.Method.invoke(java.lang.Object, java.lang.Object...) line: 498
org.jboss.resteasy.core.MethodInjectorImpl.invoke(org.jboss.resteasy.spi.HttpRequest, org.jboss.resteasy.spi.HttpResponse, java.lang.Object) line: 137
org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(org.jboss.resteasy.spi.HttpRequest, org.jboss.resteasy.spi.HttpResponse, java.lang.Object) line: 288
org.jboss.resteasy.core.ResourceMethodInvoker.invoke(org.jboss.resteasy.spi.HttpRequest, org.jboss.resteasy.spi.HttpResponse, java.lang.Object) line: 242
org.jboss.resteasy.core.ResourceMethodInvoker.invoke(org.jboss.resteasy.spi.HttpRequest, org.jboss.resteasy.spi.HttpResponse) line: 229
org.jboss.resteasy.core.SynchronousDispatcher.invoke(org.jboss.resteasy.spi.HttpRequest, org.jboss.resteasy.spi.HttpResponse, org.jboss.resteasy.core.ResourceInvoker) line: 356
org.jboss.resteasy.core.SynchronousDispatcher.invoke(org.jboss.resteasy.spi.HttpRequest, org.jboss.resteasy.spi.HttpResponse) line: 179
org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, boolean) line: 220
org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 56
org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 51
org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher(javax.servlet.http.HttpServlet).service(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 770
com.alibaba.dubbo.rpc.protocol.rest.DubboHttpServer$RestHandler.handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 89
总体上看,默认情况下 http server由jetty实现。rest部分由resteasy实现。当然,也可以配置成其他方式。
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!