Apache Dubbo 是一款微服务开发框架,它提供了 RPC通信 与 微服务治理 两大关键能力。这意味着,使用 Dubbo 开发的微服务,将具备相互之间的远程发现与通信能力, 同时利用 Dubbo 提供的丰富服务治理能力,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。同时 Dubbo 是高度可扩展的,用户几乎可以在任意功能点去定制自己的实现,以改变框架的默认行为来满足自己的业务需求。
Apache Dubbo是一款高性能的Java RPC框架。其前身是阿里巴巴公司开源的、轻量级的开源Java RPC 框架,可以和Spring框架无缝集成。
Dubbo提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。
服务是 Dubbo 中的核心概念,一个服务代表一组 RPC 方法的集合,服务是面向用户编程、服务发现机制等的基本单位。Dubbo 开发的基本流程是:用户定义 RPC 服务,通过约定的配置 方式将 RPC 声明为 Dubbo 服务,然后就可以基于服务 API 进行编程了。对服务提供者来说是提供 RPC 服务的具体实现,而对服务消费者来说则是使用特定数据发起服务调用。
RPC
RPC全称为remote procedure call,即远程过程调用。比如两台服务器A和B,A服务器上部署一个应用,B服务器上部署一个应用,A服务器上的应用想调用B服务器上的应用提供的方法,由于两个应用不在一个内存空间,不能直接调用,所以需要通过网络来表达调用的语义和传达调用的数据。 需要注意的是RPC并不是一个具体的技术,而是指整个网络远程调用过程。
Dubbo 基于消费端的自动服务发现能力,其基本工作原理如下图:
虚线是异步访问,实线是同步访问;蓝色色虚线:在启动时完成的功能;绿色线条是程序运行过程中的执行功能与流程;
Provider: 暴露服务的提供方
Consumer: 调用远程服务的服务消费方
Registry: 服务注册与发现的注册中心
Monitor: 统计服务的调用次数和调用时间的监控中心
Container: 服务运行的容器
调用关系说明:
0. 服务容器负责启动,加载,运行服务提供者。
1. 服务提供者在启动时,向注册中心注册自己提供的服务。
2. 服务消费者在启动时,向注册中心订阅自己所需的服务。
3. 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给 消费者。
4. 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失 败,再选另一台调用。
5. 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中 心。
Dubbo官方推荐使用Zookeeper作为服务注册中心(Regitry)
介绍
Zookeeper 是 Apache Hadoop 的子项目,是一个树型的目录服务,支持变更推送,适合作为 Dubbo 服务的注册中心,工业强度较高,可用于生产环境,并推荐使用 。
Zookeeper的树形目录结构:
流程说明:
服务提供者(Provider)启动时: 向 /dubbo/com.foo.BarService/providers 目录下写入自己的 URL 地址
服务消费者(Consumer)启动时: 订阅 /dubbo/com.foo.BarService/providers 目录下的提供者 URL 地址。并向 /dubbo/com.foo.BarService/consumers 目录下写入自己的 URL 地址
监控中心(Monitor)启动时: 订阅 /dubbo/com.foo.BarService 目录下的所有提供者和消费者 URL 地址
安装
cd conf
mv zoo_sample.cfg zoo.cfg
这里是使用的虚拟机CentOS7作为示例安装与使用。
第一步:在虚拟机上安装jdk;
第二步:将把 zookeeper 的压缩包(我们这里使用的是 zookeeper-3.4.6.tar.gz)上传到linux 系统;
第三步:解压缩包。-C参数后面跟进自己解压的路径;
tar -zxvf zookeeper-3.4.6.tar.gz -C /tool/zookeeper
第四步:进入解压好的目录,需要创建一个data目录,作为配置文件的放置处;
mkdir data
第五步:进入解压目录下的conf目录,将把zoo_sample.cfg 复制一份并改名为zoo.cfg
cd conf
mv zoo_sample.cfg zoo.cfg
第六步:编辑复制的zoo.cfg文件,需要修改data属性,改为第四步创建的data目录下
dataDir=/tool/zookeeper/data
这样zookeeper的准备就做完了。
启动与停止zookeeper
首先我们需要进入zookeeper中,解压好的zookeeper-3.4.6目录下的bin目录下执行linux的shell命令。
相关命令:
启动服务命令
./zkServer.sh start
停止服务命令
./zkServer.sh stop
查看服务状态:
./zkServer.sh status
客户端连接
./zkCli.sh
Dubbo作为一个RPC框架,其最核心的功能就是要实现跨网络的远程调用。我们的案例就简单的创建两个示范应用,一个作为服务的提供方(provider),一个作为服务的消费方(consumer)。通过Dubbo来实现服务消费方远程调用服务提供方的方法。
服务提供方开发
maven的pom.xml引入的相关依赖如下:
4.0.0
com.cqh
dubbo-provider
1.0-SNAPSHOT
war
UTF-8
1.8
1.8
5.0.5.RELEASE
org.springframework
spring-context
${spring.version}
org.springframework
spring-beans
${spring.version}
org.springframework
spring-webmvc
${spring.version}
org.springframework
spring-jdbc
${spring.version}
org.springframework
spring-aspects
${spring.version}
org.springframework
spring-jms
${spring.version}
org.springframework
spring-context-support
${spring.version}
mysql
mysql-connector-java
5.1.47
com.alibaba
druid
1.1.6
org.mybatis
mybatis-spring
1.3.2
com.alibaba
dubbo
2.6.0
org.apache.zookeeper
zookeeper
3.4.7
com.github.sgroschupf
zkclient
0.1
org.javassist
javassist
3.24.0-GA
com.alibaba
fastjson
1.2.75
org.apache.maven.plugins
maven-compiler-plugin
3.8.1
1.8
org.apache.tomcat.maven
tomcat7-maven-plugin
8081
/
在webapp目录下添一个web.xml文件
web.xml配置文件:
contextConfigLocation
classpath:applicationContext*.xml
org.springframework.web.context.ContextLoaderListener
之后我们在main.java目录下创建接口与实现类(作为提供服务)
接口 HelloService:
public interface HelloService {
public String sayHello(String name);
}
创建一个实现类实现该接口 HelloServiceImpl(注意这里的@Service注解是使用的alibaba dubbbo包下的Service):
import com.alibaba.dubbo.config.annotation.Service;
import com.kkb.service.HelloService;
@Service
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String name) {
return "hello : " + name;
}
}
我们还需要在src/main/resources下创建applicationContext-service.xml:
上面的配置文件web.xml用于指定我们上面开启的zookeeper服务IP地址、指定使用zookeeper的端口与协议、用于提供服务的包名等。这样我们tomcat开启后,就会在zookeeper上注册我们提供的服务,用于其他服务来远程调用等。
服务消费方开发
另开一个moudle,pom.xml与web.xml配置文件是答题相同的,只需要该部分内容:
pom.xml 需要将引入的tomcat插件的端口好改为 8002
web.xml 中需要引入servlet的相关配置,需要接收url请求
添加内容:
springmvc
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath*:applicationContext-web.xml
springmvc
/
我们需要沿用服务提供方中我们需要调用服务的接口 HelloService
然后我们创建一个controller用于接受请求 HelloController:
import com.alibaba.dubbo.config.annotation.Reference;
import com.cqh.service.HelloService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/dubbo")
public class HelloController {
@Reference
private HelloService helloService;
@RequestMapping("/hello")
@ResponseBody
public String sayHello(String name) {
// 远程调用
String result = helloService.sayHello(name);
System.out.println(result);
return result;
}
最后,在src/main/resources下创建applicationContext-web.xml:
这个配置文件中指定我们的zookeeper服务注册中心的IP地址、使用的协议端口等、指定需要远调用服务的包名。
提供者暴露服务的整体机制:
服务注册中心运作机制:
服务消费者大概机制:
服务消费者首先持有远程服务实例生成的 Invoker,然后把 Invoker 转换成用户接口的动态代理引用,服务引用的入口点在 ReferenceBean。
大致说明一下远程调用过程:
我们在浏览器中调r的url:http://localhost:8082/dubbo/hello?name=wang,访问我们的HelloController,controller中我们沿用的服务提供方provider的HelloService接口,也就是说我们只是在服务消费方consumer调用自己的HelloService接口方法(这个接口并没有被实现),但是我们在这个引用的helloService对象上有一个Dubbo的注解@Reference,这样Dubbo就是创建该接口动态代理对象,在我们指定的zookeeper中寻找到服务提供方提供注册的服务(相当于订阅过程),然后远程服务实例生成的 Invoker,然后把 Invoker 转换成用户接口的动态代理引用;而我们的服务提供方(provider)提供的服务:HelloServiceImpl这个实现类,我们添加了@Servcice注解,这样就把我们的服务注册到了我们指定的zookeeper上,于是实现了远程服务调用。
示例图:
将provider与consumer的tomcat运行,这样会将自己注册至制zookeeper处:
可以看到我们注册的consumers于providers的相关信息:
这样我们url调用查看:
我们在applicationContext-**.xml的配置文件中有协议与端口的配置
一般在服务提供者一方配置,可以指定使用的协议名称和端口号。
其中Dubbo支持的协议有:dubbo、rmi、hessian、http、webservice、rest、redis等。 推荐使用的是dubbo协议。 dubbo 协议采用单一长连接和 NIO 异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。不适合传送大数据量的服务,比如传文件,传视频等,除非请求 量很低。 也可以在同一个工程中配置多个协议,不同服务可以使用不同的协议。
例:
不同协议的相关区别,具体需要去官网查看对比:
RPC协议 | Apache Dubbohttps://dubbo.apache.org/zh/docs/v3.0/references/protocols/
如果我们在服务提供者provider提供的服务上添加@Transactionnal事务控制注解后,服务就发布不成功了。原因是事务控制的底层原理是为服务提供者类创建代理对象,而默认情况下Spring是基于JDK动态代理方式创建 代理对象,而此代理对象的完整类名为com.sun.proxy.$Proxy42(最后两位数字不是固定的),导致 Dubbo在发布服务前进行包匹配时无法完成匹配,进而没有进行服务的发布。
解决方案:
1. 修改applicationContext-service.xml配置文件,开启事务控制注解支持时指定proxy-target-class 属性,值为true。其作用是使用cglib代理方式为Service类创建代理对象。
2. 修改HelloServiceImpl类,在Service注解中加入interfaceClass属性,值为HelloService.class, 作用是指定服务的接口类型。
@Service(interfaceClass = HelloService.class)
@Transactional
public class HelloServiceImpl implements HelloService {
public String sayHello(String name) {
return "hello " + name;
}
}
此处也是必须要修改的,否则会导致发布的服务接口为SpringProxy,而不是HelloService接口。