算法艺术创作
algorithmic-art
通过代码和可调参数创作生成艺术与交互式视觉效果。
golang-patterns【设计与创意】Use this skill when the user says 'Go goroutine', 'Go concurrency', 'Go channel', 'Go error handling', 'Go HTTP', 'Go middleware', 'Go context', 'Go worker pool', 'Go mutex', 'Go select', or when implementing Go-specific patterns. This skill enforces: goroutine lifecycle (every goroutine has a stop condition), errgroup for parallel work, error wrappi…
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Implement Go concurrency, error handling, and HTTP server patterns correctly. Every goroutine has a stop condition. Every error is wrapped. Every server shuts down gracefully.
Exact user phrases: "Go goroutine", "Go concurrency", "Go channel", "Go error handling", "Go HTTP", "Go middleware", "Go context", "Go worker pool", "Go mutex", "Go select", "Go graceful shutdown".
Before activating, verify:
No file output. Produces code examples as text.
Pattern: {name}
Problem: {what this solves}
Code:
{implementation}
No preamble. No postamble. No explanations. No filler/hedging/transitions. Compress output — why use many token when few do trick.
Per pattern: 20 lines of code + 2 lines explanation.
| Pattern | Use Case | Channel Type | Goroutine Count |
|---|---|---|---|
| Worker Pool | Fixed-size task processing | Buffered | Fixed (N workers) |
| Fan-Out | Distribute work to parallel consumers | Unbuffered | Dynamic |
| Fan-In | Merge results from multiple sources | Buffered merge | 1 merger + N producers |
| Pipeline | Sequential processing stages | Unbuffered | 1 per stage |
| Pub/Sub | Broadcast to multiple consumers | Buffered per sub | 1 per subscriber |
Decision: Fixed rate-limited processing → Worker Pool. Parallel independent tasks → Fan-Out/Fan-In. Sequential transformations → Pipeline. Event broadcast → Pub/Sub.
| Strategy | Mechanism | Best For |
|---|---|---|
| errgroup | First error cancels group | Parallel independent tasks |
| Result channel | Errors sent alongside results | Fan-In patterns |
| Custom error aggregator | Collect all errors | Batch validation |
| panic/recover | Fatal unexpected errors | Library boundaries only |
Decision: Fail-fast parallel work → errgroup. Continue-on-error collection → Result channel.
Every goroutine must have a mechanism to stop. Never start a goroutine without a done/cancel signal.
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go func() {
for {
select {
case <-ctx.Done():
return
case job := <-jobChan:
process(job)
}
}
}()
g, ctx := errgroup.WithContext(context.Background())
for _, item := range items {
item := item
g.Go(func() error {
result, err := processItem(ctx, item)
if err != nil {
return fmt.Errorf("process item %d: %w", item.ID, err)
}
results = append(results, result)
return nil
})
}
if err := g.Wait(); err != nil {
return fmt.Errorf("batch processing failed: %w", err)
}
func producer(ctx context.Context, items []Work) <-chan Work {
out := make(chan Work)
go func() {
defer close(out)
for _, item := range items {
select {
case <-ctx.Done():
return
case out <- item:
}
}
}()
return out
}
// Struct-based DI
type UserHandler struct {
createUser *application.CreateUserUseCase
getUser *application.GetUserUseCase
}
func NewUserHandler(createUser *application.CreateUserUseCase, getUser *application.GetUserUseCase) *UserHandler {
return &UserHandler{createUser, getUser}
}
func (h *UserHandler) Create(w http.ResponseWriter, r *http.Request) {
var dto application.CreateUserDTO
if err := json.NewDecoder(r.Body).Decode(&dto); err != nil {
writeError(w, http.StatusBadRequest, "INVALID_JSON", err.Error())
return
}
user, err := h.createUser.Execute(r.Context(), dto)
if err != nil {
writeError(w, http.StatusInternalServerError, "INTERNAL", err.Error())
return
}
writeJSON(w, http.StatusCreated, user)
}
// Closure-based DI (simpler)
func NewCreateHandler(uc *application.CreateUserUseCase) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var dto application.CreateUserDTO
if err := json.NewDecoder(r.Body).Decode(&dto); err != nil {
writeError(w, http.StatusBadRequest, "INVALID_JSON", err.Error())
return
}
user, err := uc.Execute(r.Context(), dto)
if err != nil {
writeError(w, http.StatusInternalServerError, "INTERNAL", err.Error())
return
}
writeJSON(w, http.StatusCreated, user)
}
}
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
server := &http.Server{Addr: ":8080", Handler: mux}
go func() {
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("server error: %v", err)
}
}()
<-ctx.Done()
shutdownCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := server.Shutdown(shutdownCtx); err != nil {
log.Fatalf("shutdown error: %v", err)
}
type WorkerPool struct {
jobs chan Job
results chan Result
workers int
}
func NewWorkerPool(workers int) *WorkerPool {
return &WorkerPool{
jobs: make(chan Job, 100),
results: make(chan Result, 100),
workers: workers,
}
}
func (wp *WorkerPool) Start(ctx context.Context) {
for i := 0; i < wp.workers; i++ {
go func(id int) {
for {
select {
case <-ctx.Done():
return
case job, ok := <-wp.jobs:
if !ok {
return
}
wp.results <- processJob(job)
}
}
}(i)
}
}
func (wp *WorkerPool) Submit(job Job) {
wp.jobs <- job
}
func (wp *WorkerPool) Results() <-chan Result {
return wp.results
}
func (wp *WorkerPool) Stop() {
close(wp.jobs)
}
func fanOut(ctx context.Context, source <-chan Work, n int) []<-chan Work {
workers := make([]<-chan Work, n)
for i := 0; i < n; i++ {
workers[i] = worker(ctx, source)
}
return workers
}
func worker(ctx context.Context, jobs <-chan Work) <-chan Work {
results := make(chan Work)
go func() {
defer close(results)
for j := range jobs {
select {
case <-ctx.Done():
return
case results <- process(j):
}
}
}()
return results
}
func fanIn(ctx context.Context, channels []<-chan Work) <-chan Work {
out := make(chan Work)
var wg sync.WaitGroup
for _, ch := range channels {
wg.Add(1)
go func(c <-chan Work) {
defer wg.Done()
for v := range c {
select {
case <-ctx.Done():
return
case out <- v:
}
}
}(ch)
}
go func() {
wg.Wait()
close(out)
}()
return out
}
type Middleware func(http.Handler) http.Handler
func Chain(h http.Handler, middleware ...Middleware) http.Handler {
for i := len(middleware) - 1; i >= 0; i-- {
h = middleware[i](h)
}
return h
}
// Usage
handler := Chain(
userHandler,
LoggerMiddleware,
AuthMiddleware("admin"),
RateLimitMiddleware(100),
)
type contextKey string
const UserIDKey contextKey = "user_id"
func WithUserID(ctx context.Context, id string) context.Context {
return context.WithValue(ctx, UserIDKey, id)
}
func UserIDFromContext(ctx context.Context) (string, bool) {
id, ok := ctx.Value(UserIDKey).(string)
return id, ok
}
sync.Pool for frequently allocated, short-lived objects (request contexts, buffers)make([]T, 0, capacity) when capacity is knownruntime.GC() only after known allocations (unit tests), never in productionpprof — CPU, heap, goroutine, mutex profiles// Structured logging with slog (Go 1.21+)
log := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo,
}))
log.Info("request completed", "method", r.Method, "path", r.URL.Path, "status", 200, "duration", time.Since(start))
| Anti-Pattern | Why | Fix |
|---|---|---|
time.Sleep for synchronization | Flaky tests, wasted time | Use channels, WaitGroup, errgroup |
context.Background() in library code | Never cancellable | Pass context from caller |
| Closing channel from receiver | Panics sender | Creator closes, receiver reads |
panic in non-recoverable position | Crashes process | Return error instead |
Global sync.WaitGroup | Unclear ownership | Scoped to function/call |
select without default on single channel | Blocks forever | Use for range or single <-ch |
context.WithTimeout for database and HTTP requestsReadHeaderTimeout, ReadTimeout, WriteTimeout, IdleTimeout on serverfunc TestWorkerPool_ProcessesAllJobs(t *testing.T) {
pool := NewWorkerPool(3)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
pool.Start(ctx)
for i := 0; i < 10; i++ {
pool.Submit(Job{ID: i})
}
results := make([]Result, 0, 10)
for i := 0; i < 10; i++ {
select {
case r := <-pool.Results():
results = append(results, r)
case <-ctx.Done():
t.Fatal("timeout waiting for results")
}
}
assert.Len(t, results, 10)
}
Always run with -race flag: go test -race ./.... Use sync.WaitGroup or errgroup for coordination. Never use atomic as a substitute for proper synchronization design.
go vet and -race in CI — catch deadlocks and data races before they reach production.No artifact produced. Next skill: backend-testing — test Go patterns. Carry forward: concurrency model, HTTP handler DI pattern, graceful shutdown.
function createModule<T>(config: ModuleConfig): T { const dependencies = initializeDependencies(config); const module = new Module(dependencies); module.hooks.onInit(); return module as T; }
class ConfigBuilder { private config: AppConfig = new AppConfig(); withDatabase(url: string): ConfigBuilder { ... } withCache(ttl: number): ConfigBuilder { ... } withLogging(level: string): ConfigBuilder { ... } build(): AppConfig { return this.config; } }
| Metric | Threshold | Severity | Action |
|---|---|---|---|
| Error rate | > 1% | Critical | Rollback or fix |
| p95 latency | > 500ms | Warning | Profile and optimize |
| Uptime | < 99.9% | Critical | Investigate infrastructure |
| Memory usage | > 80% | Warning | Check for leaks |
| CPU usage | > 80% | Warning | Scale up or optimize |
type ServerOption func(*Server)
func WithPort(port int) ServerOption {
return func(s *Server) {
s.port = port
}
}
func WithTimeout(d time.Duration) ServerOption {
return func(s *Server) {
s.timeout = d
}
}
func NewServer(opts ...ServerOption) *Server {
s := &Server{port: 8080, timeout: 30 * time.Second}
for _, opt := range opts {
opt(s)
}
return s
}
type Pool struct {
jobs chan Job
results chan Result
done chan struct{}
}
func NewPool(numWorkers int) *Pool {
p := &Pool{
jobs: make(chan Job, 100),
results: make(chan Result, 100),
done: make(chan struct{}),
}
for i := 0; i < numWorkers; i++ {
go p.worker()
}
return p
}
func (p *Pool) worker() {
for job := range p.jobs {
result := job.Execute()
p.results <- result
}
}
signal.NotifyContext for SIGINT/SIGTERM. Drain connections before exit.GOMAXPROCS set to CPU limit in container. ulimit -n for file descriptors.go_memstats_alloc_bytes.context.Context for cancellation.pprof endpoints behind admin guard. CPU and heap profiles on demand.-ldflags="-s -w" for smaller binaries. -trimpath for reproducible builds.| Anti-Pattern | Why It Hurts | Fix |
|---|---|---|
| Global state | Race conditions. Untestable. | Dependency injection. Pass dependencies explicitly. |
| Background goroutines without cleanup | Leaked goroutines. | errgroup.WithContext for lifecycle management. |
context.Background() everywhere | No cancellation propagation. | Derive context from request. Pass through call chain. |
| Interface pollution | Many single-method interfaces. | Define interfaces where they're consumed, not produced. |
recover() outside deferred func | Panic not caught. | Always use defer recover() in goroutine entry points. |
| Deep package nesting | Import cycles. Hard to navigate. | Flat or shallow package layout. internal/ for private code. |
json.Marshal / json.Unmarshal with json.Encoder/json.Decoder for streams.sync.Pool for frequently allocated objects. Reduces GC pressure.strings.Builder over bytes.Buffer for string concatenation.make([]T, 0, capacity) when size is known.map with struct keys over string keys for structured lookups.io.Copy with io.Pipe for streaming transformations.-gcflags="-m".runtime.GOMAXPROCS(0) matches CPU quota. No oversubscription.-PGO flag in Go 1.21+.validator package for struct tags. Always validate at boundaries.database/sql. Never string concatenation.html/template auto-escapes. Never use template.HTML with user input.os.Getenv with defaults.http.Server with TLSConfig. Minimum TLS 1.2. Strong cipher suites.rs/cors middleware. Allow specific origins only.golang-jwt/jwt. Short expiry. Refresh token rotation.Cache hierarchy: L1 (in-memory local) → L2 (distributed Redis/Memcached) → L3 (CDN/Edge). Cache invalidation: TTL-based (simple, stale), event-based (complex, fresh), write-through (consistent, higher write latency), write-behind (fast writes, eventual consistency).