DevGang
Авторизоваться

Создание обратного прокси-сервера (gRPC-Gateway)

В этой статье мы рассмотрим создание API со следующим шагом реализации обратного прокси.

Мы будем использовать docker и docker-compose, поэтому я рекомендую сначала установить docker и docker-compose и убедиться, что вы можете запускать контейнеры.

Перейдите к вашему GOPATH, если вы не знаете, какой у вас каталог пути, вы можете запустить echo $GOPATH, и он напечатает правильный путь. Там создайте  src/github/com/yaairfernando  структуру папок, заменив последний каталог вашим дескриптором GitHub. Как только вы окажетесь там, создайте следующую структуру проекта:

mkdir socialMedia 
cd socialMedia 
touch docker-compose.yaml

Давайте начнем с определения yaml-файла docker-compose.

version: '2.4'
services:
  proxy:
    container_name: 'proxy'
    image: 'sma:proxy'
    build:
      context: ./proxy
      args:
        - GITHUB_ACCESS_TOKEN=${GITHUB_ACCESS_TOKEN}
    command: bash -c  "./grpc-gateway"
    ports:
      - '9094:8081'
    environment:
      - GRPC_URL=sma:8080
      - REST_PORT=8081

В приведенном выше файле yaml мы определяем proxy службу, из конфигурации этой службы мы видим, что имя контейнера — proxy, а образ, который будет использоваться sma:proxy. Если образ докера не существует, он создаст его из ./proxy папку, мы также передаем в качестве аргументов токен доступа GitHub, это необходимо только в том случае, если вы создали частное репо для дизайна API.

Вам нужно будет передать токен доступа GitHub к образу докера, чтобы получить доступ к частным репозиториям из файла докера.

Мы также указываем команду и две переменные среды для запуска: одну, чтобы указать оставшийся порт, на котором будет работать сервер, и URL-адрес grpc, который указывает на службу, которую прокси-сервер будет передавать запросы.

Мы также указываем, что порт, который эта служба будет предоставлять, будет 9094 внутренним портом 8081.

Давайте теперь создадим папку прокси и main.go файл:

mkdir proxy
cd proxy
touch main.go

Внутри папки прокси мы запустим эту команду, чтобы создать модуль go

go mod init github.com/yaairfernando/proxy

Это создаст go.mod файл в папке прокси.

module github.com/yaairfernando/proxy

go 1.18

Файл main.go выглядит так:

package main

import (
	"os"

	"github.com/yaairfernando/proxy/server"
)

var (
	grpcURL  = os.Getenv("GRPC_URL")
	restPort = os.Getenv("REST_PORT")
)

func main() {
	srv := server.New(grpcURL, restPort)
	srv.Start()
}

Основная функция только инициализирует новый сервер, передавая grpcURLrestPort и вызывая метод запуска на экземпляре сервера.

Давайте посмотрим, как выглядит файл сервера. Создайте папку сервера и server.go файл.

mkdir server
cd server
touch server.go
package server

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

	"github.com/felixge/httpsnoop"
	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
	"github.com/yaairfernando/sma/protos/src/go/sma"
	"google.golang.org/grpc"
)

type Server struct {
	GrpcURL  string
	RestPort string
	srv      *http.Server
}

func New(grpcURL, restPort string) *Server {
	return &Server{
		GrpcURL:  grpcURL,
		RestPort: restPort,
	}
}

func (s *Server) Start() {
	grpcURL := GetGrpcURL(s.GrpcURL)
	restPort := GetRestPort(s.RestPort)

	ctx := context.Background()
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	mux := runtime.NewServeMux(runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.JSONPb{}))

	handler := s.handlerWithLogger(mux)
	opts := []grpc.DialOption{grpc.WithInsecure()}
	err := s.registerHandlers(ctx, mux, grpcURL, opts)
	if err != nil {
		log.Fatal(err)
	}

	s.srv = &http.Server{
		Handler: handler,
		Addr:    fmt.Sprintf(":%s", restPort),
	}
	s.listenAndServe()
}

func (s *Server) registerHandlers(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
	err = sma.RegisterPostsHandlerFromEndpoint(ctx, mux, endpoint, opts)

	return
}

func (s *Server) handlerWithLogger(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
		m := httpsnoop.CaptureMetrics(handler, writer, request)
		log.Printf("http[%d]-- %s -- %s -- %s\n", m.Code, m.Duration, request.URL.Path, request.Method)
	})
}

func (s *Server) listenAndServe() {
	err := s.srv.ListenAndServe()
	if err != nil {
		log.Fatal(err)
	}
}

