架构设计:系统间通信(15)——服务治理与Dubbo 上篇
1、上篇中“自定义服务治理框架”的问题
在之前的文章中(《架构设计:系统间通信(13)——RPC实例Apache Thrift 下篇(1)》、《架构设计:系统间通信(14)——RPC实例Apache Thrift 下篇(2)》),我们基于服务治理的基本原理,自己实现了一个基于zookeeper + thrift的服务治理框架。但实际上前文中我们自行设计的服务治理框架除了演示基本原理外,并没有多大的实际使用价值,因为还有很多硬性需求是没有实现的:
访问权限:在整个系统生态环境中,不是任何用户都可以随意访问任何的接口。那么除了访问接口的用户组、用户和密码管理以外(或者是公私钥文件),还需要限制用户的访问权限。例如规定用户组A下所有的用户才能够访问接口X。
版本控制:就像上篇文中描述的问题一样。在比较大的系统中,某一个子系统一般都是使用集群环境运行(有多个工作节点),且必须24小时连续工作不允许中断。那么要进行服务接口升级,一般是先升级一部分服务节点,其间让另一部分服务节点继续提供服务,造成了在一个时间段同一个子系统提供的某一个服务可能出现服务接口版本的不兼容。为了子系统能够保证24小时连续提供服务,就需要标注服务接口的版本号,让之前没有完成升级的服务节点提供/访问老版本的服务接口;已经完成升级的服务节点提供/访问新版本的服务接口。
服务时效控制、次数控制:各个子系统提供的服务本身也是具有时效性的。比如某一个服务Y只在每天早上10:00——11:00才能提供访问服务,且对于某个用户来说每天只能调用100次。再比如说,
· `性能措施:性能是评价一款服务治理框架是否优秀的重要标准。在服务治理框架的各层都应该有足够的性能优化措施。例如在服务注册层/服务仓库层,注册通知是如何实现的、“能够提供的”服务列表是否进行本地缓存、缓存何时更新、访问令牌如何实现、如何确定访问节点等等问题都直接影响性能;在RPC层/服务实现层,如何支持非阻塞异步网络IO、是采用现成的网络通信框架(Netty、MINA、Grizzly等等)还是自行编写网络通信框架、如何进行信息的序列化等等问题也会直接影响性能。
其他细节问题:能够应用到生产环境的服务治理框架还需要关注很多细节问题,例如多个子系统所注册服务可能出现的服务名称重复的问题,类似的这种问题如何解决、服务治理框架本身的稳定性如何进行保证、是否需要支持跨语言通信的问题等等。
从这篇文章开始,我们详细介绍一款能够应用到生产环境的服务治理框架:由阿里巴巴开发团开源贡献的DUBBO服务治理框架。希望能够帮助各位读者优化自己的生产环境架构方案。
2、服务治理框架:DUBBO
DUBBO是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,是阿里巴巴SOA服务化治理方案的核心框架,每天为2,000+个服务提供3,000,000,000+次访问量支持,并被广泛应用于阿里巴巴集团的各成员站点。
——摘自DUBBO官网(http://dubbo.io/)
在DUBBO的官网有详细的学习资料和实例代码。这里我们介绍DUBBO服务治理框架,是为了将我们这个系列文章的中心思想:系统间通信原理,讲清楚。那么为了讲清楚DUBBO服务治理框架的工作原理,首先我们就要演示一下它的基本使用方法。
3、基本使用
3-1、准备示例工程
您可以在以下站点下载后文中提到的各种组件:
DUBBO官网和用户手册:
DUBBO服务治理框架V2.4.10版本:
zookeeper官网:
Maven官网:
Maven官方仓库:
3-1-1、示例工程基本环境
- 请在工程中引入maven的支持。并且导入相关的依赖(如果您不知道maven为何物,请按这样的简单步骤做:下载一个纯净的eclipse->在市场搜索maven->安装maven插件->打开设置界面指定maven路径->创建maven工程->打开工程下的pom.xml文件):
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>yinwenjie.test</groupId>
<artifactId>dubboService</artifactId>
<version>0.0.1</version>
<packaging>jar</packaging>
<name>dubboService</name>
<url>http://maven.apache.org</url>
<properties>
<version.log4j>1.2.14</version.log4j>
<version.slf4j.api>1.7.5</version.slf4j.api>
<version.slf4j.log4j12>1.7.5</version.slf4j.log4j12>
<version.slf4j.jcl.over.slf4j>1.7.5</version.slf4j.jcl.over.slf4j>
<!-- jackson -->
<jackson.version>1.9.13</jackson.version>
</properties>
<dependencies>
<!-- apache commons -->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.5</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${version.slf4j.api}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${version.slf4j.jcl.over.slf4j}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${version.slf4j.log4j12}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${version.log4j}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.7.4</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.7.4</version>
</dependency>
<!-- dubbo -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.4.10</version>
</dependency>
<!-- zookeeper -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.3.4</version>
</dependency>
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.3</version>
</dependency>
</dependencies>
</project>
您已经注意到了我们使用的DUBBO的版本为2.4.10,并且准备使用zookeeper作为DUBBO服务管理仓库;zookeeper连接的版本使用3.3.4,还引入了一个0.3版本的zkclient组件。Log4j的支持是我自己的编码习惯,如果您不是用Log4j同样可以运行工程,但是您可以使用System.out的方式输出日志了。
3-1-2、安装并使用zookeeper
如果要使用zookeeper作为DUBBO服务管理仓库,那么您还应该安装zookeeper服务器(单点服务即可)。这篇文章主要讲解的是DUBBO的基本使用,如果您还不了解zookeeper服务的基本知识,可以参考我另外几篇文章《hadoop系列:zookeeper(1)——zookeeper单点和集群安装》、《hadoop系列:zookeeper(2)——zookeeper核心原理(选举)》、《hadoop系列:zookeeper(3)——zookeeper核心原理(事件)》。这里假定您用于测试的zookeeper服务器已经安装成功,并且能够正常使用。
这里要说明一下:在网上能够找到的很多文章中,都是介绍的DUBBO和zookeeper构建工作环境。这让很多读者误以为DUBBO只能和zookeeper联合工作,但实际情况并不是这样的。在DUBBO服务治理框架中,服务管理仓库的主要作用是记录服务状态,通知服务变化,管理服务信息,除了zookeeper以外实际上DUBBO服务治理框架还支持在Redis上实现注册管理仓库。另外还有一个Simple的用于在本地实现一个注册管理仓库,专门用于测试用。
3-3、定义和启动DUBBO-RPC服务器端
由于DUBBO和Spring是无缝集成的,所以当您通过maven引入DUBBO组件时,Spring的基础组件就会被自动引入。DUBBO V2.4.10 版本使用的是Spring 2.5.6的版本。
下面我们首先对DUBBO服务提供端的Spring进行配置:
<?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:context="http://www.springframework.org/schema/context"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- 注解包扫描位置: -->
<context:component-scan base-package="yinwenjie.test.dubboService.*" />
<!-- 接入dubbo的应用程序名称 -->
<dubbo:application name="ws-demo" />
<!-- 注册仓库地址:zookeeper组件,192.168.61.128:2181 -->
<dubbo:registry address="zookeeper://192.168.61.128:2181" />
<!-- 用dubbo协议在20880端口暴露服务 -->
<dubbo:protocol name="dubbo" port="20880" />
<!--
声明需要暴露的服务接口,
请注意ref属性中指定的MyService接口实现类,它并没有在xml文件中定义,而是使用注解的方式在class中定义
-->
<dubbo:service interface="yinwenjie.test.dubboService.iface.MyService" ref="MyServiceImpl"/>
</beans>
下面是RPC服务接口和服务接口实现的代码:
package yinwenjie.test.dubboService.iface;
/**
* 这是服务接口
* @author yinwenjie
*/
public interface MyService {
/**
* 这是一个测试接口
* @param field1 参数1
* @param field2 参数2
* @return 相加后返回
*/
public String doMyTest(String field1 , String field2);
}
服务接口的实现:
package yinwenjie.test.dubboService.impl;
import org.springframework.stereotype.Component;
import yinwenjie.test.dubboService.iface.MyService;
@Component("MyServiceImpl")
public class MyServiceImpl implements MyService {
/* (non-Javadoc)
* @see yinwenjie.test.dubboService.iface.MyService#doMyTest(java.lang.String, java.lang.String)
*/
@Override
public String doMyTest(String field1, String field2) {
return field1 + field2;
}
}
服务接口和服务接口的实现都是非常简单的JAVA代码了,应该不需要进行什么特别的说明了。只需要补充两个注意事项:
可以看出服务接口和服务接口的实现本身并没有引入和RPC结束任何相关的类,也就是说服务接口和服务接口的实现甚至自己都不知道将被某个RPC框架所调用。
dubbo:protocol标签中,我们指定的协议名称为dubbo(小写)。这里的bubbo是指的DUBBO服务治理框架中自带的一种RPC协议。就像前文说到的那样,从官网给出的资料来看,DUBBO服务治理框架还支持rmi、hessian、http、webService、Hessian等多种通信协议。
下面我们启动DUBBO-RPC服务器端:
package yinwenjie.test.dubboService;
import org.apache.log4j.BasicConfigurator;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ServiceMainProcessor {
//Log4j的日志基本配置(要使用这种方式首先引入log4j的支持)
static {
BasicConfigurator.configure();
}
private static Object WAITOBJECT = new Object();
public static void main(String[] args) throws Exception {
new ClassPathXmlApplicationContext(new String[]{"application-service.xml"});
/*
* 这里锁定这个应用程序,和DUBBO框架本身的工作原理没有任何关系,只是为了让其不退出
* 当然您也可以使用ClassPathXmlApplicationContext中的start方法,效果一样。
*
* 个人习惯
* */
synchronized (ServiceMainProcessor.WAITOBJECT) {
ServiceMainProcessor.WAITOBJECT.wait();
}
}
}
3-4、定义和启动DUBBO-RPC客户端
以下是RPC服务消费方(RPC客户端)的xml配置。在我们的测试工程中,客户端和服务端是放在一个工程里面的。放在一个工程里面是为了节约演示程序在工程设置方面不必要的工作量,但是这不影响它们在两个独立的JVM上面运行,也不影响DUBBO服务治理框架工作原理的演示。下面的XML是DUBBO服务治理框架RPC客户端的spring配置演示:
<?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://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- 客户端应用程称呼名称 -->
<dubbo:application name="ws-demo-client" />
<!-- 注册仓库地址:zookeeper组件,192.168.61.128:2181 -->
<dubbo:registry address="zookeeper://192.168.61.128:2181" />
<!-- 引用的服务,那个interface一定是一个被引入到DUBBO仓库的接口定义 -->
<dubbo:reference id="myService" interface="yinwenjie.test.dubboService.iface.MyService"/>
</beans>
对于DUBBO-RPC框架来说,它其中的服务消费者并不需要进行多余的任何定义。只需要知道要访问的服务接口(和定义的服务版本,如果没有定义服务版本默认为0.0.0)。下面的代码将启动DUBBO-RPC客户端,并且完成RPC调用:
package yinwenjie.test.dubboClient;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.BasicConfigurator;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import yinwenjie.test.dubboService.iface.MyService;
public class ClientMainProcessor {
//Log4j的日志基本配置(要使用这种方式首先引入log4j的支持)
static {
BasicConfigurator.configure();
}
/**
* 日志
*/
private static final Log LOGGER = LogFactory.getLog(ClientMainProcessor.class);
/**
* 锁定用
*/
private static Object WAITOBJECT = new Object();
public static void main(String[] args) throws Exception {
ApplicationContext app = new ClassPathXmlApplicationContext(new String[]{"application-client.xml"});
// 开始RPC调用
MyService myService = (MyService)app.getBean("myService");
LOGGER.info("myService = " + myService.doMyTest("1234", "abcde"));
// 这里锁定这个应用程序,和DUBBO框架本身的工作原理没有任何关系,只是为了让其不退出
synchronized (ClientMainProcessor.WAITOBJECT) {
ClientMainProcessor.WAITOBJECT.wait();
}
}
}
3-5、运行效果
我们先来看看DUBBO服务治理框架中服务提供端的运行效果:
一定要注意运行效果是加上了Log4j的效果的,如果您没有加载Log4j的依赖,那么应该用System.out进行控制台输出。另外,zookeeper服务器一定要安装好,并且可以正常使用。
以下是DUBBO服务治理框架服务消费者(客户端)的运行效果:
上图红圈处Log4j所打印出来的调用内容,说明DUBBO服务治理框架已经完成了服务实现的调用。
在这个章节中,我们使用的DUBBO的配置文件都是最简化的配置信息。实际上包括service、reference、protocol、registry、application、monitor、module、provider、consumer等在内的DUBBO服务治理框架标签提供了丰富的配置属性。您可以参考官网http://dubbo.io/Configuration+Reference-zh.htm中的详细说明,在下篇文章我们对DUBBO运行原理的讲解中,还会提及其中常用的、比较重要的标签作用。
4、后文介绍
通过这篇文章的讲解,相信读者已经掌握了DUBBO服务治理框架的基本使用方式。下一篇文章开始,我们将介绍DUBBO服务治理框架的运行原理,从它的运行原理中重点窥探我们这个系列讨论中讨论的核心话题:网络IO模型、通信协议、消息封装格式以及这些元素对服务性能的影响。