5.8. docker-compose整合go-kit和redis、mysql

未匹配的标注

无水印图片找不到嘞,文中图片来自知乎自己以前的账号,现在是开源到。

定义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语法可看菜鸟教程:

Docker Compose | 菜鸟教程

文中部分知识来自拉勾教育《Go微服务实战38讲》。


关注和赞赏都是对笔者最大的支持
关注和赞赏都是对笔者最大的支持

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
讨论数量: 0
发起讨论 查看所有版本


暂无话题~