公司为搭建分布式系统学习使用dubbo框架
参考文档: 阿里巴巴Duboo官网
一 、知识点
- SOA(Service Oriented Architecture):面向服务的架构。
- 由 服务治理、服务注册和发现、RPC、监控中心、调度中心以及服务路由、负载均衡等功能模块组成的资源调度和治理中心。
二、 RPC的演进
- 框架演进:ORM–>MVC–>RPC–>SOA
- 架构演进:单一应用架构–>垂直应用架构—>分布式服务架构–>流动计算架构
- 单一应用架构
- 网站流量小的时候,将所有功能集中到单一应用以减少成本。性能瓶颈在于 ORM框架。
- 垂直应用架构
- 网站流量增大,横向扩展带来的效益越来越低,将应用拆分为互不相干的几个应用以提升效率。性能瓶颈在于MVC框架。
- 分布式服务架构
- 当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。 此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。
- 弹性计算架构
- 当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。 此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键。
三、 架构简介
架构图
节点角色
- Provider:服务提供方,在启动时向注册中心注册服务;
- Consumer:服务消费方,向注册中心请求服务提供方列表,在本地做负载均衡调用服务;
- Register:注册中心,提供服务注册与发现(一般由Zookeeper担当),通过长连接与Provider和Consumer保持连接,负责监控Provider的上下线并及时通知Consumer;
- Monitor:监控中心,负责统计服务的性能数据;
- Container:服务运行容器。
调用关系
- “0.start”:服务运行容器启动、加载、运行服务提供方,一般由Spring容器启动Jar运行;
- “1.register”: 服务提供方在启动时,向注册中心注册自己的IP、服务接口等信息;
- “2.subscribe”: 服务消费方向注册中心订阅自己感兴趣的服务提供方;
- “3.notify”: 注册中心在服务提供方发生变更时将基于长连接向消费方推送消息;
- “4.invoke”: 服务消费方在本地对服务列表做软负载均衡算法,选择最优的服务提供方进行RPC调用;
- “5.count”: 消费方和提供方向监控中心 定时异步推送服务调用次数和时间,消费方的包括网络耗时,提供方不包括网络耗时。
性能说明
- 连通性:注册中心通过长连接与Provider和Consumer进行通信,Provider和Consumer直接通过PRC直接调用,其只在启动时向注册中心注册和订阅,注册中心不转发请求,当注册中心宕机,Consumer依然可以通过本地缓存的Provider的地址列表进行调用;Provider和Consumer与监控中心定时地通过异步通信方式进行服务调用信息上传;
- 健壮性: 服务提供者无状态,任意一台宕掉后,不影响使用,服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复。
- 伸缩性:注册中心为对等集群,可动态增加机器部署实例,所有客户端将自动发现新的注册中心,服务提供者无状态,可动态增加机器部署实例,注册中心将推送新的服务提供者信息给消费者。
- 扩展性:当服务集群规模进一步扩大,带动IT治理结构进一步升级,需要实现动态部署,进行弹性计算,现有分布式服务架构不会带来阻力。
四、 使用简介
Dubbo是基于Spring的框架,其配置采用XML和注解的方式,使用Spring的BeanFactory加载Bean,运行在Spring容器中。
服务提供方
定义服务接口
DemoService.java
package com.alibaba.dubbo.demo;
public interface DemoService {
String sayHello(String name);
}
打包成Jar包在服务提供方和消费者处使用,该工程里除了接口定义以外,还有各种实体类和通用工具类
实现服务接口
DemoServiceImpl.java
package com.alibaba.dubbo.demo.provider;
import com.alibaba.dubbo.demo.DemoService;
public class DemoServiceImpl implements DemoService {
public String sayHello(String name) {
return "Hello " + name;
}
}
公司里将接口定义和服务实现分为了两个工程,不便于开发人员使用,建议采用Maven的多项目结构,将接口定义和服务实现定义在同一个父项目下。
暴露服务
- XML方式
<beans>
<!--省略schema--!>
<!-- 提供方应用信息,用于计算依赖关系 -->
<dubbo:application name="hello-world-app" />
<!-- 使用multicast广播注册中心暴露服务地址 -->
<dubbo:registry address="multicast://224.5.6.7:1234" />
<!-- 用dubbo协议在20880端口暴露服务 -->
<dubbo:protocol name="dubbo" port="20880" />
<!-- 声明需要暴露的服务接口 -->
<dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService" />
<!-- 和本地bean一样实现服务 -->
<bean id="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl" />
</beans>
- 注解方式(推荐)
FooServiceImpl.java
import com.alibaba.dubbo.config.annotation.Service;
@Service(version="1.0.0")
public class FooServiceImpl implements FooService {
}
provider.xml
<!-- 公共信息,也可以用dubbo.properties配置 -->
<dubbo:application name="annotation-provider" />
<dubbo:registry address="127.0.0.1:4548" />
<!-- 扫描注解包路径,多个包用逗号分隔,不填pacakge表示扫描当前ApplicationContext中所有的类-->
<dubbo:annotation package="com.foo.bar.service" />
启动
public static void main(String[] args) throws Exception {
//加载xml配置启动Spring的BeanFactory
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"http://10.20.160.198/wiki/display/dubbo/provider.xml"});
context.start();
System.in.read(); // 按任意键退出
}
服务启动时,将向注册中心注册本服务的IP、接口等信息
服务消费方
引用远程服务接口
xml方式
consumer.xml
<!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 -->
<dubbo:application name="consumer-of-helloworld-app" />
<!-- 使用multicast广播注册中心暴露发现服务地址 -->
<dubbo:registry address="multicast://224.5.6.7:1234" />
<!-- 生成远程服务代理,可以和本地bean一样使用demoService -->
<dubbo:reference id="demoService" interface="com.alibaba.dubbo.demo.DemoService" />
注解方式(推荐)
@Component
public class BarAction {
@Reference(version="1.0.0")
private FooService fooService;
}
调用服务
public static void main(String[] args) throws Exception { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"http://10.20.160.198/wiki/display/dubbo/consumer.xml"}); context.start(); DemoService demoService = (DemoService)context.getBean("demoService"); // 获取远程服务代理 String hello = demoService.sayHello("world"); // 执行远程方法 System.out.println( hello ); // 显示调用结果 }