【探花交友DAY 01】Dubbo的使用

1. Dubbo的历史

2011年10月27日,阿里巴巴开源了自己的SOA服务化治理方案的核心框架Dubbo,服务治理和SOA的设计理念开始逐渐在国内软件行业中落地,并被广泛应用。Dubbo主要有两种使用方式:

  • 早期版本的dubbo遵循SOA的思想,是面向服务架构的重要组件。
  • 如今版本的Dubbo作为Spring Cloud的二进制通信方案来发挥Dubbo的性能优势

2. Dubbo快速入门

2.1 Dubbo的基本架构

在这里插入图片描述
节点角色说明:

节点角色说明
Provider暴露服务的服务提供方。
Consumer调用远程服务的服务消费方。
Registry服务注册与发现的注册中心。
Monitor统计服务的调用次数和调用时间的监控中心。

Dubbo的作用和Feign是一样的,但是Feign是应用层的协议进行远程通讯,而Dubbo是位于传输层的,因此Dubbo更加接近底层一些,因此在高并发的情况下,Dubbo的效率要更高一些,因此我们使用Dubbo来带Feign

不同角色的说明:

  • 服务容器负责启动,加载运行服务的提供者
  • 服务提供者在启动的时候就将自己注册到注册中心中
  • 消费者在启动的时候,向注册中心订阅自己需要的服务
  • 服务消费者从提供者的地址列表中,基于负载均衡算法,选择一个提供者进行调用,如果调用失败,则选择另一台调用
  • 监控中心会监控消费者和提供者的调用次数和时间。

2.2 Nacos注册中心

Nacos是阿里巴巴的产品,是一个集服务发现,配置管理的平台,在国内受欢迎程度较高。
在这里插入图片描述
安装步骤:

  • 解压Nacos到没有中文和特殊符号的目录中
  • 进入bin目录中,然后运行下面的指令
#进入bin目录
cd bin
#启动
startup.cmd -m standalone
  • 在浏览器访问http://127.0.0.1:8848/nacos

2.3 管理后台

DubboAdmin是阿里巴巴管理提供的管理控制台,可以实现服务查询,详情展示,服务测试等功能。借由DubboAdmin可以更好的帮助开发人员对服务进行管理和监控。

安装步骤如下:

#1、下载代码: 
git clone https://github.com/apache/dubbo-admin.git
#2、在 dubbo-admin-server/src/main/resources/application.properties中指定注册中心地址
#3、构建
mvn clean package -D maven.test.skip=true
#4、启动
mvn --projects dubbo-admin-server spring-boot:run
#或者
cd dubbo-admin-distribution/target; java -jar dubbo-admin-0.1.jar
#5、访问 http://localhost:8080

注意:管理后台只在仅仅使用Dubbo的时候使用,后续将Dubbo和Spring Cloud整合后可以直接通过Nacos进行管理

2.4 入门案例

需求:使用Dubbo构建分布式架构,完成根据用户id查询用户
在这里插入图片描述

2.4.1 服务提供者

创建user-provider模块导入依赖

<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!--mybatis-->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
    </dependency>

    <!--dubbo的起步依赖-->
    <dependency>
        <groupId>org.apache.dubbo</groupId>
        <artifactId>dubbo-spring-boot-starter</artifactId>
        <version>2.7.8</version>
    </dependency>

    <dependency>
        <groupId>org.apache.dubbo</groupId>
        <artifactId>dubbo-registry-nacos</artifactId>
        <version>2.7.8</version>
    </dependency>
</dependencies>

编写引导类

package cn.itcast.user;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@MapperScan("cn.itcast.user.mapper")
@SpringBootApplication
public class UserProviderApplication {

    public static void main(String[] args) {
        SpringApplication.run(UserProviderApplication.class, args);
    }

}

UserService接口

package cn.itcast.user.service;
import cn.itcast.user.domain.User;

public interface UserService {
    User queryById(Long id);
}

UserServiceImpl

package cn.itcast.user.service;


import cn.itcast.user.api.UserService;
import cn.itcast.user.domain.User;
import cn.itcast.user.mapper.UserMapper;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Autowired;

@DubboService
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

	//根据id查询用户名称
    public String queryUsername(Long id) {
        return userMapper.findById(id).getUsername();
    }
}

因为是远程调用,因此之前的@Service注解需要替换成@DubboService,并且之后在yml的配置文件中需要指定包扫描,让Dubbo扫描到

Application.yml
需要配置的内容

  • 协议:端口,协议名称
  • 注册中心地址
  • 包扫描位置
server:
  port: 18081
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/dubbo-demo?useSSL=false
    username: root
    password: root
    driver-class-name: com.mysql.jdbc.Driver
  application:
    name: user-provider
