This is a creation in Article, where the information may have evolved or changed.
Reprinted from: http://luodw.cc/2016/08/23/golang01/
Internship period has been using Golang, today want to write a blog, talk about my thoughts on golang, and analysis of the implementation of Groupcache;
View of Xiamen University
Before touching Golang, in my impression, server development is a common nginx multi-process, memcache Multi-threading, Redis single-threaded; later, Golang this multi-coprocessor, a connection corresponding to a co-process pattern deeply attracted me, compared to the process, the thread, The co-process granularity is relatively small, and the cost of the co-process switching is very little compared with the processes and threads, so a connection corresponding to a co-process can be achieved;
Golang is very suitable for writing services, the first is the language level to support high concurrency, followed by the HTTP and RPC interface encapsulation, the user writes the C/S architecture server, can use the HTTP-based REST API interface to achieve client and server-side communication, At the same time can also use RPC to achieve client and server-side communication, and support a variety of data format transmission, such as XML,JSON,GOB, etc., and finally, the deployment is simple, generate executable files, run directly;
Learning Golang, the first is to learn the basic grammar, this online has a lot of tutorials, there is a "go language programming" is very suitable for getting started, and secondly, you need to learn some things at the bottom of the go language level, such as co-process switching, socket programming principle and GC, etc., which is very necessary, because understanding these, The use of Golang will be more handy, for example, Golang network programming, how to inform a description have event arrives? When a process starts blocking in a read call, how is it awakened when there is data reached? I recommend two Gitbook, a fit to get started, a fit in depth:
In-depth understanding of Go
Implemention of Golang
"Copyright Notice" blog content by Rodowen's private dishes have copyright, permission to reprint, but please indicate the original link http://luodw.cc/2016/08/23/golang01/
After understanding the basics of grammar and some underlying principles, I think I can look at Golang's own framework, which is great, it's good for learning, especially HTTP and RPC, because learning the two frameworks can see how an HTTP request is implemented and how an RPC process is implemented And, of course, some third-party open source frameworks are also well written;
OK, let's look at how the cache library Groupcache is implemented.
Groupcache
The Groupcache is a KV structure, which is produced by memcache authors, as described on the official GitHub:
Groupcache is a caching and cache-filling library, intended as a replacement for memcached in many cases.
That is, Groupcache is a kv cache, which is used to replace memcache in some respects, but after I have studied this framework, I find that this framework is not a very useful scenario because Groupcache can only get, not update and delete, It is also not possible to set the expiration time, only the least recently accessed data can be eliminated by LRU, and some data will be cached with Groupcache if it is not changed for a long time; Groupcache already in DL. Google.com, Blogger, Google Code, Google Fiber, Google production monitoring systems and other projects to put into use.
But Groupcache still has its advantages, Groupcache is both a server and a client, when there is no data found in the local Groupcache cache, through a consistent hash, find the corresponding peer server for that key, in the HTTP protocol, Obtain the required data from the peer server, and one of the other things is that when multiple clients access keys that are not present in the memcache, it causes multiple clients to fetch data from MySQL and insert the memcache simultaneously, and in the same case, Groupcache only one client obtains data from MySQL, and the other client blocks until the first client obtains the data and returns it to multiple clients;
Groupcache is a cache library, which means that it is not a complete software and needs to implement the main function itself. Can write a test program, run Groupcache, I read some blog is directly quoted playing with Groupcache this blog test program, the test program, the client and Groupcache through RPC communication, and Groupcache Peer communication between peers via HTTP protocol, this is a good practice, because if the client and server communication and Groupcache communication between the use of the same port, then in the concurrency, it will seriously affect performance; is the architecture diagram of this test program: Groupcache structure diagram
The principle is that if the client is using a set or get command, then the direct operation is the data source (database or file), if the call is the Cget command, then the groupcache to find data;
Groupcache internally implements LRU and consistent hashing, and I think you can look at how Golang implements LRU and consistent hashing. The following is a simple analysis of the implementation of the Groupcache get function and the communication between peer;
Groupcache Get function implementation
When the client is connected to Groupcache, only get to get the data, if the local needs of the data, then directly return, if not, through the consistency hash function to determine the peer of the key, and then through HTTP to get data from this peer If the peer has the required data, then the HTTP reply to the previous Groupcache;groupcache received, saved in the local Hotcache, and returned to the client, if the peer does not have the required data, The Groupcache gets the data from the data source (database or file), saves the data locally maincache, and returns it to the client;
Func (g *group) Get (ctx Context, key string, dest Sink) error {
G.peersonce.do (G.initpeers)
G.stats.gets.add (1)//This is the Groupcache state data, that is, the number of Get +1
if dest = = Nil {
return errors. New ("Groupcache:nil dest Sink")
}
Find local caches, including Maincache and Hotcache
Value, CacheHit: = G.lookupcache (key)
If CacheHit {
If hit, return directly
G.stats.cachehits.add (1)
Return Setsinkview (dest, value)
}
If there is no local hit, the peer gets
Destpopulated: = False
Value, destpopulated, err: = G.load (CTX, Key, dest)
If err! = Nil {
return err
}
If destpopulated {
return Nil
}
Assigns value to Dest to return
Return Setsinkview (dest, value)
}
This get function is very simple, first check whether the local cache exists, the existence is returned, does not exist to the peer, and then see how the Load function is implemented;
Func (g *group) load (ctx Context, key string, dest Sink) (value Byteview, destpopulated bool, err error) {
G.stats.loads.add (1)
The following loadgroup is a guarantee that when data does not exist, only one client obtains data from a peer or a data source.
Other clients block until after the first client data, all clients return; This is mostly through sync. Waitgroup implementation
Viewi, err: = G.loadgroup.do (Key, Func () (interface{}, error) {
If value, CacheHit: = G.lookupcache (key); CacheHit {
G.stats.cachehits.add (1)
return value, nil
}
G.stats.loadsdeduped.add (1)
var value byteview
var err error
If peer, OK: = G.peers.pickpeer (key); OK {
Get data from peer
Value, err = G.getfrompeer (CTX, peer, key)
If Err = = Nil {
G.stats.peerloads.add (1)
return value, nil
}
G.stats.peererrors.add (1)
}
Getting data from a data source
Value, err = g.getlocally (CTX, Key, dest)
If err! = Nil {
G.stats.localloaderrs.add (1)
return nil, err
}
G.stats.localloads.add (1)
Destpopulated = True
Storing data obtained from a data source in a local Maincache
G.populatecache (key, value, &g.maincache)
return value, nil
})
If Err = = Nil {
Value = Viewi. (Byteview)
}
Return
}
This load function first obtains data from peer, and if the peer has no data, it fetches data directly from the data source (database or file); OK, first look at how the Groupcache gets the data from the data source, and then analyze if the data is fetched from the peer;
Func (g *group) getlocally (ctx Context, key string, dest Sink) (Byteview, error) {
ERR: = G.getter.get (CTX, Key, dest)
If err! = Nil {
Return byteview{}, err
}
Return Dest.view ()
}
The getlocallly function is mainly to use NewGroup to create a group simultaneous into the getter, call this getter's get function to get data from the data source.
Func newgroup (name string, cachebytes Int64, Getter Getter) *group {
Return NewGroup (name, cachebytes, getter, nil)
}
That is, when Groupcache and peer do not have the required data, the user can define where to get the data and how to obtain the data, that is, to define the instance of the getter;
Get data from peer
When data is not present in the local groupcache, the data is fetched from the peer, and we take a look at the Getfrompeer function implementation
Func (g *group) Getfrompeer (ctx Context, Peer Protogetter, key string) (Byteview, error) {
To reduce the amount of data transmitted, the data is transferred between peers via PB
Req: = &PB. getrequest{
Group: &g.name,
Key: &key,
}
Res: = &PB. getresponse{}
ERR: = Peer. Get (CTX, req, res)
If err! = Nil {
Return byteview{}, err
}
Value: = Byteview{b:res. Value}
If Rand. INTN (10) = = 0 {//10% The probability of acquiring data from peer is stored locally hotcache
G.populatecache (key, value, &g.hotcache)
}
return value, nil
}
This protogetter is an interface, the Httpgetter struct implements this interface, and the peer that passed in Getfrompeer function is httpgetter, so we can look at the Get function of httpget this struct
Func (H *httpgetter) Get (context context, in *PB. Getrequest, out *PB. GetResponse) Error {
U: = fmt. Sprintf (
"%v%v/%v",
H.baseurl,
Url. Queryescape (in. Getgroup ()),
Url. Queryescape (in. GetKey ()),
)
Req, Err: = http. Newrequest ("GET", U, Nil)
If err! = Nil {
return err
}
TR: = http. Defaulttransport
If H.transport! = Nil {
TR = H.transport (context)
}
Res, err: = tr. Roundtrip (req)
If err! = Nil {
return err
}
Defer Res. Body.close ()
If Res. StatusCode! = http. Statusok {
Return to FMT. Errorf ("Server returned:%v", Res.) Status)
}
Bufferpool is an object pool of type Bytes.buffer
B: = Bufferpool.get (). (*bytes. Buffer)
B.reset ()
Defer Bufferpool.put (b)
_, err = Io. Copy (b, Res.) (Body)//Copy the acquired data to B
If err! = Nil {
Return to FMT. Errorf ("Reading response body:%v", err)
}
Err = Proto. Unmarshal (B.bytes (), out)//data present in Out
If err! = Nil {
Return to FMT. Errorf ("Decoding response body:%v", err)
}
return Nil
}
This function first initiates an HTTP request to the peer and then encapsulates the request to the out *PB. GetResponse, returned to Getfrompeer, and eventually returned to the client;
Summarize
This article is mainly to talk about my views on learning Golang, as well as the analysis of the implementation of the principle of groupcache, the analysis is not very fine, mainly on the framework of the analysis, the Groupcache has a holistic understanding, then to see the details of the section, will be much simpler.
Look at the Sqlmock Open source framework these days, the main function is to simulate the database operation in unit testing, the main principle is to implement a driver. In the process of looking at this sqlmock, we must first understand the Database/sql and go-sql-driver, know how the two work together, so as to understand the realization of sqlmock; a few days and then database/ The implementation principle of SQL and Go-sql-driver is sent out.