SpringBoot+Dubbo搭建简单的分布式服务
1、重要的概念
分布式、RPC、Dubbo
1.1、分布式
分布式或者说 SOA 分布式重要的就是面向服务,说简单的分布式就是我们把整个系统拆分成不同的服务然后将这些服务放在不同的服务器上减轻单体服务的压力提高并发量和性能。比如电商系统可以简单地拆分成订单系统、商品系统、登录系统等等。
1.2、 RPC
RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。比如两个不同的服务A,B部署在两台不同的机器上,那么服务 A 如果想要调用服务 B 中的某个方法该怎么办呢?使用 HTTP请求 当然可以,但是可能会比较慢而且一些优化做的并不好。RPC 的出现就是为了解决这个问题。
1.3、Dubbo
Apache Dubbo 是一款高性能、轻量级的开源Java RPC 框架,它提供了三大核心能力:面向接口的远程方法调用、智能容错和负载均衡、以及服务自动注册和发现。简单来说 Dubbo 是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。
1.4、Dubbo 架构
Dubbo 的架构,Dubbo 官方推荐使用 zookeeper 作为注册中心
上述节点简单说明:
- Provider 暴露服务的服务提供方
- Consumer 调用远程服务的服务消费方
- Registry 服务注册与发现的注册中心
- Monitor 统计服务的调用次数和调用时间的监控中心
- Container 服务运行容器
调用关系说明:
- 服务容器负责启动,加载,运行服务提供者。
- 服务提供者在启动时,向注册中心注册自己提供的服务。
- 服务消费者在启动时,向注册中心订阅自己所需的服务。
- 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
- 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
- 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
1.5、使用Dubbo的原因
其实开发分布式程序,也可以直接基于 HTTP 接口进行通信,但是为什么要用 Dubbo呢?
主要可以从 Dubbo 提供的下面四点特性来说为什么要用 Dubbo:
- 负载均衡——同一个服务部署在不同的机器时该调用那一台机器上的服务
- 服务调用链路生成——服务之间互相是如何调用的
- 服务访问压力以及时长统计——当前系统的压力主要在哪里,如何来扩容和优化
- 服务降级——某个服务挂掉之后调用备用服务
2、搭建分布式服务(开发工具:Idea)
2.1、zookeeper 环境安装搭建
1. Windows环境下
1. 下载(3.5.5开始下载带bin的版本)
下载链接:http://archive.apache.org/dist/zookeeper/
2. 解压
进入apache-zookeeper-3.5.6-bin目录
3. 创建data目录
在apache-zookeeper-3.5.6-bin目录下创建data目录
4. 配置文件复制改名
在conf目录下将 zoo_sample.cfg 文件复制一份改名为 zoo.cfg
5. 修改配置文件zoo.cfg
修改dataDir的值改为
dataDir=../data
6. 启动测试
进入bin目录下启动服务端,在地址输入cmd回车,输入zkServer.cmd回车启动服务端
同样的方式打开一个dos窗口输入,输入zkCli.cmd启动客户端启动,在客户端输入几个命令看是否启动成功
ls / :查看节点
create -e /test 123456:创建节点
get /test:获取节点
2. Linux环境下
Linux环境: CentOS 7.4
注意:如果是阿里云服务器必须配置一个安全组,不然应用程序会无法访问zookeeper 服务器
我使用的是 CentOS 7.4 阿里云服务器,注意:如果你也同样阿里云服务器必须配置一个安全组,不然你的应用程序会无法访问你的 zookeeper 服务器,这一点我在后面也提到了。
1. 下载
下载链接: http://mirror.bit.edu.cn/apache/zookeeper/
下载完成后上传到Linux上
或者直接在Linux中使用 wget https://archive.apache.org/dist/zookeeper/zookeeper-3.5.6/zookeeper-3.5.6.tar.gz
命令下载
2. 解压
tar -zxvf zookeeper-3.5.6.tar.gz
解压完毕之后修改一下解压之后所得的文件夹名
mv zookeeper-3.5.6 zookeeper
删除 zookeeper 安装包
rm -rf zookeeper-3.5.6.tar.gz
3. 创建data目录
进入zookeeper目录,创建data文件夹
mkdir data
进入 data 文件夹 然后执行pwd
命令,复制所得的当前目录位置
4. 配置文件复制改名
进入/zookeeper/conf目录下,复制zoo_sample.cfg,命名为zoo.cfg
cp zoo_sample.cfg zoo.cfg
5. 修改配置文件
使用 vim zoo.cfg
命令修改配置文件
vim 文件------>进入文件----->命令模式------>按i进入编辑模式----->编辑文件 ------->按Esc进入底行模式----->输入:wq/q! (输入wq代表写入内容并退出,即保存;输入q!代表强制退出不保存。)
修改配置文件中的 data 属性:
dataDir=/usr/local/zookeeper/data
6. 启动测试
进入 /zookeeper/bin 目录然后执行下面的命令
./zkServer.sh start
执行 ./zkServer.sh status
查看当前 zookeeper 状态。
或者运行 netstat -lntup
命令查看网络状态,可以看到 zookeeper 的端口号 2181 已经被占用
注意没有关闭防火墙可能出现的问题!!!
如果使用的是阿里云服务器,注意配置相关安全组:
-
进入本实例安全组页面
-
选择配置规则
-
选择添加安全组规则,然后按照下图配置
2.2、模拟需求
某个电商系统,订单服务需要调用用户服务获取某个用户的信息
模块 | 功能 |
---|---|
订单服务Web模块 | 创建订单等 |
用户服务Service模块 | 查询用户信息等 |
测试预期结果:订单服务Web模块在A服务器,用户服务模块在B服务器,A可以远程调用B的功能
1. 工程架构
建议将服务接口、服务模型、服务异常等均放在 API 包中;
在搭建项目时建议将接口项目、服务提供者以及服务消费者都放在同一空包下
根据Dubbo官方文档服务最佳实践
最终项目样式
2、主要思路
1、将服务提供者注册到注册中心(暴露服务)
2、让服务消费者从注册中心订阅服务提供者的服务地址(消费服务)
2.3、创建一个空项目springboot-dubbo
作用:用于将接口项目、服务提供者以及服务消费者都放在同一空包下
File–>New–>Project–>Empty Project
2.4、创建接口项目:dubbo-interface
作用:dubbo-interface 会被打成 jar 包,它的作用只是提供接口。
主要步骤:
1. 创建 Maven 项目;
2. 创建接口类
3. 将项目打成 jar 包供其他项目使用
1. dubbo-interface 项目创建
File->New->Module… ,然后选择Maven类型的项目,其他的按照提示一步一步走就好。
2. pom文件内容
<?xml version="1.0" encoding="UTF-8"?>
<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>com.shiory</groupId>
<artifactId>dubbo-interface</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<!-- lombok依赖 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
3. 创建用户实体类与接口
实体类,需要进行序列化,不然无法调用,实现Serializable接口即可
import lombok.Data;
import java.io.Serializable;
/**
* 用户信息实体类
* @author 汐小旅Shiory
* @Date 2019年10月31日10:06:09
*/
@Data
public class User implements Serializable {
/**
* 用户id
*/
private Long id;
/**
* 用户名
*/
private String username;
/**
* 用户密码
*/
private String password;
/**
* 用户收货地址
*/
private String address;
public User(){
}
public User(Long id, String username, String password, String address){
this.id = id;
this.username = username;
this.password = password;
this.address = address;
}
}
接口
/**
* 用户服务接口
* @author 汐小旅Shiory
* @Date 2019年10月31日10:07:25
*/
public interface UserService {
/**
* 根据用户id查询用户信息
* @param uid
* @return
*/
public User queryUserById(long uid);
}
4.创建订单接口
/**
* 订单服务接口
* @author 汐小旅Shiory
* @Date 2019年10月31日10:13:25
*/
public interface OrderService {
/**
* 创建订单
* @param userId
*/
public User createOrder(long userId);
}
5.dubbo-interface项目最终项目结构
6. 将项目打成 jar 包
将项目打成 jar 包,安装到maven仓库供其他项目使用
点击右边的 Maven 然后选择Lifecycle --> install ,这样 jar包安装到maven仓库了。
2.5、创建服务提供者:dubbo-provider
作用:用于提供服务供服务消费者使用
主要步骤:
1. 创建springboot项目;
2. 加入dubbo、zookeeper以及接口的相关依赖 jar 包;
3. 在application.properties 配置文件中配置 dubbo 相关信息;
4. 实现接口类;
5. 服务提供者启动类编写
1. dubbo-provider 项目创建
注意在项目创建之前不要选中dubbo-interface,不然就会是在dubbo-interface上创建项目,所以选中非dubbo-interface的位置
File->New->Module… ,然后选择 Spring Initializr类型的项目
此步注意勾选上 web 模块
2. pom 文件引入相关依赖
需要引入 dubbo 、zookeeper以及接口的相关依赖 jar 包。
注意将本项目和 dubbo-interface 项目的 dependency 依赖的 groupId 和 artifactId 改成自己的。dubbo 整合spring boot 的 jar 包可在下面链接中找到dubbo-spring-boot-starter。注意是阿里巴巴的dubbo
https://mvnrepository.com/artifact/com.alibaba.boot/dubbo-spring-boot-starter
其中dubbo-spring-boot-starter的0.2.0版本已经包含了zookeeper的客户端操作依赖,所以无需再添加。
也可以从dubbo的github上找到,根据对应的SpringBoot版本导入对应的dubbo依赖,此处使用的SpringBoot版本是2.x版本,由于高版本会有很多问题,此处不使用0.2.1.RELEASE版本,而使用0.2.0,
链接:https://github.com/apache/dubbo-spring-boot-project
pom文件中的依赖
<dependencies>
<!-- Web依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 引入我们自己安装到仓库的dubbo-interface项目依赖 -->
<dependency>
<groupId>com.shiory</groupId>
<artifactId>dubbo-interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- 引入dubbo-starter依赖-->
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>0.2.0</version>
</dependency>
</dependencies>
3. 配置服务提供者
在 application.properties 配置文件中配置 dubbo 相关信息
配置很简单,这主要得益于 springboot 整合 dubbo 专属的@EnableDubbo
注解提供的 Dubbo 自动配置。
# 应用服务端口
server.port=8081
# 1.指定当前服务的名称/应用名称(同样的服务名称相同,不要和别的服务同名)
dubbo.application.name=dubbo-provider
# 2.指定注册中心的位置(协议、地址:IP:端口)
dubbo.registry.protocol=zookeeper
dubbo.registry.address=127.0.0.1:2181
# 3.指定通信规则(通信协议、通信端口)
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880
# 4.连接监控中心(使用协议的方式:让它自己从注册中心发现)
#dubbo.monitor.protocol=registry
# 5.暴露服务:在Service接口实现类中使用dubbo中的@Service注解,再加上一个Spring的@Component注解用于将实现类加到容器中
# 6.开启基于注解的Dubbo功能:在主程序类中加入@EnableDubbo注解
4. 实现接口
注意: @Service 注解使用的时 Dubbo 提供的而不是 Spring 提供的。另外,加了Dubbo 提供的 @Service
注解之后还需要加入Spring的==@Component==注解
import com.alibaba.dubbo.config.annotation.Service;
import org.springframework.stereotype.Component;
/**
* 用户服务提供者实现用户接口
* @author 汐小旅Shiory
* @Date 2019年10月31日10:39:54
*/
@Service //暴露服务:用Dubbo中的@Service注解,而不是Spring中的@Service注解
@Component("userService")
public class UserServiceImpl implements UserService {
@Override
public User queryUserById(long uid) {
//模拟从数据库查询到的用户信息
if (uid == 2L) {
User user = new User(2L,"admin","123456","杭州");
return user;
}
return new User();
}
}
5. 服务提供者启动类编写
注意:不要忘记加上 @EnableDubbo 注解开启基于注解Dubbo功能。
import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@EnableDubbo //开启基于注解的Dubbo功能
@SpringBootApplication
public class DubboProviderApplication {
public static void main(String[] args) {
SpringApplication.run(DubboProviderApplication.class, args);
}
}
2.6、创建服务消费者 dubbo-consumer
作用:用于消费服务(远程调用服务)
主要步骤:
1. 创建springboot项目;
2. 加入dubbo、zookeeper以及接口的相关依赖 jar 包;
3. 在application.properties 配置文件中配置 dubbo 相关信息;
4. 实现接口类;
5. 服务提供者启动类编写
1. dubbo-consumer 项目创建
和提供者创建一致
2. pom 文件引入相关依赖
和提供者的pom文件中的依赖一致
3. 配置服务消费者
# 应用服务端口
server.port=8082
# 1.指定当前服务的名称/应用名称(同样的服务名称相同,不要和别的服务同名)
dubbo.application.name=dubbo-consumer
# 2.指定注册中心的位置(协议、地址)
dubbo.registry.protocol=zookeeper
dubbo.registry.address=127.0.0.1:2181
# 3.指定通信规则(通信协议、通信端口)
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880
# 4.连接监控中心(使用协议的方式:让它自己从注册中心发现)
#dubbo.monitor.protocol=registry
# 5.调用远程服务:使用@Reference注解注入远程服务
# 6.开启基于注解的Dubbo功能:在主程序类中加入@EnableDubbo注解
4. 实现接口
在Order接口的实现类中调用远程服务(也可以在Controller层调用),使用==@Reference==注解注入远程服务
import com.alibaba.dubbo.config.annotation.Reference;
import org.springframework.stereotype.Service;
/**
* 订单服务层
* @author 汐小旅Shiory
* @Date 2019年10月31日13:28:17
*/
@Service("orderService")
public class OrderServiceImpl implements OrderService {
/**
* 使用@Reference注解注入远程服务
*/
@Reference(interfaceName = "userService")
private UserService userService;
@Override
public User createOrder(long userId) {
// 远程调用UserService(服务提供者)
User user = userService.queryUserById(userId);
return user;
}
}
5. 编写一个简单 Controller
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
/**
* 订单服务web层
* @author 汐小旅Shiory
* @Date 2019年10月31日13:24:58
*/
@RequestMapping("/order")
@RestController
public class OrderController {
@Resource(name = "orderService")
private OrderService orderService;
@GetMapping(value = "{uid}")
public ResponseEntity<User> createOrder(@PathVariable("uid") long userId){
User user = orderService.createOrder(userId);
if (user == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(user);
}
}
6. 服务消费者启动类编写
注意:不要忘记加上 @EnableDubbo 注解开启基于注解Dubbo功能。
import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@EnableDubbo //开启基于注解的Dubbo功能
@SpringBootApplication
public class DubboConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(DubboConsumerApplication.class, args);
}
}
3. 测试效果
保证zookeeper已经启动,启动dubbo-provider、dubbo-consumer
浏览器访问 http://localhost:8082/order/2,页面输出 User的信息,测试成功