前言
之前以及了解过了springboot cloud 微服务的一系列框架,其中dubbo在3之前一直作为一款优秀的rpc框架存在(对标spring cloud中的feign组件),为此duboo3出以后,dubbo也逐渐变成了一个微服务整合平台,目前我进行学习的是个时候遇到很多坑dubbo x springboot 官方开发文档
环境安装
弱依赖环境 统一建议使用docker 安装
运行zookper 作为注册中心 naocs当然也可以
docker run --name some-zookeeper -p 2181:2181 --restart always -d zookeeper
管理可视化界面
docker run --name dubbo-admin -e admin.registry.address=zookeeper://some-zookeeper:2181 -p 8080:8080 --link some-zookeeper --restart always -d apache/dubbo-admin
此时访问虚拟机地址 :8080 默认密码账户 root
和springcloud不同的是,dubbo中一个接口实现就为一个服务
搭建工程
下面的操作都是根据官网来的
目录结构
父工程pom
<?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>org.example</groupId>
<artifactId>Dubbo-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>dubbo-comsumer</module>
<module>dubbo-provider</module>
<module>dubbo-commom</module>
</modules>
<properties>
<dubbo.version>3.2.0-beta.4</dubbo.version>
<spring-boot.version>2.7.12</spring-boot.version>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>3.1.10</version>
</dependency>
<!-- Spring Boot -->
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper-curator5</artifactId>
<version>${dubbo.version}</version>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
子模块-privider 服务提供者
<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.itheima</groupId>
<artifactId>dubbo-provider</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<parent>
<groupId>org.example</groupId>
<artifactId>Dubbo-demo</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>dubbo-commom</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- dubbo -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper-curator5</artifactId>
<type>pom</type>
<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>
</project>
其中common 是自己抽离出来的通用接口模块,这里一切都和springcloud中使用feign类似远程调用工具feign
一样
commom 通用接口模块
其中的定义的一个接口
public interface DoubleUserService {
public String getUser();
}
服务端实现接口
使用dubboservice 暴露接口
@DubboService(version = "2.0")
public class DoubleUserServiceImpl2 implements DoubleUserService {
@Override
public String getUser() {
return "数据库查询出是:李四";
}
}
该注解参数
第二个实现接口的类 指定版本为1
@DubboService(version = "1.0")
public class DoubleUserServiceImpl implements DoubleUserService {
@Override
public String getUser() {
return "数据库查询出来是username: 王二";
}
}
yaml 配置文件
dubbo:
application:
name: dubbo-springboot-demo-provider
protocol:
name: dubbo
port: -1
registry:
address: zookeeper://${zookeeper.address:192.168.249.133}:2181
#我的工程跟官网这里不加会报错 官网不会 很奇怪
parameters:
blockUntilConnectedWait: 50
server:
port: 8762
启动类
@SpringBootApplication
@EnableDubbo(scanBasePackages = "org.example.Service")
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class,args);
}
}
此时成功注册并且web界面可以观察
服务消费端(目前有问题没有解决)
这里我是跟着官网来的,但是问题出现的很严重
pom文件
<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.itheima</groupId>
<artifactId>dubbo-comsumer</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<parent>
<groupId>org.example</groupId>
<artifactId>Dubbo-demo</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>dubbo-commom</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>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper-curator5</artifactId>
<type>pom</type>
<exclusions>
<exclusion>
<artifactId>slf4j-reload4j</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.itheima</groupId>
<artifactId>dubbo-provider</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- spring boot starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
</project>
作为服务消费方,使用到dubbo服务的控制层
@RestController
@RequestMapping("user")
public class Usercontroller {
@DubboReference(version="1.0") //指定调用版本
private DoubleUserService userService;
@GetMapping("info1")
public String get1(){
String s = userService.getUser();
return "info:"+s;
}
}
启动类 这个enable我百度文档说加不加都不所谓
//@EnableDubbo
@SpringBootApplication
public class ComsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ComsumerApplication.class,args);
}
我指定了使用的服务接口,并且指定了版本
配置文件
dubbo:
application:
name: dubbo-springboot-demo-comsumer
protocol:
name: dubbo
port: -1
registry:
address: zookeeper://${zookeeper.address:192.168.249.133}:2181
parameters:
blockUntilConnectedWait: 50
server:
port: 8444 # 选择一个可用的端口
问题1:出现了报错 端口已经绑定 并且无论我切换任意端口都无法接口该问题
java.net.BindException: Address already in use: bind
at java.base/sun.nio.ch.Net.bind0(Native Method) ~[na:na]
at java.base/sun.nio.ch.Net.bind(Net.java:555) ~[na:na]
at java.base/sun.nio.ch.ServerSocketChannelImpl.netBind(ServerSocketChannelImpl.java:337) ~[na:na]
at java.base/sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:294) ~[na:na]
at io.netty.channel.socket.nio.NioServerSocketChannel.doBind(NioServerSocketChannel.java:141) ~[netty-transport-4.1.92.Final.jar:4.1.92.Final]
at io.netty.channel.AbstractChannel$AbstractUnsafe.bind(AbstractChannel.java:562) ~[netty-transport-4.1.92.Final.jar:4.1.92.Final]
at io.netty.channel.DefaultChannelPipeline$HeadContext.bind(DefaultChannelPipeline.java:1334) ~[netty-transport-4.1.92.Final.jar:4.1.92.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeBind(AbstractChannelHandlerContext.java:600) ~[netty-transport-4.1.92.Final.jar:4.1.92.Final]
at io.netty.channel.AbstractChannelHandlerContext.bind(AbstractChannelHandlerContext.java:579) ~[netty-transport-4.1.92.Final.jar:4.1.92.Final]
at io.netty.channel.DefaultChannelPipeline.bind(DefaultChannelPipeline.java:973) ~[netty-transport-4.1.92.Final.jar:4.1.92.Final]
at io.netty.channel.AbstractChannel.bind(AbstractChannel.java:260) ~[netty-transport-4.1.92.Final.jar:4.1.92.Final]
at io.netty.bootstrap.AbstractBootstrap$2.run(AbstractBootstrap.java:356) ~[netty-transport-4.1.92.Final.jar:4.1.92.Final]
at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java:174) ~[netty-common-4.1.92.Final.jar:4.1.92.Final]
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:167) ~[netty-common-4.1.92.Final.jar:4.1.92.Final]
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:470) ~[netty-common-4.1.92.Final.jar:4.1.92.Final]
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:569) ~[netty-transport-4.1.92.Final.jar:4.1.92.Final]
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) ~[netty-common-4.1.92.Final.jar:4.1.92.Final]
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.92.Final.jar:4.1.92.Final]
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.92.Final.jar:4.1.92.Final]
at java.base/java.lang.Thread.run(Thread.java:840) ~[na:na]
但是dubbo-adnmin 界面却可以发现该服务
问题2:于是既然服务已经发现,那么我尝试访问这个消费者的控制层,因为这里会对服务提供者进行远程调用,可是报错
No provider available from registry 192.168.249.133:2181 for service org.example.api.DoubleUserService:1.0.0 on consumer 192.168.10.98 use dubbo version 3.2.0-beta.4, please check status of providers(disabled, not registered or in blacklist).
我已经各自百度2天了,官方文档也没有类似问题 ,本文章长期更新,等解决了在补完
问题解决办法
问题二解决了 我把注册中心从zookper切换为了nacosdubbo配置中心文档
注册中心切换为nacos
因为nacos3适配的nacos在2.0以上,nacos在2.2过后也发生了大改变(需要挂载更多端口),为此这里安装的是nacos 2.2.0
docker run --name nacos-server \
-p 8848:8848 \
-p 7848:7848 \
-p 9848:9848 \
-p 9849:9849 \
--privileged=true \
--restart=always \
-e JVM_XMS=256m \
-e JVM_XMX=256m \
-e MODE=standalone \
-e PREFER_HOST_MODE=hostname \
-v /mydata/nacos/logs:/home/nacos/logs \
-d nacos/nacos-server:v2.2.0
消费者和服务提供者pom文件
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>dubbo-commom</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- dubbo -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper-curator5</artifactId>
<type>pom</type>
<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>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.apache.dubbo</groupId>-->
<!-- <artifactId>dubbo</artifactId>-->
<!-- <version>3.0.9</version>-->
<!-- </dependency>-->
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>2.2.0</version>
</dependency>
</dependencies>
服务提供模块配置文件
dubbo:
application:
name: dubbo-springboot-demo-provider
protocol:
name: dubbo
port: -1
registry:
address: nacos://192.168.249.133:8848?username=nacos&password=nacos
# parameters:
# blockUntilConnectedWait: 50
server:
port: 8762
打开服务器/虚拟机ip:8848/nacos 密码账户 默认都是nacos
发现注册成功 dubbo一个接口就是一个服务
然后对应的修改消费端口的配置文件
dubbo:
config-center:
timeout: 10000
application:
name: dubbo-springboot-demo-comsumer
protocol:
name: dubbo
registry:
address: nacos://192.168.249.133:8848?username=nacos&password=nacos
timeout: 10000
# parameters:
# blockUntilConnectedWait: 50
server:
port: 7777 # 选择一个可用的端口
启动后发现端口依旧报错 修改任意端口都是该问题
Address already in use: bind
但是当我访问消费端的控制层
!!!既然出数据了,并且我在生产者provider添加了日志
确实成功证明dubbo的rpc功能可以实现
dubbo3中的新特性
因为dubbo是国内开发的,所以文档很容易阅读,其中
其中比较有意思的是通信协议 dubbo支持多种协议,和springcloud不同的是,dubbo有个更多的通信协议,并且新增了triple(http2.0)能够更好的融入微服务的生态圈子
dubbo切换triple协议
新⼀代RPC协议定义了全新的 RPC通信协议Triple,⼀句话概括Triple:它是基于HTTP/ 2构建的RPC协议,完全兼容gRPC,并在此基础上扩展出了更丰富的语义。 使⽤Triple协议更容易到适配⽹关、Mesh架构,Triple协议让Dubbo更⽅便的与各种⽹关、Sidecar组件配合⼯作。多语⾔友好,推荐配合Protobuf使⽤Triple协议,使⽤IDL定义服务,使⽤
Protobuf编码业务数据。
流式通信⽀持。Triple协议⽀持RequestStream、ResponseStream、Bi-directionStream
当使⽤Triple协议进⾏RPC调⽤时,⽀持多种⽅式来调⽤服务,只不过在服务接⼝中要定义不同的⽅法
引入依赖
使用dubbo的模块
<!-- triple 通信协议-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-triple</artifactId>
<version>3.2.0</version>
</dependency>
提供者和消费者修改配置文件
dubbo:
application:
name: dubbo-springboot-demo-provider
#更换通信协议
protocol:
name: tri
port: -1
registry:
address: nacos://192.168.249.133:8848?username=nacos&password=nacos
# parameters:
# blockUntilConnectedWait: 50
server:
port: 8762
重启后在
在nacos查看服务详情,发现通信协议已经更换
流式协议 使用triple后可以使用流式通讯
因为dubbo底层使用到netty 非阻塞io所以流失通信和netty也十分类似
这些回调方法确实与 NIO(非阻塞IO)中的事件处理机制类似,它们在特定的事件发生时被触发。流式通信中使用的StreamObserver接口来实现双向流式通信
更改原来的dubbo接口模块,新建俩个接口 ,一个接口不做返回,一个接口返回给流的对象
public interface DoubleUserService {
public String getUser();
/**
* 在 Java 接口中,从 Java 8 开始引入了默认方法(Default Methods),它允许在接口中提供方法的默认实现。这对于向现有接口添加新的方法而不破坏实现类的现有代码是非常有用的。默认方法使用 default 关键字声明。
*
* 在你提供的 DoubleUserService 接口中,sayHelloStream 方法是一个默认方法,它提供了一个默认实现,返回一个 StreamObserver<String> 对象。这是合法的,并不会导致编译错误。
*
* 默认方法的引入主要是为了在不破坏接口的同时,为接口提供一些新的功能。实现类可以选择覆盖默认方法,也可以直接使用默认实现。
*
* 至于你注释掉的
* @param
*/
// default public void setUser(String user ){
// System.out.println("摩羯阿婆");
// }
default void sayHelloServerStream(String name, StreamObserver<String>
response) {}
// / SERVER_STREAM
// CLIENT_STREAM / BI_STREAM
default StreamObserver<String> sayHelloStream(StreamObserver<String> response) {
return response;
}
}
服务提供模块 既然实现接口
@DubboService(version = "2.0")
public class DoubleUserServiceImpl2 implements DoubleUserService {
@Override
public String getUser() {
return "数据库查询出是:李四";
}
// / SERVER_STREAM
@Override
public void sayHelloServerStream(String name, StreamObserver<String>
response) {
//向客户端的流中添加数据
response.onNext(name + " hello");
response.onNext(name + " world1");
response.onNext(name + "你好3 ");
response.onNext(name + " 不好");
response.onCompleted();
}
@Override
public StreamObserver<String> sayHelloStream(StreamObserver<String> response) {
return new StreamObserver<String>() {
@Override
public void onNext(String data) {
//
// 接收客户端发送过来的数据,然后返回数据给客户端
response.onNext("result" + data);
}
@Override
public void onError(Throwable throwable) {}
@Override
public void onCompleted() {
System.out.println("completed");
}
};
}
}
接口服务消费模块
@RestController
@RequestMapping("user")
public class Usercontroller {
// 原生rpc远程调用
// @Autowired
// RestTemplate restTemplate;
@DubboReference( version="2.0")
private DoubleUserService userService;
@GetMapping("info")
public String get1(@RequestParam("name") String name){
String s = userService.getUser();
userService.sayHelloServerStream(name, new StreamObserver<String>() {
@Override
public void onNext(String data) {
System.out.println("流中添加的数据为data:"+data);
}
@Override
public void onError(Throwable throwable) {
throwable.getCause().printStackTrace();
System.out.println("流中数据出现错误");
}
@Override
public void onCompleted() {
System.out.println("流中数据已经全部发送完毕");
}
});
return "info:"+s;
}
@GetMapping("write")
public String get2() {
StreamObserver<String> stream = userService.sayHelloStream(new StreamObserver<String>() {
@Override
public void onNext(String data) {
System.out.println("流中添加的数据为data:" + data);
}
@Override
public void onError(Throwable throwable) {
throwable.getCause().printStackTrace();
System.out.println("流中数据出现错误");
}
@Override
public void onCompleted() {
System.out.println("流中数据已经全部发送完毕");
}
});
stream.onNext("写入的数据1");
stream.onNext("写入的数据2");
stream.onNext("写入的数据3");
stream.onCompleted();
return "操作成功";
}
}
写的方法以StreamObserver 包裹字符串的流式对象为参数,并且需要实现三个回调方法,当服务消费端传递name的时候,服务提供模块将传递的属性写入流,而此时服务消费模块的流中也能通过回调读取
测试访问这个mapping 让服务消费方调用这个dubbo服务
服务提供模块向流中写入数据,这里数据式通过mapping消费模块传递服务提供模块,服务提供模块写入数据的
此时就已经完整服务提供模块向流中写入数据,
服务消费模块给服务提供模块写入数据
既然要写入数据,那么需要拿到流式对象,所以接口模块定义的方法返回这个流对象
default StreamObserver<String> sayHelloStream(StreamObserver<String> response) {
return response;
}
接口实现 服务提模块
从读取的回调函数中,把服务消费方写入的数据在写入流中,这样服务消费方可以显示
@Override
public StreamObserver<String> sayHelloStream(StreamObserver<String> response) {
return new StreamObserver<String>() {
@Override
public void onNext(String data) {
//
// 接收客户端发送过来的数据,然后返回数据给客户端
response.onNext("result" + data);
}
@Override
public void onError(Throwable throwable) {}
@Override
public void onCompleted() {
System.out.println("completed");
}
};
}
}
服务消费方
@GetMapping("write")
public String get2() {
StreamObserver<String> stream = userService.sayHelloStream(new StreamObserver<String>() {
@Override
public void onNext(String data) {
System.out.println("流中添加的数据为data:" + data);
}
@Override
public void onError(Throwable throwable) {
throwable.getCause().printStackTrace();
System.out.println("流中数据出现错误");
}
@Override
public void onCompleted() {
System.out.println("流中数据已经全部发送完毕");
}
});
stream.onNext("写入的数据1");
stream.onNext("写入的数据2");
stream.onNext("写入的数据3");
stream.onCompleted();
return "操作成功";
}
拿到改接口的返回值,也就是这个流对象,在流中写入数据 因为nio流是双向的,所以服务提供方也可以拿到流中的数据,但是服务提供方没法消费这个数据,而是再次写入流,
//提供方写入流
public void onNext(String data) {
//
// 接收客户端发送过来的数据,然后返回数据给客户端
response.onNext("result" + data);
}
写入流后,然后服务消费方触发回调,进行消费
消费方 消费流中的数据
public void onNext(String data) {
System.out.println("流中添加的数据为data:" + data);
}
测试
服务消费端空输出,之前写入给服务提供方的数据