基于Dubbo的分布式系统实现
体系架构
Dubbo是一个非常轻量级的分布式RPC框架,它使用Spring进行配置,而非注入式的编程方式,深受大家的喜爱。粗浅的研究了一下其应用,并使用Web容器(Jetty)实现了一个基于Dubbo的分布式系统,提供多种服务,并可以在多个服务源中进行切换。这个Demo系统的参与者包括
- 服务的消费者(Consumer)可以是多种形式,这里采用war包的方式放到Jetty服务器中。后台由Spring容器进行管理,以获得服务的句柄;前台为了简便,使用最基本的Servlet来展现获取的服务内容;
- 服务的提供者(Provider)同样可以采用多种形式,这里也同样采用war包方式放到Jetty服务器中,并由Spring容器进行管理。系统可以有多个Provider,这就需要多个Jetty环境,并在注册中心注册;
- 服务注册中心(Registry)用于管理所有的服务,Consumer和Provider都需要在配置中指明注册中心地址。Registry可以使用Redis和Zookeeper实现,本Demo使用后者。
- Dubbo实现的监控中心(Monitor)和服务治理程序(Admin)用于监控系统的使用情况和性能,同时在多个提供者的情况下,提供服务治理功能,例如权重,生效/失效设置等。
下图描述了整个体系架构。
绘图1.jpgAPI的定义
API是Java接口定义的约定Consumer和Provider都可以使用的函数列表。Consumer通过API了解Service能够提供的服务;Provider提供服务的实现。其中
- Service.java只是一个空接口,所有的服务都继承自这个接口。
public interface Service { }
- DateService.java提供一个关于日期的服务,调用后返回一个当前的日期。
public interface DateService extends Service { public DateServiceResult getCurrentDate(); }
- MathService.java提供一个关于数字的服务,调用后返回一个排序的数组(相对于参数)。
public interface MathService extends Service { public NumberServiceResult sort(int[] numbers); }
- StringService.java提供一个关于字符串的服务,调用后返回一个翻转的字符串(相对于参数)。
public interface StringService extends Service { public StringServiceResult reverse(String str); }
API作为一个独立的,纯的Java的jar包发布给Consumer使用。
服务的消费者
为了简单起见,这里只将服务的Consumer作为基本的Servlet来处理。如果Servlet可以走通,应用到JSP,或是JSF环境中都水到渠成。Consumer中最重要的是获取服务的句柄,因为由于有API的存在,在静态编程中,是很简单的,且没有技术难度。获取句柄的代码在init方法中,如下所示
@Override public void init() throws ServletException { super.init(); ServletContext servletContext = this.getServletContext(); WebApplicationContext ctx = WebApplicationContextUtils .getWebApplicationContext(servletContext); dateService = (DateService) ctx.getBean("dateService"); }
显而易见,我们通过Spring容器获得真实的对象(由Provider实现),并从对象的调用结果中获取我们需要的内容。
服务的提供者
服务的Provider是比较简单的,只需要实现API所定义的方法即可,这里就不列举了。但为了将这个实现加载到Dubbo的系统中,也需要Spring容器,并打成War包发布到Jetty中。
Spring容器的配置
web.xml
web.xml的配置对于Consumer和Provider都是一样的,即定义Spring容器,如下所示
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/classes/applicationContext*.xml</param-value> </context-param>
作为Consumer,还要定义Servlet的映射,不再赘述。
Consumer
<dubbo:application name="dubbo-evaluation-consumer" /> <dubbo:registry address="zookeeper://10.16.2.46:2181" /> <dubbo:monitor protocol="registry" /> <dubbo:reference id="stringService" interface="com.synnex.dubboevaluation.service.StringService" /> <dubbo:reference id="mathService" interface="com.synnex.dubboevaluation.service.MathService" /> <dubbo:reference id="dateService" interface="com.synnex.dubboevaluation.service.DateService" />
对于Consumer,比较重要的是
- 使用 dubbo:registry 声明注册中心;
- 使用 dubbo:reference 声明接口。
Provider
<dubbo:application name="dubbo-evaluation-provider" /> <dubbo:registry address="zookeeper://10.16.2.46:2181" /> <dubbo:protocol name="dubbo" port="20880" /> <dubbo:monitor protocol="registry"/> <dubbo:service interface="com.synnex.dubboevaluation.service.StringService" ref="stringService" /> <bean id="stringService" class="com.synnex.dubboevaluation.service.StringServiceImpl" /> <dubbo:service interface="com.synnex.dubboevaluation.service.MathService" ref="mathService" /> <bean id="mathService" class="com.synnex.dubboevaluation.service.MathServiceImpl" /> <dubbo:service interface="com.synnex.dubboevaluation.service.DateService" ref="dateService" /> <bean id="dateService" class="com.synnex.dubboevaluation.service.DateServiceImpl" />
对于Provider,比较重要的是
- 使用 dubbo:registry 声明注册中心;
- 使用 dubbo:service 声明服务;
- 使用 bean id="xxxService" 声明服务的实现。