Merge pull request #132 from syumai/split-wait-for-ready-from-serve

split Done funcs from workers.Serve / cron.ScheduleTask
This commit is contained in:
syumai 2024-11-08 02:40:40 +09:00 committed by GitHub
commit 605532b98b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 168 additions and 16 deletions

View File

@ -1,6 +1,6 @@
.PHONY: dev .PHONY: dev
dev: dev:
wrangler dev wrangler dev --test-scheduled
.PHONY: build .PHONY: build
build: build:

View File

@ -18,3 +18,12 @@ make dev # run dev server
make build # build Go Wasm binary make build # build Go Wasm binary
make deploy # deploy worker make deploy # deploy worker
``` ```
#### Testing cron schedule
* With curl command below, you can test the cron schedule.
- see: https://developers.cloudflare.com/workers/runtime-apis/handlers/scheduled/#background
```
curl "http://localhost:8787/__scheduled?cron=*+*+*+*+*"
```

View File

@ -3,7 +3,9 @@ package main
import ( import (
"context" "context"
"fmt" "fmt"
"time"
"github.com/syumai/workers/cloudflare"
"github.com/syumai/workers/cloudflare/cron" "github.com/syumai/workers/cloudflare/cron"
) )
@ -15,6 +17,11 @@ func task(ctx context.Context) error {
fmt.Println(e.ScheduledTime.Unix()) fmt.Println(e.ScheduledTime.Unix())
cloudflare.WaitUntil(func() {
time.Sleep(1 * time.Second)
fmt.Println("Run sub task after returning from main task")
})
return nil return nil
} }

View File

@ -0,0 +1,3 @@
build
node_modules
.wrangler

View File

@ -0,0 +1,12 @@
.PHONY: dev
dev:
wrangler dev --test-scheduled
.PHONY: build
build:
go run ../../cmd/workers-assets-gen
tinygo build -o ./build/app.wasm -target wasm -no-debug ./...
.PHONY: deploy
deploy:
wrangler deploy

View File

@ -0,0 +1,29 @@
# multiple-handlers
* This example shows how to use multiple handlers in a single worker.
## Development
### Requirements
This project requires these tools to be installed globally.
* wrangler
* tinygo
### Commands
```
make dev # run dev server
make build # build Go Wasm binary
make deploy # deploy worker
```
#### Testing cron schedule
* With curl command below, you can test the cron schedule.
- see: https://developers.cloudflare.com/workers/runtime-apis/handlers/scheduled/#background
```
curl "http://localhost:8787/__scheduled?cron=*+*+*+*+*"
```

View File

@ -0,0 +1,7 @@
module github.com/syumai/workers/_examples/multiple-handlers
go 1.21.3
require github.com/syumai/workers v0.0.0
replace github.com/syumai/workers => ../../

View File

View File

@ -0,0 +1,38 @@
package main
import (
"context"
"fmt"
"net/http"
"github.com/syumai/workers"
"github.com/syumai/workers/cloudflare/cron"
)
func main() {
handler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
fmt.Fprint(w, "Hello, world!")
})
task := func(ctx context.Context) error {
e, err := cron.NewEvent(ctx)
if err != nil {
return err
}
fmt.Println(e.ScheduledTime.Unix())
return nil
}
// set up the worker
workers.ServeNonBlock(handler)
cron.ScheduleTaskNonBlock(task)
// send a ready signal to the runtime
workers.Ready()
// block until the handler or task is done
select {
case <-workers.Done():
case <-cron.Done():
}
}

View File

@ -0,0 +1,10 @@
name = "multiple-handlers"
main = "./build/worker.mjs"
compatibility_date = "2023-02-24"
workers_dev = false
[triggers]
crons = ["* * * * *"]
[build]
command = "make build"

View File

