集成Dubbo和Zookeeper

  • 1.Zookeeper的介绍与安装
  • 1.1 Zookeeper概述
  • 1.2 Zookeeper的安装与启动
  • 2.Spring Boot集成Dubbo
  • 2.1 Dubbo概述
  • 2.2 服务与接口拆分思路
  • 2.3 服务与接口拆分实践
  • 2.4 正式版发布
  • 2.5 Service服务端开发
  • 2.6 Service服务注册
  • 2.7 Client客户端开发


本章主要介绍如何安装并运行Zookeeper,Spring Boot集成Dubbo,spring-boot-book-v2项目的服务拆分和实践,正式版API如何发布,服务注册等内容。

1.Zookeeper的介绍与安装

1.1 Zookeeper概述

ZooKeeper是一个开源的、分布式应用程序协调服务,它提供的功能包括命名服务配置管理集群管理分布式锁等。

  1. 命名服务。可以简单理解为电话簿。电话号码不好记,但是人名好记。要打谁的电话,直接查人名就好了。分布式环境下,经常需要对应用/服务进行统一命名,便于识别不同的服务。类似于域名与IP之间的对应关系,域名容易记住。ZooKeeper通过名称来获取资源和服务的地址以及提供者等信息。
  2. 配置管理。分布式系统都有大量的服务器,比如在搭建Hadoop的HDFS的时候,需要在一台Master主机器上配置好HDFS需要的各种配置文件,然后通过scp命令把这些配置文件拷贝到其他节点上,这样各个机器拿到的配置信息是一致的,才能成功运行HDFS服务。Zookeeper提供了这样的一种服务,集中管理配置的方法,在这个集中的地方修改了配置,所有对这个配置感兴趣的都可以获得变更。这样就省去了手动复制配置,还保证了可靠和一致性。
  3. 集群管理。所谓集群管理包含两点:是否有机器退出和加入、选举Master。在分布式集群中,经常由于各种原因(比如硬件故障、软件故障、网络问题等),有些新的节点会加入进来,也有老的节点会退出集群。这个时候,集群中有些机器(比如Master节点)需要感知到这种变化,然后根据这种变化做出对应的决策。Zookeeper集群管理可以感知变化并给出对应的策略。
  4. 分布式锁。Zookeeper的一致性文件系统,使得锁的问题变得容易了。锁服务可以分为两类,一类是保持独占,另一类是控制时序。单机程序的各个进程需要对互斥资源进行访问时需要加锁,分布式程序分布在各个主机上的进程对互斥资源进行访问时也需要加锁。很多分布式系统有多个可服务的窗口,但是在某个时刻只让一个服务去干活,当这台服务出问题的时候锁释放,立即fail over到另外的服务。在很多分布式系统中都是这么做的,这种设计有一个更好听的名字叫LeaderElection(leader选举)。举个通俗点的例子,比如银行取钱,有多个窗口,但是,只能有一个窗口对你服务。如果正在对你服务的窗口的柜员突然有急事走了,怎么办呢?找大堂经理(Zookeeper),大堂经理会为你指定另外的窗口继续为你服务。

Zookeeper一个最常用的使用场景是用于担任服务生产者和服务消费者的注册中心,这也是接下来的章节中会使用到的。服务生产者将自己提供的服务注册到Zookeeper中心,服务消费者在进行服务调用的时候先到Zookeeper中查找服务,获取到服务生产者的详细信息之后,再去调用服务生产者的内容与数据。具体如图所示。



keycloak springboot集成 springboot集成zookeeper_java


1.2 Zookeeper的安装与启动

Zookeeper的安装与启动非常简单,具体步骤如下:

  1. 官方网站https://archive.apache.org/dist/zookeeper/下载对应的安装包,这里选用zookeeper-3.3.6版本。
  2. 将下载好的安装包解压到D盘目录下。在解压后的目录中找到配置文件zoo_sample.cfg(存放在D:\zookeeper-3.3.6\zookeeper-3.3.6\conf\目录下),该配置文件是Zookeeper为提供的最简单的配置文件,我们复制一份并重新命名为zoo.cfg,Zookeeper启动时会默认在conf目录下读取zoo.cfg配置文件。
  3. zoo.cfg配置文件生成之后,在目录D:\zookeeper-3.3.6\zookeeper-3.3.6\bin下双击zkServer.cmd文件启动zookeeper(如果是Linux环境,执行zkServer.sh文件)。在命令行窗口中看到如图所示Zookeeper的启动信息,代表Zookeeper安装成功。


