文章目录
Dubbo简介
Apache Dubbo 是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。
Dubbo 是一款高性能、轻量级的开源Java RPC框架,提供面向接口代理的高性能RPC调用、智能负载均衡、服务自动注册和发现、运行期流量调度、可视化服务治理和运维等功能。
Dubbo技术架构
Dubbo几个核心节点
节点 | 角色说明 |
---|---|
Provider | 暴露服务的服务提供方 |
Consumer | 调用远程服务的服务消费方 |
Registry | 服务注册与发现的注册中心 |
Monitor | 统计服务的调用次数和调用时间的监控中心 |
Container | 服务运行容器 |
Dubbo框架分层
第一层,Service层,接口层
说明:
给服务提供者和消费者来实现的(留给开发人员来实现);
Provider端作用:
定义接口,暴露服务
Consumer端作用:
引入client依赖,消费服务
第二层,Config层,配置层
说明:
主要是对 Dubbo 进行各种配置的,Dubbo 相关配置
Provider端作用:
由服务配置类ServiceConfig进行初始化工作及服务暴露;
核心方法:ServiceConfig.export();
Consumer端作用:
所有的dubbo引用服务均由ReferenceConfig进行服务的引用;
调用过程:ReferenceConfig.get();–>ReferenceConfig.init();
第三层,Proxy层,服务代理层
说明:
透明生成客户端的 stub 和服务单的 skeleton,调用的是接口,实现类没有,所以得生成代理,代理之间再进行网络通讯、负责均衡等
Provider端作用:
生成客户端的代理,调用过程:
ReferenceConfig.get();–>ReferenceConfig.init();–>ReferenceConfig.createProxy(map)–>AbstractProxyFactory.getProxy(invoker)–>JavassistProxyFactory.getProxy(invoker,interfaces)
Consumer端作用:
生成服务端的代理,调用过程:
ServiceConfig.doExport();–>ServiceConfig.doExportUrls()–>ServiceConfig.doExportUrlsFor1Protocol()–>ServiceConfig.exportLocal(url)–>StubProxyFactoryWrapper.getInvoker(proxy,type,url)–>JavassistProxyFactory.getInvoker(proxy,type,url),
第四层,registry 层,服务注册层
说明:
负责服务的注册与发现,Provider和consumer端都会将数据注册到zookeeper上
Provider端作用:
注册provider的信息到zookeeper上,调用过程:
ServiceConfig.doExport();–>ServiceConfig.doExportUrls()–>ServiceConfig.doExportUrlsFor1Protocol()–>RegistryProtocol.export(originInvoker)–>FailbackRegistry.register(url);
Consumer端作用:
向服务器发送订阅请求,调用过程:
ReferenceConfig.createProxy()–>RegistryProtocol.refer()–>RegistryProtocol.doRefer()–>FailbackRegistry.register(url);
首先注册consumer的信息到zookeeper上,调用过程:
RegistryProtocol.doRefer()–>FailbackRegistry.subscribe();
zookeeper的通知:
ZkclientZookeeperClient.stateChanged(state)通知所有的监听
第五层 cluster 层,路由层
说明:
封装多个服务提供者的路由以及负载均衡,将多个实例组合成一 个服务;
首先是初始时会先从zookeeper读取路由信息,调用过程:
ReferenceConfig.createProxy()–>RegistryDirectory.init()–>-AbstractDirectory().init()–>AbstractDirectory.setRouters()
调用时,会进行路由匹配,调用过程:
MockClusterInvoker.invoker()–>AbstractClusterInvoker.invoke(invocation)–>AbstractCluster.list(invocation)
第六层 monitor 层,监控层
说明:
主要是通过filter过滤每一个请求,并且进行信息采集,并且汇总一分钟内的数据,定时发送给监控服务:
第七层 protocol 层,远程调用层
说明:
封装 rpc 调用
Provider端作用:
进行服务的创立,调用过程:
ServiceConfig.doExport();–>ServiceConfig.doExportUrls()–>ServiceConfig.doExportUrlsFor1Protocol()–>RegistryProtocol.export(originInvoker)–>RegistryProtocol.doLocalExport()–>DubboProtocol.export()–>DubboProtocol.openServer()–>DubboProtocol.createServer(url)
Consumer端作用:
调用远程服务,调用过程:
ReferenceConfig.createProxy()–>RegistryProtocol.refer()–>RegistryProtocol.doRefer()–>DubboProtocol.refer()–>DubboProtocol.getClients()–>DubboProtocol.getSharedClient(),
第八层 exchange 层,信息交换层
说明:
封装请求响应模式,同步转异步;
在这一层主要有两个类HeaderExchangeServer和HeaderExchangeClient,进行消息的发送和接收
第九层 transport 层,网络传输层
说明:
抽象 mina 和 netty 为统一接口;
Provider端作用:
生成一个NettyServer,调用过程:
ServiceConfig.doExport();–>ServiceConfig.doExportUrls()–>ServiceConfig.doExportUrlsFor1Protocol()–>RegistryProtocol.export(originInvoker)–>RegistryProtocol.doLocalExport()–>DubboProtocol.export()–>DubboProtocol.openServer()–>DubboProtocol.createServer(url)–>NettyTrasporter.bind(),生成一个NettyServer,
线程池是FixedThreadPool,默认线程是200
Consumer端作用:
生成一个NettyClient,调用过程:
ReferenceConfig.createProxy()–>RegistryProtocol.refer()–>RegistryProtocol.doRefer()–>DubboProtocol.refer()–>DubboProtocol.getClients()–>DubboProtocol.getSharedClient()–>NettyTransporter.connect(),
线程池是CachedThreadPool,默认线程存活时间是1分钟
在这一层是利用的第三方框架netty
第十层 serialize 层,数据序列化层
说明:
数据序列化层。序列化层统一用的是第三方序列化框架hession
基于xml方式暴露和消费服务
Dubbo配置项说明:
配置名称 | 是否必须 | 说明 |
---|---|---|
dubbo:application | 是 | 就是整个项目在分布式架构中的唯一名称,可以在 name 属性中配置,另外还可以配置 owner 字段,表示属于谁 |
dubbo:monitor | 否 | 监控中心配置, 用于配置连接监控中心相关信息,可以不配置,不是必须的参数。 |
dubbo:registry | 否 | 配置注册中心的信息,比如,这里我们可以配置 zookeeper 作为我们的注册中心。address 是注册中心的地址,这里我们配置的是 N/A 表示由 dubbo 自动分配地址。或者说是一种直连的方式,不通过注册中心。 |
dubbo:protocol | 否 | 服务发布的时候 dubbo 依赖什么协议,可以配置 dubbo、webserovice、Thrift、Hessain、http等协议。 |
dubbo:service | 否 | 这个节点就是我们的重点了,当我们服务发布的时候,我们就是通过这个配置将我们的服务发布出去的。interface 是接口的包路径,ref 是配置的接口的 bean。 |
bean | 否 | 配置 spring 的接口一样,配置接口的 bean |
基于xml服务暴漏方式
在项目resources目录下,创建provider.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!--定义了提供方应用信息,用于计算依赖关系;在 dubbo-admin 或 dubbo-monitor 会显示这个名字,方便辨识-->
<!--<dubbo:application name="shop-web-provider" owner="programmer" organization="dubbox"/>-->
<dubbo:application name="dubbo-provider" owner="dubbo-provider" />
<!--使用 zookeeper 注册中心暴露服务,注意要先开启 zookeeper-->
<!--<dubbo:registry address="multicast://224.5.6.7:1234"/>-->
<dubbo:registry address="zookeeper://localhost:2181"/>
<!-- 用dubbo协议在20880端口暴露服务 -->
<dubbo:protocol name="dubbo" port="20880"/>
<!--具体实现该接口的 bean-->
<bean id="orderService" class="com.deepflow.shop.service.impl.OrderServiceImpl"/>
<!--使用 dubbo 协议实现定义好的 DemoService 接口-->
<dubbo:service interface="com.deepflow.shop.service.OrderService" ref="orderService"/>
</beans>
然后再主启动类main方法里加载该文件:
@SpringBootApplication
public class Provider {
public static void main(String[] args) throws Exception {
System.setProperty("java.net.preferIPv4Stack", "true");
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext(new String[]{"provider.xml"});
context.start();
System.out.println("服务提供方已经启动...");
System.in.read(); // press any key to exit
}
}
基于xml方式消费服务
在项目resources目录下,创建consumer.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- consumer's application name, used for tracing dependency relationship (not a matching criterion),
don't set it same as provider -->
<dubbo:application name="shop-web-consumer" owner="programmer" organization="dubbed"/>
<!-- use multicast registry center to discover service -->
<!--<dubbo:registry address="multicast://224.5.6.7:1234"/>-->
<dubbo:registry address="zookeeper://localhost:2181"/>
<!-- generate proxy for the remote service, then demoService can be used in the same way as the
local regular interface -->
<dubbo:reference id="orderService" check="false" interface="com.deepflow.shop.service.OrderService"/>
</beans>
然后再主启动类main方法里加载该文件:
public class ConsumerWeb {
public static void main(String[] args) {
System.setProperty("java.net.preferIPv4Stack", "true");
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"consumer.xml"});
context.start();
System.out.println("微商城移动端 消费方(Consumer)启动......");
OrderService orderService = (OrderService) context.getBean("orderService"); // get remote service proxy
System.out.println("消费方(Consumer)");
while (true) {
try {
Thread.sleep(1000);
String hello = orderService.sayHello("第2个:我是移动端"); // call remote method
System.out.println(hello); // get result
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
}
}
基于注解方式暴露和消费服务
dubbo框架核心注解
注解名 | 全限定路径 | 说明 |
---|---|---|
@EnableDubbo | com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo | 放到启动类上。@EnableDubbo整合了三个注解@EnableDubboConfig、@DubboComponentScan、@EnableDubboLifecycle。@EnableDubbo的功能都是由这三个注解完成的 |
@Service | com.alibaba.dubbo.config.annotation.Service | 放到要暴露的服务接口上 |
@Reference | com.alibaba.dubbo.config.annotation.Reference | 放到要消费的服务接口上 |
基于注解的方式暴露服务
在项目resources目录下,创建application.yml文件:
配置项的含义和xml一致
server:
port: 28891
dubbo:
application:
name: user
registry:
address: zookeeper://localhost:2181
protocol:
port: 20880
name: dubbo
然后再启动类上添加@EnableDubbo注解
@SpringBootApplication
@EnableDubbo
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
System.out.println(
String.format(
"%s UserApplication service server started!",
new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss]").format(new Date())
)
);
}
}
然后定义一个service及其实现类
public interface UserService {
String getUserName(String name);
}
@Service(interfaceClass = UserService.class, timeout = 4000)
public class UserServiceImpl implements UserService {
@Override
public String getUserName(String name) {
System.out.println("[" + new SimpleDateFormat("HH:mm:ss").format(new Date()) + "] Hello " + name +
", request from consumer: " + RpcContext.getContext().getRemoteAddress());
return "Hello " + name + ", response form provider: " + RpcContext.getContext().getLocalAddress();
}
}
基于注解的方式消费服务
在项目resources目录下,创建application.yml文件:
server:
port: 28890
dubbo:
application:
name: backend
registry:
address: zookeeper://localhost:2181
protocol:
port: 20880
name: dubbo
然后再项目启动类上,添加@EnableDubbo注解
@SpringBootApplication
@EnableDubbo
public class Backend {
public static void main(String[] args) {
SpringApplication.run(Backend.class, args);
System.out.println(
String.format(
"%s Backend service server started!",
new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss]").format(new Date())
)
);
}
}
然后通过@Reference引入服务
@Component
public class UserProxy {
@Reference(interfaceClass = UserService.class, timeout = 3000)
private UserService userService;
public String getUserName(String name) {
return userService.getUserName(name);
}
}