searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

Dubbo学习分享1--第一个Dubbo3项目

2024-10-11 10:17:41
14
0

什么是Dubbo

简介

Apache Dubbo 是一款 RPC 服务开发框架,用于解决微服务架构下的服务治理与通信问题,官方提供了 Java、Golang 等多语言 SDK 实现。使用 Dubbo 开发的微服务原生具备相互之间的远程地址发现与通信能力, 利用 Dubbo 提供的丰富服务治理特性,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。Dubbo 被设计为高度可扩展,用户可以方便的实现流量拦截、选址的各种定制逻辑。

在云原生时代,Dubbo 相继衍生出了 Dubbo3、Proxyless Mesh 等架构与解决方案,在易用性、超大规模微服务实践、云原生基础设施适配、安全性等几大方向上进行了全面升级。

架构图

以上是Dubbo的工作原理图,从抽象架构上分为两层,分别是服务治理抽象控制面和Dubbo数据面。

服务治理控制面:对Dubbo治理体系的抽象表达,包含注册中心、流量管控策略、Dubbo Admin 控制台等。

Dubbo数据面:代表集群部署的所有Dubbo进程。Dubbo定义了微服务应用开发与调用规范并负责完成数据传输的编码工作。

基于spring boot的第一个dubbo项目

项目框架搭建

一个Dubbo Demo项目需要定义三个东西,分别是定义接口的interface,服务提供者provider和服务消费者consumer。所以我在项目中新建了三个子模块分别完成上述功能。

依赖引入

我在父项目中定义了一些公共的依赖,指定了整个项目使用的spring boot和dubbo版本,避免由于版本不一致带来的问题。同时,demo使用了zookeeper作为注册中心,所以在项目中还引入了对zookeeper客户端的依赖。父项目pom关键部分如下:

<properties>
  <java.version>1.8</java.version>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  <spring-boot.version>2.6.13</spring-boot.version>
  <dubbo.version>3.2.0-beta.4</dubbo.version>
</properties>

<dependencies>
  <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.30</version>
  </dependency>
</dependencies>

<!--    dependencyManagement 只是声明依赖的版本,不会引入实际的依赖-->
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-dependencies</artifactId>
      <version>${spring-boot.version}</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
    <!--            dubbo-->
    <dependency>
      <groupId>org.apache.dubbo</groupId>
      <artifactId>dubbo-bom</artifactId>
      <version>${dubbo.version}</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
    <!--注册中心依赖,此处使用zookeeper-->
    <dependency>
      <groupId>org.apache.dubbo</groupId>
      <artifactId>dubbo-dependencies-zookeeper-curator5</artifactId>
      <version>${dubbo.version}</version>
    </dependency>

  </dependencies>
</dependencyManagement>

在demo中,针对不同的子模块我们引入了大致相同的依赖,其pom关键部分如下。此外consumer和provider因需要使用interface提供的接口,所以在consumer和provier中还需加入对interface模块的依赖。

<parent>
  <artifactId>dubbo-demo</artifactId>
  <groupId>com.example</groupId>
  <version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>dubbo-provider</artifactId>

<dependencies>
  <dependency>
    <groupId>com.example</groupId>
    <artifactId>dubbo-interface</artifactId>
    <version>${project.parent.version}</version>
  </dependency>

  <!-- dubbo -->
  <dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-spring-boot-starter</artifactId>
  </dependency>

  <dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-dependencies-zookeeper-curator5</artifactId>
    <exclusions>
      <exclusion>
        <artifactId>slf4j-reload4j</artifactId>
        <groupId>org.slf4j</groupId>
      </exclusion>
    </exclusions>
  </dependency>

  <!-- spring boot starter -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
  </dependency>
</dependencies>

编写demo代码

编写interface

本模块较为简单,只是简单定义了一个DemoService接口,该接口定义了一个 sayHi(String name)方法用于打招呼。

public interface DemoService {
    String sayHi(String name);
}

编写provider

provider作为服务提供者需要实现两个目的:实现接口与注册服务

实现接口

demo中对DemoService接口进行了简单实现,实现代码如下。此实现的sayHi(String name)方法接收一个name并返回打招呼信息。

@DubboService
public class DemoServiceImpl implements DemoService {
    @Override
    public String sayHi(String name) {
        return String.format("Hi %s, I am interface service provider", name);
    }
}

