kotlin-fest

0aac627116c6e2a87b9ae179500801df?s=47 stormcat24
August 25, 2018
13k

 kotlin-fest

Kotlinで愛でるMicroservices

0aac627116c6e2a87b9ae179500801df?s=128

stormcat24

August 25, 2018
Tweet

Transcript

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

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

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

  4. ϫΠͱ,PUMJO

  5. None
  6. IUUQTTQFBLFSEFDLDPNTUPSNDBUTQBSLGSBNFXPSLXJUILPUMJO

  7. IUUQTTQFBLFSEFDLDPNTUPSNDBUUPVSPGCVJMETZTUFNJOLPUMJO

  8. IUUQTTQFBLFSEFDLDPNTUPSNDBUOBUJWF

  9. 4QFDJBM5IBOLT ‣ IUUQCMPHTPVTIJNF ‣ ϒϩάͰαʔόαΠυ,PUMJO΍HSQDKBWBͷ஌ݟଟ਺ ‣ '3&4)-*7&ͷαʔόαΠυ,PUMJOΛਪਐ ‣ ݱࡏ͸"OESPJE΍ͬͯΔ ‣

    ࠓ೔͸Ո଒αʔϏεʢΩϟϯϓʣ⛺Ͱෆࡏ TPVTIJO TPVTIJ@OP[BXB
  10. ͕ጪґͨͭ͠΋ΓͰ࿩͠·͢ ˞ጪґϓϨθϯ

  11. 4FSWFS4JEF,PUMJOͯ͠Δͻͱ

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

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

  15. .JDSPTFSWJDFTͷཧ༝ ‣ Մ༻ੑͷ޲্ͱো֐ͷہॴԽ ‣ Ұ෦͕յΕͯ΋ɺΞϓϦέʔγϣϯશମͱͯ͠͸ΫϦςΟ ΧϧϛογϣϯΛ਱ߦͰ͖ΔΑ͏ʹ ‣ 4FSWJDF͝ͱʹҧ͏ݴޠɾٕज़Λར༻Ͱ͖Δ ‣ Ұ෦ͷ௅ઓతͳٕज़ͷ౤ೖ

    ‣ ΤϯδχΞͷϞνϕʔγϣϯʢ͜Εେ͖͍ʣ ‣ ΋ͪΖΜେมͳ͜ͱ΋͋Δ
  16. ‣ (PMBOH ,PUMJOͷϋΠϒϦουߏ੒ ‣ ഑৴γεςϜɺ௿ϨΠϠʔܥ͸(PMBOH ‣ ৽نͷ"1*͸جຊ,PUMJOͰ ‣ 3&45 H31$

    ‣ Ұ෦QVCMJDͳH31$ΤϯυϙΠϯτ͕͋Δ ݱࡏͷ'3&4)-*7&
  17. "SDIJUFDUVSF

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

  19. ϝϯόʔͷ৺ͷڣͼ ‣ /VMM҆શʂ ‣ ߴ֊ؔ਺΄͍͠ʂ ‣ ֦ுؔ਺ॻ͖͍ͨʂ ‣ EBUBDMBTT ‣

    ΢ν+BWB͔͚Δਓଟ͍ʂʢ͔͠͠+BWB͸ͳ͍Αͳʣ ‣ +BWB͔ͩΒH31$࢖͑Δ ‣ ,PUMJO͔Θ͍͍Α,PUMJO
  20. (P΋޷͖Ͱ͢Αʁ ɹɹɹɹɹɹɹɹɹʢదࡐదॴେࣄʣ

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

    ‣ ݱࡏɺ,PUMJO੡ͷ4FSWJDF͕໿ݸՔಇ
  22. ࠾༻͍ͯ͠Δ'SBNFXPSL ‣ 4QSJOH#PPU ‣ 4QBSL'SBNFXPSL

  23. 4QSJOH#PPU

  24. 4UBDL ‣ 4QSJOH#PPU4QSJOH'SBNFXPSL ‣ HSQDKBWB ‣ +BDLTPO ‣ %PNB ‣

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

  26. 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Ͱ͞Βʹγϡοͱ͢Δ
  27. 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ͷςετ
  28. 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<TaskModel>(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" } }
  29. H31$ ‣ 3&45GVM"1*ͷଞʹH31$ͷΤϯυϙΠϯτ΋༻ҙ ‣ (PPHMF͕։ൃͨ͠31$ϓϩτίϧͷҰͭ ‣ )551ϕʔε ‣ σʔλϑΥʔϚοτ͸1SPUPDPM#VGGFST͕ར༻͞ΕΔ͜ͱ ͕ଟ͍

    ‣ (PMBOH+BWB3VCZ/PEFKT1ZUIPO$0CK$%BSU ‣ .JDSPTFSWJDFTؒͷϓϩτίϧʹ͸͏͚ͬͯͭ
  30. H31$ͱ4QSJOH ‣ HSQDTQSJOHCPPUTUBSUFSͰ4QSJOH#PPUͱ࿈ܞ ‣ IUUQTHJUIVCDPN-PH/FUHSQDTQSJOHCPPUTUBSUFS ‣ 4QSJOHͷࢿ࢈Λ׆༻͠ͳ͕ΒH31$Ͱ͖Δ ‣ !(3QD4FSWJDFͷ"OOPUBUJPOΛ࢖͏

  31. 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$ϝιουͷఆٛ
  32. H31$4FSWFS class TaskBackendServer( private val getTaskService: GetTaskService, private val getTaskListService:

    GetTaskListService ) : TaskServiceGrpc.TaskServiceImplBase() { override fun getTaskService(request: TaskInbound?, responseObserver: StreamObserver<TaskOutbound>?) { val taskId = GRpcInboundValidator.validTaskInbound(request) // ૹ৴͢ΔΦϒδΣΫτͷߏங val task = getTaskService(GetTaskCommand(taskId.toLong())) val msg = TaskOutbound.newBuilder().setTaskId(taskId).build() // ݁ՌΛclient΁ responseObserver?.onNext(msg) responseObserver?.onCompleted() }
  33. 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<TaskOutbound, GrpcException>(outbound) } catch (e: Exception) { val status = Status.fromThrowable(e) Result.Failure<TaskOutbound, GrpcException>(status with status.description) } }.await().fold({ it }, { throw it }) DPSPVUJOFͰඇಉظॲཧΛγϯϓϧʹ
  34. HPPHMFQSPUPCVGXSBQQFSTͷ׆༻ ‣ QSPUPCVGW͔Β͸SFRVJSFEϑΟʔϧυ͕ແ͍ ‣ QSPUPCVGͰ0QUJPOBMͷҙຯ෇͚͕Ͱ͖ͳ͍ ‣ IUUQTHJUIVCDPNQSPUPDPMCVGGFSTQSPUPCVG ‣ HPPHMFQSPUPCVGͷNFTTBHFΛ࢖͏ ‣

    HPPHMFQSPUPCVG*OU7BMVF ‣ HPPHMFQSPUPCVG4USJOH7BMVF
  35. ஋͕͋Δ͔ͷҙຯ෇͚ message TaskListInbound { google.protobuf.UInt32Value page = 1; } val

    page = when { request.hasPage() -> request.page.value else -> DEFAULT_PAGE_LIMIT }
  36. 4QSJOH#PPUͱ,PUMJOॴײ ‣ ެࣜͳ,PUMJOαϙʔτຊ౰ʹ͋Γ͕͍ͨ ‣ 4QSJOHͷࢿ࢈΍ΤίγεςϜʹॿ͚ΒΕΔ ‣ ·ͣ͸3PVUFS'VODUJPO͔Β ‣ H31$ݺͼग़͠͸DPSPVUJOFͰ ‣

    HPPHMFQSPUPCVGXSBQQFSTͷ׆༻Λ ‣ IUUQTHJUIVCDPNTPVTIJOTQSJOHLPUMJOBQQMJDBUJPO
  37. 4QBSL'SBNFXPSL

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

    ىಈ͕ߴ଎
  39. ࠇ΂͜ຊͰ΋ ѻͬͯΔΑʂ

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

  41. 5SZ import spark.kotlin.* fun main(args: Array<String>) { val http: Http

    = ignite() http.get("/hello") { "Kotlin Fest!!" } } $ curl http://localhost:4567/hello Kotlin Fest!!
  42. $POUSPMMFS package io.stormcat.controller
 
 import spark.*
 
 class EchoController {


    
 val echo = Route { req, res ->
 "Hello, ${req.queryParams("name")}!"
 }
 }
  43. '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")
 }
 }
  44. 'JMUFST package io.stormcat
 
 import io.stormcat.controller.EchoController
 import io.stormcat.filter.ResponseHeaderFilter
 import spark.Spark.*


    
 fun main(args: Array<String>) {
 
 // filters
 after(ResponseHeaderFilter())
 
 // routing
 get("/echo", EchoController().echo)
 
 }

  45. %* 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)
 }
 }
  46. %*ʢ(VJDF fun main(args: Array<String>) {
 
 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੡ʣ ۪௚ʹ%*͍ͯ͘͠ελΠϧ
  47. ബ͍͔Β֦ுؔ਺͕େ׆༂͢Δ package io.stormcat.controller
 
 import spark.Request
 
 fun Request.authUser(): User

    {
 val user = this.attribute<User>("authUser")
 return user ?: throw RuntimeException("Authorization Required")
 } XSBQQFS΍IFMQFSΑΓɺؔ਺Λੜ΍ͤʂ
  48. 4QBSL'SBNFXPSLॴײ ‣ ߏ੒ɺϥΠϒϥϦ૊Έ߹Θͤͷࣗ༝͞ ‣ ۪௚͞͸͋Δ͕ɺϋϚΓϙΠϯτ΋গͳ͍ ‣ ,PUMJOͰΑΓ؆ૉʹॻ͚Δ͠ɺ֦ுؔ਺౳ʹΑͬͯΑΓڧ ྗʹ΋ͳΔ

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

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

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

  52. QSPUP؅ཧͷํ๏

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

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

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

    MPDLϑΝΠϧରԠ
  56. &YBNQMF QSPUP@PVUEJSUIJSEQBSUZ <<EFQFOEFODJFT>> UBSHFUHJUIVCDPNTUPSNDBUTFSWJDFBQSPUP CSBODINBTUFS JHOPSFE< IPHF  GVHBQSPUP 

    > <<EFQFOEFODJFT>> UBSHFUHJUIVCDPNTUPSNDBUTFSWJDFCQSPUP SFWJTJPOW  <<EFQFOEFODJFT>> UBSHFUHJUIVCDPNTUPSNDBUTFSWJDFD SFWJTJPOW QBUIQBUIQSPUPCVG
  57. QBDLBHJOHEFQMPZ

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

    $*Ͱ͸%PDLFSΠϝʔδͱ)FMN$IBSUΛ࿉੒ ‣ IFMNJOTUBMMVQHSBEFSFQPZPVS@LPUMJO@BQQ
  59. .POJUPSJOH ‣ +7..JDSPNFUFS 1SPNFUIFVT ‣ ֤4FSWJDFʹ.JDSPNFUFSͷΤϯυϙΠϯτΛ࢓ࠐΈɺ 1SPNFUIFVT͔ΒεΫϨΠϐϯά ‣ 1PEʢίϯςφ܈ʣ͕࡞੒͞ΕΔͱɺࣗಈతʹ؂ࢹର৅ͱ ͳΔ

  60. ·ͱΊ

  61. 4FSWFS4JEF,PUMJOͷॴײ ‣ ͱʹ͔࣮͘༻త ‣ ͳͥօ,PUMJOͰॻ͔ͳ͍ͷ͔ ‣ +BWB,PUMJOͷ৔߹ɺࣦ͏΋ͷ͸͋·Γͳ͍Ͱ͠ΐ͏ ‣ ,PUMJO੡'SBNFXPSLʹ߆Δඞཁ͸ແ͍ ‣

    ,UPS ,PEFJOFUD ‣ ٕज़બఆʹ໎ͬͨΒɺͱΓ͋͑ͣ4QSJOH#PPUબ΅͏
  62. .JDSPTFSWJDFTͱͯ͠΋,PUMJO ‣ 4QBSL'SBNFXPSLɺ,UPSͷΑ͏ͳϚΠΫϩϑϨʔϜϫʔΫ ͷ׆༂ͷ৔΋ଟ͍ ‣ HSQDKBWBͰ.JDSPTFSWJDFTͷੜ࢈ੑ޲্ ‣ ʮͪΐͬͱ,PUMJOͰ.JDSPTFSWJDFTݸ࡞ΔΘʯ͕ୈҰา ‣ ҆ఆͨ͠.JDSPTFSWJDFT͕΋ͨΒ͢΋ͷ

    ‣ εΫϥοϓϏϧυɺ௅ઓతٕज़ͷ౤ೖͷ͠΍͢͞ ‣ Ҿ͖ଓ͖ੵۃతʹ,PUMJOΛ׆༻͍͖ͯ͠·͢
  63. 5IBOLT 4FSWFS4JEF,PUMJO΍͍͖ͬͯ