Skip to content

Commit

Permalink
complete basic structure
Browse files Browse the repository at this point in the history
  • Loading branch information
yankeguo committed Jun 14, 2023
1 parent befda6d commit 3b96a7a
Show file tree
Hide file tree
Showing 38 changed files with 515 additions and 417 deletions.
113 changes: 31 additions & 82 deletions app.go
Original file line number Diff line number Diff line change
@@ -1,91 +1,56 @@
package winterfx

import (
"github.com/guoyk93/winterfx/core/probefx"
"github.com/guoyk93/winterfx/core/routerfx"
"github.com/prometheus/client_golang/prometheus/promhttp"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.uber.org/fx"
"net/http"
"net/http/pprof"
_ "net/http/pprof"
"strings"
"sync/atomic"
)

// HandlerFunc handler func with [Context] as argument
type HandlerFunc func(c Context)

// App the main interface of [summer]
type App interface {
// Handler inherit [http.Handler]
http.Handler

// HandleFunc register an action function with given path pattern
//
// This function is similar with [http.ServeMux.HandleFunc]
HandleFunc(pattern string, fn HandlerFunc)
}

type app struct {
Params
*Params

hMain *http.ServeMux
probe probefx.Probe
router routerfx.Router

hProm http.Handler
hProf http.Handler

cc chan struct{}

failed int64
}

func (a *app) HandleFunc(pattern string, fn HandlerFunc) {
a.hMain.Handle(
pattern,
otelhttp.NewHandler(
otelhttp.WithRouteTag(
pattern,
http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
c := newContext(rw, req)
c.loggingResponse = a.LoggingResponse
func() {
defer c.Perform()
fn(c)
}()
}),
),
pattern,
),
)
}

func (a *app) serveReadiness(rw http.ResponseWriter, req *http.Request) {
c := newContext(rw, req)
c := routerfx.NewContext(rw, req)
defer c.Perform()

//TODO: add readiness check
s, failed := "OK", false
s, failed := a.probe.CheckReadiness(c)

status := http.StatusOK
if failed {
atomic.AddInt64(&a.failed, 1)
status = http.StatusInternalServerError
} else {
atomic.StoreInt64(&a.failed, 0)
}

c.Code(status)
c.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")

c.Code(status)
c.Text(s)
}

