【原创 转载请注明出处】

本文是学习了dubbo之后自己手动写的,比较通俗,很多都是自己学习之后的理解,写的过程中没有参考任何文章。

另外dubbo也有官方文档,但是比较官方,也可以多看看dubbo的官方中文文档。

代码示例连接:dubbodemo


一、dubbo的相关概念

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+JDK 8,来搭建dubbo项目

前提是你安装好zookeeper,idea安装maven插件(也可以用其他IDE,过程类似)。

先说一下搭建的整体思路,是在idea里创建一个maven project,然后在这个project里创建三个model,分别是定义api接口的(将来发布到maven仓库),实现api的(服务提供者)以及服务消费者。

1、创建project

在idea里【file】-【new】-【project】

然后按下图选择

填写完相关信息后一路Next就好了,然后在这个project上右键【new】-【model】,创建为student-api,然后一路next后项目结构就是这样的

2、编写api接口

创建完project和第一个model之后,在student-api编写我们的接口。我们创建一些实体类和接口。

具体代码:

public interface StudentService{
ResultVO<Student> getStudentByName(String name);
}

StudentService:

public class Student implements Serializable{
private String name;
private String sex;
private int age; public Student() {
} public Student(String name, String sex, int age) {
this.name = name;
this.sex = sex;
this.age = age;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public String getSex() {
return sex;
} public void setSex(String sex) {
this.sex = sex;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
}
}

Student

public class  ResultVO<T> implements Serializable{
private T data;
private String msg;
private String status; public ResultVO() {
} public ResultVO(T data, String msg, String status) {
this.data = data;
this.msg = msg;
this.status = status;
} public T getData() {
return data;
} public void setData(T data) {
this.data = data;
} public String getMsg() {
return msg;
} public void setMsg(String msg) {
this.msg = msg;
} public String getStatus() {
return status;
} public void setStatus(String status) {
this.status = status;
}
}

ResultVO

3、api打包

我们在第2步写了一个简单的接口,和一些实体类,这一步我们需要将api整个项目打包,安装到本地maven仓库。因为服务提供者和消费者都依赖这个api项目。具体操作如下

选中student-api右键【maven】-【install -e】

这一步操作的前提是,你的IDE安装了maven插件,如果没安装,右键是没有【maven】选项的,具体怎么安装这里不讲。

install -e是执行maven的命令,此操作会将项目打包并安装到maven本地仓库,-e是会在idea控制台打日志。

执行命令后过一会查看控制台

就是成功了,安装目录页告诉我们了。我们去对应目录验证下,已经有了。样我们就可以像以往引用别人的依赖那样引用我们自己的api了

4、创建服务提供者

这个时候我们就可以创建服务提供者了,同样像创建student-api那样创建服务提供者命名为student-server

只不过,在选择时我们将它创建为webapp,因为这是一个spring web项目

5、引入依赖

在project(父项目,即最外层的那个)的pom.xml里引入dubbo和zkClient的依赖,因为服务提供者和消费者都需要这个依赖,所以写在父项目里。

<!--spring web-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.13.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
</dependency>
<!--引入dubbo-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.5.4</version>
</dependency>
<!--引入zookeeper-->
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
<version>0.1</version>
<exclusions>
<exclusion>
<artifactId>junit</artifactId>
<groupId>junit</groupId>
</exclusion>
<exclusion>
<artifactId>log4j</artifactId>
<groupId>log4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--json数据转换-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>

pom依赖

引入student-api的依赖

  <dependency>
<groupId>com.dubbodemo.me</groupId>
<artifactId>student-api</artifactId>
<version>1.0</version>
</dependency>

6、实现服务

在student-server项目里实现StudentService接口

@Service
public class StudentServiceImpl implements StudentService{
@Override
public ResultVO<Student> getStudentByName(String name) {
Student student = new Student(name,"m",24);
ResultVO<Student> studentResultVO = new ResultVO<Student>(student,"success","1");
return studentResultVO;
}
}

StudentServiceImpl

7、创建消费者

服务提供者已经写好,接下来我们写服务消费者。和student-server创建方式一样,只不过命名为student-client

在这个项目里就不用引入dubbo的依赖了,因为我们已经把依赖放到父项目里了,各个model都能引用

此时项目都创建完了,看看结构

然后我们在student-client里调用服务,我们写一个控制层StudentController.java

@Controller
public class StudentController{
Logger logger = Logger.getLogger(StudentController.class);
@Resource
private StudentService studentService;
@ResponseBody
@RequestMapping("/getData")
public ResultVO<Student> getData(String name){
ResultVO<Student> studentByName = studentService.getStudentByName(name);
Student student = studentByName.getData();
logger.info("+++++++++返回码是:"+studentByName.getStatus()+" name:"+student.getName()+" resultSet:"+student.getSex()); return studentByName;
}
}

StudentController

至此,我们的代码已经全部写完了,接下来就是把服务提供者和服务消费者都启动,来验证一下。

8、配置

你会发现直到我们把代码都写完了,还没用到有关dubbo的东西呢,没错,dubbo的关键是配置。

首先需要明确的是,student-server和student-client会以web项目启动,而student-client还要用到Spring MVC。

我们先来配置student-server,在resources下创建ApplicationContext.xml为什么是ApplicationContext.xml?因为服务提供者只用到了spring,现在是提供服务没有mvc)

<?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-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!--开启注解-->
<context:annotation-config/>
<!--扫描注解-->
<context:component-scan base-package="com.dubbodemo.me.service"/>
<!--dubbo可以和spring无缝整合-->
<!--配置目的:找到注册中心,告诉注册中心,是谁在向它注册,IP是多少,提供服务的端口是多少--> <!--1、配置别名,目的是在后台好区分到底是谁, name 可以随便写,最好语义化-->
<dubbo:application name="student-server"/> <!--2、注册服务, zookeeper 注册中心; address 注册中心的地址;protocol 注册中心的协议-->
<dubbo:registry protocol="zookeeper" address="127.0.0.1:2181"/> <!--3、告诉注册中心我要提供的服务 interface 代表发布的服务的类型 ref代表要发布的是哪个服务(具体的实现) timeout超时时间-->
<dubbo:service interface="com.dubbodemo.me.api.StudentService" ref="studentServiceImpl" timeout="6000"/> <!--4、配置服务的端口,因为消费者必须通过IP+端口才能访问我的服务,我们在注册中心注册时注册中心就已经知道我们的IP了,
所以现在只需要告诉他端口 端口可以随便写,前提不可被其他程序占用。一个dubbo被发布时必须独占一个端口-->
<dubbo:protocol name="dubbo" port="12003"/>
</beans>

ApplicationContext.xml

服务提供者配置完了,我们再来配置消费者,在student-client的resources下创建spring-mvc.xml(为什么是spring-mvc.xml?因为服务消费者我们用的是spring mvc,到时候访问controller验证服务调用)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p" 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-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!--开启注解-->
<context:annotation-config/>
<!--扫描路径-->
<context:component-scan base-package="com.dubbodemo.me.controller"/>
<!--1、配置别名,目的是在后台好区分到底是谁, name 可以随便写,最好语义化-->
<dubbo:application name="student-client"/>
<!--2找到注册中心-->
<dubbo:registry address="127.0.0.1:2181" protocol="zookeeper" />
<!--3告诉注册中心你要什么 id可以随便写-->
<dubbo:reference interface="com.dubbodemo.me.api.StudentService" id="studentServiceImpl"/>
<!--因为我是消费者,不提供服务,所以不用配置端口 当我也是服务提供者时需要配置--> <!--json转换 我们将实体类直接返回 它会帮我们转成json-->
<mvc:annotation-driven />
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="com.fasterxml.jackson.databind.ObjectMapper">
<property name="dateFormat">
<bean class="java.text.SimpleDateFormat">
<constructor-arg type="java.lang.String" value="yyyy-MM-dd" />
</bean>
</property>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
</beans>

spring-mvc.xml

student-server的web.xml配置:读取spring配置文件,加载spring

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!-- <async-supported>true</async-supported> -->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<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>
</web-app>

web.xml

student-client的web.xml配置: 读取spring配置文件,加载spring  mvc模块

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!-- <async-supported>true</async-supported> -->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<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>
</web-app>

web.xml

9、验证

验证之前,我们先启动zookeeper

然后再依次启动student-server项目和student-client项目

启动完成后,访问student-client的接口localhost:9001/getData?name=tom

控制台日志

调用成功了。本教程源码已经上传到GitHub上了,传送门

题外话:为什么两个web项目的web.xml配置不一样?

student-client里的controller是交给SpringMVC来处理,并不是spring容器。若交给spring容器来处理,代表项目一启动就要创建出来controller,这个时候可能还没有链接网络(没连zookeeper)可能还拿不到远程对象,就会注入失败,所以不能交给spring容器来处理。

