美文网首页
Paypal工程博客

Paypal工程博客

作者: Queen〇fLaponia | 来源:发表于2018-03-28 13:20 被阅读0次
20180328 Paypal工程博客 copy

草原

1月30日 20:30来自iPhone客户端

PayPal 尝试用 akka stream 和 akka actor 来处理交易,这给他们带来了一个从更自然的角度来重新审视业务模型的机会,并且,将处理时间缩短了 80% 以及获得了接近 0 的失败率: Learnings from Using a Reactive Platform – Akka/Squbs | PayPal Engineering BlogO网页链接

    草原老师推荐,Squbs是产品infoQ有介绍

Learnings from Using a Reactive Platform – Akka/Squbs

By Balaji Srinivasaraghavan December 13, 2017

Introduction

Simple and reliable APIs are important for winning customer trust in

all industries. This is especially true when the API is a customer

facing endpoint that accepts payment instructions. Add to that the

ability to submit 15,000 payments in a single request and reliability

becomes critical.

Let’s talk a bit aboutPayouts– a PayPal product through which users can submit mass payment requests. It’s an asynchronous interaction – a request with multiple payment instructions returns a token. The caller can then use the token to poll for the payment outcomes or listen for webhooks.

As you can imagine, customers like to be sure we got each request

right and want to be kept up to date on the status of the money

movements. The actual request processing involves orchestrating calls to

a slew of services ranging from ones that provide information on

recipients to services that perform the actual money movement.

We recently finished work on improving the features offered by the product and on further improving its reliability. One of the changes we made was to useAkkato build the payment orchestration engine as a data flow pipeline. Discussed below are some learnings from that exercise.


Concurrency – Harder Than It Needs to Be?

Object-oriented design does a great job of capturing the static

aspects of a real world system with entities, their attributes and

relationships. These aspects can be translated directly to Java

artifacts – classes with variables using inheritance and composition.

Real world entities also have a dynamic nature to them – the way in

which they come to life over the lifetime of the application and how

they interact with each other. Java unfortunately does not have a

sufficiently nuanced abstraction for representing such changes in states

of entities over time or their degree of concurrency. Instead, we have a

CPU’s (hardware) model of the world – threads and processors, that has

no equivalent in the real world. This results in an impedance mismatch

that the programmer is left to reconcile.

Some improvements in the building blocks for representing concurrent

aspects came with Java 8. While we didn’t get a better model, we did get

some elegant constructs that reduced the pain of using Java for such

tasks.

Lambdas made multithreaded Java code actually readable. Java 8 introduced the ability to compose futures.

And, we got streams. Streams trace their roots back to a programming

paradigm from the 1960s called data flow programming. The model lets us

compose a series of transformations on a dataset as a pipeline. While we

concentrate on the logical composition of a data processing job, the

framework is tasked with figuring out the physical orchestration of

parallel processing for the job.

Java’s implementation of streams was designed with the expectation

that it would not have to deal with high latency (typically IO). This

makes it unsuitable for building services. But, reactive frameworks such

as Akka provide implementations that account for high latency tasks

such as service invocations within a stream.

Data flow programmingusing streams and theactor modelprovide great conceptual models for reasoning about the dynamic aspects of a real world system. These models represent a paradigm shift once adopted. We can use them to represent concurrency using abstractions that are intuitive. The same model can be represented in Java as well and brought to life by the underlying platform, without developers having to manually code for concurrency using lower level primitives.


Learnings From Using Reactive Streams and the Actor Model

To set the context, the primary design

goal of the system is to reliably accept and process payments. That

means we maintain an accurate record of progress and prioritize features

such as check pointing & auto-recovery over response time and

throughput. That does not mean the solution can be “slow” though. The

difference in emphasis only means we need to get more done in the same

amount of time.

Below are some of the learnings we had over the course of the project as it relates to reactive streams and using the actor model. The ideas come from a variety of sources including other teams who had used Akka before us, the team which maintainsSqubs(Akka with PayPal operationalization) as well as our own experience.

Foundational Patterns for Reuse

Over time, there are a bunch of stream patterns that recur throughout

the application. It helps to create a pattern library as we encounter

them.

Select between two flows based on a predicate

Select one of many flows based on a decider function

Bypass running a flow and short circuit if an error was encountered in a previous stage

Retry an operation based on a decider (see one from Squbshere)

Akka provides some patterns as well –akka-stream-contrib

An example of a flow selector that selects one of many flows based on a decider function would be:

public static Flow selectByIndex(ActorSystem system, Function numericPartitioner, Flow... innerFlows) {

return Flow.fromGraph(GraphDSL.create(builder -> {

  // given a data element in the stream, return the index of the flow to be used to process it

  UniformFanOutShape top = builder.add(Partition.create(innerFlows.length, msg -> {

    int partition = numericPartitioner.apply(msg);

    return partition;

  }));

  // merge the output of the individual flows back to the primary flow

  // this is made possible by the use of a homogeneous envelope type across the flow

  akka.stream.scaladsl.MergePreferred.MergePreferredShape bottom = builder.add(MergePreferred.create(innerFlows.length - 1));

  int index = 0;

  for (Flow flow : innerFlows) {

  FlowShape tgt = builder.add(flow);

  builder.from(top.out(index)).toInlet(tgt.in());

  //last flow is the preferred flow

  builder.from(tgt.out()).toInlet((index + 1 == innerFlows.length) ? bottom.preferred() : bottom.in(index));

  index++;

}

return FlowShape.of(top.in(), bottom.out());

}));

}

While individually most of these helpers are not complex to create,

they save a lot of effort for the team over time. This is especially

true for ones such as a predicate based selector that show up

everywhere. These otherwise would have to be written as graph stages.

With helpers, we can now use flow composition to put them to use and

