锘??xml version="1.0" encoding="utf-8" standalone="yes"?>
(閲戝簡鐨勪笓鏍?2021.2)
濡備笅鍒涘緩 StatefulSet 鍜?Headless Service: test.yaml
```
apiVersion: v1
kind: Service
metadata:
name: headless-svc
labels:
app: headless-svc
spec:
ports:
- port: 80
name: aaaa
- port: 20080
name: bbbb
selector:
app: headless-pod
clusterIP: None
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: statefulset-test
spec:
serviceName: headless-svc
replicas: 3
selector:
matchLabels:
app: headless-pod
template:
metadata:
labels:
app: headless-pod
spec:
containers:
- name: myhttpd
image: httpd
ports:
- containerPort: 80
- containerPort: 20080
```
閮ㄧ講錛?br />```
kubectl apply -f test.yaml
```
鐒跺悗寮涓涓?shell:
```
kubectl run mygolang -it --image golang -- bash
If you don't see a command prompt, try pressing enter.
root@mygolang:/go#
```
鍏堣涓?vim:
```
apt install vim
```
鐒跺悗鍐欎釜 golang 嫻嬭瘯紼嬪簭錛屽悜 DNS 鏌ヨ bbbb 鏈嶅姟鐨勫湴鍧涓庣鍙o細
```
root@mygolang:/jinqing# cat main.go
package main
import (
"fmt"
"github.com/davecgh/go-spew/spew"
"net"
)
func main() {
cname, addresses, err := net.LookupSRV("bbbb", "tcp", "headless-svc.default.svc.cluster.local")
if err != nil {
fmt.Printf("failed: %s\n", err)
}
fmt.Printf("cname: %s\n", cname)
spew.Dump(addresses)
}
root@mygolang:/jinqing#
```
榪愯緇撴灉涓猴細
```
cname: _bbbb._tcp.headless-svc.default.svc.cluster.local.
([]*net.SRV) (len=3 cap=4) {
(*net.SRV)(0xc00000e220)({
Target: (string) (len=58) "statefulset-test-0.headless-svc.default.svc.cluster.local.",
Port: (uint16) 20080,
Priority: (uint16) 0,
Weight: (uint16) 33
}),
(*net.SRV)(0xc00000e1e0)({
Target: (string) (len=58) "statefulset-test-2.headless-svc.default.svc.cluster.local.",
Port: (uint16) 20080,
Priority: (uint16) 0,
Weight: (uint16) 33
}),
(*net.SRV)(0xc00000e200)({
Target: (string) (len=58) "statefulset-test-1.headless-svc.default.svc.cluster.local.",
Port: (uint16) 20080,
Priority: (uint16) 0,
Weight: (uint16) 33
})
}
```
澶氭榪愯鍙戠幇緇撴灉欏規搴忓浐瀹氾紝騫舵病鏈夋寜鏉冮噸闅忔満銆?br />
Service yaml 瀹氫箟涓紝蹇呴』涓烘瘡涓鍙e懡鍚嶏紝涓嶇劧娌℃硶鏌ヨ銆?br />
```
cname, addresses, err := net.LookupSRV("bbbb", "tcp", "headless-svc.default.svc.cluster.local")
```
鍙埄鐢ㄩ粯璁ゅ煙鍚嶅悗緙綆鍐欎負
```
cname, addresses, err := net.LookupSRV("bbbb", "tcp", "headless-svc")
```
杈撳嚭鐩稿悓銆?br />
]]>
(閲戝簡鐨勪笓鏍?2020.9)
鍦ㄥ帇嫻嬩腑鍙戠幇鎬繪湁鍑犱釜璇鋒眰瓚呮椂錛岃秴鏃舵椂闀胯澶т篃浼氭湁錛岃屾垚鍔熺殑璇鋒眰寤舵椂榪滃皬浜庤秴鏃舵椂闂淬?br />鏌ラ敊鐨勭涓鏂瑰悜鏄煡緗戠粶搴撲腑鏈夋秷鎭涪澶便?br />璺熻釜鎵鏈夋秷鎭紝鍙戠幇瓚呮椂鐨勬秷鎭簲璇ユ槸姝e父澶勭悊騫惰繑鍥炰簡銆?br />浜庢槸鏌ユ帴鏀跺簲絳旀秷鎭悗鐨勫鐞嗭紝鏈緇堟壘鍒頒唬鐮侊細
```go
func (c *Client) onRpcRet(cbIndex uint32, ...) {
ii, ok := c.callbacks.Load(cbIndex)
if !ok {
// logger.Errorf("onRpcRet can not find cbIndex %d", cbIndex) // 鍙兘鏄秴鏃跺凡鍒?br /> return
}
```
鎵撳紑鏃ュ織錛屽彂鐜拌秴鏃剁殑璇鋒眰瀵瑰簲鏈夎鏉¢敊璇棩蹇椼?br />姝ゅ鍥炶皟涓嶅瓨鍦ㄧ殑鎯呭喌錛屾甯告槸鍏堣秴鏃跺垹闄ゅ洖璋冿紝鐒跺悗鍐嶆敹鍒板簲絳斻?br />鐜板湪鏄厛鏀跺埌浜嗗簲絳旓紝鍙戠幇鎵句笉鍒板洖璋冿紝鐒跺悗榪囦簡涓孌墊椂闂翠細琚垽涓鴻秴鏃舵棤鍝嶅簲銆?br />
灝嗕笅闈唬鐮佹崲涓搴忓氨濂戒簡錛?br />```go
if err := c.Session.Send(msg); err != nil {
...
return
}
c.callbacks.Store(cbIndex, ...)
```
鏀逛負
```go
// 蹇呴』鍏堣鍥炶皟錛岀劧鍚庡彂閫侊紝鍥犱負搴旂瓟鍙兘浼氬緢蹇?br /> c.callbacks.Store(cbIndex, ...)
if err := c.Session.Send(msg); err...
```
鍘嬫祴鏃跺洜涓哄姞鍘嬫満CPU鏄弧璐熻澆榪愯漿錛屾墍浠?Send() 鍜?Store() 涔嬮棿鍙兘浼氶棿闅旀暟姣錛?br />瓚沖 rpc 璇鋒眰澶勭悊瀹屾垚騫惰繑鍥烇紝鑰屽簲絳旇繑鍥炴椂鍥炶皟榪樻病璁劇疆銆?br />
鍏?Send() 鍚?Store() 鍐欎唬鐮佷細紼嶅井綆鍗曠偣錛屽洜涓?Send() 澶辮觸鍚庡彲浠ョ洿鎺ヨ繑鍥炪?br />鍏?Store() 鍚?Send() 鏃訛紝Send() 澶辮觸鍒欓渶瑕佺浉搴?Delete().
]]>
(閲戝簡鐨勪笓鏍?2020.9)
golang 鐨?math 鍖呭凡緇忓畾涔変簡浠ヤ笅甯擱噺錛?br />
Constants
```
const (
E = 2.71828182845904523536028747135266249775724709369995957496696763 // https://oeis.org/A001113
Pi = 3.14159265358979323846264338327950288419716939937510582097494459 // https://oeis.org/A000796
Phi = 1.61803398874989484820458683436563811772030917980576286213544862 // https://oeis.org/A001622
Sqrt2 = 1.41421356237309504880168872420969807856967187537694807317667974 // https://oeis.org/A002193
SqrtE = 1.64872127070012814684865078781416357165377610071014801157507931 // https://oeis.org/A019774
SqrtPi = 1.77245385090551602729816748334114518279754945612238712821380779 // https://oeis.org/A002161
SqrtPhi = 1.27201964951406896425242246173749149171560804184009624861664038 // https://oeis.org/A139339
Ln2 = 0.693147180559945309417232121458176568075500134360255254120680009 // https://oeis.org/A002162
Log2E = 1 / Ln2
Ln10 = 2.30258509299404568401799145468436420760110148862877297603332790 // https://oeis.org/A002392
Log10E = 1 / Ln10
)
```
Mathematical constants.
```
const (
MaxFloat32 = 3.40282346638528859811704183484516925440e+38 // 2**127 * (2**24 - 1) / 2**23
SmallestNonzeroFloat32 = 1.401298464324817070923729583289916131280e-45 // 1 / 2**(127 - 1 + 23)
MaxFloat64 = 1.797693134862315708145274237317043567981e+308 // 2**1023 * (2**53 - 1) / 2**52
SmallestNonzeroFloat64 = 4.940656458412465441765687928682213723651e-324 // 1 / 2**(1023 - 1 + 52)
)
```
Floating-point limit values. Max is the largest finite value representable by the type. SmallestNonzero is the smallest positive, non-zero value representable by the type.
```
const (
MaxInt8 = 1<<7 - 1
MinInt8 = -1 << 7
MaxInt16 = 1<<15 - 1
MinInt16 = -1 << 15
MaxInt32 = 1<<31 - 1
MinInt32 = -1 << 31
MaxInt64 = 1<<63 - 1
MinInt64 = -1 << 63
MaxUint8 = 1<<8 - 1
MaxUint16 = 1<<16 - 1
MaxUint32 = 1<<32 - 1
MaxUint64 = 1<<64 - 1
)
```
Integer limit values.
]]>
(閲戝簡鐨勪笓鏍?2020.7)
golang涓彲浠ュ皢鍙傛暟綾誨瀷璁句負 interface{}, 榪欐牱灝卞彲浠ヤ紶鍏ヤ換鎰忕被鍨嬬殑鍙傛暟錛?br />鍜?C++ 涓?void* 鐨勪綔鐢ㄧ浉浼箋?br />浣嗘槸榪欑涓囪兘綾誨瀷搴旇灝介噺灝戠敤錛屽敖閲忎嬌鐢ㄥ叿浣撶殑綾誨瀷錛屾垨鑰呬嬌鐢ㄤ竴涓叿浣撶殑鎺ュ彛綾誨瀷銆?br />涓昏鐨勫師鍥犳槸, 璁╃紪璇戞湡鐨勭被鍨嬫鏌ユ尅浣忕紪鐮侀敊璇紝鍑忓皯榪愯鏈熺殑閿欒銆?br />
渚嬪錛実o-mongo-driver 鏈変釜鍒涘緩绱㈠紩鐨勫弬鏁幫細
```
type IndexModel struct {
// A document describing which keys should be used for the index. It cannot be nil.
// This must be an order-preserving type such as bson.D. Map types such as bson.M are not valid.
Keys interface{}
...
}
```
鍏朵腑 Keys 鍙互鏄換鎰忕被鍨嬶紝濡?1234, "abcd", 褰撶劧涓嶇鍚堢儲寮曡姹傜殑綾誨瀷浼氳繑鍥炲け璐ャ?br />浣嗘槸 bson.M 綾誨瀷錛屼細鍒涘緩绱㈠紩鎴愬姛錛屼絾鏄儲寮曠殑嬈″簭浼氭湁閿欒銆?br />娉ㄩ噴涓凡鎸囧嚭錛屼笉瑕佺敤 bson.M, 搴旇浣跨敤 bson.D.
姝g‘鐨?Keys 濡備笅錛岃〃紺哄鍚堢儲寮?(field1, field2)錛?琛ㄧず姝e簭錛?1鍒欏弽搴忥細
```
indexModel.Keys := bson.D{{"field1", 1}, {"field2", 1}}
```
濡傛灉浣跨敤 bson.M, 瀹為檯涓婃槸涓?map錛?br />```
indexModel.Keys := bson.M{"field1"錛?, "field2": 1}
```
鍥犱負 map 鎴愬憳鐨勬搴忎笉瀹氾紝鏈鍚庡垱寤虹殑绱㈠紩鍙兘鏄?(field1, field2)錛屼篃鍙兘鏄?(field2, field1)銆?br />
姝ゅ綾誨瀷鍏佽 interface{} 鐨勬兂娉曟槸錛屽厑璁鎬換鎰忕被鍨嬶紝浼?bson 緙栫爜鍚庝紶緇?mongo 鏈嶅姟鍣紝騫朵笉浼氳繘琛岀被鍨嬫鏌ャ?br />榪欑鐏墊椿鎬ч潪甯稿鏄撻犳垚閿欒錛屽茍涓斿浣曚嬌鐢ㄤ篃涓嶆槑紜? 浠呴潬娉ㄩ噴浣滅敤寰堝皬銆?br />
]]>
(閲戝簡鐨勪笓鏍?2020.4)
grpc 瀵規瘡涓姹傝繘琛岃礋杞藉潎琛°傝礋杞藉潎琛$殑鏂瑰紡鏈夛細
* 浠g悊妯″紡
* 瀹㈡埛绔疄鐜?br />* 澶栭儴璐熻澆鍧囪
鍙傝冿細gRPC LB https://blog.csdn.net/xiaojia1100/article/details/78842295
gRPC 涓礋杞藉潎琛$殑涓昏鏈哄埗鏄閮ㄨ礋杞藉潎琛°?br />
gRPC 瀹氫箟浜嗗閮ㄨ礋杞藉潎琛℃湇鍔$殑鎺ュ彛錛歨ttps://github.com/grpc/grpc/tree/master/src/proto/grpc/lb/v1
* load_balancer.proto 瀹㈡埛绔悜 lb 鏈嶆煡璇㈠悗绔垪琛?br />* load_reporter.proto lb 鏈嶅悜鍚庣鏈嶆煡璇㈣礋杞?br />
https://github.com/bsm/grpclb 瀹炵幇浜嗕竴涓?grpc 鐨勫閮ㄨ礋杞藉潎琛℃湇銆?br />鍥犱負鍏跺疄鐜版棭浜庤礋杞藉潎琛℃湇鐨勬帴鍙h鑼冿紝鎵浠ユ帴鍙e畾涔変笌 grpc 瑙勮寖涓嶅悓銆?br />瑙?issue#26: https://github.com/bsm/grpclb/issues/26#issuecomment-613873655
grpclb 鐩墠浠呮敮鎸?consul 鏈嶅姟鍙戠幇銆?br />
鏍囧噯鐨?grpclb 瀹炵幇鐩墠濂藉儚鍙湁 https://github.com/joa/jawlb銆?br />jawlb 閫氳繃 Kubernetes API 鏉ュ彂鐜版湇鍔°?br />
浠ヤ笅嫻嬭瘯 grpc 瀹㈡埛绔粠 jawlb 鏈嶆煡璇㈡湇鍔″櫒鍒楄〃錛岀劧鍚庤姹傛湇鍔°?br />棣栧厛鍦ㄦ湰鏈哄紑浜嗗涓?greeter 鏈嶅疄渚嬶紝绔彛涓嶅悓銆?br />鐒跺悗鏇存敼 greeter 瀹㈡埛绔紝涓嶈鐩存帴榪?greeter 鏈嶅湴鍧錛岃屾槸閰嶄竴涓?jawlb 鏈嶅湴鍧銆?br />鍚屾椂鏇存敼 jawlb, 鍒犻櫎鏈嶅姟鍙戠幇錛屾敼涓哄浐瀹氳緭鍑烘湰鏈烘湇鍔″垪琛紝瀹氭椂鍒囨崲銆?br />
greeter 鏄寚 grpc-go 涓殑渚嬪瓙錛歡rpc-go\examples\helloworld\greeter
## greeter 鏈嶆洿鏀?br />
娣誨姞鍙傛暟鎸囧畾鏈嶅姟绔彛銆?br />
```
package main
import (
"fmt"
"log"
"net"
"github.com/spf13/pflag"
"github.com/spf13/viper"
"golang.org/x/net/context"
"google.golang.org/grpc"
pb "google.golang.org/grpc/examples/helloworld/helloworld"
)
// GreeterServer is used to implement helloworld.GreeterServer.
type GreeterServer struct {
}
// SayHello implements helloworld.GreeterServer
func (s *GreeterServer) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
msg := fmt.Sprintf("Hello %s from server-%d", in.Name, viper.GetInt("port"))
return &pb.HelloReply{Message: msg}, nil
}
func main() {
pflag.Int("port", 8000, "server bind port")
pflag.Parse()
viper.BindPFlags(pflag.CommandLine)
port := viper.GetInt("port")
addr := fmt.Sprintf(":%d", port)
lis, err := net.Listen("tcp", addr)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &GreeterServer{})
s.Serve(lis)
}
```
## greeter 瀹㈡埛绔洿鏀?br />```
package main
import (
"context"
"log"
"os"
"time"
"github.com/sirupsen/logrus"
"google.golang.org/grpc"
_ "google.golang.org/grpc/balancer/grpclb"
pb "google.golang.org/grpc/examples/helloworld/helloworld"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/resolver"
"google.golang.org/grpc/resolver/manual"
)
const (
defaultName = "world"
)
func init() {
grpclog.SetLogger(logrus.New())
}
func main() {
rb := manual.NewBuilderWithScheme("whatever")
rb.InitialState(resolver.State{Addresses: []resolver.Address{
{Addr: "127.0.0.1:8888", Type: resolver.GRPCLB},
}})
conn, err := grpc.Dial("whatever:///this-gets-overwritten", grpc.WithInsecure(), grpc.WithBlock(),
grpc.WithResolvers(rb))
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewGreeterClient(conn)
name := defaultName
if len(os.Args) > 1 {
name = os.Args[1]
}
for {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})
cancel()
if err != nil {
log.Fatalf("could not greet: %v", err)
time.Sleep(time.Second)
continue
}
log.Printf("Greeting: %s", r.GetMessage())
time.Sleep(time.Second)
}
}
```
鏈変互涓嬫洿鏀癸細
* import _ "google.golang.org/grpc/balancer/grpclb"
* grpc.Dial("whatever:///this-gets-overwritten", grpc.WithResolvers(rb))
+ 閲囩敤涓涓嚜瀹氫箟瑙f瀽鍣紝鐢ㄦ潵鑾峰彇 jawlb 鍦板潃
+ Scheme("whatever") 鍙互浠繪剰錛岀敤浣滆В鏋愬櫒鍚嶅瓧
+ 鐩爣 this-gets-overwritten 鍙互浠繪剰錛屽洜涓?jawlb 蹇界暐浜嗚鍚嶅瓧
+ 127.0.0.1:8888 鏄?jawlb 鍦板潃
* 鏀逛負姣忕璇鋒眰涓嬈?br />
姝e父鐨?grpclb 鏄湪 DNS 涓緗?SRV 璁板綍錛?br />姝ゅ嫻嬭瘯閬垮厤璁劇疆 DNS, 閲囩敤浜嗕竴涓嚜瀹氫箟瑙f瀽鍣紝
浠g爜涓婂浜嗗嚑琛屻?br />鐢?DNS 璁劇疆鐨勫ソ澶勬槸, 鍙互鐩存帴瑙f瀽涓哄悗绔?IP, 涔熷彲浠ユ坊鍔?grpclb, 浠g爜涓婂鍚岀洿鎺ヨ繛鎺ュ悗绔細
```
conn, err := grpc.Dial("dns:///myservice.domain.com", grpc.WithInsecure())
```
## jawlb 鏇存敼
### main.go
鍒犻櫎鎵鏈夐厤緗紝鏀逛負鍥哄畾鏈満 8888 绔彛鐩戝惉銆?br />
* 鍒犻櫎 `envconfig.MustProcess("JAWLB", &cfg)`
* listen() 鏀逛負
```
func listen() (conn net.Listener, err error) {
conn, err = net.Listen("tcp", ":8888")
return
}
```
### watch.go
```
package main
import (
"context"
"fmt"
"net"
"time"
)
func watchService(ctx context.Context) (_ <-chan ServerList, err error) {
ch := make(chan ServerList)
go func() {
ticker := time.NewTicker(10 * time.Second)
i := 0
for {
select {
case <-ctx.Done():
ticker.Stop()
close(ch)
return
case <-ticker.C:
i += 1
fmt.Printf("i = %d\n", i)
ports := []int32{8010, 8020}
var servers []Server
for _, port := range ports {
servers = append(servers, Server{IP: net.ParseIP("127.0.0.1"), Port: port + int32(i%2)})
}
ch <- servers
} // select
} // for
}()
return ch, nil
}
```
鍒犻櫎鎵鏈夋湇鍔″彂鐜頒唬鐮侊紝鏀逛負姣?0縐掑垏鎹㈢鍙o細8010,8020 <-> 8011,8021
## 榪愯
### jawlb
```
λ jawlb.exe
2020/04/16 15:35:17 waiting for TERM
i = 1
2020/04/16 15:35:27 endpoints:
2020/04/16 15:35:27 127.0.0.1:8011
2020/04/16 15:35:27 127.0.0.1:8021
i = 2
2020/04/16 15:35:37 endpoints:
2020/04/16 15:35:37 127.0.0.1:8010
2020/04/16 15:35:37 127.0.0.1:8020
```
### server
榪愯 4 涓疄渚嬶細
```
server --port 8010
server --port 8020
server --port 8011
server --port 8021
```
### client
```
λ client
INFO[0002] lbBalancer: handle SubConn state change: 0xc00008a590, CONNECTING
INFO[0002] Channel Connectivity change to CONNECTING
INFO[0002] lbBalancer: handle SubConn state change: 0xc00008a5f0, CONNECTING
INFO[0002] Subchannel picks a new address "127.0.0.1:8021" to connect
INFO[0002] Subchannel Connectivity change to READY
INFO[0002] lbBalancer: handle SubConn state change: 0xc00008a590, READY
INFO[0002] Channel Connectivity change to READY
INFO[0002] Subchannel Connectivity change to READY
INFO[0002] lbBalancer: handle SubConn state change: 0xc00008a5f0, READY
2020/04/16 15:37:47 Greeting: Hello world from server-8021
2020/04/16 15:37:48 Greeting: Hello world from server-8011
2020/04/16 15:37:49 Greeting: Hello world from server-8021
2020/04/16 15:37:50 Greeting: Hello world from server-8011
2020/04/16 15:37:51 Greeting: Hello world from server-8021
2020/04/16 15:37:52 Greeting: Hello world from server-8011
2020/04/16 15:37:53 Greeting: Hello world from server-8021
2020/04/16 15:37:54 Greeting: Hello world from server-8011
2020/04/16 15:37:55 Greeting: Hello world from server-8021
2020/04/16 15:37:56 Greeting: Hello world from server-8011
INFO[0012] lbBalancer: processing server list: servers:<ip_address:"\000\000\000\000\000\000\000\000\000\000\377\377\177\000\000\001" port:8020 > servers:<ip_address:"\000\000\000\000\000\000\000\000\000\000\377\377\177\000\000\001" port:8010 >
INFO[0012] lbBalancer: server list entry[0]: ipStr:|127.0.0.1|, port:|8020|, load balancer token:||
INFO[0012] lbBalancer: server list entry[1]: ipStr:|127.0.0.1|, port:|8010|, load balancer token:||
2020/04/16 15:37:57 Greeting: Hello world from server-8020
2020/04/16 15:37:58 Greeting: Hello world from server-8010
2020/04/16 15:37:59 Greeting: Hello world from server-8020
2020/04/16 15:38:00 Greeting: Hello world from server-8010
2020/04/16 15:38:01 Greeting: Hello world from server-8020
2020/04/16 15:38:02 Greeting: Hello world from server-8010
2020/04/16 15:38:03 Greeting: Hello world from server-8020
2020/04/16 15:38:04 Greeting: Hello world from server-8010
2020/04/16 15:38:05 Greeting: Hello world from server-8020
2020/04/16 15:38:06 Greeting: Hello world from server-8010
INFO[0022] lbBalancer: processing server list: servers:<ip_address:"\000\000\000\000\000\000\000\000\000\000\377\377\177\000\000\001" port:8021 > servers:<ip_address:"\000\000\000\000\000\000\000\000\000\000\377\377\177\000\000\001" port:8011 >
INFO[0022] lbBalancer: server list entry[0]: ipStr:|127.0.0.1|, port:|8021|, load balancer token:||
INFO[0022] lbBalancer: server list entry[1]: ipStr:|127.0.0.1|, port:|8011|, load balancer token:||
2020/04/16 15:38:07 Greeting: Hello world from server-8011
2020/04/16 15:38:08 Greeting: Hello world from server-8021
2020/04/16 15:38:09 Greeting: Hello world from server-8011
```
## 緇撹
瀹㈡埛绔簲鐢ㄤ竴涓嚜瀹氫箟 resolver 瑙f瀽 "whatever:///this-gets-overwritten"錛?br />鑾峰彇鍒?`{Addr: "127.0.0.1:8888", Type: resolver.GRPCLB}`,
鐭ラ亾榪欐槸涓涓?grpclb錛屼簬鏄寜 load_balancer.proto 鐨勫畾涔夋煡璇?jawlb 鏉ヨ幏鍙栧悗绔湴鍧鍒楄〃銆?br />
jawlb 姣?10s 鏇存柊涓嬈℃湇鍔″櫒鍒楄〃錛屾瘡嬈¤緭鍑哄涓湴鍧銆傚鎴風鍦ㄥ涓湴鍧闂磋疆鎹㈣姹傘?br />
## 鍏朵粬嫻嬭瘯
* 涓嶅紑 jawlb錛屽鎴風灝嗘棤娉曟垚鍔熻姹傦紝鐩村埌 jawlb 寮鍚墠鎴愬姛
* 涓斿叧闂?jawlb, 璇鋒眰浠嶄細鎴愬姛錛屼絾鏄繚鎸佷負鏈鍚庣殑鏈嶅姟鍣ㄥ垪琛?br /> + 鍚屾椂浼氫笉鏂皾璇曢噸榪?jawlb, 浣嗘槸閲嶈繛鎴愬姛鍚庢病鏈夊垏鎹㈡湇鍔★紝搴旇鏄釜閿欒
* Dial() 涓嶅姞 grpc.WithBlock() 鍙傛暟, 鎶ラ敊錛歛ll SubConns are in TransientFailure
```
λ client
INFO[0000] parsed scheme: "whatever"
INFO[0000] ccResolverWrapper: sending update to cc: {[{127.0.0.1:8888 <nil> 1 <nil>}] <nil> <nil>}
INFO[0000] ClientConn switching balancer to "grpclb"
INFO[0000] Channel switches to new LB policy "grpclb"
INFO[0000] lbBalancer: UpdateClientConnState: {ResolverState:{Addresses:[{Addr:127.0.0.1:8888 ServerName: Attributes:<nil> Type:1 Metadata:<nil>}] ServiceConfig:<nil> Attributes:<nil>} BalancerConfig:<nil>}
INFO[0000] parsed scheme: "grpclb-internal"
INFO[0000] ccResolverWrapper: sending update to cc: {[{127.0.0.1:8888 <nil> 0 <nil>}] <nil> <nil>}
INFO[0000] ClientConn switching balancer to "pick_first"
INFO[0000] Channel switches to new LB policy "pick_first"
INFO[0000] Subchannel Connectivity change to CONNECTING
INFO[0000] blockingPicker: the picked transport is not ready, loop back to repick
INFO[0000] pickfirstBalancer: HandleSubConnStateChange: 0xc00003fb10, {CONNECTING <nil>}
INFO[0000] Channel Connectivity change to CONNECTING
INFO[0000] Subchannel picks a new address "127.0.0.1:8888" to connect
INFO[0000] CPU time info is unavailable on non-linux or appengine environment.
INFO[0000] Subchannel Connectivity change to READY
INFO[0000] pickfirstBalancer: HandleSubConnStateChange: 0xc00003fb10, {READY <nil>}
INFO[0000] Channel Connectivity change to READY
INFO[0000] lbBalancer: processing server list: servers:<ip_address:"\000\000\000\000\000\000\000\000\000\000\377\377\177\000\000\001" port:8010 > servers:<ip_address:"\000\000\000\000\000\000\000\000\000\000\377\377\177\000\000\001" port:8020 >
INFO[0000] lbBalancer: server list entry[0]: ipStr:|127.0.0.1|, port:|8010|, load balancer token:||
INFO[0000] lbBalancer: server list entry[1]: ipStr:|127.0.0.1|, port:|8020|, load balancer token:||
INFO[0000] Subchannel Connectivity change to CONNECTING
INFO[0000] Subchannel Connectivity change to CONNECTING
INFO[0000] Channel Connectivity change to TRANSIENT_FAILURE
INFO[0000] lbBalancer: handle SubConn state change: 0xc00008a220, CONNECTING
INFO[0000] Channel Connectivity change to CONNECTING
INFO[0000] lbBalancer: handle SubConn state change: 0xc00008a280, CONNECTING
2020/04/16 16:40:06 could not greet: rpc error: code = Unavailable desc = all SubConns are in TransientFailure
```
]]>
(閲戝簡鐨勪笓鏍?2020.3)
鐢?mgo 鐨?Bulk 鎺ュ彛鎿嶄綔 mongodb 鐨勪唬鐮侊紝鍦?mongodb 4.2 姝e父錛?br />鑰岃繛 mongodb 2.6 鎶ラ敊錛?br />```
error parsing element 0 of field documents :: caused by :: wrong type for '0' field, expected object, found 0: null
```
浠g爜濡備笅錛?br />```
func (u *Util) AddUsers(userIDs []int) error {
docs := make([]interface{}, len(userIDs))
for _, userID := range userIDs {
docs = append(docs, dbdoc.UsersDoc{
UserID: userID,
})
}
bulk := u.c().Bulk()
bulk.Insert(docs...)
_, err := bulk.Run()
return err
}
```
璇曠潃鏀規垚濡備笅璋冪敤灝辨垚鍔燂細
```
func (u *Util) AddUsers(userIDs []int) error {
bulk := u.c().Bulk()
for _, userID := range userIDs {
bulk.Insert(dbdoc.UsersDoc{
UserID: userID,
})
}
_, err := bulk.Run()
return err
}
```
鑰?Insert() 鏃犺鏄涓繕鏄崟涓弬鏁幫紝搴曞眰瀹炵幇鏄竴鏍風殑銆?br />
杈撳叆鍗曚釜濡?[]int{123} 榪樻槸鍑洪敊.
灝?Insert(docs...) 鏀規垚 Insert(docs[0]) 鏃跺彂鐜板厓绱犱負絀恒?br />鏈緇堟壘鍒頒簡閿欒鐨勪唬鐮佽錛?br />```
- docs := make([]interface{}, len(userIDs))
+ docs := make([]interface{}, 0, len(userIDs))
```
楂樼増鏈殑mongodb緇撴灉姝g‘搴旇鏄粬蹇界暐浜嗙┖鐨勬枃妗c?br />
鎰熻 golang make() 鍗沖彲浠?涓弬鏁頒篃鍙互3涓弬鏁拌皟鐢ㄧ殑璁捐鏄釜閿欒銆?br />
]]>
(閲戝簡鐨勪笓鏍?2020.3)
瀵煎嚭 channel 鐨勫寘閮芥瘮杈冮毦鐢ㄣ傚鉤甯鎬篃涓嶄細灝嗘帴鍙h璁℃垚 channel.
浠ヤ笅鎽樿嚜錛歨ttps://studygolang.com/articles/12135?fr=sidebar
涓嶈瀵煎嚭騫跺彂鍘熻
Go 鎻愪緵浜嗛潪甯告槗浜庝嬌鐢ㄧ殑騫跺彂鍘熻錛岃繖涔熷鑷翠簡瀹冭榪囧害鐨勪嬌鐢ㄣ傛垜浠富瑕佹媴蹇冪殑鏄?channel 鍜?sync package 銆傛湁鐨勬椂鍊欐垜浠細瀵煎嚭涓涓?channel 緇欑敤鎴蜂嬌鐢ㄣ傚彟澶栦竴涓父瑙佺殑閿欒灝辨槸鍦ㄤ嬌鐢?sync.Mutex 浣滀負緇撴瀯浣撳瓧孌電殑鏃跺欐病鏈夋妸瀹冭緗垚縐佹湁銆傝繖騫朵笉鎬繪槸寰堢碂緋曪紝涓嶈繃鍦ㄥ啓嫻嬭瘯鐨勬椂鍊欏嵈闇瑕佽冭檻鐨勬洿鍔犲叏闈€?br />
褰撴垜浠鍑?channel 鐨勬椂鍊欐垜浠氨涓鴻繖涓寘鐨勭敤鎴峰甫鏉ヤ簡嫻嬭瘯涓婄殑涓浜涗笉蹇呰鐨勯夯鐑︺備綘姣忓鍑轟竴涓?channel 灝辨槸鍦ㄦ彁楂樼敤鎴峰湪嫻嬭瘯鏃跺欑殑闅懼害銆備負浜嗗啓鍑烘紜殑嫻嬭瘯錛岀敤鎴峰繀欏昏冭檻榪欎簺錛?br />
浠涔堟椂鍊欐暟鎹彂閫佸畬鎴愩?br /> 鍦ㄦ帴鍙楁暟鎹殑鏃跺欐槸鍚︿細鍙戠敓閿欒銆?br /> 濡傛灉闇瑕佸湪鍖呬腑娓呯悊浣跨敤榪囩殑channel鐨勬椂鍊欒鎬庝箞鍋氥?br /> 濡備綍灝?API 灝佽鎴愪竴涓帴鍙o紝浣挎垜浠笉鐢ㄧ洿鎺ュ幓璋冪敤瀹冦?br />
]]>
(閲戝簡鐨勪笓鏍?2020.2)
鍋囪搴旂敤鍚嶄負 MyMod, 涓誨嚱鏁扮殑鏂囦歡涓?main.go, 閭e氨鍒涘緩濡備笅鐨?main_test.go 鏂囦歡錛?br />```
package main
/* 嫻嬭瘯鏁翠釜鏈嶅姟鍣ㄣ?/span>
go test -c -covermode=count -coverpkg ./...
鐢熸垚 MyMod.test.exe. 澶嶅埗鍒?bin 鐩綍涓嬭繍琛岋細
MyMod.test.exe --systemTest --test.coverprofile MyMod.cov
鐢熸垚浠g爜瑕嗙洊嫻嬭瘯緇撴灉 MyMod.cov, 闇瑕佸湪 go.mod 綆$悊鐨勭洰褰曚笅鎵ц
go tool cover -html=MyMod.cov -o MyMod.html
鎵撳紑 MyMod.html 鏌ョ湅緇撴灉銆?/span>
*/
import (
"flag"
"fmt"
"testing"
)
var systemTest *bool
func init() {
systemTest = flag.Bool("systemTest", false, "Set to true when running system tests")
}
// Test started when the test binary is started. Only calls main.
func TestSystem(t *testing.T) {
if *systemTest {
fmt.Println("Test system...")
main()
}
}
```
MyMod.cov 浼氱Н绱紝鍙姞鍏?Git.
MyMod.cov 鎻愪氦鍓嶏紝闇瀵瑰叾鎺掑簭錛屾柟渚挎煡鐪?diff.
鎺掑簭鑴氭湰 sort_cov.bat 濡備笅錛?br />```
head -1 MyMod.cov > tmp.cov
cat MyMod.cov | sed '1d' | sort >> tmp.cov
cp tmp.cov MyMod.cov
del tmp.cov
pause
```
]]>
(閲戝簡鐨勪笓鏍?2020.2)
github 鎼?consistent, 鎸夋槦鏁頒緷嬈″療鐪?br />## stathat/consistent
Consistent hash package for Go.
688鏄?
stathat 搴旇鏄釜鏁版嵁緇熻鐨勪簯鏈嶅姟銆俢onsistent琚涓烘槸鐢熶駭鍙敤鐨勩?br />鎵鏈夋枃妗i兘鍦?godoc. 紺轟緥鍜屾帴鍙g畝媧佹槗鎳傘?br />## lafikl/consistent
A Go library that implements Consistent Hashing and Consistent Hashing With Bounded Loads.
575 鏄?br />
鏈夌晫璐熻澆鐨勪竴鑷存у搱甯岀畻娉?br />
紺轟緥涓槸 c.GetLeast() 鑾峰彇鏈灝忚礋杞斤紝涓嶅儚鏄竴鑷存ash鐨勫簲鐢ㄣ?br />浠庝唬鐮佺湅錛屽簲璇ユ槸鏈榪戠殑鏈弧杞斤紝涓嶆槸鏈灝忚礋杞姐?br />
`MaxLoad()`璇存槑鏄劇ず鏈澶ц礋杞芥槸鑷姩璁劇疆涓?(total_load/number_of_hosts)*1.25銆?br />
`const replicationFactor = 10`
娌℃湁涓嬈¤幏鍙栧涓殑鎺ュ彛銆?br />## serialx/hashring
Consistent hashing "hashring" implementation in golang (using the same algorithm as libketama)
367 鏄?br />
鏄粠 Python 搴撶Щ妞嶇殑銆傚彲浠ヨ緗?weight.
紺轟緥
```
replicaCount := 3
ring := hashring.New(serversInRing)
server, _ := ring.GetNodes("my_key", replicaCount)
```
瀹為檯涓婃槸 GetN 鐨勫姛鑳姐傚唴閮ㄥ疄鐜扮己灝?replicationFactor## buraksezer/consistent
Consistent hashing with bounded loads in Golang
263 鏄?br />
鏈彁渚涚己鐪乭ash鍑芥暟銆?br />
澶氫簡涓涓猔PartitionCount`
c.loads 浠呯敤浜庢煡璇㈣礋杞斤紝騫舵病鏈夌敤浜庢湁鐣岃礋杞姐?br />
緇撹鏄繖涓笉鏄湁鐣岃礋杞界殑涓鑷存у搱甯岋紝鍚屾椂涓鑷存у搱甯岀殑瀹炵幇涓嶅悓瀵誨父銆?br />## 鍏朵粬
jump hash 涓嶈兘縐婚櫎鑺傜偣錛屼笉鑰冭檻### dgryski/go-jump
go-jump: Jump consistent hashing
281 鏄?br />### lithammer/go-jump-consistent-hash
鈿★笍 Fast, minimal memory, consistent hash algorithm
138 鏄?img src ="http://www.shnenglu.com/jinq0123/aggbug/217148.html" width = "1" height = "1" />
]]>
(閲戝簡鐨勪笓鏍?2020.2)
golang 涓殑鎺ュ彛濡備笅錛?br />
```
type Writer interface {
Write func(p []byte) (n int, err error)
}
```
涓鑸珹PI鍙傛暟瑕佹眰涓涓帴鍙o紝鑰屼笉鏄竴涓嚱鏁版寚閽堬紝濡?io.Copy() 闇瑕佽緭鍏ヤ竴涓?Writer 鍜?Reader錛?br />```
func Copy(dst Writer, src Reader) (written int64, err error)
```
鑰屼笉鏄繖鏍?涓嚱鏁版寚閽堬細
```
func CopyWithFunc(writeFunc func([]byte) (int, error), readRunc func([]byte) (int, error)) (written int64, err error)
```
澶у緇熶竴浣跨敤鎺ュ彛錛岃屼笉鏄帴鍙e拰鍑芥暟鎸囬拡娣風敤錛屽彲浠ラ伩鍏岮PI澶嶆潅鍖栥?br />濡?io.Copy() 鏈?涓弬鏁幫紝濡傛灉瑕佹敮鎸佹帴鍙e拰鍑芥暟鎸囬拡娣風敤錛屽氨浼氬彉鎴?涓?Copy() 閲嶈澆銆?br />golang 娌℃湁閲嶈澆錛屽氨鍙兘鐢?涓笉鍚岀殑鍑芥暟鍚嶃?br />
鍦ㄥ疄闄呬嬌鐢ㄤ腑錛岄渶瑕佸皢鍑芥暟杞寲鎴愭帴鍙o紝鎵嶈兘璋冪敤 io.Copy().
濡傛湁涓涓嚱鏁?
```
func MyWriteFunction(p []byte) (n int, err error) {
fmt.Print("%v",p)
return len(p),nil
}
```
璋冪敤 io.Copy() 鏃墮渶瑕佸垱寤轟竴涓?Writer錛屽茍灝嗚鍑芥暟鎸囬拡杞瀷涓篧riter鍚庝嬌鐢ㄣ?br />榪欓噷鐢?`WriteFunc` 綾誨瀷瀹炵幇 Writer銆?br />
```
type WriteFunc func(p []byte) (n int, err error)
func (wf WriteFunc) Write(p []byte) (n int, err error) {
return wf(p)
}
```
WriteFunc 鏈韓鏄釜涓?MyWriteFunction 鍚岀被鍨嬬殑鍑芥暟綾誨瀷錛屽悓鏃跺疄鐜頒簡 Writer 鎺ュ彛銆?br />鎵浠?MyWriteFunction 鍙互鐩存帴杞垚WriteFunc綾誨瀷鎴愪負涓涓?Writer.
榪欐牱灝卞彲浠ヨ皟鐢?io.Copy() 浜嗭細
```
io.Copy(WriteFunc(MyWriteFunction), strings.NewReader("Hello world"))
```
鍙傝冿細https://stackoverflow.com/questions/20728965/golang-function-pointer-as-a-part-of-a-struct
]]>
(閲戝簡鐨勪笓鏍?2020.1)
golang 紼嬪簭涓嫻嬪埌 DATA RACE, 鏄?chan 鍏抽棴鍜屽彂閫佸啿紿侊細
==================
WARNING: DATA RACE
Write at 0x00c000098010 by goroutine 68:
runtime.closechan()
/usr/lib/golang/src/runtime/chan.go:327 +0x0
valky/common/tcp.(*Session).Close()
/var/tmp/src/f4f4f712-7894-4d98-83dd...
valky/common/tcp.(*Session).recvloop()
/var/tmp/src/f4f4f712-7894-4d98-83dd...
Previous read at 0x00c000098010 by goroutine 100:
runtime.chansend()
/usr/lib/golang/src/runtime/chan.go:140 +0x0
valky/common/tcp.(*Session).Send()
/var/tmp/src/f4f4f712-7894-4d98-83dd...
main.(*Role).sendMsg()
/var/tmp/src/f4f4f712-7894-4d98-83dd...
==================
Found 1 data race(s)
鏌ヤ簡涓涓?chan 鍏抽棴鐨勬紜仛娉曪紝鍙戠幇浜嗕竴綃囬潪甯歌緇嗙殑鏂囩珷錛?br />[How to Gracefully Close Channels](https://go101.org/article/channel-closing.html)
鏂囦腑鎸囧嚭錛宑han 澶氭鍏抽棴錛屾垨鑰呭湪鍏抽棴鐨?chan 涓婂彂閫侊紝閮戒細 panic.
涓婇潰鐨?DATA RACE 灞炰簬騫歌繍錛屾病鏈?panic銆?br />
chan 鍏抽棴鏃剁殑鍘熷垯鏄細涓嶈鍦ㄦ帴鏀跺崗紼嬩腑鍏抽棴錛屽茍涓旓紝濡傛灉鏈夊涓彂閫佽呮椂灝變笉瑕佸叧闂璫han浜嗐?br />
涓婇潰鐨凞ATA RACE 鏄湪鎺ユ敹鍗忕▼涓叧闂璫han.
鏂囦腑璇︾粏鍒楀嚭浜嗗縐嶆柟妗堝叧闂璫han.
濡傛灉綺楁毚鐐癸紝鍙互鐩存帴鍔犱釜 recover. 鍏朵粬鏂規閮芥槸瑕佷繚璇佸彂閫佸畬鎴愬悗鍐嶅叧闂?br />
]]># open-match鍖歸厤嫻佺▼
(閲戝簡鐨勪笓鏍?2019.1)
https://github.com/GoogleCloudPlatform/open-match
open-match 鏄竴涓氱敤鐨勬父鎴忓尮閰嶆鏋躲?br />鐢辨父鎴忔彁渚涜嚜瀹氫箟鐨勫尮閰嶇畻娉曪紙浠ocker闀滃儚鐨勬柟寮忔彁渚涳級銆?br />
鍒嗕負澶氫釜榪涚▼錛屽悇榪涚▼涔嬮棿鍏變韓涓涓?redis.
* 鍓嶇, 鎺ユ敹鐜╁鍔犲叆 redis錛屾垚鍔熷悗閫氱煡鐜╁鎴塊棿鏈嶅湴鍧
* 鍚庣錛岃緗竴灞娓告垙鐨勫尮閰嶈鍒欙紝璁劇疆鎴塊棿鏈嶅湴鍧
* MMFOrc錛屽惎鍔ㄥ尮閰嶇畻娉?MMF)
* MMF, 鑷畾涔夊尮閰嶇畻娉曪紝璇誨彇 redis 鑾峰彇鐜╁錛屽尮閰嶆垚鍔熷氨灝嗙粨鏋滃啓鍏?redis. 浠呭尮閰嶄竴灞灝遍鍑恒?br />
娓告垙鏈嶄腑榪炴帴 open-match 鐨勫墠绔笌鍚庣鐨勮繘紼嬶紝鍒嗗埆縐頒負 frontendclient 鍜?Director銆?br />杈撳叆鍒?閮ㄤ喚錛屼竴鏄帺瀹朵俊鎭紝浜屾槸瀵瑰眬淇℃伅銆?br />Director 鍚戝悗绔緭鍏ュ灞淇℃伅錛屽氨浼氭敹鍒頒竴涓帴涓涓殑瀵瑰眬浜哄憳鍒楄〃.
Director 闇瑕佷負姣忎釜瀵瑰眬寮鎴塊棿錛岀劧鍚庨氱煡鍚庣鎴塊棿鍦板潃銆?br />鍚庣灝嗘埧闂村湴鍧鍐欏叆 redis, 鐒跺悗鍓嶇璇誨彇鍒版埧闂村湴鍧錛屽氨閫氱煡 frontendclient錛岃鐜╁榪涘叆鎴塊棿銆?br />## test/cmd/frontendclient
妯℃嫙澶у巺鏈嶆垨緇勯槦鏈嶏紝榪炴帴鍓嶇API, 璇鋒眰鍖歸厤鐜╁/闃熶紞銆傛垚鍔熷悗灝嗘敹鍒版埧闂存湇(DGS)鐨勫湴鍧(Assignment)銆?br />
Player 瀹為檯涓婃槸涓涓槦浼嶏紝鍏朵腑ID瀛楁鏄敤絀烘牸鍒嗛殧鐨勫涓狪D.
铏界劧鍙傛暟綾誨瀷閮芥槸 Player, CreatePlayer() 鍙傛暟涓烘暣涓槦浼嶏紝鑰?GetUpdates() 鍙傛暟鏄崟涓帺瀹躲?br />
main() 涓垱寤哄涓帺瀹訛紝姣忎釜鐜╁璋冪敤 GetUpdates() 浠ヨ幏鍙栫粨鏋滐紝go waitForResults() 涓鐞嗙粨鏋溿?br />waitForResult() 璇誨彇嫻佷腑鐨勫尮閰嶇粨鏋滐紝鍘嬪叆 resultsChan錛堜絾濂藉儚 resultsChan 浠呯敤浜庢墦鍗幫級銆?br />鎵鏈夌帺瀹跺悎騫跺埌 g 瀹炰緥涓紝鐒跺悗璋冪敤 CreatePlayer() 璇鋒眰鍖歸厤銆?br />
cleanup() 璋冪敤 DeletePlayer() 鏉ュ垹闄ゅ尮閰嶈姹傦紝涓嶄粎闇鍒犻櫎鏁翠釜闃熶紞錛屼篃闇瑕佸垹闄ゅ崟涓帺瀹躲?br />
濂藉儚鏈鍚庡彇緇撴灉娌″彇瀵瑰湴鏂癸紝搴旇浠?resultChan 涓幏鍙?Assignment, 騫剁敤璇ュ湴鍧 udpClient().
鐪嬩簡璇ョず渚嬪氨鍙互鐞嗚В frontend.proto## examples/backendclient
MatchObject.Properties 鏄粠 testprofile.json 璇誨彇鐨勶紝搴旇鏀瑰悕涓?Profile 鏄惁鏇村ソ鐐癸紵
pbProfile 鏄?MatchObject錛孭rofile 絳夊悓浜?MatchObject?
Profile 鐨勫畾涔夋槸 MMF 鎵闇鐨勬墍鏈夊弬鏁般?br />`pbProfile.Properties = jsonProfile` 閲嶅浜?閬嶃?br />
ListMatches()鍒楀嚭榪欎釜Profile鐨勬墍鏈夊尮閰嶃?br />鏀跺埌涓涓尮閰嶅悗錛岄』鐢–reateAssignments()灝嗘埧闂存湇鍦板潃, 縐頒負 Assignment, 鍙戦佸埌鎵鏈夋父鎴忓鎴風銆?br />## cmd/frontendapi
CreatePlayer() 灝?Player 瀵硅薄鍐欏叆 redis, 閿間負 Player.Id, 綾誨瀷涓?HSET銆?br />瀵?Player 鐨勬瘡涓?attribute錛屾坊鍔犲埌 ZSET 涓幓銆?br />姝ゅ Player 鏄竴緇勭帺瀹躲?br />
GetUpdates() 姣忛殧2s璇誨彇redis, Player鏁版嵁鏈夊彉鍖栨椂灝卞彂閫併傛澶?Player 鏄崟涓帺瀹躲?br />
濡傛灉CreatePlayer()涓槦浼嶅彧鏈変竴涓帺瀹訛紝
鍒欏啓鍏ョ殑Player涓嶨etUpdates()涓鍙栫殑鐜╁鏄悓涓涓猺edis閿?br />## cmd/backendapi
CreateMatch() 涓?profile 綾誨瀷涓?MatchObject, 鏄竴涓瘮璧涚殑闄愬埗鏉′歡銆?br />profile 鍏堝啓鍏?redis, 閿負 profile.Id.
`requestKey := xid() + "." + profile.Id`,
騫跺皢 requestKey 鍔犲叆 redis 闆嗗悎 "profileq"銆?br />鐒跺悗姣?s鏌ヨ redis, 鐪嬫槸鍚︽湁 requestKey 閿嚭鐜幫紝騫惰繑鍥炶鍊箋?br />
ListMatch() 姣?s璋冪敤涓嬈?CreateMatch().
DeleteMatch() 浠呬粎鍒犻櫎 Id 榪欎釜閿?br />
CreateAssignments() 涓哄涓槦浼嶈緗瓵ssignment, 鍗蟲埧闂村湴鍧銆?br />閬嶅巻鎵鏈塕oster涓殑Player瀵硅薄錛屽湪redis涓緗瓵ssignment.
(Assignment 鏇存敼鍚庯紝浼氳Е鍙戝墠绔洿鏂般?
灝嗘墍鏈?Player.Id 浠?"proposed" 縐誨埌 "deindexed"錛岃繖涓や釜鏄?ZSET, 鍒嗗間負鍔犲叆鏃墮棿銆?br />Roster 搴旇鏄瘮璧涗腑鐨勯樀钀ワ紝濡傜孩鏂癸紝钃濇柟錛屾瘡涓樀钀ヤ腑鍙湁澶氫釜闃熶紞銆?br />
DeleteAssignments() 浠呬粎閬嶅巻鎵鏈?Player 瀵硅薄鏉ュ垹闄?Assignment 瀛楁銆?br />## cmd/mmforc
鍖歸厤嫻佺▼鏄敱 mmforc (matchmaking function orchestrator) 鎺у埗鐨勩?br />
mmforc 姣忕浠?redis 鐨?profileq 涓彇鍑?100 涓垚鍛? 鍏朵腑 profileq 鏄釜set綾誨瀷錛?br />浣跨敤鍛戒護涓篳SPOP profileq 100`.
瀵規瘡涓?profile, 鍒涘緩涓涓?k8s 浠誨姟錛?br />
```
// Kick off the job asynchrnously
go mmfunc(ctx, profile, cfg, defaultMmfImages, clientset, &pool)
```
姣忛殧10s, 榪樻湁鎵鏈夊尮閰嶄換鍔¢兘瀹屾垚鍚庯紝闇瑕?`checkProposals`, 鍗沖垱寤?evaluator 浠誨姟銆?br />
profileq 涓殑鍏冪礌 profile 涓哄瓧絎︿覆錛宮atchObjectID.profileID銆?br />浠?profileID 涓洪敭錛屽彲浠ヤ粠 redis 璇誨彇 profile 鐨勫唴瀹? profile 鏄釜 MatchObject 瀵硅薄銆?br />
profile 鐨勫唴瀹逛負 json 涓詫紝鍏朵腑 "jsonkeys.mmfImages" 涓?mmf (matchmaking function) 闀滃儚銆?br />
濡傛灉profile璇誨彇澶辮觸錛屾垨鑰?mmfImages 涓虹┖錛屽垯浣跨敤榛樿鐨勯暅鍍忋俶mfImages 鏈潵浼氭敮鎸佸涓暅鍍忋?br />
閫氳繃 MMF_* 鐜鍙橀噺浼犲叆鍚勭鍙傛暟.## mmf
紺轟緥錛歟xamples\functions\golang\manual-simple
浠庣幆澧冨彉閲?"MMF_PROFILE_ID" 瑙f瀽鍑?profileID, 騫跺悜 redis 鏌ヨ(HGETALL) profile錛孒SET 綾誨瀷銆?br />
浠?profile 涓彇 pools 瀛楁錛屽嵆鍖歸厤鏉′歡銆?br />pools 鍒嗕負澶氫釜 pool, 姣忎釜 pool 涓湁澶氫釜 filter, 姣忎釜 filter 鍚?redis 鍙栫鍚堢殑 Player.
profile 鐢ㄥ埌浠ヤ笅瀛楁錛?br />
* "properties.playerPool"
json涓詫紝鏄竴浜涜繃婊ゆ潯浠訛紝濡?#8220;mmr: 100-999”
* "properties.roster"
json涓? 鏄涓槦浼嶅ぇ灝忥紝濡?“red: 4”
紺轟緥瑙侊細`examples\backendclient\profiles\testprofile.json`### 綆鍗曞尮閰嶈繃紼?/h3>
simple mmf 鐨勫尮閰嶈繃紼嬪涓嬶細
1. 浠?redis 鏌ヨ profile錛岃幏鍙栬繃婊ゆ潯浠跺拰鍚勯槦浼嶅ぇ灝?br />1. 姣忎釜榪囨護鏉′歡鍚?redis 鏌ヨ錛屾墍鏈夌粨鏋滅殑浜ら泦涓哄彲閫夋垚鍛?br />1. 鍘婚櫎 ignoreList, 鍗蟲渶榪?800s 鍐呭凡鍖歸厤鎴愬姛鐨勬垚鍛橈紝鍗?proposal 鍜?deindexed ZSET 鍒楄〃銆?br />1. 濡傛灉鍙夋垚鍛樹釜鏁板お灝忥紝鍒?insufficient_players 騫墮鍑?br />1. 鍒嗛厤鍚勪釜闃熶紞鎴愬憳
1. 鍚?redis 璁板綍緇撴灉### 緇撴灉
profile 涓坊鍔?roster錛屽嵆鍚勯樀钀ユ垚鍛樺悕鍗曪紝瀛樺叆 prososalKey.
淇濆瓨涓嶅垎闃熶紞鐨勬垚鍛樺悕鍗曘?br />鐒跺悗鍚?"proposalq" 娣誨姞 prososalKey### 緇嗚妭
poolRosters 浠?(pool鍚? filter attribute) 涓洪敭錛屽間負 Player ID 鍒楄〃.
淇濆瓨浠?redis 鏌ヨ鐨勭鍚堟潯浠剁殑 Player ID.
overlaps 浠?pool 鍚嶄負閿紝淇濆瓨絎﹀悎璇ool涓墍鏈塮ilter鐨?Player ID 鍒楄〃錛屽幓闄?ignore list.
rosters 鏄?profile 涓殑 "properties.rosters" 瀛楁銆備笉鐭ヤ綍鐢紵
閬嶅巻 rosters, 涓烘瘡涓樀钀ョ殑姣忎釜player鎵懼埌瀵瑰簲pool鐨凱layerID, 淇濆瓨鍒?mo.Rosters.
鍏朵腑 profileRosters 濂藉儚娌$敤銆?br />
]]>
Go 1.11 鏀寔 module.
浠g爜涓嶉渶瑕佸湪 GOPATH/src 鐩綍涓嬨?br />
鍏堝垵濮嬪寲妯″潡錛岀敓鎴?`go.mod`
E:\temp
λ mkdir -p testmod\hello
E:\temp
λ cd testmod\hello\
E:\temp\testmod\hello
λ go mod init github.com/jinq0123/hello
go: creating new go.mod: module github.com/jinq0123/hello
鍒涘緩 `hello.go`
package main
import (
"fmt"
"rsc.io/quote"
)
func main() {
fmt.Println(quote.Hello())
}
鏋勫緩鏃舵姤 `golang.org/x/text` 榪炰笉涓婏細
E:\temp\testmod\hello
λ go build
go: golang.org/x/text@v0.0.0-20170915032832-14c0d48ead0c: unrecog
nized import path "golang.org/x/text" (https fetch: Get https://g
olang.org/x/text?go-get=1: dial tcp 216.239.37.1:443: connectex:
A socket operation was attempted to an unreachable network.)
go: error loading module requirements
`go.mod` 娣誨姞
replace golang.org/x/text => github.com/golang/text v0.3.0
鐒跺悗鏋勫緩灝辨垚鍔熶簡錛?br />
E:\temp\testmod\hello
λ go build
go: finding github.com/golang/text v0.3.0
go: downloading rsc.io/sampler v1.3.0
go: downloading github.com/golang/text v0.3.0
E:\temp\testmod\hello
λ hello.exe
Hello, world.
濡傛灉涓嶅姞鐗堟湰鍙鳳紝鍒欎細鎶ラ敊錛?br />
go.mod:9: replacement module without version must be directory path (rooted or starting with ./ or ../)
Go 1.11.1 replace 榪樻湁闂錛屼粛浼氳瘯鍥捐繛鎺ュ師鍦板潃銆傜洰鍓嶇増鏈?1.11.4 鍙互鐢ㄣ?br />
鍙傝冿細
https://github.com/golang/go/wiki/Modules
]]>
(閲戝簡鐨勪笓鏍?2018.10)
鐢?gotest 榪愯涓涓祴璇曪紝寰 mongodb 涓彃鍏ヤ竴鏉★紝鍙戠幇鏈夋椂鐏碉紝鏈夋椂涓嶇伒銆?br />
鍥犱負閿欒鍦版鐤?mgo 鐢ㄩ敊浜嗭紝鑰楄垂浜嗕笉灝戞椂闂淬?br />鏈緇堝彂鐜版槸鍥犱負 gotest 鏄湁緙撳瓨鐨勶紝杈撳嚭鐨勬槸涓婃榪愯鐨勭粨鏋滐紝浣嗘槸騫舵病鏈夊疄闄呰繍琛屼唬鐮併?br />
榪愯鏈夋晥鏄洜涓轟唬鐮佸垰鏀硅繃錛屾祴璇曟椂浼氬疄闄呰繍琛屻?br />
鏈緇堜篃鏄棤鎰忛棿鍙戠幇鐨勩傜粰 mgo 寮鍚簡璋冭瘯鏃ュ織錛岀劧鍚庢瘮杈?嬈¤繍琛岋紝鍙戠幇杈撳嚭鏄竴鏍風殑錛?br />鍙湁涓琛屼笉鍚岋細
ok mail-server/server 0.519s
ok mail-server/server (cached)
鏄庣‘鏄劇ず浜嗙2嬈℃槸緙撳瓨銆傚墠闈㈣繍琛屼簡鍑犲崄嬈¢兘蹇界暐浜?cached 榪欎釜杈撳嚭銆?br />
涓轟簡紱佹緙撳瓨錛屽彲鍔犱笂 -count=1 鍙傛暟錛?br />go test -count=1
]]>
(閲戝簡鐨勪笓鏍?2018.9)
灝嗘湇鍔$敤NodePort鏆撮湶鍒板緗戯紝涓洪伩鍏嶇鍙e啿紿侊紝涓嶆寚瀹歂odePort,
鑰屾槸璁﹌8s鑷姩閫夋嫨涓涓鍙c?br />
$ cat get_node_port.yaml
kind: Service
apiVersion: v1
metadata:
name: jq-service
spec:
type: NodePort
selector:
app: MyApp
ports:
- protocol: TCP
port: 80
$ kubectl apply -f get_node_port.yaml
service "jq-service" configured
$ kubectl describe svc/jq-service
Name: jq-service
Namespace: default
Labels: <none>
Annotations: kubectl...
Selector: app=MyApp
Type: NodePort
IP: 10.104.228.187
Port: <unset> 80/TCP
TargetPort: 80/TCP
NodePort: <unset> 32115/TCP
Endpoints: <none>
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>
鍙互鐪嬪埌k8s鍒嗛厤浜哊odePort 32115銆?br />
鐒跺悗闇瑕佽幏鍙栬繖涓姩鎬佺殑NodePort錛屼互閫氱煡瀹㈡埛绔繛鎺ヨ绔彛銆?br />
package main
import (
"context"
"fmt"
"log"
"io/ioutil"
"github.com/ghodss/yaml"
"github.com/ericchiang/k8s"
corev1 "github.com/ericchiang/k8s/apis/core/v1"
)
func main() {
data, err := ioutil.ReadFile("config")
if err != nil {
panic(err)
}
// Unmarshal YAML into a Kubernetes config object.
var config k8s.Config
if err := yaml.Unmarshal(data, &config); err != nil {
panic(err)
}
client, err := k8s.NewClient(&config)
// client, err := k8s.NewInClusterClient()
if err != nil {
log.Fatal(err)
}
var svc corev1.Service
if err := client.Get(context.Background(), "default", "jq-service", &svc); err != nil {
log.Fatal(err)
}
fmt.Printf("%d\n", *svc.Spec.Ports[0].NodePort)
}
榪愯鏃墮渶瑕佸鍒禼onfig: `cp ~/.kube/config .`
]]>
(閲戝簡鐨勪笓鏍?2018.8)
grpc-go 涓涓嬭繛鎺ユ湇鍔″櫒錛岃姹傚皢鍦ㄥ涓狪P涔嬮棿杞漿銆?br />
conn, err := grpc.Dial(
"dns:///rng-headless:8081",
grpc.WithBalancerName(roundrobin.Name),
grpc.WithInsecure())
鏍囧噯鐨勭洰鏍囧悕搴旇鏄繖鏍風殑錛歚"dns://authority/endpoint_name"`,
姝ゅ authority 涓虹┖錛岃瑙侊細https://github.com/grpc/grpc/blob/master/doc/naming.md
鏈嶅姟鍣ㄥ紑3涓疄渚嬶紝鎵鏈夎姹傚湪3涓疄渚嬩笂杞漿錛?br />
[jinqing@host-10-2-3-4 RoundRobin]$ kubectl run -it --rm jinqing-roundrobin --image=jinq0123/roundrobin:4
If you don't see a command prompt, try pressing enter.
2018/08/28 10:18:01 request 7754383576636566559
2018/08/28 10:18:02 request 2543876599219675746
2018/08/28 10:18:03 request 927204261937181213
2018/08/28 10:18:04 request 7754383576636566559
2018/08/28 10:18:05 request 2543876599219675746
2018/08/28 10:18:06 request 927204261937181213
...
鏈嶅姟鍣ㄨ繑鍥炰竴涓殢鏈烘暟錛屼笉鍚屽疄渚嬬殑闅忔満鏁頒笉鍚屻備唬鐮佹槸浠?br />https://github.com/kcollasarundell/balancing-on-k8s 淇敼鐨勩?br />
...
const (
port = ":8081"
)
type server struct{}
var r int64
func init(){
rand.Seed(time.Now().UnixNano())
r = rand.Int63()
}
func (s *server) Rng(context.Context, *rng.Source) (*rng.RN, error) {
return &rng.RN{RN: r}, nil
}
func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
rng.RegisterRngServer(s, &server{})
// Register reflection service on gRPC server.
reflection.Register(s)
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
鍏堢紪璇戯紝鎵撳寘鎴愰暅鍍忥紝鐒跺悗鐢?`balancing-on-k8s\backend\kube.yaml` 榪愯錛?br />kubectl apply -f kube.yaml
`backend\kube.yaml` 鍒涘緩浜嗕竴涓?ClusterIP 鏈嶅姟鍜屼竴涓?Headless 鏈嶅姟錛岄儴緗蹭簡 3 涓湇鍔″櫒瀹炰緥銆?br />[jinqing@host-10-2-3-4 RoundRobin]$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 93d
rng-cluster ClusterIP 10.111.30.205 <none> 8081/TCP 4h
rng-headless ClusterIP None <none> 8081/TCP,8080/TCP 4h
瀹㈡埛绔槸涓涓畝鍗曠殑grpc, 瀹氭椂鍙戦佽姹傦紝鎵撳嵃榪斿洖鐨勯殢鏈烘暟銆?br />`balancing-on-k8s\clientSideBalancer\RoundRobin\main.go`涓殑鍦板潃闇瑕佹坊鍔犵鍙o紝
涓嶇劧grpc浼氬幓榪炴帴 443 绔彛鑰屽け璐ャ?br />
鎵╁鍚庯紝嫻嬪埌澶ф3鍒嗛挓鍚庢墠鐪嬪埌璐熻澆杞Щ銆傜緝瀹瑰悗浼氱珛鍗崇敓鏁堛?br />kubectl scale --replicas=5 deployment/rng
濡傛灉鏄?ClusterIP 鏈嶅姟, 鍒欐湇鍔″悕瀵瑰簲涓涓狢lusterIP;
濡傛灉鏄?Headless 鏈嶅姟錛屽垯鏈嶅姟鍚嶅搴斿悇涓狿od鐨処P:
/ # nslookup rng-headless
Server: 10.96.0.10
Address: 10.96.0.10#53
Name: rng-headless.default.svc.cluster.local
Address: 10.244.3.27
Name: rng-headless.default.svc.cluster.local
Address: 10.244.0.108
Name: rng-headless.default.svc.cluster.local
Address: 10.244.2.66
/ # nslookup rng-cluster
Server: 10.96.0.10
Address: 10.96.0.10#53
Name: rng-cluster.default.svc.cluster.local
Address: 10.111.30.205
/ #
濡傛灉鍘婚櫎 "dns:///", 浠呬粎鏄煙鍚嶅姞绔彛錛?br />
conn, err := grpc.Dial(
"rng-headless:8081",
grpc.WithBalancerName(roundrobin.Name),
...
鍒欏彧浼氳姹傚悓涓涓疄渚嬨傚彧鏈夊綋璇ュ疄渚媝od琚垹闄ゅ悗鎵嶄細鍒囨崲鍒板彟涓涓疄渚嬨?br />浣跨敤緙╁鏃跺彂鐜頒細浼樺厛鍒犻櫎娌℃湁瀹㈡埛绔繛鎺ョ殑瀹炰緥銆?br />鐢?涓鎴風榪炴帴鍒頒笉鍚屾湇鍔″櫒瀹炰緥錛岀劧鍚庣緝瀹逛負1瀹炰緥錛屽氨鍙互鐪嬪埌璇鋒眰鍒囨崲銆?br />
濡傛灉瀹㈡埛绔拰鏈嶅姟鍣ㄦ暟閲忓緢澶э紝榪欎釜dns璐熻澆鍧囪 灝變笉鍚堥備簡錛屽洜涓哄鎴風浼氳繛鎺ユ瘡涓湇鍔″櫒瀹炰緥銆?br />
鍙傝冿細
Exploring Kubernetes Service Discovery and loadbalancing ( https://kca.id.au/post/k8s_service/ )
]]>
(閲戝簡鐨勪笓鏍?2018.7)
闆嗙兢鍐呭鎴風闇瑕佹墦鍖呮垚docker闀滃儚錛屼笂浼犻暅鍍忥紝鐒跺悗鐢?kubectl run 榪愯錛?br />榪樿璁劇疆鐢ㄦ埛瑙掕壊錛屽お楹葷儲錛岃繕鏄敤闆嗙兢澶栧鎴風嫻嬭瘯姣旇緝鏂逛究銆?br />
瀹㈡埛绔簱浣跨敤 ericchiang/k8s, 姣斿畼鏂圭殑 client-go 瑕佺畝鍗曡澶氥?br />
闆嗙兢鍐呭鎴風浣跨敤`k8s.NewInClusterClient()`鍒涘緩錛?br />闆嗙兢澶栧鎴風浣跨敤 `NewClient(config *Config)`, 闇瑕佽緭鍏ラ厤緗紝
閰嶇疆灝辨槸浠?~/.kube/config 璇誨彇鐨勩?br />鍙傝?https://github.com/ericchiang/k8s/issues/79
浠g爜濡備笅錛?br />
package main
import (
"context"
"fmt"
"log"
"io/ioutil"
"github.com/ghodss/yaml"
"github.com/ericchiang/k8s"
corev1 "github.com/ericchiang/k8s/apis/core/v1"
)
func main() {
data, err := ioutil.ReadFile("config")
if err != nil {
panic(err)
}
// Unmarshal YAML into a Kubernetes config object.
var config k8s.Config
if err := yaml.Unmarshal(data, &config); err != nil {
panic(err)
}
client, err := k8s.NewClient(&config)
// client, err := k8s.NewInClusterClient()
if err != nil {
log.Fatal(err)
}
var nodes corev1.NodeList
if err := client.List(context.Background(), "", &nodes); err != nil {
log.Fatal(err)
}
for _, node := range nodes.Items {
fmt.Printf("name=%q schedulable=%t\n", *node.Metadata.Name, !*node.Spec.Unschedulable)
}
}
yaml 搴撶敤浜?ghodss/yaml錛屼笉鑳界敤 go-yaml, 涓嶇劧鎶ラ敊
`yaml: unmarshal errors`
瑙侊細https://github.com/ericchiang/k8s/issues/81
澶嶅埗 .kube/config 鍒拌繍琛岀洰褰曪紝榪愯鍒楀嚭鎵鏈夎妭鐐癸細
[jinqing@host-10-1-2-19 out-cluster]$ cp ~/.kube/config .
[jinqing@host-10-1-2-19 out-cluster]$ ./out-cluster
name="host-10-1-2-20" schedulable=true
name="host-10-1-2-21" schedulable=true
name="host-10-1-2-22" schedulable=true
name="host-10-1-2-19" schedulable=true
]]>
(閲戝簡鐨勪笓鏍?2018.6)
鎽樿嚜錛?br />https://www.ardanlabs.com/blog/2017/02/package-oriented-design.html
If a package wants to import another package at the same level:
* Question the current design choices of these packages.
* If reasonable, move the package inside the source tree for the package that wants to import it.
* Use the source tree to show the dependency relationships.
]]>
(閲戝簡鐨勪笓鏍?2018.6)
鎽樿嚜錛?br />
https://talks.golang.org/2014/organizeio.slide#1
The name of a package
Keep package names short and meaningful.
Don't use underscores, they make package names long.
io/ioutil not io/util
suffixarray not suffix_array
Don't overgeneralize. A util package could be anything.
The name of a package is part of its type and function names.
On its own, type Buffer is ambiguous. But users see:
buf := new(bytes.Buffer)
Choose package names carefully.
Choose good names for users.
]]>
(閲戝簡鐨勪笓鏍?2018.6)
grpc-go鏈嶅姟鍣ㄧ殑姣忎釜璇鋒眰閮藉湪涓涓嫭绔嬬殑鍗忕▼涓墽琛屻?br />緗戞父鏈嶅姟鍣ㄤ腑錛屼竴鑸姹備細璋冪敤娓告垙鎴塊棿鐨勬柟娉曪紝鑰屾埧闂存槸涓涓嫭绔嬬殑鍗忕▼銆?br />鍙互灝嗘埧闂村疄鐜頒負actor錛実rpc璇鋒眰閫氳繃Call()鎴朠ost()鏂規硶鏉ユ墽琛屻?br />鍏朵腑Call()浼氱瓑寰呰繑鍥烇紝鑰孭ost()浼氬紓姝ユ墽琛屾棤榪斿洖鍊箋?br />
type Room struct {
// actC 鏄叾浠栧崗紼嬪悜Room鍗忕▼鍙戦佸姩浣滅殑Channel錛屽崗紼嬩腑灝嗕緷嬈℃墽琛屽姩浣溿?/span>
// Action 鍔ㄤ綔, 鏄棤鍙傛暟鏃犺繑鍥炲肩殑鍑芥暟.
actC chan func()
...
}
// Run 榪愯鎴塊棿鍗忕▼.
func (r *Room) Run() {
ticker := time.NewTicker(20 * time.Millisecond)
defer ticker.Stop()
for r.running {
select {
case act := <-r.actC:
act()
case <-ticker.C:
r.tick()
}
}
}
// Call calls a function f and returns the result.
// f runs in the Room's goroutine.
func (r *Room) Call(f func() interface{}) interface{} {
// 緇撴灉浠巆h榪斿洖
ch := make(chan interface{}, 1)
r.actC <- func() {
ch <- f()
}
// 絳夊緟鐩村埌榪斿洖緇撴灉
return <-ch
}
// Post 灝嗕竴涓姩浣滄姇閫掑埌鍐呴儴鍗忕▼涓墽琛?
func (r *Room) Post(f func()) {
r.actC <- f
}
grpc鏈嶅姟鏂規硶濡傦細
func (m *RoomService) Test(ctx context.Context, req *pb.TestReq) (*pb.TestResp, error) {
conn := conn_mgr.GetConn(ctx)
if conn == nil {
return nil, fmt.Errorf("can not find connection")
}
room := conn.GetRoom()
resp := room.Call(func() interface{} {
return room.Test(req)
})
return resp.(*pb.TestResp), nil
}
]]>