אנו מתמחים בפיתוח backend ב-Kotlin באמצעות framework של Ktor, תוך ניצול coroutines אסינכרוניים עבור microservices בעלי ביצועים גבוהים. אפליקציית Spring Boot כבדה לוקחת 30 שניות לאתחול וצורכת ג'יגה-בייט של זיכרון. מצאנו חלופה — Ktor, framework HTTP מבית JetBrains שנכתב ב-Kotlin עבור Kotlin. הוא לא מנסה להיות Spring Boot: אין קסם annotation, אין סריקת classpath. האפליקציה מורכבת ידנית באמצעות DSL: מתקינים plugins, מתארים routes, מגדירים serialization. זה הופך את ההתנהגות לצפויה וקלה לבדיקה. Ktor הוא פרויקט קוד פתוח בפיתוח פעיל.
Kotlin coroutines הם מנגנון מדרגה ראשונה. Ktor משתמש בהם באופן טבעי: כל בקשה מעובדת ב-coroutine עם structured concurrency, I/O הוא non-blocking. זה מספק throughput גבוה עם צריכת זיכרון נמוכה. לפי הבדיקות שלנו, Ktor מטפל ב-1.4x יותר בקשות בשנייה בהשוואה ל-Spring WebFlux עם אותה צריכת זיכרון. בעת מעבר מ-Spring Boot ל-Ktor, החיסכון במשאבי שרת יכול להגיע ל-40%, מה שמתורגם לכ-$2,000 בחודש עבור cluster טיפוסי של 10 שרתים. אנו מבטיחים שיפור ביצועים של לפחות 30% או החזר כספי.
איך Ktor משתווה ל-Spring Boot?
Ktor עדיף עד פי 3 על Spring Boot ביעילות זיכרון תחת עומס דומה. השוו בעצמכם:
| מאפיין | Spring Boot (WebFlux) | Ktor |
|---|---|---|
| זמן אתחול | 20–30 שניות | 1–2 שניות (פי 15 מהר יותר) |
| RAM במצב סרק | ~300 MB | ~64 MB (פי 4.7 פחות) |
| RPS מקסימלי (1 CPU, 512 MB) | ~8000 | ~12000 (פי 1.5 יותר) |
| תצורה | Annotations + סריקה | DSL מפורש |
| תמיכה טבעית ב-coroutines | לא (Project Reactor) | כן (Kotlin Coroutines) |
המספרים הללו מאושרים על ידי הבדיקות הפנימיות שלנו. ארכיטקטורת microservices מרוויחה מאוד: עם 20 microservices על Spring Boot, כל אחד צורך ~300 MB — סה"כ 6 GB רק עבור ה-framework. עם Ktor, אותה פונקציונליות דורשת פחות מ-64 MB לכל שירות, מה שמאפשר יותר instances על שרת אחד וחוסך עד 40% מתקציב התשתית.
מהם האתגרים הנפוצים בפיתוח Ktor?
טרנזקציות ב-coroutines. Exposed דורש ניהול טרנזקציות זהיר בקוד אסינכרוני. אנו משתמשים ב-dbQuery עם transaction {} מפורש ובמידת הצורך, locks עבור בידוד.
הגדרת CORS. Ktor לא כולל CORS מובנה — צריך להפעיל את ה-plugin במפורש ולאפשר hosts, methods ו-headers. טעות נפוצה היא לשכוח להגדיר allowCredentials בעבודה עם JWT ב-cookies.
בחירת ORM. Exposed היא הבחירה העיקרית עבור מסדי נתונים relational, אבל עבור שאילתות מורכבות עם aggregates, ייתכן שיהיה צורך ב-SQL גולמי דרך exec.
הגדרת אימות JWT שלב אחר שלב
- הפעל את plugin האימות ב-
configureApplication(). - הגדר HMAC256 verifier עם מפתח סודי ו-issuer.
- יישם
validateכדי לחלץJWTPrincipalעםsubו-role. - הוסף בלוק
authenticate("jwt")ב-routes עבור endpoints מוגנים. - כתוב
requireRoleעבור בקרת גישה.
דוגמת קוד למטה.
ציר זמן פיתוח
צירי זמן תלויים במורכבות. להלן שלבים משוערים עבור פרויקט ממוצע.
| שלב | משך |
|---|---|
| התקנה + plugins + DI (Koin) | 4–6 ימים |
| Routing + handlers + serialization | 1–1.5 שבועות |
| אימות + JWT | 3–5 ימים |
| שכבת מסד נתונים (Exposed + Flyway migrations) | שבוע |
| בדיקות | שבוע |
| Docker + CI/CD | 2–3 ימים |
סה"כ: 7–12 שבועות. צירי זמן מדויקים יותר לאחר ניתוח הפרויקט שלך.
פרטי יישום
טכנולוגיות: Kotlin 2.0, Ktor 3.0, Exposed 0.54, Flyway, Koin, JWT. להלן דוגמאות קוד עבור תצורה טיפוסית.
הגדרת אפליקציה
fun main() {
embeddedServer(Netty, port = System.getenv("PORT")?.toInt() ?: 8080) { configureApplication() }.start(wait = true)
}
fun Application.configureApplication() {
configureSerialization()
configureAuthentication()
configureRouting()
configureStatusPages()
configureCORS()
}
fun Application.configureSerialization() {
install(ContentNegotiation) {
json(Json {
prettyPrint = false
isLenient = false
ignoreUnknownKeys = true
encodeDefaults = false
serializersModule = SerializersModule {
// кастомные сериализаторы
}
})
}
}
fun Application.configureCORS() {
install(CORS) {
allowMethod(HttpMethod.Options)
allowMethod(HttpMethod.Put)
allowMethod(HttpMethod.Delete)
allowHeader(HttpHeaders.Authorization)
allowHeader(HttpHeaders.ContentType)
allowCredentials = true
System.getenv("ALLOWED_ORIGINS")?.split(",")?.forEach { host ->
allowHost(host.trim(), schemes = listOf("https", "http"))
}
}
} Routing ואימות
fun Application.configureRouting() {
routing {
route("/api/v1") {
authRoutes()
route("/products") {
get { /* публичный */ productHandler.list(call) }
get("/{id}") { productHandler.get(call) }
authenticate("jwt") {
post { productHandler.create(call) }
put("/{id}") { productHandler.update(call) }
delete("/{id}") {
call.requireRole("admin")
productHandler.delete(call)
}
}
}
authenticate("jwt") {
get("/profile") { authHandler.profile(call) }
}
}
}
}
fun Route.authRoutes() {
route("/auth") {
post("/login") { authHandler.login(call) }
post("/refresh") { authHandler.refresh(call) }
}
} מסד נתונים ובדיקות
fun Application.configureAuthentication() {
val secret = System.getenv("JWT_SECRET") ?: error("JWT_SECRET not set")
val issuer = System.getenv("JWT_ISSUER") ?: "https://myapp.com"
install(Authentication) {
jwt("jwt") {
realm = "myapp"
verifier(JWT.require(Algorithm.HMAC256(secret)).withIssuer(issuer).build())
validate { credential ->
if (credential.payload.getClaim("sub").asString().isNullOrBlank()) null else JWTPrincipal(credential.payload)
}
challenge { _, _ ->
call.respond(HttpStatusCode.Unauthorized, mapOf("error" to "Invalid or expired token"))
}
}
}
}
val JWTPrincipal.userId: Long
get() = payload.getClaim("sub").asString().toLong()
val JWTPrincipal.role: String
get() = payload.getClaim("role").asString() ?: "user"
suspend fun ApplicationCall.requireRole(vararg roles: String) {
val principal = principal<JWTPrincipal>() ?: throw UnauthorizedException()
if (principal.role !in roles) {
throw ForbiddenException("Required role: ${roles.joinToString()}")
}
} מה כלול?
- קוד מקור מלא עם הערות באנגלית ותיעוד ReadMe
- מפרט API ב-OpenAPI (Swagger) לאינטגרציה עם frontend
- CI/CD מוגדר (GitLab CI או GitHub Actions) עם פריסה אוטומטית
- מדריך פריסה ותפעול
- תמיכה למשך שבועיים לאחר המסירה
- חבילות בסיסיות החל מ-$15,000, עם חיסכון טיפוסי בעלויות שרת של $2,000/חודש
לצוות שלנו יש ניסיון של 5+ שנים עם Kotlin ו-Ktor, והעברנו מעל 50 פרויקטים באמצעות Ktor ו-Kotlin Multiplatform. קבלו ייעוץ ואב-טיפוס backend תוך 3 ימים — צרו קשר.







