pool

package
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 7, 2026 License: MIT Imports: 5 Imported by: 0

Documentation

Overview

Package pool is a generic solution for async job dispatching from web server. While Go natively supports async jobs by using the keyword "go", but this may lead to several unwanted consequences. Suppose we have a typical http handler:

func Handle(req *http.Request, resp http.ResponseWriter) {}

If we dispatch async jobs using "go" like this:

  func Handle(req *http.Request, resp http.ResponseWriter) {
    go AsyncWork()
	   resp.Write([]byte("ok"))
  }

Let's go through all the disadvantages. First, the backpressure is lost. There is no way to limit the maximum goroutine the handler can create. clients can easily flood the server. Secondly, the graceful shutdown process is ruined. The http server can shutdown itself without losing any request, but the async jobs created with "go" are not protected by the server. You will lose all unfinished jobs once the server shuts down and program exits. lastly, the async job may want to access the original request context, maybe for tracing purposes. The request context terminates at the end of the request, so if you are not careful, the async jobs may be relying on a dead context.

Package pool creates a goroutine worker pool at beginning of the program, limits the maximum concurrency for you, shuts it down at the end of the request without losing any async jobs, and manages the context conversion for you.

Add the dependency to core:

var c *core.C = core.New()
c.Provide(pool.Providers())

Then you can inject the pool into your http handler:

type Handler struct {
    pool *pool.Pool
}

func (h *Handler) ServeHTTP(req *http.Request, resp http.ResponseWriter) {
   pool.Go(request.Context(), AsyncWork(asyncContext))
   resp.Write([]byte("ok"))
}
Example
package main

import (
	"context"
	"fmt"
	"net/http"
	"time"

	"github.com/yanghp/core"
	"github.com/yanghp/core/contract/lifecycle"
	"github.com/yanghp/core/control/pool"

	"github.com/gorilla/mux"
)

func main() {
	c := core.Default(
		core.WithInline("http.addr", ":9777"),
		core.WithInline("log.level", "none"),
	)
	c.Provide(pool.Providers())

	c.Invoke(func(m *pool.Manager, dispatcher lifecycle.HTTPServerStart) {
		p := pool.NewPool(m, 10)
		dispatcher.On(func(ctx context.Context, payload lifecycle.HTTPServerStartPayload) error {
			go func() {
				if _, err := http.Get("http://localhost:9777/"); err != nil {
					panic(err)
				}
			}()
			return nil
		})
		c.AddModule(core.HttpFunc(func(router *mux.Router) {
			router.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
				p.Go(request.Context(), func(asyncContext context.Context) {
					select {
					case <-asyncContext.Done():
						fmt.Println("async context cancelled")
					case <-time.After(time.Second):
						fmt.Println("async context will not be cancelled")
					}
				})
				writer.Write(nil)
			})
		}))
	})

	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancel()

	c.Serve(ctx)

}
Output:
async context will not be cancelled

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Providers

func Providers() di.Deps

Providers provide a *Manager to the core.

Types

type Manager

type Manager struct {
	// contains filtered or unexported fields
}

Manager manages a pool of workers.

func NewManager

func NewManager() *Manager

NewManager returns a new manager.

func (*Manager) Get

func (m *Manager) Get() *Worker

Get returns a worker from the free list. If the free list is empty, create a new one.

func (*Manager) Go

func (m *Manager) Go(ctx context.Context, f func(context.Context))

Go runs function with no concurrency limit.

func (*Manager) Module

func (m *Manager) Module() interface{}

Module implements the di.Modular interface.

func (*Manager) Release

func (m *Manager) Release(w *Worker)

Release put the worker back into the free list. If the free list is full, discard the worker. If the worker has surpassed the max duration, discard and managerStoppedCh the worker.

func (*Manager) Run

func (m *Manager) Run(ctx context.Context) error

Run starts the manager. It should be called during the initialization of the program.

type Pool

type Pool struct {
	// contains filtered or unexported fields
}

Pool is an async worker pool. It can be used to dispatch the async jobs from web servers. See the package documentation about its advantage over creating a goroutine directly.

func NewPool

func NewPool(manager *Manager, cap int) *Pool

NewPool returns *Pool

func (*Pool) Go

func (p *Pool) Go(requestContext context.Context, function func(asyncContext context.Context))

Go dispatchers a job to the async worker pool. requestContext is the context from http/grpc handler, and asyncContext is the context for async job handling. The asyncContext contains all values from requestContext, but its cancellation has nothing to do with the request. If the pool has reached max concurrency, the job will be executed in the current goroutine. In other word, the job will be executed synchronously.

func (*Pool) Wait

func (p *Pool) Wait()

Wait waits for all the async jobs to finish.

type Worker

type Worker struct {
	// contains filtered or unexported fields
}

func NewWorker

func NewWorker() *Worker

func (*Worker) Run

func (w *Worker) Run(ctx context.Context)

func (*Worker) Stop

func (w *Worker) Stop()

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL