文章目录
- Dubbo
- dubbo架构
- spring直连方式consumer调用provider
- springboot直连方式consumer调用provider
- 通过nacos注册中心调用dubbo发布的接口
- dubbo-admin
- 其它
- dubbo不能传输文件对象和inputstream
- 解决dubbo序列化前置问题,在消费端配置
- 参数的传递
- org.apache.dubbo.common.serialize.SerializationException: java.lang.UnsupportedOperationException: CollectionDeserializer[interface java.util.List], dubbo version: 3.3.0-beta.4, current host: 192.168.1.161, error code: 4-20. This may be caused by , go to https://dubbo.apache.org/faq/4/20 to find instructions. java.io.IOException: org.apache.dubbo.common.serialize.SerializationException: java.lang.UnsupportedOperationException: CollectionDeserializer[interface java.util.List]
- version
- 应用级和接口级服务发现
- Triple 协议
Dubbo
dubbo是阿里巴巴的开源的高性能的java RPC框架,现在是Apache基金会的管理与支持之下,可以实现如服务发现、负载均衡、流量调度等服务治理诉求。dubbo的微服务解决方案DNS(dubbo、nacos、sentinel)
官网地址:https://cn.dubbo.apache.org/zh-cn/
dubbo架构
- provider 功能提供者
- consumer 功能调用者、消费者
- commons-api 通用内容,是consumer和provider中,公共使用的实体类、service的接口,通过maven依赖引用的方式,引入到需要的项目中
- registry 注册中心,可用可不用,但是如果有多个provider,做了集群,其中哪个挂了,consumer调用的话就会出问题,而注册中心可以做健康检查,通过负载均衡的轮询或者其它机制,调用健康的provder,保证服务的正常调用。
spring直连方式consumer调用provider
这里开始采用spring的方式,手动通过xml文件,发布接口
dubbo父工程
目录结构:
- 父工程引入的依赖:
<?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.wzw</groupId>
<artifactId>dubbo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>dubbo-api</module>
<module>dubbo-provider</module>
<module>dubbo-consumer</module>
</modules>
<properties>
<maven.compiler.source>22</maven.compiler.source>
<maven.compiler.target>22</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!-- 父工程依赖管理 -->
<dependencyManagement>
<dependencies>
<!-- 日志 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.36</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
</dependency>
<!-- dubbo -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>3.2.0-beta.4</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-x-discovery</artifactId>
<version>4.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
dubbo-api
通用内容,是consumer和provider中,公共使用的实体类、service的接口,通过maven依赖引用的方式,引入到需要的项目中,不用启动,main方法可以删
- 目录结构
- User
package com.wzw.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.io.Serializable;
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class User implements Serializable {
private String name;
private String password;
}
- UserService
package com.wzw.service;
public interface UserService {
boolean login(String name,String password);
}
dubbo-provider
- 引入依赖
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>3.2.0-beta.4</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-x-discovery</artifactId>
<version>4.3.0</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.8.0</version>
<exclusions>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-handler</artifactId>
</exclusion>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-epoll</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
- 目录结构
- UserServiceImpl
package com.wzw.service;
public class UserServiceImpl implements UserService {
@Override
public boolean login(String name, String password) {
//输出当前类名全路径,调用的时候好观察
System.out.println("com.wzw.service.UserServiceImpl:"+name+"-"+password);
return false;
}
}
- DubboProviderMain
package com.wzw;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.concurrent.CountDownLatch;
public class DubboProviderMain {
private static final Logger logger = LoggerFactory.getLogger(DubboProviderMain.class);
public static void main(String[] args) throws InterruptedException {
//加载xml的配置文件,点击applicationContext-provider.xml,如果能跳转,路径就没问题
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-provider.xml");
//启动
context.start();
logger.info("1111");
//因为是main,运行完就结束了,所以要阻塞,让它一直提供服务,如果是springboot,它是一直运行的,所以springboot不用阻塞
new CountDownLatch(1).await();
}
}
- applicationContext-provider.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.xsd
http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<dubbo:application name="dubbo-provider"/>
<!-- 使用dubbo协议 端口20880-->
<dubbo:protocol name="dubbo" port="20880"/>
<!-- 创建接口对象 -->
<bean id="userService" class="com.wzw.service.UserServiceImpl"/>
<!-- 发布接口,要发布的是上面创建的userService,以提供consumer来调用-->
<dubbo:service interface="com.wzw.service.UserService" ref="userService"/>
</beans>
- 启动服务,观察控制台,发布RPC服务接口成功
springboot直连方式consumer调用provider
直接在dubbo工程下新建两个springboot项目,dubbo-boot-provider和dubbo-boot-consumer
- 目录结构
dubbo-boot-provider
-
目录结构
-
引入依赖
<?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.7.12</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.wzw</groupId>
<artifactId>dubbo-boot-provider</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>dubbo-boot-provider</name>
<description>dubbo-boot-provider</description>
<dependencies>
<!--dubbo-api-->
<dependency>
<groupId>com.wzw</groupId>
<artifactId>dubbo-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--dubbo-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>3.3.0-beta.2</version>
</dependency>
<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>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- 配置文件
spring:
application:
name: dubbo-boot-provider
dubbo: # dubbo 是根节点,表示Dubbo的相关配置
protocol: # protocol 是子节点,表示协议配置;
name: dubbo # name 是protocol的属性,表示协议的名称
port: -1 # port 是protocol的属性,表示协议端口
- UserServiceImpl
package com.wzw.dubbobootprovider.service;
import com.wzw.service.UserService;
import org.apache.dubbo.config.annotation.DubboService;
@DubboService
public class UserServiceImpl implements UserService {
@Override
public boolean login(String name, String password) {
System.out.println("dubbo-boot-provider UserServiceImpl:"+name+" "+password);
return false;
}
}
- DubboBootProviderApplication
启动类只加了@EnableDubbo表示开启dubbo
package com.wzw.dubbobootprovider;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@EnableDubbo
@SpringBootApplication
public class DubboBootProviderApplication {
public static void main(String[] args) {
SpringApplication.run(DubboBootProviderApplication.class, args);
}
}
- 小结
- 配置类
添加dubbo配置信息dubbo: # dubbo 是根节点,表示Dubbo的相关配置 protocol: # protocol 是子节点,表示协议配置; name: dubbo # ;name 是protocol的属性,表示协议的名称 port: -1 # port 是protocol的属性,表示协议端口
- 要发布的接口,类名上使用
@DubboService
- 启动类添加
@EnableDubbo
- 配置类
dubbo扫描类的方式
- 启动类上面使用
@EnableDubbo
,会自动扫描包及其子包下的@DubboService
修饰的类。 - 使用
@DubboComponentScan
,如果要发布的接口不在@EnableDubbo
的包及其子包下,可以在启动类上面使用@DubboComponentScan(basePackages={"com.wzw.service"})
来显示指定扫描的类。 - 配置文件中通过使用
dubbo.scan.base-packages=com.wzw.dubbobootprovider.service
来指定要扫描的类,等同于@EnableDubbo
dubbo-boot-consumer
- 引入依赖
<?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.7.12</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.wzw</groupId>
<artifactId>dubbo-boot-consumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>dubbo-boot-consumer</name>
<description>dubbo-boot-consumer</description>
<dependencies>
<!--dubbo-api-->
<dependency>
<groupId>com.wzw</groupId>
<artifactId>dubbo-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--dubbo-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>3.3.0-beta.2</version>
</dependency>
<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>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- 配置文件
spring:
application:
name: dubbo-boot-consumer
dubbo: # Dubbo 协议的根节点
application: # 应用名
qos-enable: false # 是否开启QOS服务
- 测试类
消费者就不用启动服务了,直接测试类调用
package com.wzw.dubbobootconsumer;
import com.wzw.service.UserService;
import org.apache.dubbo.config.annotation.DubboReference;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class DubboBootConsumerApplicationTests {
//这个地址是服务提供者启动的时候,接口发布地址,服务端控制台启动信息中有
@DubboReference(url = "dubbo://192.168.8.1:20880/com.wzw.service.UserService")
private UserService userService;
@Test
void test1(){
boolean login = userService.login("zhangsan", "pwd123");
System.out.println(login);
}
}
- 小结
-
配置类
dubbo: # Dubbo 协议的根节点 application: # 应用名 qos-enable: false # 是否开启QOS服务
-
调用接口的地方需要使用
@DubboReference
,并指定服务端提供的接口地址@DubboReference(url = "dubbo://192.168.8.1:20880/com.wzw.service.UserService")
-
测试
- 启动provider
查看控制台,这里有发布的dubbo的接口,这个接口地址,消费者调用的时候配置在@DubboReference
@DubboReference(url = "dubbo://192.168.8.1:20880/com.wzw.service.UserService")
执行consumer的测试方法,查看结果
- 这是provider接口的输出,成功调取
- consumer的调用方法输出,也成功获取到返回结果
通过nacos注册中心调用dubbo发布的接口
- 目录结构
- 引入依赖,consumer比provider多一个web依赖,其它的依赖都是一样的,通过consumer的controller来测试的,如果还是测试类测试的话,不引入web也行
<!--公共的service模块依赖-->
<dependency>
<groupId>com.wzw</groupId>
<artifactId>dubbo-api</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<!-- web应用依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- nacos依赖 -->
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>2.3.2</version>
</dependency>
<!--dubbo-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>3.3.0-beta.2</version>
</dependency>
<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>
</dependency>
- 配置文件,consumer和provider的配置文件也是一样的,也可以配置其它参数,但是这里只是简单测试,只需要配置nacos的地址就行
- provider的代码,service类用的是@DubboService,然后启动类记得加@EnableDubbo
- consumer代码,service类用的是@DubboReference
- 启动调用接口
调用返回成功,查看控制台输出也是正常
dubbo-admin
- dubbo-admin管理平台,是图形化的服务管理页面,从注册中心获取所有提供者和消费者进行配置管理。没有正式发行。
官方介绍
- dubbo-adminq其实就是一个项目,前端是vue,后端是springboot,部署运行就可以
其它
dubbo不能传输文件对象和inputstream
Dubbo 中传递 File 对象和 InputStream 有一些特定的注意事项,因为这些对象通常是与文件系统或网络流相关的资源,不适合直接在网络中传输。
解决:使用byte[]传输
提供服务方:alioss为例
/**
* 上传文件到OSS
* @param objectName 存储名称
* @param bytes 字节对象
* @return returnType=url 返回url文件地址 returnType=fileName 返回文件路径
* @throws Exception
*/
public String uploadFile(String objectName, byte[] bytes, UploadSetupBody uploadSetupBody) {
OSS ossClient=new OSSClientBuilder().build(uploadSetupBody.getEndpoint(), uploadSetupBody.getAccessKeyId(), uploadSetupBody.getAccessKeySecret());
//访问文件的临时路径
String url="";
try(InputStream inputStream=new java.io.ByteArrayInputStream(bytes)) {
// 创建PutObjectRequest对象。
PutObjectRequest putObjectRequest = new PutObjectRequest(uploadSetupBody.getBucketName(), objectName, inputStream);
// 上传字符串。
PutObjectResult result = ossClient.putObject(putObjectRequest);
url="https://"+uploadSetupBody.getBucketName()+"."+uploadSetupBody.getEndpoint().substring(uploadSetupBody.getEndpoint().lastIndexOf("/")+1)+"/"+objectName;
} catch (Exception e) {
e.printStackTrace();
}
return objectName;
}
服务调用方:
/**
* 通用上传请求(单个)
* return 文件访问url
*/
@PostMapping("/upload")
public AjaxResult uploadFile(MultipartFile file) throws Exception {
try {
//云存储服务配置,AccessKeyID、Bucket、Endpoint等
UploadSetupBody uploadSetupBody = storageServerService.selectWycStorageServerConfig("1");
if(uploadSetupBody==null){
return AjaxResult.error("未获取到存储服务");
}
//构建文件全路径
String objectName = uploadSetupBody.getPreFix()+"/" + DateUtils.datePath() + "/" + storageServerService.createFileName(file.getOriginalFilename());
//返回全路径
String filePath = storageServerService.uploadFile(objectName, file.getBytes(),uploadSetupBody);
//根据路径获取url
String url = storageServerService.getOssUrl(filePath,uploadSetupBody);
Map<String, String> map = new HashMap<>();
map.put("url", url);
// map.put("filePath", objectName);
AjaxResult ajax = AjaxResult.success(map);
return ajax;
} catch (Exception e) {
return AjaxResult.error(e.getMessage());
}
}
解决dubbo序列化前置问题,在消费端配置
dubbo:
consumer:
filter: -authenticationPrepare,-contextHolderParametersSelectedTransfer
参数的传递
- consumer存参数
//存字符串
RpcContext.getClientAttachment().setAttachment("name", "张三")
//存对象
RpcContext.getClientAttachment().setObjectAttachment("loginUser", SecurityUtils.getLoginUser())
- provider取参数
//取字符串
RpcContext.getServerContext().getAttachment("name");
//取对象
RpcContext.getServerContext().getObjectAttachment("loginUser");
org.apache.dubbo.common.serialize.SerializationException: java.lang.UnsupportedOperationException: CollectionDeserializer[interface java.util.List], dubbo version: 3.3.0-beta.4, current host: 192.168.1.161, error code: 4-20. This may be caused by , go to https://dubbo.apache.org/faq/4/20 to find instructions. java.io.IOException: org.apache.dubbo.common.serialize.SerializationException: java.lang.UnsupportedOperationException: CollectionDeserializer[interface java.util.List]
错误信息:
看着像是List的问题,但是跟List没关系,是因为在实体属性加了校验的注释
,但是controller的实体入参的地方,没有加@Validated
,加上@Validated
就好了
version
调用对用的版本
@DubboService(version = "1.0.0")
public class UserServiceImpl implements UserService {
....
}
@DubboService(version = "1.0.1")
public class UserServiceImpl2 implements UserService {
.....
}
//调用的就是对应版本1.0.1,UserServiceImpl2
@DubboReference(version = "1.0.1")
private UserService userService;
应用级和接口级服务发现
不配置的情况下,dubbo3.0默认是all(接口级+应用级均注册)
- 仅应用级注册
instance
# 仅应用级注册
dubbo:
registry:
register-mode: instance
- 仅接口级注册
dubbo: # dubbo 是根节点,表示Dubbo的相关配置
registry:
address: nacos://192.168.8.1:8848 # dubbo注册到nacos的地址
register-mode: interface
Triple 协议
dubbo支持的协议
Triple
基于 HTTP/2 并且完全兼容 gRPC 协议,可以直接使用 curl、浏览器访问后端 Dubbo 服务
- 设置协议,服务提供者提供triple协议的访问
dubbo:
protocol:
name: tri #triple协议
port: 5000 #端口
调用方式
一般的这种请求,就是Unary 请求
- 提供服务
@DubboService
public class UserServiceImpl implements UserService {
@Override
public boolean login(String name, String password) {
System.out.println("dubbo-boot-nacos-provider UserServiceImpl:"+name+" "+password);
return false;
}
}
- 调用
@RestController
public class ConsumerController {
@DubboReference
private UserService userService;
@GetMapping("/testDubbo")
public boolean testDubbo()
{
return userService.login("wzw","123456");
}
}
Streaming 通信模式
- 接口需要发送大量数据,这些数据无法被放在一个 RPC 的请求或响应中,需要分批发送,但应用层如果按照传统的多次 RPC 方式无法解决顺序和性能的问题,如果需要保证有序,则只能串行发送
- 流式场景,数据需要按照发送顺序处理, 数据本身是没有确定边界的
- 推送类场景,多个消息在同一个调用的上下文中被发送和处理
- StreamObserver需要用到依赖
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-common</artifactId>
<version>3.3.0-beta.2</version>
</dependency>
SERVER_STREAM(服务端流)
- 接口增加方法
public interface UserService {
//UNARY
boolean login(String name,String password);
//SERVER_STREAM
default void sayHelloServerStream(String name, StreamObserver<String> response){
}
//CLIENT_STREAM
default StreamObserver<String> sayHelloClientStream(StreamObserver<String> response){
return response;
}
}
- provider
@DubboService
public class UserServiceImpl implements UserService {
@Override
public void sayHelloServerStream(String name, StreamObserver<String> response) {
response.onNext("hello "+name);
response.onNext("hello "+name);
response.onNext("hello "+name);
response.onCompleted();
}
}
- consumer
@RestController
public class ConsumerController {
@DubboReference
private UserService userService;
@GetMapping("/sayHello")
public String sayHello(String name){
userService.sayHelloServerStream(name,new StreamObserver<String>() {
@Override
public void onNext(String data) {
System.out.println("onNext: " + data);
}
@Override
public void onError(Throwable throwable) {
}
@Override
public void onCompleted() {
}
});
return "success";
}
}
- 测试