35.2 Dubbo



一、本章内容

1.1 主要内容

二、初识Dubbo

2.1 Dubbo框架:微服务之间,的高性能的RPC通信

官网:

用一句话概括,告诉别人这是什么。很重要。

Dubbo核心定义:微服务之间,的高性能RPC通信

历史:

2.2 RPC:远程调用

Dubbo框架,正是一款RPC理念的框架。当然,也有其他的RPC理念的框架:

2.3 HTTP vs RPC

2.4 Dubbo的工作原理(图)

5个角色

工作原理的图示:

其中:

  • init----------- 初始化
  • sync----------- 同步调用( 后顺序,排队)-------------- same,只能走同一条路
  • async--------- 异步(多个大脑同时思考,多条路同时走)

三、案例实操

以案例,来演示Dubbo的一个使用流程。

3.1 主要过程

以之前的Spring Cloud项目为基础,有两个模块服务:

  • 服务的提供者--------------------- 课程列表 list
  • 服务的调用者、消费者--------- 课程价格 price

3.2 服务提供者的开发:list模块

0.前提准备

创建项目:

这是一个基础的Maven项目:

在此基础上,进行升级:新建一个子模块

删掉src目录,并新建一个子的maven项目

结果如下:

1.引入依赖

在父项目的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>
    <packaging>pom</packaging>
    <modules>
        <module>producer</module>
    </modules>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.12.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.imooc</groupId>
    <artifactId>dubbo-practice</artifactId><!--1.1 老师的这个习惯挺好,备课用的prepare,正课改为practice-->
    <version>0.0.1</version>
    <name>dubbo-practice</name>
    <description>Demo project for Spring Boot</description>

    <!--1.2 相当于属性,一种变量,以方便最下面直接进行引用-->
    <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>
            <!--2.1 引入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>
            <!--2.2 引入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>

</project>
本节课的重点:是Dubbo,以前的熟悉的代码直接复制,并讲解重点以温习理解。

在子模块的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">
    <parent>
        <artifactId>dubbo-practice</artifactId>
        <groupId>com.imooc</groupId>
        <version>0.0.1</version>
    </parent>

    <modelVersion>4.0.0</modelVersion>

    <artifactId>producer</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>


    <dependencies>
        <!--1.1 Spring Boot依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <!--1.2 Spring Boot test相关的依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--1.3 Dubbo相关的两个依赖-->
        <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>
        <!--1.4 引入Zookeeper的依赖-->
        <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>
        <!--1.5 Web功能-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--1.6 mysql-connector,需要与MySQL版本对应-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--1.7 MyBatis依赖-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.1</version>
        </dependency>
    </dependencies>

</project>

2.添加注解@Service

1.service层

注意:

在里,不需要写Controller层 。因为Controller 是建立在Web的思维,对外提供HTTP服务。

但是,现在的服务,并不是一个HTTP服务,而只是一个Dubbo服务,只把service写好即可。

/**
 *  课程列表的服务:从数据库中,拿到课程列表并展示出来
 */
public interface CourseListService {

    List<Course> getCourseList();

}
/**
 *  课程列表服务的实现类
 */
@Service(version = "${demo.service.version}")             // 注意:这里的注解,是来自apache.dubbo框架的,不是Spring的    // 版本号不写死,而是放在配置文件中
public class CourseListServiceImpl implements CourseListService {

    @Resource
    CourseMapper courseMapper;


    @Override
    public List<Course> getCourseList() {
        return courseMapper.findValidCourses();
    }

}

2.mapper层:

/**
 *  课程的mapper类
 */
@Mapper                             // 让MyBatis可以找到它
@Repository                         
public interface CourseMapper {

    @Select("SELECT * FROM course WHERE valid = 1")     // 简单情况,可直接写在注解里   // 复杂:可以用MyBatis Generator方式,去自动生成XML,会有一些精细操作,比如判空
    List<Course> findValidCourses();


}

3.把本模块服务(Dubbo),注册到Zookeeper上

