DM Processor

Case of Study

REPLACING

Spring Integration

Spring Integration

  • Message Class
    • Overusing CPU resources (Generating UUID)
    • Impossible to extend the class (Old version 2.2.6)
  • Spring Chain
    • Every chain declared in a XML file
    • NO Unit Tests of the chain
    • Depends on Spring core
  • Scalability
    • Blocking solution
    • Running on a single JVM
    • Non-able to scale out

Introducing Akka Streams

Why?

  • Reactive solution
    • Async
    • Non-Blocking
    • Resilient
    • Message Driven
    • Back-pressure
  • Compatible with the current DM technology stack
  • Part of the standard Reactive Streams definition
  • Easy to test every single flow
  • Faster than Spring Integration (~50%)

Introducing Akka Streams

Responsive

Message Driven

Elastic

Resilient

Introducing Akka Streams

Stream Composition: Single Shapes

Introducing Akka Streams

Stream Composition: Composite Shapes

Introducing Akka Streams

Stream Composition: Partial Flow Graphs

Introducing Akka Streams

Event Inbound Flow

Introducing Akka Streams

<int:channel id="eventInboundChannel" />

<int:chain input-channel="eventInboundChannel" output-channel="eventPreProcessingChannel">
    <int:transformer ref="eventHeaderEnricher" method="applyHeaders"/>
    <int:filter ref="payloadValidationFilter" method="accept" discard-channel="validationFailureSuppressedChannel"/>
</int:chain>

<int:channel id="validationFailureSuppressedChannel" />

<int:chain auto-startup="true" input-channel="validationFailureSuppressedChannel" output-channel="eventSuppressedChannel">
    <int:header-enricher>
        <int:header name="suppressedReason" value="ValidationFailure"/>
    </int:header-enricher>
</int:chain>

<int:channel id="eventPreProcessingChannel" />

<int:header-value-router input-channel="eventPreProcessingChannel" header-name="eventRefreshRequest"
                         default-output-channel="eventProcessingChannel">
    <int:mapping value="true" channel="eventRefreshChannel"/>
</int:header-value-router>

Event Inbound Channel defined using Spring

Introducing Akka Streams

trait EventInboundFlow extends ValidationFailureSuppressedFlow with EventSuppressedPublisherFlow {

  this: Gateway with EventPipelineBeans =>

  private val payloadFilterFlow = filterPartialFlowGraph(payloadValidationFilter.accept(_))

  private val eventRefreshFilterFlow = filterPartialFlowGraph(_.isContained(MessageUtil.EVENT_REFRESH_REQUEST_HEADER))

  lazy val eventInboundFlow = FlowGraph.partial() { implicit b =>
    val headerEnricherFlow = b.add(partialFlow(eventHeaderEnricher.applyHeaders(_)))
    val payloadFilter = b.add(payloadFilterFlow)
    val erf = b.add(eventRefreshFilterFlow)
    val validationSuppressed = b.add(validationFailureSuppressedFlow)

    headerEnricherFlow ~> payloadFilter
                          payloadFilter.out(0) ~> erf
                          payloadFilter.out(1) ~> validationSuppressed

    UniformFanOutShape(headerEnricherFlow.inlet, erf.out(0), erf.out(1), validationSuppressed.outlet)
  }.named("eventInboundFlow")

}

Event Inbound Flow: Coding

Introducing Akka Streams

trait EventInboundFlow extends ValidationFailureSuppressedFlow with EventSuppressedPublisherFlow {

  this: Gateway with EventPipelineBeans =>

  private val payloadFilterFlow = filterPartialFlowGraph(payloadValidationFilter.accept(_))

  private val eventRefreshFilterFlow = filterPartialFlowGraph(_.isContained(MessageUtil.EVENT_REFRESH_REQUEST_HEADER))

  lazy val eventInboundFlow = FlowGraph.partial() { implicit b =>
    val headerEnricherFlow = b.add(partialFlow(eventHeaderEnricher.applyHeaders(_)))
    val payloadFilter = b.add(payloadFilterFlow)
    val erf = b.add(eventRefreshFilterFlow)
    val validationSuppressed = b.add(validationFailureSuppressedFlow)

    headerEnricherFlow ~> payloadFilter
                          payloadFilter.out(0) ~> erf
                          payloadFilter.out(1) ~> validationSuppressed

    UniformFanOutShape(headerEnricherFlow.inlet, erf.out(0), erf.out(1), validationSuppressed.outlet)
  }.named("eventInboundFlow")

}

Introducing Akka Streams

"Event Inbound Flow" should {

    val eventRefreshHeaders = Map(MessageUtil.EVENT_REFRESH_REQUEST_HEADER -> Boolean.box(true))

    "Have every message with the header 'eventRefreshRequest' and value 'true' in filterOut and empty stream in notFilterOut" in withMessageSource(eventRefreshHeaders) { source => messages =>

      val (filterOut, notFilterOut, suppressedOut) = flowGraph(source)

      val filterResult = Await.result(filterOut, 1000.millis)

      // Should be an Empty stream
      intercept[NoSuchElementException] {
        Await.result(notFilterOut, 1000.millis)
      }

      intercept[NoSuchElementException] {
        Await.result(suppressedOut, 1000.millis)
      }

      filterResult.getHeaders should (contain key (MessageUtil.EVENT_REFRESH_REQUEST_HEADER) and contain value (true))
    }

}

Event Inbound Flow: Unit Test

BENCHMARKING

CONCLUSION

Introducing Akka Streams

By Gabriel Volpe

Introducing Akka Streams

  • 1,390