转载我的个人博客:tomonori.cc

最近公司在写数据可视化平台的一个项目,提供给公司运营内部使用。关于运营用户这方面,打算直接使用对应app的运营管理平台(另外一个使用java服务端)调用登陆接口,java服务端已经编写好接口,这边golang直接调用他们那的接口,这边处理存储请求获取到的用户信息和存储token的功能,就有了redis缓存相关的操作。在测试中,本来是用的运营管理平台的redis缓存服务器,不过我打算在我自己的服务器,直接搭建个redis缓存数据库。仅记此文章,作为查询和学习的记录。

安装

  • 在redis官网中下载tar.gz压缩包,保证最新版,在你当前的目录下(我的是在/home/download/中)
# 解压缩
tar xzf redis-x.x.xx.tar.gz
# cd进去进行编译
cd redis-x.x.xx.tar.gz
make
复制代码
  • 编译完,在当前目录下会生成src的目录,编译完成的所有项都在这里面(redis-server、redis-cli)
# 当前目录:/home/download/redis-x.x.xx
# 将redis文件移动到/usr/local/目录下,不移动也可以,我不打算做环境变量,保证我每次都能记得文件包在哪
# 请务必记得也移动redis.conf配置文件
cp -r ./src /usr/local/redis
cp ./redis.conf /usr/local/redis/config/
复制代码

配置

  • 修改redis.conf配置文件
# /usr/local/redis/config/redis.conf
vi redis.conf

# 关于redis.conf中的项,可以去查询文档,这边修改这些项
requirepass xxxxxxxx # 开启auth密码验证(如果在服务端中设置了密码登陆,这边不设置的话,会出现“ERR Client sent AUTH, but no password is set”)
bind 0.0.0.0 # 如果在服务器厂商那开放了6379端口,任然不能从本地连接服务器redis的话,可以进行设置
daemon yes # 设置为守护进程,这个不多说
复制代码

使用

  • 开启持久化和配置文件启动
# /usr/local/redis
./redis-server ./config/redis.conf --appendonly yes
复制代码
# 接上
# 使用redis终端进行测试
./redis-cli
复制代码
  • 在本地中的使用,本地使用的是windwos端的RedisDesktopManager

Golang代码编写

直接上代码吧

# Config/config.ini
[redis]
ADDR = your server address:6379
PASSWORD = 123456
DB = 0
复制代码
// Middlewares/Cache/redis.go
package Cache

import (
	"fmt"
	"sync"
	"github.com/go-redis/redis"
	"time"
	"virtual-data/Middlewares/setting"
)

type RedisStorage struct {
}

var (
	instance *RedisStorage
	once sync.Once
	client *redis.Client
	err error
)

func Instance() *RedisStorage {
	once.Do(func() {
		instance = &RedisStorage{}
	})
	return instance
}

func (r *RedisStorage) Init() (issue error) {
	var (
		addr, password string
		db int
	)
	sec, err := setting.Cfg.GetSection("redis")
	if err != nil {
		fmt.Errorf("Fial to get config section 'redis': %s\n", err)
	}
	addr = sec.Key("ADDR").String()
	password = sec.Key("PASSWORD").String()
	db, _ = sec.Key("DB").Int()

	client = redis.NewClient(&redis.Options{
		Addr: addr,
		Password: password,
		DB: db,
	})

	_, err = client.Ping().Result()
	if err != nil {
		return err
	}

	return nil
}

// 存
func (r *RedisStorage) Set(key, value string, expiration time.Duration) error {
	err := client.Set(key, value, expiration).Err()
	return err
}

// 查
func (r *RedisStorage) Get(key string) (string, error) {
	result, err := client.Get(key).Result()
	return result, err
}

// 删
func (r *RedisStorage) Del(key string) error {
	err := client.Del(key).Err()
	return err
}
复制代码

在登陆控制器中使用

  • ViewModel 返回数据格式声明
// ViewModel/Login/v_login.go
package Login

type ResultData struct {
	CreateTime int64 `json:"createTime"`
	UserId int `json:"userId"`
	Token string `json:"token"`
	RealName string `json:realName`
}

type Vresult struct {
	Code int `json:"code"`
	Data ResultData `json:"data"`
}

type Verror struct {
	Code int `json:"code"`
	ThrowType string `json:"throwType"`
	Message string `json:"message"`
}
复制代码
  • LoginController 登陆接口方法编写
// Controllers/LoginController.go
package Controllers

import (
	"encoding/json"
	"fmt"
	"github.com/astaxie/beego/validation"
	"github.com/kataras/iris"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"
	"virtual-data/Middlewares/Cache"
	"virtual-data/Middlewares/setting"
	VMlogin "virtual-data/ViewModel/Login"
)

func Login(ctx iris.Context) {
	userName := ctx.Request().FormValue("username")
	passWord := ctx.Request().FormValue("password")

	valid := validation.Validation{}
	valid.Required(userName, "username").Message("账号不能为空")
	valid.Required(passWord, "password").Message("密码不能为空")

	if !valid.HasErrors() {
		result, err := requestJavaServerService(userName, passWord)
		if err != nil {
			ctx.JSON(Result(http.StatusBadGateway, err))
		} else {
			ctx.JSON(result)
		}
	} else {
		ctx.JSON(Result(http.StatusBadRequest, valid.Errors[0].Message))
	}
}

/**
 * 请求java服务端接口,返回请求成功或错误的值
 * @method requestJavaServerService
 * @param username string
 * @param password string
 * @return interface{}
 * @return error
 */
func requestJavaServerService(username, password string) (interface{}, error) {
	client := &http.Client{}
	var err error
	var urlAddr string
	userInfo := url.Values{}
	userInfo.Add("userName", username)
	userInfo.Add("password", password)

	sec, err := setting.Cfg.GetSection("request")
	if err != nil {
		fmt.Errorf("Fial to get config section 'request': %s\n", err)
	}
	// 在配置文件中配置好的请求地址
	urlAddr = sec.Key("url").String()

	request, err := http.NewRequest(
		"POST",
		urlAddr,
		strings.NewReader(userInfo.Encode()),
	)
	request.Header.Set("Content-type", "application/x-www-form-urlencoded")
	request.Header.Set("cms-token", "null")
	request.Header.Set("cms-channel", "0")
	if err != nil {
		return nil, err
	}

	parseObj, err := parseResponseBody(client, request)
	if err != nil {
		return nil, err
	}

	return parseObj, nil
}


type tempBody struct {
	Code int `json:"code"`
	Data interface{} `json:"data"`
}
/**
 * 处理请求成功的body
 * @method parseResponseBody
 * @param client *http.Client
 * @param request *http.Request
 * @return obj interface{}
 * @return err error
 */
func parseResponseBody(client *http.Client, request *http.Request) (obj interface{}, err error) {
	var resultObj VMlogin.Vresult
	var errorObj VMlogin.Verror
	var tempObj tempBody
	resp, err := client.Do(request)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	body, _ := ioutil.ReadAll(resp.Body)
	err = json.Unmarshal(body, &tempObj)
	if err != nil {
		return nil, err
	}
	if tempObj.Code == 200 {
		_ = json.Unmarshal(body, &resultObj)
		obj = resultObj
		if err = cacheManagerService(resultObj); err != nil {
			return nil, err
		}
	} else {
		_ = json.Unmarshal(body, &errorObj)
		obj = errorObj
	}
	return obj, nil
}

/**
 * 设置处理缓存的工厂
 * @method cacheManagerService
 * @param resultObj VMlogin.Vresult
 * @return error
 */
func cacheManagerService(resultObj VMlogin.Vresult) error {
	var oldResultObj VMlogin.Vresult
	var redisKey string
	var redisValue string
	var err error

	// 组合查询用户信息的声明和赋值
	redisKey = FormatRedisString(REDIS_KEY_USER, resultObj.Data.UserId)
	redisJsonValue, _ := json.Marshal(resultObj.Data)
	redisValue = string(redisJsonValue)

	// 查询是否存在此用户
	infoVal, _ := Cache.Instance().Get(redisKey)

	// json反序列化查询到的json字符串,将存储在缓存中的值赋值到一个变量上
	_ = json.Unmarshal([]byte(infoVal), &oldResultObj.Data)
	// 如果返回空字符串,则是新用户,创建新的缓存
	// 否则根据缓存的token组合去将旧的缓存删除,覆盖新的用户信息和创建新的token
	if infoVal == "" {
		// 设置新的用户信息缓存
		err = Cache.Instance().Set(redisKey, redisValue, 744 * time.Hour)
		if err != nil {
			return err
		}

		// 设置新的token信息缓存
		redisKey = FormatRedisString(REDIS_KEY_TOKEN, resultObj.Data.Token)
		redisValue = strconv.Itoa(resultObj.Data.UserId)
		err = Cache.Instance().Set(redisKey, redisValue, 744 * time.Hour)
		if err != nil {
			return err
		}
	} else {
		// 声明并赋值旧的token信息
		oldToken := oldResultObj.Data.Token
		// 删除旧的token
		err = Cache.Instance().Del(FormatRedisString(REDIS_KEY_TOKEN, oldToken))
		if err != nil {
			return err
		}

		// 设置覆盖新的用户信息
		err = Cache.Instance().Set(redisKey, redisValue, 744 * time.Hour)
		if err != nil {
			return err
		}

		// 设置新的token
		redisKey = FormatRedisString(REDIS_KEY_TOKEN, resultObj.Data.Token)
		redisValue = strconv.Itoa(resultObj.Data.UserId)
		err = Cache.Instance().Set(redisKey, redisValue, 744 * time.Hour)
		if err != nil {
			return err
		}
	}
	return nil
}

复制代码