Оригинальная ссылка, перепечатайте и укажите источник.
Код для этой статьи:GitHub
Этот каталог статей:
Микросервисная архитектура
единая кодовая база
При использовании Laravel для веб-проектов ранее структура каталогов была разделена в соответствии с MVC, то есть уровень контроллера обрабатывает бизнес-логику, уровень модели обрабатывает CURD базы данных, а уровень представления обрабатывает рендеринг данных и взаимодействие со страницей. Так же, как MVP и MVVM, код всего проекта сосредоточен в кодовой базе для бизнес-обработки. Этот метод единого кода агрегации быстро реализует бизнес на ранней стадии, но выявляет множество проблем на более поздней стадии:
- Сложность в разработке и обслуживании: с увеличением сложности бизнеса степень связанности кода имеет тенденцию к увеличению, и его нелегко расширять по горизонтали после того, как несколько модулей связаны друг с другом.
- Низкая эффективность и надежность: Чрезмерный объем кода будет замедлять скорость отклика, а потенциальные проблемы безопасности приложения будут накапливаться.
разделить кодовую базу
Микросервис — программная архитектура, которая разбирает большой и агрегированный бизнес-проект на небольшой и независимый бизнес-модуль, модуль — это сервис, а последовательный с помощью эффективных протоколов (Protobuf, JSON и т. д.) называют друг друга RPC. Этот метод разделения библиотеки кода имеет следующие особенности:
- Каждая служба должна быть небольшим, отдельным работающим бизнес-модулем, подобным Unix Do One Well.
- Каждый сервис должен быть автоматизирован и развернут (распределен), не затрагивая другие сервисы.
- Тщательная проверка и обработка ошибок в каждой службе для повышения надежности
Сравнение двух
По сути, это просто разные способы агрегирования и разделения кода.
Ссылаться на:Преимущества и недостатки архитектуры микросервиса
Создавайте микросервисы
Микросервис UserInfoService
Затем создайте микросервис, который обрабатывает информацию о пользователе: UserInfoService. Клиент запрашивает у сервера подробную информацию, такую как возраст и должность пользователя, через имя. Сначала необходимо установить компиляторы gRPC и protoc:
go get -u google.golang.org/grpc
go get -u github.com/golang/protobuf/protoc-gen-go
Структура каталогов
├── proto
│ ├── user.proto // 定义客户端请求、服务端响应的数据格式
│ └── user.pb.go // protoc 为 gRPC 生成的读写数据的函数
├── server.go // 实现微服务的服务端
└── client.go // 调用微服务的客户端
процесс вызова
Протокол Protobuf
Каждый микросервис имеет свою собственную независимую кодовую базу, которая требует эффективного протокола при общении между ними и должна следовать определенной структуре данных для разбора и кодирования передаваемых данных, которая часто определяется protobuf в микросервисах.
Protobuf(буферы протокола) это своего рода Google, запущенныйдвоичное кодирование данныхформат по сравнению с XML и JSONкодировка текстовых данныхФормат более выгоден:
Более быстрое чтение и запись, меньший размер файла
Он не имеет имен тегов XML или имен полей JSON, он более легкий,больше ссылок
языковой нейтралитет
Просто определите файл .proto, вы можете использовать компилятор protobuf, соответствующий каждому языку, для его компиляции, а сгенерированный файл содержит функции для кодирования и декодирования сообщений.
для JSON
- В PHP вам нужно использовать
json_encode()
иjson_decode()
Для кодирования и декодирования вам нужно использовать стандартную библиотеку json в Golang.Marshal()
иUnmarshal()
… обременительно каждый раз анализировать и кодировать - Достоинства: хорошая читаемость, низкая стоимость разработки
- Недостатки: более низкая скорость чтения и записи и больше места для хранения, чем у protobuf.
Для протобафа
- .Proto может быть сгенерирован.php или *.pb.go… На функции кодирования и декодирования, сгенерированные компилятором в файле, можно напрямую ссылаться в проекте
- Преимущества: эффективный и легкий, одно определение и многократное использование
- Недостатки: плохая читаемость, высокая стоимость разработки.
Файл user.proto, определяющий микросервис.
syntax = "proto3"; // 指定语法格式,注意 proto3 不再支持 proto2 的 required 和 optional
package proto; // 指定生成的 user.pb.go 的包名,防止命名冲突
// service 定义开放调用的服务,即 UserInfoService 微服务
service UserInfoService {
// rpc 定义服务内的 GetUserInfo 远程调用
rpc GetUserInfo (UserRequest) returns (UserResponse) {
}
}
// message 对应生成代码的 struct
// 定义客户端请求的数据格式
message UserRequest {
// [修饰符] 类型 字段名 = 标识符;
string name = 1;
}
// 定义服务端响应的数据格式
message UserResponse {
int32 id = 1;
string name = 2;
int32 age = 3;
repeated string title = 4; // repeated 修饰符表示字段是可变数组,即 slice 类型
}
Скомпилируйте файл user.proto
# protoc 编译器的 grpc 插件会处理 service 字段定义的 UserInfoService
# 使 service 能编码、解码 message
$ protoc -I . --go_out=plugins=grpc:. ./user.proto
сгенерировать user.pb.go
package proto
import (
context "golang.org/x/net/context"
grpc "google.golang.org/grpc"
)
// 请求结构
type UserRequest struct {
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
}
// 为字段自动生成的 Getter
func (m *UserRequest) GetName() string {
if m != nil {
return m.Name
}
return ""
}
// 响应结构
type UserResponse struct {
Id int32 `protobuf:"varint,1,opt,name=id" json:"id,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"`
Age int32 `protobuf:"varint,3,opt,name=age" json:"age,omitempty"`
Title []string `protobuf:"bytes,4,rep,name=title" json:"title,omitempty"`
}
// ...
// 客户端需实现的接口
type UserInfoServiceClient interface {
GetUserInfo(ctx context.Context, in *UserRequest, opts ...grpc.CallOption) (*UserResponse, error)
}
// 服务端需实现的接口
type UserInfoServiceServer interface {
GetUserInfo(context.Context, *UserRequest) (*UserResponse, error)
}
// 将微服务注册到 grpc
func RegisterUserInfoServiceServer(s *grpc.Server, srv UserInfoServiceServer) {
s.RegisterService(&_UserInfoService_serviceDesc, srv)
}
// 处理请求
func _UserInfoService_GetUserInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {...}
Реализовать службу микросервера
Процесс реализации
ссылка на код
package main
import (...)
// 定义服务端实现约定的接口
type UserInfoService struct{}
var u = UserInfoService{}
// 实现 interface
func (s *UserInfoService) GetUserInfo(ctx context.Context, req *pb.UserRequest) (resp *pb.UserResponse, err error) {
name := req.Name
// 模拟在数据库中查找用户信息
// ...
if name == "wuYin" {
resp = &pb.UserResponse{
Id: 233,
Name: name,
Age: 20,
Title: []string{"Gopher", "PHPer"}, // repeated 字段是 slice 类型
}
}
err = nil
return
}
func main() {
port := ":2333"
l, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("listen error: %v\n", err)
}
fmt.Printf("listen %s\n", port)
s := grpc.NewServer()
// 将 UserInfoService 注册到 gRPC
// 注意第二个参数 UserInfoServiceServer 是接口类型的变量
// 需要取地址传参
pb.RegisterUserInfoServiceServer(s, &u)
s.Serve(l)
}
Запустите прослушиватель:
Звонок клиента
Процесс реализации
ссылка на код
package main
import (...)
func main() {
conn, err := grpc.Dial(":2333", grpc.WithInsecure())
if err != nil {
log.Fatalf("dial error: %v\n", err)
}
defer conn.Close()
// 实例化 UserInfoService 微服务的客户端
client := pb.NewUserInfoServiceClient(conn)
// 调用服务
req := new(pb.UserRequest)
req.Name = "wuYin"
resp, err := client.GetUserInfo(context.Background(), req)
if err != nil {
log.Fatalf("resp error: %v\n", err)
}
fmt.Printf("Recevied: %v\n", resp)
}
Вызов запуска завершается успешно:
Суммировать
В процессе реализации микрослужбы UserInfoService, описанной выше, будет обнаружено, что каждая микрослужба должна сама управлять портом прослушивания сервера, который вызывается после подключения клиента.Когда микрослужб много, управление портом будет более проблематичным. , По сравнению с gRPC,go-microРеализовано обнаружение служб для удобного управления микросервисами. В следующем разделе мы изучим их вместе с докеризацией служб.
Больше ссылок:Руководства по серии микросервисов Nginx