Skip to content

Repository files navigation

Blog Microservice

Это минимальная реализация задания 1. Сервер возвращает захардкоденные ответы, без реального хранилища.

Отсчет к дз1: Как запустить локально

Скопируйте проект и выполните:

go mod download
go run ./cmd/server

После начала работы сервера, можно отправить запросы на него. Например:

# Через HTTP gateway
curl "http://localhost:8080/v1/posts?limit=10&offset=0" | jq

# С пользовательским заголовком
curl -H "x-user-id: user123" "http://localhost:8080/v1/posts?limit=5&offset=0" | jq

# Создать новый пост
curl -X POST "http://localhost:8080/v1/posts" \
  -H "Content-Type: application/json" \
  -H "x-user-id: alice" \
  -d '{"body": "Это мой первый пост через HTTP API!"}' | jq

# Редактировать пост
curl -X PUT "http://localhost:8080/v1/posts/1" \
  -H "Content-Type: application/json" \
  -H "x-user-id: alice" \
  -d '{"body": "Отредактированный текст поста"}' | jq

# Поставить лайк
curl -X POST "http://localhost:8080/v1/posts/1:like" \
  -H "Content-Type: application/json" \
  -H "x-user-id: bob" \
  -d '{}' | jq

# Убрать лайк
curl -X POST "http://localhost:8080/v1/posts/1:unlike" \  
  -H "Content-Type: application/json" \
  -H "x-user-id: bob" \
  -d '{}' | jq

# Удалить пост
curl -X DELETE "http://localhost:8080/v1/posts/1" \
  -H "x-user-id: alice" | jq

Так же через браузер можно проверить его страничку

open http://localhost:8080/swagger/

Отсчет к дз 2:

Для начала нужно утановить запустить Redis, для этого нужно установить его и ввести следующие комманды в терминал:

sudo apt install redis-server
redis-server

Далее установим и запустим PosgreSQL:

sudo apt install postgresql postgresql-contrib
sudo service postgresql start

Создадим таблицы в PosgreSQL и выдадим неоходимые права на сехму и на таблицы в ней:

# Очистить всё и создать заново
sudo -u postgres psql -c "DROP DATABASE IF EXISTS blog;"
sudo -u postgres psql -c "DROP USER IF EXISTS blog_user;"

# Создать базу и пользователя
sudo -u postgres psql -c "CREATE DATABASE blog;"
sudo -u postgres psql -c "CREATE USER blog_user WITH PASSWORD 'blog_password';"
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE blog TO blog_user;"

# Выдать права в базе blog
sudo -u postgres psql -d blog -c "GRANT ALL ON SCHEMA public TO blog_user;"
sudo -u postgres psql -d blog -c "GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO blog_user;"
sudo -u postgres psql -d blog -c "GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO blog_user;"
sudo -u postgres psql -d blog -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO blog_user;"
sudo -u postgres psql -d blog -c "CREATE EXTENSION IF NOT EXISTS \"pgcrypto\";"

И мы должны получить следующий вывод:

DROP DATABASE
DROP ROLE
CREATE DATABASE
CREATE ROLE
GRANT
GRANT
GRANT
GRANT
ALTER DEFAULT PRIVILEGES
CREATE EXTENSION

Далее можно запускать сервер:

go run ./cmd/server

После того как сервер запустился можем проверить, работоспособность пробными запросами:

psql -h localhost -U blog_user -d blog -c "SELECT * FROM posts;"
psql -h localhost -U blog_user -d blog -c "SELECT * FROM authors;"

alt text

