Termux — отличный инструмент для разработки и тестирования сервисов прямо с мобильного устройства. В этой статье мы соберём минимальную, но полноценную связку: gRPC-сервер на Go, описание API через Protobuf и автоматическую генерацию клиентского кода. В результате вы сможете быстро разворачивать микросервисы, обновлять контракты и не вручную править клиентские вызовы.
Фокус статьи — практический: командами в Termux, воспроизводимый сценарий генерации и запуск нескольких компонентов в локальной сети (без привязки к обходу блокировок).
Архитектура примера: контракт и клиент
Сделаем два микросервиса (для демонстрации связей):
- user-service — gRPC-сервер, реализует методы работы с пользователями (например, получение списка).
- client — клиент, который генерируем автоматически из .proto файлов и вызывает user-service.
Параллельно покажем шаблон, как масштабировать под несколько сервисов: добавляете протокол — обновляете генерацию — пересобираете клиент/сервер.
Подготовка окружения в Termux
Перед началом убедитесь, что у вас установлены актуальные пакеты. Обновим репозитории и поставим базовые утилиты. Для Go и генерации кода пригодятся git, curl, unzip и инструменты сборки.
pkg update -y
pkg upgrade -y
pkg install -y git curl unzip clang make tar jq
Установка Go в Termux
Есть несколько способов поставить Go. Один из удобных — через официальные бинарники. Ниже — типовой сценарий: скачиваем архив, распаковываем и добавляем путь в $PATH. Команды могут отличаться по версии; выбирайте актуальную стабильную версию Go.
# Пример: скачиваем Go (версию подставьте актуальную)
GO_VERSION=1.22.5
cd /sdcard
curl -L -o go.tar.gz https://go.dev/dl/go${GO_VERSION}.linux-arm64.tar.gz
rm -rf /data/data/com.termux/files/usr/local/go
mkdir -p /data/data/com.termux/files/usr/local
tar -xzf go.tar.gz -C /data/data/com.termux/files/usr/local
# Настройка PATH (если ещё не сделано)
# Добавьте в конец ~/.bashrc или ~/.zshrc строки:
# export PATH="$PATH:/data/data/com.termux/files/usr/local/go/bin"
echo 'export PATH="$PATH:/data/data/com.termux/files/usr/local/go/bin"' >> ~/.bashrc
source ~/.bashrc
# Проверка
go version
Если у вас другой ABI (arm/arm64), подберите соответствующий дистрибутив Go для вашей архитектуры.
Поднимаем структуру проекта
Создадим рабочую папку. В ней будет:
proto/— файлы.protouser-service/— сервер на Goclient/— клиент на Go (генерируется частично)scripts/— скрипт генерации
mkdir -p termux-grpc-example/{proto,user-service,client,scripts}
cd termux-grpc-example
Описание API в Protobuf
Создадим протокол сервиса UserService. Например, метод ListUsers, который возвращает список пользователей.
cat > proto/user.proto <<'EOF'
syntax = "proto3";
package user.v1;
option go_package = "termux-grpc-example/gen/user/v1;userv1";
message User {
string id = 1;
string name = 2;
}
message ListUsersRequest {
int32 limit = 1;
}
message ListUsersResponse {
repeated User users = 1;
}
service UserService {
rpc ListUsers(ListUsersRequest) returns (ListUsersResponse);
}
EOF
Ключевой момент — option go_package. Он задаёт, куда будут попадать сгенерированные Go-файлы. Это упростит дальнейшую сборку.
Установка protoc и плагинов для генерации Go-кода
Для генерации gRPC и Protobuf под Go нужны:
- protoc
- protoc-gen-go
- protoc-gen-go-grpc
В Termux можно поставить protoc и плагины через загрузку и go install.
# protoc (пример для linux-arm64; при необходимости поменяйте архитектуру)
PROTOC_VERSION=27.2
cd /sdcard
curl -L -o protoc.zip https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VERSION}/protoc-${PROTOC_VERSION}-linux-aarch_64.zip
unzip -o protoc.zip -d /data/data/com.termux/files/usr
# Проверка
/data/data/com.termux/files/usr/bin/protoc --version || protoc --version
# Плагины (установятся в GOPATH/bin)
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
# Убедитесь, что GOPATH/bin в PATH
echo 'export PATH="$PATH:$(go env GOPATH)/bin"' >> ~/.bashrc
source ~/.bashrc
# Проверка
which protoc-gen-go
which protoc-gen-go-grpc
Автоматическая генерация клиентского кода
Чтобы клиентский код не обновлялся вручную, сделаем скрипт генерации. Он будет:
- вызывать
protoc - генерировать стандартные структуры Protobuf
- генерировать интерфейсы/код для gRPC
- размещать результат в заранее согласованной директории
gen/
Обычно клиент и сервер используют одни и те же сгенерированные типы.
cat > scripts/generate.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT_DIR"
mkdir -p gen
# Генерация Go кода из proto
protoc \
--proto_path="$ROOT_DIR/proto" \
--go_out="$ROOT_DIR" \
--go_opt=module=termux-grpc-example \
--go-grpc_out="$ROOT_DIR" \
--go-grpc_opt=module=termux-grpc-example \
"$ROOT_DIR/proto/user.proto"
echo "Generation complete. Output in ./gen (as per option go_package)."
EOF
chmod +x scripts/generate.sh
# Запуск генерации
./scripts/generate.sh
# Посмотреть что сгенерировалось
find gen -maxdepth 5 -type f -name '.go' | head
Примечание: конкретная структура папок в gen/ зависит от значения option go_package. Важно, чтобы Go-пакеты соответствовали import путям в ваших проектах.
Реализация gRPC-сервера user-service на Go
Теперь создадим сервер. Он будет реализовывать интерфейс UserService и возвращать фиктивные данные.
cd user-service
# Инициализация модуля Go
# Имя модуля можно сделать одинаковым для простоты примера
cat > go.mod <<'EOF'
module termux-grpc-example/user-service
go 1.22
require (
google.golang.org/grpc v1.66.0
google.golang.org/protobuf v1.34.2
)
replace termux-grpc-example => ..
EOF
mkdir -p internal
cat > internal/server.go <<'EOF'
package internal
import (
"context"
"fmt"
userv1 "termux-grpc-example/gen/user/v1"
)
type Server struct {
userv1.UnimplementedUserServiceServer
}
func (s Server) ListUsers(ctx context.Context, req userv1.ListUsersRequest) (userv1.ListUsersResponse, error) {
limit := int(req.GetLimit())
if limit <= 0 {
limit = 5
}
users := make([]userv1.User, 0, limit)
for i := 0; i < limit; i++ {
users = append(users, &userv1.User{
Id: fmt.Sprintf("u%03d", i+1),
Name: fmt.Sprintf("User %d", i+1),
})
}
return &userv1.ListUsersResponse{Users: users}, nil
}
EOF
cat > main.go <<'EOF'
package main
import (
"log"
"net"
"google.golang.org/grpc"
"termux-grpc-example/user-service/internal"
userv1 "termux-grpc-example/gen/user/v1"
)
func main() {
// Для локальной разработки на телефоне удобно слушать на 0.0.0.0
addr := ":50051"
lis, err := net.Listen("tcp", addr)
if err != nil {
log.Fatalf("listen: %v", err)
}
grpcServer := grpc.NewServer()
userv1.RegisterUserServiceServer(grpcServer, &internal.Server{})
log.Printf("user-service listening on %s", addr)
if err := grpcServer.Serve(lis); err != nil {
log.Fatalf("serve: %v", err)
}
}
EOF
# Подтянуть зависимости
go mod tidy
# Сборка/запуск
cd ..
cd user-service
# В отдельном окне Terminal запустите:
# go run .
# (Обычно это шаг, который вы повторяете после правок)
go run .
EOF
Остановить сервис можно нажатием Ctrl+C.
Реализация клиента с использованием сгенерированного кода
Клиент будет использовать сгенерированные структуры и stub для вызовов. Сгенерированный код приходит из тех же .proto, поэтому после изменения контракта достаточно снова запустить генерацию.
cd ../client
cat > go.mod <<'EOF'
module termux-grpc-example/client
go 1.22
require (
google.golang.org/grpc v1.66.0
google.golang.org/protobuf v1.34.2
)
replace termux-grpc-example => ..
EOF
cat > main.go <<'EOF'
package main
import (
"context"
"log"
"time"
"google.golang.org/grpc"
userv1 "termux-grpc-example/gen/user/v1"
)
func main() {
// На практике IP берите из вашей локальной сети.
// Для простоты: предполагаем, что клиент и сервер запущены на одном устройстве.
// Тогда используйте 127.0.0.1.
// Если между устройствами — подставьте IP телефона/хоста в локальной сети.
target := "127.0.0.1:50051"
conn, err := grpc.Dial(target, grpc.WithInsecure())
if err != nil {
log.Fatalf("dial: %v", err)
}
defer conn.Close()
c := userv1.NewUserServiceClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), 5time.Second)
defer cancel()
resp, err := c.ListUsers(ctx, &userv1.ListUsersRequest{Limit: 3})
if err != nil {
log.Fatalf("ListUsers: %v", err)
}
for _, u := range resp.GetUsers() {
log.Printf("%s: %s", u.GetId(), u.GetName())
}
}
EOF
go mod tidy
# Запуск клиента (в отдельном терминале, пока сервер работает)
go run .
Если сервер и клиент на разных устройствах: вместо 127.0.0.1 укажите IP того устройства, где работает user-service. Убедитесь, что порт 50051 доступен в вашей локальной сети.
Запуск микросервисов в локальной сети (опционально)
Если вы хотите проверять взаимодействие между несколькими устройствами в одной локальной сети, используйте инфраструктуру вашей сети (Wi‑Fi и т.п.). При необходимости можно организовать локальную сеть через VPN-средства, но строго для объединения устройств в локальный контур разработки.
Примерно ваши действия выглядят так:
- сервер слушает
:50051(или0.0.0.0:50051) - клиент указывает
targetкак IP сервера в локальной сети - проверяете, что firewall/настройки сети не блокируют соединение
В случае проблем начните с проверки: сервер доступен с устройства клиента на уровне TCP (хотя gRPC — поверх TCP).
Изменение контракта: как не сломать клиента
Один из ключевых плюсов Protobuf + генерации — снижение риска ручных несоответствий. Процесс изменения обычно такой:
- обновляете
proto/user.proto - перегенерируете код командой
./scripts/generate.sh - пересобираете/перезапускаете сервер и клиента
Например, после изменения протокола выполните:
cd /sdcard/termux-grpc-example
./scripts/generate.sh
# затем перезапустите
# (в разных терминалах)
# cd user-service && go run .
# cd client && go run .
Продвижение к микросервисам: практические рекомендации
- Разделяйте протоколы: для каждого сервиса держите отдельные
.protoили папки. - Соглашения по версиям: используйте namespace вида
user.v1. - Единая генерация: держите генерацию в одном скрипте (как показано в
scripts/generate.sh), чтобы команда разработки делала одно и то же. - Наблюдаемость: добавляйте логирование в сервер и печатайте информацию о входящих запросах.
Заключение
Мы собрали практичный пример разработки микросервисов на Go в Termux: описали API в Protobuf, настроили генерацию клиентского и серверного кода для gRPC, реализовали user-service и запустили клиента, который вызывает методы через сгенерированные stub’ы. Такой подход ускоряет итерации и уменьшает вероятность ошибок при изменении контракта.
Если хотите, РыбинскЛАБ поможет довести проект до промышленного уровня: структура модулей, автоматизация сборки/генерации, Docker-ориентированный workflow, CI для репорта контрактов и стабильная схема версионирования. Обращайтесь в РыбинскЛАБ — поможем внедрить gRPC/Protobuf микросервисы под ваши задачи.