简单、有趣的Kotlin异步微服务框架(一)_ 初始Ktor

3.1 通过embeddedServer方式构建

通过embeddedServer函数构建Ktor Server应用是一种最为简单的方式也是官方默认推荐使用的一种方式。embeddedServer函数是通过在代码中配置服务器参数并快速运行应用程序的简单方法,不需要额外配置文件。比如在下面的代码段中,它接收服务器容器引擎类型和端口参作为参数,传入Netty服务器容器引擎和端口8080,启动应用后就会在8080端口监听。

  • Application.kt

package com.mikyou.ktor.samplecom.mikyou.ktor.sample

import io.ktor.application.*
import io.ktor.http.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*

fun main(args: Array) {
embeddedServer(Netty, port = 8080) {//除了支持Netty还支持Jetty、Tomcat、CIO(Coroutine-based I/O)
routing {
get(“/”) {
call.respondText(“Hello Ktor”)
}
}
}.start(wait = true)
}

3.2 通过EngineMain方式构建

EngineMain方式则是选定的引擎启动服务器,并加载外部一个 application.conf 文件中指定的应用程序模块. 然后在 application.conf 配置文件中配置应用启动参数,比如服务监听端口等

  • Application.kt

package com.mikyou.ktor.sample

import io.ktor.application.*
import io.ktor.response.*
import io.ktor.routing.*

fun main(args: Array): Unit = io.ktor.server.netty.EngineMain.main(args)

fun Application.module(testing: Boolean = false) {//该module函数实际上是Application的扩展函数,要想该函数运行需要通过application.conf中配置该函数
routing {
get(“/”) {
call.respondText(“Hello Ktor”)
}
}
}

  • application.conf

ktor {
deployment {
port = 8080 //配置端口
}
application {
modules = [ com.mikyou.ktor.sample.ApplicationKt.module ] //配置加载需要加载的module函数
}
}

4. 如何架构一个成熟的Ktor应用

由上面可知构建一个简单的Ktor Server可以说是非常简单,然而要构建一个成熟的Ktor Server应用也是类似,主要是多了一些如何模块化组织业务模块和更清晰化去架构业务。 主要分为以下7个步骤: image.png

4.1 选择构建Server的方式

构建Ktor Server应用主要分为两种: 一种是通过embeddedServer方式构建,另一种则是通过EngineMain方式构建。 具体的选择使用方式参考上面第3节

4.2 选择Server Engine

要想运行Ktor服务器应用程序,就需要首先创建和配置服务器。服务器配置其中就包括服务器引擎配置,各种引擎特定的参数比如主机地址和启动端口等等。 Ktor支持大多数目前主流的Server Engine,其中包括:

  • Tomcat
  • Netty
  • Jetty
  • CIO(Coroutine-based I/O)

此外Ktor框架还提供一种类型引擎TestEngine专门供测试时使用。 要想使用上述指定的Server Engine,就需要添加Server Engine相关的依赖,Ktor是既支持Gradle来管理库的依赖也支持Maven来管理。

4.3 配置服务参数

配置服务引擎参数,由于构建Server方式不同,所以配置引擎参数也不一样。对于embeddedServer函数方式构建的Ktor应用可以直接通过代码函数参数方式指定,对于EngineMain方式则通过修改配置文件 application.conf 。

4.3.1 embeddedServer函数方式

fun main(args: Array) {
embeddedServer(Tomcat, port = 8080) {//配置了服务器引擎类型和启动端口
routing {
get(“/”) {
call.respondText(“Hello Ktor”)
}
}
}.start(wait = true)
}

//除了服务器引擎类型和启动端口的配置,还支持一些参数的配置

fun main() {
embeddedServer(Netty, port = 8080, configure = {
connectionGroupSize = 2 //指定用于接收连接的Event Group的大小
workerGroupSize = 5 //指定用于处理连接,解析消息和执行引擎的内部工作的Event Group的大小,
callGroupSize = 10 //指定用于运行应用程序代码的Event Group的大小
}) {
routing {
get(“/”) {
call.respondText(“Hello Ktor”)
}
}
}.start(wait = true)
}
//设置可以定制一个EngineEnvironment用于替代默认的ApplicationEngineEnvironment,我们可以通过源码可知,embeddedServer函数内部默认构建一个ApplicationEngineEnvironment。
fun main() {
embeddedServer(Netty, environment = applicationEngineEnvironment {
log = LoggerFactory.getLogger(“ktor.application”)
config = HoconApplicationConfig(ConfigFactory.load())

module {
main()
}

connector {
port = 8080
host = “127.0.0.1”
}
}).start(true)
}

4.3.2 EngineMain方式
  • 如果是选择EngineMain方式构建Server, 那么就需要通过修改 applicaton.conf