也就是整合Dubbo和Zookeeper


# 指定版本:大版本.中版本.小版本
demo.service.version=1.0.0

# 数据库的配置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/course_practice?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
spring.datasource.username=root
spring.datasource.password=1234abcd

# 日志配置
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}

# 让mybatis,开启驼峰命名转换(将下划线的字段--->驼峰形式)
mybatis.configuration.map-underscore-to-camel-case=true

# 项目名称
spring.application.name=course-list


# dubbo的通信协议是dubbo(因为也可以是HTTP,所以这里要明示以下)
dubbo.protocol.name=dubbo
# dubbo的端口:-1表示随机找一个,因为它不重要,最终都是通过名字来查找。Dubbo会自动根据名字找到端口号
dubbo.protocol.port=-1

# dubbo的注册地址:是Zookeeper这个人服务注册与发现中心
dubbo.registry.address=zookeeper://127.0.0.1:2181
dubbo.registry.file=${user.home}/dubbo-cache/${spring.application.name}/dubbo.cache

# 扫描以下路径,因为dubbo的注解@Service,就在该路径下
dubbo.scan.base-packages=com.imooc.producer.service.impl

3.3 服务消费者的开发:price模块

0.前提准备

新建项目:

1.引入依赖、编写配置文件

本子模块的依赖:

<?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">
    <parent>
        <artifactId>dubbo-practice</artifactId>
        <groupId>com.imooc</groupId>
        <version>0.0.1</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>consumer</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>


    <dependencies>
        <!--1.1 Spring Boot依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <!--1.2 Spring Boot测试相关的依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--1.3 Dubbo相关的两个依赖-->
        <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>
        <!--1.4 引入Zookeeper的依赖-->
        <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>
        <!--1.5 Web功能-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--1.6 mysql-connector,需要与MySQL版本对应-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--1.7 MyBatis依赖-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.1</version>
        </dependency>
        <dependency>
            <groupId>com.imooc</groupId>
            <artifactId>producer</artifactId>
            <version>0.0.1</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

</project>

配置文件:

# 因为本模块price是对外提供服务的
server.port=8084

# 指定版本:大版本.中版本.小版本
demo.service.version=1.0.0

# 数据库的配置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/course_practice?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
spring.datasource.username=root
spring.datasource.password=1234abcd

# 日志配置
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}

# 让mybatis,开启驼峰命名转换(将下划线的字段--->驼峰形式)
mybatis.configuration.map-underscore-to-camel-case=true

# 项目名称
spring.application.name=course-price


# dubbo的通信协议是dubbo(因为也可以是HTTP,所以这里要明示以下)
dubbo.protocol.name=dubbo
# dubbo的端口:-1表示随机找一个,因为它不重要,最终都是通过名字来查找。Dubbo会自动根据名字找到端口号
dubbo.protocol.port=-1

# dubbo的注册地址:是Zookeeper这个人服务注册与发现中心
dubbo.registry.address=zookeeper://127.0.0.1:2181
dubbo.registry.file=${user.home}/dubbo-cache/${spring.application.name}/dubbo.cache

2.MVC三层

1.mapper层:

/**
 *  课程价格的mapper类
 */
@Mapper
@Repository
public interface CoursePriceMapper {

    @Select("SELECT * FROM course_price WHERE course_id = #{courseId}")
    CoursePrice findCoursePrice(Integer courseId);

}

2.service层:

public interface CoursePriceService {

    CoursePrice getCoursePrice(Integer courseId);

    List<CourseAndPrice> getCoursesAndPrice();

}
/**
 * 课程价格服务
 */
@Service                    // 因为本模块服务price,是对外暴露HTTP请求,所以使用的是Spring的
public class CoursePriceServiceImpl implements CoursePriceService {

    @Resource
    CoursePriceMapper coursePriceMapper;

    @Reference(version = "${demo.service.version}")       // 指定Dubbo服务(即list模块)的版本,会从Zookeeper寻找,从配置文件中读取
    CourseListService courseListService;


    // 1.单纯的查询课程价格
    @Override
    public CoursePrice getCoursePrice(Integer courseId) {
        return coursePriceMapper.findCoursePrice(courseId);
    }


    // 2. 先拿到课程列表(一定依赖其他模块服务list),遍历一遍,最终匹配上价格
    @Override
    public List<CourseAndPrice> getCoursesAndPrice() {
        List<CourseAndPrice> courseAndPriceList = new ArrayList<>();
        List<Course> courses = courseListService.getCourseList();          // 利用到Dubbo通信,来直接使用其他模块服务
        for (int i = 0; i < courses.size(); i++) {
            Course course = courses.get(i);
            // 将课程列表信息,与课程价格进行组合,组成一个courseAndPrice对象
            if (course != null) {
                CoursePrice coursePrice = getCoursePrice(course.getCourseId());
                CourseAndPrice courseAndPrice = new CourseAndPrice();
                courseAndPrice.setPrice(coursePrice.getPrice());
                courseAndPrice.setName(course.getCourseName());
                courseAndPrice.setId(course.getId());
                courseAndPrice.setCourseId(course.getCourseId());

                courseAndPriceList.add(courseAndPrice);
            }
        }
        return courseAndPriceList;
    }

}

3.controller层:

/**
 *  课程价格的控制器
 */
@RestController                         // controller+能返回JSON数据
public class CoursePriceController {

    @Resource
    CoursePriceService coursePriceService;



    // 只是自己的模块服务
    @GetMapping("/price")
    public Integer getCoursePrice(Integer courseId) {
        CoursePrice coursePrice = coursePriceService.getCoursePrice(courseId);
        return coursePrice.getPrice();          // 查询拿到某个课程Id的价格
    }



    // 在price模块中,整合两个服务
    @GetMapping("/coursesAndPrice")
    public List<CourseAndPrice> getCoursesAndPrice(){
        List<CourseAndPrice> coursesAndPrice = coursePriceService.getCoursesAndPrice();     // 利用到Dubbo通信
        return coursesAndPrice;
    }


}

注意:远程调用的注解

3.4 两个模块服务的结果

1.前提:

  • 要先启动Zookeeper
  • 默认情况下,被依赖的类即生产者list模块,必须先启动;

我遇到的问题:

分析:

解决:

参考:https://www.jianshu.com/p/181a69836fe9

2.结果

浏览器:

从接口的角度看,成功访问:

从IDEA的生产者的日志中看:

关于FeHelper:

作用:

我的:

综上所述,就完成Dubbo服务的创建、编写、调用。

3.5 小结

上面的Dubbo通信的项目,与原始的Spring Boot还是有区别的:

(1)在启动消费者之前,必须把环境(ZK、依赖的服务)准备好

(2)在生产者编写时,要记得配置:以让Dubbo去找到对应的地址进行扫描(只是生产者,否则报错)

(3)生产者:要配置dubbo相关的内容、实现类头上用dubbo的@Service注解并指定版本

​ 消费者:要配置dubbo相关的内容、属性调用时用dubbo的@Reference注解并指定版本(以视为本地类)

  • 其中:(2)(3)中,“要配置dubbo相关的内容” 的,生产者(Dubboo服务)、消费者区别

  • 其中,(3)生产者(Dubboo服务)、消费者区别

3.6 我对Zookeeper、Dubbo的理解

Zookeeper的架构图

定位:为分布式应用程序提供分布式协调服务的框架。

Dubbo的架构图:

定位:微服务之间,的高性能的RPC通信。

以上两者的关系:有点像以前学习《32.3 微服务入门》中的Spring Cloud这个微服务框架中的两个组件:

  • Eureka组件------------------------------------------- Zookeeper框架
  • 利用Feign,方便服务间调用--------------------- Dubbo框架

声明:Jerry's Blog|版权所有,违者必究|如未注明,均为原创|本网站采用BY-NC-SA协议进行授权

转载:转载请注明原文链接 - 35.2 Dubbo


Follow excellence, and success will chase you.