SlideShare une entreprise Scribd logo
1  sur  77
Télécharger pour lire hors ligne
サーバーサイドでの非同期処
理で色々やったよ
Koji Lin, LINE Fukuoka
@kojilin
自己紹介
● LINE Fukuoka Corp
○ Java でサーバーサイド開発
● Taiwan Java User Group メンバー
○ https://www.meetup.com/taiwanjug/
全てがシンプルだった
List<Item> getRanking(String country) {
String rankingType = dao.getRankingType("JP");
return dao.getRanking(rankingType).stream()
.map(item -> ...)
.collect(toList());
}
複雑化していくシステム
List<Item> getRanking(String country) {
String rankingType = api.getRankingType("JP");
List<String> rankingIds =
searchClient.getRanking(rankingType);
return rankingIds.stream()
.map(dao::getItem)
.map(...)
.collect(toList());
}
● パフォーマンスや機能の追加/複雑さを軽減するため、いろん
なサービス/ミドルウェア/チームに分ける
Latency が上がっていく
● いろんな API やミドルウェア等のアクセスが増え、各リモート
のラウンドトリップの積み重ね
● リクエストスレッドがブロックされて、軽いリクエストも影響を受
ける
何故サーバーサイドで非同期?
● 並行できるタスクも自然に順序に書いてしまう
● 1 スレッド 1 リクエスト
○ 同時処理数 <= 最高スレッド数
○ 重い処理が軽い処理をブロック
○ CPU とメモリの無駄遣い
■ デフォルトの -Xss は 1024KB
■ コンテキストスイッチのコスト
同期なコードを非同期に書き直す
● フレームワークの変更
● 戻り値の型は Guava ListenableFuture を選択
○ CompletableFuture に対応するライブラリがまだ少ない
○ Futures#transform で非同期が組み合わせれる
○ Dagger で非同期 DI が利用できる
● ストレージアクセス
● リモート API アクセス
フレームワークを社内製品へ
● RESTful と Thrift RPC のエンドポイントを提供
○ Spring Web/Spark と Facebook Nifty を使っていた
● 社内製でオープンソースの Armeria に移行
○ https://github.com/line/armeria
○ Netty ベース HTTP/2 対応の非同期 RPC/REST library
● 実際 Nifty + swift で非同期も可能です
○ https://github.com/facebook/swift
○ その swift ではない !
REST Controller も非同期へ
● 全てのフレームワークを Spring Web
● 一部同期の Controller から非同期
○ Spring Web の DeferredResult<T>
@RequestMapping("/hello")
public DefferredResult<String> hello() {
DeferredResult<String> deferredResult = new
DeferredResult<>();
... // callback で deferredResult.setResult("hello");
return deferredResult;
}
Thrift とは?
● RPC フレームワーク
● .thrift の IDL を定義
service HelloService{
string hello(1: string name)
}
● Thrift Compiler で対応の言語のコードを生成
● ロジックを入れて、サポートしてるライブラリ上でデプロイすれ
ば良い
同期の Iface から非同期の AsyncIface へ
@Override
public String hello(String name) {
return "Hello, " + name + '!';
}
@Override
public void hello(String name,
AsyncMethodCallback<String> resultHandler) {
resultHandler.onComplete("Hello, " + name + '!');
}
ストレージアクセス
● MySQL
○ MyBatis
○ Guava ListeningExecutorService と組み合わせる
■ Async JDBC 自体がまだ無い
● MongoDB
○ MongoDB Asynchronous Java Driver に変更
MyBatis の API
<E> List<E> selectList(String statement,
Object parameter);
JDBC のアクセスを非同期に
ListeningExecutorService executor;
public <E> ListenableFuture<List<E>> selectList(
String statement, Object parameter) {
return executor.submit(() ->
delegate.selectList(statement, parameter));
}
MongoDB のアクセスを非同期に
private MongoCollection<Model> collection;
public void list(String id, int offset, int limit,
SingleResultCallback<Model> callback) {
collection.find(eq(ID, id))
.skip(offset)
.limit(limit).into(list, callback));
}
MongoDB を ListenableFuture に
public ListenableFuture<List<Model>> list(
String id, int offset, int limit) {
SettableFuture<List<Model>> future =
SettableFuture.create();
collection.find(eq(ID, id))
.skip(offset)
.limit(limit).into(list, (result, t) -> {
if (t != null) { future.setException(t); }
else { future.set(result); }
});
return future;
}
リモート API アクセス
● Apache HttpComponents から Armeria の HttpClient へ
○ Apache HttpComponents にも Async Client がある
● REST API が多すぎるので、Retrofit を利用して、ネットワーク
層は Armeria の HttpClient
Retrofit と併用
● Retrofit で API を Java コードへマッピングする
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
GitHubService service =
retrofit.create(GitHubService.class);
Retrofit と GuavaCallAdapterFactory
● Retrofit は戻り値の型を拡張できる
public interface GitHubService {
@GET("users/{user}/repos")
ListenableFuture<List<Repo>> listRepos(
@Path("user") String user);
}
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addCallAdapterFactory(
GuavaCallAdapterFactory.create())
.build();
IO 関連のアクセスを全て非同期化
Guava の transform で組み合わせ
ListenableFuture<String> result =
FuturesExtra.syncTransform(
dao.getUser("id"),
user -> user.getName());
ListenableFuture<Image> result =
Futures.transformAsync(
dao.getUser("id"),
user -> apiClient.getIcon(user));
Concurrent 化
● Zipkin でアクセスチェック
移行で大変だったとこ
● 同期から非同期の慣れ、特にコードがどっちも併存してい
る時、Future#get に逃げやすい
○ 非同期で event-loop、thread-pool を使うので、小さ
なブロックもパフォーマンスに影響が出る
○ リクエストからレスポンスまでの完全な非同期コードを
準備する
○ コードレビューを頑張る
移行で大変だったとこ
● 次の非同期タスクの発火スレッドは?
○ リクエスト関連の情報をフレームワークのイベントループ
ThreadLocal を多用している
○ デフォルトでは今のスレッドか前のタスクのスレッドを使う
○ transform メソッドで executor を設定する
Futures.transformAsync(
dao.getUser("id"),
user -> apiClient.getIcon(user),
executor);
その他
● Spotify の Futures-extra を多用
○ https://github.com/spotify/futures-extra
○ Guava 19 で transform メソッドのオーバロディングで
コンパイルウォーニングがめんどくさい
● AsyncRetrier と ConcurrencyLimiter も便利
AsyncRetrier
int retryCount = 3;
int delayMillis = 100;
AsyncRetrier retrier = AsyncRetrier.create(
Executors.newSingleThreadScheduledExecutor());
ListenableFuture<List<String>> listFuture =
retrier.retry(() -> api.listByRanking("JP"),
retryCount,
delayMillis);
ConcurrencyLimiter
int maxConcurrentCount = 100;
int maxQueueSize = 1000;
ConcurrencyLimiter<List<String>> concurrencyLimiter =
ConcurrencyLimiter.create(maxConcurrentCount,
maxQueueSize);
ListenableFuture<List<String>> listFuture =
concurrencyLimiter.add(() ->
dao.listByRanking("JP"));
● 非同期化で一気にリモートアクセスが一杯流せてリソー
スを喰いつくすのを防ぐ
これが複雑になっていくと!?
Futures.transformAsync(
Futures.transformAsync(getRankingType("JP"),
dao::listByRanking,
executor),
ids -> Futures.allAsList(ids.stream()
.map(client::getUserById)
.collect(toList())),
executor);
これが複雑になっていくと!?
Futures.transformAsync(
Futures.transformAsync(getRankingType("JP"),
dao::listByRanking,
executor),
ids -> Futures.allAsList(ids.stream()
.map(client::getUserById)
.collect(toList())),
executor);
これが複雑になっていくと!?
Futures.transformAsync(
Futures.transformAsync(getRankingType("JP"),
dao::listByRanking,
executor),
ids -> Futures.allAsList(ids.stream()
.map(client::getUserById)
.collect(toList())),
executor);
これが複雑になっていくと!?
Futures.transformAsync(
Futures.transformAsync(getRankingType("JP"),
dao::listByRanking,
executor),
ids -> Futures.allAsList(ids.stream()
.map(client::getUserById)
.collect(toList())),
executor);
Dagger Producers で複雑さを軽減
● Dagger
○ コンパイル時依存性を解決する DI フレームワーク
● Dagger Producers
○ 非同期な DI を実現
○ メソッドのリターンタイプを ListenableFuture<T> にして、
受取メソッドのパラメータを T にすると Dagger がよしなに
組み合わせてくれる
@ProducerModule
public static class RankingGraph {
@ProductionComponent(modules = { RankingGraph.class, ExecutorModule.class })
interface Component {
ListenableFuture<List<Item>> getRanking();
}
public RankingGraph(Service service, String country) {
...
}
@Produces
public ListenableFuture<String> getRankingType() {
return service.getRankingType(country);
}
@Produces
public ListenableFuture<List<String>> listByRanking(String type) {
return service.listByRanking(type);
}
@Produces
public ListenableFuture<List<Item>> listUsers(List<String> ids) {
return Futures.allAsList(ids.stream().map(service::getItemById).collect(toList()));
}
}
@ProducerModule
public static class RankingGraph {
@ProductionComponent(modules = { RankingGraph.class, ExecutorModule.class })
interface Component {
ListenableFuture<List<Item>> getRanking();
}
public RankingGraph(Service service, String country) {
...
}
@Produces
public ListenableFuture<String> getRankingType() {
return service.getRankingType(country);
}
@Produces
public ListenableFuture<List<String>> listByRanking(String type) {
return service.listByRanking(type);
}
@Produces
public ListenableFuture<List<Item>> listUsers(List<String> ids) {
return Futures.allAsList(ids.stream().map(service::getItemById).collect(toList()));
}
}
@ProducerModule
public static class RankingGraph {
@ProductionComponent(modules = { RankingGraph.class, ExecutorModule.class })
interface Component {
ListenableFuture<List<Item>> getRanking();
}
public RankingGraph(Service service, String country) {
...
}
@Produces
public ListenableFuture<String> getRankingType() {
return service.getRankingType(country);
}
@Produces
public ListenableFuture<List<String>> listByRanking(String type) {
return service.listByRanking(type);
}
@Produces
public ListenableFuture<List<Item>> listUsers(List<String> ids) {
return Futures.allAsList(ids.stream().map(service::getItemById).collect(toList()));
}
}
@ProducerModule
public static class RankingGraph {
@ProductionComponent(modules = { RankingGraph.class, ExecutorModule.class })
interface Component {
ListenableFuture<List<Item>> getRanking();
}
public RankingGraph(Service service, String country) {
...
}
@Produces
public ListenableFuture<String> getRankingType() {
return service.getRankingType(country);
}
@Produces
public ListenableFuture<List<String>> listByRanking(String type) {
return service.listByRanking(type);
}
@Produces
public ListenableFuture<List<Item>> listUsers(List<String> ids) {
return Futures.allAsList(ids.stream().map(service::getItemById).collect(toList()));
}
}
Dagger が生成したコード
呼び出しコード
ListenableFuture<List<Item>> result =
DaggerRankingGraph_Component
.builder()
.rankingGraph(new RankingGraph(service, "JP"))
.build()
.getRanking();
Dagger を使ったメリット
● transformAsync 等でのネストが減った
● 発火スレッドが全て ExecutorModule で指定した executor で
始まる
● Convention 化し易い
● メソッド毎にバラバラで書いても、コンパイルタイムで揃ってる
か検査してくれる
● 実際 Guava ドキュメントも勧めてる(?)
これで一通り完成
Thread 数の減少
Latency の改善
Latency の改善
CPU 利用率の改善
CompletableFutureが増えてきた
● CompletableFuture は Java 8 で追加されてるので、
ライブラリ開発者は優先に使い始める
● けどコードベースはほとんど Guava ListenableFuture
● Dagger もがんがん使っている
● Spotify の Future-extra の CompletableFutureExtra で
ListenableFuture に変換
なぜ RxJava2 に移行した?
● ListenableFuture だけで組み合わせが書きやすくない
○ Guava 23 で FluentFuture がある
● Dagger Producers
○ Module の再利用が大変だった
○ 微妙に読みやすくない
● RxJava2 がそろそろ安定してそうだった
RxJava2
● Java VM implementation of Reactive Extensions
● A library for composing asynchronous and event-based
programs by using observable sequences.
RxJava2
● Single<T>
○ 1 個のデータ
○ CompletableFuture<T> で中身は絶対 null ではない
● Maybe<T>
○ 空っぽか1個のデータ
○ CompletableFuture<T> で中身は null かも知れない
● Completable
○ 空っぽ
○ CompletableFuture<Void>
RxJava2
● Observable<T>
○ 0 から n 個のデータ
○ backpressure なし
○ 基本サーバサイドでは使わない
● Flowable<T>
○ 0 から n 個のデータ
○ backpressure あり
RxJava2 でサーバサイド
● サーバでリモートアクセスは基本 1 リクエスト/1レスポンスな
ので、Single<T>、Maybe<T> と Completable で API を設計
できる
RxJava2 で設計した API
class UserDao {
public Single<User> get(String id){...}
public Maybe<User> find(String id){...}
public Completable delete(String id){...}
public Flowable<User> listAll(){...}
public Single<List<User>> listAllSingle(){...}
}
JDBC のアクセスを RxJava2 に
ListeningExecutorService asyncExecutor;
public <E> Single<List<E>> selectListRx(
String statement, Object parameter) {
return toSingle(asyncExecutor.submit(() ->
delegate.selectList(statement, parameter)));
}
MongoDB を RxJava2 に
Public Single<List<Model>> listRx(
String id, int offset, int limit) {
SettableFuture<List<Model>> future =
SettableFuture.create();
collection.find(eq(ID, id))
.skip(offset)
.limit(limit).into(list, (result, t) -> {
if (t != null) { future.setException(t); }
else { future.set(result); }
});
return toSingle(future);
}
MongoDB を RxJava2 に
● 実際 MongoDB には reactive extension 対応の Driver があ
るので、それを RxJava2 化すればいい
Public Single<List<Model>> listRx(
String id, int offset, int limit) {
return Flowable.fromPublisher(
collection.find(eq(ID, id))
.skip(offset)
.limit(limit))
.toList();
}
Retrofit と RxJava2CallAdapterFactory
● Retrofit の戻り値の型をRxJava2 に
public interface GitHubService {
@GET("users/{user}/repos")
Single<List<Repo>> listRepos(
@Path("user") String user);
}
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addCallAdapterFactory(
RxJava2CallAdapterFactory.create())
.build();
非同期の組み合わせ
Single<List<User>> ranking =
api.getRankingType("JP")
// Single<List<String>>
.flatMapSingle(this::listByRanking)
// Flowable<String>
.flatterAsFlowable(list -> list)
.concatMapEager(this::getUserById)
.toList();
@ProducerModule
public static class RankingGraph {
@ProductionComponent(modules = { RankingGraph.class, ExecutorModule.class })
interface Component {
ListenableFuture<List<Item>> getRanking();
}
public RankingGraph(Service service, String country) {
...
}
@Produces
public ListenableFuture<String> getRankingType() {
return service.getRankingType(country);
}
@Produces
public ListenableFuture<List<String>> listByRanking(String type) {
return service.listByRanking(type);
}
@Produces
public ListenableFuture<List<Item>> listUsers(List<String> ids) {
return
Futures.allAsList(ids.stream().map(service::getItemById).collect(toList()));
}
}
ListenableFuture<List<Item>> result =
DaggerRankingGraph_Component
.builder()
.rankingGraph(new RankingGraph(service, "JP"))
.build()
.getRanking();
リトライ
api.listByRanking("JP")
.retry(10)
.flatMap({
...
})... //
リトライ
api.listByRanking("JP")
.retryWhen(throwableFlowable -> {
return throwableFlowable.flatMap(thrown -> {
if (thrown instanceof IOException) {
return Flowable.timer(1000,
MILLISECONDS);
}
return Flowable.error(thrown);
}).flatMap({
...
})... //
移行で大変だったとこ
● ListenableFuture と Dagger で慣れ始めたのに、またかよ...
○ RxJava2 は Stream API な感じでつなげていけるので、で
きれば同じような感覚で開発してもらいたい
○ Project Reactor とかもあるし、似てるようなプログラミング
手法がでてくる
移行で大変だったとこ
● RxJava は null を容赦しない !
○ Flowable/Single/Maybe に null を入れたら、NPE !
Single.just("koji")
.map(id -> null) // NPE !!!
...//
移行で大変だったとこ
● Eager vs Lazy
○ Future<User> getUser(String id)
■ 呼んだ瞬間に発火
○ Single<User> getUser(String id)
■ 戻り値に subscribe した時に発火
■ でも RxJava2 と Future 変換があるので、実はそうで
もない
移行で大変だったとこ
● subscribe は絶対 Spring web controller と thrift handler で
呼ぶ
○ 他の層での subscribeOn は基本出現しない方向
移行で大変だったとこ
● Flowable の flatMap vs concatEagerMap
○ 順番守りたいなら concatEagerMap
http://www.nurkiewicz.com/2017/08/flatmap-vs-concatmap-vs-co
ncatmapeager.html
Flowable.just("koji", "kishida", "tempo")
.flatMapSingle(id -> api.fetchUser(id))
...//
Flowable.just("koji", "kishida", "tempo")
.concatMapEager(id ->
api.fetchUser(id).toFlowable())
...//
移行で大変だったとこ
● 複数回 subscribe 問題
Single<User> user = client.getUser("1234");
Single<Profile> first = user.map(...)...;
Single<Company> second = user.map(...)...;
Single.zip(first, second, (profile, address) -> {
...
})...;
移行で大変だったとこ
● 複数回 subscribe 問題
Single<User> user = client.getUser("1234").cache();
Single<Profile> first = user.map(...)...;
Single<Company> second = user.map(...)...;
Single.zip(first, second, (profile, address) -> {
...
})...;
移行で大変だったとこ
● 同じく発火スレッド問題
api.getRankingType("JP")
.flatMap(type -> getRanking(type)) // どの thread?
.flatMap(ids -> xxx) // どの thread?
.map(...)
.... // 色々
移行で大変だったとこ
● 次に非同期がある前に必ず observeOn
api.getRankingType("1234")
.ovserveOn(Schedulers.from(executor))
.flatMap(type -> getRanking(type))
.ovserveOn(Schedulers.from(executor))
.flatMap(ids -> xxx)
.map(...) // map 且つ重くないならいらない
.ovserveOn(Schedulers.from(executor))
  .flatMap(...)
.... // 色々
その他
● RxJava2 と Java8 CompletableFuture の変換ライブラ
リ
○ akarnokd/RxJava2Jdk8Interop
● Debugging と色々な便利 operators/transformers
○ akarnokd/RxJava2Extensions
例えば filterAsync
Flowable.just(...)
.compose(FlowableTransformers.filterAsync(
user -> {
return dao.isAvailable(user);
}))
.map(...)
...
これから...
● Java には extension method みたいなものがないので、
filterAsync みたいな compose でするしかない
● 複雑な組み合わせ以外は、async/await みたいなものがほし
い
● ……..kotlin かな?
資料とか
● Reactive Programming with RxJava
● Going Reactive with Spring 5 & Project Reactor
○ Devoxx youtube: https://youtu.be/yAXgkSlrmBA
Q&A

Contenu connexe

Tendances

オブジェクト指向エクササイズのススメ
オブジェクト指向エクササイズのススメオブジェクト指向エクササイズのススメ
オブジェクト指向エクササイズのススメ
Yoji Kanno
 
WebSocketのキホン
WebSocketのキホンWebSocketのキホン
WebSocketのキホン
You_Kinjoh
 
イベント・ソーシングを知る
イベント・ソーシングを知るイベント・ソーシングを知る
イベント・ソーシングを知る
Shuhei Fujita
 
日本語テストメソッドについて
日本語テストメソッドについて日本語テストメソッドについて
日本語テストメソッドについて
kumake
 

Tendances (20)

ツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところ
ツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところ
ツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところ
 
例外設計における大罪
例外設計における大罪例外設計における大罪
例外設計における大罪
 
RLSを用いたマルチテナント実装 for Django
RLSを用いたマルチテナント実装 for DjangoRLSを用いたマルチテナント実装 for Django
RLSを用いたマルチテナント実装 for Django
 
OpenAPI 3.0でmicroserviceのAPI定義を試みてハマった話
OpenAPI 3.0でmicroserviceのAPI定義を試みてハマった話OpenAPI 3.0でmicroserviceのAPI定義を試みてハマった話
OpenAPI 3.0でmicroserviceのAPI定義を試みてハマった話
 
SpringBootTest入門
SpringBootTest入門SpringBootTest入門
SpringBootTest入門
 
JavaでCPUを使い倒す! ~Java 9 以降の CPU 最適化を覗いてみる~(NTTデータ テクノロジーカンファレンス 2019 講演資料、2019...
JavaでCPUを使い倒す! ~Java 9 以降の CPU 最適化を覗いてみる~(NTTデータ テクノロジーカンファレンス 2019 講演資料、2019...JavaでCPUを使い倒す! ~Java 9 以降の CPU 最適化を覗いてみる~(NTTデータ テクノロジーカンファレンス 2019 講演資料、2019...
JavaでCPUを使い倒す! ~Java 9 以降の CPU 最適化を覗いてみる~(NTTデータ テクノロジーカンファレンス 2019 講演資料、2019...
 
なぜ、いま リレーショナルモデルなのか(理論から学ぶデータベース実践入門読書会スペシャル)
なぜ、いま リレーショナルモデルなのか(理論から学ぶデータベース実践入門読書会スペシャル)なぜ、いま リレーショナルモデルなのか(理論から学ぶデータベース実践入門読書会スペシャル)
なぜ、いま リレーショナルモデルなのか(理論から学ぶデータベース実践入門読書会スペシャル)
 
怖くないSpring Bootのオートコンフィグレーション
怖くないSpring Bootのオートコンフィグレーション怖くないSpring Bootのオートコンフィグレーション
怖くないSpring Bootのオートコンフィグレーション
 
オブジェクト指向エクササイズのススメ
オブジェクト指向エクササイズのススメオブジェクト指向エクササイズのススメ
オブジェクト指向エクササイズのススメ
 
Swagger ではない OpenAPI Specification 3.0 による API サーバー開発
Swagger ではない OpenAPI Specification 3.0 による API サーバー開発Swagger ではない OpenAPI Specification 3.0 による API サーバー開発
Swagger ではない OpenAPI Specification 3.0 による API サーバー開発
 
WebSocketのキホン
WebSocketのキホンWebSocketのキホン
WebSocketのキホン
 
イベント・ソーシングを知る
イベント・ソーシングを知るイベント・ソーシングを知る
イベント・ソーシングを知る
 
関数型プログラミングのデザインパターンひとめぐり
関数型プログラミングのデザインパターンひとめぐり関数型プログラミングのデザインパターンひとめぐり
関数型プログラミングのデザインパターンひとめぐり
 
日本語テストメソッドについて
日本語テストメソッドについて日本語テストメソッドについて
日本語テストメソッドについて
 
今からでも遅くないDBマイグレーション - Flyway と SchemaSpy の紹介 -
今からでも遅くないDBマイグレーション - Flyway と SchemaSpy の紹介 -今からでも遅くないDBマイグレーション - Flyway と SchemaSpy の紹介 -
今からでも遅くないDBマイグレーション - Flyway と SchemaSpy の紹介 -
 
Dockerからcontainerdへの移行
Dockerからcontainerdへの移行Dockerからcontainerdへの移行
Dockerからcontainerdへの移行
 
Marp Tutorial
Marp TutorialMarp Tutorial
Marp Tutorial
 
磯野ー!関数型言語やろうぜー!
磯野ー!関数型言語やろうぜー!磯野ー!関数型言語やろうぜー!
磯野ー!関数型言語やろうぜー!
 
世界一わかりやすいClean Architecture
世界一わかりやすいClean Architecture世界一わかりやすいClean Architecture
世界一わかりやすいClean Architecture
 
SPAセキュリティ入門~PHP Conference Japan 2021
SPAセキュリティ入門~PHP Conference Japan 2021SPAセキュリティ入門~PHP Conference Japan 2021
SPAセキュリティ入門~PHP Conference Japan 2021
 

Similaire à サーバーサイドでの非同期処理で色々やったよ

速くなければスマフォじゃない - インターンバージョン-
速くなければスマフォじゃない - インターンバージョン-速くなければスマフォじゃない - インターンバージョン-
速くなければスマフォじゃない - インターンバージョン-
Kazunari Hara
 
Data api workshop at Co-Edo
Data api workshop at Co-EdoData api workshop at Co-Edo
Data api workshop at Co-Edo
Yuji Takayama
 
勉強会force#4 Chatter Integration
勉強会force#4 Chatter Integration勉強会force#4 Chatter Integration
勉強会force#4 Chatter Integration
Kazuki Nakajima
 

Similaire à サーバーサイドでの非同期処理で色々やったよ (20)

ゆるふわJava8入門
ゆるふわJava8入門ゆるふわJava8入門
ゆるふわJava8入門
 
速くなければスマフォじゃない - インターンバージョン-
速くなければスマフォじゃない - インターンバージョン-速くなければスマフォじゃない - インターンバージョン-
速くなければスマフォじゃない - インターンバージョン-
 
Implementation patterns
Implementation patternsImplementation patterns
Implementation patterns
 
初めての Data api
初めての Data api初めての Data api
初めての Data api
 
「Windows 8 ストア アプリ開発 tips」 hokuriku.net vol.11 (2013年1月26日)
「Windows 8 ストア アプリ開発 tips」  hokuriku.net vol.11 (2013年1月26日)「Windows 8 ストア アプリ開発 tips」  hokuriku.net vol.11 (2013年1月26日)
「Windows 8 ストア アプリ開発 tips」 hokuriku.net vol.11 (2013年1月26日)
 
Scala on Hadoop
Scala on HadoopScala on Hadoop
Scala on Hadoop
 
WebRTC getStats - WebRTC Meetup Tokyo 5 LT
WebRTC getStats - WebRTC Meetup Tokyo 5 LTWebRTC getStats - WebRTC Meetup Tokyo 5 LT
WebRTC getStats - WebRTC Meetup Tokyo 5 LT
 
Data api workshop at Co-Edo
Data api workshop at Co-EdoData api workshop at Co-Edo
Data api workshop at Co-Edo
 
OSSから学ぶSwift実践テクニック
OSSから学ぶSwift実践テクニックOSSから学ぶSwift実践テクニック
OSSから学ぶSwift実践テクニック
 
初めての Data api cms どうでしょう - 大阪夏の陣
初めての Data api   cms どうでしょう - 大阪夏の陣初めての Data api   cms どうでしょう - 大阪夏の陣
初めての Data api cms どうでしょう - 大阪夏の陣
 
Azure で Serverless 初心者向けタッチ&トライ
Azure で Serverless 初心者向けタッチ&トライAzure で Serverless 初心者向けタッチ&トライ
Azure で Serverless 初心者向けタッチ&トライ
 
Data apiで実現 進化するwebの世界
Data apiで実現 進化するwebの世界Data apiで実現 進化するwebの世界
Data apiで実現 進化するwebの世界
 
Entity Framework
Entity FrameworkEntity Framework
Entity Framework
 
初めての Data API CMS どうでしょう - 仙台編 -
初めての Data API   CMS どうでしょう - 仙台編 -初めての Data API   CMS どうでしょう - 仙台編 -
初めての Data API CMS どうでしょう - 仙台編 -
 
Rubyとクラウドサービスで実現したEC決済共通基盤@Ruby Association ビジネスセミナー201401
Rubyとクラウドサービスで実現したEC決済共通基盤@Ruby Association ビジネスセミナー201401Rubyとクラウドサービスで実現したEC決済共通基盤@Ruby Association ビジネスセミナー201401
Rubyとクラウドサービスで実現したEC決済共通基盤@Ruby Association ビジネスセミナー201401
 
Dynamic Data
Dynamic DataDynamic Data
Dynamic Data
 
Cubby 2008-09-06
Cubby 2008-09-06Cubby 2008-09-06
Cubby 2008-09-06
 
Rx java x retrofit
Rx java x retrofitRx java x retrofit
Rx java x retrofit
 
勉強会force#4 Chatter Integration
勉強会force#4 Chatter Integration勉強会force#4 Chatter Integration
勉強会force#4 Chatter Integration
 
Pfi Seminar 2010 1 7
Pfi Seminar 2010 1 7Pfi Seminar 2010 1 7
Pfi Seminar 2010 1 7
 

Plus de koji lin (18)

G1GC
G1GCG1GC
G1GC
 
Using armeria to write your RPC
Using armeria to write your RPCUsing armeria to write your RPC
Using armeria to write your RPC
 
使用 Java 上的 future/promise API
使用 Java 上的 future/promise  API使用 Java 上的 future/promise  API
使用 Java 上的 future/promise API
 
Annotation processing and code gen
Annotation processing and code genAnnotation processing and code gen
Annotation processing and code gen
 
Jcconf
JcconfJcconf
Jcconf
 
Use Lambdas in Android
Use Lambdas in AndroidUse Lambdas in Android
Use Lambdas in Android
 
docker intro
docker introdocker intro
docker intro
 
Java8 time
Java8 timeJava8 time
Java8 time
 
Java8 stream
Java8 streamJava8 stream
Java8 stream
 
Java8 lambda
Java8 lambdaJava8 lambda
Java8 lambda
 
Idea13
Idea13Idea13
Idea13
 
CompletableFuture
CompletableFutureCompletableFuture
CompletableFuture
 
Raspberry Pi with Java
Raspberry Pi with JavaRaspberry Pi with Java
Raspberry Pi with Java
 
Services you can use to monitor and analyze mobile app
Services you can use to monitor and analyze mobile appServices you can use to monitor and analyze mobile app
Services you can use to monitor and analyze mobile app
 
Programming with Threads in Java
Programming with Threads in JavaProgramming with Threads in Java
Programming with Threads in Java
 
JQuery
JQueryJQuery
JQuery
 
山頂洞人日記 - 回歸到最純樸的開發
山頂洞人日記 -  回歸到最純樸的開發山頂洞人日記 -  回歸到最純樸的開發
山頂洞人日記 - 回歸到最純樸的開發
 
Android Location-based應用開發分享
Android Location-based應用開發分享Android Location-based應用開發分享
Android Location-based應用開發分享
 

Dernier

Dernier (11)

NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
 
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアルLoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
 
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
 
新人研修 後半 2024/04/26の勉強会で発表されたものです。
新人研修 後半        2024/04/26の勉強会で発表されたものです。新人研修 後半        2024/04/26の勉強会で発表されたものです。
新人研修 後半 2024/04/26の勉強会で発表されたものです。
 
論文紹介:Video-GroundingDINO: Towards Open-Vocabulary Spatio-Temporal Video Groun...
論文紹介:Video-GroundingDINO: Towards Open-Vocabulary Spatio-Temporal Video Groun...論文紹介:Video-GroundingDINO: Towards Open-Vocabulary Spatio-Temporal Video Groun...
論文紹介:Video-GroundingDINO: Towards Open-Vocabulary Spatio-Temporal Video Groun...
 
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
 
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
 
LoRaWANスマート距離検出センサー DS20L カタログ LiDARデバイス
LoRaWANスマート距離検出センサー  DS20L  カタログ  LiDARデバイスLoRaWANスマート距離検出センサー  DS20L  カタログ  LiDARデバイス
LoRaWANスマート距離検出センサー DS20L カタログ LiDARデバイス
 
Observabilityは従来型の監視と何が違うのか(キンドリルジャパン社内勉強会:2022年10月27日発表)
Observabilityは従来型の監視と何が違うのか(キンドリルジャパン社内勉強会:2022年10月27日発表)Observabilityは従来型の監視と何が違うのか(キンドリルジャパン社内勉強会:2022年10月27日発表)
Observabilityは従来型の監視と何が違うのか(キンドリルジャパン社内勉強会:2022年10月27日発表)
 
論文紹介: The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games
論文紹介: The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games論文紹介: The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games
論文紹介: The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games
 
論文紹介:Selective Structured State-Spaces for Long-Form Video Understanding
論文紹介:Selective Structured State-Spaces for Long-Form Video Understanding論文紹介:Selective Structured State-Spaces for Long-Form Video Understanding
論文紹介:Selective Structured State-Spaces for Long-Form Video Understanding
 

サーバーサイドでの非同期処理で色々やったよ