本文是学习了dubbo之后自己手动写的,比较通俗,很多都是自己学习之后的理解,写的过程中没有参考任何文章。
另外dubbo也有官方文档,但是比较官方,也可以多看看dubbo的官方中文文档。
代码示例连接:demoTest
dubbo是阿里的一个分布式服务开源框架,它的设计理念就是把一个大而全的项目模块化,每个模块都是一个独立的项目。
为什么要把大项目拆分成多个小项目呢?
因为随着项目越做越大,代码、功能越来越多,导致代码的复用性就会降低,项目变得庞大臃肿,维护起来比较麻烦,改一个功能所有的代码都需要重新打包发布,还可能会影响其他模块;对于开发者来说所有的开发者都在同一个项目里开发代码,虽然有版本管理软件(SVN,Git等),但即使这样也会使开发者开发时遇到许多问题。
所以就诞生了分布式,分布式的原则就是将项目拆分若干个小项目,实现模块化,每个项目只关注自己的功能(假如按功能划分模块),需要其他模块的数据时就去调用它,分工更加明确。dubbo就是一个基于spring的分布式框架,可以和spring无缝整合。
那么模块之间该如何调用呢?
我们称服务提供者为服务生产者,服务调用者为服务消费者,他们两个是如何通信呢?消费者如何找到生产者呢?显然是通过网络,通过网络就必须要遵循一定的协议,约定,也就时需要有一个第三者或叫中间人、中介,称为注册中心,他来定义通讯的协议、规则。服务生产者和服务消费者二者必须都来遵循这个规则。
服务生产者在启动程序时把服务发布到注册中心,告诉注册中心他叫啥,他提供的服务的类型,他的IP和端口就行了,而服务消费者在启动程序时也去连接注册中心,告诉注册中心他叫啥,告诉注册中心他想要什么类型的服务。对于服务提供方和服务消费方来说,他们还有可能兼具这两种角色,即既需要提供服务,有需要消费服务。
具体怎么实现调用呢?在项目里的表现如何?
模块之间要想调用,提供服务方需要创建服务接口,打成jar包,发布这个jar包,服务提供者面向接口编程,服务消费者调用服务时也用这个服务接口的jar包,创建服务接口的实例。
为什么要创建服务接口并发布成jar包?因为服务的提供方和服务的调用方都要用到这个接口。服务消费者要使用这个服务,需要获得服务的实例(只关心服务的类型)来调用服务提供的方法,而服务的提供者也只需要实现接口就可以了。
有了协议为什么还用dubbo?
那我们直接遵循协议,去和注册中心打交道不就行了吗,为什么还产生了dubbo呢?
毕竟有需求就有市场,归根结底还是因为我们懒。因为所有的人都要遵循这个协议,怎么连接网络,怎么传参数,格式如何等这些工作都是重复性的,是所有项目共性的,每次都写不就很麻烦吗,所以dubbo来帮你实现,来帮你完成这些共性的繁琐的工作,你只需要关注业务本身就行了,那些麻烦的协议dubbo来帮你遵循,所以就产生了dubbo,并且可能他做的这些工作比你自己写起来更高效比你写的代码好。
服务的管理
当项目越来越大,模块、服务越来越多,一个项目可能会调用很多服务,或者服务之间相互调用,这个时候你可能就不知道你这个服务调用了哪些服务或都被谁调用了,性能如何等,并且管理起来比较麻烦,别担心,有服务治理中心可以帮你解决这些问题。它是dubbo的一个后台监控系统,叫dubbo-admin,可以下载他的war包运行在web容器里就可以用了。
使用dubbo框架来完成一个小demo
我们用idea+spring+maven+zookeeper,来搭建dubbo项目
前提是你安装好maven和zookeeper,这里不再讲述。
1、首先创建一个project,在pom.xml引入dubbo和zkClient的依赖,因为3和4都需要这个依赖,所以写在父项目里。
<!--引入dubbo-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.5.9</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--引入zookeeper-->
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
<version>0.1</version>
</dependency>
2、在这个project里创建一个model,命名为service,创建一个接口:
@Service
public interface MyService{
<!--这个方法没有任何意义只是为了演示-->
String getData(String name);
}
将这个父project右键选择Run maven的install,右键,如图:
把项目打成jar包并安装到maven本地仓库,成功后会在本地仓库里看到
3、再创建一个model,命名为webTest,服务消费者,添加spring-webmvc的依赖,以及刚才打的服务接口的依赖
<dependency>
<groupId>com.yya</groupId>
<artifactId>service</artifactId>
<version>1.0</version>
</dependency>
创建controller,引入服务接口实例,创建一个controller方法
@Resource
private MyService myService;//注入服务接口的实例
@ResponseBody
@ResultMapping("/getData")
public String getData(String name){
String data = myService.getData(name);
return data;
}
4、再创建一个model,命名为serviceImpl,服务的提供者,添加spring-webmvc的依赖,以及刚才打的服务接口的依赖(和3一样),创建MyService的实现类
@Service
public class MyServiceImpl implements MyService{
//实现接口
public String getData(String name){
return "good morning,"+name;
}
}
创建完成后的结构是这样的
5、webTest和serviceImpl都是web项目,需要配置web.xml和spring配置文件,首先配置serviceImpl这个项目,
在resources目录下创建ApplicationContext.xml文件,配置如下:
<!--扫描注解-->
<context:component-scan base-package="com.yya.service"/>
<!--dubbo可以和spring无缝整合,因为他本身就是用的spring-->
<!--目的:找到注册中心,告诉他我是谁,端口是多少-->
<!--1、配置别名,目的是在后台好区分到底是谁,name可以随便写,最好语义化-->
<dubbo:application name="test1"/>
<!--2、zookeeper注册中心;address注册中心的地址IP+端口;protocol注册中心的协议-->
<dubbo:registry address="localhost:2181" protocol="zookeeper"/>
<!--3、告诉注册中心我是谁即暴露服务 interface代表发布的服务类型 ref代表要发布哪个具体服务 timeout超时时间-->
<dubbo:service interface="com.yya.service.TestService" ref="testServiceImpl" timeout="5000"/>
<!-- 和本地bean一样实现服务 -->
<bean id="testServiceImpl" class="com.yya.TestServiceImpl" />
<!--4、配置服务的端口,因为消费者必须通过IP端口才能访问我发布的服务,我们在注册中心注册时注册中心就已经知道我们的IP了,所以现在只需要告诉他端口
注册中心消费者可通过哪个端口找到我。端口可以随便写,但不可以被占用。一个dubbo被发布时必须独占一个端口-->
<dubbo:protocol name="dubbo" port="12334"/>
web.xml配置:读取spring配置文件,加载spring
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:ApplicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
6、webTest和serviceImpl都是web项目,需要配置web.xml和spring配置文件,配置webTest这个项目,
在resources目录下创建spring-MVC.xml文件,配置如下:
<!--扫描注解-->
<context:component-scan base-package="com.yya.controller"/>
<!--找到注册中心,告诉他你想要什么服务-->
<!--我是谁-->
<dubbo:application name="diaoyongzhe"/>
<!--找到注册中心-->
<dubbo:registry address="localhost:2181" protocol="zookeeper" />
<!--3告诉他你要什么-->
<dubbo:reference interface="com.yya.service.TestService" id="testServiceImpl"/>
web.xml配置:
<!-- springmvc 核心,请求分发器 -->
<servlet>
<servlet-name>springMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<!--启动项目时就创建这个servlet对象,这个参数必须在init-param之后出现-->
<load-on-startup>2</load-on-startup>
</servlet>
<!--配置拦截路径-->
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
启动zookeeper,zookeeper需要先安装好(如何安装zookeeper),启动成功后,先启动serviceImpl项目,再启动webTest项目。启动之前在二者的pom文件里加Tomcat的插件,用插件的方式启动,把端口改成不一样的。
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>9000</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
启动完成后,访问localhost:8080/getData?name=tom
返回good morning,tom代表调用成功。后来又改了,改成返回对象的了。可以参考这个demo,我已经上传到GitHub上了,点击下载:demoTest,使用的是jdk8
题外话:为什么两个web项目的web.xml配置不一样?
webTest里的controller是交给SpringMVC来处理,并不是spring容器。若交给spring容器来处理,代表项目一启动就要创建出来controller,这个时候可能还没有链接网络(没连zookeeper)可能还拿不到远程对象,就会注入失败,所以不能交给spring容器来处理。