SOA
一个基本的面向服务编程(soa)的分布式项目需要provider和consumer,也就是最基本的服务提供方和服务消费方,其中provider还可以细分为Interface和IService的实现关系进一步在业务上细分服务。本实例就不细分provider了,为了方便理解仅做最基本的provider和consumer划分。
注:本实例的一切注入方式均为注解扫描注入方式。
服务提供者Provider
一个已集成spring框架的项目,项目名为Provider,项目结构图如下。
本实例中,新建的项目类型为Maven Project->maven-archetype-webapp。
Dynamic Web Module为2.5
JDK为1.7
1、Pom.xml
除了基本的springframework以外,主要需要引入以下包:
Zookeeper
<!-- zookeeper -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.3.6</version>
<exclusions>
<exclusion>
<artifactId>netty</artifactId>
<groupId>io.netty</groupId>
</exclusion>
</exclusions>
</dependency>
Dubbo
<!-- dubbo -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.5.3</version>
<exclusions>
<exclusion>
<artifactId>spring</artifactId>
<groupId>org.springframework</groupId>
</exclusion>
<exclusion>
<artifactId>netty</artifactId>
<groupId>io.netty</groupId>
</exclusion>
</exclusions>
</dependency>
zkClient
<!-- zkclient -->
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
<version>0.1</version>
<exclusions>
<exclusion>
<artifactId>netty</artifactId>
<groupId>io.netty</groupId>
</exclusion>
</exclusions>
</dependency>
配置pom.xml的过程中可能会遇到很多编译上的报错,多是由包与包之间的依赖关系引起,也和开发环境有关,自行百度,具体问题具体解决。搭建过程中遇到太多问题了,有空会将常见问题做一个汇总,如配置过程中遇到瓶颈问题,请在本文下方留言联系我。
2、Web.xml
做最基本的spring集成配置即可,配好ContextLoaderListener和DispatcherServlet,同时建立好applicationContext.xml和dubbo-provider.xml的上下文引用即可。
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 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_2_5.xsd">
<!-- 配置web.xml,使其具有springmvc特性,主要配置两处,一个是ContextLoaderListener,一个是DispatcherServlet -->
<!-- ContextLoaderListener初始化Spring上下文时需要使用到的contextConfigLocation参数 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:applicationContext.xml,classpath*:dubbo-provider.xml</param-value>
</context-param>
<!-- spring mvc 配置 -->
<!-- 配置DispatcherServlet表示,该工程将采用springmvc的方式。启动时也会默认在/WEB-INF目录下查找XXX-servlet.xml作为配置文件,
XXX就是DispatcherServlet的名字,该文件中将配置两项重要的mvc特性:HandlerMapping,负责为DispatcherServlet这个前端控制器的请求查找Controller;
ViewResolver,负责为DispatcherServlet查找ModelAndView的视图解析器。
此处使用指定的配置文件spring-mvc.xml -->
<!-- 优点:支持rest风格,Url美观
缺点:会拦截静态资源 -->
<servlet>
<servlet-name>contacts</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<!--<param-value>/WEB-INF/classes/spring-mvc-servlet.xml</param-value>-->
<param-value>classpath*:/spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- url-pattern配置为/,不带文件后缀,会造成其它静态文件(js,css等)不能访问。如配为*.do,则不影响静态文件的访问 -->
<servlet-mapping>
<servlet-name>contacts</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
3、applicationContext.xml
这里的applicationContext.xml配置很简单,只需要一个注解扫描的配置即可。
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<!-- 加入spring注解扫描-->
<context:component-scan base-package="com.lhx."/>
</beans>
扫描com.lhx.路径下的所以class文件和jar包内的文件的注解,注册到beanFactory中。
注:这里的配置与后面的UserService的注解有一些联系,暂且记住这么一回事,后面会详细说明
4、dubbo-provider.xml
具体配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd
">
<!-- 提供方应用信息,用于计算依赖关系 -->
<dubbo:application name="dubbo_provider" />
<!-- 使用zookeeper注册中心暴露服务地址 端口是zookeeper 中配置的2181 -->
<dubbo:registry address="zookeeper://127.0.0.1:2181" />
<!-- <dubbo:registry address="multicast://224.5.6.7:1234" /> -->
<!-- 用dubbo协议在20880端口暴露服务 -->
<dubbo:protocol name="dubbo" port="20880" />
<!-- 使用注解方式暴露接口 -->
<dubbo:annotation package="com.lhx.user"/>
</beans>
配置项含义如代码行注释所示。
这里的有一点要说明的是,因为之前已经提过,本实例的一切注入方式均为注解注入,所以后面再配置注解的时候要十分注意注解的引用,也就是import的包是来自于spring还是dubbo。
5、UserServiceImpl
基于第3步的铺垫,这里要着重说明一下服务的实现类。
UserServiceImpl
如上图所示
@Service的引用是来自于com.alibaba.dubbo.config.annotation.Service
@Component的引用是来自于org.springframework.stereotype.Component
这里比较有意思了,一般我们使用注解方式注入bean的话,@Service是拿来专门注入Service服务层的注解,@Component是实例化普通POJO的,可以拿来注入Controller、Service、Repository等,只有当组件不好归类的时候,我们可以使用这个注解进行标注。
为什么我们在这里注入UserServiceImpl的服务实现类的时候要用@Component,是因为@Service要留给dubbo去暴露服务,因为注解重名的原因,我们只能用@Component去作为替代。所以引用@Service这个注解时一定要注意是不是import的com.alibaba.dubbo.config.annotation.Service,否则就会出现zookeeper无法捕捉dubbo暴露的服务,从而无法在zookeeper中注册这个服务。
6、UserService
就是一个接口,注解方式暴露服务依托于接口实现类,xml方式暴露的话配置依赖会涉及到接口,但是我认为xml方式暴露服务相比于注解暴露太麻烦,在这里就不多提了。
package com.lhx.user.service;
import org.springframework.stereotype.Service;
public interface UserService {
public String sayHello();
}
服务消费者Consumer
1、pom.xml
插件引用于Provider一致,配置dubbo/zookeeper/zkClient即可,有一点不同就是消费者Consumer需要引用服务提供者Provider的Jar包。
右键Provider项目,Run As->Maven Install可将项目编译打包,打包的格式有jar、war、rar等,供外部程序引用自然是打包成Jar包,打包格式的设置在pom.xml中定义。
2、Web.xml
同服务提供者,做最基本的spring集成配置即可,配好ContextLoaderListener和DispatcherServlet,同时建立好applicationContext.xml和dubbo-provider.xml的上下文引用即可。
3、applicationContext.xml
同服务提供者,这里的applicationContext.xml配置很简单,只需要一个注解扫描的配置即可。
4、dubbo-consumer.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd
">
<!-- 提供方应用信息,用于计算依赖关系 -->
<dubbo:application name="dubbo_consumer" />
<!-- 使用zookeeper注册中心暴露服务地址 端口是zookeeper 中配置的2181 -->
<dubbo:registry address="zookeeper://127.0.0.1:2181" />
<!-- <dubbo:registry address="multicast://224.5.6.7:1234" /> -->
<!-- 用dubbo协议在20880端口暴露服务 -->
<dubbo:protocol name="dubbo" port="20880" />
<!-- 使用注解方式注册消费者 -->
<dubbo:annotation package="com.lhx.user.controller"/>
</beans>
5、UserController
除了spring的注解以外,就是消费者需要注册调用的服务,用dubbo的Reference注解即可。
package com.lhx.user.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import com.alibaba.dubbo.config.annotation.Reference;
import com.lhx.user.service.UserService;
@Controller
public class UserController {
@Reference
private UserService userService;
@RequestMapping(value="/hello", method=RequestMethod.GET)
public @ResponseBody String hello() {
return userService.sayHello();
}
}
运行效果
值得一提的是,dubbo将服务发布在zookeeper上这一步骤是无需依托于相应的消费者是否存在的,服务可以独立存在,即使没有消费者。
所以本实例建议在编写完Provider后即可运行查看dubbo-admin的管理页面是否已经注册到了我们新加的服务,只有在服务确认发布(暴露)成功后再编写相应的消费者才是有的放夭。
1、服务者(Provider)
2、消费者(Consumer)
结语
学会搭建dubbo+zookeeper只是摸到了分布式框架的门槛,真正入门还需要学习很多其他方面的知识,诸如分布式事务、分布式缓存、负载均衡等等。
博主由于工作上的需要,也正处于这样的一个学习阶段,希望能够凭借着自己的努力把一些个人心得在这里分享。