Все теми же коммандами (curl'ми из начала редмишника) из первого дз можно проверить работоспособность сервера

Теперь попробуем создать пост и проверить сохраняется ли он в бд (для поста нужно заранее узнать айди вашего пользователя и вписать в соответсвующее поле):

# Вставить сгенерированный x-user-id, он не постоянный при разныъ сеансах
curl -v -X POST "http://localhost:8080/v1/posts" \
  -H "Content-Type: application/json" \
  -H "x-user-id: af448f1a-e198-4044-9f2c-4ca7493afc49" \
  -d '{"body": "Это созданный пост!"}'

  # Проверяем бд
  sudo -u postgres psql -d blog -c "SELECT * FROM posts;"
  sudo -u postgres psql -d blog -c "SELECT * FROM authors;"

Видим, что при существующем айди в таблице появляется новый пост, а авторов остается то же количество, а при несущуствующем айди появляется новый автор, у которого появляется один пост, так что все работает как и планировалось

alt text

Для теста редактирования и удаления постов, можно воспользоваться кодом ниже:

curl -X PUT "http://localhost:8080/v1/posts/eb118ba7-097e-41ab-b133-5501348d8508" \
  -H "Content-Type: application/json" \
  -H "x-user-id: af448f1a-e198-4044-9f2c-4ca7493afc49" \
  -d '{"body": "Этот пост заредачен!"}' | jq # Заменить пост айди на какой-нибудь пост из бд и юзер айди на юзера, который создал этот пост, причем если указать айди не автора поста, то пост нельзя будет поменять

curl -X DELETE "http://localhost:8080/v1/posts/fec1937b-efd4-4dd9-9e2a-61f8fd0169dc" \
  -H "x-user-id: 51a424f8-5ce3-4849-9a60-39403fcb5ea6" | jq #  Заменить пост айди на какой-нибудь пост из бд и юзер айди на юзера, который создал этот пост, причем если указать айди не автора поста, то пост нельзя будет удалить

curl -X GET "http://localhost:8080/v1/posts" | jq # Получить все посты
  

Аналогично можно проверить все варианты разных ответов функций, которые сохранились с предыдущего дз.

Теперь проверим Redis:

# Ставим лайк от пользователя bob
curl -X POST "http://localhost:8080/v1/posts/ef21f5ea-2716-497d-a512-753b80a509a2:like" \
  -H "Content-Type: application/json" \
  -H "x-user-id: a24d1dca-73d6-437d-8714-8e5933fcf0ab" \
  -d '{}' | jq

curl -X POST "http://localhost:8080/v1/posts/ef21f5ea-2716-497d-a512-753b80a509a2:unlike" \
  -H "Content-Type: application/json" \
  -H "x-user-id: a24d1dca-73d6-437d-8714-8e5933fcf0ab" \
  -d '{}' | jq

alt text

Пользователь может поставить лайк только один раз, потом будет выдаваться ошибка. Пользовтель может убрать лайк только с того поста, который он пролайкал. Тоесть система лайков имплементированна верно.
Для общего числа лайкнувших пост использован тип данных hash

// post_likes: {post_id -> like_count}
HGET post_likes "ef21f5ea-2716-497d-a512-753b80a509a2"

Для флага 'лайкнул ли пользователь' использован тип данных set

// post_likes_users:{post_id}: {user_id1, user_id2, ...}
SISMEMBER post_likes_users:ef21f5ea-... a24d1dca-...

Кеширование главное страницы присутствует:

time curl -s "http://localhost:8080/v1/posts?limit=10&offset=0" > /dev/null # Первый запрос будет отображаться в терминале, в котором запущен сервер

time curl -s "http://localhost:8080/v1/posts?limit=10&offset=0" > /dev/null # Второй запрос не будет отображаться в терминале и будет быстрее

Чтобы оптимальнее сделать получение персонализированных флагов лайкнутости нужно вместо полноценного цикла (тогда будет 10 запросов подряд) использовать пайплайн, тогда мы делаем всего один сетевой запрос. Для этого пайплайн использован в следующих функциях:

# Использование pipeline в GetLikedFlags
pipe := r.client.Pipeline()
for _, postID := range postIDs {
    cmds[postID] = pipe.SIsMember(ctx, key, userID)
}
_, err := pipe.Exec(ctx) # один вызов


# Использование pipeline в GetLikesCount
pipe := r.client.Pipeline()  
for _, postID := range postIDs {
    cmds[postID] = pipe.HGet(ctx, "post_likes", postID)
}
_, err := pipe.Exec(ctx) # один вызов

Отсчет к дз3:

Дз небольшое и не затрагивает функционал проекта, только немного изменяет его. Вместе со стартом сервера будем логгировать запросы в консоль, собирать метрики в prometheus

Для проверки логгирования в консоли, можно дать серверу запросы и увидеть логи, например:

# Запрос всех постов
curl "http://localhost:8080/v1/posts?limit=5" | jq

# Сделать пост
curl -X POST "http://localhost:8080/v1/posts" \
  -H "Content-Type: application/json" \
  -H "x-user-id: test-user-123" \
  -d '{"body": "Тест логирования"}' | jq

# Лайкнуть пост
curl -X POST "http://localhost:8080/v1/posts/ef21f5ea-2716-497d-a512-753b80a509a2:like" \
  -H "Content-Type: application/json" \
  -H "x-user-id: a24d1dca-73d6-437d-8714-8e5933fcf0ab" \
  -d '{}' | jq

Вот пример логгирования в консоль, в консоли в начале лога написанно чего это лог, gRPC или Redis'a. alt text

Для проверки метрик Prometheus:, нужно пройти по http://localhost:8080/metrics, там будут лежать все логи.

alt text

Видим, что все запросы попадают в bucket "le=0.005". Ошибки в like и unlike потому, что я намеренно ставил два лайка и убирал два лайка с одного и того же аккаунта.

Отсчет к дз4:

Докеризируем наш сервис, для этого напишем Dockerfile и docker-compose.yml, после чего нужно исполнить

sudo docker-compose up -build

После чего сервис скомпилируется в бинарник и запустится в контейнере:

alt text

Отсчет к дз4:

Добавлены тесты для бд и кэша с дефолтными параметрами и с недефолтными, для тестирования нужно:

go test ./internal/service -v

Получаем два пасса alt text

Далее в отдельной директории client/ делаем клиента который первой фазой параллельно 100 горутинами будет создавать посты, далее 1000 горутинами с рандомными limit и offset будет делать запросы. После чего напишем для него собственные Dockerfile docker-compose и go.mod. После чего сначала запускаем сервер коммандой

sudo docker compose up -d blog-service postgres redis

И далее запускаем клиента (если написать просто docker compose up --build, то клиент прогонит все быстрее чем сервер поднимер редис и постгрес и будет 0 созданных постов)

sudo docker compose run --rm blog-client

Тогда получим:

alt text

Получим, что примерно половина запросов выполнились до таймаута. Таким образом сервер протестирован с клиент в отдельном контейнере как требовалось в тз.

About

Go microblog backend with gRPC, REST API, Redis, PostgreSQL, and Docker.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages