サーバーレスをJavaで組むためのSpring Cloud Function入門 / Introduction to Spring Cloud Function to build serverless in Java

サーバーレスをJavaで組むためのSpring Cloud Function入門 / Introduction to Spring Cloud Function to build serverless in Java

アプリケーションをSpring Bootを使って開発する方は多いと思います。慣れ親しんだSpringを、サーバーレスなアプリにも適用できれば便利そうですよね。それを実現するのが、Spring Cloud Function。Spring Cloud Functionのメリットや使い方を紹介します。

D5af8c48874265a2b461a8b3d990f395?s=128

Y. Sakamoto

July 18, 2019
Tweet

Transcript

  1. 1 Copyright © Acroquest Technology Co., Ltd. All rights reserved.

    サーバーレスをJavaで組むための Spring Cloud Function入門 2019-07-18 Acroquest Technology株式会社 阪本 雄一郎 関ジャバ’19 7月度
  2. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 2

    阪本 雄一郎 @phonypianist アクロクエストテクノロジー株式会社 シニアアーキテクト 兼 大阪事業所所長 • IoT、サーバーレスアーキテクチャ - 社会インフラ向け制御システム - 公共交通向け設備管理システム • BCI • 「Java本格入門」執筆
  3. Acroquestのミッション・ビジョン Copyright © Acroquest Technology Co., Ltd. All rights reserved.

    3 テクノロジストチームとして ビジネスの革新的価値創出に挑戦する ビジョン Acroquestの創り出す技術で 地球を感動で進化させる ミッション
  4. 「働きがいのある会社(GPTW)」 ランキング(従業員25~99人部門) 1位 を 3回 受賞 1位 1位 1位

  5. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 5

    はじめに
  6. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 6

    タイトルに「サーバーレス」 と書きましたが、 今回のポイントはサーバーレス ではありません
  7. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 7

    Spring Cloud Functionは、 あらゆる処理を 抽象化した「Function」として 統一的に扱うことにより システムを構築可能にすること です
  8. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 8

    Functionを実装しましょう。
  9. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 9

    おわり。
  10. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 10

    といったら史上最速で終わってしまうので・・・
  11. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 11

    イメージしやすいサーバーレスから 入っていくことにします
  12. ①Serverless Architectureのおさらい ②Spring Cloud Functionとは ③Hello Spring Cloud Function! ④Spring

    Cloud Function 「入門」 ⑤まとめ Copyright © Acroquest Technology Co., Ltd. All rights reserved. 12 目次
  13. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 13

    Serverless Architecture のおさらい ①
  14. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 14

    Serverless Architecture とは
  15. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 15

    サーバ管理を考慮しなくてよい アーキテクチャ
  16. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 16

    クラウドベンダーが管理 クラウドベンダーが管理 開発者は 業務ロジックのみに集中 Function
  17. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 17

    そうは問屋が卸さない
  18. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 18

    1. クラウドベンダー依存のコード 2. アプリケーションのライフサイクル がアンコントローラブル
  19. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 19

    AWS Lambdaの例 https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/get-started-step4-optional.html より引用 public class Hello { public String myHandler(int myCount, Context context) { LambdaLogger logger = context.getLogger(); logger.log("received : " + myCount); return String.valueOf(myCount); } }
  20. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 20

    Azure Functionsの例 https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-reference-java より引用 public class Function { public String echo(@HttpTrigger(name = "req", methods = {"post"}, authLevel = AuthorizationLevel.ANONYMOUS) String req, ExecutionContext context) { return String.format(req); } }
  21. public class Function { public String echo(@HttpTrigger(name = "req", methods

    = {"post"}, authLevel = AuthorizationLevel.ANONYMOUS) String req, ExecutionContext context) { return String.format(req); } } Copyright © Acroquest Technology Co., Ltd. All rights reserved. 21 Azure Functionsの例 https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-reference-java より引用 java.util.Functionと 同じ名前はやめよう これはいったい・・・ 型が合ってない
  22. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 22

    レビューしたいわけじゃない
  23. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 23

    AWS Lambdaと Azure Functionsで 書き方が違う
  24. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 24

    AWS Lambdaの例 public String myHandler(int myCount, Context context) { public String echo(@HttpTrigger(name = "req", methods = {"post"}, authLevel = AuthorizationLevel.ANONYMOUS) String req, ExecutionContext context) { Azure Functionsの例
  25. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 25

    クラウドベンダー依存の関数 ロジックもそれに引きずられる テストや動作確認がしづらくなる アジリティの低下
  26. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 26

    違いを気にせず書きたい 意識を「処理」に集中させたい テストしやすくしたい
  27. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 27

    そこで Spring Cloud Function
  28. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 28

    Spring Cloud Function とは ②
  29. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 29

    Spring Cloud Functionとは ロジックをすべて「Function」として 記述できるようにしたもの
  30. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 30

    Spring Cloud Functionの特徴 1. クラウド非依存な書き方 (呼び出し部分) 2. API提供 3. 定義による構築 勝手に選んだ
  31. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 31

    Spring Cloud Functionの特徴 1. クラウド非依存な書き方 (呼び出し部分) 2. API提供 3. 定義による構築
  32. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 32

    Spring Cloud Function 業 務 ロ ジ ッ ク (Function) AWS 基盤 Azure 基盤 クラウドに依存しない 関数定義 Open whisk 基盤 AWS Adapter Azure Adapter Openwhisk Adapter クラウド依存部分を 吸収
  33. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 33

    Spring Cloud Functionの特徴 1. クラウド非依存な書き方ができる (呼び出し部分) 2. API提供 3. 定義による構築
  34. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 34

    Function (Supplier/Consumer) はAPI公開される
  35. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 35

    FunctionでAPI部品を構築する
  36. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 36

    Spring Cloud Function 業 務 ロ ジ ッ ク (Function) curl POST http://localhost:8080/function {"message": "hello"} Bean名 Controller不要
  37. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 37

    Spring Cloud Function 業 務 ロ ジ ッ ク (Supplier) curl GET http://localhost:8080/supplier Bean名 Controller不要
  38. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 38

    Spring Cloud Function 業 務 ロ ジ ッ ク (Consumer) curl POST http://localhost:8080/consumer {"message": "hello"} Controller不要 Bean名
  39. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 39

    Spring Cloud Functionの特徴 1. クラウド非依存な書き方ができる (呼び出し部分) 2. API提供 3. 定義による構築
  40. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 40

    spring-cloud-function-compiler を追加することで、 application.ymlに Functionを定義できる
  41. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 41

    アプリケーション起動時に コンパイル→Bean登録 ※クラウドのサーバーレス基盤 (AWS Lambda/Azure Functions)は JREのため、この機能は使用できない
  42. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 42

    定義で記述されたFunctionも 普通のFunctionと同じ API公開される
  43. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 43

    Spring Cloud Function application.yml の upper 定 義 curl POST http://localhost:8080/upper {"message": "hello"} 定義名 (Bean名)
  44. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 44

    spring-cloud-function-task を追加することで、 application.ymlに Functionのタスクフローを定義できる
  45. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 45

    consumer function supplier function
  46. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 46

    ロジックを細分化し Functionに分離することで、 抽象度・独立性を高める
  47. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 47

    Hello Spring Cloud Function! ③
  48. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 48

    @SpringBootApplication public class Application { public static void main(String... args) { SpringApplication.run(Application.class, args); } } メインクラス(ローカル起動用) 普通のSpringBoot アプリと同じ
  49. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 49

    @Component("echo") public class EchoFunction implements Function<Flux<EchoRequest>, Flux<EchoResponse>> { @Override public Flux<EchoResponse> apply(Flux<EchoRequest> requestParam) { return requestParam.map( param -> new EchoResponse(param.getMessage())); } } java.util.Functionを実装 Supplier/ConsumerもOK reactor.core.publisher.Flux 業務ロジック
  50. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 50

    MANIFEST.MFのStart-Classに Applicationクラスを指定する spring-boot-maven-pluginと maven-shade-pluginがやってくれる
  51. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 51

    AWS Lambda 基 盤 SpringBootApiGateway RequestHandler EchoFunction API Gateway Lambda Spring Cloud Function SQSの場合は SpringBootRequestHandler 業務ロジック
  52. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 52

    テストは2通り 1. Springを使わない場合 2. Springを使う場合
  53. class EchoFunctionTest { @Test void echo() { EchoFunction echoFunction =

    new EchoFunction(); EchoRequest echoRequest = new EchoRequest(); echoRequest.setMessage("foo"); EchoResponse echoResponse = echoFunction.apply(echoRequest); assertThat(echoResponse.getMessage()).isEqualTo("foo"); } } Copyright © Acroquest Technology Co., Ltd. All rights reserved. 53 1. Springを使わない場合
  54. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 54

    class EchoFunctionTest { @Test void echoWithSpring() { System.setProperty("function.name", "echo"); try (SpringBootApiGatewayRequestHandler requestHandler = new SpringBootApiGatewayRequestHandler(Application.class)) { APIGatewayProxyRequestEvent requestEvent = new APIGatewayProxyRequestEvent().withBody("{¥"message¥":¥"foo¥"}"); APIGatewayProxyResponseEvent responseEvent = (APIGatewayProxyResponseEvent) requestHandler.handleRequest( requestEvent, null); assertThat(responseEvent.getBody()).isEqualTo("{¥"message¥":¥"foo¥"}"); } } } 呼び出す関数を指定 (デフォルトはfunction) 2. Springを使う場合 (API Gateway)
  55. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 55

    Springのライブラリを使ってみる
  56. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 56

    @SpringBootApplication public class Application { public static void main(String... args) { SpringApplication.run(Application.class); } @Bean WebClient webClient() { return WebClient.create(); } } SpringのWebClientを使ってみる SpringBootアプリと同じく Bean定義
  57. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 57

    SpringのWebClientを使ってみる @Component("dispatch") public class DispatchFunction implements Function<Flux<EchoRequest>, Flux<EchoResponse>> { private final WebClient webClient; private final Environment environment; public DispatchFunction(WebClient webClient, Environment environment) { this.webClient = webClient; this.environment = environment; } @Override public Flux<EchoResponse> apply(Flux<EchoRequest> requestParam) { String uri = environment.getProperty("app.echo.uri"); return requestParam.flatMap(param -> webClient .post().uri(uri).body(BodyInserters.fromObject(param)) .retrieve().bodyToMono(EchoResponse.class)); } } SpringBootアプリと同じく Autowired
  58. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 58

    Componentが増えると 初期化が遅くなる 自前で登録する
  59. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 59

    Componentを自前で登録する @SpringBootApplication public class Application implements ApplicationContextInitializer<GenericApplicationContext> { public static void main(String... args) { SpringApplication.run(Application.class, args); } Function<EchoRequest, EchoResponse> echo() { return new EchoFunction(); } @Override public void initialize(GenericApplicationContext context) { context.registerBean("echo", FunctionRegistration.class, () -> new FunctionRegistration<>(echo()) .type(FunctionType.from(EchoRequest.class).to(EchoResponse.class))); } } class EchoFunctionからは @Componentを削除しておく Functionを1つずつ登録
  60. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 60

    spring-cloud-function-compiler を使って Functionをapplication.ymlに定義
  61. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 61

    spring.cloud.function: compile: upper: type: function inputType: dto.Message outputType: dto.Message lambda: message -> new dto.Message(message.getText().toUpperCase()) Bean名 supplier/function/consumer application.yml FunctionをLambda式で記述
  62. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 62

    spring-cloud-function-task を使って タスクフローを定義
  63. spring.cloud.function: compile: textGenerator: type: supplier outputType: Flux<dto.Message> lambda: () ->

    Flux.fromIterable(List.of(new dto.Message("hoge"), new dto.Message("fuga"), new dto.Message("piyo"))) upper: type: function inputType: dto.Message outputType: dto.Message lambda: message -> new dto.Message(message.getText().toUpperCase()) decorator: type: function inputType: dto.Message outputType: dto.Message lambda: message -> new dto.Message("[" + message.getText() + "]") Copyright © Acroquest Technology Co., Ltd. All rights reserved. 63 application.yml printer: type: consumer inputType: dto.Message lambda: System.out::println task: supplier: textGenerator function: upper|decorator consumer: printer supplier/function/consumerと関連付ける 「|」を使って複数のFunctionをつなげる
  64. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 64

    アプリケーション起動直後に タスクフローが実行される
  65. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 65

    ロジックを細分化してFunctionに Queueから読み取り順次処理 用途例
  66. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 66

    もう使いこなせますよね!
  67. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 67

    Spring Cloud Function 「入門」 ④
  68. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 68

    「入門」=門に入る
  69. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 69

    Spring Cloud Function 宅にお邪魔します
  70. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 70

    AWS Lambda 基 盤 SpringBoot RequestHandler EchoFunction API Gateway Lambda Spring Cloud Function どこでFluxに? Spring Contextの初期化は?
  71. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 71

    人ん家を物色して盗むべし ソースを読んで理解すべし
  72. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 72

    public Object handleRequest( E event, Context context) { initialize(); Object input = convertEvent(event); Publisher<?> output = apply(extract(input)); return result(input, output); } SpringBootRequestHandler エントリーポイント 初期化 →親クラスの SpringFunctionInitializerへ 関数呼び出し
  73. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 73

    protected void initialize() { if (!this.initialized.compareAndSet( false, true)) { return; } logger.info("Initializing: " + this.configurationClass); SpringApplication builder = springApplication(); this.context = builder.run(); context.getAutowireCapableBeanFactory() .autowireBean(this); String name = context.getEnvironment() .getProperty("function.name"); if (name == null) { name = "function"; } if (this.catalog == null) { if (context.containsBean(name)) { this.function = getAndInstrumentFromContext(name); } } else { SpringFunctionInitializer SpringBoot 初期化 呼び出すFunction名 の取得
  74. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 74

    Springの初期化は 関数が初めて呼び出された 「直後」に行われる ※呼び出される直前ではない
  75. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 75

    public Object handleRequest( E event, Context context) { initialize(); Object input = convertEvent(event); Publisher<?> output = apply(extract(input)); return result(input, output); } protected Object convertEvent(E event) { return event; } private Flux<?> extract(Object input) { if (input instanceof Collection) { return Flux.fromIterable((Iterable<?>) input); } return Flux.just(input); } private Object result( Object input, Publisher<?> output) { List<O> result = new ArrayList<>(); for (Object value : Flux.from(output).toIterable()) { result.add(convertOutput(value)); } if (isSingleValue(input) && result.size() == 1) { return result.get(0); } return result; } SpringBootRequestHandler コレクションから Fluxに変換 Fluxからリストに 変換
  76. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 76

    API Gatewayの場合、 Fluxの効果はない
  77. @Override protected Object convertEvent(KinesisEvent event) { List<E> payloads = deserializePayloads(event.getRecords());

    if (functionAcceptsMessage()) { return wrapInMessages(payloads); } else { return payloads; } } Copyright © Acroquest Technology Co., Ltd. All rights reserved. 77 SpringBootKinesisEventHandler レコードのリストを取得 →Flux化 SpringBootRequestHandlerのサブクラス
  78. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 78

    Kinesis Data Streamの場合、 Fluxの効果はありそう
  79. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 79

    SpringFunctionInitializer SpringBootRequestHandler SpringBootApiGateway RequestHandler SpringBootKinesisEventHandler SpringApplication FunctionalSpringApplication 継承 依存 手抜きクラス図
  80. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 80

    まとめ ⑤
  81. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 81

    1. Spring Cloud Functionで、 ロジックを「Function」という形で抽象化して 扱える 2. Reactorが使える、ただし効果の有無は注意 3. お宅訪問重要! https://github.com/phonypianist/spring-cloud-function-example サンプルコードはここに置きました。
  82. Copyright © Acroquest Technology Co., Ltd. All rights reserved. 82

    Enjoy Spring Cloud Function & Code Reading! Evolve the Earth with Emotion of Technology