赞
踩
1. 定义flag参数的相关函数
参数有三个:第一个为 参数名称,第二个为 默认值,第三个是 使用说明
(1)通过 flag.String(),Bool(),Int() 等 flag.Xxx() 方法,该种方式返回一个相应的指针。
var ip = flag.Int("flagname", 1234, "help message for flagname")
(2)通过 flag.XxxVar() 方法将 flag 绑定到一个变量,该种方式返回 值类型。
var flagvar int
flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname")
(3)通过 flag.Var() 绑定自定义类型,自定义类型需要实现 Value 接口 (Receiver 必须为指针)
fmt.Println("flagvar has value ", flagvar)
测试代码:
package main import ( "flag" "fmt" //"os" ) // 例如输入:go run xxx.go -ok -id 11111 -port 8899 -name TestUser very goo func main() { //fmt.Println(os.Args) // 1. 从命令行中获取参数。 // 1.1 通过 flag.String(),Bool(),Int() 等 flag.Xxx() 方法,该种方式返回一个相应的指针。 ok := flag.Bool("ok", false, "is ok") // 若命令行不设置ok参数, 则默认为参2,这里默认为false。若设置了ok参数但是没有设置值,默认为true,即-ok传进,而不是-ok=true。 id := flag.Int("id", 0, "id") port := flag.String("port", ":8080", "http listen port") // 1.2 通过 flag.XxxVar() 方法将 flag 绑定到一个变量,该种方式返回 值类型。该值会赋值给传进去的地址。这里为&name。 var name string flag.StringVar(&name, "name", "Jack", "name") fmt.Println("ok:", *ok) fmt.Println("id:", *id) fmt.Println("port:", *port) fmt.Println("name:", name) // 2. 解析命令行。只有解析了命令行,才能得到用户传进来的值,否则全是默认值即取参2的值。可以查看上面的打印。 flag.Parse() // flag.Usage() // 3. 将其它参数也获取,方便观察。 others := flag.Args() fmt.Println("===============================") fmt.Println("ok:", *ok) fmt.Println("id:", *id) fmt.Println("port:", *port) fmt.Println("name:", name) fmt.Println("other:", others) }
注意:
1)cmd -flag // 只支持bool类型
2)cmd -flag xxx // 只支持非bool类型
3)cmd -flag=xxx// 支持所有
其中cmd指go run xxx.go这些命令。
验证上面三点:
例子1:先验证第1点,使用 ok 这个 flag 验证,这个例子首先说明支持bool类型。然后运行第二条命令,id为int类型,id的赋值去掉后,说明其它类型不支持,所以验证cmd -flag 只支持bool类型。
go run xxx.go -ok -id 11111 -port 8899 -name TestUser very goo
go run xxx.go -ok -id -port 8899 -name TestUser very goo
图1结果看到,命令行填了ok这个flag,但是不赋值,默认值是true(注意与代码的默认值false区别,代码的默认值只有-ok这个flag不存在才会取这个值)。
图2结果,说明不支持非bool。大家可以自己试试其它非bool类型的。
例子2:
go run 5-1-cli-flag.go -ok false -id 11111 -port 8899 -name TestUser very goo
go run 5-1-cli-flag.go -ok -id 11111 -port 8899 -name TestUser very goo
运行第一条命令,图1结果看到,虽然我在命令行给ok的flag赋值为false,但是打印还是true,并且导致后面的flag无法解析,说明bool类型不支持cmd -flag xxx的格式。
运行第二条命令,结果看到,int、string这些类型都是支持cmd -flag xxx的格式。
例子3:
go run 5-1-cli-flag.go -ok=false -id=11111 -port=8899 -name=TestUser very goo
结果看到,说明bool、int、string类型是支持cmd -flag=xxx这种写法的。能成功获取到所有的值。
uuid唯一ID在实际开发会经常用到,主要是方便在分布式的场景生成uuid,例如生成订单号,因为订单号需要唯一。所有这个知识点非常重要。
测试代码:
package main import ( "fmt" "log" "math/rand" "reflect" "time" "gitee.com/GuaikOrg/go-snowflake/snowflake" "github.com/chilts/sid" "github.com/kjk/betterguid" "github.com/oklog/ulid" "github.com/rs/xid" uuid "github.com/satori/go.uuid" "github.com/segmentio/ksuid" "github.com/sony/sonyflake" ) const FOR_LOOP = 1000000 // 1. 通过"github.com/rs/xid"的算法生成uuid。 func genXid() { id := xid.New() fmt.Printf("github.com/rs/xid: %s, len:%d\n", id.String(), len(id.String())) } // 2. 通过"github.com/segmentio/ksuid"的算法生成uuid。 func genKsuid() { id := ksuid.New() fmt.Printf("github.com/segmentio/ksuid: %s, len:%d\n", id.String(), len(id.String())) } // 3. 通过"github.com/kjk/betterguid"的算法生成uuid。 func genBetterGUID() { id := betterguid.New() fmt.Printf("github.com/kjk/betterguid: %s, len:%d\n", id, len(id)) } // 4. 通过"github.com/oklog/ulid"的算法生成uuid。 func genUlid() { t := time.Now().UTC() entropy := rand.New(rand.NewSource(t.UnixNano())) id := ulid.MustNew(ulid.Timestamp(t), entropy) fmt.Printf("github.com/oklog/ulid: %s, len:%d\n", id.String(), len(id.String())) } // 5. 通过"gitee.com/GuaikOrg/go-snowflake/snowflake"(雪花算法)的算法生成uuid。 // 源码链接:https://gitee.com/GuaikOrg/go-snowflake func genSnowflake() { flake, err := snowflake.NewSnowflake(int64(0), int64(0)) if err != nil { log.Fatalf("snowflake.NewSnowflake failed with %s\n", err) } id := flake.NextVal() fmt.Printf("gitee.com/GuaikOrg/go-snowflake:%x, type:%s\n", id, reflect.TypeOf(id)) } // 6. 通过"github.com/sony/sonyflake"(索尼算法)的算法生成uuid。 func genSonyflake() { flake := sonyflake.NewSonyflake(sonyflake.Settings{}) id, err := flake.NextID() if err != nil { log.Fatalf("flake.NextID() failed with %s\n", err) } fmt.Printf("github.com/sony/sonyflake: %x, type:%s\n", id, reflect.TypeOf(id)) } // 7. 通过"github.com/chilts/sid"的算法生成uuid。 func genSid() { id := sid.Id() fmt.Printf("github.com/chilts/sid: %s, len:%d\n", id, len(id)) } // 8. 通过"github.com/satori/go.uuid"的算法生成uuid。 func genUUIDv4() { id, err := uuid.NewV4() if err != nil { fmt.Printf("get uuid error [%s]", err) } fmt.Printf("github.com/satori/go.uuid: %s, len:%d\n", id, len(id)) } func testGenXid(n int) { t0 := time.Now() for i := 0; i < n; i++ { _ = xid.New() } elapsed := time.Since(t0) fmt.Println("github.com/rs/xid n:", n, "time:", elapsed) } func testGenKsuid(n int) { t0 := time.Now() for i := 0; i < n; i++ { _ = ksuid.New() } elapsed := time.Since(t0) fmt.Println("github.com/segmentio/ksuid n:", n, "time:", elapsed) } func testGenBetterguid(n int) { t0 := time.Now() for i := 0; i < n; i++ { _ = betterguid.New() } elapsed := time.Since(t0) fmt.Println("github.com/kjk/betterguid n:", n, "time:", elapsed) } func testGenUlid(n int) { t0 := time.Now() for i := 0; i < n; i++ { t := time.Now().UTC() entropy := rand.New(rand.NewSource(t.UnixNano())) _ = ulid.MustNew(ulid.Timestamp(t), entropy) } elapsed := time.Since(t0) fmt.Println("github.com/oklog/ulid n:", n, "time:", elapsed) } func testGenSnowflake(n int) { t0 := time.Now() flake, err := snowflake.NewSnowflake(int64(0), int64(0)) if err != nil { log.Fatalf("snowflake.NewSnowflake failed with %s\n", err) } for i := 0; i < n; i++ { _ = flake.NextVal() } elapsed := time.Since(t0) fmt.Println("gitee.com/GuaikOrg/go-snowflake n:", n, "time:", elapsed) } func testGenSonyflake(n int) { t0 := time.Now() flake := sonyflake.NewSonyflake(sonyflake.Settings{}) // 注意这一行的位置 for i := 0; i < n; i++ { _, err := flake.NextID() if err != nil { log.Fatalf("flake.NextID() failed with %s\n", err) } } elapsed := time.Since(t0) fmt.Println("github.com/sony/sonyflake n:", n, "time:", elapsed) } func testGenSid(n int) { t0 := time.Now() for i := 0; i < n; i++ { _ = sid.Id() } elapsed := time.Since(t0) fmt.Println("github.com/chilts/sid n:", n, "time:", elapsed) } func testGenUUIDv4(n int) { t0 := time.Now() for i := 0; i < n; i++ { _, err := uuid.NewV4() if err != nil { fmt.Printf("get uuid error [%s]", err) } } elapsed := time.Since(t0) fmt.Println("github.com/satori/go.uuid n:", n, "time:", elapsed) } func main() { // 一:简单效果展示。生成uuid并打印出来。 fmt.Printf("效果展示...\n") // 1. genXid() genXid() genXid() // 2. genKsuid() // 3. genBetterGUID() // 4. genUlid() // 5. genSnowflake() // 6. genSonyflake() // 7. genSid() // 8. genUUIDv4() // 二:对上面8个生成uuid的算法进行性能测试,测试方法:生成同样多的uuid,不同的算法花费了多长的时间。 fmt.Printf("性能测试...\n") testGenXid(FOR_LOOP) testGenKsuid(FOR_LOOP) testGenBetterguid(FOR_LOOP) testGenUlid(FOR_LOOP) testGenSnowflake(FOR_LOOP) testGenSonyflake(FOR_LOOP) testGenSid(FOR_LOOP) testGenUUIDv4(FOR_LOOP) }
结果:
下面重点讲解比较重要的两个算法,一个是雪花算法,另一个是索尼算法。
snowflake算法使用的一个64 bit的整型数据,被划分为四部分。
(下面的这几句话是先根据机器位分析,然后根据同一台机器可以生成多少id,并分析这台机器多少年不出现重复的情况来总结的。)
所以根据上面的意思,那么在分布式场景下,最多可以有1024台机器生成唯一的id。
并且在一台机器上面,每毫秒可以最多生成4096个序列号。
根据41bits的时间戳长度限制,如果程序连续运行那么最多可以运行69年不出现重复的id,如果超过69年,那么这台机器就可能出现重复的uuid。
69年是这样算的:
(( ((2 << 40)/1000) / 3600 ) /24 ) / 365 = 69年
2左移40位是因为本身2就是相当于左移了一位,所以2<<40=2^41次方。你可以试试2<<2,结果等价于2^3=8。
然后除以1000是因为毫秒转换成秒。 除以3600是转成小时。 除以24是转换成天。 除以365是转换成年。
索尼算法是改进后的雪花算法。同样分成四部分,意义一样,只不过位数和一些组成部分的顺序调整了一下。
所以根据上面的意思,那么在分布式场景下,最多可以有2^16=65536台机器生成唯一的id。
并且在一台机器上面,每10毫秒可以最多生成256个序列号。
根据39bits的时间戳长度限制,如果程序连续运行那么最多可以运行174年不出现重复的id,如果超过174年,那么这台机器就可能出现重复的uuid。
174年的求法和上面雪花算法一样,但需要注意单位:
(( ((2 << 38)/100) / 3600 ) /24 ) / 365 = 174年
答:sonyflake对于snowflake的改进是用空间换时间,时间戳位数减少,以从69年升至174年。但是1秒最多生成的ID从409.6w降至2.56w条。
答:这是因为改进后的雪花算法的时间戳变成了39bit,单位为10ms,由于序列号为8bit,所以在同一台机器中,10ms只能生成256个uuid。如果看代码的处理,非常粗暴,就是直接sleep对应的时间来限制,所以改进后的索尼算法是非常耗时的。讲出sleep是这一点是非常关键的。面试官可能会给你加分。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。