Ce diaporama a bien été signalé.
Nous utilisons votre profil LinkedIn et vos données d’activité pour vous proposer des publicités personnalisées et pertinentes. Vous pouvez changer vos préférences de publicités à tout moment.
1
Taro L. Saito
Treasure Data
October 17th, 2020
Scala Matsuri 2020
Scala For Everything
From Frontend to Backend Applicat...
Copyright 1995-2020 Treasure Data. All rights reserved.
About Me: Taro L. Saito
2
● Treasure Data
○ Principal Software Eng...
Copyright 1995-2020 Treasure Data. All rights reserved.
Today’s Topic
● There are a lot of libraries and frameworks for ba...
Copyright 1995-2020 Treasure Data. All rights reserved.
Airframe: Core Scala Modules of Treasure Data
● Airframe (GitHub: ...
Copyright 1995-2020 Treasure Data. All rights reserved.
Treasure Data: A Ready-to-Use Cloud Data Platform
5
Logs
Device
Da...
Copyright 1995-2020 Treasure Data. All rights reserved.
Scala.js Application: Presto Service Monitor
● Presto Usage: More ...
Copyright 1995-2020 Treasure Data. All rights reserved.
Server APIs and Library for Spark
Airframe DI
DataFrame MPC1
airfr...
Copyright 1995-2020 Treasure Data. All rights reserved.
Scala is Versatile
● Backend
○ Can leverage JVM ecosystem (e.g.,
n...
Copyright 1995-2020 Treasure Data. All rights reserved.
Bridging Scala and Scala.js with RPC
Program Function
Call
Return
...
Copyright 1995-2020 Treasure Data. All rights reserved.
Existing Approaches for Implementing RPC
● REST
○ Define function i...
Copyright 1995-2020 Treasure Data. All rights reserved.
API Design War
https://cloud.google.com/blog/products/api-manageme...
Copyright 1995-2020 Treasure Data. All rights reserved.
Why Not Using Scala for RPC Interface?
● Scala is a functional and...
Copyright 1995-2020 Treasure Data. All rights reserved.
Scala-First Approach: Airframe RPC
RPC InterfaceScala.js Client RP...
Copyright 1995-2020 Treasure Data. All rights reserved.
Airframe RPC Backend Is Pluggable
RPC Interface RPC Web Server
Gen...
Copyright 1995-2020 Treasure Data. All rights reserved.
Building Blocks of RPC Frameworks
● 1. Message serializer and dese...
Copyright 1995-2020 Treasure Data. All rights reserved.
MessagePack-Based Object Serialization
● MessagePack
○ A compact b...
Copyright 1995-2020 Treasure Data. All rights reserved.
airframe-codec: Pack/Unpack Interface
● Pack
○ Converting Data to ...
Copyright 1995-2020 Treasure Data. All rights reserved.
Pre-defined Codecs in airframe-codec
● Primitive Codecs
○ ByteCodec...
Copyright 1995-2020 Treasure Data. All rights reserved.
Serializing Complex Objects
Pack
Unpack
IntCodec
StringCodec
Doubl...
Copyright 1995-2020 Treasure Data. All rights reserved.
Defining RPC Interface with Scala
● Add @RPC annotation to a trait ...
Copyright 1995-2020 Treasure Data. All rights reserved.
Serializing Function Call Request
● Function call arguments
○ A se...
Copyright 1995-2020 Treasure Data. All rights reserved.
Implementing RPC Servers
● Extending the RPC interface trait
Serve...
Copyright 1995-2020 Treasure Data. All rights reserved.
● Finagle: Twitter’s HTTP server written in Scala
○ HTTP/1 based. ...
Copyright 1995-2020 Treasure Data. All rights reserved.
airframe-http: gRPC Backend
● Suited to internal microservices
● H...
Copyright 1995-2020 Treasure Data. All rights reserved.
Advanced Topic: Extending gRPC
● gRPC is data-format agonistic
fra...
Copyright 1995-2020 Treasure Data. All rights reserved.
RPC Performance Comparison (Greeter Service)
● Airframe RPC
○ serd...
Copyright 1995-2020 Treasure Data. All rights reserved.
sbt-airframe: Generating RPC Clients
● sbt-airframe plugin
○ Read ...
Copyright 1995-2020 Treasure Data. All rights reserved.
sbt-airframe: Generating Open API Schema
● RPC Interface -> Open A...
Copyright 1995-2020 Treasure Data. All rights reserved.
Making An RPC Call
RPCを実行する。サーバー、クライアント共にScalaで動いている

Program Func...
Copyright 1995-2020 Treasure Data. All rights reserved.
Scala.js Programming Tips
● Scala.js: Compiling Scala -> JavaScrip...
Copyright 1995-2020 Treasure Data. All rights reserved.
Debugging Scala.js Code with airframe-log
● Showing logs at the de...
Copyright 1995-2020 Treasure Data. All rights reserved.
Rendering DOM with Scala.js
● DOM element:
○ <div class=”container...
Copyright 1995-2020 Treasure Data. All rights reserved.
Rendering DOM
● Current Best Practice
○ Using Scala functions for
...
Copyright 1995-2020 Treasure Data. All rights reserved.
Complex DOM Rendering
● Airframe Rx
● RxElement interface
○ def re...
Copyright 1995-2020 Treasure Data. All rights reserved.
Airframe Rx
● Rx.variable
○ Re-render DOM if the variable is updat...
Copyright 1995-2020 Treasure Data. All rights reserved.
Reactive Stream Interface: Rx[X]
● Rx[X]
○ ReactiveX stream implem...
Copyright 1995-2020 Treasure Data. All rights reserved.
gRPC Streaming with Airframe Rx
● Interactive communication betwee...
Copyright 1995-2020 Treasure Data. All rights reserved.
Airframe Enables Scala-Oriented Development
38
RPC InterfaceScala....
Copyright 1995-2020 Treasure Data. All rights reserved.
Summary
● Scala is a powerful language that can consolidate existi...
Prochain SlideShare
Chargement dans…5
×
Prochain SlideShare
What to Upload to SlideShare
Suivant
Télécharger pour lire hors ligne et voir en mode plein écran

2

Partager

Télécharger pour lire hors ligne

Scala for Everything: From Frontend to Backend Applications - Scala Matsuri 2020

Télécharger pour lire hors ligne

Scala is a powerful language; You can build front-end applications with Scala.js, and efficient backend application servers for JVM. In this session, we will learn how to build everything with Scala by using Airframe OSS framework.

Airframe is a library designed for maximizing the advantages of Scala as a hybrid of object-oriented and functional programming language. In this session, we will learn how to use Airframe to build REST APIs and RPC (with Finagle or gRPC) services, and how to create frontend applications in Scala.js that interact with the servers using functional interfaces for dynamically updating web pages.

Scala for Everything: From Frontend to Backend Applications - Scala Matsuri 2020

  1. 1. 1 Taro L. Saito Treasure Data October 17th, 2020 Scala Matsuri 2020 Scala For Everything From Frontend to Backend Applications Scala無双: フロントエンドからバックエンドも全てScalaでなぎ倒せ

  2. 2. Copyright 1995-2020 Treasure Data. All rights reserved. About Me: Taro L. Saito 2 ● Treasure Data ○ Principal Software Engineer ○ Living in US for 5 years ● Ph.D. of Computer Science ○ Database systems, genome science. ● OSS: ○ Airframe, snappy-java (used in Parquet, Spark, etc.), sbt-sonatype, sbt-pack, etc. ● Book: 自己紹介

  3. 3. Copyright 1995-2020 Treasure Data. All rights reserved. Today’s Topic ● There are a lot of libraries and frameworks for backend and frontend applications ● Airframe ○ An OSS library collection that enables Scala-oriented development ● Goal: Forget everything other than Scala 3 Scala無双:Scala以外を全て駆逐する
 Airframe Scala.js Web Browsers JSON
  4. 4. Copyright 1995-2020 Treasure Data. All rights reserved. Airframe: Core Scala Modules of Treasure Data ● Airframe (GitHub: wvlet/airframe) ○ 20+ Common Utilities for Scala and Scala.js (Scala Matsuri 2019) ■ Logging, Dependency Injection (DI), JSON/MessagePack parser, etc. ○ Airframe RPC, Airframe Rx (Scala Matsuri 2020) ○ AirSpec ■ Testing framework for Scala and Scala.js (ScalaDays. Seattle, May 2021) 4 Knowledge Experiences Design Decisions Products 24/7 Services Business Values Programming OSS Outcome Airframe AirframeはScala.jsでも利用することを前提に開発されてきたOSSツール群

  5. 5. Copyright 1995-2020 Treasure Data. All rights reserved. Treasure Data: A Ready-to-Use Cloud Data Platform 5 Logs Device Data Batch Data PlazmaDB Table Schema Data Collection Cloud Storage Distributed Data Processing Jobs Job Management SQL Editor Scheduler Workflows Machine Learning Treasure Data OSS Third Party OSS Data トレジャーデータはすぐに使えるクラウドデータプラットフォーム

  6. 6. Copyright 1995-2020 Treasure Data. All rights reserved. Scala.js Application: Presto Service Monitor ● Presto Usage: More than 1,000,000 queries / day (2020), 4 regions 6 Scala.jsで開発されたアプリケーションの例

  7. 7. Copyright 1995-2020 Treasure Data. All rights reserved. Server APIs and Library for Spark Airframe DI DataFrame MPC1 airframe-codec airframe-msgpack Plazma Public API airframe-http airframe-finagle Airframe DI Airframe RPC airframe-fluentd Master Worker DesignSparkContext TDSparkContext TDSparkService MPC1 Reader/Writer IO Manager Airframe DI airframe-http airframe-config airframe-launcher airframe-jmx airframe-metrics airframe-control airframe-metrics td-spark.jarairframe-log airframe-log airframe-codec airframe-json Airframe 7 Sparkをサポートするために、ScalaとAirframeで作られたサービスが使われている
 ● MPC1: MessagePack Columnar Format ● See also td-spark internals (Spark Meetup Tokyo 2020)
  8. 8. Copyright 1995-2020 Treasure Data. All rights reserved. Scala is Versatile ● Backend ○ Can leverage JVM ecosystem (e.g., netty, gRPC, etc.) ● Frontend ○ Scala.js: ■ 1.0.0 is released in 2020 ● The current version is 1.3.0 ■ Compile Scala code into Javascripts ○ Out of scope of this talk ■ iOS/iPhone native applications ● How can we bridge server (Scala JVM) and client (Scala.js) applications? Scalaはバックエンド、フロントエンド開発に使える言語に。では両者をどう繋ぐ?
 Scala.js Web Browsers Serve-Client Interaction 8
  9. 9. Copyright 1995-2020 Treasure Data. All rights reserved. Bridging Scala and Scala.js with RPC Program Function Call Return Local Function Call (ideal) Program Function Serialize Deserialize Request Data Deserialize Response Data Serialize ローカル関数呼び出しと違い、実際にはネットワーク経由でのRPCが必要になる
 Remote Procedure Call (reality) Network Client Call Return Server Call Return 9 Scala.js Scala.js
  10. 10. Copyright 1995-2020 Treasure Data. All rights reserved. Existing Approaches for Implementing RPC ● REST ○ Define function interfaces with HTTP endpoints (e.g., GET/POST/PUT/DELETE, etc.) ○ REST web frameworks for Scala ■ Play framework, akka-http, Finatra, Finch, skinny-framework, etc. ● gRPC ○ Define function interfaces with Google’s ProtocolBuffers (.proto) schema language ○ Generate server and client code stubs from proto files ○ Scala wrappers: ■ ScalaPB, muScala, akka-grpc, etc. RPCを実装するには、RESTやgRPCなどが使われている
 Program Function Serialize Deserialize Request Data Deserialize Response Data Serialize Client Call Return Server Call Return 10
  11. 11. Copyright 1995-2020 Treasure Data. All rights reserved. API Design War https://cloud.google.com/blog/products/api-management/ understanding-grpc-openapi-and-rest-and-when-to-use-th em 11 ● Which API design should we choose? ● REST API ○ Designing good APIs over HTTP requires experiences and significant amount of time ● gRPC ○ Requires special software like code generators for using the ecosystem around ProtocolBuffers ● And also, there are too many choices for web frameworks しかし、APIデザイン、フレームワーク選びでの争いは付きない

  12. 12. Copyright 1995-2020 Treasure Data. All rights reserved. Why Not Using Scala for RPC Interface? ● Scala is a functional and statically-typed object-oriented programming language ○ Scala functions = RPC methods ○ Scala objects (statically typed) = RPC request/response types ● Scala is a perfect fit for RPC! 12 関数型で静的型付きオブジェクト指向言語のScalaはRPCインターフェースに適している

  13. 13. Copyright 1995-2020 Treasure Data. All rights reserved. Scala-First Approach: Airframe RPC RPC InterfaceScala.js Client RPC Web Server Generates Scala.js Web Application API Documentation 13 Airframe RPCはScalaをサーバー・クライアント共通インターフェースとして使う新手法
 RPC Call RPC Call Implements ● Forget about REST or ProtocolBuffers! ● Use Scala as a unified interface between server and clients Airframe
  14. 14. Copyright 1995-2020 Treasure Data. All rights reserved. Airframe RPC Backend Is Pluggable RPC Interface RPC Web Server Generates API Documentation 14 Airframe RPCのServer, Clientは差し替え可能
 Airframe RPC Call RPC Call Implements ● Backend server ○ Finagle (HTTP/1), gRPC (HTTP/2) ● Backend client ○ Finagle, gRPC, okhttp, Java URLConnection client etc. RPC Clients
  15. 15. Copyright 1995-2020 Treasure Data. All rights reserved. Building Blocks of RPC Frameworks ● 1. Message serializer and deserializer ● 2. Network data format ○ JSON (REST), Protobuf (gRPC), or MessagePack (Airframe RPC) ● 3. RPC interface language ○ REST API, Protobuf, or Scala ● 4. HTTP client and server implementation ○ Code generator 15 Program Function Serialize Deserialize Request Data Deserialize Response Data Serialize Client Call Return Server Call Return RPCフレームワークを作るのに必要な構成要素

  16. 16. Copyright 1995-2020 Treasure Data. All rights reserved. MessagePack-Based Object Serialization ● MessagePack ○ A compact binary format compatible with JSON Object Object Pack Unpack PackUnpack Server SideClient Side 16 Scala.js JSON Airframe MessagePackを使ってクライアントとサーバー間通信を行う

  17. 17. Copyright 1995-2020 Treasure Data. All rights reserved. airframe-codec: Pack/Unpack Interface ● Pack ○ Converting Data to MessagePack ● Unpack ○ Converting MessagePack to Data Input Output Pack Unpack PackUnpack 17 Airframe MessageCodec MessageCodec MessagePackを通したデータ変換インターフェースをScalaに適用

  18. 18. Copyright 1995-2020 Treasure Data. All rights reserved. Pre-defined Codecs in airframe-codec ● Primitive Codecs ○ ByteCodec, CharCodec, ShortCodec, IntCodec, LongCodec ○ FloatCodec, DoubleCodec ○ StringCodec ○ BooleanCodec ○ TimeStampCodec ● Collection Codec ○ ArrayCodec, SeqCodec, ListCodec, IndexSeqCodec, MapCodec, etc. ○ OptionCodec ● Java-specific Codec ○ UUIDCodec, FileCodec, ZonedDateTimeCodec, InstantCodec ○ JDBCResultSetCodec ● etc. 18 Scalaで必要なほぼ全てのデータ型へのマッピングをサポート
 Airframe
  19. 19. Copyright 1995-2020 Treasure Data. All rights reserved. Serializing Complex Objects Pack Unpack IntCodec StringCodec DoubleCodec MessageCodec.of[A] 19 オブジェクトの型に合わせてCodecを合成できる
 Serialize Deserialize JSON
  20. 20. Copyright 1995-2020 Treasure Data. All rights reserved. Defining RPC Interface with Scala ● Add @RPC annotation to a trait (= RPC interface) ● Define model classes (= data structures) using case classes ● Use the same interface and model classes between servers and clients RPC InterfaceをScalaで記述する
 20
  21. 21. Copyright 1995-2020 Treasure Data. All rights reserved. Serializing Function Call Request ● Function call arguments ○ A sequence of argument name -> value, …. ● Example: ○ Map(“person” -> Person(1, “leo”), “message” -> “Hello RPC!”) ○ Serialize this Map into MessagePack with airframe-codec ● HTTP Mapping ○ HTTP method: POST ○ Path: /(package name)/hello ■ e.g., /hello.api.v1/hello ○ Content body: serialized function call argument data 21 関数呼び出しの引数もシリアライズする

  22. 22. Copyright 1995-2020 Treasure Data. All rights reserved. Implementing RPC Servers ● Extending the RPC interface trait Serverの実装はRPC traitの継承
 22
  23. 23. Copyright 1995-2020 Treasure Data. All rights reserved. ● Finagle: Twitter’s HTTP server written in Scala ○ HTTP/1 based. Accessible from web browsers running Scala.js code airframe-http: Starting A Finagle Backend RPC Server 23 Airframe Scala.js airframe-rx-html airframe-http: FinagleバックエンドのHTTPサーバーを起動する
 airframe-http-finagle
  24. 24. Copyright 1995-2020 Treasure Data. All rights reserved. airframe-http: gRPC Backend ● Suited to internal microservices ● HTTP/2 based protocol ○ Multiplexing multiple RPC requests within a single connection ● No .proto file is required 24 Airframe airframe-http: gRPCバックエンドサーバーを起動する。ProtoBuf定義は必要ない

  25. 25. Copyright 1995-2020 Treasure Data. All rights reserved. Advanced Topic: Extending gRPC ● gRPC is data-format agonistic framework ○ To support Scala interface and MessagePack, we have two extension points in grpc-java. ● MethodDescriptor ○ Define gRPC endpoints corresponding to Scala functions ○ Register marshallers ● Request/ResponseMarshaller ○ Define how to encode/decode RPC request/response data 25 Airframe RPCはgrpc-javaを拡張してMessagePackデータの受け渡しをしている

  26. 26. Copyright 1995-2020 Treasure Data. All rights reserved. RPC Performance Comparison (Greeter Service) ● Airframe RPC ○ serde: MessagePack (airframe-codec) <-> Scala case classes ○ Finagle (HTTP1) or gRPC (HTTP2) ● ScalaPB ○ serde: Protobuf <-> Scala case classes ○ gRPC (HTTP2) ● grpc-java ○ serde: Protobuf <-> Java classes ○ gRPC (HTTP2) ● Notes ○ Using Finagle with HTTP2 had almost no benefit over HTTP1 ■ Multiplexing RPC requests over HTTP2 is the key performance factor ○ Overhead of ScalaPB ■ Scala Future (10% overhead) ■ Mapping Protobuf to Scala case classes (10% overhead) 26 gRPCはリクエスト多重送信により高速。MessagePackによるオーバーヘッドは少ない
 HTTP/1 HTTP/2 (gRPC)
  27. 27. Copyright 1995-2020 Treasure Data. All rights reserved. sbt-airframe: Generating RPC Clients ● sbt-airframe plugin ○ Read RPC interface classes ○ Generate HTTP client code for accessing the RPC server ○ OpenAPI schema generation support sbt-airframe Code Generation RPC Client Scala.js 27 Scala.js Client HTTP/gRPC Client Open API Spec Cross-Language RPC Client RPCクライアントを生成するsbt-airframeプラグイン

  28. 28. Copyright 1995-2020 Treasure Data. All rights reserved. sbt-airframe: Generating Open API Schema ● RPC Interface -> Open API schema YAML file ● Generating RPC Clients & Swagger Documentation 28 ScalaのインターフェースからOpenAPIのYAMLを生成する
 RPC Interface API Documentation Open API Spec (YAML) Cross-Language RPC Client Generate sbt-airframe API Documentation
  29. 29. Copyright 1995-2020 Treasure Data. All rights reserved. Making An RPC Call RPCを実行する。サーバー、クライアント共にScalaで動いている
 Program Function Serialize Deserialize Request Data Deserialize Response Data Serialize Client Call Response Server Call Response AirframeScala.js 29
  30. 30. Copyright 1995-2020 Treasure Data. All rights reserved. Scala.js Programming Tips ● Scala.js: Compiling Scala -> JavaScript ● Use Pure Scala Code ○ [NG] Java code ■ Dependent libraries also need to be pure Scala ● Airframe is carefully designed to minimize non-Scala dependencies ○ [NG] Annotation, runtime-reflection ○ [OK] Scala Macros (compile-time meta-programming) ● RPC ○ Asynchronous calls using Scala Future ○ Await(Future) is not supported ■ Because JS is a single-threaded model. ■ Need to properly chain Future operators with map, flatMap, rescue, etc. 30 Scala.jsでのプログラミングのコツ
 Scala.js
  31. 31. Copyright 1995-2020 Treasure Data. All rights reserved. Debugging Scala.js Code with airframe-log ● Showing logs at the developer console of the browser 31 airframe-logとブラウザのコンソールでScala.jsコードのデバッグ

  32. 32. Copyright 1995-2020 Treasure Data. All rights reserved. Rendering DOM with Scala.js ● DOM element: ○ <div class=”container”> …. </div> ○ scalajs-dom library has functions necessary for generating DOMs ● Previous approach: Converting XML literal into DOM elements ○ monadic-html, Binding.scala ○ But XML literal will be deprecated in Dotty (Scala 3.0) 32 Scala.jsでのDOMレンダリング手法には様々な手法がある

  33. 33. Copyright 1995-2020 Treasure Data. All rights reserved. Rendering DOM ● Current Best Practice ○ Using Scala functions for building DOM elements ● Option 1: Separation between DOM elements and attributes ○ ScalaTags, Slinky ○ Too many parentheses! ● Option 2: No separation between DOM elements and attributes ○ scalajs-react, Airframe Rx ○ More Scala-friendly syntax 33 DOMのelement, attributeを区別しない方が関数型に適している
 ScalaTags Airframe Rx
  34. 34. Copyright 1995-2020 Treasure Data. All rights reserved. Complex DOM Rendering ● Airframe Rx ● RxElement interface ○ def render(): RxElement ○ Support nesting with apply(...) ■ Embedding Scala functions and collections 34 airframe-rx-html: Scalaの関数をネストしてDOMをレンダリングする

  35. 35. Copyright 1995-2020 Treasure Data. All rights reserved. Airframe Rx ● Rx.variable ○ Re-render DOM if the variable is updated ● Gathering best practices from existing libraries ○ Type-safe DOM elements and attributes ■ ScalaTags, scalajs-react ○ Reactive streaming ■ Monix ■ Scala Collection ■ Scala.rx ○ Cancelable Interface ■ Cleanup DOM and event handlers when updating DOM ■ Monix, monadic-html 35 既存技術の良いところ取りをしたAirframe Rx

  36. 36. Copyright 1995-2020 Treasure Data. All rights reserved. Reactive Stream Interface: Rx[X] ● Rx[X] ○ ReactiveX stream implementation ○ e.g., Monix, Scala.Rx ● Reactive DOM Rendering ○ Rewriting DOM interactively upon upstream data change ● Reactive Operators ○ map, flatMap, filter, join, etc. ○ Almost the same with Scala collection APIs 36 Reactive stream interface Rx[X]でインタラクティブなDOMを作成

  37. 37. Copyright 1995-2020 Treasure Data. All rights reserved. gRPC Streaming with Airframe Rx ● Interactive communication between server and clients with reactive stream interface: Rx[A] 37 gRPC StreamingもRxインターフェースでサポートできる

  38. 38. Copyright 1995-2020 Treasure Data. All rights reserved. Airframe Enables Scala-Oriented Development 38 RPC InterfaceScala.js Client RPC Web Server Generates Scala.js Web Application API Documentation RPC Call RPC Call Implements Airframe AirframeでScala中心のアプリケーション開発を可能に
 ● Only need to learn how to write Scala for kick-starting client/server application development
  39. 39. Copyright 1995-2020 Treasure Data. All rights reserved. Summary ● Scala is a powerful language that can consolidate existing technologies ● Minimizing the learning cost of application development with Scala-First approach ○ Airframe OSS (GitHub: wvlet/airframe) is designed for this purpose 39 まとめ:既存技術の長所を兼ね備えることで、Scala無双を可能にするAirframe
 Airframe Scala.js Web Browsers JSON
  • koukamenashi

    Oct. 17, 2020
  • umablue

    Oct. 17, 2020

Scala is a powerful language; You can build front-end applications with Scala.js, and efficient backend application servers for JVM. In this session, we will learn how to build everything with Scala by using Airframe OSS framework. Airframe is a library designed for maximizing the advantages of Scala as a hybrid of object-oriented and functional programming language. In this session, we will learn how to use Airframe to build REST APIs and RPC (with Finagle or gRPC) services, and how to create frontend applications in Scala.js that interact with the servers using functional interfaces for dynamically updating web pages.

Vues

Nombre de vues

1 541

Sur Slideshare

0

À partir des intégrations

0

Nombre d'intégrations

323

Actions

Téléchargements

7

Partages

0

Commentaires

0

Mentions J'aime

2

×