一. dubbo简介
dubbo是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,是阿里巴巴SOA服务化治理方案的核心框架。
二. 架构
引用dubbo的架构图:
Provider: 暴露服务的服务提供方。
Consumer: 调用远程服务的服务消费方。
Registry: 服务注册与发现的注册中心。
Monitor: 统计服务的调用次调和调用时间的监控中心。
Container: 服务运行容器。
三. 应用
从上图可知dubbo存在两个基本的角色:服务提供者Provider和服务消费者Consumer。
它的原理其实是dubbo服务提供者暴露出服务,然后消费者请求暴露的服务,由dubbo创建服务代理处理业务。其中接口服务是消费者和提供者共享的。
下面列举一个最简单的项目来示例用法,项目示例使用maven构建。
1. 服务提供者
项目采用多模块构建,项目结构如下:
模块说明:
common:共同享用的模块,一般存放domain或者常用的工具类。
service: 暴露的service服务接口。
service-impl: 服务的实现类,也就是业务层逻辑代码。
web: 服务启动的模块,一般是项目配置和页面等。
模块直接的关系如下图:
pom.xml仅引入了必须的jar包支持,且只应用在父模块的pom.xml:
pom.xml
dobbo可以完全使用spring的配置来做或者完全使用注解的方式。因为这里都是使用了最简洁的构建,所以仅使用了spring框架作为dubbo的支撑。
common模块只定义了domain,这里使用一个User的栗子,此处需要注意的是,如果要在分布式中传递对象,该对象必须序列化:
1 public class User implements Serializable { 2 3 private static final long serialVersionUID = 1L; 4 private Long id; 5 private String name; 6 private int age; 7 8 public Long getId() { 9 return id;10 }11 12 public void setId(Long id) {13 this.id = id;14 }15 16 public String getName() {17 return name;18 }19 20 public void setName(String name) {21 this.name = name;22 }23 24 public int getAge() {25 return age;26 }27 28 public void setAge(int age) {29 this.age = age;30 }31 32 @Override33 public String toString() {34 return "User [id=" + id + ", name=" + name + ", age=" + age + "]";35 }36 37 }
service模块中定义了一个service接口,用于暴露服务:
1 import com.bigbang.common.domain.User;2 3 public interface UserService {4 5 User getUserById(Long id);6 7 }
service-impl中代码是对服务的实现:
1 import com.bigbang.common.domain.User; 2 import com.bigbang.service.UserService; 3 4 public class UserServiceImpl implements UserService { 5 6 @Override 7 public User getUserById(Long id) { 8 User user = new User(); 9 user.setId(id);10 user.setAge(20);11 user.setName("BigBang");12 return user;13 }14 15 }
web模块没有代码,仅作为项目的启动,配置了spring和dubbo的配置文件,首先spring的配置,applicationContext.xml:
1 <?xml version="1.0" encoding="UTF-8"?>2 <beans xmlns="http://www.springframework.org/schema/beans"3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"4 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd5 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">6 7 <import resource="dubbo.xml" />8 </beans>
这里引入了dubbo的配置文件,dubbo.xml:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo 5 http://code.alibabatech.com/schema/dubbo/dubbo.xsd"> 6 7 <!-- 提供方应用信息,用于计算依赖关系 --> 8 <dubbo:application name="provider" /> 9 10 <!-- 使用multicast广播注册中心暴露服务地址 -->11 <dubbo:registry address="multicast://224.5.6.7:1234" />12 13 <!-- 用dubbo协议在20880端口暴露服务 -->14 <dubbo:protocol name="dubbo" port="20880" />15 16 <!-- 声明需要暴露的服务接口 -->17 <dubbo:service interface="com.bigbang.service.UserService"18 ref="userService" />19 20 <bean id="userService" class="com.bigbang.service.impl.UserServiceImpl" />21 </beans>
项目启动文件web.xml配置仅加载spring:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app id="WebApp_ID" version="2.5" 3 xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> 5 <context-param> 6 <param-name>contextConfigLocation</param-name> 7 <param-value>classpath*:applicationContext.xml</param-value> 8 </context-param> 9 <listener>10 <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>11 </listener>12 <welcome-file-list>13 <welcome-file>index.jsp</welcome-file>14 </welcome-file-list>15 </web-app>
2. 服务消费者
服务消费者可以看做是一个客户端,不限于web或者普通的java程序,此处还是使用一个maven webapp项目作为示例,为了从简,使用spring支撑dubbo,使用servlet做应用请求。
项目结构如下:
pom.xml文件如下,仅引入spring和dubbo必须的包:
pom.xml
application.xml配置文件如下:
1 <?xml version="1.0" encoding="UTF-8"?>2 <beans xmlns="http://www.springframework.org/schema/beans"3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"4 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">5 6 <import resource="dubbo.xml" />7 </beans>
dubbo.xml配置文件如下:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo 5 http://code.alibabatech.com/schema/dubbo/dubbo.xsd"> 6 7 <!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 --> 8 <dubbo:application name="consumer" /> 9 10 <!-- 使用multicast广播注册中心暴露发现服务地址 -->11 <dubbo:registry address="multicast://224.5.6.7:1234" />12 13 <!-- 用dubbo协议在20880端口暴露服务 -->14 <dubbo:protocol name="dubbo" port="20880" />15 16 <!-- 生成远程服务代理,可以和本地bean一样使用 -->17 <dubbo:reference id="userService" interface="com.bigbang.service.UserService" />18 </beans>
项目启动配置文件web.xml:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns="http://java.sun.com/xml/ns/javaee" 4 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 5 id="WebApp_ID" version="2.5"> 6 <display-name>consumer</display-name> 7 <context-param> 8 <param-name>contextConfigLocation</param-name> 9 <param-value>classpath*:applicationContext.xml</param-value>10 </context-param>11 <listener>12 <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>13 </listener>14 <welcome-file-list>15 <welcome-file>index.jsp</welcome-file>16 </welcome-file-list>17 <servlet>18 <description>用户查询servlet</description>19 <display-name>UserQueryServlet</display-name>20 <servlet-name>UserQueryServlet</servlet-name>21 <servlet-class>com.bigbang.servlet.UserQueryServlet</servlet-class>22 <load-on-startup>1</load-on-startup>23 </servlet>24 <servlet-mapping>25 <servlet-name>UserQueryServlet</servlet-name>26 <url-pattern>/userQueryServlet</url-pattern>27 </servlet-mapping>28 </web-app>
index.jsp页面很简单,就是做一个提交form的请求:
1 <form action="/userQueryServlet" method="post">2 ID:<input name="id" type="text" placeholder="请输入要查询的id" /><br/>3 <input type="submit" value="sumbit"/>4 </form>
UserServlet.java代码也很简单,仅仅用于接收请求并且调用暴露的服务:
1 package com.bigbang.servlet; 2 3 import java.io.IOException; 4 import javax.servlet.ServletException; 5 import javax.servlet.http.HttpServlet; 6 import javax.servlet.http.HttpServletRequest; 7 import javax.servlet.http.HttpServletResponse; 8 9 import org.springframework.web.context.ContextLoader;10 import org.springframework.web.context.WebApplicationContext;11 12 import com.bigbang.common.domain.User;13 import com.bigbang.service.UserService;14 15 public class UserQueryServlet extends HttpServlet {16 private static final long serialVersionUID = 1L;17 18 private UserService userService;19 20 /**21 * @see HttpServlet#HttpServlet()22 */23 public UserQueryServlet() {24 super();25 }26 27 /**28 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse29 * response)30 */31 protected void doGet(HttpServletRequest request, HttpServletResponse response)32 throws ServletException, IOException {33 doPost(request, response);34 }35 36 /**37 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse38 * response)39 */40 protected void doPost(HttpServletRequest request, HttpServletResponse response)41 throws ServletException, IOException {42 String idStr = request.getParameter("id");43 Long id = Long.parseLong(idStr);44 User user = userService.getUserById(id);45 response.getWriter().write(user.toString());46 }47 48 @Override49 public void init() throws ServletException {50 super.init();51 WebApplicationContext applicationContext = ContextLoader.getCurrentWebApplicationContext();52 UserService userService = (UserService) applicationContext.getBean("userService");53 this.userService = userService;54 System.out.println("获取userService对象完成:" + userService);55 }56 57 }
在servlet的init()方法中,我使用了spring类获取暴露的服务接口,实际上获取到的是dubbo创建的代理对象。
四. 运行
因为dubbo是基于长连接发送数据的,所以消息订阅者在启动时就会去查找这个认为已经注册的服务,如果不存在这个服务,则会报错。所以应该先注册服务,在开启订阅者订阅。故应该先启动Provider项目再启动Consumer项目。
一切运行成功之后,打开Consumer的index.jsp页面:
输入id点击提交之后:
说明服务调用成功,分布式作用起效了。