This is a creation in Article, where the information may have evolved or changed.
Overview
GRPC is the Google Open source RPC framework, based on HTTP2 implementation, and supports cross-language, now basically covers the mainstream language. The implementation of cross-language is mainly due to the protobuf, by writing the proto file, and using the Protobuf tool to generate the corresponding language class library.
For a new language like go, the eco-chain is still in the development stage, as is the microservices framework, which will build a microservices communication framework based on the grpc-go version.
1. Mechanism for registration and release of services
1.1 Resolved issues
Service registration and release are primarily resolved service dependency issues, in general, if a service invokes B service, the most straightforward approach is to configure the IP address and port. But as service dependencies change, the configuration will be complex and the configuration of all related services needs to be modified when the service is migrated. This will be very difficult to maintain and prone to problems.
Therefore, in order to solve this service dependency, service registration and release came into being.
1.2 mechanism
Service registration and discovery are mainly divided into the following points.
- Service Information Release
This is primarily the service name, IP information, and some attachment metadata. Register to the Service Registration Publishing center via the registration interface.
When the service stops unexpectedly, the client needs to perceive that the service is stopped and kicks the IP address of the service to a list of available IP addresses, which can be achieved using a timed heartbeat.
Through service registration and publication, a service can be deployed to deploy multiple instances, and the client realizes the direct load balancing of the instances, thus realizing the scale-out of the service.
Therefore, service registration and release can be summarized as the service will escalate information, the client pulls the service information, call through the service name, when the service goes down, the client kicks off the service, the client automatically added to the call list when the service is new.
2. Implement
Grpc-go the entire implementation of the full use of Go interface features, so through the expansion of the interface, can easily realize the registration and discovery of services, where the service registry to consider the availability and consistency, generally using ETCD or zookeeper to achieve, here to implement ETCD version.
For complete code and usage examples see: https://github.com/g4zhuj/grp ...
2.1 Client
The specific need to implement a few interfaces, for the client, the simplest way to implement only two interface methods to implement resolve (), and Next (), and then use the polling load balancing method.
It is implemented mainly through the get interface of ETCD and the Watch interface.
//用于生成Watcher,监听注册中心中的服务信息变化func (er *etcdRegistry) Resolve(target string) (naming.Watcher, error) { ctx, cancel := context.WithTimeout(context.TODO(), resolverTimeOut) w := &etcdWatcher{ cli: er.cli, target: target + "/", ctx: ctx, cancel: cancel, } return w, nil}
The next interface is mainly used to obtain registered service information, through channel and watch, when the service information occurs//changes, the next interface will return the changes to the GRPC framework for service information changes. Func (EW *etcdwatcher) Next ([ ]*naming. Update, error) {var updates []*naming. When Update//is first acquired, the Listener channel is created and the service information obtained is returned if Ew.watchchan = = Nil {//create new Chan resp, err: = Ew.cli. Get (Ew.ctx, Ew.target, Etcd. Withprefix (), Etcd. Withserializable ()) if err! = Nil {return nil, err} for _, KV: = Range resp. Kvs {var upt naming. Update If err: = json. Unmarshal (KV. Value, &upt); Err! = Nil {Continue} updates = append (updates, &upt)}//Create ETCD Watcher Monitor the target (service name) information. OPTs: = []ETCD. Opoption{etcd. Withrev (resp. Header.revision + 1), Etcd. Withprefix (), Etcd. Withprevkv ()} Ew.watchchan = Ew.cli.Watch (context. TODO (), Ew.target, opts ...) Return updates, nil}//Block listener, return to upper WRSP when service changes, OK: = <-ew.watchchan if!ok {err: = status. Error (Codes. Unavailable, "Etcd Watch closed") return nil, err} if wrsp. ERR ()! = nil {return nil, wrsp. ERR ()} for _, E: = Range wrsp. Events {var upt naming. Update var err error switch E.type {case ETCD. Eventtypeput:err = json. Unmarshal (E.kv.value, &upt) upt. Op = naming. ADD case Etcd. Eventtypedelete:err = json. Unmarshal (E.prevkv.value, &upt) upt. Op = naming. Delete} if Err! = Nil {Continue} updates = append (updates, &upt)} RET Urn updates, nil}
2.2 Service Side
The server only needs to escalate the service information and keep the heartbeat on time, which is achieved through the ETCD put interface and the KeepAlive interface.
Specific as follows:
Func (er *etcdregistry) Register (CTX context. Context, target string, update naming. Update, opts ... wrapper. registryoptions) (err Error) {//Serializes the service information into JSON format var upbytes []byte if upbytes, err = json. Marshal (update); Err! = Nil {return status. Error (codes. Invalidargument, Err. Error ()} ctx, Cancel: = context. Withtimeout (context. TODO (), resolvertimeout) er.cancal = Cancel Rgopt: = wrapper. Registryoption{ttl:wrapper. Defaultreginfttl} for _, opt: = Range opts {opt (&rgopt)} switch update. Op {case naming. ADD:LSRSP, err: = Er.lsCli.Grant (CTX, Int64 (Rgopt.ttl/time. Second)) If err! = Nil {return ERR}//put service information to ETCD, and set the value of key TTL, through the KeepAlive interface behind/ /To renew the TTL and not receive a renewal request at a time exceeding the TTL, the service may be suspended, thereby clearing the service information etcdopts: = []ETCD. Opoption{etcd. Withlease (Lsrsp.id)} key: = target + "/" + update. Addr _, Err = Er.cli.KV.Put (CTX, Key, String (upbytes), etcdopts ...) If err! = Nil {REturn Err}//Keep heartbeat Lsrspchan, err: = er.lsCli.KeepAlive (context. TODO (), lsrsp.id) if err! = Nil {return err} go func () {for { _, OK: = <-lsrspchan if!ok {grpclog. Fatalf ("%v keepalive channel is closing", key) Break}}} () Case n Aming. Delete: _, Err = Er.cli.Delete (CTX, target+ "/" +update. ADDR) Default:return status. Error (codes. Invalidargument, "Unsupported op")} return nil}
3. Reference
https://grpc.io/
https://coreos.com/etcd/
Https://github.com/g4zhuj/grp ...