ktor {
application {
modules = [ com.mikyou.ktor.sample.ApplicationKt.module ] //配置加载需要加载的module模块,这里配置实际上就是Application中module扩展函数
}
}

//除了可以配置需要加载module模块,还可以配置端口或主机,SSL等
ktor {
deployment {
port = 8080 //配置端口
sslPort = 8443 //配置SSL端口
watch = [ http2 ]
}
application {
modules = [ com.mikyou.ktor.sample.ApplicationKt.module ] //配置加载需要加载的module模块
}
security {//配置SSL签名和密钥
ssl {
keyStore = build/test.jks
keyAlias = testkey
keyStorePassword = test
privateKeyPassword = test
}
}
}
//application.conf文件包含一个自定义jwt(Json Web Token)组,用于存储JWT设置。
ktor {
deployment {
port = 8080 //配置端口
sslPort = 8443 //配置SSL端口
watch = [ http2 ]
}
application {
modules = [ com.mikyou.ktor.sample.ApplicationKt.module ] //配置加载需要加载的module模块
}
security {//配置SSL签名和密钥
ssl {
keyStore = build/test.jks
keyAlias = testkey
keyStorePassword = test
privateKeyPassword = test
}
}
jwt {//JWT配置
domain = “https://jwt-provider-domain/”
audience = “jwt-audience”
realm = “ktor sample app”
}
}

  • 预定义属性

  • 命令行运行

可以使用command运行ktor的jar,并且指定端口

java -jar sample-app.jar -port=8080

可以通过config参数指定xxx.conf的路径

java -jar sample-app.jar -config=xxx.conf

还可以通过-P指定运行应用程序代码的Event Group的大小

java -jar sample-app.jar -P:ktor.deployment.callGroupSize=7

  • 代码中读取application.conf中的配置

代码中读取application.conf中配置是一件很实用的操作,比如连接数据库时配置都可以通过自定义属性来实现。比如下面这个例子:

ktor {
deployment {//预定义属性
port = 8889
host = www.youkmi.cn
}

application {
modules = [ com.mikyou.ApplicationKt.module ]
}

#LOCAL(本地环境)、PRE(预发环境)、ONLINE(线上环境)
env = LOCAL//自定义属性
security {//把db相关配置放入security,日志输出会对该部分内容用*进行隐藏处理
localDb {//自定义属性localDb
url = “jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai”
driver = “com.mysql.cj.jdbc.Driver”
user = “xxx”
password = “xxx”
}
remoteDb {//自定义属性remoteDb
url = “jdbc:mysql://192.168.0.101:3306/mydb?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai”
driver = “com.mysql.cj.jdbc.Driver”
user = “xxx”
password = “xxx”
}
}
}

在appliction.conf自定义了属性配置后,如何在Ktor代码获取呢?请看如下代码:

const val KEY_ENV = “ktor.env”

//自定义属性的key,就是根据配置中层级通过.连接,有点类似JSON的取值调用
const val KEY_LOCAL_DB_URL = “ktor.security.localDb.url”
const val KEY_REMOTE_DB_URL = “ktor.security.remoteDb.url”

const val KEY_LOCAL_DB_DRIVER = “ktor.security.localDb.driver”
const val KEY_REMOTE_DB_DRIVER = “ktor.security.remoteDb.driver”

const val KEY_LOCAL_DB_USER = “ktor.security.localDb.user”
const val KEY_REMOTE_DB_USER = “ktor.security.remoteDb.user”

const val KEY_LOCAL_DB_PWD = “ktor.security.localDb.password”
const val KEY_REMOTE_DB_PWD = “ktor.security.remoteDb.password”

fun Application.configureDb(vararg tables: Table) {
//获取当前Env环境
//通过Application中environment实例对象拿到其config对象,通过config以key-value形式获取配置中的值,不过只支持获取String和List
val env = environment.config.propertyOrNull(KEY_ENV)?.getString() ?: “LOCAL”

val url = environment
.config
.property(if (env == “LOCAL”) KEY_LOCAL_DB_URL else KEY_REMOTE_DB_URL)//如果是LOCAL环境就切换到本地数据库连接方式
.getString()

val driver = environment
.config
.property(if (env == “LOCAL”) KEY_LOCAL_DB_DRIVER else KEY_REMOTE_DB_DRIVER)
.getString()

val user = environment
.config
.property(if (env == “LOCAL”) KEY_LOCAL_DB_USER else KEY_REMOTE_DB_USER)
.getString()

val pwd =environment
.config
.property(if (env == “LOCAL”) KEY_LOCAL_DB_PWD else KEY_REMOTE_DB_PWD)
.getString()

//连接数据库
Database.connect(url = url, driver = driver, user = user, password = pwd)

//创建数据库表
transaction {
tables.forEach {
SchemaUtils.create(it)
}
}
}

