一、初识Dubbo
1、Dubbo是什么
- 轻量级,高性能的RPC框架
- 并不是要成为一个微服务的全面解决方案
- 以Java语言而出名
2、Dubbo现状
- 全称是Apache Dubbo
- 微店,网易云音乐,考拉,滴滴,中国电信,人寿
3、Dubbo的故事:主要历程
- 09年开始做,做的第1个版本
- 10年初的时候,架构升级,Dubbo 2.0
- 开源
- one company战略
- 合到HSF去
- 第3节点,捐给Apache
二、RPC介绍
- RPC———远程过程调用
- 早期单机时代:IPC
- 网络时代:把IPC扩展到网络上,这就是RPC
- 实现RPC很头疼,于是就有了RPC框架
- 调用其他机器上的程序和调用本地的程序一样方便
常见的RPC框架
- 阿里的Dubbo
- 新浪的Montan
- Facebook的Thrift
- 各个框架都有各自的优缺点
HTTP和RPC对比
- 传输效率
- 性能消耗,主要在于序列化和反序列化的耗时
- 负载均衡
三、Dubbo工作原理
1、服务容器负责启动,加载,运行服务提供者
2、服务提供者在启动时,向注册中心注册自己提供的服务
3、服务消费者在启动时,向注册中心订阅自己所需的服务
4、注册中心返回服务提供者地址列表给消费者
5、从提供者地址列表中,选一台提供者进行调用
6、定期发送一次统计数据到监控中心
四、案例实操:项目编写
以实现课程查询的接口,来实操整合Dubbo和Zookeeper,并实现服务间的调用。
1、项目创建
项目创建IDEA创建Maven项目,删除原src,创建子模块,项目结构如下:
父模块依赖添加pom.xml:
parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.12.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>1.8</java.version>
<spring-boot.version>2.1.12.RELEASE</spring-boot.version>
<dubbo.version>2.7.4.1</dubbo.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Apache Dubbo -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-bom</artifactId>
<version>${dubbo.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>${dubbo.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
</exclusion>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</dependencyManagement>
2、服务提供者(producer)的开发
producer模块引入依赖pom.xml:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Dubbo Spring Boot Starter -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.4.1</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
</dependency>
<!-- Zookeeper dependencies -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper</artifactId>
<version>${dubbo.version}</version>
<type>pom</type>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Web 功能 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MySQL connector, 需要与 MySQL 版本对应 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- MyBatis依赖-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
</dependencies>
entity包下的实体类Course.java(生成get和set方法):
public class Course implements Serializable {
Integer id;
Integer courseId;
String name;
//1上架,0下架
Integer valid;
}
mapper包下的CourseMapper.java:
@Mapper
@Repository
public interface CourseMapper {
@Select("SELECT * FROM course WHERE valid = 1")
List<Course> findValidCourses();
}
service包下的CourseListService.java:
public interface CourseListService {
List<Course> getCourseList();
}
impl包下的CourseListServiceImpl.java(需要注意的是@Service的包将不在是我们平时的http,而是换成了dubbo):
import com.ygayddcxy.producer.entity.Course;
import com.ygayddcxy.producer.mapper.CourseMapper;
import com.ygayddcxy.producer.service.CourseListService;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
@Service(version = "${demo.service.version}")
public class CourseListServiceImpl implements CourseListService {
@Autowired
private CourseMapper courseMapper;
@Override
public List<Course> getCourseList() {
return courseMapper.findValidCourses();
}
}
application.properties配置:
demo.service.version=1.0.0
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/course_prepare?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
spring.datasource.username=root
spring.datasource.password=1214
logging.pattern.console=%clr(%d{${LOG_DATEFORMAT_PATTERN:HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:%wEx}
spring.application.name=course-list
#dubbo协议
dubbo.protocol.name=dubbo
#-1随机找一个端口号
dubbo.protocol.port=-1
#dubbo注册
dubbo.registry.address=zookeeper://192.168.17.132:2181
# 配置中心连接时间改为20秒
dubbo.config-center.timeout=20000
dubbo.registry.file=${user.home}/dubbo-cache/${spring.application.name}/dubbo.cache
mybatis.configuration.map-underscore-to-camel-case=true
dubbo.scan.base-packages=com.ygayddcxy.producer.service.impl
服务提供者的启动类DubboProducerApplication.java:
@EnableAutoConfiguration
public class DubboProducerApplication {
public static void main(String[] args) {
SpringApplication.run(DubboProducerApplication.class, args);
}
}
3、服务消费方(consumer)的开发
先引入依赖pom.xml:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Dubbo Spring Boot Starter -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.4.1</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
</dependency>
<!-- Zookeeper dependencies -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper</artifactId>
<version>${dubbo.version}</version>
<type>pom</type>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Web 功能 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MySQL connector, 需要与 MySQL 版本对应 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- MyBatis依赖-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>com.ygayddcxy</groupId>
<artifactId>producer</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
添加application.properties配置:
demo.service.version=1.0.0
server.port=8084
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/course_prepare?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
spring.datasource.username=root
spring.datasource.password=1214
logging.pattern.console=%clr(%d{${LOG_DATEFORMAT_PATTERN:HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:%wEx}
spring.application.name=course-price
#dubbo协议
dubbo.protocol.name=dubbo
#-1随机找一个端口号
dubbo.protocol.port=-1
#dubbo注册
dubbo.registry.address=zookeeper://192.168.17.132:2181
# 配置中心连接时间改为20秒
dubbo.config-center.timeout=20000
dubbo.registry.file=${user.home}/dubbo-cache/${spring.application.name}/dubbo.cache
在entity包下添加实体类:
添加实体类CoursePrice.java(自行补充get和set):
public class CoursePrice implements Serializable {
Integer id;
Integer courseId;
Integer price;
}
添加实体类CourseAndPrice.java(自行补充get和set):
public class CourseAndPrice implements Serializable {
Integer id;
Integer courseId;
String name;
Integer price;
}
在dao包下:
添加CoursePriceMapper.java:
@Mapper
@Repository
public interface CoursePriceMapper {
@Select("SELECT * FROM course_price WHERE course_id = #{courseId}")
CoursePrice findCoursePrices(Integer courseId);
}
在service包下添加CoursePriceService.java:
// 课程价格服务
public interface CoursePriceService {
CoursePrice getCoursePrice(Integer courseId);
List<CourseAndPrice> getCoursesAndPrice();
}
在impl包下添加CoursePriceServiceImpl.java:
// 课程价格服务
@Service
public class CoursePriceServiceImpl implements CoursePriceService {
@Autowired
CoursePriceMapper coursePriceMapper;
@Reference(version = "${demo.service.version}")
CourseListService courseListService;
@Override
public CoursePrice getCoursePrice(Integer courseId) {
return coursePriceMapper.findCoursePrices(courseId);
}
@Override
public List<CourseAndPrice> getCoursesAndPrice() {
List<CourseAndPrice> courseAndPriceList = new ArrayList<>();
List<Course> courseList = courseListService.getCourseList();
for (int i = 0; i < courseList.size(); i++) {
Course course = courseList.get(i);
if (course != null) {
CoursePrice price = getCoursePrice(course.getCourseId());
if (price != null && price.getPrice() > 0) {
CourseAndPrice courseAndPrice = new CourseAndPrice();
courseAndPrice.setId(course.getId());
courseAndPrice.setCourseId(course.getCourseId());
courseAndPrice.setName(course.getName());
courseAndPrice.setPrice(price.getPrice());
courseAndPriceList.add(courseAndPrice);
}
}
}
return courseAndPriceList;
}
}
在controller包下添加CoursePriceController.java:
@RestController
public class CoursePriceController {
@Autowired
CoursePriceService coursePriceService;
@GetMapping({"/price"})
public Integer getCoursePrice(Integer courseId) {
CoursePrice coursePrice = coursePriceService.getCoursePrice(courseId);
if (coursePrice != null) {
return coursePrice.getPrice();
} else {
return -1;
}
}
@GetMapping({"/coursesAndPrice"})
public List<CourseAndPrice> getCoursesAndPrice() {
return coursePriceService.getCoursesAndPrice();
}
}
添加服务消费方的启动类DubboConsumerApplication.java:
@SpringBootApplication
public class DubboConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(DubboConsumerApplication.class, args);
}
}
至此项目的代码实现便写到这里,接下来便是测试项目,首先需要启动Zookeeper,然后在启动服务提供者,最后在启动服务消费方。
服务提供者:
服务消费方:
项目启动完成后,浏览器访问:http://127.0.0.1:8084/coursesAndPrice
案例实操
- 自动检查zk和依赖的服务
- dubbo.scan.base-packages配置
- 实现服务间调用
最后在附上用到的数据库表结构命令:
资料:数据库结构与数据
/*
Navicat Premium Data Transfer
Source Server : MySQL
Source Server Type : MySQL
Source Server Version : 80022
Source Host : localhost:3306
Source Schema : course_prepare
Target Server Type : MySQL
Target Server Version : 80022
File Encoding : 65001
Date: 25/04/2022 00:11:20
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for course
-- ----------------------------
DROP TABLE IF EXISTS `course`;
CREATE TABLE `course` (
`id` int(0) NOT NULL,
`course_id` int(0) NULL DEFAULT NULL,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL,
`valid` int(0) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of course
-- ----------------------------
INSERT INTO `course` VALUES (1, 1, '深入理解Java虚拟机', 1);
INSERT INTO `course` VALUES (2, 2, 'Java编程思想', 1);
-- ----------------------------
-- Table structure for course_price
-- ----------------------------
DROP TABLE IF EXISTS `course_price`;
CREATE TABLE `course_price` (
`id` int(0) NOT NULL,
`course_id` int(0) NULL DEFAULT NULL,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL,
`price` int(0) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of course_price
-- ----------------------------
INSERT INTO `course_price` VALUES (1, 1, '深入理解Java虚拟机', 399);
INSERT INTO `course_price` VALUES (2, 2, 'Java编程思想', 999);
SET FOREIGN_KEY_CHECKS = 1;