注册服务

  1. 启用Dubbo支持: 在SpringBoot中,使用@EnableDubbo注解可以启用Dubbo支持、自动配置和注解扫描,该注解可以放在主类上。
@SpringBootApplication
@EnableDubbo
public class ProviderApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProviderApplication.class, args);
    }
}
  1. 定义需要注册的接口: 在Dubbo3中,使用@DubboService注解标注的方法将被注册到注册中心中。(在实现接口中已标注)
  2. 配置Dubbo参数(每个参数的注意事项已标注在配置文件上),注意一个在这篇文章中提到的坑,使用虚机或其他远端环境部署zookeeper时,连接zookeeper可能超时导致服务启动失败,此时需修改如下的两个字段。
dubbo:
  application:
    name: dubbo-provider #服务名称
    qos-port: 20780 #运维命令服务端口, 可不配置
  protocol:
    name: dubbo #RPC 协议
    port: 20880 #RPC 端口 -1代表使用默认端口20880
  registry:
    address: zookeeper://${zookeeper_ip}:${zookeeper_service_port} #注册中心类型和地址
    timeout: 250000 #连接注册中心超时时间,单位毫秒
    parameters:
      blockUntilConnectedWait: 250 #连接注册中心超时时间,单位秒, 默认10
    subscribe: false #只注册,不订阅

编写consumer

consumer作为服务消费者需要实现两个目的:订阅服务和消费接口

订阅服务

  1. 启用Dubbo支持:
@SpringBootApplication
@EnableDubbo
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }
}
  1. 使用代理接口:使用@DubboReference注解可以将依赖标注为使用Dubbo代理,Dubbo会自动从Provider处寻找实现。
@Component
public class DemoServiceTask implements CommandLineRunner {
    // CommandLineRunner接口, 实现此接口的类的run方法会在springboot context加载完成后被调用。
    @DubboReference(loadbalance = "roundrobin")
    private DemoService demoService;
    @Override
    public void run(String... args) throws Exception {
        for (int i = 0; i < 10; i++) {
            String name = "test" + i;
            String sayHi = demoService.sayHi(name);
            System.out.println(sayHi);
        }
    }
}
  1. 配置参数:和provider类似,consumer也需要配置Dubbo参数且配置大同小异。consumer需要订阅服务所以dubbo.registry.sybscribe不能配置为false。
dubbo:
  application:
    name: dubbo-consumer
  protocol:
    name: dubbo
    port: 20870
  registry:
    address: zookeeper://${zookeeper_ip}:${zookeeper_service_port}
    register: false  # 只订阅服务,不注册
    timeout: 250000
    parameters:
      blockUntilConnectedWait: 250

消费接口

在订阅服务.使用代理接口处已展示消费接口使用的代码,该Task实现了CommandLineRunner,其run(String... args)方法将会在SpringBoot加载完上下文后执行。该代码将会调用10次provider提供的sayHi(String name)实现,如果有多个provider,则采用轮询机制作为负载均衡。

测试结果

各模块运行结果

  1. provider出现如下标识,代表provider启动成功。

  1. consumer启动成功并成功调用provider的服务

  1. provider处也有相关日志

zookeeper节点分析

在上面的demo中使用了zookeeper作为注册中心,那么dubbo数据具体在zookeeper中怎么组织的呢?本例使用了ZooInspector查看了zookeeper的节点信息,截图如下。

/dubbo:组名称,默认为dubbo,如果在项目中配置dubbo.registry.group中修改为其他值,那么此处节点名称也为其他值。

/dubbo/com.example.dinterface.DemonService:接口节点,下面包含了接口的配置,路由,消费者列表和提供者列表,其中提供者列表以URL的形式给出,本例中的URL信息如下:

dubbo://172.25.16.39:20880/com.example.dinterface.DemoService?anyhost=true&application=dubbo-provider&background=false&deprecated=false&dubbo=2.0.2&dynamic=true&executor-management-mode=default&file.cache=true&generic=false&interface=com.example.dinterface.DemoService&methods=sayHi&pid=17664&prefer.serialization=fastjson2,hessian2&release=3.2.0-beta.4&service-name-mapping=true&side=provider&timestamp=1721965937083