keycloak springboot集成 springboot集成zookeeper_zookeeper_02


2.Spring Boot集成Dubbo

2.1 Dubbo概述

Dubbo是阿里巴巴B2B平台技术部开发的一款Java服务平台框架以及SOA治理方案。其功能主要包括:高性能NIO通信及多协议集成服务动态寻址与路由软负载均衡与容错依赖分析与降级等。Dubbo简单的底层框架如图所示。



keycloak springboot集成 springboot集成zookeeper_dubbo_03


Registry是服务注册与发现的注册中心,Provider是暴露服务的服务提供方,Consumer是调用远程服务的服务消费方,Monitor是统计服务的调用次数和调用时间的监控中心,Container是服务运行容器。Dubbo简单的调用关系如下:

  • 服务容器Container负责启动、加载、运行服务提供者Provider。
  • 服务提供者Provider在启动时,向注册中心Registry注册自己提供的服务。
  • 服务消费者Consumer在启动时,向注册中心Registry订阅自己所需的服务。
  • 注册中心Registry返回服务提供者地址列表给消费者Provider,如果有变更,注册中心Registry将基于长连接推送。变更数据给消费者Consumer。
  • 服务消费者Consumer从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
  • 服务消费者Consumer和提供者Provider在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心Monitor。

Dubbo将注册Registry中心进行抽象,使得它可以外接不同的存储媒介给注册中心Registry提供服务,可以作为存储媒介的有ZooKeeperMemcachedRedis等。引入ZooKeeper作为存储媒介,也就把ZooKeeper的特性引进来。首先是负载均衡,单注册中心的承载能力是有限的,在流量达到一定程度的时候就需要分流,负载均衡就是为了分流而存在的。一个ZooKeeper群配合相应的Web应用就可以很容易达到负载均衡;然后是资源同步,单单有负载均衡还不够,节点之间的数据和资源需要同步,ZooKeeper集群天然具备这样的功能;最后是命名服务,将树状结构用于维护全局的服务地址列表,服务提供者在启动的时候,向ZooKeeper上的指定节点目录下写入自己的URL地址,这个操作就完成了服务的发布。ZooKeeper的其他特性还有Mast选举、分布式锁等。ZooKeeper的知识在上节中已经有简单的介绍,这里就不再重复赘述。

2.2 服务与接口拆分思路

spring-boot-book -v2项目已经集成了很多技术,也定义了很多的接口。但是对于真实的项目来说,特别是对于互联网公司项目来说,spring-boot-book-v2这个大的服务承载的内容太多,诸多服务接口(比如AyUserService、AyMoodService、AyRoleService等)糅合在一块对外提供服务,已经违背了微服务理念。因此,我们有必要对spring-boot-book-v2项目进行服务拆分,使它被拆分成一个个小的服务,我们可以按照业务或者功能维度对服务进行拆分。具体如图所示。



keycloak springboot集成 springboot集成zookeeper_spring boot_04


spring-boot-book-v2项目被拆分为用户服务、角色服务、说说服务等。spring-boot-book-v2项目依赖于这些底层的服务为其提供相应的功能,而用户服务、角色服务和说说服务是面向接口API编程,符合基本的编程原则。通过服务的拆分和面向接口编程,对于项目扩展和团队分工都有莫大的好处。

2.3 服务与接口拆分实践

我们已经清楚了服务拆分的原因和好处,本节在项目中实践它。首先,在spring-boot-book-v2项目下添加ay-user-api、ay-mood-api、ay-role-api接口模块。具体步骤如下:

  1. 选择spring-boot-book-v2【右键】→【New】→【Module】,在弹出的窗口中选择【Spring Initializr】,然后单击【Next】按钮。
  2. 在Name输入框中输入模块的名称ay-user-api,其他选项按照默认即可。然后单击【Next】按钮。
  3. 一路单击【Next】按钮,在Module name输入框中输入模块的名称ay-user-api,然后单击【Finish】按钮。

