目录
  1. 1. 1.分布式思维
  2. 2. 2. RPC介绍及场景
  3. 3. 3.dubbo简介
  4. 4. 4. dubbo的基础配置使用
    1. 4.1. 1. xml配置方式
分布式架构-dubbo

1.分布式思维

集群与分布式

上图是由单机到集群、分布式的演变过程。

最早的javaweb雏形

特征:tomcat + servlet + jsp + mysql。一个war包打天下
项目结构:ssh/ssm三层结构。

javaweb的集群发展

特征:硬件机器的横向复制,对整个项目结构无影响。

javaweb的分布式发展

特征:将Service层单独分离出去,成为一个单独的项目jar。单独运行。Web服务器通过rpc框架,对分离出去的service进行调用。

javaweb的微服务发展

特征:从业务角度,细分业务为微服务,每一个微服务是一个完整的服务(从http请求到返回)。
​ 在微服务内部,将需要对外提供的接口,包装成rpc接口,对外部开放。

2. RPC介绍及场景

RPC远程过程调用(Remote Procedure Call)

调用远程计算机上的服务,就像调用本地服务一样。

RPC实现的切入口

​ 我们知道从本质上讲,某个JVM内的对象方法,是无法在该JVM外部被调用的。

如下代码:

1
2
OrderService orderService = (OrderService) ctx.getBean("orderService");
OrderEntiry entiry = orderService.getDetail("1");

orderService.getDetail(“1”)的这一句调用,是无法脱离本地 jvm 环境被调用的。因为外部在调用的时候,没办法得到orderService对象的,本地对orderService也不会认识。

但是,好在java中除了对象方法的调用之外,还有通过反射方式的调用:

1
2
Method method = target.getClass().getMethod(methodName, argTypes);
return method.invoke(target,args);

所以对于本地,只要外部传入了反射需要的目标对象orderService, 方法名称getDetail,和参数值“1”,就能通过反射调用并返回。所以对于外部来说,可以将上述的调用改成如下方式:

1
2
3
4
5
6
Map<String,String> info = new HashMap();
info.put("target","orderService");
info.put("methodName","getDetail");
info.put("arg","1");
//反射调用
Object result = InvokeUtils.call(info,ctx)

对于本地来说,只要告诉我反射需要的信息,target/method/arg,就能调用本地的任何对象方法。

现在只要通过网络传输反射信息,就能通过网络的方式调用不同JVM的方法。网络通信的方法很多,如http/rmi/webservice等,选任何一种方式进行通信即可。

3.dubbo简介

dubbo的使命

上面已经说明了RPC的调用过程,即实现方式,其实在实际工作中,服务节点的RPC调用错综复杂,会变成如下所示的网状调用:

所以,我们除了关心RPC的过程调用之外,还需要考虑:

  1. 服务方是集群时,如何挑选一台服务器来响应客户端?

  2. 因网络抖动引起的调用失败,如何重试来弥补?

  3. 服务方机器的动态增减,如何能够让客户端及时了解并做出调整?

  4. ………….

    Dubbo的使命,即解决上述围绕RPC过程的存在问题。

高并发RPC解决方案

基于TCP的RPC实现,阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的RPC实现服务的输出和输入功能,可以和Spring框架无缝集成。

Provider: 暴露服务的服务提供方。
Consumer: 调用远程服务的服务消费方。
Registry: 服务注册与发现的注册中心。
Monitor: 统计服务的调用次调和调用时间的监控中心。
Container: 服务运行容器。

更多dubbo介绍可以查看官方文档:

4. dubbo的基础配置使用

1. xml配置方式

dubbo基础标签认识:

dubbo:application

1
2
3
服务提供方应用名称, 方便用于依赖跟踪
属性:name 即定义应用名称,如:
<dubbo:application name="dubbo-xml-server" />

dubbo:registry

注册中心配置。对应的配置类: org.apache.dubbo.config.RegistryConfig。同时如果有多个不同的注册中心,可以声明多个 <dubbo:registry>标签,并在<dubbo:service><dubbo:reference>的 registry属性指定使用的注册中心。

