java dubbo 图片序列化传输

Dubbo图片序列化实践
本文介绍了一种解决Java BufferedImage不支持序列化的问题方案,通过转换为SerializableRenderedImage并进行序列化处理,使得图片可以在Dubbo服务间传输。文章详细记录了从图片读取、转换到序列化和反序列化的全过程。
公司最近用上了 dubbo + spring + redis  , 由于系统 原来用做图片合成,字体合成操作,在序列化问题上遇到不少麻烦.

图片合成 ,java 图片合成是用 bufferedImage来操作的 ,这个BufferedImage 是不支持序列化的 ,就不能在dubbo间传输了,问题很大。
经过研究 (需要导入jai_codec.jar  ,  jai_core.jar 这是 java jai java图像增强包)  例子为:转为序列化对象,和转回操作,如果需要转为bufferedimage ,需要同时缓存原 type 图片类型。这个类型转换后会丢失(如果设置错误,会变色)。

 File imageFile = new File("D:\\abcd.jpg");
 BufferedImage picImage = ImageIO.read(imageFile);
 int type = picImage.getType(); // type图片类型

 SerializableRenderedImage serializableRenderedImage = new SerializableRenderedImage(picImage,true);   //转为可序列化图片对象
  int width = serializableRenderedImage.getWidth();
  int height = serializableRenderedImage.getHeight();
  BufferedImage bi = new BufferedImage(width, height,type); 
  
  Graphics2D g = bi.createGraphics();
  g.drawRenderedImage(serializableRenderedImage, AffineTransform.getScaleInstance(1, 1)); //将对象转回 bufferedImage;1 为缩放比率 ,也就是1比1 不缩放,可按照要求修改
  g.dispose(); //关画笔,清缓存
-------------------------------------------------------------------------------------------------------------------------------------
 如果是需要直接输出图片文件,只需要
  ImageIO.write(serializableRenderedImage, "jpg", new File("D:\\test.jpg")); 
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 将bufferedImage 转为 SerializableRenderedImage ,这个SerializableRenderedImage 支持序列化,但是这个序列化对象是不能在dubbo 间传输的(传输后会变得不可用)。
 dubbo 的序列化方式与 java原生的不同。
 接下来需要
--------------------------------------------序列化---------------------------
 ByteArrayOutputStream baos = new ByteArrayOutputStream();
  ObjectOutputStream ops = new ObjectOutputStream(baos);
  ops.writeObject(serializableRenderedImage);
  byte[] byteArray = baos.toByteArray();  // 我们要的结果 二进制数组 (这个数组是可以缓存redis的,redis支持二进制缓存,也可以转为json字符串缓存)
  ---------------------------------------反序列化-----------------------------
  ByteArrayInputStream bais = new ByteArrayInputStream(byteArray);
  ObjectInputStream ois = new ObjectInputStream(bais);
  Object objImage = ois.readObject();
  SerializableRenderedImage sriOut = (SerializableRenderedImage)objImage;    //转回序列化对象
----------------------------------------------------------------------------------
 dubbo 不能直接传输 SerializableRenderedImage 我们用序列化方法序列好后,在dubbo间传输byte[] 数组对象。在需要操作时候,再转回bufferedImage

-------------------------------------------------------------
知识点:
java 序列化要求(可跨工程传输):
1.继承serailzable接口
2.包路径相同,类完全相同

























































