1. Три решения для балансировки нагрузки
Построение высокодоступных и высокопроизводительных коммуникационных сервисов обычно реализуется с использованием таких механизмов, как регистрация и обнаружение сервисов, балансировка нагрузки и отказоустойчивая обработка. В зависимости от того, где находится реализация балансировки нагрузки, обычно есть три решения:
- 1. Централизованный LB (прокси-модель)
- 2. Внутрипроцессный LB (клиент с поддержкой балансировки)
- 3. Независимый процесс LB (Внешняя служба балансировки нагрузки)
Источник здесь, написано очень подробно:адрес ссылки
2. Подготовка gRPC
gRPC по умолчанию использует протокольные буферы, которые представляют собой зрелый механизм сериализации данных структуры с открытым исходным кодом Google (конечно, другие форматы данных, такие как JSON, также могут использоваться). Его клиентская часть предоставляет интерфейсы Objective-C и Java, а серверная часть имеет интерфейсы Java, Golang, C++ и другие, что обеспечивает решение для мобильного терминала (iOS/Androi) для связи на стороне сервера.адрес ссылки
1. Устанавливаем brew, это Baidu Google.
2. Терминал:
brew install autoconf automake libtool
3. Установите протобуф Golang
go get -u github.com/golang/protobuf/proto // golang protobuf 库
go get -u github.com/golang/protobuf/protoc-gen-go //protoc --go_out 工具
3. Простой протобуф
../proto/hello.proto
syntax = "proto3";
package proto;
message SayReq {
string content = 1;
}
message SayResp {
string content = 1;
}
service Test{
rpc Say(SayReq) returns (SayResp) {}
}
В протоке, командной входной клемма Protoc --goout = Plugins = GRPC :. Hello.proto
Создайте файл hello.pb.go и используйте protoc для создания файлов .pb.go, необходимых для разных языков.
Вот введение в четыре протокола, Если вам не нужны потоки, вы можете их игнорировать:
1: Простой RPC
2: Потоковая RPC на стороне сервера
3: Клиентская потоковая передача RPC
4: Двунаправленный потоковый RPC
Четыре пути соответствуют четырем службам, определенным ниже: файл test.proto: сервисный тест{
rpc LZX1(SayReq) returns (SayResp) {}
rpc LZX2(SayReq) returns (stream SayResp) {}
rpc LZX3(stream SayReq) returns (SayResp) {}
rpc LZX4(stream SayReq) returns (stream SayResp) {}
}
Сгенерированный файл test.pb.go:
type TestClient interface {
LZX1(ctx context.Context, in *SayReq, opts ...grpc.CallOption) (*SayResp, error)
LZX2(ctx context.Context, in *SayReq, opts ...grpc.CallOption) (Test_LZX2Client, error)
LZX3(ctx context.Context, opts ...grpc.CallOption) (Test_LZX3Client, error)
LZX4(ctx context.Context, opts ...grpc.CallOption) (Test_LZX4Client, error)
}
Следовательно, функции без потока, то есть первый простой RPC, соответствуют только один к одному.Пока есть поток, возвращаемый тип — структура службы имя_имя функции поток, переданные параметры не будут включать Другие параметры.
4.6 Алгоритмы балансировки нагрузки
1. Метод опроса
2. Случайный метод
3. Хэширование исходного адреса
4. Метод взвешенного опроса
5. Взвешенный случайный метод
6, метод минимального количества соединений
Описание алгоритма можно найти здесь:адрес ссылки
V. Примеры GRPC
Пример ссылки на источник:адрес ссылки
Архитектура:
В следующем используется случайная балансировка нагрузки:
клиент etcd/клиент/random/main.go
package main
import (
etcd "github.com/coreos/etcd/client"
grpclb "github.com/liyue201/grpc-lb"
"github.com/liyue201/grpc-lb/examples/proto"
registry "github.com/liyue201/grpc-lb/registry/etcd"
"golang.org/x/net/context"
"google.golang.org/grpc"
"log"
"strconv"
"time"
)
func main() {
etcdConfg := etcd.Config{
Endpoints: []string{"http://120.24.44.201:2379"},
}
r := registry.NewResolver("/grpc-lb", "test", etcdConfg) // 加载registry
b := grpclb.NewBalancer(r, grpclb.NewRandomSelector()) //加载grpclbs
c, err := grpc.Dial("", grpc.WithInsecure(), grpc.WithBalancer(b))
if err != nil {
log.Printf("grpc dial: %s", err)
return
}
defer c.Close()
client := proto.NewTestClient(c)
var num int
for i := 0; i < 1000; i++ {
resp, err := client.Say(context.Background(), &proto.SayReq{Content: "random"})
if err != nil {
log.Println(err)
time.Sleep(time.Second)
continue
}
time.Sleep(time.Second)
num++
log.Printf(resp.Content + ", clientOfnum: " + strconv.Itoa(num))
}
}
Сервер etcd/server/main.go
package main
import (
"flag"
"fmt"
etcd "github.com/coreos/etcd/client"
"github.com/liyue201/grpc-lb/examples/proto"
registry "github.com/liyue201/grpc-lb/registry/etcd"
"golang.org/x/net/context"
"google.golang.org/grpc"
"log"
"net"
"sync"
"time"
)
var nodeID = flag.String("node", "node1", "node ID")
var port = flag.Int("port", 8080, "listening port")
type RpcServer struct {
addr string
s *grpc.Server
}
func NewRpcServer(addr string) *RpcServer {
s := grpc.NewServer()
rs := &RpcServer{
addr: addr,
s: s,
}
return rs
}
func (s *RpcServer) Run() {
listener, err := net.Listen("tcp", s.addr)
if err != nil {
log.Printf("failed to listen: %v", err)
return
}
log.Printf("rpc listening on:%s", s.addr)
proto.RegisterTestServer(s.s, s)
s.s.Serve(listener)
}
func (s *RpcServer) Stop() {
s.s.GracefulStop()
}
var num int
func (s *RpcServer) Say(ctx context.Context, req *proto.SayReq) (*proto.SayResp, error) {
num++
text := "Hello " + req.Content + ", I am " + *nodeID + ", serverOfnum: " + strconv.Itoa(num)
log.Println(text)
return &proto.SayResp{Content: text}, nil
}
func StartService() {
etcdConfg := etcd.Config{
Endpoints: []string{"http://120.24.44.201:2379"},
}
registry, err := registry.NewRegistry(
registry.Option{
EtcdConfig: etcdConfg,
RegistryDir: "/grpc-lb",
ServiceName: "test",
NodeID: *nodeID,
NData: registry.NodeData{
Addr: fmt.Sprintf("127.0.0.1:%d", *port),
//Metadata: map[string]string{"weight": "1"},
},
Ttl: 10 * time.Second,
})
if err != nil {
log.Panic(err)
return
}
server := NewRpcServer(fmt.Sprintf("0.0.0.0:%d", *port))
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
server.Run()
wg.Done()
}()
wg.Add(1)
go func() {
registry.Register()
wg.Done()
}()
//stop the server after one minute
//go func() {
// time.Sleep(time.Minute)
// server.Stop()
// registry.Deregister()
//}()
wg.Wait()
}
func main() {
flag.Parse()
StartService()
}
Код на стороне сервера выглядит следующим образом: запуск трех служебных процессов с помощью следующей команды, а затем запуск клиента.
go run main.go -node node1 -port 28544
go run main.go -node node2 -port 18562
go run main.go -node node3 -port 27772
6. Окончательный эффект
(Клиент продолжает случайным образом обращаться к трем серверам)
Клиент:
Сервер node1:
Сервер node2:
Сервер node3:
Отключите сервер node1, node3, клиент все еще может работать, и подключитесь только к серверу node2:
Клиент:
сервер node2:
Снова запустите серверы node1 и node3, клиент автоматически подключится и продолжит произвольный доступ к 3 серверам:
Клиент:
Сервер node1:
Сервер node3:
Наконец, все серверы закрыты, а клиент находится в состоянии блокировки.