@ -5,13 +5,17 @@ import (
"fmt" "fmt"
"syscall/js" "syscall/js"
"github.com/syumai/workers"
"github.com/syumai/workers/internal/jsutil" "github.com/syumai/workers/internal/jsutil"
"github.com/syumai/workers/internal/runtimecontext" "github.com/syumai/workers/internal/runtimecontext"
) )
type Task func(ctx context.Context) error type Task func(ctx context.Context) error
var scheduledTask Task var (
scheduledTask Task
doneCh = make(chan struct{})
)
func runScheduler(eventObj js.Value) error { func runScheduler(eventObj js.Value) error {
ctx := runtimecontext.New(context.Background(), eventObj) ctx := runtimecontext.New(context.Background(), eventObj)
@ -46,16 +50,21 @@ func init() {
jsutil.Binding.Set("runScheduler", runSchedulerCallback) jsutil.Binding.Set("runScheduler", runSchedulerCallback)
} }
//go:wasmimport workers ready
func ready()
// ScheduleTask sets the Task to be executed // ScheduleTask sets the Task to be executed
func ScheduleTask(task Task) { func ScheduleTask(task Task) {
scheduledTask = task scheduledTask = task
ready() workers.Ready()
select {} <-Done()
} }
// ScheduleTaskNonBlock sets the Task to be executed but does not signal readiness or block // ScheduleTaskNonBlock sets the Task to be executed but does not signal readiness or block
// indefinitely. The non-blocking form is meant to be used in conjunction with [workers.Serve]. // indefinitely. The non-blocking form is meant to be used in conjunction with [workers.Serve].
func ScheduleTaskNonBlock(task Task) { scheduledTask = task } func ScheduleTaskNonBlock(task Task) {
scheduledTask = task
}
// Done returns a channel which is closed when the task is done.
// Currently, this channel is never closed to support cloudflare.WaitUntil feature.
func Done() <-chan struct{} {
return doneCh
}

View File

@ -25,3 +25,15 @@ func Serve(handler http.Handler) {
fmt.Fprintln(os.Stderr, "warn: this server is currently running in non-JS mode. to enable JS-related features, please use the make command in the syumai/workers template.") fmt.Fprintln(os.Stderr, "warn: this server is currently running in non-JS mode. to enable JS-related features, please use the make command in the syumai/workers template.")
http.ListenAndServe(addr, handler) http.ListenAndServe(addr, handler)
} }
func ServeNonBlock(http.Handler) {
panic("ServeNonBlock is not supported in non-JS environments")
}
func Ready() {
panic("Ready is not supported in non-JS environments")
}
func Done() <-chan struct{} {
panic("Done is not supported in non-JS environments")
}

View File

@ -16,7 +16,7 @@ import (
var ( var (
httpHandler http.Handler httpHandler http.Handler
closeCh = make(chan struct{}) doneCh = make(chan struct{})
) )
func init() { func init() {
@ -52,7 +52,7 @@ type appCloser struct {
} }
func (c *appCloser) Close() error { func (c *appCloser) Close() error {
defer close(closeCh) defer close(doneCh)
return c.ReadCloser.Close() return c.ReadCloser.Close()
} }
@ -84,16 +84,32 @@ func handleRequest(reqObj js.Value) (js.Value, error) {
return w.ToJSResponse(), nil return w.ToJSResponse(), nil
} }
//go:wasmimport workers ready // Serve serves http.Handler on a JS runtime.
func ready()
// Server serves http.Handler on a JS runtime.
// if the given handler is nil, http.DefaultServeMux will be used. // if the given handler is nil, http.DefaultServeMux will be used.
func Serve(handler http.Handler) { func Serve(handler http.Handler) {
ServeNonBlock(handler)
Ready()
<-Done()
}
// ServeNonBlock sets the http.Handler to be served but does not signal readiness or block
// indefinitely. The non-blocking form is meant to be used in conjunction with Ready and WaitForCompletion.
func ServeNonBlock(handler http.Handler) {
if handler == nil { if handler == nil {
handler = http.DefaultServeMux handler = http.DefaultServeMux
} }
httpHandler = handler httpHandler = handler
ready() }
<-closeCh
//go:wasmimport workers ready
func ready()
// Ready must be called after all setups of the Go side's handlers are done.
func Ready() {
ready()
}
// Done returns a channel which is closed when the handler is done.
func Done() <-chan struct{} {
return doneCh
} }