属性:address 指定注册中心地址。如:

1
<dubbo:registry address="zookeeper://192.168.80.30:2181" />

dubbo:provider

服务提供者缺省值配置。对应的配置类: org.apache.dubbo.config.ProviderConfig。同时该标签为<dubbo:service><dubbo:protocol>标签的缺省值设置。

1
<dubbo:provider timeout="3000" />

dubbo:consumer

服务消费者缺省值配置。配置类: org.apache.dubbo.config.ConsumerConfig 。同时该标签为 <dubbo:reference> 标签的缺省值设置。

1
<dubbo:consumer timeout="3000"/>

dubbo:protocol

服务提供者协议配置。对应的配置类: org.apache.dubbo.config.ProtocolConfig。同时,如果需要支持多协议,可以声明多个 <dubbo:protocol> 标签,并在 <dubbo:service> 中通过 protocol 属性指定使用的协议。

属性:id 定义协议ID,可在<dubbo:service> 使用该ID

属性:name 协议名称,指定需要使用的协议

属性:port 指定服务的端口号

1
2
<dubbo:protocol id="d1"  name="dubbo" port="20880" />
<dubbo:protocol id="d2" name="dubbo" port="20882" />

dubbo:service

服务提供者暴露服务配置。对应的配置类:org.apache.dubbo.config.ServiceConfig

属性:interface 指定服务接口名

属性: ref 指定对象引用名称

属性: protocol 指定使用的协议

1
<dubbo:service interface="com.longshilee.service.OrderService" ref="orderService" protocol="d1" />

dubbo:reference

服务消费者引用服务配置。对应的配置类: org.apache.dubbo.config.ReferenceConfig

属性:id 服务引用BeanId,对应service中的ref

属性:interface 指定服务接口名

1
<dubbo:reference id="orderService" interface="com.longshilee.service.OrderService" />

以上标签已经够搭建简单的dubbo服务使用了,更多详细的dubbo标签属性详解,请到官方文档上查看

XML标签方式使用实例

接下来使用xml标签的方式搭建一个简单的dubbo服务示例:

使用开发工具搭建一个maven项目,项目中有三个子项目,名称自取,我这的示例为:dubbo-api、dubbo-xml-server、dubbo-xml-client

dubbo-api 用于定义项目用到的接口,及所有其他项目用到的公共的类等

dubbo-xml-server 作为dubbo的服务提供方项目

dubbo-xml-client 作为dubbo的服务调用方项目


dubbo-api

项目结构:

只定义了两个需要的接口。

定义了一个sayHello方法,代码如下:

1
2
3
4
5
package com.longshilee.service;

public interface OrderService {
String sayHello();
}
1
2
3
4
5
package com.longshilee.service;

public interface UserService {
String sayHello();
}

dubbo-xml-server

项目结构:

实现了api中的OrderService,实现类为OrderServiceImpl:

从代码可以看到我们使用了spring的@service注解,因为dubbo跟spring是无缝链接的,所以我们在使用dubbo的时候会跟spring一起用,所以我们在pom里导入了spring的相关的包。

同时我们也引入了dubbo-api,因为我们需要使用api中的OrderService.如下所示:

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

<dependencies>
<!-- spring支持 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>