func (a *app) serveLiveness(rw http.ResponseWriter, req *http.Request) {
c := newContext(rw, req)
c := routerfx.NewContext(rw, req)
defer c.Perform()

c.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")

if a.ReadinessCascade > 0 &&
atomic.LoadInt64(&a.failed) > a.ReadinessCascade {
if a.probe.CheckLiveness() {
c.Code(http.StatusInternalServerError)
c.Text("CASCADED")
c.Text("CASCADED FAILURE")
} else {
c.Code(http.StatusOK)
c.Text("OK")
Expand All @@ -94,61 +59,45 @@ func (a *app) serveLiveness(rw http.ResponseWriter, req *http.Request) {

func (a *app) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
// alive, ready, metrics
if req.URL.Path == a.ReadinessPath {
if req.URL.Path == a.PathReadiness {
// support readinessPath == livenessPath
a.serveReadiness(rw, req)
return
} else if req.URL.Path == a.LivenessPath {
} else if req.URL.Path == a.PathLiveness {
a.serveLiveness(rw, req)
return
} else if req.URL.Path == a.MetricsPath {
} else if req.URL.Path == a.PathMetrics {
a.hProm.ServeHTTP(rw, req)
return
}

// pprof
if strings.HasPrefix(req.URL.Path, "/debug/pprof") {
a.hProf.ServeHTTP(rw, req)
http.DefaultServeMux.ServeHTTP(rw, req)
return
}

// concurrency
if a.cc != nil {
<-a.cc
defer func() {
a.cc <- struct{}{}
}()
}

// serve with main handler
a.hMain.ServeHTTP(rw, req)
a.router.ServeHTTP(rw, req)
}

type Options struct {
fx.In

*Params

probefx.Probe
routerfx.Router
}

// New create an [App] with [Option]
func New(opts Options) App {
a := &app{
Params: opts.Params,
probe: opts.Probe,
router: opts.Router,
}

// create handlers
{
a.hMain = &http.ServeMux{}
a.hProm = promhttp.Handler()
m := &http.ServeMux{}
m.HandleFunc("/debug/pprof/", pprof.Index)
m.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
m.HandleFunc("/debug/pprof/profile", pprof.Profile)
m.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
m.HandleFunc("/debug/pprof/trace", pprof.Trace)
a.hProf = m
}

// create concurrency controller
if a.Concurrency > 0 {
a.cc = make(chan struct{}, a.Concurrency)
for i := 0; i < a.Concurrency; i++ {
a.cc <- struct{}{}
}
}
a.hProm = promhttp.Handler()
return a
}
31 changes: 0 additions & 31 deletions core/checkfx/check.go

This file was deleted.

1 change: 0 additions & 1 deletion core/checkfx/check_test.go

This file was deleted.

79 changes: 0 additions & 79 deletions core/checkfx/manager.go

This file was deleted.

9 changes: 7 additions & 2 deletions core/flagfx/flag.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ import (
// Args is the command-line arguments
type Args []string

// OverrideArgs supplies the command-line arguments
func OverrideArgs(v []string) fx.Option {
return fx.Replace(Args(v))
}

// ArgsFromCommandLine loads the flag set args from command-line arguments
func ArgsFromCommandLine() Args {
return os.Args[1:]
Expand All @@ -22,11 +27,11 @@ type DecoderResult[T any] struct {
fx.Out
JointPoint JointPoint `group:"winterfx_core_flagfx_jointpoints"`

Value T
Value *T
}

// AsDecoderFunc wraps a flag set decoder function with joint points
func AsDecoderFunc[T any](fn func(fset *flag.FlagSet) T) func(fset *flag.FlagSet) DecoderResult[T] {
func AsDecoderFunc[T any](fn func(fset *flag.FlagSet) *T) func(fset *flag.FlagSet) DecoderResult[T] {
return func(fset *flag.FlagSet) DecoderResult[T] {
return DecoderResult[T]{
Value: fn(fset),
Expand Down
8 changes: 8 additions & 0 deletions core/otelfx/fx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package otelfx

import "go.uber.org/fx"

var Module = fx.Module(
"winterfx_core_otelfx",
fx.Invoke(Setup),
)
10 changes: 10 additions & 0 deletions core/otelfx/http.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package otelfx

import (
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"net/http"
)

func InstrumentHTTPHandler(pattern string, h http.Handler) http.Handler {
return otelhttp.NewHandler(otelhttp.WithRouteTag(pattern, h), pattern)
}
6 changes: 3 additions & 3 deletions otel.go → core/otelfx/setup.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package winterfx
package otelfx

import (
"github.com/go-logr/logr"
Expand All @@ -11,8 +11,8 @@ import (
"net/http"
)

// SetupOTEL setup opentelemetry
func SetupOTEL() (err error) {
// Setup setup opentelemetry
func Setup() (err error) {
// zipkin exporter
var ze *zipkin.Exporter
if ze, err = zipkin.New("", zipkin.WithLogr(logr.Discard())); err != nil {
Expand Down
4 changes: 2 additions & 2 deletions otel_test.go → core/otelfx/setup_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package winterfx
package otelfx

import (
"github.com/stretchr/testify/require"
Expand All @@ -7,6 +7,6 @@ import (
)

func TestSetupOTEL(t *testing.T) {
require.NoError(t, SetupOTEL())
require.NoError(t, Setup())
_ = otel.GetTracerProvider()
}
23 changes: 23 additions & 0 deletions core/probefx/checker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package probefx

import (
"context"
"go.uber.org/fx"
)

type CheckerFunc func(ctx context.Context) error

type checker struct {
name string
fn CheckerFunc
}

func AsCheckerProvider[T any](fn func(v T) (name string, cfn CheckerFunc)) any {
return fx.Annotate(
func(v T) checker {
name, cfn := fn(v)
return checker{name: name, fn: cfn}
},
fx.ResultTags(`group:"winterfx_core_probefx_checkers"`),
)
}
Loading

0 comments on commit 3b96a7a

Please sign in to comment.