尝试在Redis Cloud中使用Mercari/Datastore的Memcache集成代替GAE/Go1.12
简而言之
在上一篇文章中,我已经完成了GAE第二代/Go的构建,请使用Redis Cloud替代Memcache。所以,今天我将尝试构建Redis Cloud。
2019年度版的文章(用GAE第二代+Go v1.12构建echo服务器的方法)可以在以下链接中找到:
https://qiita.com/br_branch/items/a26480a05ecb97ac20b3
这次的源代码也在以下链接中公开。
https://github.com/brbranch/GaeGo112EchoSample/tree/feature/redis
本文的目标
最终,我们将使上一次使用的 mercari/datastore 和 redis 进行协同操作。
注册 Redis Cloud

我們將在這裡進行註冊,點擊「免費開始」。當您註冊郵箱後,將收到激活郵件,點擊該郵件後需要輸入姓名、密碼等資訊。完成所有步驟後,您將被轉至這樣的畫面。

首先选择Essentials,然后选择免费的30MB计划。
保持免费,似乎不需要进行信用卡注册之类的步骤。

当前步骤为创建数据库。
由于此处可公开查看,请确保密码设置为强密码(考虑直接使用此页面预先填入的密码是否可行)。

据说,数据淘汰策略是指当内存达到最大容量时如何清除数据的方法。详细信息可在此处找到:
https://redis-documentasion-japanese.readthedocs.io/ja/latest/topics/lru-cache.html#eviction-policies
以下是引述的内容。
-
- noeviction : メモリ使用量が制限に達しており、クライアントが追加のメモリを要求するコマンド(DEL やいくつかの例外を除く、ほとんどの書き込みコマンド)を実行しようとした場合はエラーを返す。
-
- allkeys-lru : 新しいデータのためにスペースを空けるため、もっとも最近使われていない(LRU)キーから削除するよう試みる。
-
- volatile-lru : 新しいデータのためにスペースを空けるため、もっとも最近使われていない(LRU)キーから削除するよう試みる。ただし、 expire set が指定されたキーのみを対象とする。
- volatile-random : 新しいデータのためにスペースを空けるため、ランダムなキーを選んで削除する。ただし、 expire set が指定されたキーのみを対象とする。
首先,由于只有30MB的空间,我觉得使用allkeys-lru可能是个不错的选择。
這樣做後,設定已完成。
因此,我們試著從上次創建的倉庫中呼叫。
我想尝试一下玩一会儿。 (Wǒ .)
所以,首先我們來玩一玩,一邊看著Google的文件。
使用Redis Labs Redis保存应用程序数据的缓存
我打算先尝试保存和获取字符串。
package client
import "github.com/gomodule/redigo/redis"
type redisClient struct {
pool *redis.Pool
}
var client *redisClient = nil
func GetRedisClient() *redisClient {
if client == nil {
panic("client is not initialized")
}
return client
}
func InitRedis(endpoint string, password string) {
if client != nil {
return
}
client = &redisClient{}
client.pool = &redis.Pool{
Dial: func() (redis.Conn, error) {
conn, err := redis.Dial("tcp", endpoint)
if password == "" {
return conn, err
}
if err != nil {
return nil, err
}
// パスワードが設定されてる場合のみ認証する(ローカルだと不要なため)
if password != "" {
if _, err := conn.Do("AUTH", password); err != nil {
conn.Close()
return nil, err
}
} else if (strings.HasPrefix(endpoint, "redis")) {
// EndpointがRedusCloudなのにパスワードがないのはおかしい
conn.Close()
return nil, errors.New("invalid password.")
}
return conn, nil
},
}
}
func (c *redisClient) GetConnection() redis.Conn {
return c.pool.Get()
}
func (c *redisClient) PutString(key string, value string) error {
con := c.GetConnection()
defer con.Close()
_, err := con.Do("SET", key, value)
return err
}
func (c *redisClient) GetString(key string) (string, error) {
con := c.GetConnection()
defer con.Close()
return redis.String(con.Do("GET", key))
}
主要程序(main.go)只记录与上次相比的差异部分。
func main() {
// Endpointを指定
redisAddr := os.Getenv("REDIS_ADDR")
// Passwordを指定
redisPass := os.Getenv("REDIS_PASS")
client.InitRedis(redisAddr, redisPass)
// Redisの操作
e.GET("/redis/put/:name", func(e echo.Context) error {
name := e.Param("name")
if err := client.GetRedisClient().PutString("test", name); err != nil {
log.Fatalf("faield to get redis (reason: %v)", err)
return e.String(http.StatusInternalServerError, "error")
}
return e.String(http.StatusOK, fmt.Sprintf("put redis: %s", name))
})
e.GET("/redis/get", func(e echo.Context) error {
if name, err := client.GetRedisClient().GetString("test"); err != nil {
log.Fatalf("faield to get redis (reason: %v)", err)
return e.String(http.StatusInternalServerError, "error")
} else {
return e.String(http.StatusOK, fmt.Sprintf("get redis: %s", name))
}
})
}
通过实际操作 http://localhost:8080/redis/put/hogehoge ,成功保存了字符串。


