docker-compose整合go-kit和mysql、Redis
专栏之前有使用go-kit、gorm、go-Redis,所以部分内容就忽略。
定义model
主要实现SelectByEmail(查找)和Save(新建)两个方法:
package model
import "time"
type UserEntity struct {
ID int64
Username string
Password string
Email string
CreatedAt time.Time
}
func (UserEntity) TableName() string {
return "user"
}
type UserDao interface {
SelectByEmail(email string) (*UserEntity, error)
Save(user *UserEntity) error
}
type UserDaoImpl struct{}
func (u *UserDaoImpl) SelectByEmail(email string) (*UserEntity, error) {
user := &UserEntity{}
err := db.Where("email = ?", email).First(user).Error
return user, err
}
func (u *UserDaoImpl) Save(user *UserEntity) error {
return db.Create(user).Error
}
业务service
package service
import (
"context"
"demo7-docker-compose/model"
"errors"
"log"
"time"
"github.com/jinzhu/gorm"
)
type UserInfoDTO struct {
ID int64 `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
}
var (
ErrUserExisted = errors.New("user is existed")
ErrPassword = errors.New("email and password are not match")
ErrRegistering = errors.New("email is registering")
)
type RegisterUser struct {
Username string
Password string
Email string
}
type UserService interface {
Login(ctx context.Context, email, pass string) (*UserInfoDTO, error)
Register(ctx context.Context, user *RegisterUser) (*UserInfoDTO, error)
}
type UserServiceImpl struct {
userDao model.UserDao
}
func MakeUserServiceImpl(userDao model.UserDao) UserService {
return &UserServiceImpl{
userDao,
}
}
func (userService *UserServiceImpl) Login(ctx context.Context, email, password string) (*UserInfoDTO, error) {
user, err := userService.userDao.SelectByEmail(email)
if err == nil {
if user.Password == password {
return &UserInfoDTO{
ID: user.ID,
Username: user.Username,
Email: user.Email,
}, nil
} else {
return nil, ErrPassword
}
} else {
log.Printf("err : %s", err)
}
return nil, err
}
func (userService UserServiceImpl) Register(ctx context.Context, user *RegisterUser) (*UserInfoDTO, error) {
ret := model.RedisClient.SetNX(user.Email, 1, time.Duration(5)*time.Second)
if ret.Val() == false {
return nil, ErrRegistering
}
defer model.RedisClient.Del(user.Email)
existUser, err := userService.userDao.SelectByEmail(user.Email)
if (err == nil && existUser == nil) || err == gorm.ErrRecordNotFound {
newUser := &model.UserEntity{
Username: user.Username,
Password: user.Password,
Email: user.Email,
}
err = userService.userDao.Save(newUser)
if err == nil {
return &UserInfoDTO{
ID: newUser.ID,
Username: newUser.Username,
Email: newUser.Email,
}, nil
}
}
if err == nil {
err = ErrUserExisted
}
return nil, err
}
此处注意分布式锁的实现,利用了Redis的setnx方法,并可以设置过期时间,对应Redis中的
SET key value [EX seconds] [PX milliseconds] [NX|XX]
ret := model.RedisClient.SetNX(user.Email, 1, time.Duration(5)*time.Second)
if ret.Val() == false {
return nil, ErrRegistering
}
defer model.RedisClient.Del(user.Email)
对于endpoint和transport,就与之前的没有太大差别了,我们这里就暂时略去,有兴趣可点击文章最后的源码查看。
mysql和Redis容器
mysql的Dockerfile:
FROM mysql:5.7
WORKDIR /docker-entrypoint-initdb.d
ENV LANG=C.UTF-8
COPY user.sql .
运行mysql-for-user容器
docker run -itd --name mysql-for-user -p 3316:3306 -e MYSQL_ROOT_PASSWORD=111111 mysql-for-user
这里容器启动的时候是可以执行user.sql的。(可通过docker logs mysql-for-user查看容器启动信息)
/usr/local/bin/docker-entrypoint.sh: running /docker-entrypoint-initdb.d/user.sql
Redis容器的启动简单
docker pull Redis:5.0
docker run -itd --name Redis5 -p 6389:6379 Redis:5.0
然后我们可以运行主程序,智能到8089端口:
go run main.go -service.port 8089
测试一下。
可以实现登录与注册功能。
打包会员镜像
先编译:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o user .
user的dockerfile
FROM alpine:3.12
WORKDIR /
COPY ./user /
ENTRYPOINT ["./user"]
构建镜像:
docker build -t user-alpine .
docker-compose
version: '2.1'
services:
user13:
image: user-alpine
depends_on:
- Redis
- mysql
ports:
- "8088:8088"
links:
- Redis
- mysql
networks:
- user
mysql:
image: mysql-for-user
ports:
- "3306:3306"
expose:
- "3306"
environment:
- MYSQL_ROOT_PASSWORD=111111
networks:
- user
restart: always
Redis:
image: Redis:5.0
ports:
- "6379:6379"
expose:
- "6379"
networks:
- user
networks:
user:
driver: bridge
说明:
- depends_on 依赖,此处表示user13依赖Redis、mysql,被依赖者会优先构建,但是是可能脚本为运行成功的,所以user13是有可能刚开始连接不上mysql的,docker start即可
- links 连接,此处user13连接到Redis、mysql,可以用它们替代连接数据库的host
- environment 环境变量
- expose 对links暴露的端口
运行docker-compose up即可。
可以看见服务运行成功了。wow~
dcoker composer语法可看菜鸟教程:
文中部分知识来自《Go微服务实战38讲》,有兴趣可前往查看:
docker-compose整合go-kit和mysql、Redis
https://blog.puresai.com/2021/02/12/goexample7/