<think>我们正在讨论的是在Dubbo中使用Protobuf进行序列化和数据传输的实现方式。根据引用内容,我们需要关注的关键点包括: 1. 必要的依赖 2. 配置方式 3. 服务定义和实现的步骤 根据之前的讨论,标准流程是先编写.proto文件,然后生成Java代码,再实现服务。同时,Dubbo需要配置使用protobuf序列化。 步骤分解: 1. 添加依赖:需要引入protobuf-java、protobuf-java-util(但注意在Dubbo的protobuf序列化依赖中可能已经包含,或者需要排除重复的版本)和dubbo-serialization-protobuf。 2. 编写.proto文件:定义服务和消息结构。 3. 使用protoc生成Java代码。 4. 实现服务接口(在服务端)。 5. 配置Dubbo使用protobuf序列化。 下面详细说明每一步: 步骤1:添加Maven依赖 在pom.xml中添加以下依赖: ```xml <dependencies> <!-- Dubbo Spring Boot Starter --> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>3.0.7</version> <!-- 请使用最新稳定版本 --> </dependency> <!-- Protobuf序列化支持 --> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-serialization-protobuf</artifactId> <version>3.0.7</version> </dependency> <!-- Protobuf核心库,注意:dubbo-serialization-protobuf可能已经包含,但版本可能不匹配,建议显式声明并统一版本 --> <dependency> <groupId>com.google.protobuf</groupId> <artifactId>protobuf-java</artifactId> <version>3.21.12</version> <!-- 使用与dubbo-serialization-protobuf兼容的版本 --> </dependency> </dependencies> ``` 注意:如果dubbo-serialization-protobuf内部依赖了protobuf-java,且版本不一致,可以通过排除依赖然后显式引入指定版本。 步骤2:编写.proto文件 例如,在src/main/proto目录下创建user_service.proto: ```protobuf syntax = "proto3"; option java_package = "com.example.service"; option java_outer_classname = "UserServiceProto"; service UserService { rpc GetUser (UserRequest) returns (UserResponse) {} } message UserRequest { int32 user_id = 1; } message UserResponse { string name = 1; int32 id = 2; string email = 3; } ``` 步骤3:生成Java代码 使用protobuf-maven-plugin插件(推荐): ```xml <build> <plugins> <plugin> <groupId>org.xolstice.maven.plugins</groupId> <artifactId>protobuf-maven-plugin</artifactId> <version>0.6.1</version> <configuration> <protocArtifact>com.google.protobuf:protoc:3.21.12:exe:${os.detected.classifier}</protocArtifact> <pluginId>grpc-java</pluginId> <outputDirectory>${project.build.directory}/generated-sources/protobuf</outputDirectory> <clearOutputDirectory>false</clearOutputDirectory> </configuration> <executions> <execution> <goals> <goal>compile</goal> <goal>compile-custom</goal> </goals> </execution> </executions> </plugin> </plugins> </build> ``` 然后运行`mvn clean compile`,生成的代码会在target/generated-sources/protobuf目录下。 步骤4:实现服务接口 在服务端,我们需要实现生成的UserService接口(实际是抽象类,因为protobuf生成的是抽象类,内部有具体的服务定义)。注意:Dubbo3中,我们通常使用Triple协议,它支持Protobuf原生方式。 例如: ```java package com.example.service.impl; import com.example.service.UserServiceProto; import org.apache.dubbo.config.annotation.DubboService; @DubboService public class UserServiceImpl extends UserServiceProto.UserServiceImplBase { @Override public void getUser(UserServiceProto.UserRequest request, io.grpc.stub.StreamObserver<UserServiceProto.UserResponse> responseObserver) { // 根据request.getUserId()获取用户信息 UserServiceProto.UserResponse response = UserServiceProto.UserResponse.newBuilder() .setId(request.getUserId()) .setName("John Doe") .setEmail("john.doe@example.com") .build(); responseObserver.onNext(response); responseObserver.onCompleted(); } } ``` 步骤5:配置Dubbo使用protobuf序列化 在application.properties中配置: ```properties dubbo.protocol.name=tri dubbo.protocol.serialization=protobuf ``` 注意:Dubbo3推荐使用Triple协议(基于HTTP/2),它原生支持Protobuf序列化。 另外,在消费端,我们需要引用这个服务接口(使用生成的接口): ```java @DubboReference private UserServiceProto.UserService userService; ``` 然后就可以像调用本地方法一样调用远程服务了。 注意事项: 1. 确保.proto文件在服务提供方和消费方保持一致,否则会出现序列化错误。 2. 如果使用ZooKeeper或Nacos作为注册中心,需要配置注册中心地址。 3. 在消费方,同样需要引入.proto文件并生成相同的Java类(或者通过共享jar包方式共享接口定义)。 以上就是在Dubbo中使用Protobuf进行序列化和数据传输的实现方式。 根据上述内容,生成相关问题:</think>### Dubbo 中使用 Protobuf 实现序列化和数据传输的实现方式 #### 1. **核心依赖配置** 在 `pom.xml` 中添加必需依赖: ```xml <dependencies> <!-- Dubbo 基础依赖 --> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>3.0.7</version> </dependency> <!-- Protobuf 序列化核心依赖 --> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-serialization-protobuf</artifactId> <version>3.0.7</version> </dependency> <!-- Protobuf Java 支持 --> <dependency> <groupId>com.google.protobuf</groupId> <artifactId>protobuf-java</artifactId> <version>3.21.12</version> </dependency> <!-- 排除冲突依赖 --> <exclusions> <exclusion> <groupId>com.google.protobuf</groupId> <artifactId>protobuf-java-util</artifactId> </exclusion> </exclusions> </dependencies> ``` > **关键点**: > - `dubbo-serialization-protobuf` 是 Dubbo 的 Protobuf 序列化实现[^1] > - 需排除冲突的 `protobuf-java-util` 避免版本问题[^1] #### 2. **定义 Protobuf 契约(.proto 文件)** 创建 `user_service.proto`: ```protobuf syntax = "proto3"; option java_package = "com.example.service"; option java_outer_classname = "UserServiceProto"; // 定义服务接口 service UserService { rpc GetUser (UserRequest) returns (UserResponse); } // 请求消息 message UserRequest { int32 user_id = 1; } // 响应消息 message UserResponse { string name = 1; string email = 2; int32 age = 3; } ``` #### 3. **生成 Java 代码** 使用 `protoc` 编译器生成代码: ```bash protoc --java_out=./src/main/java user_service.proto ``` 生成文件路径: ``` src/main/java/com/example/service/UserServiceProto.java ``` #### 4. **服务端实现** ```java import com.example.service.UserServiceProto; import org.apache.dubbo.config.annotation.DubboService; @DubboService(serialization = "protobuf") // 指定序列化协议 public class UserServiceImpl extends UserServiceProto.UserServiceImplBase { @Override public void getUser(UserServiceProto.UserRequest request, StreamObserver<UserServiceProto.UserResponse> responseObserver) { // 业务逻辑 UserServiceProto.UserResponse response = UserServiceProto.UserResponse.newBuilder() .setName("张三") .setEmail("zhangsan@example.com") .setAge(30) .build(); responseObserver.onNext(response); responseObserver.onCompleted(); } } ``` #### 5. **客户端调用** ```java import com.example.service.UserServiceProto; import org.apache.dubbo.config.annotation.DubboReference; public class UserConsumer { @DubboReference(serialization = "protobuf") private UserServiceProto.UserService userService; public void getUser() { UserServiceProto.UserRequest request = UserServiceProto.UserRequest.newBuilder() .setUserId(1001) .build(); UserServiceProto.UserResponse response = userService.getUser(request); System.out.println("响应数据: " + response.getName()); } } ``` #### 6. **Dubbo 配置** 在 `application.yml` 中全局配置: ```yaml dubbo: protocol: name: dubbo serialization: protobuf # 全局序列化协议 registry: address: nacos://127.0.0.1:8848 # 注册中心地址 ``` #### 7. **工作流程** ```mermaid graph TD A[定义.proto文件] --> B[生成Java代码] B --> C[服务端实现接口] C --> D[注册到注册中心] D --> E[客户端引用服务] E --> F[Protobuf序列化请求] F --> G[网络传输] G --> H[服务端反序列化] H --> I[业务处理] I --> J[Protobuf序列化响应] J --> K[客户端反序列化] ``` #### 关键特性说明 1. **跨语言支持** `.proto` 文件作为统一契约,支持多语言客户端调用[^2] 2. **高性能序列化** Protobuf 二进制编码比 JSON 体积小 3-5 倍,解析速度快 2-10 倍[^3] 3. **协议兼容性** 需确保服务端和客户端使用 **相同版本的 .proto 文件**,字段变更遵循向后兼容规则 4. **Dubbo 3 优化** 使用 Triple 协议(基于 HTTP/2)时,自动支持 Protobuf 流式传输[^2] > **注意事项**: > - 字段编号在 `.proto` 中定义后不可随意修改 > - 删除字段应标记 `reserved` 防止未来误用 > - 使用 `optional` 字段保证向前兼容性
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值