Dubbo知识点
1、概念
Dubbo 是一个分布式服务框架,致力于提供高性能和透明化的 RPC 远程服务调用方案,以及 SOA 服务治理方案。
通信机制:使用netty通信服务进行通信,netty是基于nio字符缓冲流来实现。
2、架构图
3、工作原理
具体调用流程如下:
(1)服务器负责启动,加载,运行服务提供者。
(2)服务提供者在启动时,向注册中心注册自己所提供的服务。
(3)服务消费者在启动时,向注册中心订阅自己所需的服务。
(4)注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于TCP长连接推送变更数据给消费者。
(5)服务消费者从提供的服务列表中,基于软负载均衡算法,选一台提供者进行调用,如果失败,则选择另一台调用。
(6)服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到检测中心。
4、zookeeper环境搭建
可参照这篇文章进行部署安装:
https://blog.csdn.net/ITWUYI/article/details/109196253
5、Dubbo控制台admin环境搭建
(1) 下载源码打包
去github上下载相关代码,打包成war。
利用maven命令打包:
mvn install -Dmaven.test.skip=true
命令执行成功结束后,在 dubbo-admin 目录下会生成target文件夹,文件夹下会出现一个war包。
若未成功生成war包,可进入dubbo-admin目录,执行命令mvn package -Dmaven.test.skip ,把dubbo-admin打成war包。
(2) 解压
将上面的dubbo的war打包文件复制到centos 7 服务器tomcat下webapps/ROOT目录(注:最好先删除ROOT下已有文件,否则在dubbo管理页面会报错IllegalAccessError)。然后解压,在webapps/ROOT/WEB-INF下,有一个dubbo.properties文件,里面配置指向 Zookeeper server的地址(使用的是Zookeeper 的注册中心),要修改成相应zookeeper的ip地址和端口。
(3)启动tomcat
到tomcat目录的bin目录下
运行tomcat
./startup.sh
关闭tomcat服务
./shutdown.sh
(4)访问dubbo管理控制中心
http://192.168.13.21:8080/dubbo-admin
用户名和密码:root
完毕。
6、代码实现
下面用一个简单的项目讲解dubbo+springboot是如何集成和如何运行协作的?
api是服务提供者的接口API,最普通的maven项目。
dubbo-provider是所谓的服务提供者,springboot项目。
dubbo-consumer是服务消费者,springboot项目。
(1)新建子模块api,groupId为com.itwuyi,artifactId为api。
这个模块用来存放微服务中所有的实体类,以及所有的服务提供者提供的服务接口。它使用普通的maven项目。
(一)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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itwuyi</groupId>
<artifactId>api</artifactId>
<version>1.0-SNAPSHOT</version>
</project>
(二)新建包model,用于存放所有的model,包下新建实体类User:
package model;
import java.io.Serializable;
public class User implements Serializable {
private Integer id;
private String username;
private String password;
private String tel;
private String address;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getTel() {
return tel;
}
public void setTel(String tel) {
this.tel = tel;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
(三)新建包service,用于存放所有服务提供者提供的服务接口,包下新建UserService:
package service;
import model.User;
public interface UserService {
User findUserById(Integer id);
}
(四)使用maven install打包为jar安装到仓库。
(2)新建子模块dubbo-provider,springboot项目,作为服务提供者。groupId为com.itwuyi,artifactId为dubbo-provider。
(一)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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.itwuyi</groupId>
<artifactId>dubbo-provider</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>dubbo-provider</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.itwuyi</groupId>
<artifactId>api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- dubbo的依赖 -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.6</version>
</dependency>
<!-- zk的依赖 -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper</artifactId>
<version>2.7.6</version>
<type>pom</type>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
使用zk作为注册中心、配置管理中心,这里添加的zk依赖是作为zkCli。
zookeeper的依赖要用去掉slf4j部分,因为spring-boot-strat已经包含了slf4j,如果不去掉会报错:(slf4j绑定了多个jar包中的Logger)。
dubbo可以使用多种注册中心,比如zookeeper、redis等,上面的zookeeper依赖只包含以zookeeper作为注册中心的依赖,服务只能以zookeeper作为注册中心。
(二)新建包service,包下新建类UserServiceImpl实现服务接口UserService。
package com.itwuyi.dubboprovider.service;
import model.User;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.stereotype.Component;
import service.UserService;
//此处的@Service是dubbo下的注解,不是spring的注解
@Service
@Component
public class UserServiceImpl implements UserService {
@Override
public User findUserById(Integer id) {
User user = new User();
user.setId(id);
user.setUsername("itwuyi");
return user;
}
}
(三)配置文件
连接到zkServer的时间开销大,如果不将注册的超时时间设置大一些,可能会报错:zookeeper not connected,时间不够,还没连上就超时取消了。
另外,注意查看是否搭配了zookeeper集群(如果只启动了一台,也会出现这个现象)。
#如果指定了spring应用名称,可以缺省dubbo的应用名称,这2个至少要配置1个,缺省dubbo的应用名称时默认值是spring的应用名称
#dubbo.application.name=dubbo-service
spring.application.name=dubbo-provider
#注册中心地址
dubbo.registry.address=zookeeper://192.168.13.21:2181
#端口号可以写在address中,也可以单独写。实质是从address中获取的port是null,后面设置的port覆盖了null
#dubbo.registry.port=2181
#指定dubbo使用的协议、端口
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880
#指定注册到zk上超时时间,ms
dubbo.registry.timeout=12000
dubbo.config-center.timeout=12000
#指定实现服务(提供服务)的包
dubbo.scan.base-packages=com.itwuyi.dubboprovider.service
(四)springboot启动类
package com.itwuyi.dubboprovider;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableDubbo //会扫描所有的包,从中找出dubbo的@Service标注的类
// @DubboComponentScan(basePackages = "com.itwuyi.dubboprovider.service") //只扫描指定的包
public class DubboProviderApplication {
public static void main(String[] args) {
SpringApplication.run(DubboProviderApplication.class, args);
}
}
(3)新建子模块dubbo-consumer,springboot web项目,作为服务消费者。groupId为com.itwuyi,artifactId为dubbo-consumer
(一)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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.itwuyi</groupId>
<artifactId>dubbo-consumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>dubbo-consumer</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.itwuyi</groupId>
<artifactId>api</artifactId>
<version>1.0-SNAPSHOT</version>
</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.6</version>
</dependency>
<!-- zk的依赖 -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper</artifactId>
<version>2.7.6</version>
<type>pom</type>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
(二)新建包controller,包下新建类OrderController来调用服务。
package com.itwuyi.dubboconsumer.controller;
import model.User;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import service.UserService;
@Controller
public class OrderController {
@Reference//dubbo注解
private UserService userService;
@RequestMapping("/consumer/{id}")
@ResponseBody
public User getUser(@PathVariable Integer id){
//调用服务
System.out.println(id);
User user= userService.findUserById(id);
return user;
}
}
这里的@Reference引入的包是org.apache.dubbo.config.annotation.Reference,而不是jdk自带的。
(三)配置文件
spring.application.name=dubbo-consumer
#dubbo.application.name=dubbo-consumer
#注册中心地址
dubbo.registry.address=zookeeper://192.168.13.21:2181
#dubbo.registry.port=2181
#协议、端口
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880
#连接zk的超时时间,ms
dubbo.registry.timeout=12000
#启动应用时是否检查注册中心上有没有依赖的服务,默认true
#dubbo.consumer.check=false
缺省dubbo.consumer.check配置时,默认为true,要检查。
消费者连接注册中心时,会订阅要调用的服务,如果提供该服务的服务器一台都没有,会报错,这个消费者无法启动,这样在消费者启动时就能检查到是否有可用的生产者,提前发现问题。
调试时,如果先启动消费者,后启动|未启动生产者,消费者往往启动不了,报错:创建不了xxx bean,因为 Injection of @Reference dependencies is failed ,没有生产者提供该服务。
设置为false,消费者启动时不检查,就算没有生产者提供该服务,消费者也能正常启动,只是调用该服务时会出错。
4、先启动生产者,再启动消费者
启动生产者的控制台:
启动消费者的控制台:
当一切都启动成功,去浏览器访问,结果如下所示:
zkServer上,dubbo的根目录是/dubbo,
一个提供的服务对应一个znode,包名.服务接口名的形式,/dubbo/service.providerService
这个节点的子节点providers存储此服务的所有提供者的注册信息,子节点consumers存储订阅此服务的所有消费者的信息。
dubbo @Service标注的类所实现的接口,会自动在zkServer上创建一个对应的znode,包名.接口名的形式。
生产者实现该接口,并使用dubbo @Service标识该类是服务提供者,实现的接口就是它提供的服务;消费者使用@Reference来订阅、引用服务。
5、dubbo配置相关说明
指定注册中心
#协议、ip:port可以写在一起
dubbo.config-center.address=zookeeper://192.168.13.21:2181
#可以分开写
dubbo.registry.protocol=zookeeper
dubbo.registry.address=192.168.13.21:2181
#如果是集群,逗号分隔
dubbo.registry.address=192.168.13.21:2181,192.168.13.22:2181
设置连接到注册中心的超时时间
#这个是提供者、消费者都可以使用的
dubbo.registry.timeout=12000
#可以使用专门的
dubbo.consumer.timeout=12000
dubbo.provider.timeout=12000
7、排查问题
(1)springboot整合dubbo时,消费者调用提供者的方法时出现空指针异常?
1)提供者的实现类的@Service注解要用的是com.alibaba.dubbo.config.annotation(或者org.apache.dubbo.config.annotation)包下的。
2)提供者的实现类不能缺少**@Component**注解,这个类还是要交给spring管理的(这个地方很多人容易犯错,甚至看到有些分享者分享出来的代码都忘了加上这个注解)。
3)主程序要加上@EnableDubbo注解。
4)查看提供者的application.yml或者application.properties里面的dubbo.scan.base-packages=xxx,检查要扫描的包路径正确不正确。
后续如有更优的方法,会继续补充。