代码拉取完成,页面将自动刷新
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: wsfuyibing <682805@qq.com>
// Date: 2024-07-24
package src
import (
"context"
"fmt"
"gitee.com/go-libs/config"
"gitee.com/go-libs/crontab"
"gitee.com/go-libs/log"
"gitee.com/go-wares/framework-iris/framework/src/conf"
"gitee.com/go-wares/framework-iris/framework/src/middlewares"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/middleware/pprof"
"github.com/kataras/iris/v12/mvc"
"reflect"
"strings"
"sync/atomic"
"time"
)
// Application
// is a component for iris web framework.
type Application struct {
cancel context.CancelFunc
ctx context.Context
annotationControllers []*ControllerEntry
annotationCrontabs []*crontab.Worker
annotationHandlers []*HandlerEntry
annotationMiddlewares map[string]iris.Handler
cron crontab.Cron
framework *iris.Application
middlewares []iris.Handler
parallelRunning, running int32
parallelRunners []ParallelRunner
rule iris.ExecutionRules
}
// AddAnnotationControllers
// adds a controller entry list into application.
func (o *Application) AddAnnotationControllers(entries ...*ControllerEntry) *Application {
var list = make([]*ControllerEntry, 0)
// Range
// given controllers.
for _, entry := range entries {
if entry == nil || entry.Controller == nil {
continue
}
list = append(list, entry)
}
// Add to
// controller list if necessary.
if len(list) > 0 {
o.annotationControllers = append(o.annotationControllers, list...)
}
return o
}
// AddAnnotationCrontabs
// adds a crontab worker list into application.
func (o *Application) AddAnnotationCrontabs(workers ...*crontab.Worker) *Application {
var list = make([]*crontab.Worker, 0)
// Range
// crontabs with given list in parameter.
for _, worker := range workers {
if worker == nil {
continue
}
list = append(list, worker)
}
// Add to
// crontab list if necessary.
if len(list) > 0 {
o.annotationCrontabs = append(o.annotationCrontabs, list...)
}
return o
}
// AddAnnotationHandlers
// adds an annotation handlers list for application.
func (o *Application) AddAnnotationHandlers(entries ...*HandlerEntry) *Application {
var list = make([]*HandlerEntry, 0)
// Range
// given handlers list.
for _, v := range entries {
if v == nil {
continue
}
// Request method
// convert to upper-case letter.
if v.Method != "" {
v.Method = strings.ToUpper(v.Method)
} else {
v.Method = "ANY"
}
list = append(list, v)
}
// Add to
// handler list if necessary.
if len(list) > 0 {
o.annotationHandlers = append(o.annotationHandlers, list...)
}
return o
}
// AddAnnotationMiddlewares
// adds an annotation middlewares mapping for application.
func (o *Application) AddAnnotationMiddlewares(mapper map[string]iris.Handler) *Application {
// Range
// given middleware mapping.
for name, handler := range mapper {
// Remove
// handler by name from global mapping.
if handler == nil {
if _, ok := o.annotationMiddlewares[name]; ok {
delete(o.annotationMiddlewares, name)
}
continue
}
// Update
// global middlewares mapping.
o.annotationMiddlewares[name] = handler
}
return o
}
// AddMiddlewares
// adds a global middleware list into application. All controllers and handlers
// are affected.
func (o *Application) AddMiddlewares(middlewares ...iris.Handler) {
for _, x := range middlewares {
if x != nil {
o.middlewares = append(o.middlewares, x)
}
}
}
// AddParallels
// adds a runner list into application.
func (o *Application) AddParallels(runners ...ParallelRunner) *Application {
var list = make([]ParallelRunner, 0)
// Range
// given runners list.
for _, runner := range runners {
if runner == nil {
continue
}
list = append(list, runner)
}
// Add to
// parallel runner list if necessary.
if len(list) > 0 {
o.parallelRunners = append(o.parallelRunners, list...)
}
return o
}
// Framework
// returns an iris web framework application.
func (o *Application) Framework() *iris.Application {
return o.framework
}
// +---------------------------------------------------------------------------+
// | Access methods |
// +---------------------------------------------------------------------------+
// Init
// prepare application required fields.
func (o *Application) init() *Application {
o.annotationControllers = make([]*ControllerEntry, 0)
o.annotationCrontabs = make([]*crontab.Worker, 0)
o.annotationHandlers = make([]*HandlerEntry, 0)
o.annotationMiddlewares = make(map[string]iris.Handler)
o.middlewares = make([]iris.Handler, 0)
o.parallelRunners = make([]ParallelRunner, 0)
return o.initIris()
}
// InitIris
// prepare application fields for iris web framework.
func (o *Application) initIris() *Application {
// Interrupt
// listener register.
iris.RegisterOnInterrupt(o.onInterrupt)
// Execution rules
// config for middlewares and main handler.
//
// - false: i.Next() must be called at end of middleware.
// - true: do not call next.
o.rule = iris.ExecutionRules{
Begin: iris.ExecutionOptions{Force: false},
Done: iris.ExecutionOptions{Force: false},
Main: iris.ExecutionOptions{Force: true},
}
// Framework
// bound for iris web framework.
o.framework = iris.New()
o.framework.SetExecutionRules(o.rule)
return o
}
// LoadCrontabs
// prepare a process for crontab used to start with iris.
func (o *Application) loadCrontabs() {
// Create
// a crontab process.
cron := crontab.New("crontab")
// Range
// registered crontab workers and add into cron process if validated.
for _, x := range o.annotationCrontabs {
if err := cron.Add(x); err != nil {
log.Errorf(`[iris][crontab] invalid worker: error="%v"`, err)
continue
}
log.Infof(`[iris][crontab] strategy="%v", handler="%s"`, x.GetStrategy(), x.GetClass())
}
// Add
// a paralleling handler for crontab if necessary.
if len(cron.GetWorkers()) > 0 {
o.cron = cron
// Add to
// paralleling handlers list.
o.AddParallels(func(ctx context.Context) {
if err := o.cron.Start(ctx); err != nil {
log.Errorf(`[iris][crontab] start failed: error="%v"`, err)
}
})
}
}
// LoadIrisConfiguration
// prepare iris web framework configuration with yaml file. Load builtin
// if not accessed.
func (o *Application) loadIrisConfiguration() {
// From config file.
if f := config.Seek("iris.yml", "iris.yaml"); f.Found() {
log.Infof(`[iris][configuration] from yaml file: path="%s"`, f.String())
o.framework.Configure(iris.WithConfiguration(iris.YAML(f.String())))
return
}
// With builtin.
log.Infof(`[iris][configuration] with builtin`)
o.framework.Configure(iris.WithConfiguration(iris.Configuration{
LogLevel: "disable",
DisableStartupLog: true,
EnablePathIntelligence: false,
EnablePathEscape: true,
ForceLowercaseRouting: true,
FireMethodNotAllowed: true,
DisableBodyConsumptionOnUnmarshal: true,
TimeFormat: time.DateTime,
Charset: "UTF-8",
}))
}
func (o *Application) loadIrisDebugProf() {
if conf.Config.DebugProf {
p := pprof.New()
s := strings.TrimSuffix(strings.TrimPrefix(conf.Config.DebugProfPrefix, "/"), "/")
log.Infof(`[iris] enable debug pprof: path="/%s"`, s)
o.framework.Any(fmt.Sprintf("/%s", s), p)
o.framework.Any(fmt.Sprintf("/%s/{action:path}", s), p)
}
}
// LoadIrisMiddlewares
// bind registered middlewares on iris.
func (o *Application) loadIrisMiddlewares() {
// Error
// middleware on all error handlers.
o.framework.OnAnyErrorCode(middlewares.Error)
// Global
// middlewares ahead of user middlewares around all request handlers.
o.framework.UseRouter(middlewares.Tracing, middlewares.Catch)
// User
// middlewares around all request handlers after builtin.
if n := len(o.middlewares); n > 0 {
o.framework.UseRouter(o.middlewares...)
log.Infof(`[iris][middleware] bind global middlewares: count="%d"`, n)
}
}
// LoadIrisHandlers
// load annotation handlers and bind on iris.
func (o *Application) loadIrisHandlers() {
// Range
// registered simple request handler on iris route.
for _, v := range o.annotationHandlers {
ls := make([]string, 0)
ig := make([]string, 0)
ms := make([]iris.Handler, 0)
// Load
// middlewares with annotation definitions.
for _, k := range v.Middlewares {
if fn, ok := o.annotationMiddlewares[k]; ok {
ls = append(ls, k)
ms = append(ms, fn)
} else {
ig = append(ig, k)
}
}
// Bind.
o.framework.Party(v.Path, ms...).Handle(v.Method, "", v.Handler)
// Logger
// handler action.
cs := fmt.Sprintf(`[iris][handler] route="%s %s"`, v.Method, v.Path)
if len(ls) > 0 {
cs += fmt.Sprintf(`, middlewares=%v`, ls)
}
if len(ig) > 0 {
cs += fmt.Sprintf(`, ignored-middlewares=%v`, ig)
}
log.Infof(`%s`, cs)
}
}
// LoadIrisControllers
// load annotation controllers and bind on iris.
func (o *Application) loadIrisControllers() {
var ref reflect.Type
// Iterate added controllers.
for _, v := range o.annotationControllers {
// Controller
// must be a pointer.
if ref = reflect.TypeOf(v.Controller); ref.Kind() != reflect.Ptr {
log.Errorf(`[iris][controller] controller must be a pointer: controller="%s"`, ref.String())
continue
}
// Create mvc controller.
func(entry *ControllerEntry, t reflect.Type) {
// Prepare controller middlewares.
ks := make([]string, 0)
ms := make([]iris.Handler, 0)
// Compare annotation middlewares with mapper.
for _, k := range entry.Middlewares {
if fn, ok := o.annotationMiddlewares[k]; ok {
ks = append(ks, k)
ms = append(ms, fn)
} else {
log.Errorf(`[iris][controller] undefined middleware: middleware="%s", controller="%s.%s"`, k, t.PkgPath(), t.Name())
}
}
// Create api party.
party := o.framework.Party(entry.Prefix, ms...)
// Controller added on iris application.
log.Infof(`[iris][controller] add controller: prefix="%s", middleware=%v, controller="%s.%s"`, entry.Prefix, ks, t.PkgPath(), t.Name())
mvc.Configure(party, func(a *mvc.Application) {
a.Handle(entry.Controller)
})
}(v, ref.Elem())
}
}
func (o *Application) onInterrupt() {}
// Start
// starts the iris web framework as a service based on current application
// fields.
func (o *Application) start(ctx context.Context) {
// Decrement
// running statistic.
defer atomic.AddInt32(&o.running, -1)
// Increment
// running statistic.
if n := atomic.AddInt32(&o.running, 1); n > 1 {
log.Errorf(`[iris] service started already`)
return
}
// Context
// create and logger.
log.Infof(`[iris] service startup: name="%s", host="%s", port="%d", pid="%d"`, conf.Config.Name, conf.Config.Host, conf.Config.Port, conf.Config.Pid)
o.ctx, o.cancel = context.WithCancel(ctx)
// Load
// dependent components and configuration.
o.loadIrisConfiguration()
o.loadCrontabs()
o.loadIrisMiddlewares()
o.loadIrisHandlers()
o.loadIrisControllers()
o.loadIrisDebugProf()
// Called when done.
defer func() {
// Catch
// start fatal and recover it.
if r := recover(); r != nil {
log.Fatalf(`[iris] service panic: fatal="%v"`, r)
}
// Cancel
// context if necessary.
if o.ctx.Err() == nil {
o.cancel()
}
// Wait
// paralleling handlers done.
o.waitParallel()
// Revert context.
o.ctx, o.cancel = nil, nil
log.Infof(`[iris] service closed`)
}()
// Start
// iris application and paralleling handlers.
if err := o.framework.Configure(func(_ *iris.Application) {
o.startParallel()
}).Run(iris.Addr(fmt.Sprintf(`%s:%d`, conf.Config.Host, conf.Config.Port))); err != nil {
log.Errorf(`[iris] %v`, err)
}
}
// StartParallel
// starts a paralleling runner list in goroutines.
func (o *Application) startParallel() {
// Range
// added paralleling runners.
for _, x := range o.parallelRunners {
atomic.AddInt32(&o.parallelRunning, 1)
// Run
// handler in a goroutine.
go func(runner ParallelRunner) {
// Called
// when goroutine done.
defer func() {
if r := recover(); r != nil {
log.Fatalf(`[iris][paralleling] runner panic: fatal="%v"`, r)
}
atomic.AddInt32(&o.parallelRunning, -1)
}()
// Run
// paralleling handler.
runner(o.ctx)
}(x)
}
}
// WaitParallel
// block 30 seconds until all paralleling runners are done.
func (o *Application) waitParallel() {
// Return
// if no paralleling runner.
if len(o.parallelRunners) == 0 {
return
}
// Prepare.
var (
duration = time.Second * 30
now = time.Now()
)
// Loop
// until all paralleling runners are done.
for {
time.Sleep(time.Millisecond * 10)
// Break
// if all paralleling handlers done.
if atomic.LoadInt32(&o.parallelRunning) == 0 {
break
}
// Break
// if blocked duration is greater than limit.
if time.Now().Sub(now).Nanoseconds() >= duration.Nanoseconds() {
break
}
}
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。