/dubbo/mapping:服务映射,将接口映射为服务(provider)名称

/dubbo/metadata: 关于接口,providers和consumers的元信息

/dubbo/config: 和dubbo动态配置相关

/dubbo/org.apache.dubbo.meta.MetadataService: dubbo自带的元数据服务相关信息

/service: 用于组织和管理服务提供者的注册信息

应用级注册

在dubbo中,服务注册是以接口为单位的,当服务者提供者数量或接口数量过多时对于注册中心压力较大。所以在dubbo3中引入以应用为单位的服务注册。

在Demo中我们定义了一个app-provider模块,该模块设置dubbo.registry.register-mode为instance标识该模块以应用为单位在注册中心进行注册。该模块也实现了DemoService的sayHi(String name)方法,实现的代码如下

@DubboService
public class DemoServiceImpl implements DemoService {
    @Override
    public String sayHi(String name) {
        return String.format("Hello %s. I am app service provider", name);
    }
}

启动app-provider模块,原本运行的comsumer模块打印日志,从注册中心获得通知,得到了新的服务信息。

zookeeper节点更新为以下情况,

dubbo3默认情况下会同时以应用级和接口级进行注册(如 provider)用于兼容和过渡,而我们的app-provider指定了只进行应用级注册所以/service/dubbo-app-provider项目新增一个节点而/dubbo/com.example.dinterface.DemoService/providers下则没有新增提供者。此外/dubbo/metadata/com.example.dinterface.DemonService/provider下页新增dubbo-app-provider服务提供者用于标识该服务实现了com.example.dinterface.DemoService接口,这些元数据是通过MetaDataService获取的。

负载均衡

当集群中存在多个provider都实现并注册了同一接口时,客户端可根据配置来决定自己想要优先使用哪个provider提供的服务,其中一个配置便是负载均衡(loadBalance)。在上面的consumer实现中已经指定了调用DemoService的负载均衡策略为轮询(roundrobin)。接下来基于上面的两个providers,Demo尝试十次调用结果如下。

0条评论
0 / 1000
杨****攀
4文章数
0粉丝数
杨****攀
4 文章 | 0 粉丝
杨****攀
4文章数
0粉丝数
杨****攀
4 文章 | 0 粉丝
原创

Dubbo学习分享1--第一个Dubbo3项目

2024-10-11 10:17:41
14
0

什么是Dubbo

简介

Apache Dubbo 是一款 RPC 服务开发框架,用于解决微服务架构下的服务治理与通信问题,官方提供了 Java、Golang 等多语言 SDK 实现。使用 Dubbo 开发的微服务原生具备相互之间的远程地址发现与通信能力, 利用 Dubbo 提供的丰富服务治理特性,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。Dubbo 被设计为高度可扩展,用户可以方便的实现流量拦截、选址的各种定制逻辑。

在云原生时代,Dubbo 相继衍生出了 Dubbo3、Proxyless Mesh 等架构与解决方案,在易用性、超大规模微服务实践、云原生基础设施适配、安全性等几大方向上进行了全面升级。

架构图

以上是Dubbo的工作原理图,从抽象架构上分为两层,分别是服务治理抽象控制面和Dubbo数据面。

服务治理控制面:对Dubbo治理体系的抽象表达,包含注册中心、流量管控策略、Dubbo Admin 控制台等。

Dubbo数据面:代表集群部署的所有Dubbo进程。Dubbo定义了微服务应用开发与调用规范并负责完成数据传输的编码工作。

基于spring boot的第一个dubbo项目

项目框架搭建

一个Dubbo Demo项目需要定义三个东西,分别是定义接口的interface,服务提供者provider和服务消费者consumer。所以我在项目中新建了三个子模块分别完成上述功能。

依赖引入

我在父项目中定义了一些公共的依赖,指定了整个项目使用的spring boot和dubbo版本,避免由于版本不一致带来的问题。同时,demo使用了zookeeper作为注册中心,所以在项目中还引入了对zookeeper客户端的依赖。父项目pom关键部分如下:

<properties>
  <java.version>1.8</java.version>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  <spring-boot.version>2.6.13</spring-boot.version>
  <dubbo.version>3.2.0-beta.4</dubbo.version>
</properties>

<dependencies>
  <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.30</version>
  </dependency>
</dependencies>

<!--    dependencyManagement 只是声明依赖的版本,不会引入实际的依赖-->
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-dependencies</artifactId>
      <version>${spring-boot.version}</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
    <!--            dubbo-->
    <dependency>
      <groupId>org.apache.dubbo</groupId>
      <artifactId>dubbo-bom</artifactId>
      <version>${dubbo.version}</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
    <!--注册中心依赖,此处使用zookeeper-->
    <dependency>
      <groupId>org.apache.dubbo</groupId>
      <artifactId>dubbo-dependencies-zookeeper-curator5</artifactId>
      <version>${dubbo.version}</version>
    </dependency>

  </dependencies>
</dependencyManagement>

在demo中,针对不同的子模块我们引入了大致相同的依赖,其pom关键部分如下。此外consumer和provider因需要使用interface提供的接口,所以在consumer和provier中还需加入对interface模块的依赖。

<parent>
  <artifactId>dubbo-demo</artifactId>
  <groupId>com.example</groupId>
  <version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>dubbo-provider</artifactId>

<dependencies>
  <dependency>
    <groupId>com.example</groupId>
    <artifactId>dubbo-interface</artifactId>
    <version>${project.parent.version}</version>
  </dependency>

  <!-- dubbo -->
  <dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-spring-boot-starter</artifactId>
  </dependency>

  <dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-dependencies-zookeeper-curator5</artifactId>
    <exclusions>
      <exclusion>
        <artifactId>slf4j-reload4j</artifactId>
        <groupId>org.slf4j</groupId>
      </exclusion>
    </exclusions>
  </dependency>

  <!-- spring boot starter -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
  </dependency>
</dependencies>

编写demo代码

编写interface

本模块较为简单,只是简单定义了一个DemoService接口,该接口定义了一个 sayHi(String name)方法用于打招呼。

public interface DemoService {
    String sayHi(String name);
}

编写provider

provider作为服务提供者需要实现两个目的:实现接口与注册服务

实现接口

demo中对DemoService接口进行了简单实现,实现代码如下。此实现的sayHi(String name)方法接收一个name并返回打招呼信息。

@DubboService
public class DemoServiceImpl implements DemoService {
    @Override
    public String sayHi(String name) {
        return String.format("Hi %s, I am interface service provider", name);
    }
}

注册服务

  1. 启用Dubbo支持: 在SpringBoot中,使用@EnableDubbo注解可以启用Dubbo支持、自动配置和注解扫描,该注解可以放在主类上。
@SpringBootApplication
@EnableDubbo
public class ProviderApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProviderApplication.class, args);
    }
}
  1. 定义需要注册的接口: 在Dubbo3中,使用@DubboService注解标注的方法将被注册到注册中心中。(在实现接口中已标注)
  2. 配置Dubbo参数(每个参数的注意事项已标注在配置文件上),注意一个在这篇文章中提到的坑,使用虚机或其他远端环境部署zookeeper时,连接zookeeper可能超时导致服务启动失败,此时需修改如下的两个字段。
dubbo:
  application:
    name: dubbo-provider #服务名称
    qos-port: 20780 #运维命令服务端口, 可不配置
  protocol:
    name: dubbo #RPC 协议
    port: 20880 #RPC 端口 -1代表使用默认端口20880
  registry:
    address: zookeeper://${zookeeper_ip}:${zookeeper_service_port} #注册中心类型和地址
    timeout: 250000 #连接注册中心超时时间,单位毫秒
    parameters:
      blockUntilConnectedWait: 250 #连接注册中心超时时间,单位秒, 默认10
    subscribe: false #只注册,不订阅

编写consumer

consumer作为服务消费者需要实现两个目的:订阅服务和消费接口

订阅服务

  1. 启用Dubbo支持:
@SpringBootApplication
@EnableDubbo
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }
}
  1. 使用代理接口:使用@DubboReference注解可以将依赖标注为使用Dubbo代理,Dubbo会自动从Provider处寻找实现。
