Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Ильяс Нежибицкий — Применение языка Go в разработке платформенных решений

Ильяс Нежибицкий — Применение языка Go в разработке платформенных решений

Ozon Tech

April 20, 2023
Tweet

More Decks by Ozon Tech

Other Decks in Technology

Transcript

  1. func main() { l := SetLogger(New()) service := CreateNewService(l) }

    // CreateNewService создает инстанс сервиса. func CreateNewService(l *Logger) *Service { return &Service{ l: l, } }
  2. func main() { l := SetLogger(New()) service := CreateNewService(l) }

    // CreateNewService создает инстанс сервиса. func CreateNewService(l *Logger) *Service { return &Service{ l: l, } } func FromContext(ctx context.Context) *Logger { l := global if logger, ok := ctx.Value(loggerContextKey).(*Logger); ok { l = logger } return l } func (s *Service) DoSomeUsefulJob(ctx context.Context) error { logger := FromContext(ctx) // Do anything here return nil }
  3. var ( // global глобальный экземпляр логгера. global *Logger )

    func init() { SetLogger(New()) } // SetLogger устанавливает глобальный логгер. func SetLogger(l *Logger) { global = l } func FromContext(ctx context.Context) *Logger { l := global if logger, ok := ctx.Value(loggerContextKey).(*Logger); ok { l = logger } return l }
  4. func WithMetrics(handlerName string, next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter,

    r *http.Request) { clientName := getClientNameFromRequest(r) started := time.Now() next.ServeHTTP(w, r) timeTaken := metrics.SinceSeconds(started) responseStatus := getStatus(r.Context()) ResponseTime.WithLabelValues(handlerName, responseStatus, clientName).Observe(timeTaken) RequestsTotal.WithLabelValues(handlerName, responseStatus, clientName).Inc() }) }
  5. func WithTracing(next http.Handler) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request)

    { ctx := context.WithValue(r.Context(), "start", time.Now()) span := opentracing.StartSpan(r.URL.Path, ext.RPCServerOption(extractSpanContext(ctx))) ctx = opentracing.ContextWithSpan(ctx, span) r = r.WithContext(ctx) next.ServeHTTP(w, r) span.Finish() } }
  6. func (th *TraceHandler) HandleRPC(ctx context.Context, st stats.RPCStats) { switch st.(type)

    { case *stats.End: if span := opentracing.SpanFromContext(ctx); span != nil { span.Finish() } } } func TapHandler(ctx context.Context, info *tap.Info) (context.Context, error){ ctx = context.WithValue(ctx,"start", time.Now()) span := opentracing.StartSpan(info.FullMethodName, ext.RPCServerOption(extractSpanContext(ctx))) ctx = opentracing.ContextWithSpan(ctx, span) return ctx, nil }
  7. func loggerWithSpanContext(l *Logger, sc opentracing.SpanContext) *Logger { if sc, ok

    := sc.(jaeger.SpanContext); ok { return l.With( zap.Stringer("trace_id", sc.TraceID()), zap.Stringer("span_id", sc.SpanID()), ).Sugar() } return l } func FromContext(ctx context.Context) *Logger { l := global if logger, ok := ctx.Value(loggerContextKey).(*Logger); ok { l = logger } span := opentracing.SpanFromContext(ctx) if span == nil { return l } return loggerWithSpanContext(l, span.Context()) }
  8. func (rl *ratelimMW) Handle(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter,

    r *http.Request) { if err := rl.limiter.DoIfAllowed(r.Context(), r.Method, func() { next.ServeHTTP(w, r) }); err != nil { w.WriteHeader(HTTPStatusFromCode(status.Code(err))) _, _ = w.Write([]byte(err.Error())) } }) }
  9. func (rl *Limiter) UnaryServerInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, h

    grpc.UnaryHandler) (interface{}, error) { var rsp interface{} err := rl.DoIfAllowed(ctx, info.FullMethod, func() (err error) { rsp, err = h(ctx, req) return }) return rsp, err }
  10. // UnaryClientInterceptor returns CB unary client interceptor. func (g *RTCircuitBreakerGroup)

    UnaryClientInterceptor(target string) grpc.UnaryClientInterceptor { return func() error { cb := g.GetCircuit(target, method) err := cb.Execute( ctx, func(ctx context.Context) error { return invoker(ctx, method, req, reply, cc, opts...) }, g.fallbackFunc, ) if cErr, ok := err.(circuit.Error); ok && cErr.CircuitOpen() { return ErrorCircleIsOpen } return err } }
  11. Подключение Circuit breaker в gRPC 49 conn, err := grpc.Dial(

    host, grpc.WithChainUnaryInterceptor( circuitbreaker.DefaultRTCircuitBreaker.UnaryClientInterceptor(host), ), )
  12. func (p *RTProvider) LaunchWatching(ctx context.Context) { b := backoff.WithContext(defaultBackoffOpts, ctx)

    go func() { b.Reset() if err := backoff.Retry(func() error { return p.eventWatcher(ctx, p.key) }, b); err != nil { p.l.Error("RT watcher has been stopped", err) } }() }
  13. // Should be launched in a goroutine func (p *RTProvider)

    eventWatcher(ctx context.Context, key string) error { for { select { case <-p.ticker.C: v, err := p.downloadVariable(key) if err != nil { p.l.Error("failed to pull cluster", zap.Error(err)) } p.varCallback(v) case <-ctx.Done(): // context was cancelled, cancel any retries for that key prefix return backoff.Permanent(ctx.Err()) } } }
  14. • Старайтесь поддерживать DRY не только в рамках вашей разработки,

    но и между командами Выводы 55 *DRY - Don’t repeat yourself
  15. • Старайтесь поддерживать DRY не только в рамках вашей разработки,

    но и между командами Выводы 56 *DRY - Don’t repeat yourself • Наличие общей библиотеки позволяет быстро добавлять общие решения на всю компанию
  16. • Старайтесь поддерживать DRY не только в рамках вашей разработки,

    но и между командами Выводы 57 *DRY - Don’t repeat yourself • Наличие общей библиотеки позволяет быстро добавлять общие решения на всю компанию • Разработчикам не приходится писать одни и те же механизмы для удовлетворения требований к сервисам
  17. • Старайтесь поддерживать DRY не только в рамках вашей разработки,

    но и между командами Выводы 58 *DRY - Don’t repeat yourself • Наличие общей библиотеки позволяет быстро добавлять общие решения на всю компанию • Разработчикам не приходится писать одни и те же механизмы для удовлетворения требований к сервисам • Существует множество готовых механизмов, способных облегчить разработку и эксплуатацию ваших сервисов
  18. • Старайтесь поддерживать DRY не только в рамках вашей разработки,

    но и между командами Выводы 59 *DRY - Don’t repeat yourself • Наличие общей библиотеки позволяет быстро добавлять общие решения на всю компанию • Разработчикам не приходится писать одни и те же механизмы для удовлетворения требований к сервисам • Существует множество готовых механизмов, способных облегчить разработку и эксплуатацию ваших сервисов • Чем раньше вы выделите платформенную библиотеку, тем проще в последствии будет внедрять общие решения на все ваши сервисы
  19. • Старайтесь поддерживать DRY не только в рамках вашей разработки,

    но и между командами Выводы 60 *DRY - Don’t repeat yourself • Наличие общей библиотеки позволяет быстро добавлять общие решения на всю компанию • Разработчикам не приходится писать одни и те же механизмы для удовлетворения требований к сервисам • Существует множество готовых механизмов, способных облегчить разработку и эксплуатацию ваших сервисов • Чем раньше вы выделите платформенную библиотеку, тем проще в последствии будет внедрять общие решения на все ваши сервисы • Но и не стоит начинать стартап с написания платформы :-)