В этом файле сервера есть вся логика для регистрации обработчика Posts ресурса, добавления простого регистратора, создания HTTP-сервера и прослушивания указанного порта.

  1. В строке 36 мы создаем новый серверный мультиплексор
  2. В строке 38 мы обернули этот сервер простым регистратором.
  3. В строке 40 регистрируем все обработчики. В нашем случае у нас есть только один для Posts ресурса на данный момент
  4. Наконец, в строках 45–49 мы создаем HTTP-сервер, передающий обработчик, и начинаем слушать

Как видите, некоторые вспомогательные функции в этом файле не определены. Я добавил их в utils файл.

package server

func GetGrpcURL(url string) string {
	return LoadEnv(url, "0.0.0.0:8080")
}

func GetRestPort(port string) string {
	return LoadEnv(port, "3000")
}

func LoadEnv(attr, def string) string {
	if len(attr) > 0 {
		return attr
	}

	return def
}

Хорошо, давайте теперь создадим Dockerfile:

FROM golang:1.18-alpine AS builder

ARG GITHUB_ACCESS_TOKEN

ENV GO111MODULE=on \
    CGO_ENABLED=0 \
    GOOS=linux \
    GOARCH=amd64 \
    GOPRIVATE=github.com/yaairfernando \
    GOPROXY=direct

RUN apk --no-cache add git bash && \
  addgroup -S sma-grpc-proxy && \
  adduser -SG sma-grpc-proxy sma-grpc-proxy

WORKDIR $GOPATH/github.com/yaairfernando/sma-grpc-proxy

RUN git config --global url."https://${GITHUB_ACCESS_TOKEN}:x-oauth-basic@github.com/yaairfernando".insteadOf "https://github.com/yaairfernando"

COPY go.mod .

RUN go mod download

COPY . .

RUN CGO_ENABLED=0 go build -ldflags='-w -s' -o /usr/bin/sma-grpc-proxy

FROM scratch

COPY --from=builder /etc/passwd /etc/passwd
COPY --from=builder /usr/bin/sma-grpc-proxy /usr/bin/sma-grpc-proxy

USER sma-grpc-proxy
EXPOSE 8081
ENTRYPOINT ["/usr/bin/sma-grpc-proxy"]

Я не буду углубляться в то, что делает этот файл докера, но, по сути, он использует образ Golang в качестве сборщика, устанавливает некоторые переменные среды go, устанавливает рабочий каталог и копирует go.mod файл. После этого он загружает все зависимости, копирует остальные файлы и, наконец, устанавливает точку входа.

Это go.mod файл со всеми используемыми пакетами.

module github.com/yaairfernando/proxy

go 1.18

require (
	github.com/felixge/httpsnoop v1.0.3
	github.com/grpc-ecosystem/grpc-gateway/v2 v2.10.0
	github.com/yaairfernando/sma v0.1.0
	google.golang.org/grpc v1.46.2
)

require (
	github.com/golang/protobuf v1.5.2 // indirect
	golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
	golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
	golang.org/x/text v0.3.7 // indirect
	google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3 // indirect
	google.golang.org/protobuf v1.28.0 // indirect
)

Это окончательная структура папок для проекта:

Мы почти закончили. Теперь давайте загрузим нужные нам пакеты go, выполнив следующие команды в папке прокси:

go mod download
go mod tidy

Как видите, для sma пакета мы указали, что нам нужна версия v0.1.0 из этого пакета, но когда мы создавали дизайн API, мы не создавали никаких версий.  Давайте сделаем это.

Создать тег Git

Вернитесь туда, где у вас есть sma проект, и давайте создадим тег:

git tag v0.1.0

И отправляем в репозиторий:

git push origin v0.1.0

Теперь, вернувшись в proxy папку, запустите эту команду, чтобы загрузить эту версию sma пакета:

go get github.com/yaairfernando/sma@v0.1.0

Приведенная выше команда загрузит sma пакет с указанной версией.

Сборка Docker-сервиса

Теперь мы можем создать прокси-сервис, запустив:

docker-compose up --build -d proxy

При выполнении этой команды вы получите некоторые ошибки, говорящие о загрузке некоторых пакетов, которые мы указали в go.mod файле. Просто загрузите пакеты, запустив go mod download package_path. И повторно запустите команду docker-compose.

После завершения сборки мы должны увидеть работающий контейнер.  Отправить POST-запрос на http://localhost:9094/v1/postsпока не получится, но лог можно посмотреть.

Прокси работает
Прокси работает

Мы реализовали обратный прокси.

#Golang
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

В этом месте могла бы быть ваша реклама

Разместить рекламу