了解Spring Cloud Stream内容类型

时间:2017-08-24 16:46:22

标签: spring-cloud-stream spring-cloud-dataflow

我写了一个类似于this one的自定义处理器。特别是,处理器采用InputDto并返回json。在Q& A的指导下,我的自定义处理器有一个带有以下内容的application.properties文件:

spring.cloud.stream.bindings.input.content-type=application/x-java-object;com.company.InputDto
spring.cloud.stream.bindings.output.content-type=application/json

this Q&A我用这一行创建了一个spring.integration.properties文件:

spring.integration.readOnly.headers=contentType

我正在进行自动化集成测试。到目前为止一切都很好。

我在SCDF shell中创建了一个包含处理器的流。 timehttpclient效果很好,因此我没有为这些显示详细的参数。在这里添加了换行符&在整个可读性方面。

stream create --name test --definition "time <args> 
  | httpclient <args> 
  | splitter --expression=\"#jsonPath(payload,'$..[0:]')\" 
  | myprocessor 
  | log"

我启用了调试日志记录。 httpclient生成包含contentType=text/plain和有效负载的消息:

payload=[{"name":"first","url":"url1"},{"name":"second","url":"url2"}]

splitter按日志创建两条消息(按预期方式):

message: GenericMessage [payload={name=first, url=url1}, 
  headers={...contentType=text/plain, ... }]

message: GenericMessage [payload={name=second, url=url2}, 
  headers={...contentType=text/plain, ... }]

我编写的自定义处理器因此异常而失败:

org.springframework.messaging.converter.MessageConversionException: Cannot 
convert from [java.util.LinkedHashMap] to [com.company.InputDto] for 
GenericMessage [payload={name=first, url=url1}, headers={sequenceNumber=1, 
   kafka_offset=xx, sequenceSize=x, correlationId=yy, id=zzz, 
   kafka_receivedPartitionId=0, contentType=text/plain, 
   kafka_receivedTopic=test.splitter, timestamp=1503591622851}]
    at 
 org.springframework.messaging.handler.annotation.support.PayloadArgumentResolver.resolveArgument(PayloadArgumentResolver.java:142) ~[spring-messaging-4.3.10.RELEASE.jar!/:4.3.10.RELEASE] 
....

我不确定LinkedHashMap的来源。我尝试将application.properties文件更改为:

spring.cloud.stream.bindings.input.content-type=application/json;com.company.InputDto

但没有帮助。我也尝试过添加

--spring.cloud.stream.bindings.output.contentType='application/json'

在创建流时splitter(遵循sample app中的路线),但仍然会获得例外。

我花了好几个小时,却看不到我错过的东西。感谢任何帮助。

我的自定义处理器使用Spring Cloud Dalston.SR3。我使用的是SCDF Server和shell 1.3.0.M1。使用Kafka活页夹。

更新,更多信息。我查看了Splitter启动器中的代码,并编写了一个小测试用例来模拟它正在做什么。

final ExpressionEvaluatingSplitter splitter = new ExpressionEvaluatingSplitter(
    new SpelExpressionParser().parseExpression(
        "T(com.jayway.jsonpath.JsonPath).read(payload, '$..[0:]')"));
final PollableChannel channel = new QueueChannel();
splitter.setOutputChannel(channel);
splitter.handleMessage(new GenericMessage<Object>(
  "[{\"name\":\"first\",\"url\":\"url1\"},
    {\"name\":\"second\",\"url\":\"url2\"}]"));
final Message<?> message = channel.receive(10);
System.out.println("payload class: " + message.getPayload().getClass());
System.out.println("payload: " + message.getPayload());
System.out.println(channel.receive(10));

这会产生输出:

payload class: class java.util.LinkedHashMap
payload: {name=first, url=url1}
GenericMessage [payload={name=second, url=url2}, headers={sequenceNumber=2,
  correlationId=xx, id=yy, sequenceSize=2, timestamp=1503609649518}]

啊哈,LinkedHashMap!现在我只需要说服拆分器将输出发送为纯文本或json,而不是Map。

更新2.我已经能够在不使用任何自定义处理器的情况下复制此问题。

stream create --name test --definition "time <args>
  | httpclient <args>
  | splitter --expression=\"#jsonPath(payload,'$.[0:]')\" 
         --outputType=\"application/json\"
  | transform --expression=\"new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsString(payload) != null\" 
         --outputType=\"application/json\"
  | log"

运行时,拆分器日志文件包含此异常(删节),臭名昭着的&#34;有效负载不得为空&#34;错误:

2017-08-25 13:09:30,322 DEBUG -kafka-listener-2 o.s.i.c.DirectChannel:411 - preSend on channel 'output', message: GenericMessage [payload={name=first, url=url1}, headers={sequenceNumber=1, kafka_offset=xx sequenceSize=2, correlationId=yy, id=zz, kafka_receivedPartitionId=0, contentType=text/plain, kafka_receivedTopic=test.httpclient, timestamp=1503680970322}]
2017-08-25 13:09:30,328 ERROR -kafka-listener-2 o.s.k.l.LoggingErrorHandler:37 - Error while processing: ConsumerRecord(topic = test.httpclient, partition = 0, offset = 52606, CreateTime = 1503680967030, checksum = 2166925613, serialized key size = -1, serialized value size = 470, key = null, value = [B@6567451e)
org.springframework.messaging.MessageDeliveryException: failed to send Message to channel 'output'; nested exception is java.lang.IllegalArgumentException: payload must not be null
    at     org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:449) ~[spring-integration-core-.3.8.RELEASE.jar!/:4.3.8.RELEASE]

看起来分割器很难将LinkedHashMap转换为JSON。有什么方法可以进行转换吗?

我还尝试将httpclient处理器的outputType设置为显式application/json,但它似乎没有什么区别。 (在docs之后。示例显示带有shell引号的outputType值,我也尝试过没有,没有区别。)

使用命令将应用程序加载到SCDF服务器中(替换为&#39;。&#39; in bit-ly所以SO会接受链接)

app import --uri http://bit-ly/Bacon-RELEASE-stream-applications-kafka-10-maven

注意到一些事情。调试日志记录始终显示内容类型为text/plain,因此它似乎没有获取我明确设置的内容类型。此外,如果我删除transform处理器,错误就会消失。我在日志中看到了数据,但没有看到JSON格式,只是这样:

{name=first, url=url1}

2 个答案:

答案 0 :(得分:0)

如果您愿意,可以尝试设置拆分器的输出内容类型,并删除输入处理器的任何contentType定义。 JSON-&gt; POJO应该来自邮件头。

答案 1 :(得分:0)

我能够通过显式设置inputType和outputType来实现纯Spring流和包含我自定义处理器的流的工作。

stream create --name test --definition "time 
  | httpclient <args> --outputType=\"application/json\" 
  | splitter --expression=\"#jsonPath(payload,'$.[0:]')\" 
     --inputType=\"application/json\" 
     --outputType=\"application/json\" 
  | myprocessor 
     --inputType=\"application/json\" 
     --outputType=\"application/json\" 
  | log"

Gary Russell提到此问题已在Spring Cloud Stream Ditmars.M2(1.3.0.M2)中得到纠正。