【背景】
分布式架构的普遍运用是类似dubbo等SOA框架出现的必然条件,旨在方便各个服务间的相互调用。这里借用dubbo官网的一句话:分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进。
上图可以看出传统的mvc(垂直架构)之后(RPC、SOA)均是将服务与接口分离,以便于多种平台的调用,达到一次开发重复使用的效果,简单的理解就是将service层单独分离出来,而controller层在调用service是夸服务器,传统的方式可以采用httpclient进行服务器间的接口调用,但是那样做除了代码冗余,不方便外,其次就是效率问题。
【架构】
节点说明:
Provider: 暴露服务的服务提供方。
Consumer: 调用远程服务的服务消费方。
Registry: 服务注册与发现的注册中心(服务在调用之前先要注册)。
Monitor: 统计服务的调用次调和调用时间的监控中心。
Container: 服务运行容器。
运行流程:
0 : 容器启动,加载服务(加载服务接口)
1 : 服务提供方向注册中心注册自己的服务(将所有的服务接口信息到注册中心进行注册登记)
2 : 消费方向注册中心发送接口请求(我要调用xxx接口)
3 : 注册中心返回相应的服务地址(你要调用的接口在xxx地址,你去调用吧)
4 :消费方依据服务地址调用服务(消费者拿着注册中心给的地址去调用服务)
5 : Monitor进行服务调用的统计(谁来调用的,什么时间调用的等)
【优点】
(1) 连通性:
注册中心负责服务地址的注册与查找,相当于目录服务,服务提供者和消费者只在启动时与注册中心交互,注册中心不转发请求,压力较小
监控中心负责统计各服务调用次数,调用时间等,统计先在内存汇总后每分钟一次发送到监控中心服务器,并以报表展示
服务提供者向注册中心注册其提供的服务,并汇报调用时间到监控中心,此时间不包含网络开销
服务消费者向注册中心获取服务提供者地址列表,并根据负载算法直接调用提供者,同时汇报调用时间到监控中心,此时间包含网络开销
注册中心,服务提供者,服务消费者三者之间均为长连接,监控中心除外
注册中心通过长连接感知服务提供者的存在,服务提供者宕机,注册中心将立即推送事件通知消费者
注册中心和监控中心全部宕机,不影响已运行的提供者和消费者,消费者在本地缓存了提供者列表
注册中心和监控中心都是可选的,服务消费者可以直连服务提供者
(2) 健状性:
监控中心宕掉不影响使用,只是丢失部分采样数据
数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务
注册中心对等集群,任意一台宕掉后,将自动切换到另一台
注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯
服务提供者无状态,任意一台宕掉后,不影响使用
服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复
(3) 伸缩性:
注册中心为对等集群,可动态增加机器部署实例,所有客户端将自动发现新的注册中心
服务提供者无状态,可动态增加机器部署实例,注册中心将推送新的服务提供者信息给消费者
(4) 升级性:
当服务集群规模进一步扩大,带动IT治理结构进一步升级,需要实现动态部署,进行流动计算,现有分布式服务架构不会带来阻力:
【入门案例】
【1:准备】
导入jar包
dubbo的jar
spring的jar : 做dubbo配置
zk的jar : 做注册中心
启动zookeeper服务
这里建两个工程,分离service与controller模拟跨服务调用
【2:provider服务方】
user.java
public class User implements Serializable {
private String name;
private int age;
public User(){}
public User(String name,int age){
this.name = name;
this.age = age;
}
//getter setter 方法
//tostring 方法
}
SampleService.java
public interface SampleService {
String sayHello(String name);
public List getUsers();
}
SampleServiceImpl.java
public class SampleServiceImpl implements SampleService {
@Override
public String sayHello(String name) {
return "Hello " + name;
}
@Override
public List getUsers() {
List list = new ArrayList<>();
//模拟数据库数据
User user1 = new User("zhangsan",16);
User user2 = new User("lisi",14);
list.add(user1);
list.add(user2);
return list;
}
}
Provider.java
public class Provider {
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"sample-provider.xml"});
context.start();
//为保证服务一直开着,利用输入流阻塞来模拟,开发中,这些应该打jar包在服务器上运行
System.in.read();
}
}
sample-provider.xml配置
运行Provider.java启动注册服务接口
【3:consumer消费方】
user.java
public class User implements Serializable {
private String name;
private int age;
public User(){}
public User(String name,int age){
this.name = name;
this.age = age;
}
//getter setter 方法
//tostring 方法
}
SampleService.java
public interface SampleService {
String sayHello(String name);
public List getUsers();
}
Consumer.java
public class Consumer {
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"sample-consumer.xml"});
context.start();
//获取service
SampleService sampleService = (SampleService) context.getBean("sampleService");
String hello = sampleService.sayHello("xiaojiang");
System.out.println(hello);
List list = sampleService.getUsers();
for(int i = 0 ; i
sample-consumer.xml配置
运行Consumer.java调用服务
这样就完成了项目间的简单调用
总结:
1:服务方提供的接口,消费方也要有相同的接口才能调用,只不过具体的接口实现在服务方
2:zookeeper注册中心可以理解为一个桥梁,记录着服务接口的信息,以供消费方进行调用
3:如果牵扯到具体的引用对象pojo,消费方要对应上服务方
4:xml总结
图解:
具体可以查看dubbo官方文档:http://dubbo.io/