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
|
||||
|
||||
func helloHandler(wr http.ResponseWriter, r *http.Request) {
|
||||
...
|
||||
// ...
|
||||
}
|
||||
|
||||
func showInfoHandler(wr http.ResponseWriter, r *http.Request) {
|
||||
...
|
||||
// ...
|
||||
}
|
||||
|
||||
func showEmailHandler(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("/email/show", showEmailHandler)
|
||||
http.HandleFunc("/friends/show", showFriendsHandler)
|
||||
...
|
||||
// ...
|
||||
}
|
||||
|
||||
```
|
||||
|
@ -119,7 +119,10 @@ var req = RegisterReq {
|
||||
}
|
||||
|
||||
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 进行错误信息定制,读者可以自行探索。
|
||||
@ -166,7 +169,9 @@ type T struct {
|
||||
}
|
||||
|
||||
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 false
|
||||
@ -188,7 +193,9 @@ func validate(v interface{}) (bool, string) {
|
||||
tagValStr := strings.Split(tagContent, "=")
|
||||
tagVal, _ := strconv.ParseInt(tagValStr[1], 10, 64)
|
||||
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
|
||||
}
|
||||
case reflect.String:
|
||||
|
@ -135,7 +135,9 @@ func NewBucketWithRate(rate float64, capacity int64) *Bucket
|
||||
```go
|
||||
func (tb *Bucket) Take(count int64) time.Duration {}
|
||||
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) WaitMaxDuration(count int64, maxWait time.Duration) bool {}
|
||||
```
|
||||
|
@ -29,7 +29,10 @@
|
||||
这样我们 controller 中的入口函数就变成了下面这样:
|
||||
|
||||
```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
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
按白名单、按业务线、按 UA、按分发渠道发布,本质上和按城市发布是一样的,这里就不再赘述了。
|
||||
@ -193,9 +192,12 @@ hash.go:
|
||||
```go
|
||||
package main
|
||||
|
||||
import "crypto/md5"
|
||||
import "crypto/sha1"
|
||||
import "github.com/spaolacci/murmur3"
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
|
||||
"github.com/spaolacci/murmur3"
|
||||
)
|
||||
|
||||
var str = "hello world"
|
||||
|
||||
@ -214,7 +216,6 @@ func murmur32() uint32 {
|
||||
func murmur64() uint64 {
|
||||
return murmur3.Sum64([]byte(str))
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
hash_test.go
|
||||
@ -296,7 +297,8 @@ func murmur64(p string) uint64 {
|
||||
```
|
||||
|
||||
```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 以内,是可以接受的。
|
||||
|
@ -7,7 +7,6 @@
|
||||
Twitter 的 snowflake 算法是这种场景下的一个典型解法。先来看看 snowflake 是怎么一回事:
|
||||
|
||||
```
|
||||
|
||||
datacenter_id sequence_id
|
||||
unused
|
||||
│ │
|
||||
@ -28,7 +27,6 @@ Twitter 的 snowflake 算法是这种场景下的一个典型解法。先来看
|
||||
│ │
|
||||
|
||||
time in milliseconds worker_id
|
||||
|
||||
```
|
||||
|
||||
首先确定我们的数值是 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++ {
|
||||
id := n.Generate()
|
||||
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)
|
||||
}
|
||||
|
||||
```
|
||||
|
@ -201,7 +201,6 @@ func main() {
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
看看运行结果:
|
||||
@ -297,7 +296,6 @@ func main() {
|
||||
log.Printf("etcdsync.Unlock OK")
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
etcd 中没有像 ZooKeeper 那样的 sequence 节点。所以其锁实现和基于 ZooKeeper 实现的有所不同。在上述示例代码中使用的 etcdsync 的 Lock 流程是:
|
||||
@ -351,7 +349,9 @@ func newPools(servers []string) []redsync.Pool {
|
||||
}
|
||||
|
||||
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)
|
||||
m := rs.NewMutex("/lock")
|
||||
|
||||
@ -362,7 +362,6 @@ func main() {
|
||||
fmt.Println("lock success")
|
||||
unlockRes := m.Unlock()
|
||||
fmt.Println("unlock result: ", unlockRes)
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -57,15 +57,18 @@ elasticsearch 是开源分布式搜索引擎的霸主,其依赖于 Lucene 实
|
||||
|
||||
```go
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@ -235,7 +238,6 @@ func insertDocument(db string, table string, obj map[string]interface{}) {
|
||||
// insert success
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
获取:
|
||||
@ -246,8 +248,10 @@ func query(indexName string, typeName string) (*elastic.SearchResult, error) {
|
||||
q := elastic.NewBoolQuery().Must(elastic.NewMatchPhraseQuery("id", 1),
|
||||
elastic.NewBoolQuery().Must(elastic.NewMatchPhraseQuery("male", "m")))
|
||||
|
||||
q = q.Should(elastic.NewMatchPhraseQuery("name", "alex"),
|
||||
elastic.NewMatchPhraseQuery("name", "xargin"))
|
||||
q = q.Should(
|
||||
elastic.NewMatchPhraseQuery("name", "alex"),
|
||||
elastic.NewMatchPhraseQuery("name", "xargin"),
|
||||
)
|
||||
|
||||
searchService := esClient.Search(indexName).Type(typeName)
|
||||
res, err := searchService.Query(q).Do()
|
||||
@ -263,7 +267,9 @@ func query(indexName string, typeName string) (*elastic.SearchResult, error) {
|
||||
删除:
|
||||
|
||||
```go
|
||||
func deleteDocument(indexName string, typeName string, obj map[string]interface{}) {
|
||||
func deleteDocument(
|
||||
indexName string, typeName string, obj map[string]interface{},
|
||||
) {
|
||||
id := obj["id"]
|
||||
|
||||
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 是如下形式:
|
||||
|
||||
```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 是如下形式:
|
||||
@ -399,7 +407,9 @@ select * from wms_orders where update_time >= date_sub(now(), interval 10 minute
|
||||
当然,考虑到边界情况,我们可以让这个时间段的数据与前一次的有一些重叠:
|
||||
|
||||
```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 字段,并且每次创建和更新都要保证该字段有正确的时间值。否则我们的同步逻辑就会丢失数据。
|
||||
|
@ -63,7 +63,6 @@ func request(params map[string]interface{}) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
我们循环一遍 slice,两两交换,这个和我们平常打牌时常用的洗牌方法类似。看起来没有什么问题。
|
||||
@ -167,7 +166,6 @@ func main() {
|
||||
|
||||
fmt.Println(cnt1, "\n", cnt2)
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
输出:
|
||||
|
@ -65,7 +65,6 @@ func main() {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## 分布式爬虫
|
||||
@ -316,7 +315,6 @@ func startConsumer() {
|
||||
func main() {
|
||||
startConsumer()
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
从代码层面上来讲,这里的生产者和消费者其实本质上差不多。如果日后我们要灵活地支持增加、减少各种网站的爬取的话,应该思考如何将这些爬虫的策略、参数尽量地配置化。
|
||||
|
Loading…
x
Reference in New Issue
Block a user