1package etcdv3 2 3import ( 4 "context" 5 "io" 6 "time" 7 8 "github.com/go-kit/kit/endpoint" 9 "github.com/go-kit/kit/log" 10 "github.com/go-kit/kit/sd" 11 "github.com/go-kit/kit/sd/lb" 12 "google.golang.org/grpc" 13) 14 15func Example() { 16 // Let's say this is a service that means to register itself. 17 // First, we will set up some context. 18 var ( 19 etcdServer = "10.0.0.1:2379" // in the change from v2 to v3, the schema is no longer necessary if connecting directly to an etcd v3 instance 20 prefix = "/services/foosvc/" // known at compile time 21 instance = "1.2.3.4:8080" // taken from runtime or platform, somehow 22 key = prefix + instance // should be globally unique 23 value = "http://" + instance // based on our transport 24 ctx = context.Background() 25 ) 26 27 options := ClientOptions{ 28 // Path to trusted ca file 29 CACert: "", 30 31 // Path to certificate 32 Cert: "", 33 34 // Path to private key 35 Key: "", 36 37 // Username if required 38 Username: "", 39 40 // Password if required 41 Password: "", 42 43 // If DialTimeout is 0, it defaults to 3s 44 DialTimeout: time.Second * 3, 45 46 // If DialKeepAlive is 0, it defaults to 3s 47 DialKeepAlive: time.Second * 3, 48 49 // If passing `grpc.WithBlock`, dial connection will block until success. 50 DialOptions: []grpc.DialOption{grpc.WithBlock()}, 51 } 52 53 // Build the client. 54 client, err := NewClient(ctx, []string{etcdServer}, options) 55 if err != nil { 56 panic(err) 57 } 58 59 // Build the registrar. 60 registrar := NewRegistrar(client, Service{ 61 Key: key, 62 Value: value, 63 }, log.NewNopLogger()) 64 65 // Register our instance. 66 registrar.Register() 67 68 // At the end of our service lifecycle, for example at the end of func main, 69 // we should make sure to deregister ourselves. This is important! Don't 70 // accidentally skip this step by invoking a log.Fatal or os.Exit in the 71 // interim, which bypasses the defer stack. 72 defer registrar.Deregister() 73 74 // It's likely that we'll also want to connect to other services and call 75 // their methods. We can build an Instancer to listen for changes from etcd, 76 // create Endpointer, wrap it with a load-balancer to pick a single 77 // endpoint, and finally wrap it with a retry strategy to get something that 78 // can be used as an endpoint directly. 79 barPrefix := "/services/barsvc" 80 logger := log.NewNopLogger() 81 instancer, err := NewInstancer(client, barPrefix, logger) 82 if err != nil { 83 panic(err) 84 } 85 endpointer := sd.NewEndpointer(instancer, barFactory, logger) 86 balancer := lb.NewRoundRobin(endpointer) 87 retry := lb.Retry(3, 3*time.Second, balancer) 88 89 // And now retry can be used like any other endpoint. 90 req := struct{}{} 91 if _, err = retry(ctx, req); err != nil { 92 panic(err) 93 } 94} 95 96func barFactory(string) (endpoint.Endpoint, io.Closer, error) { return endpoint.Nop, nil, nil } 97