Remove readme
This commit is contained in:
2 files changed
-388
No files matched your search
-187
@@ -1,187 +0,0 @@
|
|||||||
# Kotlin cloudflare worker
|
|
||||||
|
|
||||||
纯Kotlin编写的库,可以在Cloudflare worker上运行Kotlin/JS
|
|
||||||
|
|
||||||
# 快速开始
|
|
||||||
|
|
||||||
## 配置
|
|
||||||
|
|
||||||
> 项目依赖于Kotlin协程库, 请确保协程库版本号大于`1.10.2`
|
|
||||||
|
|
||||||
```kotlin
|
|
||||||
plugins {
|
|
||||||
kotlin("multiplatform") version "2.2.21"
|
|
||||||
}
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
maven("https://repo.maven.rtast.cn/releases")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```kotlin
|
|
||||||
// 最新版本请前往下方链接查看
|
|
||||||
// https://next.pkg.rtast.cn/#/releases/cn/rtast/kotlin-cfworker/core/
|
|
||||||
implementation("cn.rtast.kotlin-cfworker:core:1.0.2")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Hello world程序
|
|
||||||
|
|
||||||
```kotlin
|
|
||||||
kotlin {
|
|
||||||
js(IR) {
|
|
||||||
nodejs {
|
|
||||||
outputModuleName = "kotlin-cloudflare-worker"
|
|
||||||
binaries.executable()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
> Minimal hello world app
|
|
||||||
|
|
||||||
```kotlin
|
|
||||||
/**
|
|
||||||
* 不要修改这个函数的任何内容
|
|
||||||
*/
|
|
||||||
fun main() {
|
|
||||||
@Suppress("unused_expression")
|
|
||||||
val eventListener = EventListener { event ->
|
|
||||||
val dyn = event.asDynamic()
|
|
||||||
event.asDynamic().respondWith(handleRequest(dyn.request as Request))
|
|
||||||
Unit
|
|
||||||
}
|
|
||||||
js("addEventListener('fetch', eventListener)")
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 这里是处理http请求的函数
|
|
||||||
*/
|
|
||||||
@JsExport
|
|
||||||
fun handleRequest(request: Request): Promise<Response> = GlobalScope.promise {
|
|
||||||
val server = WorkerApplication().apply {
|
|
||||||
route("/") {
|
|
||||||
respondText("Hello kotlin cloudflare worker")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return@promise server.handle(request)
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
> 在本地运行之前需要先安装wrangler
|
|
||||||
|
|
||||||
在项目根目录创建一个名为`wrangler.toml`的文件,然后将下面的内容复制进去并修改为你自己的信息
|
|
||||||
|
|
||||||
```toml
|
|
||||||
name = "kotlin-cloudflare-worker" # 可以是任何名字
|
|
||||||
account_id = "<your account id>"
|
|
||||||
workers_dev = false
|
|
||||||
preview_urls = false
|
|
||||||
compatibility_date = "2022-08-11"
|
|
||||||
main = "kotlin-cloudflare-worker.js" # 必须和`outputModuleName`一致
|
|
||||||
```
|
|
||||||
|
|
||||||
运行gradle任务编译出js `gradlew compileProductionExecutableKotlinJs`,
|
|
||||||
将wrangler.toml复制到 `build/compileSync/js/main/productionExecutable/kotlin`,
|
|
||||||
并且进入该目录下运行`wrangler dev`
|
|
||||||
|
|
||||||
## 部署
|
|
||||||
|
|
||||||
使用`wrangler deploy`来部署到cloudflare worker, 在此之前你需要先使用`wrangler login`来登录
|
|
||||||
|
|
||||||
# 注意事项
|
|
||||||
|
|
||||||
Cloudflare worker 使用V8引擎来驱动js脚本,线程模型为单线程,所有事件均由event loop(事件循环)驱动,在此之外的所有任务
|
|
||||||
都会被drop, 所以不要使用协程库中的launch或任何可以凭空创建出一个挂起作用域的函数来调用suspend函数, 取而代之的是你需要从
|
|
||||||
`handleRequest`方法中传播`CoroutineScope`(suspend), 就像下面这样
|
|
||||||
|
|
||||||
```kotlin
|
|
||||||
// 不要使用这种方法
|
|
||||||
val scope = CoroutineScope(Dispatcher.DEFAULT)
|
|
||||||
|
|
||||||
fun blockingFunction() {
|
|
||||||
scope.launch {
|
|
||||||
// 这里的代码永远不会被执行
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 使用下面的代码来正确的调用suspend函数
|
|
||||||
|
|
||||||
fun main() {
|
|
||||||
@Suppress("unused_expression")
|
|
||||||
val eventListener = EventListener { event ->
|
|
||||||
val dyn = event.asDynamic()
|
|
||||||
event.asDynamic().respondWith(handleRequest(dyn.request as Request))
|
|
||||||
Unit
|
|
||||||
}
|
|
||||||
js("addEventListener('fetch', eventListener)")
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsExport
|
|
||||||
fun handleRequest(request: Request): Promise<Response> = GlobalScope.promise {
|
|
||||||
return Promise.resolve(Response("Hello"))
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
# Http 客户端
|
|
||||||
|
|
||||||
不要使用ktor来作为http客户端, 因为cloudflare worker不是一个标准的nodejs环境, 你需要使用 fetch
|
|
||||||
`cn.rtast.cfworker.client.fetch` 来发送http请求, 注意不是`window.fetch`, `window.fetch`是浏览器API
|
|
||||||
|
|
||||||
# Websocket
|
|
||||||
|
|
||||||
仅支持Websocket服务器
|
|
||||||
|
|
||||||
> 使用类似ktor的语法注册websocket端点
|
|
||||||
|
|
||||||
```kotlin
|
|
||||||
@JsExport
|
|
||||||
fun handleRequest(request: Request): Promise<Response> = GlobalScope.promise {
|
|
||||||
val server = WorkerApplication().apply {
|
|
||||||
route("/") {
|
|
||||||
respondText("Hello kotlin cloudflare worker")
|
|
||||||
}
|
|
||||||
|
|
||||||
webSocket("/ws") {
|
|
||||||
onMessage {
|
|
||||||
println(it.readText())
|
|
||||||
}
|
|
||||||
|
|
||||||
onClose {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return@promise server.handle(request)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
# ByteArray 和 ByteBuffer 转换
|
|
||||||
|
|
||||||
```kotlin
|
|
||||||
import cn.rtast.cfworker.util.toByteArray
|
|
||||||
import cn.rtast.cfworker.util.toArrayBuffer
|
|
||||||
|
|
||||||
val bb: ByteBuffer = ...
|
|
||||||
// 将ByteBuffer转换为ByteArray
|
|
||||||
val ba: ByteArray = bb.toByteArray()
|
|
||||||
|
|
||||||
// 将ByteArray转换为ByteBuffer
|
|
||||||
val bb2: ByteBuffer = ba.toArrayBuffer()
|
|
||||||
```
|
|
||||||
|
|
||||||
# CPU时间限制
|
|
||||||
|
|
||||||
cloudflare worker 免费版本限制处理每个请求的CPU时间为10ms(不包括IO等待时间), 如果你需要执行CPU密集型任务或需要更高的配额,
|
|
||||||
请考虑升级到Workers paid或使用JS来减少标准库和协程库的加载时间.
|
|
||||||
|
|
||||||
即使使用此库注册了超过15个api端点,并且都具备文件处理功能,每个请求占用的cpu时间平均值不超过10ms+-2ms,
|
|
||||||
在冷启动时处理首次请求时间会大幅增加,最高可达50ms,但worker仍然可以处理并返回请求.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
> 上图反应了过去24小时每个请求的平均cpu时间(P50)
|
|
||||||
|
|
||||||
# 已经使用此项目的实例
|
|
||||||
|
|
||||||
https://repo.maven.rtast.cn
|
|
||||||
@@ -1,201 +0,0 @@
|
|||||||
# Kotlin cloudflare worker
|
|
||||||
|
|
||||||
A library to running kotlin/js on cloudflare worker.
|
|
||||||
|
|
||||||
<div style="display: block">
|
|
||||||
<a href="README.md">English</a>
|
|
||||||
<a href="README-zh.md">中文</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
# Get started
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
> `kotlinx.coroutines` is required
|
|
||||||
|
|
||||||
```kotlin
|
|
||||||
plugins {
|
|
||||||
kotlin("multiplatform") version "2.2.21"
|
|
||||||
}
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
maven("https://repo.maven.rtast.cn/releases")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```kotlin
|
|
||||||
// to get latest version of this lib, go to
|
|
||||||
// https://next.pkg.rtast.cn/#/releases/cn/rtast/kotlin-cfworker/core/
|
|
||||||
implementation("cn.rtast.kotlin-cfworker:core:1.0.2")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Run app
|
|
||||||
|
|
||||||
```kotlin
|
|
||||||
kotlin {
|
|
||||||
js(IR) {
|
|
||||||
nodejs {
|
|
||||||
outputModuleName = "kotlin-cloudflare-worker"
|
|
||||||
binaries.executable()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
> Minimal hello world app
|
|
||||||
|
|
||||||
```kotlin
|
|
||||||
/**
|
|
||||||
* DO NOT EDIT THIS FUNCTION
|
|
||||||
*/
|
|
||||||
fun main() {
|
|
||||||
@Suppress("unused_expression")
|
|
||||||
val eventListener = EventListener { event ->
|
|
||||||
val dyn = event.asDynamic()
|
|
||||||
event.asDynamic().respondWith(handleRequest(dyn.request as Request))
|
|
||||||
Unit
|
|
||||||
}
|
|
||||||
js("addEventListener('fetch', eventListener)")
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsExport
|
|
||||||
fun handleRequest(request: Request): Promise<Response> = GlobalScope.promise {
|
|
||||||
val server = WorkerApplication().apply {
|
|
||||||
route("/") {
|
|
||||||
respondText("Hello kotlin cloudflare worker")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return@promise server.handle(request)
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
> Before run worker locally, you need to install wrangler and login
|
|
||||||
|
|
||||||
First, create `wrangler.toml` in your root project and configure it
|
|
||||||
|
|
||||||
```toml
|
|
||||||
name = "kotlin-cloudflare-worker" # any name you want
|
|
||||||
account_id = "<your account id>"
|
|
||||||
workers_dev = false
|
|
||||||
preview_urls = false
|
|
||||||
compatibility_date = "2022-08-11"
|
|
||||||
main = "kotlin-cloudflare-worker.js" # Set this value same as `outputModuleName`
|
|
||||||
```
|
|
||||||
|
|
||||||
Then run gradle task `gradlew compileProductionExecutableKotlinJs`,
|
|
||||||
copy wrangler.toml into `build/compileSync/js/main/productionExecutable/kotlin`,
|
|
||||||
and run `wrangler dev` command in `build/compileSync/js/main/productionExecutable/kotlin`,
|
|
||||||
|
|
||||||
## Deploy
|
|
||||||
|
|
||||||
Run gradle task, copy wrangler.toml, but use `wrangler deploy` to deploy to cloudflare worker
|
|
||||||
|
|
||||||
# Note
|
|
||||||
|
|
||||||
JavaScript uses a single-threaded execution model. All asynchronous work is ultimately driven by the event loop (
|
|
||||||
including Kotlin coroutines on JS).
|
|
||||||
|
|
||||||
Therefore, DO NOT create a CoroutineScope(Dispatchers.Default) or launch CPU-bound coroutines, as there is no real
|
|
||||||
background thread on JS targets.
|
|
||||||
|
|
||||||
All coroutines run on same event loop thread, and all coroutines not in same event loop will be dropped.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```kotlin
|
|
||||||
// DO NOT DO THIS
|
|
||||||
val scope = CoroutineScope(Dispatcher.DEFAULT)
|
|
||||||
|
|
||||||
fun blockingFunction() {
|
|
||||||
scope.launch {
|
|
||||||
// this block will never execute
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Instead of do this
|
|
||||||
|
|
||||||
fun main() {
|
|
||||||
@Suppress("unused_expression")
|
|
||||||
val eventListener = EventListener { event ->
|
|
||||||
val dyn = event.asDynamic()
|
|
||||||
event.asDynamic().respondWith(handleRequest(dyn.request as Request))
|
|
||||||
Unit
|
|
||||||
}
|
|
||||||
js("addEventListener('fetch', eventListener)")
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsExport
|
|
||||||
fun handleRequest(request: Request): Promise<Response> = GlobalScope.promise {
|
|
||||||
return Promise.resolve(Response("Hello"))
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
# Http client
|
|
||||||
|
|
||||||
Cloudflare worker is not a standard nodejs environment, do http requests must
|
|
||||||
use fetch api(not `window.fetch`, this is browser api), use `cn.rtast.cfworker.client.fetch` instead,
|
|
||||||
|
|
||||||
# Websocket
|
|
||||||
|
|
||||||
Only websocket server is supported
|
|
||||||
|
|
||||||
> ktor like expression
|
|
||||||
|
|
||||||
```kotlin
|
|
||||||
@JsExport
|
|
||||||
fun handleRequest(request: Request): Promise<Response> = GlobalScope.promise {
|
|
||||||
val server = WorkerApplication().apply {
|
|
||||||
route("/") {
|
|
||||||
respondText("Hello kotlin cloudflare worker")
|
|
||||||
}
|
|
||||||
|
|
||||||
webSocket("/ws") {
|
|
||||||
onMessage {
|
|
||||||
println(it.readText())
|
|
||||||
}
|
|
||||||
|
|
||||||
onClose {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return@promise server.handle(request)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
# ByteArray and ByteBuffer cast
|
|
||||||
|
|
||||||
Kotlin cloudflare worker provided api to mutual conversion,
|
|
||||||
|
|
||||||
```kotlin
|
|
||||||
import cn.rtast.cfworker.util.toByteArray
|
|
||||||
import cn.rtast.cfworker.util.toArrayBuffer
|
|
||||||
|
|
||||||
val bb: ByteBuffer = ...
|
|
||||||
// convert ByteBuffer to ByteArray
|
|
||||||
val ba: ByteArray = bb.toByteArray()
|
|
||||||
|
|
||||||
// convert ByteArray to ByteBuffer
|
|
||||||
val bb2: ByteBuffer = ba.toArrayBuffer()
|
|
||||||
```
|
|
||||||
|
|
||||||
# CPU Time limit
|
|
||||||
|
|
||||||
Cloudflare worker free tier have a limit of worker cpu limit (10ms, exclude IO waiting time), `kotlin cloudflare worker`
|
|
||||||
depends on `kotlin-stdlib` and `kotlinx.coroutines`, which are quite heavy*, When performing CPU-bound task,
|
|
||||||
please carefully consider whether to use JavaScript instead of Kotlin.
|
|
||||||
|
|
||||||
But it works for my maven repository, I registered over 15 api endpoints.
|
|
||||||
The avg cpu time for each endpoint does not exceed 10ms, but it may increase significantly during
|
|
||||||
cold start (Over 50-200ms, but still responsible)
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
> This image shows last 24 hours avg cpu time, see `P50`
|
|
||||||
|
|
||||||
# Real instance
|
|
||||||
|
|
||||||
https://repo.maven.rtast.cn
|
|
||||||
|
|
||||||
> Yes, it the maven repository but built with kotlin/js with this lib
|
|
||||||
Reference in New Issue
Block a user