@Component
public class DemoServiceTask implements CommandLineRunner {
    // CommandLineRunner接口, 实现此接口的类的run方法会在springboot context加载完成后被调用。
    @DubboReference(loadbalance = "roundrobin")
    private DemoService demoService;
    @Override
    public void run(String... args) throws Exception {
        for (int i = 0; i < 10; i++) {
            String name = "test" + i;
            String sayHi = demoService.sayHi(name);
            System.out.println(sayHi);
        }
    }
}
  1. 配置参数:和provider类似,consumer也需要配置Dubbo参数且配置大同小异。consumer需要订阅服务所以dubbo.registry.sybscribe不能配置为false。
dubbo:
  application:
    name: dubbo-consumer
  protocol:
    name: dubbo
    port: 20870
  registry:
    address: zookeeper://${zookeeper_ip}:${zookeeper_service_port}
    register: false  # 只订阅服务,不注册
    timeout: 250000
    parameters:
      blockUntilConnectedWait: 250

消费接口

在订阅服务.使用代理接口处已展示消费接口使用的代码,该Task实现了CommandLineRunner,其run(String... args)方法将会在SpringBoot加载完上下文后执行。该代码将会调用10次provider提供的sayHi(String name)实现,如果有多个provider,则采用轮询机制作为负载均衡。

测试结果

各模块运行结果

  1. provider出现如下标识,代表provider启动成功。

  1. consumer启动成功并成功调用provider的服务

  1. provider处也有相关日志

zookeeper节点分析

在上面的demo中使用了zookeeper作为注册中心,那么dubbo数据具体在zookeeper中怎么组织的呢?本例使用了ZooInspector查看了zookeeper的节点信息,截图如下。

/dubbo:组名称,默认为dubbo,如果在项目中配置dubbo.registry.group中修改为其他值,那么此处节点名称也为其他值。

/dubbo/com.example.dinterface.DemonService:接口节点,下面包含了接口的配置,路由,消费者列表和提供者列表,其中提供者列表以URL的形式给出,本例中的URL信息如下:

dubbo://172.25.16.39:20880/com.example.dinterface.DemoService?anyhost=true&application=dubbo-provider&background=false&deprecated=false&dubbo=2.0.2&dynamic=true&executor-management-mode=default&file.cache=true&generic=false&interface=com.example.dinterface.DemoService&methods=sayHi&pid=17664&prefer.serialization=fastjson2,hessian2&release=3.2.0-beta.4&service-name-mapping=true&side=provider&timestamp=1721965937083

/dubbo/mapping:服务映射,将接口映射为服务(provider)名称

/dubbo/metadata: 关于接口,providers和consumers的元信息

/dubbo/config: 和dubbo动态配置相关

/dubbo/org.apache.dubbo.meta.MetadataService: dubbo自带的元数据服务相关信息

/service: 用于组织和管理服务提供者的注册信息

应用级注册

在dubbo中,服务注册是以接口为单位的,当服务者提供者数量或接口数量过多时对于注册中心压力较大。所以在dubbo3中引入以应用为单位的服务注册。

在Demo中我们定义了一个app-provider模块,该模块设置dubbo.registry.register-mode为instance标识该模块以应用为单位在注册中心进行注册。该模块也实现了DemoService的sayHi(String name)方法,实现的代码如下

@DubboService
public class DemoServiceImpl implements DemoService {
    @Override
    public String sayHi(String name) {
        return String.format("Hello %s. I am app service provider", name);
    }
}

启动app-provider模块,原本运行的comsumer模块打印日志,从注册中心获得通知,得到了新的服务信息。

zookeeper节点更新为以下情况,

dubbo3默认情况下会同时以应用级和接口级进行注册(如 provider)用于兼容和过渡,而我们的app-provider指定了只进行应用级注册所以/service/dubbo-app-provider项目新增一个节点而/dubbo/com.example.dinterface.DemoService/providers下则没有新增提供者。此外/dubbo/metadata/com.example.dinterface.DemonService/provider下页新增dubbo-app-provider服务提供者用于标识该服务实现了com.example.dinterface.DemoService接口,这些元数据是通过MetaDataService获取的。

负载均衡

当集群中存在多个provider都实现并注册了同一接口时,客户端可根据配置来决定自己想要优先使用哪个provider提供的服务,其中一个配置便是负载均衡(loadBalance)。在上面的consumer实现中已经指定了调用DemoService的负载均衡策略为轮询(roundrobin)。接下来基于上面的两个providers,Demo尝试十次调用结果如下。

文章来自个人专栏
猫猫的学习计划
4 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0