背景
本文记录Dubbo+Zookeeper环境实现微服务的过程。
常见的微服务架构除了Dubbo+Zookeeper之外,还有SpringCloud、K8S等架构,后续会发布这两个架构的搭建过程。
实现
关于Zookeeper、Dubbo在架构中各自担任的角色,为了便于理解,我们把微服务整套架构作如下类比:
Zookeeper,相当于一个游客(实际为调用微服务的业务)可游览的动物园;
Dubbo,相当于动物园里的一个个关着各种动物的笼子,或者是一个个的展区;
而我们用SpringBoot开发的项目代码,则为笼子里关着的各种各样的动物。
通过这样的方式,Dubbo+Zookeeper实现了一个完整的微服务架构。
接下来我们将实操搭建Dubbo+Zookeeper的微服务环境。
Zookeeper
下载
解压
下载的压缩包解压(需解压两次)。
配置文件
将conf目录下的zoo_sample.cfg文件复制一份,重命名为zoo.cfg文件(Linux与Windows同样的操作),并在zoo.cfg中添加一行audit.enable=true
,允许zookeeper加载日志。
注:
- 此步骤不可缺少,否则在Zookeeper服务无法启动(闪退)。
Windows下启动Zookeeper服务
Windows,bin目录下,以管理员身份运行zkServer.cmd。
Linux,运行bin目录的zkServer.sh文件。
测试Zookeeper服务
Windows,运行bin目录下的zkCli.cmd文件进行。
Linux,运行zkCli.sh文件。
Dubbo-admin
注:
- Dubbo为SpringBoot项目。
下载
解压
引入Zookeeper依赖
Dubbo官方说明,如果注册在Zookeeper需要在Dubbo的pom.xml
配置文件中添加如下依赖:
<!--注意:zookeeper依赖的版本务必与项目zookeeper服务端的版本一致!-->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.6.3</version>
</dependency>
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
<version>0.11</version>
</dependency>
注:
zookeeper
依赖的版本要注意与项目的zookeeper
版本一致。
修改端口
需要修改dubbo-admin-server
的properties文件中修改端口,可修改为8080端口之外的端口。否则会与zookeeper产生端口冲突。
打包
解压后进行打包:DOS窗口 - mvn clean package -Dmaven.test.skip=true
注:
- 打包命令需终端有Maven环境,否则无法打包。
- 第一次打包时一般会比较慢,需耐心等待。
- 打包成功后会在目录中生成target目录保存对应的jar包。
启动测试
注:
- 登录地址为
localhost:port
,port为修改端口
章节自行设置的端口号。
可能踩的坑
在登录界面可能会出现如下报错:
System Error, please try again later! Message:Handler dispatch failed; nested exception is java.lang.NoClassDefFoundError: javax/xml/bind/DatatypeConverter
这个问题是因为JAXB API是java EE 的API,因此在javaSE 9.中不再包含这个Jar包。
java9中引入了模块的概念,默认情况下,JavaSE中将不再包含javaEE的Jar包而在java6/7/8时关于这个API都是捆绑在一起的。
排查到原因之后解决就很简单了,只需把JDK换成1.8版本即可解决。
服务注册与发现
注:
- 在本章节为了解决版本兼容问题,把zookeeper服务的版本替换为了
3.5.9
版本。
provider-service服务注册
service接口
package com.langjialing.providerservice.service;
public interface TicketService {
public String getTicket();
}
service接口实现类
package com.langjialing.providerservice.service;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.stereotype.Component;
@Component
@DubboService(version = "v1.0") //dubbo的@Service注解,在项目启动后就自动注册到注册中心
public class TicketServiceImpl implements TicketService {
@Override
public String getTicket() {
return "Ticket";
}
}
可能踩的坑
@DubboService(version = "v1.0")
注解中的version
参数必须要写明,否则服务无法启动。
provider-service的properties
# 应用名称
spring.application.name=providerService
# 应用服务 WEB 访问端口
server.port=8070
# 服务应用名称
dubbo.application.name=providerService
# 注册中心地址
dubbo.registry.address=zookeeper://127.0.0.1:2181
# 哪些服务要被注册
dubbo.scan.base-packages=com.langjialing.providerservice.service
# 修改dubbo的端口以及名称
dubbo.protocol.port=20881
dubbo.protocol.name=dubbo
可能踩的坑
dubbo.protocol.port=xxx;dubbo.protocol.name=dubbo
这两个属性是必须要在properties文件中写明的,否则服务无法启动。
provider-service的pom.xml
需在pom.xml
文件中导入dubbo、zookeeper的依赖,以及需要排除slf4j的依赖。
<!-- 导入Dubbo+zookeeper的依赖-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.8</version>
</dependency>
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
<version>0.1</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.5.9</version>
</dependency>
排除slf4j依赖:
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
可能踩的坑
这里很可能会出现如下几个坑,需务必注意:
- zookeeper依赖的版本需要与zookeeper服务的版本保持一致。
- zookeeper依赖的版本与Curator(zookeeper的依赖)依赖的版本要兼容。
Curator 2.x.x-兼容两个zk 3.4.x 和zk 3.5.x,
Curator 3.x.x-兼容兼容zk 3.5。
服务注册结果
在dubbo-admin中查看注册结果。
consumer-service服务发现
TicketService接口
注:
- 服务发现时,也需要在业务代码中新建一个与TicketServer接口完全一样的文件,并且文件地址也是需要完全相同的。否则服务无法发现。
package com.langjialing.service;
public interface TicketService {
public String getTicket();
}
UserService
package com.langjialing.service;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@Component //注册bean
@RestController
public class UserService {
//到注册中心拿到provider-service提供的票
@DubboReference(version="v1.0", timeout = 5000, check= false)//引用,pom坐标,可以定义路径相同的接口名
TicketService ticketService;
/*
//本地服务时,使用@Autowired
@Autowired
TicketService ticketService;
*/
@GetMapping("/buyTicket")
public String buyTicket(){
String ticket = ticketService.getTicket();
System.out.println("在注册中心获取了一张票=>" + ticket);
return "在注册中心获取了一张票=>" + ticket;
}
}
可能踩的坑
@DubboReference(version="v1.0", timeout = 5000, check= false)
注解中的参数是不可缺少的,否则是无法发现服务的。
测试结果
浏览器访问localhost:8002/buyTicket
,访问地址根据实际项目而定。
至此,Dubbo+Zookeeper环境搭建及服务注册与发现完整结束。
总结
微服务是通过层层密切契合的方式实现分布式的一套解决方案。
从整个Dubbo+Zookeeper的搭建过程中出现的多个依赖或者环境的兼容性问题可以深刻地体会到,把层层不同作用的组件整合起来是一件比较麻烦的事情。不过它的优点也被广泛认可的。
最后,博主需要提醒大家的是:一定要关注各个组件及依赖的版本兼容!