顺便提一句,好像无法从Redis的网站上确认内部情况。
嗯,没办法咯。
据说在上述代码的Do()函数中,第一个参数是要发送给Redis的命令。
Google文档中提到的INCR是一个用于增加数值的命令。
还有很多其他定义的命令。
Redis 命令列表
https://symfoware.blog.fc2.com/blog-entry-521.html
在本地启动Redis。
虽然以前提到的方法已经足够了,但还是希望在本地环境中进行设置。
所以,我在阅读https://redis.io/topics/quickstart的同时尝试操作了一下。
追加(2019/09/11)
不必特意制作,brew中已经有了。
在Mac上安装Redis
https://qiita.com/checkpoint/items/58b9b0193c0c46400eeb
$ brew install wget # デフォルトだとMacはwget入ってないので(もう入ってたら当然不要)
$ wget http://download.redis.io/redis-stable.tar.gz
--2019-09-07 16:52:52-- http://download.redis.io/redis-stable.tar.gz
︙
2019-09-07 16:52:59 (316 KB/s) - `redis-stable.tar.gz へ保存完了 [2014657/2014657]
$ tar xvzf redis-stable.tar.gz
x redis-stable/
x redis-stable/INSTALL
x redis-stable/sentinel.conf
x redis-stable/deps/
x redis-stable/deps/update-jemalloc.sh
x redis-stable/deps/jemalloc/
︙
$ cd redis-stable
$ make
︙
# ビルドがちゃんと全部成功してるかテストする
$ make test
\o/ All tests passed without errors!
Cleanup: may take some time... OK
在中国,我们只需要一个选择。如果测试通过且没有错误,最后一个 make test 是可选的。生成的内容会存放在 redis-stable/src 文件夹中,所以请确保将其添加到 PATH 中。
执行并协调
在将PATH设置完成后,按照以下方式执行。
$ redis-server
65327:C 07 Sep 2019 17:03:47.270 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
65327:C 07 Sep 2019 17:03:47.270 # Redis version=5.0.5, bits=64, commit=00000000, modified=0, pid=65327, just started
65327:C 07 Sep 2019 17:03:47.270 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
65327:M 07 Sep 2019 17:03:47.272 * Increased maximum number of open files to 10032 (it was originally set to 4864).
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis 5.0.5 (00000000/0) 64 bit
.-`` .-```. ```\/ _.,_ ''-._
( ' , .-` | `, ) Running in standalone mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 6379
| `-._ `._ / _.-' | PID: 65327
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | http://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-'
65327:M 07 Sep 2019 17:03:47.273 # Server initialized
65327:M 07 Sep 2019 17:03:47.273 * Ready to accept connections
因此,我们将使用 localhost:6379 将环境变量 REDIS_ADDR 传递并执行。 在这种情况下,密码是不必要的(可以使用空字符串)。


我从本地正确地获取了。
顺便提一下,刚刚安装的工具中还有一个叫做“redis-cli”的工具,你也可以在那里获取保存的内容。
$ redis-cli
127.0.0.1:6379> get test
"localRedisTest"
你进来了啊(´・ω・`)
使mercari/datastore和redis进行协作
好了,终于开始正题了。我们将通过将mercari/datastore和redis进行协作,实现Entity的缓存功能。
import (
client2 "gaego112echosample/client"
"github.com/labstack/echo"
datastore2 "go.mercari.io/datastore"
"go.mercari.io/datastore/clouddatastore"
"go.mercari.io/datastore/dsmiddleware/rediscache"
// ...省略
)
func HelloWorld(e echo.Context) error {
// ... 省略
// Redisとの連携を行う
redisConn := client2.GetRedisClient().GetConnection()
defer redisConn.Close()
mw := rediscache.New(redisConn,
// ログが出るようにする
rediscache.WithLogger(func(ctx context.Context, format string, args ...interface{}){
log.Printf(format, args...)
}),
// Redisに登録されてるか見るためにログに出力するようにしてる
rediscache.WithCacheKey(func(key datastore2.Key) string {
cacheKey := fmt.Sprintf("cache:%s", key.Encode())
log.Printf("redis cache key: %s", cacheKey)
return cacheKey
}),
)
client.AppendMiddleware(mw)
// ... 省略
}
这样的话,mericari/dtastore 可以不断注入功能。太棒了。
我会实际启动一下看看。
// ターミナル1: redisサーバー
$ redis-server
// ターミナル2: datastoreエミュレーター
$ gcloud beta emulators datastore start --host-port localhost:8059 --project test-project
// ターミナル3: ローカルサーバー
$ env DATASTORE_EMULATOR_HOST=localhost:8059 DATASTORE_PROJECT_ID=test-project REDIS_ADDR=localhost:6379 go run main.go
当尝试访问时,本地服务器会输出以下日志。
2019/09/07 17:40:38 Project ID:
2019/09/07 17:40:38 dsmiddleware/rediscache.SetMulti: incoming len=1
2019/09/07 17:40:38 redis cache key: cache:EgkKBHBvc3QQuWA
2019/09/07 17:40:38 dsmiddleware/rediscache.SetMulti: len=1
2019/09/07 17:40:38 dsmiddleware/rediscache.GetMulti: incoming len=1
2019/09/07 17:40:38 redis cache key: cache:EgkKBHBvc3QQuWA
2019/09/07 17:40:38 dsmiddleware/rediscache.GetMulti: hit=1 miss=0
当尝试使用 redis-cli 获取此 cache:EgkKBHBvc3QQuWA 时…
$ redis-cli
127.0.0.1:6379> get cache:EgkKBHBvc3QQuWA
"\x1b\xff\x83\x02\x01\x01\x0cPropertyList\x01\xff\x84\x00\x01\xff\x82\x00\x005\xff\x81\x03\x01\x01\bProperty\x01\xff\x82\x00\x01\x03\x01\x04Name\x01\x0c\x00\x01\x05Value\x01\x10\x00\x01\aNoIndex\x01\x02\x00\x00\x00\x1e\xff\x84\x00\x01\x01\acontent\x01\x06string\x0c\x06\x00\x04test\x00"
现在已经在缓存中了。太完美了!
尝试之后的感受
我可以毫不费力地根据感觉来构建,没有特别困难的部分。嗯,能够像这样理解并且进行开发是件很好的事情。暂时看来似乎可以用这个方案进行开发。
请看以下提供的建议
使用 Redis Labs Redis 进行应用程序数据的缓存存储。
Mericari / datastore 文档.ja
https://github.com/mercari/datastore/blob/master/doc_ja.go
美利坚/数据存储 文档.ja
https://github.com/mercari/datastore/blob/master/doc_ja.go
Redis 快速入门
https://redis.io/topics/quickstart
使用Redigo的基本方法 (1)
https://qiita.com/riverplus/items/c1df770838d3868c3a13
Redis 命令列表
https://symfoware.blog.fc2.com/blog-entry-521.html
我尝试了使用Golang和Redis玩耍