按照上面的步骤建立好ay-user-api模块。再按照相同的步骤,依次在spring-boot-book-v2项目下创建接口模块:ay-mood-api模块、ay-role-api模块和接口模块创建完成之后,按照相同的步骤创建接口对应的服务模块:ay-user-service模块、ay-mood-service模块、ay-role-service模块。接口模块和对应的服务模块创建完成之后,spring-boot-book-v2的项目结构如图所示。



keycloak springboot集成 springboot集成zookeeper_spring boot_05


为了方便,笔者把所有的模块都放在spring-boot-book-v2项目下,在真实的项目中并不是这样的。真实的项目中,会把接口和对应的服务单独建立一个项目,比如为ay-user-api和ay-user-service建立一个项目,ay-role-api和ay-role-service建立一个项目,ay-mood-api和ay-mood-service建立一个项目。这样不同的开发人员单独负责不同的项目,分工合作,提高开发效率。

所有的模块都建立好之后,可以把spring-boot-book-v2项目中的接口移动到对应的接口模块。比如把spring-boot-book-v2项目中的AyUserService接口移动到ay-user-api,把AyMoodService接口移动到ay-mood-api等。同时把实现类AyUserServiceImpl移动到ay-user-service,把实现类AyMoodServiceImpl移动到ay-mood-service等。这里以用户模块为例,讲解整个开发过程。

首先,ay-user-api模块创建完成之后,该模块就是一个spring-boot微服务项目,享有spring-boot为我们默认生成的各种“福利”。我们在ay-user-api包下创建api包和domain包,分别用来存放接口类和实体类,在api包下存放所有spring-boot-book-v2项目移动过来的用户接口。ay-user-api模块的目录如图所示。



keycloak springboot集成 springboot集成zookeeper_dubbo_06


为了方便,在api包下创建AyUserDubboService接口,该接口用来提供与用户相关的服务,比如增删改查等功能。AyUserDubboService类具体代码如下

public interface AyUserDubboService {
	AyUser findByUserNameAndPassword(String name, String password);
}

在AyUserDubboService类中,只有一个通过用户名和密码查询用户的接口findByUserNameAndPassword。domain包下的AyUser类具体代码如下:

public class AyUser{
	//主键
	private String id;
	//用户名
	private String name;
	//密码
	private String password;
	//邮箱
	private String mail;
	//省略set、get方法
}

这里还需要再重复强调一遍,api包下的接口理论上是spring-boot-book-v2项目下的用户接口移动过来的,这里选择创建新的接口只是为了方便而已。

2.4 正式版发布

ay-user-api模块接口开发完成之后,就可以将其发布成正式版jar包。jar包有快照版SNAPSHOT正式版。比如我们创建的ay-user-api模块默认就是快照版0.0.1-SNAPSHOT。快照版的模块是可重复修改的,而正式版的模块不可修改。模块在开发过程中,基本都是SNAPSHOT版。当模块开发并测试完成会选择升级为正式版,而且在项目上线的时候,依赖的模块都必须是正式版,否则会出现意想不到的错误。我们可以在pom文件中修改模块的版本,比如把ay-user-api修改成0.0.1正式版,具体步骤如下:

  1. 在ay-user-api模块中,把pom文件jar包版本version升级为正式版0.0.1,具体如图所示。


keycloak springboot集成 springboot集成zookeeper_dubbo_07


  1. 版本修改之后,我们在Intellij IDEA开发工具右侧单击【clean】、【install】,将ay-user-api的jar包发布到本地Maven仓库,具体如图所示。


keycloak springboot集成 springboot集成zookeeper_zookeeper_08


  1. ay-user-api正式版的jar包发布成功之后,其他项目模块就可以使用它,同时我们可以到Maven仓库查看该jar包的具体信息,具体如图所示。