focus on writing and certifying the business logic alone.


Failure is the Message

It helps a great deal to use a homogenous data type to transfer

information on success and failure down the stream without aborting

processing. To achieve that, we model stream elements as envelopes with

business information and metadata about the status of previous

transformations.

For example, say we could not reach a service as part of a stream

stage. We can embed a Try or an optional exception in the current

message when that happens. A subsequent transformation may choose to not

run and short-circuit on seeing the failure while a stage that updates

the database could choose to participate to record the outcome. A stream

stage’s behavior of not participating in processing if an exception was

recorded should be provisioned via flow composition rather than having

each stage run the same check.

Use Actors when Warranted

Akka streams should serve as the default model for most use cases.

Code modelled as a stream is not only performant but also simple to

create and maintain. But, sometimes actors provide advantages that are

difficult to achieve otherwise.

The primary use case for actors is, of course, to maintain state and

to perform tasks that benefit from supervision. Actors can work with a

single stream or across multiple streams for use cases such as

aggregation, performance or business statistics and even centralized

logging via an event bus. They are also a natural choice when you want

to separate processing of non-critical tasks away from a critical

pipeline. They are also useful for implementing blocking tasks (on a

separate thread pool) especially if we don’t need feedback on the

outcome.


Stream Decomposition Using Actors

There are advantages in decomposing a large stream into smaller

streams that are anchored in chained actors. We used such a model to

reduce the solution complexity while servicing the requirement to

checkpoint and auto-recover. Each of those actors act as a checkpoint

from where processing can resume in case of a failure.

We can model check pointing within the stream as well. But, using

chained actors with smaller and more focused streams made the code

simpler to write and easier to maintain. This is because the branching

introduced by features such as check pointing are orthogonal to business

logic. They compound the complexity already being expressed in the

stream and tend to not be amenable to local optimizations like flow

composition.

If you are designing for high velocity or performance, you are going

to want to use a single stream. But, for most other applications,

leveraging actors and streams together can help simplify the design of

complex streams.


Platform Lock-in and Portability

If we can provide a reasonable level of isolation between the actual

business logic and the code that strings them together and orchestrates

the flow, we can retain a good amount of flexibility in moving to a

different implementation of reactive streams or the actor model.

One area that needs more attention though is Akka HTTP. One of Akka’s

key strengths as a reactive platform is the way in which Akka HTTP is

tied in with Akka Streams. Together they deliver a lot of value for

developers as a simple cohesive package. But, in the context of

portability, it makes sense for us to build a layer of abstraction on

top of the Akka HTTP client. This serves two purposes –

Airgap developers from Akka HTTP to enable changing the HTTP client provider at a later point in time

A layer of indirection enables provisioning a richer set of features that’s comparable to JAX-RS – marshalling/unmarshalling, custom retry. The flip side is we lose advanced features such as client sideHTTP response streaming.


Conclusion

We’re in the midst of a significant transformation in the way we

write Java applications. Functional programming in Java 8 is fairly well

understood by now.  Reactive programming, given the massive adoption

Java streams had, has established itself as indispensable. Reactive

streams have been added to the Java 9 SDK.

Broadly, these are 3 class of applications that benefit from adopting a reactive framework.

Backend services – those with multiple downstream dependencies (micro-services) or with a need to orchestrate complex tasks

Engineering teams who want to leverage modern frameworks that

abstract complexity in traditional programing – multithreading,

non-blocking IO

Asynchronous or high velocity event streams – twitter, mobile application GUI

Akka is built for performance and ease of use. That’s a hard balance

to achieve. It’s even harder to achieve on your own. Whether it’s

concurrency or non-blocking IO, the Akka toolkit provides intuitive and

performant models for developers to build upon.

In our case, we saw an 80% reduction in processing time with the new

stack and a near 0% failure rate. Surely, as we turn on more features,

there’s more to learn and improve. But, it’s clear that adopting

reactive streams and the actor model has enabled us to service our

customers better by providing a more reliable and performant API.

相关文章

  • Paypal工程博客

    草原 1月30日 20:30来自iPhone客户端 PayPal 尝试用 akka stream 和 akka a...

  • PayPal接入过程

    Paypal 支付接入 序:参考了现有许多博客,大多数的开发者最终选择RestFulAPi进行Paypal的接入,...

  • PayPal的使用

    在阅读PayPal使用相关的博客文章后,发现很多人其实都没有对PayPal的使用有比较好的理解,他们不清楚使用的究...

  • 今天是个好日子-拿到Paypal onsite

    昨天上午Paypal电面,面试官是一个华裔女工程师,在里面Paypal做了十年,非常资深。 她问的问题比较基础,但...

  • 100篇大数据论文

    #Mark PayPal高级工程总监:读完这100篇论文 就能成大数据高手-CSDN.NET

  • 读完这100篇论文 就能成大数据高手

    作者:Anil Madan 译者:张玉宏 文源:LinkeDin 转自:CSDN PayPal高级工程总监Anil...

  • ==100篇论文读完,你就能成大数据高手!

    PayPal高级工程总监:读完这100篇论文 就能成大数据高手-CSDN.NET http://www.csdn...

  • paypal 支付流程

    推荐资料 接入 paypal PHP-sdk 支付 / 回调 / 退款全流程 paypal/PayPal-PHP-...

  • 第一性原理

    埃隆·马斯克 企业家、工程师、慈善家 PayPal、特斯拉、SpaceX太空探索技术公司、Solarcity创始人...

  • PayPal高级工程总监关于数据

    出处:CSDN 开源(Open Source)用之于大数据技术,其作用有二:一方面,在大数据技术变革之路上,开源在...

网友评论

      本文标题:Paypal工程博客

      本文链接:https://www.haomeiwen.com/subject/ahvycftx.html