4.4 通过Features添加必要功能构件

在Ktor中一个最典型的请求(Request)-响应(Response)管道模型大致是这样的: 它从一个请求开始,该请求会被路由到特定的程序处理,并经由我们的应用程序逻辑处理,最后做出响应。然而在实际的应用开发中,并不会这么简单的,但是本质上Pipeline是不变的。那么在Ktor如何更加将这个简单管道模型给丰富起来呢? 那就是向管道模式添加各种各样的Feature(功能构件或者功能插件)。 image.png

4.4.1 向管道模型添加功能构件

在许多应用开发中经常会用到一些基础通用的功能,比如内容编码、序列化、cookie、session等,这些基础通用的功能在Ktor中统称为**Features(功能构件)。所有的Features构件都类似一个插件,插入在Request、application Logic和Response切面之间。 image.png 由上图可知,当一个请求Request进来后,首先会通过Routing路由机制路由给一个特定的Handler进行处理;然而在把Request交由Handler处理之前可能会经过若干个Feature处理;然后Handler处理完这个Request请求,就会将Response响应返回给客户端,然而在将响应发送给客户端之前,它还是可能会经过若干个Feature处理,最终Response响应返回到客户端。可以看出整条从Request到Response链路就类似一个工厂流水线,每个Feature各司其职。

4.4.2 Routing本质上也是一个Feature

Feature的灵活性和可插拔性非常强大,它可以出现在Request/Response管道模型中任何一个节点部分。Routing虽然我们称为路由,但其本质也是一个Feature image.png

4.4.3 如何安装Feature

一般都是在应用初始化的时候去安装Feature即可,安装Feature非常简单。仅仅几行 install 即可搞定,如果是非内置的 Feature 还需要自己引入相关lib依赖. 除了使用现有的Feature, 还可以自定义Feature,关于如何自定义Feature属于Ktor高阶命题,后续再展开。

import io.ktor.features.*
fun Application.main() {
install(Routing)
install(Gson)
//…
}

//除了在main函数中安装,还可以在module入口函数中安装
fun Application.module() {
install(Routing)
install(Gson)
//…
}

4.5 通过Routing处理请求

Routing本质上也是一个Feature,所以Routing也需要进行install,然后就可以定义Route Handler处理请求了。

4.5.1 安装Routing路由

import io.ktor.routing.*

install(Routing) {
// …
}

//或者直接调用Application的routing扩展函数
import io.ktor.routing.*

routing {
// …
}

//因为Application的routing扩展函数内部做了处理,对于未安装Routing会自动安装Routing的容错,可以稍微瞅下源码
@ContextDsl
public fun Application.routing(configuration: Routing.() -> Unit): Routing =
featureOrNull(Routing)?.apply(configuration) ?: install(Routing, configuration)

//通过源码可以发现,如果configuration没有安装Routing就会自动安装Routing,所以大家一般看到的Routing都没有手动install过程,而是直接类似下面的代码。
fun main(args: Array) {
embeddedServer(Tomcat, port = 8080) {
routing {//直接调用Application的扩展函数routing,内部做了对于未安装Routing会自动安装Routing的容错处理
get(“/”) {
call.respondText(“Hello Ktor”)
}
}
}.start(wait = true)
}

4.5.2 定义路由处理的Handler

可以看下下面最简单的一个get服务的定义,下面用get源码来解读:

fun main(args: Array) {
embeddedServer(Tomcat, port = 8080) {
routing {
get(“/”) {//可以看到这个处理get请求的handler,它实际上是一个Route的扩展函数,一起来看看源码
call.respondText(“Hello Ktor”)
}
}
}.start(wait = true)
}

//Route.get函数源码,其实一个Route对象就是一个对应的Handler,
@ContextDsl
public fun Route.get(path: String, body: PipelineInterceptor<Unit, ApplicationCall>): Route {
return route(path, HttpMethod.Get) { //route函数本质上是一个Route的扩展函数
handle(body) //通过调用Route对象来处理的请求
}
}

//route函数本质上是一个Route的扩展函数
@ContextDsl
public fun Route.route(path: String, method: HttpMethod, build: Route.() -> Unit): Route {
val selector = HttpMethodRouteSelector(method)
return createRouteFromPath(path).createChild(selector).apply(build)//最终调用apply返回Route对象,build是传入handle(body)执行的lambda,
//也就是创建完child后返回一个Route对象,最终再调用它的handle函数
}

4.6 应用模块化

为了使得Ktor应用更具有可维护性、灵活性以及,Ktor提供一种思路就是将应用按照业务维度进行模块化设计。注意这里模块化概念并不是在项目中的一个Module,而这里module本质上是一个 Application 的扩展函数。并且可以在 application.conf 指定某一个或若干个module进行可插拔式的部署和卸载。 image.png 然后一个Module又包括了一条或若干条Request/Response的管道模型。 image.png 应用模块代码例子如下:

//定义一个accountModule,实际上是一个Application的扩展函数
fun Application.accountModule() {
routing {
loginRoute()

题外话

不管怎么样,不论是什么样的大小面试,要想不被面试官虐的不要不要的,只有刷爆面试题题做好全面的准备,当然除了这个还需要在平时把自己的基础打扎实,这样不论面试官怎么样一个知识点里往死里凿,你也能应付如流啊

这里我为大家准备了一些我工作以来以及参与过的大大小小的面试收集总结出来的一套进阶学习的视频及面试专题资料包,主要还是希望大家在如今大环境不好的情况下面试能够顺利一点,希望可以帮助到大家~

欢迎评论区讨论。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
ccountModule() {
routing {
loginRoute()

题外话

不管怎么样,不论是什么样的大小面试,要想不被面试官虐的不要不要的,只有刷爆面试题题做好全面的准备,当然除了这个还需要在平时把自己的基础打扎实,这样不论面试官怎么样一个知识点里往死里凿,你也能应付如流啊

这里我为大家准备了一些我工作以来以及参与过的大大小小的面试收集总结出来的一套进阶学习的视频及面试专题资料包,主要还是希望大家在如今大环境不好的情况下面试能够顺利一点,希望可以帮助到大家~

[外链图片转存中…(img-Xl8B6L5W-1715353169927)]

欢迎评论区讨论。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

  • 22
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Kotlin 支持的框架有:Ktor、Spring、Anko、Kodein、Kotlinx.html、Spek、Kotlin Android Extensions、Kotlin Serialization、Kotlin Coroutines 和 TornadoFX。 ### 回答2: Kotlin是一种基于Java虚拟机的编程语言,因此可以利用Java生态圈中的众多框架。此外,Kotlin也有一些独有的框架,下面是一些常见的Kotlin框架: 1. Ktor: Ktor是一个轻量级的Web框架,用于构建异步和非阻塞的Web应用程序。它提供了一个简单易用的API,支持各种服务器和客户端功能。 2. Anko: Anko是Kotlin的一个功能强大且易于使用的库,用于简化Android应用程序的开发。它提供了一系列的DSL(领域特定语言)来简化UI创建,数据库操作,异步任务等常见任务。 3. Exposed: Exposed是一个轻量级的ORM(对象关系映射)库,用于简化数据库的访问和操作。它提供了简洁的API,易于理解和使用,支持各种数据库。 4. Koin: Koin是一个轻量级的依赖注入框架,用于管理应用程序中的对象和它们之间的依赖关系。它通过提供简单直观的API,使得依赖注入变得容易。 5. TornadoFX: TornadoFX是一个用于构建JavaFX应用程序的Kotlin框架。它提供了强大且易于使用的API,通过DSL的方式简化了UI创建和事件处理。 这只是Kotlin的一些框架示例,实际上还有很多其他的框架可供选择,涵盖了各种应用程序开发的需求。Kotlin作为一种灵活且功能强大的语言,为开发人员提供了丰富的选择。 ### 回答3: Kotlin是一种基于Java虚拟机的编程语言,因其简洁、安全和互操作性等特点,逐渐在开发者中得到广泛使用。Kotlin具备强大的框架生态系统,以下是其中一些流行的框架: 1. Ktor:Ktor是一个轻量级的Web框架,用于构建可扩展且异步的后端应用程序。它提供简洁的API,易于使用和学习,并支持异步协程。 2. Spring Boot with KotlinKotlin集成了Spring Framework,使得使用Kotlin编写Spring Boot应用程序变得更加简洁和易用。Spring Boot提供了丰富的功能和工具来快速构建和部署应用程序。 3. Exposed:Exposed是一个轻量级的SQL库,用于与数据库交互。它使用Kotlin的强类型和DSL(领域特定语言)的特性,提供了简单、类型安全且易于维护的数据库访问方式。 4. Anko:Anko是一个用于Android开发Kotlin库,它提供了简化和加快Android应用程序开发的工具和实用功能,例如DSL布局构建、数据库操作和异步任务处理。 5. Arrow:Arrow是一个函数式编程库,旨在帮助开发者以函数式和声明式的方式构建应用程序。它提供了一组操作符和类型类, 帮助开发者编写简洁、可维护和高效的代码。 除了上述框架外,Kotlin还支持许多其他的框架,例如Koin(轻量级的依赖注入框架)、JUnit(单元测试框架)和Mockito(模拟框架)等。这些框架使得Kotlin在不同领域和平台上开发应用程序时更加便捷和高效。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值