mirror of
https://github.com/chai2010/advanced-go-programming-book.git
synced 2025-05-24 04:22:22 +00:00
fmt code
This commit is contained in:
parent
9ad62fe063
commit
6e50377cee
@ -47,15 +47,15 @@ func hello(wr http.ResponseWriter, r *http.Request) {
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
func helloHandler(wr http.ResponseWriter, r *http.Request) {
|
func helloHandler(wr http.ResponseWriter, r *http.Request) {
|
||||||
...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
func showInfoHandler(wr http.ResponseWriter, r *http.Request) {
|
func showInfoHandler(wr http.ResponseWriter, r *http.Request) {
|
||||||
...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
func showEmailHandler(wr http.ResponseWriter, r *http.Request) {
|
func showEmailHandler(wr http.ResponseWriter, r *http.Request) {
|
||||||
...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
func showFriendsHandler(wr http.ResponseWriter, r *http.Request) {
|
func showFriendsHandler(wr http.ResponseWriter, r *http.Request) {
|
||||||
@ -70,7 +70,7 @@ func main() {
|
|||||||
http.HandleFunc("/info/show", showInfoHandler)
|
http.HandleFunc("/info/show", showInfoHandler)
|
||||||
http.HandleFunc("/email/show", showEmailHandler)
|
http.HandleFunc("/email/show", showEmailHandler)
|
||||||
http.HandleFunc("/friends/show", showFriendsHandler)
|
http.HandleFunc("/friends/show", showFriendsHandler)
|
||||||
...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -119,7 +119,10 @@ var req = RegisterReq {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := validate(req)
|
err := validate(req)
|
||||||
fmt.Println(err) // Key: 'RegisterReq.PasswordRepeat' Error:Field validation for 'PasswordRepeat' failed on the 'eqfield' tag
|
fmt.Println(err)
|
||||||
|
|
||||||
|
// Key: 'RegisterReq.PasswordRepeat' Error:Field validation for
|
||||||
|
// 'PasswordRepeat' failed on the 'eqfield' tag
|
||||||
```
|
```
|
||||||
|
|
||||||
如果觉得这个 validator 提供的错误信息不够人性化,例如要把错误信息返回给用户,那就不应该直接显示英文了。可以针对每种 tag 进行错误信息定制,读者可以自行探索。
|
如果觉得这个 validator 提供的错误信息不够人性化,例如要把错误信息返回给用户,那就不应该直接显示英文了。可以针对每种 tag 进行错误信息定制,读者可以自行探索。
|
||||||
@ -166,7 +169,9 @@ type T struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func validateEmail(input string) bool {
|
func validateEmail(input string) bool {
|
||||||
if pass, _ := regexp.MatchString(`^([\w\.\_]{2,10})@(\w{1,}).([a-z]{2,4})$`, input); pass {
|
if pass, _ := regexp.MatchString(
|
||||||
|
`^([\w\.\_]{2,10})@(\w{1,}).([a-z]{2,4})$`, input,
|
||||||
|
); pass {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@ -188,7 +193,9 @@ func validate(v interface{}) (bool, string) {
|
|||||||
tagValStr := strings.Split(tagContent, "=")
|
tagValStr := strings.Split(tagContent, "=")
|
||||||
tagVal, _ := strconv.ParseInt(tagValStr[1], 10, 64)
|
tagVal, _ := strconv.ParseInt(tagValStr[1], 10, 64)
|
||||||
if val != tagVal {
|
if val != tagVal {
|
||||||
errmsg = "validate int failed, tag is: "+ strconv.FormatInt(tagVal, 10)
|
errmsg = "validate int failed, tag is: "+ strconv.FormatInt(
|
||||||
|
tagVal, 10,
|
||||||
|
)
|
||||||
validateResult = false
|
validateResult = false
|
||||||
}
|
}
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
|
@ -135,7 +135,9 @@ func NewBucketWithRate(rate float64, capacity int64) *Bucket
|
|||||||
```go
|
```go
|
||||||
func (tb *Bucket) Take(count int64) time.Duration {}
|
func (tb *Bucket) Take(count int64) time.Duration {}
|
||||||
func (tb *Bucket) TakeAvailable(count int64) int64 {}
|
func (tb *Bucket) TakeAvailable(count int64) int64 {}
|
||||||
func (tb *Bucket) TakeMaxDuration(count int64, maxWait time.Duration) (time.Duration, bool) {}
|
func (tb *Bucket) TakeMaxDuration(count int64, maxWait time.Duration) (
|
||||||
|
time.Duration, bool,
|
||||||
|
) {}
|
||||||
func (tb *Bucket) Wait(count int64) {}
|
func (tb *Bucket) Wait(count int64) {}
|
||||||
func (tb *Bucket) WaitMaxDuration(count int64, maxWait time.Duration) bool {}
|
func (tb *Bucket) WaitMaxDuration(count int64, maxWait time.Duration) bool {}
|
||||||
```
|
```
|
||||||
|
@ -29,7 +29,10 @@
|
|||||||
这样我们 controller 中的入口函数就变成了下面这样:
|
这样我们 controller 中的入口函数就变成了下面这样:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func CreateOrder(ctx context.Context, req *CreateOrderStruct) (*CreateOrderRespStruct, error) {
|
func CreateOrder(ctx context.Context, req *CreateOrderStruct) (
|
||||||
|
*CreateOrderRespStruct, error,
|
||||||
|
) {
|
||||||
|
// ...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -155,7 +155,6 @@ func isPassed(cityID int) bool {
|
|||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
按白名单、按业务线、按 UA、按分发渠道发布,本质上和按城市发布是一样的,这里就不再赘述了。
|
按白名单、按业务线、按 UA、按分发渠道发布,本质上和按城市发布是一样的,这里就不再赘述了。
|
||||||
@ -193,9 +192,12 @@ hash.go:
|
|||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "crypto/md5"
|
import (
|
||||||
import "crypto/sha1"
|
"crypto/md5"
|
||||||
import "github.com/spaolacci/murmur3"
|
"crypto/sha1"
|
||||||
|
|
||||||
|
"github.com/spaolacci/murmur3"
|
||||||
|
)
|
||||||
|
|
||||||
var str = "hello world"
|
var str = "hello world"
|
||||||
|
|
||||||
@ -214,7 +216,6 @@ func murmur32() uint32 {
|
|||||||
func murmur64() uint64 {
|
func murmur64() uint64 {
|
||||||
return murmur3.Sum64([]byte(str))
|
return murmur3.Sum64([]byte(str))
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
hash_test.go
|
hash_test.go
|
||||||
@ -296,7 +297,8 @@ func murmur64(p string) uint64 {
|
|||||||
```
|
```
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
map[7:999475 5:1000359 1:999945 6:1000200 3:1000193 9:1000765 2:1000044 4:1000343 8:1000823 0:997853]
|
map[7:999475 5:1000359 1:999945 6:1000200 3:1000193 9:1000765 2:1000044 \
|
||||||
|
4:1000343 8:1000823 0:997853]
|
||||||
```
|
```
|
||||||
|
|
||||||
偏差基本都在 1/100 以内,是可以接受的。
|
偏差基本都在 1/100 以内,是可以接受的。
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
Twitter 的 snowflake 算法是这种场景下的一个典型解法。先来看看 snowflake 是怎么一回事:
|
Twitter 的 snowflake 算法是这种场景下的一个典型解法。先来看看 snowflake 是怎么一回事:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
datacenter_id sequence_id
|
datacenter_id sequence_id
|
||||||
unused
|
unused
|
||||||
│ │
|
│ │
|
||||||
@ -28,7 +27,6 @@ Twitter 的 snowflake 算法是这种场景下的一个典型解法。先来看
|
|||||||
│ │
|
│ │
|
||||||
|
|
||||||
time in milliseconds worker_id
|
time in milliseconds worker_id
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
首先确定我们的数值是 64 位,int64 类型,被划分为四部分,不含开头的第一个 bit,因为这个 bit 是符号位。用 41 位来表示收到请求时的时间戳,单位为毫秒,然后五位来表示数据中心的 id,然后再五位来表示机器的实例 id,最后是 12 位的循环自增 id(到达 1111 1111 1111 后会归 0)。
|
首先确定我们的数值是 64 位,int64 类型,被划分为四部分,不含开头的第一个 bit,因为这个 bit 是符号位。用 41 位来表示收到请求时的时间戳,单位为毫秒,然后五位来表示数据中心的 id,然后再五位来表示机器的实例 id,最后是 12 位的循环自增 id(到达 1111 1111 1111 后会归 0)。
|
||||||
@ -98,10 +96,14 @@ func main() {
|
|||||||
for i := 0; i < 3; i++ {
|
for i := 0; i < 3; i++ {
|
||||||
id := n.Generate()
|
id := n.Generate()
|
||||||
fmt.Println("id", id)
|
fmt.Println("id", id)
|
||||||
fmt.Println("node: ", id.Node(), "step: ", id.Step(), "time: ", id.Time(), "\n")
|
fmt.Println(
|
||||||
|
"node: ", id.Node(),
|
||||||
|
"step: ", id.Step(),
|
||||||
|
"time: ", id.Time(),
|
||||||
|
"\n",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
当然,这个库也给我们留好了定制的后路:
|
当然,这个库也给我们留好了定制的后路:
|
||||||
@ -221,5 +223,4 @@ func main() {
|
|||||||
|
|
||||||
fmt.Println(id)
|
fmt.Println(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -201,7 +201,6 @@ func main() {
|
|||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
看看运行结果:
|
看看运行结果:
|
||||||
@ -297,7 +296,6 @@ func main() {
|
|||||||
log.Printf("etcdsync.Unlock OK")
|
log.Printf("etcdsync.Unlock OK")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
etcd 中没有像 ZooKeeper 那样的 sequence 节点。所以其锁实现和基于 ZooKeeper 实现的有所不同。在上述示例代码中使用的 etcdsync 的 Lock 流程是:
|
etcd 中没有像 ZooKeeper 那样的 sequence 节点。所以其锁实现和基于 ZooKeeper 实现的有所不同。在上述示例代码中使用的 etcdsync 的 Lock 流程是:
|
||||||
@ -351,7 +349,9 @@ func newPools(servers []string) []redsync.Pool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
pools := newPools([]string{"127.0.0.1:6379", "127.0.0.1:6378", "127.0.0.1:6377"})
|
pools := newPools([]string{
|
||||||
|
"127.0.0.1:6379", "127.0.0.1:6378", "127.0.0.1:6377",
|
||||||
|
})
|
||||||
rs := redsync.New(pools)
|
rs := redsync.New(pools)
|
||||||
m := rs.NewMutex("/lock")
|
m := rs.NewMutex("/lock")
|
||||||
|
|
||||||
@ -362,7 +362,6 @@ func main() {
|
|||||||
fmt.Println("lock success")
|
fmt.Println("lock success")
|
||||||
unlockRes := m.Unlock()
|
unlockRes := m.Unlock()
|
||||||
fmt.Println("unlock result: ", unlockRes)
|
fmt.Println("unlock result: ", unlockRes)
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -57,15 +57,18 @@ elasticsearch 是开源分布式搜索引擎的霸主,其依赖于 Lucene 实
|
|||||||
|
|
||||||
```go
|
```go
|
||||||
func equal() {
|
func equal() {
|
||||||
if postEntry.docID of '天气' == postEntry.docID of '气很' && postEntry.offset + 1 of '天气' == postEntry.offset of '气很' {
|
if postEntry.docID of '天气' == postEntry.docID of '气很' &&
|
||||||
|
postEntry.offset + 1 of '天气' == postEntry.offset of '气很' {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if postEntry.docID of '气很' == postEntry.docID of '很好' && postEntry.offset + 1 of '气很' == postEntry.offset of '很好' {
|
if postEntry.docID of '气很' == postEntry.docID of '很好' &&
|
||||||
|
postEntry.offset + 1 of '气很' == postEntry.offset of '很好' {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if postEntry.docID of '天气' == postEntry.docID of '很好' && postEntry.offset + 2 of '天气' == postEntry.offset of '很好' {
|
if postEntry.docID of '天气' == postEntry.docID of '很好' &&
|
||||||
|
postEntry.offset + 2 of '天气' == postEntry.offset of '很好' {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,7 +238,6 @@ func insertDocument(db string, table string, obj map[string]interface{}) {
|
|||||||
// insert success
|
// insert success
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
获取:
|
获取:
|
||||||
@ -246,8 +248,10 @@ func query(indexName string, typeName string) (*elastic.SearchResult, error) {
|
|||||||
q := elastic.NewBoolQuery().Must(elastic.NewMatchPhraseQuery("id", 1),
|
q := elastic.NewBoolQuery().Must(elastic.NewMatchPhraseQuery("id", 1),
|
||||||
elastic.NewBoolQuery().Must(elastic.NewMatchPhraseQuery("male", "m")))
|
elastic.NewBoolQuery().Must(elastic.NewMatchPhraseQuery("male", "m")))
|
||||||
|
|
||||||
q = q.Should(elastic.NewMatchPhraseQuery("name", "alex"),
|
q = q.Should(
|
||||||
elastic.NewMatchPhraseQuery("name", "xargin"))
|
elastic.NewMatchPhraseQuery("name", "alex"),
|
||||||
|
elastic.NewMatchPhraseQuery("name", "xargin"),
|
||||||
|
)
|
||||||
|
|
||||||
searchService := esClient.Search(indexName).Type(typeName)
|
searchService := esClient.Search(indexName).Type(typeName)
|
||||||
res, err := searchService.Query(q).Do()
|
res, err := searchService.Query(q).Do()
|
||||||
@ -263,7 +267,9 @@ func query(indexName string, typeName string) (*elastic.SearchResult, error) {
|
|||||||
删除:
|
删除:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func deleteDocument(indexName string, typeName string, obj map[string]interface{}) {
|
func deleteDocument(
|
||||||
|
indexName string, typeName string, obj map[string]interface{},
|
||||||
|
) {
|
||||||
id := obj["id"]
|
id := obj["id"]
|
||||||
|
|
||||||
res, err := esClient.Delete().Index(indexName).Type(typeName).Id(id).Do()
|
res, err := esClient.Delete().Index(indexName).Type(typeName).Id(id).Do()
|
||||||
@ -284,7 +290,9 @@ func deleteDocument(indexName string, typeName string, obj map[string]interface{
|
|||||||
比如我们有一段 bool 表达式,user_id = 1 and (product_id = 1 and (star_num = 4 or star_num = 5) and banned = 1),写成 SQL 是如下形式:
|
比如我们有一段 bool 表达式,user_id = 1 and (product_id = 1 and (star_num = 4 or star_num = 5) and banned = 1),写成 SQL 是如下形式:
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
select * from xxx where user_id = 1 and (product_id = 1 and (star_num = 4 or star_num = 5) and banned = 1)
|
select * from xxx where user_id = 1 and (
|
||||||
|
product_id = 1 and (star_num = 4 or star_num = 5) and banned = 1
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
写成 es 的 DSL 是如下形式:
|
写成 es 的 DSL 是如下形式:
|
||||||
@ -399,7 +407,9 @@ select * from wms_orders where update_time >= date_sub(now(), interval 10 minute
|
|||||||
当然,考虑到边界情况,我们可以让这个时间段的数据与前一次的有一些重叠:
|
当然,考虑到边界情况,我们可以让这个时间段的数据与前一次的有一些重叠:
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
select * from wms_orders where update_time >= date_sub(now(), interval 11 minute);
|
select * from wms_orders where update_time >= date_sub(
|
||||||
|
now(), interval 11 minute
|
||||||
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
取最近 11 分钟有变动的数据覆盖更新到 es 中。这种方案的缺点显而易见,我们必须要求业务数据严格遵守一定的规范。比如这里的,必须要有 update_time 字段,并且每次创建和更新都要保证该字段有正确的时间值。否则我们的同步逻辑就会丢失数据。
|
取最近 11 分钟有变动的数据覆盖更新到 es 中。这种方案的缺点显而易见,我们必须要求业务数据严格遵守一定的规范。比如这里的,必须要有 update_time 字段,并且每次创建和更新都要保证该字段有正确的时间值。否则我们的同步逻辑就会丢失数据。
|
||||||
|
@ -63,7 +63,6 @@ func request(params map[string]interface{}) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
我们循环一遍 slice,两两交换,这个和我们平常打牌时常用的洗牌方法类似。看起来没有什么问题。
|
我们循环一遍 slice,两两交换,这个和我们平常打牌时常用的洗牌方法类似。看起来没有什么问题。
|
||||||
@ -167,7 +166,6 @@ func main() {
|
|||||||
|
|
||||||
fmt.Println(cnt1, "\n", cnt2)
|
fmt.Println(cnt1, "\n", cnt2)
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
输出:
|
输出:
|
||||||
|
@ -65,7 +65,6 @@ func main() {
|
|||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 分布式爬虫
|
## 分布式爬虫
|
||||||
@ -316,7 +315,6 @@ func startConsumer() {
|
|||||||
func main() {
|
func main() {
|
||||||
startConsumer()
|
startConsumer()
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
从代码层面上来讲,这里的生产者和消费者其实本质上差不多。如果日后我们要灵活地支持增加、减少各种网站的爬取的话,应该思考如何将这些爬虫的策略、参数尽量地配置化。
|
从代码层面上来讲,这里的生产者和消费者其实本质上差不多。如果日后我们要灵活地支持增加、减少各种网站的爬取的话,应该思考如何将这些爬虫的策略、参数尽量地配置化。
|
||||||
|
Loading…
x
Reference in New Issue
Block a user