//对Dubbo官网的学习笔记
入门
- 背景
随着业务的发展,网站架构也要进行相应的发展以适应不断提高的并发量和更加灵活的开发业务。发展历程可以概述为:单体应用–》垂直应用架构–》分布式服务架构–》流动计算架构。 - 需求
虽然在Sping中只需要简单的配置ServiceExport类和Proxy类就可以实现RPC,方式也可以选择RMI,Hessian,Buralp,HttpInvoker等,但这只是简单的暴露和引用服务,其提供的功能也就只是实现RPC的核心概念即像调用本地服务一样的调用远端系统提供的服务。关于负载均衡,集群管理,服务的自动注册与发现这些功能都需要自己去实现。而Dubbo就为我们解决了这些问题,它的特性概括说有这几个点:
–面向接口代理的高性能RPC调用,服务是以接口为粒度的。
–智能负载均衡,内置多种负载均衡策略
–服务自动注册与发现
–高度可扩展能力
–运行期流量调度,通过配置不同的路由规则,实现灰度发布,同机房优先等功能。
–可视化的服务治理与运维。 - 架构
关于Dubbo非常经典的一张图了。首先,producer和consumer在启动后都要与注册中心进行通信,服务提供者注册自己提供的服务,而服务消费者就订阅自己需要的服务。之后注册中心将会把服务消费者需要的服务的服务提供者地址列表给消费者,如果中间发生变更,注册中心将会基于长连接推送变更数据给消费者。当服务消费者有了服务地址列表,就可以基于软负载均衡算法,选择一台提供者进行调用,如果调用失败,再选另一台调用。除了注册中心,服务提供者,服务消费者外,还有个用于统计服务的调用次数和调用时间的监控中心,当配置了这个监控中心后,服务消费者和服务提供者就会定时将其调用信息发送给监控中心。
Dubbbo架构具有以下的特点:
----->连通性:
A. 注册中心,服务提供者,服务消费者三者之间均为长连接,监控中心除外。
B. 注册中心通过长连接感知服务提供者的存在,服务提供者宕机,注册中心将立即推送事件通知消费者。
C. 注册中心和监控中心全部宕机,不影响已运行的提供者和消费者,消费者在本地缓存了提供者连接。
D. 注册中心和监控中心都是可选的,服务消费者可以直连服务提供者。
----->健壮性:
A. 服务提供者无状态,任意一台宕机后,不影响使用。
B. 服务提供者全部宕机后,消费者应用将无法使用,将会无限次重连等待服务提供者恢复。
C. 注册中心对等集群,任意一台宕机后,将自动切换到另一台。
D. 注册中心全部宕机后,服务提供者和消费者仍然可以通过本地缓存通讯。
E.监控中心宕机后不影响使用,只是丢失部分采样数据。
----->伸缩性:
A. 注册中心为对等集群,可以动态的增加机器部署实例(使用zookeeper的话就是用ZAB协议),所有客户端将自动发现新的注册中心。
B. 服务提供者无状态,可以动态的增加机器部署实例,注册中心将推送新的服务提供者信息给消费者。
----->升级性:
A可在已有的分布式架构基础上实现动态部署,进行流动计算。
快速启动
使用的Dubbo版本是2.7.1,这个版本使用的zookeeper版本是3.4.13,使用的curator版本是4.0.1。因此配置的pom文件如下:
<dependencies>
<!--引入dubbo-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>2.7.1</version>
</dependency>
<!--引入zookeeper,使用zookeeper作为注册中心,版本不对会报错-->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.13</version>
</dependency>
<!--引入zookeeper的客户端curator,版本不对会报错-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.0.1</version>
</dependency>
<!--引入日志相关的jar包,不引入会报错-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.26</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.26</version>
<scope>test</scope>
</dependency>
</dependencies>
为了方便,就将服务生产者,服务提供者写到一个工程的不同模块中了,整体目录结构如下:
其中,common-api用于提供接口,producer用于提供具体的实现,consumer通过dubbo调用远端服务。
common-api这个模块只有一个接口的定义:
package personal.johnson.dubbostudy.commonapi.api;
public interface SayHelloService {
String sayHello(String name);
}
producer实现了这个接口
package personal.johnson.dubbostudy.producer.serviceImpl;
import personal.johnson.dubbostudy.commonapi.api.SayHelloService;
public class SayHelloServiceImpl implements SayHelloService {
@Override
public String sayHello(String name) {
return "hello,"+name;
}
}
实现了这个接口之后,还需要将这个接口提供的服务暴露出去,所以需要配置dubbo的配置文件,这里写到了application-context.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://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://dubbo.apache.org/schema/dubbo
http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!-- 提供方应用信息,用于计算依赖关系 -->
<dubbo:application name="hello-world-producer" />
<!-- 使用zookeeper注册中心暴露服务地址 -->
<dubbo:registry address="localhost:2181" protocol="zookeeper"/>
<!-- 用dubbo协议在20880端口暴露服务 -->
<dubbo:protocol name="dubbo" port="20880" />
<!-- 和本地bean一样实现服务 -->
<bean id="sayHelloService" class="personal.johnson.dubbostudy.producer.serviceImpl.SayHelloServiceImpl" />
<!-- 声明需要暴露的服务接口 -->
<dubbo:service interface="personal.johnson.dubbostudy.commonapi.api.SayHelloService" ref="sayHelloService" />
</beans>
服务端即实现了bean,又配置了dubbo配置文件,现在需要做的就是启动容器,提供服务就好了,但之前要先启动注册中心。
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.concurrent.CountDownLatch;
public class ProducerTest {
public static void main(String[] args){
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:application-context.xml");
ctx.start();
System.out.println("服务提供者启动");
try {
new CountDownLatch(1).await();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
ctx.close();
}
}
}
到目前为止,服务提供者需要做的事情就完毕了。再来看看服务消费者需要做的事,服务消费者事通过dubbo来调用远端配置,dubbo会根据配置生成一个代理类放在容器中,所以先进行配置。
<?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://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://dubbo.apache.org/schema/dubbo
http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 -->
<dubbo:application name="helloworld-app-consumer" />
<!-- 使用multicast广播注册中心暴露发现服务地址 -->
<dubbo:registry protocol="zookeeper" address="localhost:2181" />
<!-- 生成远程服务代理,可以和本地bean一样使用demoService -->
<dubbo:reference id="sayHelloService" interface="personal.johnson.dubbostudy.commonapi.api.SayHelloService" />
</beans>
再容器启动后,dubbo会生成个代理类,直接从容器中使用这个bean就可以了。
import org.springframework.context.support.ClassPathXmlApplicationContext;
import personal.johnson.dubbostudy.commonapi.api.SayHelloService;
public class ConsumerTest {
public static void main (String[] args){
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:application-context.xml");
ctx.start();
SayHelloService service = ctx.getBean("sayHelloService",SayHelloService.class);
String statement = service.sayHello("dubbo");
System.out.println(statement);
}
}
看一下效果:
服务启动前,zookeeper的节点状态:
启动服务提供者:
因为确实比较简单,不怎么需要日志信息,这里就没配置log4j.properties文件了。
再次看看zookeeper的节点状态:
可以看到服务提供者已经将自身的相关信息注册到了注册中心。
启动客户端看客户端是否能够调用远程服务:
可以看到远程服务确实完成了,目前已经能够简单的使用dubbo了,接下来还需要对dubbo有更加深入的了解。