logging:
  level:
    cn.itcast: debug
  pattern:
    dateformat: HH:mm:ss:SSS
dubbo:
  protocol:
    name: dubbo
    port: 20881
  registry:
    address: nacos://127.0.0.1:8848
  scan:
    base-packages: cn.itcast.user.service
2.4.2 服务消费者

创建user-consumer模块导入依赖

<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>


    <!--dubbo的起步依赖-->
    <dependency>
        <groupId>org.apache.dubbo</groupId>
        <artifactId>dubbo-spring-boot-starter</artifactId>
        <version>2.7.8</version>
    </dependency>

    <dependency>
        <groupId>org.apache.dubbo</groupId>
        <artifactId>dubbo-registry-nacos</artifactId>
        <version>2.7.8</version>
    </dependency>
</dependencies>

配置启动类

package cn.itcast.user;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;


@SpringBootApplication
public class UserConsumerApplication {

    public static void main(String[] args) {
        SpringApplication.run(UserConsumerApplication.class, args);
    }
}

UserService接口

package cn.itcast.user.service;
import cn.itcast.user.domain.User;

public interface UserService {
    User queryById(Long id);
}

编写Controller
因为现在是远程调动,因此之前是有@Autowired注解就不可以使用了,因为目前容器中没有userService对象,要使用远程调用的@DubboReference

package cn.itcast.user.controller;
import cn.itcast.user.api.UserService;
import cn.itcast.user.domain.User;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {

    //引用远程服务
    @DubboReference
    private UserService userService;

    @GetMapping("/username/{id}")
    public String findUserName(@PathVariable("id") Long id) {
        return userService.queryUsername(id);
    }
}

Application.yml
消费者端的配置文件中仅仅需要配置注册中心地址

server:
  port: 18080
spring:
  application:
    name: user-consumer
logging:
  level:
    cn.itcast: debug
  pattern:
    dateformat: HH:mm:ss:SSS
dubbo:
  registry:
    address: nacos://127.0.0.1:8848

此时访问http://localhost:18080/user/username/1就可以访问当相关的内容了

注意,在消费者和服务者两个服务中都需要有UserService接口,并且两个接口的名字和里面的方法定义都需要一模一样在才可以实现远程方法调用

2.5 代码优化

通过上面的代码我们看到,在消费者和提供者两个模块中都需要UserService的接口,在编写代码的时候,我们很可能只修改了一处的代码,而忘记修改另一处导致远程方法调用失败。实际上,我们可以将这些公共的代码,比如实体类和接口等抽取到一个独立的模块user-api,其他模块需要的话只需要引入user-api的Maven依赖即可。
在这里插入图片描述

将接口抽取为独立模块,并且把接口有关的domain都放到这个模块中

  1. 创建user-api模块引入依赖
  2. 将UserService接口和User对象导入user-api模块下
  3. User对象实现序列化接口

实现序列化的原因是因为数据在网络上传输是以二进制的方式进行传输的

创建一个新模块user-api,将实体类对象和UserService接口拷贝到这个模块下。目录结构如下
在这里插入图片描述
在消费者和提供者的pom文件中添加对user-api的依赖

<dependency>
    <groupId>cn.itcast</groupId>
    <artifactId>user-apis</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

删除消费者和提供者中的实体类和UserService接口相关代码

3. Dubbo高级特性

3.1 超时重试

消费者在调用提供者的时候发生了阻塞,等待的情况,这个时候消费者会一直等待下去。如果此时请求比较多,那么在消费者这里积攒的请求就越来越多,导致请求大量堆积,引发雪崩现象。Dubbo使用超时机制来解决这个问题,在配置文件中设置Timeout属性,默认值为1秒

此外,Dubbo还提供了重试机制,当一次请求超时时,会重新发送请求。默认情况下,一旦超时,Dubbo会重新发送两次请求到提供者。也可以通过配置文件取消重试机制
user-consumer模块中添加配置信息

dubbo:
  registry:
    address: nacos://127.0.0.1:8848
  consumer:
    timeout: 3000
    retries: 0

一般情况下我们不使用重试机制,原因是我们进行查询数据的时候其实是可以使用重试机制的。但是一旦涉及到插入数据,那么一旦超时,Dubbo会再次发送两次请求,导致最后本来应该插入一条数据,实际上插入了三条数据。

3.2 启动检查

为了保障服务的正常可用,Dubbo 缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常
在这里插入图片描述
在正式环境这是很有必要的一项配置,可以保证整个调用链路的平稳运行

在开发时,往往会存在没有提供者的情况。由于启动检查的原因,可能导致开发测试出现问题

可以通过check=false关闭

user-consumer模块中添加配置信息

dubbo:
  registry:
    address: nacos://127.0.0.1:8848
  consumer:
    check: false

