1、Dubbo 处理流程
节点说明:
节点 | 角色名称 |
Provider | 暴露服务的服务提供方 |
Consumer | 调用远程服务的服务消费方 |
Registry | 服务注册与发现的注册中心 |
Monitor | 统计服务的调用次数和调用时间的监控中心 |
Container | 服务运行容器 负责启动 加载 运行服务提供者 |
调用关系说明:
- 虚线:代表异步调用
- 实线:代表同步访问
- 蓝色虚线:是在启动时完成的功能
- 红色虚线:是程序运行中执行的功能
调用流程:
- 服务提供者在服务容器启动时,向注册中心注册自己提供的服务。
- 服务消费者在启动时,向注册中心订阅自己所需的服务。
- 注册中心返回服务提供者地址列表给消费者,如果有变更注册中心会基于长连接推送变更数据给消费者。
- 服务消费者从提供者地址列表中,基于软负载均衡算法选一台提供者进行调用;如果调用失败,则重新选择一台。
- 服务提供者和消费者,在内存中的调用次数和调用时间、定时每分钟发送给监控中心。
2、入门案例介绍
在Dubbo中所有的的服务调用都是基于接口去进行双方交互的。双方协定好Dubbo调用中的接口,提供者来提供实现类并且注册到注册中心上。调用方则只需要引入该接口,并且同样注册到相同的注册中心上(消费者)。即可利用注册中心来实现集群感知功能,之后消费者即可对提供者进行调用。
我们所有的项目都是基于Maven去进行创建,这样相互在引用的时候只需要以依赖的形式进行展现就可以了。
并且这里我们会通过maven的父工程来统一依赖的版本。
程序实现分为以下几步骤:
- 建立maven工程 并且 创建API模块: 用于规范双方接口协定
- 提供provider模块,引入API模块,并且对其中的服务进行实现。将其注册到注册中心上,对外来统一提供服务。
- 提供consumer模块,引入API模块,并且引入与提供者相同的注册中心。再进行服务调用。
3、开发过程
项目结构如下
定义了一个父模块demo-base,然后再下面创建了三个子模块。父工程的pom.xml中主要是对Duboo相关jar进行版本控制,这样子模块导入依赖时,就不需要指定版本了。
<?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>
<groupId>com.lagou</groupId>
<artifactId>demo-base</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>service-api</module>
<module>service-provider</module>
<module>service-consumer</module>
</modules>
<properties>
<dubbo.version>2.7.3</dubbo.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>${dubbo.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-common</artifactId>
<version>${dubbo.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-zookeeper</artifactId>
<version>${dubbo.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-nacos</artifactId>
<version>${dubbo.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-dubbo</artifactId>
<version>${dubbo.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-remoting-netty4</artifactId>
<version>${dubbo.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-serialization-hessian2</artifactId>
<version>${dubbo.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- 日志配置 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.5</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.5</version>
</dependency>
<!-- json数据化转换 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
3.1、创建service-api模块
这个模块的作用提供一个接口,供生产者使用。(pom.xm中没有导入依赖,故省略)
package com.lagou.service;
public interface HelloService {
String sayHello(String name);
}
3.2、创建service-provider模块
这个模块作用是实现上面中的接口,然后借助于zookeeper暴露服务,供其消费者远程调用
注:这个模块必须导入service-api模块,不然无法实现其接口
(1)pom.xml
<?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>demo-base</artifactId>
<groupId>com.lagou</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>service-provider</artifactId>
<dependencies>
<dependency>
<groupId>com.lagou</groupId>
<artifactId>service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-zookeeper</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-dubbo</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-remoting-netty4</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-serialization-hessian2</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
(2)编写实现类。注意这里也使用了Dubbo中的@Service 注解来声明他是一个服务的提供者。
package com.lagou.service.impl;
import com.lagou.service.HelloService;
import org.apache.dubbo.config.annotation.Service;
@Service // 使用Duboo中的service注解声明服务提供者
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String name) {
return "hello:"+name;
}
}
(3)编写配置文件,用于配置dubbo。比如这里我就叫dubbo-provider.properties ,放入到resources 目录下。
dubbo.application.name=dubbo-provider
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880
- dubbo.application.name:
- dubbo.protocol.name: 对外提供的时候使用的协议
- dubbo.protocol.port: 该服务对外暴露的端口是什么,在消费者使用时,则会使用这个端口并且使用指定的协议与提供者建立连接。
(4)编写启动的main 函数。这里面做的比较简单,主要要注意注解方式中的注册中心这里是使用的本机2181端口来作为注册中心。
package com.lagou;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import java.io.IOException;
public class DubboPureMain {
public static void main(String[] args) throws IOException {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProviderConfiguration.class);
context.start();
System.in.read();
}
@Configuration
@EnableDubbo(scanBasePackages = "com.lagou.service.impl")
@PropertySource("classpath:/dubbo-provider.properties")
static class ProviderConfiguration {
@Bean
public RegistryConfig registryConfig() {
RegistryConfig config = new RegistryConfig();
config.setAddress("zookeeper://127.0.0.1:2181");
return config;
}
}
}
注意:在运行代码前,一定要确保,zookeeper服务已经启动了。还有如果运行时,报错提示zookeeper not connected ,可能会是zookeeper的超时时间设置过短,需要在配置文件去设置。还有可能是各个jar版本的问题,反正这个版本,我尝试过没有问题。控制台出现 log4j的爆红提示,是因为没有添加log4j.properties配置文件,添加上就可以了。
log4j.rootCategory=INFO,CONSOLE
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{HH:mm:ss.SSS} [%t] %-5p %c.%M\(%F:%L\) - %m%n
3.3、创建service-consumer模块
此模块作用是消费生产者暴露的服务,说人话就是,调用生产者暴露出来的的方法。
(1)pom.xml
<?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>demo-base</artifactId>
<groupId>com.lagou</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>service-consumer</artifactId>
<dependencies>
<dependency>
<groupId>com.lagou</groupId>
<artifactId>service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-zookeeper</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-dubbo</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-remoting-netty4</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-serialization-hessian2</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
(2)编写服务,用于真实的引用dubbo接口并使用。因为这里是示例,所以比较简单一些。这里面@Reference 中所指向的就是真实的第三方服务接口。
package com.lagou.bean;
import com.lagou.service.HelloService;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.stereotype.Component;
@Component
public class ConsumerComponent {
@Reference
// 引用dubbo,远程调用方法
private HelloService helloService;
public String sayHello(String name) {
return helloService.sayHello(name);
}
}
(3)编写消费者的配置文件。这里比较简单,主要就是指定了当前消费者的名称和注册中心的位置。通过这个注册中心地址,消费者就会注册到这里并且也可以根据这个注册中心找到真正的提供者列表。
dubbo.application.name=service-consumer
dubbo.registry.address=zookeeper://127.0.0.1:2181
dubbo.application.qosEnable=true
dubbo.application.qosPort=33333
dubbo.application.qosAcceptForeignIp=false
(4)编写启动类,这其中就会当用户在控制台输入了一次换行后,则会发起一次请求。
package com.lagou;
import com.lagou.bean.ConsumerComponent;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import java.io.IOException;
public class AnnotationConsumerMain {
public static void main(String[] args) throws IOException {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerConfiguration.class);
context.start();
// 获取消费者组件
ConsumerComponent service = context.getBean(ConsumerComponent.class);
while (true) {
System.in.read();
String result = service.sayHello("张三");
System.out.println("result:"+result);
}
}
@Configuration
@PropertySource("classpath:/dubbo-consumer.properties")
@ComponentScan(basePackages = "com.lagou.bean")
@EnableDubbo
static class ConsumerConfiguration {
}
}
至此,配置远程调用服务完成。
4、配置方式介绍
下面我们来使用不同的方式来对Dubbo进行配置。每种配置方式各有不同,一般可以分为以下几个
- 注解: 基于注解可以快速的将程序配置,无需多余的配置信息,包含提供者和消费者。但是这种方式有一个弊端,有些时候配置信息并不是特别好找,无法快速定位。(上面案例就是注解方式)
- XML: 一般这种方式我们会和Spring做结合,相关的Service和Reference均使用Spring集成后的。通过这样的方式可以很方便的通过几个文件进行管理整个集群配置。可以快速定位也可以快速更改。
- 基于代码方式: 基于代码方式的对上述配置进行配置。这个使用的比较少,这种方式更适用于自己公司对其框架与Dubbo做深度集成时才会使用。
5、XML方式
我们一般XML会结合Spring应用去进行使用,将Service的注册和引用方式都交给Spring去管理。下面我们还是针对于上面的demo进行实现。
这里我们针对于api模块不做处理,还是使用原先的接口。从提供者和消费者做讲解。这了我们直接通过spring的方式去做讲解。
5.1、provider模块
(1)pom.xml
<?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>demo-base-xml</artifactId>
<groupId>com.lagou</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>service-provider</artifactId>
<dependencies>
<dependency>
<groupId>com.lagou</groupId>
<artifactId>service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-zookeeper</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-nacos</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-dubbo</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-remoting-netty4</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-serialization-hessian2</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-config-spring</artifactId>
<version>2.7.3</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
</project>
注意这个xml中比使用注解方式的方式,要多导入一个依赖:
<dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-config-spring</artifactId> <version>2.7.3</version> </dependency>
(2)编写实现类,不需要引入任何的注解配置。
package com.lagou.service.impl;
import com.lagou.service.HelloService;
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String name) {
return "hello: " + name;
}
}
(3)编写dubbo-provider.xml 文件,用于对dubbo进行文件统一配置。并且对刚才的配置进行引入。
<?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:dubbo="http://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!-- 提供方应用信息,用于计算依赖关系 -->
<dubbo:application name="service-provider2"/>
<!-- 使用zookeeper注册中心暴露服务地址 -->
<dubbo:registry address="zookeeper://127.0.0.1:2181" id="r1" timeout="10000"/>
<!-- 用dubbo协议在20882端口暴露服务
<dubbo:protocol name="dubbo" port="20882" /> -->
<!-- 用dubbo协议在20883端口暴露服务 -->
<dubbo:protocol name="dubbo" port="20883" />
<!-- 声明需要暴露的服务接口 -->
<dubbo:service interface="com.lagou.service.HelloService" ref="helloService" />
<!-- 和本地bean一样实现服务 -->
<bean id="helloService" class="com.lagou.service.impl.HelloServiceImpl" />
</beans>
(4)编写模块启动类。
package com.lagou;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ProviderApplication {
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:dubbo-provider.xml");
applicationContext.start();
System.in.read();
}
}
5.2、consumer模块
(1)pom.xml
<?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>demo-base-xml</artifactId>
<groupId>com.lagou</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>service-consumer</artifactId>
<dependencies>
<dependency>
<groupId>com.lagou</groupId>
<artifactId>service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-zookeeper</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-nacos</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-dubbo</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-remoting-netty4</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-serialization-hessian2</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-config-spring</artifactId>
<version>2.7.3</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
</project>
(2)定义spring的配置xml
<?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:dubbo="http://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 -->
<dubbo:application name="service-consumer"/>
<!-- 使用zookeeper注册中心暴露发现服务地址 -->
<dubbo:registry address="zookeeper://127.0.0.1:2181" timeout="10000"/>
<!-- 生成远程服务代理,可以和本地bean一样使用demoService -->
<dubbo:reference id="helloService" interface="com.lagou.service.HelloService" timeout="4000" retries="2" />
</beans>
(3)引入启动模块。因为引用了Spring框架,所以再上一步的helloService会被当做一个bean注入到真实的环境中。在我们生产级别使用的时候,我们可以通过Spring中的包扫描机制,通过@Autowired 这种机制来进行依赖注入。
package com.lagou;
import com.lagou.service.HelloService;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ConsumerApplication {
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:dubbo-comsumer.xml");
// applicationContext.start(); 可以不启动,也能获取bean
HelloService helloService = applicationContext.getBean("helloService", HelloService.class);
System.in.read();
String result = helloService.sayHello("world");
System.out.println("result=" + result);
}
}