Play framework 2.5.x : Stream 移行ガイド を google 翻訳した。(websocket/Commet/EventSource は使ってないので未訳)
Streams移行ガイド
Play 2.5では、データとレスポンスの本体をどのようにストリームするかが大きく変わっています。
- Play 2.5では、 ストリーミング用のAkkaストリーム が使用されます。以前のバージョンのPlayでは、
WebSocket
、Chunks
など、いくつかの特別な種類のストリーミングだけでなく、ストリーミングにiteratees を使用していました。
Akkaストリームに変更する主なメリットは2つあります。まず、JavaユーザーはPlayの全機能セットにアクセスできるようになりました。ボディーパーサとフィルタを記述できます。次に、ストリーミングライブラリはPlay全体でより一貫性があります。
- Play 2.5は バイトのパケットを保持するために
ByteString
を使用します。以前は、Playはバイト配列(byte[]
/ArrayByte
)を使っていました。
`ByteString` クラスはJavaの `String` のように不変ですので、安全で使いやすいです。 `String` と同様に、構築時にデータをコピーするため、パフォーマンスコストは小さくなりますが、これは安価な連結および部分文字列演算とのバランスが取れています。
- Play 2.5は レスポンスボディに新しい
HttpEntity
タイプ を持っています。これまでのレスポンスボディは、単純なバイトストリームでした。 HTTPボディはHttpEntity
の一種です:Strict
、Streamed
、Chunked
です。
Playにどのタイプのエンティティを使用するかを伝えることで、アプリケーションはPlayのHTTP応答の送信方法をより詳細に制御できます。また、Playが本体をどのように配信するのかを簡単に最適化することもできます。
変更の概要
Play APIの次の部分が更新されました:
- 結果(
Result
本体、chunked
/feed
/stream
メソッド) - アクション(
EssentialAction
) - ボディパーサー(
BodyParser
) - WebSocket(
WebSocket
) - サーバー送信イベント(
EventSource
)
以下のタイプが変更されました:
目的 | 古いタイプ | 新しいタイプ |
---|---|---|
バイト保持 |
byte [] / 配列[Byte]
|
ByteString |
ストリーム生成 |
Enumerator 、 WebSocket.Out 、 Chunks.Out 、 EventSource.Out
|
Source |
ストリームを別のストリームに変換する | Enumeratee |
Flow |
ストリームを単一の値に変換する | Iteratee |
Accumulator |
ストリームを消費する | Iteratee |
Sink |
移行方法(API別)
次のセクションでは、APIのさまざまな部分を使用するコードを移行する方法の概要を示します。
チャンク結果の移行( chunked
、 Results.Chunked
)
Play 2.4 では、Scalaで Enumerator
、そしてJavaで Results.Chunked
オブジェクトを使用してチャンク結果を作成します。 Play 2.5では、これらのAPIの部分は引き続き使用できますが、廃止されました。
新しいAPIに移行する場合は、 StatusHeader
オブジェクトで chunked
メソッドを呼び出し、チャンクのストリームにAkka Streams Source
オブジェクトを提供することで、チャンク結果を作成できます。
より高度なユーザは、明示的に HttpEntity.Chunked
オブジェクトを作成し、それを Result
オブジェクトコンストラクタに渡すことを好むかもしれません。
-
Enumerator
をSource
に移行する方法については、Enumerator
をSource
に移行する」を参照してください。
ストリーミング結果の移行( feed
、stream
)(Scalaのみ)
Play 2.4では、Scalaユーザは Enumerator
を feed
メソッドまたは stream
メソッドに渡すことで結果をストリーミングできます。 (Javaユーザは、チャンクされた結果とは別に、結果をストリーミングする方法がありませんでした)。 feed
メソッドは、 Enumerator
のデータをストリーミングして接続を閉じました。 stream
メソッドは、HTTPバージョンの接続と Content-Length
ヘッダの有無に応じて、結果をストリーミングまたはチャンクして、おそらく接続を閉じました。
Play 2.5では、 stream
メソッドが削除され、 feed
メソッドは廃止されました。 feed
メソッドを新しいAPIに移行するかどうかを選択することができます。 stream
メソッドを使うとコードを変更する必要があります。
新しいAPIは、 Result
オブジェクトを直接作成し、その本体を表す HttpEntity
を選択することです。ストリーミングされた結果の場合、 HttpEntity.Streamed
クラスを使用することができます。 Streamed
クラスは Source
を本体とし、オプションの Content-Length
ヘッダ値をとります。 Source
の内容はクライアントに送られます。エンティティに Content-Length
ヘッダがある場合、接続はオープンのままになります。そうでなければ、ストリームの終わりを知らせるために閉じられます。
-
Enumerator
をSource
に移行する方法については、「Enumerator
をSource
に移行する」を参照してください。
未訳部分
### Migrating WebSockets (`WebSocket`)
#### Migrating Scala WebSockets
#### Migrating Java WebSockets
### Migrating Comet
#### Migrating Java Comet
#### Migrating Scala Comet
### Migrating Server-Sent events (`EventSource`)
#### Migrating Java Server-Sent events
#### Migrating Scala Server-Sent events
カスタムアクションの移行( EssentialAction
)(Scalaのみ)
Scalaのほとんどのユーザーは、アクションに Action
クラスを使用します。 Action
クラスはロジックを実行して結果を送る前に常にボディを完全に解析する EssentialAction
の一種です。 ユーザは独自のカスタムEssentialActionを書いてリクエストボディーを段階的に処理するようなことをすることができます。
Play 2.4 アプリケーションで通常の Action
を使用している場合は、マイグレーションは必要ありません。 しかし、もしあなたが EssentialAction
を書いたのであれば、それを Play 2.5 の新しいAPIに移す必要があります。 「EssentialAction」の動作は同じですが、シグネチャは Play 2.4 から変更されました:
trait EssentialAction extends (RequestHeader => Iteratee[Array[Byte], Result])
to a new signature in Play 2.5:
trait EssentialAction extends (RequestHeader => Accumulator[ByteString, Result])
移行するには、 Iteratee
を Accumulator
と Array[Byte]
を ByteString
で置き換える必要があります。
- IterateeをAccumulatorに移行する方法については、「IterateesをSinks and Accumulatorsに移行する」を参照してください。
-
Array[Byte]
をByteString
に移行する方法については、「ByteStringsへのバイト配列の移行」を参照してください。
カスタムボディパーサーの移行( BodyParser
)(Scalaのみ)
Play 2.4アプリケーションに独自のBodyParserを持っているScalaユーザーの場合は、新しいPlay 2.5 APIに移行する必要があります。 BodyParser
型シグネチャは、Play 2.4では次のようになります。
trait BodyParser[+A] extends (RequestHeader => Iteratee[Array[Byte], Either[Result, A]])
In Play 2.5 it has changed to use Akka Streams types:
trait BodyParser[+A] extends (RequestHeader => Accumulator[ByteString, Either[Result, A]])
移行するには、 Iteratee
を Accumulator
と Array[Byte]
を ByteString
で置き換える必要があります。
- Iteratee を Accumulator に移行する方法については、「IterateesをSinks and Accumulatorsに移行する」を参照してください。
- [Array [Byte]]をByteStringに移行する方法については、「ByteStringsへのバイト配列の移行」を参照してください。
Result
ボディーの移行(Scalaのみ)
Result
オブジェクトは、結果本体と接続クローズフラグの表現方法が変更されました。 body: Enumerator[Array[Byte]], connection: Connection
から body: HttpEntity
に取り替えることになりました。 HttpEntity
型にはボディに関する情報と接続を閉じる方法に関する暗黙の情報が含まれています。
Source
とオプションの Content-Length
と Content-Type
ヘッダを含む Streamed
エンティティを使って、既存の Enumerator
を移行できます。
val bodyPublisher: Publisher[ByteString] = Streams.enumeratorToPublisher(bodyEnumerator)
val bodySource: Source[ByteString, _] = Source.fromPublisher(bodyPublisher)
val entity: HttpEntity = HttpEntity.Streamed(bodySource)
new Result(headers, entity)
これらの型への移行の詳細については、 Enumerator
s の移行と ByteString
への移行の節を参照してください。
- Iteratee を Accumulator に移行する方法については、「IterateesをSinks and Accumulatorsに移行する」を参照してください。
- [Array [Byte]]をByteStringに移行する方法については、「ByteStringsへのバイト配列の移行」を参照してください。
Result
ボディのストリームはまったく必要ないかもしれません。 そうであれば、ボディに Strict
エンティティを使えます。
new Result(headers, HttpEntity.Strict(bytes))
移行する方法(型別)
このセクションでは、バイト配列とストリームを新しいAkka Streams APIに移行する方法について説明します。
Akka StreamsはAkkaプロジェクトの一部です。 PlayはAkka Streamsを使用してストリーミング機能を提供します。一連のバイトやその他のオブジェクトを送受信します。 Akkaプロジェクトには、Akkaストリームに関する多くの優れたドキュメントがあります。 PlayでAkka Streamsを使用する前に、利用可能な情報を確認するためにAkka Streamsのドキュメントを調べることをお勧めします。
APIドキュメントは、Akka APIの主要なドキュメントの akka.stream
パッケージの下にあります:
最初にAkka Streamsを使い始めるときは、 Basics のフローの操作 セクションを見ることをお勧めします。 Akka Streams APIの最も重要な部分を紹介します。
一度にアプリケーション全体を変換する必要はありません。 アプリケーションの一部は繰り返し使用でき、他の部分はAkkaストリームを使用します。 Akka Streamsは reactive streams の実装を提供し、Playのiterateesライブラリも反応ストリームの実装を提供します。その結果、Play の iteratees は Akka Streams に簡単にラップすることができます。
バイト配列( byte[]
/ Array[Byte]
)を ByteString
に移行する
Java と Scala の ByteString
のAPIドキュメントを参照してください。
例:
Scala:
// Get the empty ByteString (this instance is cached)
ByteString.empty
// Create a ByteString from a String
ByteString("hello")
ByteString.fromString("hello")
// Create a ByteString from an Array[Byte]
ByteString(arr)
ByteString.fromArray(arr)
Java:
// Get the empty ByteString (this instance is cached)
ByteString.empty();
// Create a ByteString from a String
ByteString.fromString("hello");
// Create a ByteString from an Array[Byte]
ByteString.fromArray(arr);
*.Outs
を Source
に移行する
Playでは、元の WebSocket.Out
クラス、 Chunks.Out
クラス、 EventSource.Out
クラスではなく、 Source
を使用してイベントを生成します。 これらのクラスは使用が簡単でしたが、柔軟性がなく、back pressure を適切に実装しませんでした。
*.Out
クラスは、ストリームを生成するすべての Source
で置き換えることができます。 Source
を作成する方法はたくさんあります (Java/Scala)。
*.Out
を単純なオブジェクトに置き換えて、バック・プレッシャーを心配することなく、メッセージを書いたり閉じることができたら、 Source.actorRef
メソッドを使うことができます:
Source<ByteString, ?> source = Source.<ByteString>actorRef(256, OverflowStrategy.dropNew())
.mapMaterializedValue(sourceActor -> {
sourceActor.tell(ByteString.fromString("hello"), null);
sourceActor.tell(ByteString.fromString("world"), null);
sourceActor.tell(new Status.Success(NotUsed.getInstance()), null);
return null;
});
Scala:
val source = Source.actorRef[ByteString](256, OverflowStrategy.dropNew).mapMaterializedValue { sourceActor =>
sourceActor ! ByteString("hello")
sourceActor ! ByteString("world")
sourceActor ! Status.Success(()) // close the source
}
Enumerator
を Source
に移行する
Playは多くの場所で Enumerator
を使用して値のストリームを生成します。
ステップ1: 移行APIを使用する(利用可能な場合)
Results.chunked
または Results.feed
を使用する場合は、引き続き既存のメソッドを使用できます。 これらのメソッドは廃止されました。したがって、とにかくコードを変更したい場合があります。
ステップ2: アダプタを使用して Enumerator
を Source
に変換する
既存の Enumerator
を Streams.enumeratorToPublisher
を使用してリアクティブストリーム Publisher
に変換した後、 Source.fromPublisher
を使用してパブリッシャをソースに変換することで、既存の列挙子をソースに変換できます。
val enumerator: Enumerator[T] = ...
val source = Source.fromPublisher(Streams.enumeratorToPublisher(enumerator))
ステップ3: (オプション)ソースに書き直す
列挙子ファクトリメソッドの一般的なマッピングの一覧を次に示します。
Iteratees | Akka Streams | Notes |
---|---|---|
Enumerator.apply(a) |
Source.single(a) |
|
Enumerator.apply(a, b) |
Source.apply(List(a, b))) |
|
Enumerator.enumerate(seq) |
Source.apply(seq) |
seq must be immutable |
Enumerator.repeat |
Source.repeat |
繰り返し要素は、Akkaストリームで毎回評価されません |
Enumerator.empty |
Source.empty |
|
Enumerator.unfold |
Source.unfold |
|
Enumerator.generateM |
Source.unfoldAsync |
|
Enumerator.fromStream |
StreamConverters.fromInputStream |
|
Enumerator.fromFile |
StreamConverters.fromInputStream |
java.io.File のための InputStream を作成しなければなりません。 |
Iteratee
を Sink
s と Accumulator
s に移行する
ステップ1: アダプタを使用して変換する
既存の Iteratee
を Streams.iterateeToSubscriber
を使ってリアクティブストリーム Subscriber
に変換してから Sink
に変換し、 Sink.fromSubscriber
を使ってシンクに変換することができます。例えば:
val iteratee: Iteratee[T, U] = ...
val (subscriber, resultIteratee) = Streams.iterateeToSubscriber(iteratee)
val sink = Sink.fromSubscriber(subscriber)
Accumulator
を返す必要がある場合は、代わりに Streams.iterateeToAccumulator
が使えます。
ステップ2: (オプション)Sink
に書き直す
iterateeファクトリメソッドの一般的なマッピングのリストを次に示します。
Iteratees | Akka Streams | Notes |
---|---|---|
Iteratee.fold |
Sink.fold |
|
Iteratee.head |
Sink.headOption |
|
Iteratee.getChunks |
Sink.seq |
|
Iteratee.foreach |
Sink.foreach |
|
Iteratee.ignore |
Sink.ignore |
|
Done |
Sink.cancelled |
マテリアライズされた値は、結果を生成するためにマップすることができます。アキュムレータを使用する場合は、代わりに Accumulator.done を使用できます。 |
Enumeratees
の Processor
への移行
ステップ1: アダプタを使用して変換する
Flow.fromProcessor
を使用して、既存の Enumeratee
を Flow
に変換し、その後、 Flow.fromProcessor
を使用して Processor
を Flow
に変換できます。たとえば、次のようになります。
val enumeratee: Enumeratee[A, B] = ...
val flow = Flow.fromProcessor(() => Streams.enumerateeToProcessor(enumeratee))
ステップ2 : (オプション) Flow
への書き換え
列挙型ファクトリメソッドの一般的なマッピングのリストを次に示します。
Iteratees | Akka Streams | Notes |
---|---|---|
Enumeratee.map |
Flow.map |
|
Enumeratee.mapM |
Flow.mapAsync |
Akka Streamsの並列性、つまり一度に並列にマップされる要素の数を指定する必要があります。 |
Enumeratee.mapConcat |
Flow.mapConcat |
|
Enumeratee.filter |
Flow.filter |
|
Enumeratee.take |
Flow.take |
|
Enumeratee.takeWhile |
Flow.takeWhile |
|
Enumeratee.drop |
Flow.drop |
|
Enumeratee.dropWhile |
Flow.dropWhile |
|
Enumeratee.collect |
Flow.collect |