Web Service是一种传统的远程过程调用方式,它通过XML来定义服务信息和传输数据。Dubbo是一种分布式服务治理框架,它提供了服务注册与发现,负载均衡,远程调用等。Dubbo在进行服务调用时,默认使用Dubbo 自定义的协议,但也支持各种第三方协议,如rest,grpc,web service等。
Dubbo提供了对Web Service的整合能力,主要是指可以将Web Service包装成Dubbo服务,因此提供了服务注册与发现,同时在提供者端可以让开发者像发布Dubbo服务那样,很简便地发布Web Service服务,在消费者端则可以像调用Dubbo服务那样,调用Web Service服务。
public interface DemoService {
String hello(String word);
}
因为Web Service需要使用HTTP协议,所以提供者必须使用Web容器(不管是外置的还是嵌入式的)
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<display-name>dubbo-webservicedisplay-name>
<welcome-file-list>
<welcome-file>index.jspwelcome-file>
welcome-file-list>
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:spring/*.xmlparam-value>
context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
listener>
<servlet>
<servlet-name>dubboservlet-name>
<servlet-class>org.apache.dubbo.remoting.http.servlet.DispatcherServletservlet-class>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>dubboservlet-name>
<url-pattern>/services/*url-pattern>
servlet-mapping>
web-app>
这里配置跟使用Spring MVC的配置比较类似,只是把DispatcherServlet替换为Dubbo自己的实现,用来分发请求。
这个servlet的url-pattern需要注意,所有Web Service的请求都会跟在这个pattern后面。
提供者的整合有两种方式,第一种是将Protocol的server设置为servlet,第二种是设置为jetty。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<dubbo:application name="dubbo-webservice-app" />
<dubbo:registry address="zookeeper://127.0.0.1:2181" />
<dubbo:protocol name="webservice" port="8083" server="servlet" contextpath="dubbo_samples_webservice_provider_war/services"
service-path-prefix="dubbo_samples_webservice_provider_war/services"/>
<dubbo:service interface="org.apache.dubbo.samples.webservice.DemoService" ref="demoService" />
<bean id="demoService" class="org.apache.dubbo.samples.webservice.provider.impl.DemoServiceImpl" />
beans>
配置和普通的Dubbo应用差不多,只是protocol的类型指定为webservice,同时指定了port,因为使用servlet来分发请求,所以port必须和容器对外的端口相同。这里的contextpath不是容器的contextpath,而是Dubbo定义的web service的contextPath。service-path-prefix则是容器的contextPath加上DispatchServlet的url-pattern,因为容器的contextPath和servlet的url-pattern消费者无法自动获取到,但是访问的时候必须在端口后面跟上,所以这里需要配置,这样消费者可以从这个属性拿到。比如,如果使用Tomcat,应用的目录是dubbo_samples_webservice_provider_war,那容器的contextPath就是dubbo_samples_webservice_provider_war。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<dubbo:application name="dubbo-webservice-app" />
<dubbo:registry address="zookeeper://127.0.0.1:2181" />
<dubbo:protocol name="webservice" port="8084" server="jetty" contextpath="dubbo_samples_webservice_provider_war/services"/>
<dubbo:service interface="org.apache.dubbo.samples.webservice.DemoService" ref="demoService" />
<bean id="demoService" class="org.apache.dubbo.samples.webservice.provider.impl.DemoServiceImpl" />
beans>
注意这里jetty和Servlet的区别在于,这里的jetty是嵌入式的,相当于单独起了一个Web容器,所以这里的端口必须和外层的容器的端口不一样,否则会端口冲突。同时jetty设置service-path-prefix是没有意义的,原因大家可以想一下。
import org.apache.dubbo.samples.webservice.DemoService;
public class DemoServiceImpl implements DemoService {
@Override
public String hello(String word) {
return "Time: " + System.currentTimeMillis() + ", word: " + word;
}
}
启动外层容器,如果成功,可以在浏览器中查看wsdl文件。
这个图是servlet方式。
消费者比较简单,首先配置spring应用上下文
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<dubbo:application name="demo-consumer"/>
<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
<dubbo:reference id="demoService" check="false" interface="org.apache.dubbo.samples.webservice.DemoService"/>
beans>
消费者代码
public class Application {
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/dubbo-samples-webservice-consumer.xml");
context.start();
DemoService demoService = context.getBean("demoService", DemoService.class);
String result = demoService.hello("world");
System.out.println("输出: " + result);
}
}
调用结果截图
目前service-path-prefix还没有正式合并到dubbo中,只是提交了PR。所以,目前如果使用servlet,而且容器的contextPath或者DispatcherServlet的url-pattern不为空的话,在Dubbo中使用Web Service是会失败的。
后续会把本文中的代码贡献给dubbo-samples项目。