generated from Yandex-Practicum/go-musthave-metrics-tpl
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* рефакторинг агента по итогам встречи с ментором * рефакторинг сервера по результам встречи с ментором * реализовал инкремент 6 * исправления по результатам работы статического анализатора * реализовал iter7 * pool->poll * обновление в chi * исправление по результатам статического анализатора * ещё исправления по статическому анализатору * поправил обновление метрик * убрал лишние проверки, из-за которых не проходили значения спорядком (типа 1.8e+7) * перевел агента на json * реализовал компрессию на сервере при передаче ответов * incr8 * поправил названия * обновил чтение конфигурации для сервера * добавил методы сохранения и восстановления хранилища * наработки * наработки (не собирается) * рефакторинг сервера, тесты пройдены * наработки по инкременту 9 * небольшой комментарий * ещё рефакторинг * наработки по инкременту 9 * изменения по результам работы статического анализатора + доп вопросы + немного комментариев * накидал идею по архитектуре * наработки по gomock * реализовал adapter для ping * небольшой рефакторинг, логирование + postgres * поправил имя * сделал memStorage и pgStorage заменяемыми * iter11 * добавил проверку rows.Err() * переформулировка гексогональной архитектуры с добавлением usecase * Упроситл конфигурацию сервера и вынес в cmd. * реализовал сервер * поправил агент, поправил ошибку в сервере * вопрос про docker * актуализировал вопросы * добавил функцию для обработки ошибок * реализовал обработку ошибок * Добавил обработку ошибки соединения в agent * инкремент 13 * всякая мелочевка * переименовал пару пакетов; заменил самописные заглушки на gomock * закрыл Body * небольшие правки после ревью второго спринта * актуализировал вопросы * добавил все же ReadAll * ещё вопрос
- Loading branch information
1 parent
5a46c41
commit 88e562a
Showing
69 changed files
with
5,184 additions
and
1,261 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
*.png filter=lfs diff=lfs merge=lfs -text |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
- АРХИТЕКТУРА!!! | ||
- убрал картинку - после нескольких итераций пришел к гексогональной архитектуре но с use-кейсами и доменной областью, как средством деления логики приложения | ||
в учебном приложении получилиcь области | ||
- работа с бэкапом | ||
- администривные операции(пока только ping) | ||
- работа с метриками | ||
|
||
|
||
- storage/wrapper/retriable.go - может есть более изящное решение? (дублирование кода, создание функциональные оберток) | ||
|
||
- middleware/testutils/http_handler.go - добавил Handler интерфейс ради генерации mock. Можно ли как-то заставить gomock сгенерировать mock для стандартного интерфейса http.Handler. | ||
|
||
- defer req.Body.Close() в http-хэндлере: | ||
В документации (https://pkg.go.dev/net/http#Request): | ||
The Server will close the request body. The ServeHTTP Handler does not need to. | ||
Получается что можно не заморачиваться? | ||
|
||
- DOCKER - было бы логично добавить тестирование storage на postgres, но смущает реализация unit-тестов и для локального запуска и в связке с github metricstest. (появляются вопросы по реализации - можно ли завязаться на переменные окружение POSTGRES_PASSWORD: postgres, POSTGRES_DB: praktikum и т.д). Возможно будет использование docker в кусре далее? Стоит ли сейчас заниматься? | ||
|
||
- (ОТВЕТ ПОЛУЧЕН; TODO) w.WriteHeader(http.StatusOK) - где писать, надо ли вообще писать? TODO поправить хэндлеры | ||
|
||
- (ОТВЕТ ПОЛУЧЕН; TODO ) Загрузка конфигурации (flag, env) (см internal/config/server.go) - можно ли покрыть тестами (ссылка на пример, если есть; надо ли вообще? - TODO пример - https://github.com/golang/go/blob/master/src/flag/example_test.go) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,188 @@ | ||
package main | ||
|
||
import ( | ||
"log" | ||
"context" | ||
"errors" | ||
"net/http" | ||
"os" | ||
"os/signal" | ||
"syscall" | ||
"time" | ||
|
||
"github.com/StasMerzlyakov/go-metrics/internal/server" | ||
"github.com/StasMerzlyakov/go-metrics/internal/common/wrapper/retriable" | ||
"github.com/StasMerzlyakov/go-metrics/internal/config" | ||
"github.com/StasMerzlyakov/go-metrics/internal/server/adapter/fs/backup" | ||
"github.com/StasMerzlyakov/go-metrics/internal/server/adapter/http/handler" | ||
"github.com/StasMerzlyakov/go-metrics/internal/server/adapter/http/middleware" | ||
"github.com/StasMerzlyakov/go-metrics/internal/server/adapter/http/middleware/compress" | ||
"github.com/StasMerzlyakov/go-metrics/internal/server/adapter/http/middleware/logging" | ||
"github.com/StasMerzlyakov/go-metrics/internal/server/adapter/storage/memory" | ||
"github.com/StasMerzlyakov/go-metrics/internal/server/adapter/storage/postgres" | ||
"github.com/StasMerzlyakov/go-metrics/internal/server/adapter/storage/wrapper" | ||
"github.com/StasMerzlyakov/go-metrics/internal/server/app" | ||
"github.com/StasMerzlyakov/go-metrics/internal/server/domain" | ||
"github.com/go-chi/chi/v5" | ||
"github.com/jackc/pgerrcode" | ||
"github.com/jackc/pgx/v5/pgconn" | ||
|
||
"go.uber.org/zap" | ||
) | ||
|
||
type Server interface { | ||
Start(ctx context.Context) | ||
Shutdown(ctx context.Context) | ||
} | ||
|
||
func createMiddleWareList(log *zap.SugaredLogger) []func(http.Handler) http.Handler { | ||
return []func(http.Handler) http.Handler{ | ||
logging.NewLoggingResponseMW(log), | ||
compress.NewCompressGZIPResponseMW(log), //compress.NewCompressGZIPBufferResponseMW(log), | ||
compress.NewUncompressGZIPRequestMW(log), | ||
logging.NewLoggingRequestMW(log), | ||
} | ||
} | ||
|
||
type FullStorage interface { | ||
app.Storage | ||
app.Pinger | ||
Bootstrap(ctx context.Context) error | ||
Close(ctx context.Context) error | ||
} | ||
|
||
func main() { | ||
|
||
srvConf, err := server.LoadConfig() | ||
// -------- Контекст сервера --------- | ||
srvCtx, cancelFn := context.WithCancel(context.Background()) | ||
|
||
// -------- Конфигурация ---------- | ||
srvConf, err := config.LoadServerConfig() | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
// -------- Логгер --------------- | ||
logger, err := zap.NewDevelopment() | ||
if err != nil { | ||
log.Fatal(err) | ||
// вызываем панику, если ошибка | ||
panic("cannot initialize zap") | ||
} | ||
defer logger.Sync() | ||
|
||
sugarLog := logger.Sugar() | ||
|
||
// -------- Хранилище ------------- | ||
var storage FullStorage | ||
|
||
if srvConf.DatabaseDSN != "" { | ||
storage = postgres.NewStorage(sugarLog, srvConf.DatabaseDSN) | ||
|
||
// при работе с Postgres добавляем retriable-обертку | ||
|
||
// функция, обрабатывающая ошибки; в нужных случаях выкидывает нужную ошибку(domain.ErrDBConnection) | ||
// на которую реагирует retriableWrapper | ||
pgErrPreProcFn := func(err error) error { | ||
if err == nil { | ||
return nil | ||
} | ||
var pgErr *pgconn.PgError | ||
if errors.As(err, &pgErr) { | ||
if pgerrcode.IsConnectionException(pgErr.Code) { | ||
return domain.ErrDBConnection | ||
} | ||
} | ||
|
||
var conErr *pgconn.ConnectError | ||
if errors.As(err, &conErr) { | ||
return domain.ErrDBConnection | ||
} | ||
return err | ||
} | ||
|
||
retriableConf := retriable.DefaultConfFn(domain.ErrDBConnection, pgErrPreProcFn) | ||
storage = wrapper.NewRetriable(retriableConf, sugarLog, storage) | ||
|
||
if err := server.CreateServer(srvConf); err != nil { | ||
} else { | ||
storage = memory.NewStorage() | ||
} | ||
|
||
defer storage.Close(srvCtx) | ||
|
||
if err := storage.Bootstrap(srvCtx); err != nil { | ||
panic(err) | ||
} | ||
|
||
// -------- Бэкап ------------ | ||
backupFomratter := backup.NewJSON(sugarLog, srvConf.FileStoragePath) | ||
backUper := app.NewBackup(sugarLog, storage, backupFomratter) | ||
|
||
if srvConf.Restore { | ||
// восстановленгие бэкапа | ||
if err := backUper.RestoreBackUp(srvCtx); err != nil { | ||
panic(err) | ||
} | ||
} | ||
|
||
// проверяем - нужен ли синхронный бэкап | ||
doSyncBackup := srvConf.StoreInterval == 0 | ||
|
||
if !doSyncBackup { | ||
// запускаем фоновый процесс | ||
go func() { | ||
storeInterval := time.Duration(srvConf.StoreInterval) * time.Second | ||
var ticker = time.NewTicker(storeInterval) | ||
defer ticker.Stop() | ||
for { | ||
select { | ||
case <-srvCtx.Done(): | ||
sugarLog.Infow("Run", "msg", "backup finished") | ||
return | ||
case <-ticker.C: | ||
if err := backUper.DoBackUp(srvCtx); err != nil { | ||
sugarLog.Fatalw("DoBackUp", "msg", err.Error()) | ||
} | ||
} | ||
} | ||
}() | ||
|
||
} | ||
|
||
// ---------- Http сервер ----------- | ||
httpHandler := chi.NewMux() | ||
|
||
// мидлы | ||
mwList := createMiddleWareList(sugarLog) | ||
middleware.Add(httpHandler, mwList...) | ||
|
||
// операции с метриками | ||
metricApp := app.NewMetrics(storage) | ||
handler.AddMetricOperations(httpHandler, metricApp, sugarLog) | ||
|
||
// административные операции | ||
adminApp := app.NewAdminApp(sugarLog, storage) | ||
handler.AddAdminOperations(httpHandler, adminApp, sugarLog) | ||
|
||
// запускаем http-сервер | ||
srv := &http.Server{ | ||
Addr: srvConf.URL, | ||
Handler: httpHandler, | ||
ReadTimeout: 0, | ||
IdleTimeout: 0, | ||
} | ||
|
||
go func() { | ||
if err := srv.ListenAndServe(); err != http.ErrServerClosed { | ||
sugarLog.Fatalw("ListenAndServe", "msg", err.Error()) | ||
panic(err) | ||
} | ||
}() | ||
|
||
// --------------- Обрабатываем остановку сервера -------------- | ||
exit := make(chan os.Signal, 1) | ||
signal.Notify(exit, os.Interrupt, syscall.SIGTERM) | ||
|
||
defer func() { | ||
cancelFn() | ||
srv.Shutdown(srvCtx) | ||
}() | ||
<-exit | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.