3.4、多版本

灰度发布:当出现新功能时,会让一部分用户先使用新功能,用户反馈没问题时,再将所有用户迁移到新功能。Dubbo提供了提供者多版本的支持,平滑处理项目功能升级部署
在这里插入图片描述

user-provider定义新的服务实现类UserServiceImpl2,指定版本

@DubboService(version =2.0.0)
public class UserServiceImpl2 implements UserService {
    …………
}

消费者在调用服务的时候指定版本

@RestController
@RequestMapping("/user")
public class UserController {
    //引用远程服务
    @DubboReference(version = "2.0.0")
    private UserService userService;    
    ………
}

3.5 负载均衡

在集群部署时,Dubbo提供了4种负载均衡策略,帮助消费者找到最优提供者并调用

  • Random :按权重随机,默认值。按权重设置随机概率。

  • RoundRobin :按权重轮询

在这里插入图片描述

随机轮询和按权重轮询的思想是,首先将所有的权重求和,如上图权重之和为10。然后每一个提供者根据权重有一个对应的区间,如001为[1,2],002为[3,4,5],003为[6,7,8,9,10]。
随机轮询是从[1,10]中随机产生整数,根据整数位于的区间来选择调用哪一个提供者
按权重轮询是从1开始到10,前两次为001提供者,后面3次为002提供者,后面5次为003提供者

  • LeastActive:最少活跃调用数,相同活跃数的随机。
    在这里插入图片描述
    这种方式每次都选择调用量最小的提供者服务

  • ConsistentHash:一致性 Hash,相同参数的请求总是发到同一提供者。
    在这里插入图片描述

3.6 小结

  • 启动检查:消费者在启动的时候检查提供者是否可用,如果不可用则抛出异常
  • 多版本:Dubbo支持灰度发布,通过在消费者和提供者代码中指定版本号来实现
  • 超时和重试:为了防止雪崩,Dubbo默认1秒钟超时;默认情况下超时后Dubbo会再次向提供者发送两次请求
  • 负载均衡:四种策略

4. Dubbo整合Spring Cloud

4.1 为什么需要Dubbo

  • Feign基于HTTP协议 ,在高并发场景下性能不够理想,容易成为性能瓶颈
  • Dubbo基于RPC协议,属于传输层协议
  • Dubbo默认使用Netty构造TCP长连接方式通信,性能较高
    在这里插入图片描述

4.2 整合架构

将Dubbo集成至SpringCloud主要是替换Ribbo或者Feign实现远程调用。加入Dubbo后,整体的架构如下:
在这里插入图片描述

4.3 入门案例

需求:根据订单Id查询订单的同时将订单所属用户信息一并查出
在这里插入图片描述
模块分析
在这里插入图片描述

4.3.1 Dubbo-api模块

抽取Dubbo-api模块,添加相关依赖和接口
在这里插入图片描述

<dependency>
    <groupId>cn.itcast</groupId>
    <artifactId>dubbo-domain</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
package cn.itcast.dubbo.api;
import cn.itcast.dubbo.domain.User;

public interface UserService {
    User queryById(Long id);
}
4.3.2 Dubbo-domain模块

在这里插入图片描述

在该模块中保存实体类对象

@Data
public class Order implements Serializable {
    private Long id;
    private Long price;
    private String name;
    private Integer num;
    private Long userId;
    private User user;
}
@Data
public class User implements Serializable {
    private Long id;
    private String username;
    private String address;
}
4.3.2 订单服务模块

在这里插入图片描述
引入依赖

<!--nacos注册中心的依赖-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

<!--springcloud alibaba dubbo依赖   -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>

<dependency>
    <groupId>cn.itcast</groupId>
    <artifactId>dubbo-api</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

修改Controller中方法

@RestController
@RequestMapping("order")
public class OrderController {

    @Autowired
    private OrderService orderService;

    @DubboReference
    private UserService userService;

    @GetMapping("{orderId}")
    public Order queryOrderByUserId(@PathVariable("orderId") Long orderId) {
        Order order = orderService.queryOrderById(orderId);
        Long userId = order.getUserId();
        User user = userService.queryById(userId);
        order.setUser(user);
        return order;
    }
}
4.3.3 用户服务模块

在这里插入图片描述
引入依赖

<!--nacos注册中心的依赖-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

<!--springcloud alibaba dubbo依赖   -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>

<dependency>
    <groupId>cn.itcast</groupId>
    <artifactId>dubbo-api</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

修改Service实现类

@DubboService
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;
    public User queryById(Long id) {
        return userMapper.findById(id);
    }
}

至此,代码改造完毕。

注意:在application.yml文件中一定要指定服务名称,否则会报错
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值