<!-- dubbo -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo-config-spring</artifactId>
<version>${dubbo.version}</version>
</dependency>
<!-- zookeeper注册中心 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo-registry-zookeeper</artifactId>
<version>${dubbo.version}</version>
</dependency>

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo-rpc-dubbo</artifactId>
<version>${dubbo.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo-remoting-netty</artifactId>
<version>${dubbo.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo-serialization-hessian2</artifactId>
<version>${dubbo.version}</version>
</dependency>
<dependency>
<groupId>com.longshilee</groupId>
<artifactId>dubbo-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>

同时作为服务提供方,我们需要使用xml的方式配置服务信息,在resources目录下新建dubbo-server.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<context:component-scan base-package="com.longshilee.service"/>
<!--全局配置-->
<dubbo:provider timeout="3000" />
<!-- 服务提供方应用名称, 方便用于依赖跟踪 -->
<dubbo:application name="dubbo-xml-server" />
<!-- 使用本地zookeeper作为注册中心 -->
<dubbo:registry address="zookeeper://192.168.80.30:2181" />

<!--name指示使用什么协议监听端口:dubbo/rmi/rest-->
<dubbo:protocol id="d1" name="dubbo" port="20880" />
<dubbo:protocol id="d2" name="dubbo" port="20882" />

<!-- 通过xml方式配置为bean, 让spring托管和实例化 -->
<!--<bean id="orderService" class="com.longshilee.service.OrderServiceImpl"/>-->
<!-- 声明服务暴露的接口,并暴露服务 -->
<dubbo:service interface="com.longshilee.service.OrderService" ref="orderService" protocol="d1" />
</beans>

这样我们就把orderService服务注册到了注册中心zookeeper中,供其他调用方调用。

然后我们需要一个启动类将服务启动起来,所以定义了一个简单的启动类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.longshilee.bootstrap;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.io.IOException;

public class Boot {
public static void main(String[] args) throws IOException {
ClassPathXmlApplicationContext xml = new ClassPathXmlApplicationContext("classpath:dubbo-server.xml");//指定spring启动的配置文件
xml.start();
System.out.println("dubbo 服务启动成功");
System.in.read();//任意键停止,防止服务启动之后就停了
}
}

然后运行,就能看到服务启动起来了:

1
dubbo 服务启动成功

dubbo-xml-client

项目结构:

实现了api中的UserService接口,实现类为:UserServiceImpl

可以看到代码中,使用@Resource注解将orderService直接引入了进来,理论上来说,两个完全不同的项目,想通过这样子直接定义是没办法调用的,但通过dubbo,通过注册中心,就能够实现这样子的直接调用。

跟dubbo-xml-server项目一样,我们需要在pom中引入spring和dubbo的包。详见server的pom.

然后作为dubbo服务调用者,我们需要通过xml的方式,定义这是一个dubbo调用方,所以一样的道理,在resources目录下,我们新建dubbo-client.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<context:component-scan base-package="com.longshilee.service"/>
<!-- 服务提供方应用名称, 方便用于依赖跟踪 -->
<dubbo:application name="dubbo-xml-client" />
<!-- 使用本地zookeeper作为注册中心 -->
<dubbo:registry address="zookeeper://192.168.80.30:2181" />
<dubbo:consumer timeout="3000"/>
<dubbo:reference id="orderService" interface="com.longshilee.service.OrderService" />
</beans>

通过<dubbo:reference/>标签我们定义了需要调用的服务,orderService服务。

那么这里有一个问题,我们在client项目中通过@Resource注解直接将orderService实例从springIOC容器中取出来,那这个对象是怎么来的呢,因为我们根本没有通过spring的<bean/>标签来定义这个对象。刚才已经说了,dubbo跟spring是无缝链接的,所以dubbo底层已经将该实例加入到了springIOC中。

现在我们需要一个启动类,启动dubbo客户端:

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.longshilee.bootstrap;

import com.longshilee.service.UserService;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Boot {
public static void main(String[] args) {
ClassPathXmlApplicationContext xml = new ClassPathXmlApplicationContext("classpath:dubbo-client.xml");
xml.start();
UserService userService = (UserService) xml.getBean("userService");
System.out.println(userService.sayHello());
}
}

然后运行,我们就可以看到已经调用成功了,控制台打印:

1
longshilee hello

至此,我们的dubbo服务通过xml方式已经搭建完成了。

注解的方式使用dubbo实例

注解方式底层与xml方式一致,只是表现形式不同。目标都是将dubbo的基本配置信息注入,主要涉及到的必不可少的信息为:ApplicationConfig,ProtocolConfig,RegistryConfig,service,reference.

为了方便,我们还是使用上面的项目工程,在dubbo-xml-server和dubbo-xml-client进行改造。

dubbo-xml-server

  1. 新建config目录,在config目录下新建DubboConfig配置类,用于配置dubbo服务端基础信息。使用@EnableDubbo开启注解 Dubbo 功能,其中可以加入 scanBasePackages 属性配置包扫描的路径,用于扫描并注册 bean。代码内容如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    package com.longshilee.config;

    import com.alibaba.dubbo.config.*;
    import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;

    @Configuration
    @EnableDubbo(scanBasePackages = {"com.longshilee.service"})
    public class DubboConfig {

    @Bean
    ApplicationConfig applicationConfig(){
    ApplicationConfig applicationConfig = new ApplicationConfig();
    applicationConfig.setName("dubbo-xml-server");
    return applicationConfig;
    }

    @Bean
    ProviderConfig providerConfig(){
    ProviderConfig providerConfig = new ProviderConfig();
    providerConfig.setTimeout(3000);
    return providerConfig;
    }

    @Bean
    RegistryConfig registryConfig(){
    RegistryConfig registryConfig = new RegistryConfig();
    registryConfig.setAddress("zookeeper://192.168.80.30:2181");
    return registryConfig;
    }

    @Bean
    ProtocolConfig protocolConfig(){
    ProtocolConfig protocolConfig = new ProtocolConfig();
    protocolConfig.setId("d1");
    protocolConfig.setName("dubbo");
    protocolConfig.setPort(20880);
    return protocolConfig;
    }
    }

    可以看到基本与xml的标签配置信息一致,这不过上述是用代码方式配置而已。

  2. 将OrderServiceImpl上的注解@Service,由spring的注解换成dubbo的@Service注解。

  1. 修改服务端的启动类。因为是使用注解方式,所以我们spring的启动类也要改成使用注解的方式启动。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class Boot {
    public static void main(String[] args) throws IOException {
    // ClassPathXmlApplicationContext xml = new ClassPathXmlApplicationContext("classpath:dubbo-server.xml");
    // xml.start();
    AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(DubboConfig.class);
    app.start();
    System.out.println("dubbo 服务启动成功");
    System.in.read();
    }
    }

    使用AnnotationConfigApplicationContext类启动,将配置类DubboConfig引入进来即可。

    服务端就修改完成了,接下来修改客户端。

dubbo-xml-client

跟服务端流程基本一样。

  1. 新建config目录,在config目录下新建DubboConfig配置类,基本配置也是更xml方式一样,配置消费者端的基本信息。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    package com.longshilee.config;

    import com.alibaba.dubbo.config.*;
    import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;

    @Configuration
    @EnableDubbo(scanBasePackages = "com.longshilee.service")
    public class DubboConfig {

    @Bean
    ApplicationConfig applicationConfig(){
    ApplicationConfig applicationConfig = new ApplicationConfig();
    applicationConfig.setName("dubbo-xml-client");
    return applicationConfig;
    }

    @Bean
    RegistryConfig registryConfig(){
    RegistryConfig registryConfig = new RegistryConfig();
    registryConfig.setAddress("zookeeper://192.168.80.30:2181");
    return registryConfig;
    }

    @Bean
    ConsumerConfig consumerConfig(){
    ConsumerConfig consumerConfig = new ConsumerConfig();
    consumerConfig.setTimeout(3000);
    return consumerConfig;
    }

    @Bean
    ProtocolConfig protocolConfig(){
    ProtocolConfig protocolConfig = new ProtocolConfig();
    protocolConfig.setId("d2");
    protocolConfig.setName("dubbo");
    protocolConfig.setPort(20882);
    return protocolConfig;
    }
    }
  2. 调用OrderService时,由原来使用的注解@Resource改成dubbo的@Reference,来完成Bean的自动注入。

  1. 修改启动类。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class Boot {
    public static void main(String[] args) {
    // ClassPathXmlApplicationContext xml = new ClassPathXmlApplicationContext("classpath:dubbo-client.xml");
    //// xml.start();
    // UserService userService = (UserService) xml.getBean("userService");
    AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(DubboConfig.class);
    app.start();
    UserService userService = (UserService)app.getBean(UserService.class);
    System.out.println(userService.sayHello());
    }

    启动完成之后,我们就能看到控制台打印的调用信息,说明已经调用成功了。

    1
    longshilee hello

当然注解方式处理使用代码来设置配置信息之外,也可以使用属性文件来设置配置信息。

属性文件方式

在项目的resources目录下新建属性配置文件。如:

在dubbo-xml-server的resources下新建dubbo-server.properties配置文件。内容如下:

1
2
3
4
5
dubbo.application.name=dubbo-xml-server
dubbo.provider.timeout=3000
dubbo.registry.address=zookeeper://192.168.80.30:2181
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880

然后修改原来的DubboConfig.java配置类,使用@PropertySource注解将属性文件引入进来。然后将原来的信息先注释掉。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@Configuration
@EnableDubbo(scanBasePackages = {"com.longshilee.service"})
@PropertySource("classpath:dubbo-server.properties")
public class DubboConfig {

// @Bean
// ApplicationConfig applicationConfig(){
// ApplicationConfig applicationConfig = new ApplicationConfig();
// applicationConfig.setName("dubbo-xml-server");
// return applicationConfig;
// }
//
// @Bean
// ProviderConfig providerConfig(){
// ProviderConfig providerConfig = new ProviderConfig();
// providerConfig.setTimeout(3000);
// return providerConfig;
// }
//
// @Bean
// RegistryConfig registryConfig(){
// RegistryConfig registryConfig = new RegistryConfig();
// registryConfig.setAddress("zookeeper://192.168.80.30:2181");
// return registryConfig;
// }
//
// @Bean
// ProtocolConfig protocolConfig(){
// ProtocolConfig protocolConfig = new ProtocolConfig();
// protocolConfig.setId("d1");
// protocolConfig.setName("dubbo");
// protocolConfig.setPort(20880);
// return protocolConfig;
// }

}

一样的道理在dubbo-xml-client项目的resources目录下新建dubbo-client.properties属性文件。

1
2
3
4
5
dubbo.application.name=dubbo-xml-client
dubbo.registry.address=zookeeper://192.168.80.30:2181
dubbo.consumer.timeout=3000
dubbo.protocol.name=dubbo
dubbo.protocol.port=20882

一样的在原来的DubboConfig配置类中将属性文件引入进来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Configuration
@EnableDubbo(scanBasePackages = "com.longshilee.service")
@PropertySource("classpath:dubbo-client.properties")
public class DubboConfig {

// @Bean
// ApplicationConfig applicationConfig(){
// ApplicationConfig applicationConfig = new ApplicationConfig();
// applicationConfig.setName("dubbo-xml-client");
// return applicationConfig;
// }
//
// @Bean
// RegistryConfig registryConfig(){
// RegistryConfig registryConfig = new RegistryConfig();
// registryConfig.setAddress("zookeeper://192.168.80.30:2181");
// return registryConfig;
// }
//
// @Bean
// ConsumerConfig consumerConfig(){
// ConsumerConfig consumerConfig = new ConsumerConfig();
// consumerConfig.setTimeout(3000);
// return consumerConfig;
// }
//
// @Bean
// ProtocolConfig protocolConfig(){
// ProtocolConfig protocolConfig = new ProtocolConfig();
// protocolConfig.setId("d2");
// protocolConfig.setName("dubbo");
// protocolConfig.setPort(20882);
// return protocolConfig;
// }
}

这样,以属性文件的方式配置dubbo信息就完成了。然后运行,结果是跟之前一样的。

文章作者: Shawn Lee
文章链接: https://longshilee.github.io/2020/02/27/%E5%88%86%E5%B8%83%E5%BC%8F%E6%9E%B6%E6%9E%84-dubbo/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Shawn·Lee
打赏
  • 微信
  • 支付宝

评论