dubbo入门教程-从零搭建dubbo服务的更多相关文章

  1. WCF入门教程(三)定义服务协定--属性标签

    WCF入门教程(三)定义服务协定--属性标签 属性标签,成为定义协议的主要方式.先将最简单的标签进行简单介绍,以了解他们的功能以及使用规则. 服务协定标识,标识哪些接口是服务协定,哪些操作时服务协定的 ...

  2. SpringBoot之入门教程-SpringBoot项目搭建

    SpringBoot大大的简化了Spring的配置,把Spring从配置炼狱中解救出来了,以前天天配置Spring和Mybatis,Springmvc,Hibernate等整合在一起,感觉用起来还是挺 ...

  3. Sina App Engine(SAE)入门教程(4)- SaeVCode(验证码服务)使用

    参考资料 SaeVCode api 文档 使用教程 所有的验证码原理都是生成一个vcode字符串,存到session中,和用户的输入进行比较判断,以下是一个使用验证码服务的完整实例: 首页index. ...

  4. DQN(Deep Q-learning)入门教程(零)之教程介绍

    简介 DQN入门系列地址:https://www.cnblogs.com/xiaohuiduan/category/1770037.html 本来呢,在上一个系列数据挖掘入门系列博客中,我是准备写数据 ...

  5. Docker 入门教程(4)——docker-compse 服务编排

    Docker compose 简介 compose是用来定义和运行多个Docker容器. 比如一个简单的web项目,除了web服务之外,我们可能要需要数据库容器.注册中心容器等等.那我们需要: 定义各 ...

  6. 🚴‍♂️全套MySQL数据库教程_Mysql基础入门教程,零基础小白自学MySQL数据库必备教程☔ #002 # 第二单元 MySQL数据类型、操作表#

    二.本单元知识点概述 (Ⅰ)知识点概述 二.本单元教学目标 (Ⅰ)重点知识目标 1.Mysql的数据类型2.如何选择数据类型3.创建表4.修改表5.删除表 (Ⅱ)能力目标 1.熟练创建数据库及删除数据 ...

  7. PHP入门教程-开发环境搭建

    1.PHP简介: PHP是能让你生成动态网页的工具之一.PHP网页文件被当作一般HTML网页文件来处理并且在编辑时你可以用编辑HTML的常规方法编写PHP. 2.学习需要基础: a.HTML b.Ja ...

  8. ASP.NET MVC3入门教程之环境搭建

    本文转载自:http://www.youarebug.com/forum.php?mod=viewthread&tid=90&extra=page%3D1 什么是ASP.NET MVC ...

  9. Sina App Engine(SAE)入门教程(5)- SaeSegment(中文分词服务)使用

    分词能干什么? 提取一篇文章的关键字 检测特定的段落中有没有违禁词 智能机器人 …..尽你所想 开启SAE 分词服务 首先你需要在sae的管理面板开始分词服务后才能使用sae的服务.具体的开启操作: ...

随机推荐

  1. C# recording audio based on audio in Console

    1. Install-package naudio -v 1.9.0 2. using NAudio.Wave; 3. public class NAudioHelper { public WaveI ...

  2. git遇到的错误和解决方法(长期更新)

    1:场景:将两个git合并成一个git url,由于项目超过100M,所以出现错误,以下是解决方案:

  3. Java生鲜电商平台-优惠券系统的架构设计与源码解析

    Java生鲜电商平台-优惠券系统的架构设计与源码解析 电商后台:实例解读促销系统 电商后台系统包括商品管理系统.采购系统.仓储系统.订单系统.促销系统.维权系统.财务系统.会员系统.权限系统等,各系统 ...

  4. springmvc学习笔记三:整合JDBC,简单案例==数据库事务配置(切面)

    package cn.itcast.bean; import org.springframework.jdbc.core.PreparedStatementSetter; public class U ...

  5. 在 VS Code 中遇到的一些问题

    1.在安装时未配置右键快捷菜单,想重新添加 最简单的就是重新安装一遍,在安装过程中选择好. 其次可以通过以下注册表脚本导入(保存为 .reg 文件),注意因为有中文字符,需要使用记事本保存为 ANSI ...

  6. LayoutSubviews的调用

    1.当view被添加到另一个view上时调用 2.布局子控件时调用 3.屏幕旋转的时候调用 4.当view的尺寸大小改变的时候调用

  7. rocksdb和leveldb的bloom filter比较

    memtable中的bloom filter rocksdb在memtable中添加了prefix bloom filter,就是对key取prefix,然后把这个prefix加入到bloom fil ...

  8. Costco

    1 会员制,并不是Costco成功的关键原因 Costco最早开始推行会员制的时候,其实遭遇了巨大的失败. 人们当时是不接受,也不理解会员制度的,没有多少人来办会员,Costco差点就死掉了. 那Co ...

  9. 08 在设备树里描述platform_device【转】

    转自:https://blog.csdn.net/jklinux/article/details/78575281 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原 ...

  10. pdfium 例子

    #include <stdio.h> #include <fpdfview.h> int main(int argc, char** argv) { FPDF_InitLibr ...