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

测试一下。

img

可以实现登录与注册功能。

打包会员镜像

先编译:

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即可。

img

可以看见服务运行成功了。wow~

dcoker composer语法可看菜鸟教程:

Docker Compose | 菜鸟教程

文中部分知识来自《Go微服务实战38讲》,有兴趣可前往查看:

img


docker-compose整合go-kit和mysql、Redis
https://blog.puresai.com/2021/02/12/goexample7/
作者
puresai
许可协议