Upgrade to Pro — share decks privately, control downloads, hide ads and more …

kotlin-fest

stormcat24
August 25, 2018
16k

 kotlin-fest

Kotlinで愛でるMicroservices

stormcat24

August 25, 2018
Tweet

Transcript

  1. ,PUMJOͰѪͰΔ
    .JDSPTFSWJDFT
    !TUPSNDBU
    ,PUMJO'FTULPUMJOGFTU

    View full-size slide

  2. XIPJT
    ‣ $ZCFS"HFOU *OD
    ‣ '3&4)-*7&5FDI-FBE
    ‣ IUUQTCMPHTUPSNDBUJP
    TUPSNDBU

    View full-size slide

  3. %PDLFS,VCFSOFUFT
    ࣮ફίϯςφ։ൃೖ໳
    ຊ೔ൃച
    "NB[PO,JOEMF (JIZP%JHJUBM1VCMJTIJOH
    ָఱϒοΫεɺϤυόγυοτίϜ
    )POUPɺ%..DPNFUD
    ʢٕज़ධ࿦ࣾʣ
    ˞෺ཧຊɺॳ೔Ͱ"NB[POೖՙ଴ͪ

    View full-size slide

  4. ϫΠͱ,PUMJO

    View full-size slide

  5. IUUQTTQFBLFSEFDLDPNTUPSNDBUTQBSLGSBNFXPSLXJUILPUMJO

    View full-size slide

  6. IUUQTTQFBLFSEFDLDPNTUPSNDBUUPVSPGCVJMETZTUFNJOLPUMJO

    View full-size slide

  7. IUUQTTQFBLFSEFDLDPNTUPSNDBUOBUJWF

    View full-size slide

  8. 4QFDJBM5IBOLT
    ‣ IUUQCMPHTPVTIJNF
    ‣ ϒϩάͰαʔόαΠυ,PUMJO΍HSQDKBWBͷ஌ݟଟ਺
    ‣ '3&4)-*7&ͷαʔόαΠυ,PUMJOΛਪਐ
    ‣ ݱࡏ͸"OESPJE΍ͬͯΔ
    ‣ ࠓ೔͸Ո଒αʔϏεʢΩϟϯϓʣ⛺Ͱෆࡏ
    TPVTIJO
    TPVTIJ@OP[BXB

    View full-size slide

  9. ͕ጪґͨͭ͠΋ΓͰ࿩͠·͢
    ˞ጪґϓϨθϯ

    View full-size slide

  10. 4FSWFS4JEF,PUMJOͯ͠Δͻͱ

    View full-size slide

  11. 4FSWFS4JEF,PUMJO΍͍͖ͬͯ
    ͱ͍͏ϞνϕʔγϣϯΛߴΊͯؼͬͯ΄͍͠

    View full-size slide

  12. ‣ ϥΠϒ഑৴αʔϏεʢʙʣ
    ‣ υϝΠϯมߋճɺαʔϏε໊มߋճ
    ‣ .JDSPTFSWJDFT"SDIJUFDUVSF
    ‣ "NB[PO&$4,VCFSOFUFTPO"84

    View full-size slide

  13. .JDSPTFSWJDFTͷཧ༝
    ‣ Մ༻ੑͷ޲্ͱো֐ͷہॴԽ
    ‣ Ұ෦͕յΕͯ΋ɺΞϓϦέʔγϣϯશମͱͯ͠͸ΫϦςΟ
    ΧϧϛογϣϯΛ਱ߦͰ͖ΔΑ͏ʹ
    ‣ 4FSWJDF͝ͱʹҧ͏ݴޠɾٕज़Λར༻Ͱ͖Δ
    ‣ Ұ෦ͷ௅ઓతͳٕज़ͷ౤ೖ
    ‣ ΤϯδχΞͷϞνϕʔγϣϯʢ͜Εେ͖͍ʣ
    ‣ ΋ͪΖΜେมͳ͜ͱ΋͋Δ

    View full-size slide

  14. ‣ (PMBOH ,PUMJOͷϋΠϒϦουߏ੒
    ‣ ഑৴γεςϜɺ௿ϨΠϠʔܥ͸(PMBOH
    ‣ ৽نͷ"1*͸جຊ,PUMJOͰ
    ‣ 3&45H31$
    ‣ Ұ෦QVCMJDͳH31$ΤϯυϙΠϯτ͕͋Δ
    ݱࡏͷ'3&4)-*7&

    View full-size slide

  15. "SDIJUFDUVSF

    View full-size slide

  16. ,PUMJO࠾༻ͷഎܠ
    ‣ ࠷ॳ͸(PҰล౗ͩͬͨ
    ‣ "1*Λ(PͰ͍ͬͺ͍ॻ͍ͯ͘ͷ͸πϥΠʢے೑௧ʣ
    ‣ .JDSPTFSWJDFTͩ͠ɺݴޠʹറΒΕͳ͍͍ͯ͘͡Όͳ͍͔

    View full-size slide

  17. ϝϯόʔͷ৺ͷڣͼ
    ‣ /VMM҆શʂ
    ‣ ߴ֊ؔ਺΄͍͠ʂ
    ‣ ֦ுؔ਺ॻ͖͍ͨʂ
    ‣ EBUBDMBTT
    ‣ ΢ν+BWB͔͚Δਓଟ͍ʂʢ͔͠͠+BWB͸ͳ͍Αͳʣ
    ‣ +BWB͔ͩΒH31$࢖͑Δ
    ‣ ,PUMJO͔Θ͍͍Α,PUMJO

    View full-size slide

  18. (P΋޷͖Ͱ͢Αʁ
    ɹɹɹɹɹɹɹɹɹʢదࡐదॴେࣄʣ

    View full-size slide

  19. ։ൃํ਑
    ‣ "1*Ͱ͋Ε͹جຊ,PUMJOͰ
    ‣ "1*Ͱ΋(Pͱ਌࿨ੑͷߴ͍ྖҬͰ͋Ε͹(P
    ‣ ௿ϨΠϠʔɺಈը഑৴ؔ࿈ɺಠࣗϛυϧ΢ΣΞ౳ͷόοΫ
    ΤϯυγεςϜ͸Ҿ͖ଓ͖(PͰ
    ‣ ೥ळ͔ΒҰ෦.JDSPTFSWJDFTͰ,PUMJO౤ೖ։࢝
    ‣ ݱࡏɺ,PUMJO੡ͷ4FSWJDF͕໿ݸՔಇ

    View full-size slide

  20. ࠾༻͍ͯ͠Δ'SBNFXPSL
    ‣ 4QSJOH#PPU
    ‣ 4QBSL'SBNFXPSL

    View full-size slide

  21. 4UBDL
    ‣ 4QSJOH#PPU4QSJOH'SBNFXPSL
    ‣ HSQDKBWB
    ‣ +BDLTPO
    ‣ %PNB
    ‣ 3&45ͱH31$྆ํ
    ‣ LPUMJOYDPSPVUJOF

    View full-size slide

  22. 3&45GVM"1*
    ‣ 3PVUFS'VODUJPO%4-
    ‣ 4QSJOHͷ,PUMJOαϙʔτͰొ৔
    ‣ ैདྷͷ.7$ͷΑ͏ʹ"OOPUBUJPOଟ༻Ͱ͸ͳ͘ɺ,PUMJOͷ%4-
    Ͱॻ͘

    View full-size slide

  23. 3PVUFS'VODUJPO%4-
    @Configuration
    class TaskRoutes(private val taskHandler: TaskHandler,
    private val exceptionFilter: ExceptionFilter) {
    @Bean
    fun taskRouter() = router {
    (accept(APPLICATION_JSON) and "/api").nest {
    "/task".nest {
    POST("/", taskHandler::create)
    GET("/{id}", taskHandler::fetchByTaskId)
    PUT("/{id}", taskHandler::updateByTaskId)
    DELETE("/{id}", taskHandler::deleteByTaskId)
    PUT("/{id}/finish", taskHandler::finishByTaskId)
    }
    "/tasks".nest {
    GET("/", taskHandler::fetchAll)
    }
    }
    }.filter(exceptionFilter())
    }
    4QSJOH.7$ʹൺ΂ͯγϡοͱ͢Δ
    Ξϊςʔγϣϯ஍ࠈ͔Β୤٫
    ,PUMJOͰ͞Βʹγϡοͱ͢Δ

    View full-size slide

  24. 8FC5FTU$MJFOU
    @RunWith(SpringRunner::class)
    class TaskRoutesTest {
    lateinit var client : WebTestClient
    lateinit var taskHandler: TaskHandler
    lateinit var exceptionFilter: ExceptionFilter
    val mapper = ObjectMapper().registerModule(KotlinModule())
    @Before
    fun before() {
    taskHandler = mock(TaskHandler::class)
    exceptionFilter = ExceptionFilter()
    val taskRoutes = TaskRoutes(taskHandler, exceptionFilter)
    client = WebTestClient.bindToRouterFunction(taskRoutes.taskRouter()).build()
    }
    ɾɾɾ
    }
    8FC5FTU$MJFOUͰ3PVUFS'VODUJPOͷςετ

    View full-size slide

  25. 8FC5FTU$MJFOU
    @Test
    fun `GET Task`() {
    // mock
    `when`(taskHandler.fetchByTaskId(any())).thenReturn(ok().json().body(Mono.just(mockModel)))
    client.get().uri("/api/task/1")
    .accept(MediaType.APPLICATION_JSON_UTF8)
    .exchange()
    .expectStatus().isOk
    .expectBody()
    .consumeAsStringWith {
    val actual = mapper.readValue(it, TaskModel::class)
    actual.id shouldBe 1L
    actual.title shouldBe “kotlin fest"
    actual.createdAt shouldBe "2018-08-25T16:30:00Z"
    actual.updatedAt shouldBe "2018-08-25T16:30:00Z"
    }
    }

    View full-size slide

  26. H31$
    ‣ 3&45GVM"1*ͷଞʹH31$ͷΤϯυϙΠϯτ΋༻ҙ
    ‣ (PPHMF͕։ൃͨ͠31$ϓϩτίϧͷҰͭ
    ‣ )551ϕʔε
    ‣ σʔλϑΥʔϚοτ͸1SPUPDPM#VGGFST͕ར༻͞ΕΔ͜ͱ
    ͕ଟ͍
    ‣ (PMBOH+BWB3VCZ/PEFKT1ZUIPO$0CK$%BSU
    ‣ .JDSPTFSWJDFTؒͷϓϩτίϧʹ͸͏͚ͬͯͭ

    View full-size slide

  27. H31$ͱ4QSJOH
    ‣ HSQDTQSJOHCPPUTUBSUFSͰ4QSJOH#PPUͱ࿈ܞ
    ‣ IUUQTHJUIVCDPN-PH/FUHSQDTQSJOHCPPUTUBSUFS
    ‣ 4QSJOHͷࢿ࢈Λ׆༻͠ͳ͕ΒH31$Ͱ͖Δ
    ‣ !(3QD4FSWJDFͷ"OOPUBUJPOΛ࢖͏

    View full-size slide

  28. H31$QSPUPCVGͷ*%-
    service TaskService {
    rpc GetTaskService (TaskInbound) returns (TaskOutbound) {
    option (google.api.http) = {
    get: "/v1/task"
    };
    }
    }
    message TaskInbound {
    uint32 task_id = 1;
    }
    message TaskOutbound {
    uint32 task_id = 1;
    string title = 2;
    }
    ɹ΍ΓͱΓ͢ΔNFTTBHF
    H31$ϝιουͷఆٛ

    View full-size slide

  29. H31$4FSWFS
    class TaskBackendServer(
    private val getTaskService: GetTaskService,
    private val getTaskListService: GetTaskListService
    ) : TaskServiceGrpc.TaskServiceImplBase() {
    override fun getTaskService(request: TaskInbound?,
    responseObserver: StreamObserver?) {
    val taskId = GRpcInboundValidator.validTaskInbound(request)
    // ૹ৴͢ΔΦϒδΣΫτͷߏங
    val task = getTaskService(GetTaskCommand(taskId.toLong()))
    val msg = TaskOutbound.newBuilder().setTaskId(taskId).build()
    // ݁ՌΛclient΁
    responseObserver?.onNext(msg)
    responseObserver?.onCompleted()
    }

    View full-size slide

  30. H31$$MJFOU
    suspend fun getTask(taskId: Long): TaskOutbound =
    async(CommonPool) {
    try {
    val outbound = ShutdownLoan.using(getChannel(), { channel ->
    ɹɹɹɹɹɹɹɹɹ // ͕͜͜InboundϝοηʔδߏஙɺgRPCݺͼग़͠ݩ
    val msg = TaskInbound.newBuilder().setTaskId(taskId.toInt()).build()
    TaskServiceGrpc.newBlockingStub(channel).getTaskService(msg)
    })
    Result.Success(outbound)
    } catch (e: Exception) {
    val status = Status.fromThrowable(e)
    Result.Failure(status with status.description)
    }
    }.await().fold({ it }, { throw it })
    DPSPVUJOFͰඇಉظॲཧΛγϯϓϧʹ

    View full-size slide

  31. HPPHMFQSPUPCVGXSBQQFSTͷ׆༻
    ‣ QSPUPCVGW͔Β͸SFRVJSFEϑΟʔϧυ͕ແ͍
    ‣ QSPUPCVGͰ0QUJPOBMͷҙຯ෇͚͕Ͱ͖ͳ͍
    ‣ IUUQTHJUIVCDPNQSPUPDPMCVGGFSTQSPUPCVG
    ‣ HPPHMFQSPUPCVGͷNFTTBHFΛ࢖͏
    ‣ HPPHMFQSPUPCVG*OU7BMVF
    ‣ HPPHMFQSPUPCVG4USJOH7BMVF

    View full-size slide

  32. ஋͕͋Δ͔ͷҙຯ෇͚
    message TaskListInbound {
    google.protobuf.UInt32Value page = 1;
    }
    val page = when {
    request.hasPage() -> request.page.value
    else -> DEFAULT_PAGE_LIMIT
    }

    View full-size slide

  33. 4QSJOH#PPUͱ,PUMJOॴײ
    ‣ ެࣜͳ,PUMJOαϙʔτຊ౰ʹ͋Γ͕͍ͨ
    ‣ 4QSJOHͷࢿ࢈΍ΤίγεςϜʹॿ͚ΒΕΔ
    ‣ ·ͣ͸3PVUFS'VODUJPO͔Β
    ‣ H31$ݺͼग़͠͸DPSPVUJOFͰ
    ‣ HPPHMFQSPUPCVGXSBQQFSTͷ׆༻Λ
    ‣ IUUQTHJUIVCDPNTPVTIJOTQSJOHLPUMJOBQQMJDBUJPO

    View full-size slide

  34. 4QBSL'SBNFXPSL

    View full-size slide

  35. 4QBSL'SBNFXPSL
    ‣ +FUUZϕʔεͷ.JDSP8FC'SBNFXPSL
    ‣ +BWB,PUMJOαϙʔτ
    ‣ جຊతͳϧʔςΟϯάػೳ
    ‣ 8FC4PDLFUαϙʔτ
    ‣ ىಈ͕ߴ଎

    View full-size slide

  36. ࠇ΂͜ຊͰ΋
    ѻͬͯΔΑʂ

    View full-size slide

  37. CVJMEHSBEMF
    EFQFOEFODJFT\
    JNQMFNFOUBUJPODPNTQBSLKBWBTQBSLDPSF
    JNQMFNFOUBUJPODPNTQBSLKBWBTQBSLLPUMJOBMQIB
    ^
    ‣ TQBSLDPSF͚ͩͰ΋,PUMJOͰ։ൃՄೳ
    ‣ TQBSLLPUMJO͸%4-Ͱͷ࣮૷Λαϙʔτ͢ΔʢBMQIBʣ

    View full-size slide

  38. 5SZ
    import spark.kotlin.*
    fun main(args: Array) {
    val http: Http = ignite()
    http.get("/hello") {
    "Kotlin Fest!!"
    }
    }
    $ curl http://localhost:4567/hello
    Kotlin Fest!!

    View full-size slide

  39. $POUSPMMFS
    package io.stormcat.controller


    import spark.*


    class EchoController {


    val echo = Route { req, res ->

    "Hello, ${req.queryParams("name")}!"

    }

    }

    View full-size slide

  40. 'JMUFST
    package io.stormcat.filter


    import spark.Filter

    import spark.Request

    import spark.Response


    class ResponseHeaderFilter : Filter {


    override fun handle(request: Request, response: Response) {

    response.header("Server", "Kotlin Fest Server")

    }

    }

    View full-size slide

  41. 'JMUFST
    package io.stormcat


    import io.stormcat.controller.EchoController

    import io.stormcat.filter.ResponseHeaderFilter

    import spark.Spark.*


    fun main(args: Array) {


    // filters

    after(ResponseHeaderFilter())


    // routing

    get("/echo", EchoController().echo)


    }


    View full-size slide

  42. %*
    package io.stormcat.controller


    import com.google.inject.Inject

    import io.stormcat.service.UserService

    import spark.*


    class UserController @Inject constructor(

    val userService: UserService

    ) {


    val getUser = Route { req, res ->

    val userId = req.params("id")?.toLong()
    ?: throw RuntimeException("id is required")

    userService.getUser(userId)

    }

    }

    View full-size slide

  43. %*ʢ(VJDF

    fun main(args: Array) {


    val injector = Guice.createInjector(object : AbstractModule() {

    override fun configure() {

    bindConstant().annotatedWith(

    Names.named("apiDomain")).to("api.yourexample.com")

    bind(ObjectMapper::class.java)

    .toProvider(ObjectMapperProvider::class.java)

    .`in`(Singleton::class.java)

    }

    })


    val userController = injector.getInstance(UserController::class.java)


    // routing

    get("/user/:id", userController.getUser)


    }
    ˞,PEFJOͱ͍͏%*ϥΠϒϥϦ΋͋Γ·͢
    ʢ,PUMJO੡ʣ
    ۪௚ʹ%*͍ͯ͘͠ελΠϧ

    View full-size slide

  44. ബ͍͔Β֦ுؔ਺͕େ׆༂͢Δ
    package io.stormcat.controller


    import spark.Request


    fun Request.authUser(): User {

    val user = this.attribute("authUser")

    return user ?: throw RuntimeException("Authorization Required")

    }
    XSBQQFS΍IFMQFSΑΓɺؔ਺Λੜ΍ͤʂ

    View full-size slide

  45. 4QBSL'SBNFXPSLॴײ
    ‣ ߏ੒ɺϥΠϒϥϦ૊Έ߹Θͤͷࣗ༝͞
    ‣ ۪௚͞͸͋Δ͕ɺϋϚΓϙΠϯτ΋গͳ͍
    ‣ ,PUMJOͰΑΓ؆ૉʹॻ͚Δ͠ɺ֦ுؔ਺౳ʹΑͬͯΑΓڧ
    ྗʹ΋ͳΔ

    View full-size slide

  46. ӡ༻తͳ࿩
    ˞,PUMJO͋·Γؔ܎ͳ͍Ͱ͕͢ɺ
    ӡ༻্ͷෆ҆͸ͪΌΜͱղফ͓͖ͯ͠·͠ΐ͏

    View full-size slide

  47. HSQDKBWB$16௓ͶΔ໰୊
    ‣ ·Εʹɺಥવ$16ར༻཰͕௓ͶΔ
    ‣ αʔϏεΞ΢τͯ͠΋Լ͕Γ·ͤΜ
    ‣ ͜͏ͳΔͱ࠶ىಈ͔͠ͳ͍
    ‣ ݪҼ͸/FUUZͷ0CKFDU$MFBOFS5ISFBE
    ‣ IUUQTHJUIVCDPNHSQDHSQDKBWBJTTVFT

    View full-size slide

  48. HSQDKBWB$16௓ͶΔ໰୊
    HSQDKBWBҎ߱Λ࢖͍·͠ΐ͏

    View full-size slide

  49. QSPUP؅ཧͷํ๏

    View full-size slide

  50. QSPUPͲ͏؅ཧʁ
    ‣ .JDSPTFSWJDFTߏ੒ͰϦϙδτϦ͕ଟ͘ɺ֤ϦϙδτϦ
    ʹQSPUP܈͕഑ஔ͞ΕͯΔ
    ‣ ݺͼग़͠ݩͷ4FSWJDFଆʹɺݺͼग़͠ઌͷ4FSWJDFͷQSPUPΛ
    ഑ஔ
    ‣ QSPUPCVGHSBEMFQMVHJOͰɺQSPUP͔Βελϒࣗಈੜ੒

    View full-size slide

  51. ґଘ͢ΔQSPUPϑΝΠϧूΊ

    View full-size slide

  52. QSPUPEFQ
    ‣ IUUQTHJUIVCDPNTUPSNDBUQSPUPEFQ
    ‣ (JU)VC͔ΒQSPUPϑΝΠϧΛ͔͖ूΊΔπʔϧ
    ‣ (P੡
    ‣ 50.-ϕʔεͰґଘΛఆٛ
    ‣ MPDLϑΝΠϧରԠ

    View full-size slide

  53. &YBNQMF
    QSPUP@PVUEJSUIJSEQBSUZ
    <>
    UBSHFUHJUIVCDPNTUPSNDBUTFSWJDFBQSPUP
    CSBODINBTUFS
    JHOPSFE<
    IPHF
    GVHBQSPUP
    >
    <>
    UBSHFUHJUIVCDPNTUPSNDBUTFSWJDFCQSPUP
    SFWJTJPOW

    <>
    UBSHFUHJUIVCDPNTUPSNDBUTFSWJDFD
    SFWJTJPOW
    QBUIQBUIQSPUPCVG

    View full-size slide

  54. QBDLBHJOHEFQMPZ

    View full-size slide

  55. QBDLBHJOHEFQMPZ
    ‣ %PDLFSͷNVUJMTUBHFCVJMEͰ%PDLFSΠϝʔδͭ͘Δ
    ‣ ࣮ߦίϯςφʹ͸HSBEMFXCVJMEͰͰ͖ͨTJOHMF+BS
    ‣ ,VCFSOFUFTσϓϩΠ༻ʹɺ)FMN$IBSU ,VCFSOFUFTͷσϓ
    ϩΠ༻ύοέʔδʣʹύοέʔδ͢Δ
    ‣ $*Ͱ͸%PDLFSΠϝʔδͱ)FMN$IBSUΛ࿉੒
    ‣ IFMNJOTUBMMVQHSBEFSFQPZPVS@LPUMJO@BQQ

    View full-size slide

  56. .POJUPSJOH
    ‣ +7..JDSPNFUFS1SPNFUIFVT
    ‣ ֤4FSWJDFʹ.JDSPNFUFSͷΤϯυϙΠϯτΛ࢓ࠐΈɺ
    1SPNFUIFVT͔ΒεΫϨΠϐϯά
    ‣ 1PEʢίϯςφ܈ʣ͕࡞੒͞ΕΔͱɺࣗಈతʹ؂ࢹର৅ͱ
    ͳΔ

    View full-size slide

  57. 4FSWFS4JEF,PUMJOͷॴײ
    ‣ ͱʹ͔࣮͘༻త
    ‣ ͳͥօ,PUMJOͰॻ͔ͳ͍ͷ͔
    ‣ +BWB,PUMJOͷ৔߹ɺࣦ͏΋ͷ͸͋·Γͳ͍Ͱ͠ΐ͏
    ‣ ,PUMJO੡'SBNFXPSLʹ߆Δඞཁ͸ແ͍
    ‣ ,UPS ,PEFJOFUD
    ‣ ٕज़બఆʹ໎ͬͨΒɺͱΓ͋͑ͣ4QSJOH#PPUબ΅͏

    View full-size slide

  58. .JDSPTFSWJDFTͱͯ͠΋,PUMJO
    ‣ 4QBSL'SBNFXPSLɺ,UPSͷΑ͏ͳϚΠΫϩϑϨʔϜϫʔΫ
    ͷ׆༂ͷ৔΋ଟ͍
    ‣ HSQDKBWBͰ.JDSPTFSWJDFTͷੜ࢈ੑ޲্
    ‣ ʮͪΐͬͱ,PUMJOͰ.JDSPTFSWJDFTݸ࡞ΔΘʯ͕ୈҰา
    ‣ ҆ఆͨ͠.JDSPTFSWJDFT͕΋ͨΒ͢΋ͷ
    ‣ εΫϥοϓϏϧυɺ௅ઓతٕज़ͷ౤ೖͷ͠΍͢͞
    ‣ Ҿ͖ଓ͖ੵۃతʹ,PUMJOΛ׆༻͍͖ͯ͠·͢

    View full-size slide

  59. 5IBOLT
    4FSWFS4JEF,PUMJO΍͍͖ͬͯ

    View full-size slide