通过RabbitBinder在Source / Processor / Sink之间发送HashMaps的支持似乎在新的SpringBoot(2.0.2)中发生了变化。
所有模块的公共父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>demo.stream</groupId>
<artifactId>demo-stream-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<modules>
<module>source-module</module>
<module>processor-module</module>
<module>sink-module</module>
</modules>
<packaging>pom</packaging>
<name>demo-stream</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.2.RELEASE</version>
<relativePath />
<!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.RC2</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-reactive</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
明确说明如下:
如果在出站通道上没有设置content-type属性,Spring Cloud Stream将使用基于的序列化程序序列化有效负载 Kryo序列化框架。反序列化消息 destination需要有效负载类存在于 接收者的类路径。
根据规则,它应该使用标准的java类型,例如&#34; HashMap&#34;无需自定义消息转换器。
无法让这个简单的代码在源代码和处理器之间正常工作:
来源配置和代码:
spring:
application:
name: test
cloud:
config:
uri: http://blade1:8888
name: scdf-tester
stream:
bindings:
output:
#content-type: 'application/x-java-serialized-object'
#content-type: 'application/json'
content-type:
destination: demo-stream-source-output
@EnableBinding(Source.class)
@EnableAutoConfiguration
@EnableScheduling
@Component
public class DemoSource {
@Autowired
private Source channels;
//@InboundChannelAdapter(Source.OUTPUT)
@Scheduled(fixedRate = 2000)
public MessageSource<Map<String, Object>> timerMessageSource() {
Map<String, Object> mapa = new HashMap<>();
mapa.put("string", "string");
mapa.put("string", "string");
mapa.put("long", 1212121L);
mapa.put("integer", 1212121);
Map<String, Object> mapaInner = new HashMap<>();
mapaInner.put("string", "string");
mapaInner.put("string", "string");
mapaInner.put("long", 1212121L);
mapaInner.put("integer", 1212121);
mapa.put("innerMapa", mapaInner);
channels.output().send(MessageBuilder.withPayload(mapa).build());
}
}
从调试器中可以清楚地看到有效负载转换为JSON字符串(ApplicationJsonMessageMarshallingConverter),内容类型标题设置为&#34; application / json&#34;。
这不是预期的,尽管在某些情况下它是可接受的和合法的。期望将kryo序列化hashmap作为字节数组。
调试器输出如下:
this = {AbstractMessageChannel$ChannelInterceptorList@7877}
logger = {LogFactory$Log4jLog@7884}
interceptors = {CopyOnWriteArrayList@8890} size = 1
0 = {MessageConverterConfigurer$OutboundContentTypeConvertingInterceptor@8991}
messageConverter = {CompositeMessageConverter@9043} "CompositeMessageConverter[converters=[org.springframework.cloud.stream.converter.ApplicationJsonMessageMarshallingConverter@2863f519, org.springframework.cloud.stream.converter.TupleJsonMessageConverter@e9dffa1, org.springframework.messaging.converter.ByteArrayMessageConverter@cc64ad, org.springframework.cloud.stream.converter.ObjectStringMessageConverter@97df9ef, org.springframework.cloud.stream.converter.JavaSerializationMessageConverter@3e011ee4, org.springframework.cloud.stream.converter.KryoMessageConverter@5f808e72, org.springframework.cloud.stream.converter.JsonUnmarshallingConverter@3c0a7023]]"
this$0 = {MessageConverterConfigurer@9044}
mimeType = null
MessageConverterConfigurer$AbstractContentTypeInterceptor.this$0 = {MessageConverterConfigurer@9044}
size = 1
message = {GenericMessage@9169} "GenericMessage [payload=byte[117], headers={contentType=application/json;charset=UTF-8, id=e1b9b79e-5501-0ebe-178f-59ef5538192c, timestamp=1528647360089}]"
payload = {byte[117]@9373} {"string":"string","integer":1212121,"long":1212121,"innerMapa":{"string":"string","integer":1212121,"long":1212121}}
headers = {MessageHeaders@9374} size = 3
0 = {Collections$UnmodifiableMap$UnmodifiableEntrySet$UnmodifiableEntry@12201} "contentType" -> "application/json;charset=UTF-8"
1 = {Collections$UnmodifiableMap$UnmodifiableEntrySet$UnmodifiableEntry@12202} "id" -> "e1b9b79e-5501-0ebe-178f-59ef5538192c"
2 = {Collections$UnmodifiableMap$UnmodifiableEntrySet$UnmodifiableEntry@12203} "timestamp" -> "1528647360089"
channel = {DirectChannel@6940} "output"
interceptorStack = {ArrayDeque@8833} size = 1
处理器配置和代码:
spring:
application:
name: test
cloud:
config:
uri: http://blade1:8888
name: scdf-tester
stream:
bindings:
output:
#content-type: 'application/x-java-object'
destination: demo-stream-processor-output
input:
content-type: 'application/json;type=java.util.Map'
destination: demo-stream-source-output
处理器案例1:在&#34; StreamListener&#34;当Map用作param&#34;尽力而为#34; logic提供没有有效负载的标头
recv消息:class org.springframework.messaging.MessageHeaders
@StreamListener(Processor.INPUT)
@SendTo(Processor.OUTPUT)
public Map handle(final Map message) {
LOG.info("recv message: {} {}", message.getClass(),message);
//do some transformations...
message.put("transformer_says","hello simon..");
return message;
}
日志输出如下:
2018-06-10 18:55:51 [demo-stream-source-output.anonymous.0EthCzZpTneSPp48retFQg-1] INFO demostream.modules.DemoProcessor - recv message: class org.springframework.messaging.MessageHeaders {amqp_receivedDeliveryMode=PERSISTENT, amqp_receivedRoutingKey=demo-stream-source-output, amqp_receivedExchange=demo-stream-source-output, amqp_deliveryTag=1, deliveryAttempt=1, amqp_consumerQueue=demo-stream-source-output.anonymous.0EthCzZpTneSPp48retFQg, amqp_redelivered=false, id=671bc2d8-aaba-5ddb-b02b-f92a9dba3e0f, amqp_consumerTag=amq.ctag-ZnNk0O3vNs0yhOPKMTM3Fg, contentType=application/json;charset=UTF-8, timestamp=1528649751852}
当然这里有例外:
引起:java.lang.UnsupportedOperationException:MessageHeaders是不可变的 在org.springframework.messaging.MessageHeaders.put(MessageHeaders.java:249)
处理器案例2:在&#34; StreamListener&#34;当Message被用作param&#34;尽力而为#34; logic给出了map的JSON表示而不是Original HashMap
@StreamListener(Processor.INPUT)
@SendTo(Processor.OUTPUT)
public Object handle(Message<?> message) {
LOG.info("recv message: {}",message);
String jsonMap = new String((byte[]) message.getPayload());
LOG.info("jsonMap : {}", jsonMap);
return jsonMap;
}
日志输出如下:
2018-06-10 18:52:43 [demo-stream-source-output.anonymous.lt21i1ZXQvKZkOdB6hQwUQ-1] INFO demostream.modules.DemoProcessor - recv message: GenericMessage [payload=byte[117], headers={amqp_receivedDeliveryMode=PERSISTENT, amqp_receivedRoutingKey=demo-stream-source-output, amqp_receivedExchange=demo-stream-source-output, amqp_deliveryTag=38, deliveryAttempt=1, amqp_consumerQueue=demo-stream-source-output.anonymous.lt21i1ZXQvKZkOdB6hQwUQ, amqp_redelivered=false, id=271ad47e-7b36-c01c-0c19-b3201297ddd7, amqp_consumerTag=amq.ctag-QIVzqanCxiNF-HXUTUVn7Q, contentType=application/json;charset=UTF-8, timestamp=1528649563755}]
2018-06-10 18:52:43 [demo-stream-source-output.anonymous.lt21i1ZXQvKZkOdB6hQwUQ-1] INFO demostream.modules.DemoProcessor - jsonMap : {"string":"string","integer":1212121,"long":1212121,"innerMapa":{"string":"string","integer":1212121,"long":1212121}}
以前的版本(1.5.9)和相关的流云依赖项没有任何问题。
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>1.5.9.RELEASE</version>
<relativePath></relativePath>
</parent>
请你就这种行为提出一些建议。
假设我做错了,简单的问题就是:
如何使用Kryo ser / der发送HashMap有效负载,这在我看来比JSON序列化更好(进程间开销更少)
谢谢!
祝你好运 伊万
答案 0 :(得分:0)
在源/处理器之间进行kryo二进制序列化/反序列化的解决方案是使用以下配置:
该指令:
output.content-type: application/x-java-object
触发kryo二进制序列化(否则JSON是默认值)。
来源(conf和代码)
spring:
cloud:
stream:
bindings:
output:
destination: source-output
content-type: application/x-java-object
@EnableBinding(Source.class)
public class SourceTester {
@InboundChannelAdapter(value = Source.OUTPUT, poller = @Poller(fixedDelay = "5000"))
public Message<HashMap<String,Object>> source() {
HashMap<String,Object> mapa = new HashMap<>();
mapa.put("foo","bar");
mapa.put("bar",1337);
return MessageBuilder.withPayload(mapa).build();
}
}
或另一种方法:
@EnableBinding(Source.class)
@EnableAutoConfiguration
@EnableScheduling
@Component
public class DemoSource {
@Autowired
private Source channels;
@Scheduled(fixedRate = 5000)
public MessageSource<Map<String, Object>> timerMessageSource() {
HashMap<String,Object> mapa = new HashMap<>();
mapa.put("foo","bar");
mapa.put("bar",1337);
channels.output().send(MessageBuilder.withPayload(mapa).build());
}
}
处理器(配置和代码)
spring:
cloud:
stream:
bindings:
input:
destination: source-output
output:
destination: processor-output
content-type: application/x-java-object
@EnableBinding(Processor.class)
@EnableAutoConfiguration
public class ProcessorTester {
@Transformer(inputChannel = Processor.INPUT, outputChannel = Processor.OUTPUT)
protected Message<HashMap> process(Message<HashMap> input){
input.getPayload().put("Processor", "was here");
return input;
}
}
<强>结论:强>
需要带有“类型参数HashMap”和Message的处理器处理程序(转换器)来自动触发byte []到HashMap的kryo反序列化:
protected Message<HashMap> process(Message<HashMap> input)
如果禁用输出内容类型指令(源模块或任何其他生产者)
spring:
cloud:
stream:
bindings:
output:
destination: source-output
###content-type: application/x-java-object
默认序列化是HashMap到JSON字符串。
致以最诚挚的问候,
伊万