Обзор
в предыдущей статье«Микросервисы go-kit: регистрация и обнаружение сервисов»В , арифметическая служба зарегистрирована в консуле, а служба обнаружения использует набор инструментов go-kit для реализации функции обнаружения арифметической службы. Посмотрев исходный код, можно обнаружить, что реализован только один интерфейс./calculate
, а если интерфейсов для арифметических сервисов много?
Эта проблема беспокоит меня последние несколько дней, я думаю, что go-kit даст разумное решение, но я его не нашел.sd.Factory
Я до сих пор не понимаю конструкции .
Чтобы решить эту проблему, я решил найти другой способ: динамически запрашивать экземпляр службы реестра по клиентскому HTTP-запросу, а вызов фоновой службы реализовать через реверс-прокси.
Это эквивалентно реализации простого шлюза, и все запросы, соответствующие правилам, могут вызывать серверные службы через этот шлюз. Правило здесь относится к пути к ресурсу HTTP-запроса./{serviceName}/#
. То есть: первая часть пути — это имя экземпляра службы реестра, а остальная часть — путь REST экземпляра службы. как:
/arithmetic/calculate/Add/10/2
-
arithmetic
для имени службы; -
/calculate/Add/10/2
Интерфейс для арифметических сервисов.
внедрить шлюз
Шаг 1: Идеи реализации
Клиент инициирует запрос к шлюзу, шлюз анализирует информацию в пути к ресурсу запроса, запрашивает экземпляр службы реестра в соответствии с именем службы, а затем использует технологию обратного прокси-сервера для пересылки запроса клиента в реальный экземпляр службы. на серверной части, а затем верните ответную информацию клиенту.
- Правила для HTTP-запросов следующие:
/{serviceName}/#
, иначе он не будет одобрен. - Используйте обратный прокси-пакет, предоставленный golang
httputil.ReverseProxy
Реализуйте простой обратный прокси-сервер, который может распределять нагрузку по запросам и случайным образом отправлять запросы экземплярам службы. - Используйте API-интерфейс клиента консула для динамического запроса экземпляров службы.
Шаг 2: Напишите метод обратного прокси
существуетarithmetic_consul_demo
Создайте каталог подgateway
, а затем создайте новый файл gomain.go
. Метод NewReverseProxy принимает два параметра: клиентский объект консула и средство ведения журнала, которое возвращает объект обратного прокси. Реализация этого метода заключается в следующем:
- Получить путь запроса, проверить, соответствует ли он правилам, и вернуться напрямую, если он не соответствует правилам;
- Разобрать путь запроса и получить имя службы (первая часть — это имя службы);
- Используйте клиент консула для запроса экземпляра службы, и если результат запроса найден, он случайным образом выбирается в качестве целевого экземпляра;
- В зависимости от выбранного целевого экземпляра задайте параметры обратного прокси:
Schema
,Host
,Path
.
Полный код выглядит следующим образом:
// NewReverseProxy 创建反向代理处理方法
func NewReverseProxy(client *api.Client, logger log.Logger) *httputil.ReverseProxy {
//创建Director
director := func(req *http.Request) {
//查询原始请求路径,如:/arithmetic/calculate/10/5
reqPath := req.URL.Path
if reqPath == "" {
return
}
//按照分隔符'/'对路径进行分解,获取服务名称serviceName
pathArray := strings.Split(reqPath, "/")
serviceName := pathArray[1]
//调用consul api查询serviceName的服务实例列表
result, _, err := client.Catalog().Service(serviceName, "", nil)
if err != nil {
logger.Log("ReverseProxy failed", "query service instace error", err.Error())
return
}
if len(result) == 0 {
logger.Log("ReverseProxy failed", "no such service instance", serviceName)
return
}
//重新组织请求路径,去掉服务名称部分
destPath := strings.Join(pathArray[2:], "/")
//随机选择一个服务实例
tgt := result[rand.Int()%len(result)]
logger.Log("service id", tgt.ServiceID)
//设置代理服务地址信息
req.URL.Scheme = "http"
req.URL.Host = fmt.Sprintf("%s:%d", tgt.ServiceAddress, tgt.ServicePort)
req.URL.Path = "/" + destPath
}
return &httputil.ReverseProxy{Director: director}
}
Шаг 3: Напишите основной метод
Основная задача метода main — создать объект подключения к консулу, создать объект логирования и запустить HTTP-сервис обратного прокси. Весь процесс аналогичен предыдущим примерам, вставляем код напрямую (для удобства тестирования прямо указываю адресную информацию консул-сервиса):
func main() {
// 创建环境变量
var (
consulHost = flag.String("consul.host", "192.168.192.146", "consul server ip address")
consulPort = flag.String("consul.port", "8500", "consul server port")
)
flag.Parse()
//创建日志组件
var logger log.Logger
{
logger = log.NewLogfmtLogger(os.Stderr)
logger = log.With(logger, "ts", log.DefaultTimestampUTC)
logger = log.With(logger, "caller", log.DefaultCaller)
}
// 创建consul api客户端
consulConfig := api.DefaultConfig()
consulConfig.Address = "http://" + *consulHost + ":" + *consulPort
consulClient, err := api.NewClient(consulConfig)
if err != nil {
logger.Log("err", err)
os.Exit(1)
}
//创建反向代理
proxy := NewReverseProxy(consulClient, logger)
errc := make(chan error)
go func() {
c := make(chan os.Signal)
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
errc <- fmt.Errorf("%s", <-c)
}()
//开始监听
go func() {
logger.Log("transport", "HTTP", "addr", "9090")
errc <- http.ListenAndServe(":9090", proxy)
}()
// 开始运行,等待结束
logger.Log("exit", <-errc)
}
Шаг 4: Запустите
- Запустите консул с докером. Терминал переключается на
arithmetic_consul_demo
директории выполните следующую команду:
sudo docker-compose -f docker/docker-compose.yml up
- Запустите службу арифметических операций. Чтобы проверить эффект балансировки нагрузки, я запустил два экземпляра.Обратите внимание на необходимость использования другого порта
./register/register -consul.host localhost -consul.port 8500 -service.host 192.168.192.146 -service.port 9000
./register/register -consul.host localhost -consul.port 8500 -service.host 192.168.192.146 -service.port 9002
- Введите в браузере
http://localhost:8500
видимыйarithmetic
Есть два примера, как показано ниже:
- cd в каталог в терминале
gateway
,воплощать в жизньgo build
Завершите компиляцию, затем запустите службу шлюза.
> ./gateway -consul.host localhost -consul.port 8500
> ts=2019-02-26T07:49:39.0468058Z caller=main.go:54 transport=HTTP addr=9090
Шаг 5: Тест
Используйте postman для выполнения теста запроса, как показано ниже, и вы обнаружите, что вызов службы прошел успешно.
В то же время вы можете увидеть следующий вывод на терминале, указывающий на то, что несколько запросов обращались к разным экземплярам службы:
ts=2019-02-26T07:49:39.0468058Z caller=main.go:54 transport=HTTP addr=9090
ts=2019-02-26T07:49:46.8559985Z caller=main.go:94 serviceid=arithmetic82460623-ccdc-4192-a042-c0603ef18888
ts=2019-02-26T07:50:00.1249302Z caller=main.go:94 serviceid=arithmetic65153818-27b3-4f19-8fd1-d7d698168f20
ts=2019-02-26T09:04:09.0470362Z caller=main.go:94 serviceid=arithmetic65153818-27b3-4f19-8fd1-d7d698168f20
ts=2019-02-26T09:04:10.176327Z caller=main.go:94 serviceid=arithmetic65153818-27b3-4f19-8fd1-d7d698168f20
Суммировать
В этой статье используется технология обратного прокси-сервера в сочетании с консулом реестра для реализации простого шлюза API. Поскольку golang предоставляет набор инструментов обратного прокси, весь процесс реализации относительно прост. Продукты, используемые в реальных проектах, такие как Zuul, Nginx и т. д., также включают в себя такие функции, как ограничение тока, фильтрация запросов и аутентификация личности. Шлюз реализует только прокси запроса, и основное внимание уделяется пониманию его внутреннего процесса и углублению понимания.
Ссылка в этой статье
Эта статья была впервые опубликована в моей публичной учетной записи WeChat [Xi Yiang Bar], пожалуйста, отсканируйте код, чтобы следовать!