keycloak springboot集成 springboot集成zookeeper_java_09


2.5 Service服务端开发

ay-user-api接口模块发布为正式版之后,我们继续开发ay-user-service模块。首先在ay-user-service模块中引入ay-user-api的依赖和Dubbo依赖。添加ay-user-api的依赖是因为ay-user-service模块是面向接口编程的,而添加Dubbo依赖是因为服务开发完成之后,要把该服务注册到Zookeeper中,具体代码如下:

<dependency>
	<groupId>com.example</groupId>
	<artifactId>ay-user-api</artifactId>
	<version>0.0.1</version>
</dependency>
<dependency>
	<groupId>io.dubbo.springboot</groupId>
	<artifactId>spring-starter-boot-dubbo</artifactId>
	<version>1.0.0</version>
</dependency>

在ay-user-service模块中添加完依赖之后,我们开发AyUserDubboServiceImpl服务来实现ay-user-api中的接口AyUserDubboService,AyUserDubboServiceImpl的具体代码如下:

import com.alibaba.dubbo.config.annotation.Service;
import com.example.demo.api.AyUserDubboService;
import com.example.demo.domain.AyUser;

// 对外提供用户服务类
@Service(version ="1.0")
public class AyUserDubboServiceImp implements AyUserDubboService {
	@Override
	public AyUser findByUserNameAndPassword(String name, String password){
		//连接数据库,查询用户数据,此处省略
		AyUser ayuser = new AyUser();
		ayUser.setName("阿毅")
		ayUser.setPassword("123456);
		return ayuser;
	}
}
  • @Service:这个注解不是Spring提供的,而是在com.alibaba.dubbo.config.annotation. Service包下面的,这一点要特别注意。在AyUserDubboServiceImpl类上添加@Service注解,就可以把AyUserDubboServiceImpl类注册到Zookeeper服务中心,对外提供服务了。version属性用于指定服务的版本,这里服务版本为1.0。当一个接口出现不兼容升级时,可以用版本号version过渡,版本号不同的服务相互间不引用。
  • findByUserNameAndPassword方法:理论上应该在该方法中通过JPA Repository或者MyBatis查询用户数据,这里为了方便,简单创建AyUser对象返回。

2.6 Service服务注册

ay-user-service模块开发完成之后,由于ay-user-service本身是一个Spring Boot微服务项目,因此我们可以单独运行它。找到ay-user-service模块的入口类AyUserServiceApplication(确保Zookeeper是启动状态),执行入口类的main方法便可启动ay-user-service服务。ay-user-service服务启动完成之后,可以在Intellij IDEA的控制台中查看服务在Zookeeper的注册信息,具体信息如图所示。

keycloak springboot集成 springboot集成zookeeper_spring boot_10

2.7 Client客户端开发

ay-user-api接口模块和ay-user-service服务模块开发完成之后,接下来开始开发客户端。所谓的客户端就是所有ay-user-service服务的对象。现在我们把spring-boot-book-v2作为客户端,spring-boot-book-v2本身也是一个微服务。在spring-boot-book-v2项目中调用ay-user-service模块提供的服务,需要在spring-boot-book-v2项目的pom文件添加ay-user-api依赖和Dubbo依赖,具体代码如下:

<dependency>
	<groupId>com.example</groupId>
	<artifactId>ay-user-api</artifactId>
	<version>0.0.1</version>
</dependency>
<!-- dubbo start -->
<dependency>
	<groupId>io.dubbo.springboot</groupId>
	<artifactId>spring-starter-boot-dubbo</artifactId>
	<version>1.0.0</version>
</dependency>

ay-user-api依赖和Dubbo依赖添加完成之后,就可以在代码中通过@Reference注解将ay-user-service模块提供的AyUserDubboService服务注入进来。具体代码如下:

@Reference(version="1.0")
public AyUserDubboService ayUserDubboService;
  • @Reference注解也是Dubbo框架提供的,在com.alibaba.dubbo.config.annotation.Reference包下。version是@Reference注解的属性,version的版本需要和@Service注解的version版本保持一致,否则服务将无法注入,这一点是需要特别注意的。