rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
rdb := redis.NewClient(&redis.Options{
TLSConfig: &tls.Config{
MinVersion: tls.VersionTLS12,
// Certificates: []tls.Certificate{cert},
// ServerName: "your.domain.com",
},
})
rdb := redis.NewClusterClient(&redis.ClusterOptions{
Addrs: []string{":7000", ":7001", ":7002", ":7003", ":7004", ":7005"},
// 若要根据延迟或随机路由命令,请启用以下命令之一
// RouteByLatency: true,
// RouteRandomly: true,
})
rdb := redis.NewFailoverClient(&redis.FailoverOptions{
MasterName: "master-name",
SentinelAddrs: []string{":9126", ":9127", ":9128"},
})
// doCommand go-redis基本使用示例
func doCommand() {
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
defer cancel()
// 执行命令获取结果
val, err := rdb.Get(ctx, "key").Result()
fmt.Println(val, err)
// 先获取到命令对象
cmder := rdb.Get(ctx, "key")
fmt.Println(cmder.Val()) // 获取值
fmt.Println(cmder.Err()) // 获取错误
// 直接执行命令获取错误
err = rdb.Set(ctx, "key", 10, time.Hour).Err()
// 直接执行命令获取值
value := rdb.Get(ctx, "key").Val()
fmt.Println(value)
}
go-redis 还提供了一个执行任意命令或自定义命令的 Do 方法,特别是一些 go-redis 库暂时不支持的命令都可以使用该方法执行。
go-redis 库提供了一个 redis.Nil 错误来表示 Key 不存在的错误。因此在使用 go-redis 时需要注意对返回错误的判断。在某些场景下我们应该区别处理 redis.Nil 和其他不为 nil 的错误。
// getValueFromRedis redis.Nil判断
func getValueFromRedis(key, defaultValue string) (string, error) {
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
defer cancel()
val, err := rdb.Get(ctx, key).Result()
if err != nil {
// 如果返回的错误是key不存在
if errors.Is(err, redis.Nil) {
return defaultValue, nil
}
// 出其他错了
return "", err
}
return val, nil
}
// zsetDemo 操作zset示例
func zsetDemo() {
// key
zsetKey := "language_rank"
// value
languages := []*redis.Z{
{Score: 90.0, Member: "Golang"},
{Score: 98.0, Member: "Java"},
{Score: 95.0, Member: "Python"},
{Score: 97.0, Member: "JavaScript"},
{Score: 99.0, Member: "C/C++"},
}
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
defer cancel()
// ZADD
err := rdb.ZAdd(ctx, zsetKey, languages...).Err()
if err != nil {
fmt.Printf("zadd failed, err:%v\n", err)
return
}
fmt.Println("zadd success")
// 把Golang的分数加10
newScore, err := rdb.ZIncrBy(ctx, zsetKey, 10.0, "Golang").Result()
if err != nil {
fmt.Printf("zincrby failed, err:%v\n", err)
return
}
fmt.Printf("Golang's score is %f now.\n", newScore)
// 取分数最高的3个
ret := rdb.ZRevRangeWithScores(ctx, zsetKey, 0, 2).Val()
for _, z := range ret {
fmt.Println(z.Member, z.Score)
}
// 取95~100分的
op := &redis.ZRangeBy{
Min: "95",
Max: "100",
}
ret, err = rdb.ZRangeByScoreWithScores(ctx, zsetKey, op).Result()
if err != nil {
fmt.Printf("zrangebyscore failed, err:%v\n", err)
return
}
for _, z := range ret {
fmt.Println(z.Member, z.Score)
}
}
获取所有key
vals, err := rdb.Keys(ctx, "prefix*").Result()
但是如果需要扫描数百万的 key ,那速度就会比较慢。
// scanKeysDemo2 按前缀扫描key示例
func scanKeysDemo2() {
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
defer cancel()
// 按前缀扫描key
iter := rdb.Scan(ctx, 0, "prefix:*", 0).Iterator()
for iter.Next(ctx) {
fmt.Println("keys", iter.Val())
}
if err := iter.Err(); err != nil {
panic(err)
}
}
此外,对于 Redis 中的 set、hash、zset 数据类型,go-redis 也支持类似的遍历方法。
iter := rdb.SScan(ctx, "set-key", 0, "prefix:*", 0).Iterator()
iter := rdb.HScan(ctx, "hash-key", 0, "prefix:*", 0).Iterator()
iter := rdb.ZScan(ctx, "sorted-hash-key", 0, "prefix:*", 0).Iterator(
Redis Pipeline 允许通过使用单个 client-server-client 往返执行多个命令来提高性能。区别于一个接一个地执行100个命令,你可以将这些命令放入 pipeline 中,然后使用1次读写操作像执行单个命令一样执行它们。
pipe := rdb.Pipeline()
incr := pipe.Incr(ctx, "pipeline_counter")
pipe.Expire(ctx, "pipeline_counter", time.Hour)
cmds, err := pipe.Exec(ctx)
if err != nil {
panic(err)
}
// 在执行pipe.Exec之后才能获取到结果
fmt.Println(incr.Val())
或者,你也可以使用Pipelined 方法,它会在函数退出时调用 Exec。
var incr *redis.IntCmd
cmds, err := rdb.Pipelined(ctx, func(pipe redis.Pipeliner) error {
incr = pipe.Incr(ctx, "pipelined_counter")
pipe.Expire(ctx, "pipelined_counter", time.Hour)
return nil
})
if err != nil {
panic(err)
}
// 在pipeline执行后获取到结果
fmt.Println(incr.Val())
在那些我们需要一次性执行多个命令的场景下,就可以考虑使用 pipeline 来优化。
事务
Redis 是单线程执行命令的,因此单个命令始终是原子的,但是来自不同客户端的两个给定命令可以依次执行,例如在它们之间交替执行。但是,Multi/exec能够确保在multi/exec两个语句之间的命令之间没有其他客户端正在执行命令。
// TxPipeline demo
pipe := rdb.TxPipeline()
incr := pipe.Incr(ctx, "tx_pipeline_counter")
pipe.Expire(ctx, "tx_pipeline_counter", time.Hour)
_, err := pipe.Exec(ctx)
fmt.Println(incr.Val(), err)
// TxPipelined demo
var incr2 *redis.IntCmd
_, err = rdb.TxPipelined(ctx, func(pipe redis.Pipeliner) error {
incr2 = pipe.Incr(ctx, "tx_pipeline_counter")
pipe.Expire(ctx, "tx_pipeline_counter", time.Hour)
return nil
})
fmt.Println(incr2.Val(), err)
我们通常搭配 WATCH命令来执行事务操作。从使用WATCH命令监视某个 key 开始,直到执行EXEC命令的这段时间里,如果有其他用户抢先对被监视的 key 进行了替换、更新、删除等操作,那么当用户尝试执行EXEC的时候,事务将失败并返回一个错误,用户可以根据这个错误选择重试事务或者放弃事务。
// watchDemo 在key值不变的情况下将其值+1
func watchDemo(ctx context.Context, key string) error {
return rdb.Watch(ctx, func(tx *redis.Tx) error {
n, err := tx.Get(ctx, key).Int()
if err != nil && err != redis.Nil {
return err
}
// 假设操作耗时5秒
// 5秒内我们通过其他的客户端修改key,当前事务就会失败
time.Sleep(5 * time.Second)
_, err = tx.TxPipelined(ctx, func(pipe redis.Pipeliner) error {
pipe.Set(ctx, key, n+1, time.Hour)
return nil
})
return err
}, key)
}
将上面的函数执行并打印其返回值,如果我们在程序运行后的5秒内修改了被 watch 的 key 的值,那么该事务操作失败,返回redis: transaction failed错误。

// KeepTTL是-1
log.Fatalln(rdb.Set(context.Background(),"name","1",redis.KeepTTL))
有些版本不支持-1设置不过期,
func (c cmdable) Set(ctx context.Context, key string, value interface{}, expiration time.Duration) *StatusCmd {
args := make([]interface{}, 3, 5)
args[0] = "set"
args[1] = key
args[2] = value
if expiration > 0 {
if usePrecise(expiration) {
args = append(args, "px", formatMs(ctx, expiration))
} else {
args = append(args, "ex", formatSec(ctx, expiration))
}
} else if expiration == KeepTTL {
args = append(args, "keepttl")
}
cmd := NewStatusCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}
通过阅读源码,当exp为0时就可以跳过exp的分支判断,就可以设置不过期。
在Railcasts上,我注意到一个非常有趣的功能“转到符号”窗口。它像Command-T一样工作,但显示当前文件中可用的类和方法。如何在vim中获取它? 最佳答案 尝试:helptags有各种程序和脚本可以生成标记文件。此外,标记文件格式非常简单,因此很容易将sed(1)或类似的脚本组合在一起,无论您使用何种语言,它们都可以生成标记文件。轻松获取标记文件(除了下载生成器之外)的关键在于格式化样式而不是实际解析语法。 关于ruby-on-rails-Textmate'Gotosymbol
自97年以来我一直在使用vi/vim进行各种快速编辑和管理任务,但最近才考虑使用它来替换Netbeans作为我选择的ruby编辑器。我发现一件事在Netbeans和Eclipse中非常有用的是Ctrl+Click“转到定义”功能,您可以在其中按住Ctrl键并单击一个类或方法,然后它将带您了解定义。现在,我玩过丰富的ctags和rails.vim,而且很接近,但没有雪茄。这就是我想要的:默认情况下在Netbeans和Eclipse中,您可以在本地rails中按住ctrl并单击本地方法或类项目,但你也可以ctrl+click定义在gems或用Ruby编写的系统库。以Netbeans为例
这个问题在这里已经有了答案:HowdoIcreatemultiplesubmitbuttonsforthesameforminRails?(7个答案)关闭9年前。所以..'save'%>'library'%>然后在我的Controller中:with_actiondo|a|a.savedoenda.librarydoendend问题是只有一个操作被调用...两个submit_tags调用相同的操作...知道为什么吗?或者我如何获得两个按钮以将表单提交给两种不同的方法?
IV.SYSTEMIMPLEMENTATIONWeadoptmodulardesignfollowingtheintegrationofblockchain.Itbringsmoreflexibilitybyseparatingtheimplementationofdifferentfunctionalities,sowecouldleveragetheadvantagesoftheblockchain-basedsmartcontractwhilereducingoverhead.Figure3illustrateshowdifferentmodulesareinvolvedintheint
问题我该如何做这样的事情:{{$use_ssl:=(ne$.Env.CERT_NAME"")}}其中$.Env.CERT_NAME可能为零/未定义。如果它是零,它给出这个错误:at:errorcallingne:invalidtypeforcomparison注意:我无法控制传递给Go模板的对象,因此必须完全在模板本身内解决这个问题。我尝试过的我试图通过首先检查它是否为非空来变通:{{$use_ssl:=(($.Env.CERT_NAME)&&(ne$.Env.CERT_NAME""))}}但它给出了这个错误:unexpected"&"inoperand所以我切换到这个,这在语法上是允
time包与string包可以说是在Go语言的开发中常用的两个包实际开发过程中(例如web开发)经常会遇到time类型与string类型的交互,计算比较等场景首先来了解GO语言里非常浪漫的一个点,即2006-01-0215:04:05,GO语言诞生的时间,通常用来做时间的格式化time转stringt:=time.Now()//当前时间timeLayoutStr:="2006-01-0215:04:05"t.Format(timeLayoutStr)//返回值为string,可以用一个值来接收它上述例子中,将time类型t转换为string类型,并格式化为年-月-日时-分-秒,这里的格式化是可
我正在研究ui-router。我有一个状态:.state('new-personal-orders',{url:'/orders/new-personal-orders/:catId?',template:''})在我的Controller中,我可以使用$state.go('new-personal-orders',null,{reload:true})在Html文件中我有一个anchor标记:Link如果标签被点击,状态就会改变,'new-personal-orders'变成当前状态,在url中有尾随散列。然后url看起来像:http://localhost:3000/orders/
我一直在重读SpencerTipping的优秀作品JavascriptinTenMinutes在这个使用惰性作用域创建语法宏的示例中,我终究无法弄清楚发生了什么:varf=function(){return$0+$1};varg=eval(f.toString().replace(/\$(\d+)/g,function(_,digits){return'arguments['+digits+']'}));g(5,6);//=>11(exceptonIE)特别是,$0和$1正在被一个函数定义取代——那个函数是如何被计算的?(大概是通过eval(),但我没有看到)。函数中单个下划线参数的用
我已经完美地初始化了$stateProvider并且我正在将所有这些状态与ui-sref一起使用。效果很好。用户按下按钮并通过$stateProvider进入编辑页面。在这个页面上,我有一个执行$http请求的表单:this.pushData=function(data){$http.post('/data/'+$stateParams.dataId+'/otherdata',JSON.stringify({id:otherdata.id,name:otherdata.name}),configAuth).then(functionsuccess(response){varaddedD
Go第15章:单元测试15.1先看一个需求在我们工作中,我们会遇到这样的情况,就是去确认一个函数,或者一个模块的结果是否正确,如:15.2传统的方法15.2.1传统的方式来进行测试在main函数中,调用addUpper函数,看看实际输出的结果是否和预期的结果一致,如果一致,则说明函数正确,否则函数有错误,然后修改错误代码实现:15.2.2传统方法的缺点分析不方便,我们需要在main函数中去调用,这样就需要去修改main函数,如果现在项目正在运行,就可能去停止项目。不利于管理,因为当我们测试多个函数或者多个模块时,都需要写在main函数,不利于我们管理和清晰我们思路引出单元测试。->testin