Создание пользователя rpc
cdприбытьFoodGuidesПод содержанием. Создайтеrpcпапка
mkdir -p usermanage/rpc/user && cd usermanage/rpc/user
существуетrpc/userнаписать в каталогuser.protoдокумент
goctl rpc template -o user.proto
записыватьuser.protoдокумент
syntax = "proto3";
package user;
message LoginRequest {
string email = 1;
string password = 2;
}
message RegisterRequest {
string Username = 1;
string Email = 2;
string Password = 3;
}
message UserinfoRequest {
string Userid = 1;
string Token = 2;
}
message Response {
int64 id = 1;
string email = 2;
string accessToken = 3;
int64 accessExpire = 4;
int64 refreshAfter = 5;
}
service User {
rpc Login(LoginRequest) returns(Response);
rpc Register(RegisterRequest) returns(Response);
rpc Userinfo(UserinfoRequest) returns(Response);
}
Мы определяем три интерфейса:Login Register UserInfo
генерироватьuser-rpcСлужить
goctl rpc proto -src user.proto -dir .
Проверьте этоrpc/userсодержание
➜ user git:(master) ✗ tree
.
├── etc
│ └── user.yaml
├── internal
│ ├── config
│ │ └── config.go
│ ├── logic
│ │ ├── loginlogic.go
│ │ ├── registerlogic.go
│ │ └── userinfologic.go
│ ├── server
│ │ └── userserver.go
│ └── svc
│ └── servicecontext.go
├── user
│ └── user.pb.go
├── user.go
├── user.proto
└── userclient
└── user.go
8 directories, 11 files
➜ user git:(master) ✗
API Gatewayкод вызоваuser rpcСлужить
редактироватьapi/etcвнизuser-api.yamlфайл, добавитьuser.rpcнастроить
Name: user-api
Host: 0.0.0.0
Port: 8888
User:
Etcd:
Hosts:
- localhost:2379
Key: user.rpc
редактироватьapi/internal/configвнизconfig.goфайл, добавитьUserПеременная
type Config struct {
rest.RestConf
User zrpc.RpcClientConf
}
редактироватьapi/internal/svcвнизservicecontext.goфайл, добавитьUserVariable , добавьте код создания экземпляра.
type ServiceContext struct {
Config config.Config
User userclient.User
}
func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
Config: c,
User: userclient.NewUser(zrpc.MustNewClient(c.User)),
}
}
редактироватьapi/internal/logicвнизloginlogic.goфайл, добавить вызовuser rpcизloginметод
func (l *LoginLogic) Login(req types.LoginRequest) (*types.LoginResponse, error) {
// todo: add your logic here and delete this line
resp,err := l.svcCtx.User.Login(l.ctx, &user.LoginRequest{
Email: req.Email,
Password: req.Password,
});
if err != nil {
return nil, err
}
token := types.JwtToken{
AccessToken: resp.AccessToken,
AccessExpire: resp.AccessExpire,
RefreshAfter: resp.RefreshAfter,
}
response := types.UserReply{
Id: resp.Id,
Email: resp.Email,
JwtToken: token,
}
return &types.LoginResponse{
response,
},nil
}
Определить структуру таблицы базы данных и сгенерировать код CRUD+cache
существуетusermanageСоздать подmodelпапка.
mkdir -p model & cd model
существуетmodelпод новымuser.sqlфайл и напишите следующее.
CREATE TABLE `user` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户Id',
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户名称',
`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户密码',
`email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户邮箱',
`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `name_index` (`name`),
UNIQUE KEY `email_index` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
существуетmodelВыполните следующую команду в каталоге для созданияCRUD+cacheкод,-cуказать использованиеredis cache
goctl model mysql ddl -c -src user.sql -dir .
ПроверитьmodelСтруктура каталогов
➜ model git:(master) ✗ tree
.
├── user.sql
├── usermodel.go // CRUD+cache代码
└── vars.go // 定义常量和变量
на этой машинеmysqlсоздан вfoodguidesбазу данных и создать новуюuserповерхность.
добавить часть данных
INSERT INTO `foodguides`.`user`(`id`, `name`, `password`, `email`) VALUES (1, 'Ningxi', 'd89617870c6f8a028f5728be69cc09d4cd3585b4651b7f206f1cd674bb4351ec', 'ningxi@ningxi.com');
rpcкод вызоваcrud+cacheкод
редактироватьrpc/user/etcвнизuser.yamlфайл, добавьте следующее содержимое.
Обратите внимание здесьmysqlиспользуетningxi-composeБегdockerконтейнер, поэтому параметры требуют внимания.
DataSource: root:2e70F5E6@(localhost:13306)/foodguides?parseTime=true
Table: user
Cache:
- Host: localhost:16379
AccessSecret: ad879037-d3fd-tghj-112d-6bfc35d54b7d
AccessExpire: 86400
Salt: ^&yh
редактироватьrpc/user/internal/configвнизconfig.goфайл, добавитьDataSource Cache AccessSecret AccessExpireПеременная
type Config struct {
zrpc.RpcServerConf
DataSource string
Cache cache.CacheConf
AccessSecret string
AccessExpire int64
Salt string
}
редактироватьrpc/user/internal/svcвнизserviceContext.goфайл, добавитьModelVariable , добавьте код создания экземпляра.
type ServiceContext struct {
Config config.Config
Model model.UserModel
}
func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
Config: c,
Model: model.NewUserModel(sqlx.NewMysql(c.DataSource),c.Cache),
}
}
редактироватьrpc/user/internal/logicвнизloginlogic.goфайл, добавьте следующий код
func (l *LoginLogic) Login(in *user.LoginRequest) (*user.Response, error) {
res, err := l.svcCtx.Model.FindOneByEmail(in.Email)
if err == nil {
passwords := ningxi.PasswordEncrypt(l.svcCtx.Config.Salt,in.Password)
if passwords == res.Password {
now := time.Now().Unix()
accessExpire := l.svcCtx.Config.AccessExpire
jwtToken, err := l.getJwtToken(l.svcCtx.Config.AccessSecret, now, accessExpire, res.Id)
if err != nil {
return nil, err
}
response := user.Response{
Email: res.Email,
Id: res.Id,
AccessToken: jwtToken,
AccessExpire: now + accessExpire,
RefreshAfter: now + accessExpire/2,
}
return &response, nil
} else {
return nil, errors.New("密码错误")
}
}
return nil, err
}
я здесьloginlogicдобавлено вtokenСгенерируйте соответствующий код для шифрования и проверки пароля, чтобы, когда пользователь успешно войдет в систему, мы сгенерировалиtokenвозвращен клиенту.
Исправлятьapi responseОбратный формат
Я надеюсь, что формат возврата данных запроса клиентского интерфейса будет таким
{
"code": 1,
"message": "",
"result": {
"id": 1,
"username": "",
"email": "ningxi@ningxi.com",
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MTE0NzAzOTQsImlhdCI6MTYxMTM4Mzk5NCwidXNlcklkIjoxfQ.8EJU0XDZ535NZvtCPgyOg9RVw3FAdG5AJktHYcjEGo0",
"accessExpire": 1611470394,
"refreshAfter": 1611427194
}
}
существуетfoodguidesдобавить в папкуningxiпапку и создатьningxi.goфайл, добавьте следующий код
import (
"fmt"
"golang.org/x/crypto/scrypt"
)
type HttpResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Result interface{} `json:"result"`
}
func SuccessResponse(resData interface{},message string) HttpResponse {
return HttpResponse{Code:1,Message: message,Result: resData}
}
func FailureResponse(resData interface{},message string,code int) HttpResponse {
return HttpResponse{Code:code,Message: message,Result: resData}
}
func PasswordEncrypt(salt,password string) string {
dk,_ := scrypt.Key([]byte(password), []byte(salt), 32768, 8, 1, 32)
return fmt.Sprintf("%x",string(dk))
}
Отредактируйте loginhandler.go в разделе api/internal/handler и добавьте следующий код.
func LoginHandler(ctx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.LoginRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.OkJson(w, ningxi.FailureResponse(nil,err.Error(),1000))
return
}
l := logic.NewLoginLogic(r.Context(), ctx)
resp, err := l.Login(req)
if err != nil {
httpx.OkJson(w, ningxi.FailureResponse(nil,err.Error(),1000))
} else {
httpx.OkJson(w, ningxi.SuccessResponse(resp,""))
}
}
}
запустить службу
Запустите службу, обратите внимание, что перед запуском службы необходимо убедиться, что используется та, которая использовалась в предыдущей статье.ningxi-composeбеги нормально.
запускатьuser rpcслужба, после успешного запуска,user rpcбегать по локалке8080порт
➜ FoodGuides git:(master) ✗ go run usermanage/rpc/user/user.go -f usermanage/rpc/user/etc/user.yaml
Starting rpc server at 127.0.0.1:8080...
запускатьuser apiслужба, после успешного запуска,user apiбегать по локалке8888порт
➜ FoodGuides git:(master) ✗ go run usermanage/api/user.go -f usermanage/api/etc/user-api.yaml
Starting server at 0.0.0.0:8888...
apiПроверка Если получены следующие данные, служба работает нормально
➜ ~ curl http://localhost:8888/users/login -X POST -d '{"email": "ningxi@ningxi.com","password": "809161"}' --header "Content-Type: application/json"
{"code":1,"message":"","result":{"id":1,"username":"","email":"ningxi@ningxi.com","accessToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MTE0OTU3MjAsImlhdCI6MTYxMTQwOTMyMCwidXNlcklkIjoxfQ.EN9og9owK5eW--qUIJCf0UvMKNaeenVac1lmMQFcHSM","accessExpire":1611495720,"refreshAfter":1611452520}}%
➜ ~
Понять, как работает сервис
Старый способ — понять процесс выполнения сервиса
-
user/etcвнизuser.yamlдокумент. Этот файл настраиваетuser rpcНекоторые переменные, требуемые службой, это то же самое, что иapiОбслуживание такое же. -
userвнизuser.protoдокумент. Этот файл определяетuser rpcИнформация об интерфейсе, предоставляемая службой, и последующие добавления интерфейса также обрабатываются здесь. тогда позвониgoctlВосстановите службу. -
userвнизuser.goдокумент. Файлuser rpcВходной файл для службы, здесь все начинается. -
user/userвнизuser.pb.goдокумент. Файлuser.protoгенерироватьrpcслужебный файл, содержащийrpcсерединаclientа такжеserverреализация.
папка пользователя-клиента
userclientвнизuser.goфайлrpcобслуживаниеclientКонкретный код реализации файла реализуетuser.pb.goтри интерфейса в
User interface {
Login(ctx context.Context, in *LoginRequest) (*Response, error)
Register(ctx context.Context, in *RegisterRequest) (*Response, error)
Userinfo(ctx context.Context, in *UserinfoRequest) (*Response, error)
}
когдаapiв сервисеserviceContext.goв инициализацииuserclientчас,apiСервис имеет возможность вызова этих трех интерфейсов
type ServiceContext struct {
Config config.Config
User userclient.User
}
func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
Config: c,
User: userclient.NewUser(zrpc.MustNewClient(c.User)),
}
}
внутренняя папка
rpcВнутренний код реализации в сервисе находится в этой папке.
internal/configвнизconfig.goдокумент. Вы обнаружите, что файл определяет иuser.yamlопределение аналогичное. да.user.yamlсуществуетuser.goВходной файл находится вmainметод, он анализируется какconfigобъект. Так что их значения взаимно однозначно соответствуют.
internal/logicвнизxxxlogic.goфайл, здесь реализована окончательная бизнес-логика.
internal/svcвнизservicecontext.goдокумент. Файл сохраненrpcУслугиconfigОбъект, объект подключения к базе данных Модель. пройти черезUserServerобъект переданlogicв объекте.
internal/sercerвнизuserserver.goдокумент. Файлuser rpcобслуживаниеserverКонкретный код реализации ,userserverсохраненsvc.ServiceContextобъект и реализует три интерфейса
func (s *UserServer) Login(ctx context.Context, in *user.LoginRequest) (*user.Response, error) {
l := logic.NewLoginLogic(ctx, s.svcCtx)
return l.Login(in)
}
func (s *UserServer) Register(ctx context.Context, in *user.RegisterRequest) (*user.Response, error) {
l := logic.NewRegisterLogic(ctx, s.svcCtx)
return l.Register(in)
}
func (s *UserServer) Userinfo(ctx context.Context, in *user.UserinfoRequest) (*user.Response, error) {
l := logic.NewUserinfoLogic(ctx, s.svcCtx)
return l.Userinfo(in)
}
когдаapiобслуживание черезuserclientперечислитьrpcПри подаче,userserverЗапустит соответствующий метод и, наконец, вызовет соответствующийlogicметод.
Подведение итогов процесса вызова
В качестве примера возьмем клиент, вызывающий интерфейс входа в систему.
clientчасть
apiКогда служба запускается, она инициализируетсяuserclientобъект, поэтомуapiСервис имеет звонокuser rpc способность служить
type ServiceContext struct {
Config config.Config
User userclient.User
}
func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
Config: c,
User: userclient.NewUser(zrpc.MustNewClient(c.User)),
}
}
serverчасть
user.goвходной файл черезyamlконфигурационный файл, экземплярconfigобъект.
var configFile = flag.String("f", "etc/user.yaml", "the config file")
func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
}
создавать экземплярServiceContextобъект
ctx := svc.NewServiceContext(c)
создавать экземплярUserServerобъект
srv := server.NewUserServer(ctx)
создавать экземплярrpcСлужить
s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
user.RegisterUserServer(grpcServer, srv)
})
user.RegisterUserServer(grpcServer, srv)сделал что-то вродеapiФункцию реализует маршрут в сервисе.
запускатьrpcСлужить
s.Start()
когдаapiобслуживание черезclientперечислитьloginспособ,rpc serverвызоветuserserveсерединаloginметод.
func (s *UserServer) Login(ctx context.Context, in *user.LoginRequest) (*user.Response, error) {
l := logic.NewLoginLogic(ctx, s.svcCtx)
return l.Login(in)
}
loginвызов методаlogicсерединаloginметод
После обработки данных интерфейс отвечает слой за слоем и, наконец, завершает вызов клиентского интерфейса.
Содержание этого фильма относительно длинное, в которомJwtToken,rpcсерединаclientа такжеserverДругие связанные знания, вы можете проверить информацию самостоятельно, чтобы лучше понять содержание учебника.