An overview of the
In the previous article, Go-Kit Microservices: Service Registration and Discovery, arithmetic services were registered with Consul, and discovery services implemented the discovery of arithmetic services using the Go-Kit toolset. A look at the source code shows that there is only one interface /calculate processing logic implemented, but what if the arithmetic service has many interfaces?
These days, this problem has been bothering me. I think GO-Kit will provide a reasonable solution, but I haven’t found it yet. I still don’t understand the design of SD. Factory.
To solve this problem, I decided to take a different approach: dynamically query the registry service instance based on the client HTTP request, and invoke the backend service through a reverse proxy.
This is equivalent to implementing a simple gateway through which any compliant request can invoke the back-end service. The rule here refers to the HTTP request resource path, the rule is: /{serviceName}/#. That is, the first part of the path is the name of the registry service instance, and the REST part is the REST path of the service instance. Such as:
/arithmetic/calculate/Add/10/2
Copy the code
arithmetic
Is the service name;/calculate/Add/10/2
Interface for arithmetic services.
To implement the gateway
Step-1: Implementation idea
The client initiates a request to the gateway, which parses the information in the resource path of the request, queries the service instance of the registry according to the service name, and then forwards the client request to the real service instance of the back end using the reverse proxy technology. After the request is executed, the response information is returned to the client.
- Rules for HTTP requests follow:
/{serviceName}/#
Otherwise, it will not be approved. - Use the reverse proxy package provided by Golang
httputil.ReverseProxy
Implement a simple reverse proxy that can load balance requests and randomly send them to service instances. - Use consul client API to dynamically query service instances.
Step-2: Write the reverse proxy method
Create the directory gateway under arithmetic_consul_demo and create the go file main.go. The NewReverseProxy method takes two parameters: consul client object and logging tool and returns the reverse proxy object. The implementation process of this method is described as follows:
- Obtain the request path and check whether the request path meets the rules. If the request path does not meet the rules, the system returns the request path.
- Parse the request path to get the service name (the first part is the service name);
- Consul client is used to query a service instance. If a query result is found, select a random instance as the target instance.
- Set the reverse proxy parameters based on the selected target instance:
Schema
,Host
,Path
.
The complete code is as follows:
Func NewReverseProxy(client * api.client, Logger log.logger) *httputil.ReverseProxy {// Create Director Director := func(req * http.request) {// Query the original Request path, for example: /arithmetic/calculate/10/5 reqPath := req.URL.Pathif reqPath == "" {
return} // follow the delimiter'/'ServiceName pathArray := strings.Split(reqPath,"/") serviceName := pathArray[1] // Call Consul API to query serviceName Service instance list Result, _, err := client.catalog ().service (serviceName,"", nil)
iferr ! = nil { logger.Log("ReverseProxy failed"."query service instace error", err.Error())
return
}
if len(result) == 0 {
logger.Log("ReverseProxy failed"."no such service instance", serviceName)
returnDestPath := strings.Join(pathArray[2:],"/"TGT := result[rand.int ()%len(result)] logger.log ()"service id", tgt.serviceId) // Set the proxy service address information req.url.scheme ="http"
req.URL.Host = fmt.Sprintf("%s:%d", tgt.ServiceAddress, tgt.ServicePort)
req.URL.Path = "/" + destPath
}
return &httputil.ReverseProxy{Director: director}
}
Copy the code
Step-3: Write the main method
The main method creates a Consul connection object, creates a log object, and enables the reverse proxy HTTP service. The entire process is similar to the previous examples, Posting code directly (I have specified consul address information directly for testing purposes) :
func mainVar (consulHost = flag.string () {consulHost = consulHost ();"consul.host"."192.168.192.146"."consul server ip address")
consulPort = flag.String("consul.port"."8500"."consul server port"Var logger log. logger {logger = log.newLogfmtLogger (os.stderr) logger = log.With(logger,"ts", log.DefaultTimestampUTC)
logger = log.With(logger, "caller"// Create consul API client consulConfig := api.defaultconfig () consulconfig.address ="http://" + *consulHost + ":" + *consulPort
consulClient, err := api.NewClient(consulConfig)
iferr ! = nil { logger.Log("err"Err := NewReverseProxy(consulClient, logger) errc := make(chan error) gofunc() {
c := make(chan os.Signal)
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
errc <- fmt.Errorf("%s"<-c)}() // Start listening on gofunc() {
logger.Log("transport"."HTTP"."addr"."9090")
errc <- http.ListenAndServe(": 9090", proxy)}() // start running, wait for end logger.log ()"exit", <-errc)
}
Copy the code
Step 4: run
- Launch Consul using Docker. Terminal switch to
arithmetic_consul_demo
Directory, run the following command:
sudo docker-compose -f docker/docker-compose.yml up
Copy the code
- Start the arithmetic operation service. To test the load balancing effect, I started two instances. Note Different ports must be used
/register/ register-consul. host localhost-consul. port 8500-service. host 192.168.192.146 -service.port 9000 Host localhost-consul. port 8500-service. host 192.168.192.146-service. port 9002Copy the code
- Type in the browser
http://localhost:8500
You can seearithmetic
There are two examples, as shown below:
- CD to directory in terminal
gateway
, the implementation ofgo build
Complete the compilation and start the gateway service.
>./ gateway-consul. host localhost-consul. port 8500 > ts= 2019-02-26t07:49:39.0468058zcaller=main.go:54 transport=HTTP addr=9090
Copy the code
Step 5: test
Using Postman to perform the request test as shown below, the service invocation is successful.
At the same time, the following output is displayed on the terminal, indicating that multiple requests have accessed different service instances:
Ts = 2019-02-26 T07:49:39. 0468058 zcaller= main. Go: 54 transport addr = 9090 ts = 2019-02-26 = HTTP T07:49:46. 8559985 zcaller=main.go:94 serviceid=arithmetic82460623-ccdc-4192-a042 - c0603ef18888 ts = 2019-02-26 T07:50:00. 1249302 zcaller=main.go:94 serviceid=arithmetic65153818-27b3-4f19-8fd1-d7 d698168f20 ts = 2019-02-26 T09:04:09. 0470362 zcaller=main.go:94 serviceid=arithmetic65153818-27b3-4f19-8fd1-d7 d698168f20 ts = 2019-02-26 T09:04:10. 176327 zcaller=main.go:94 serviceid=arithmetic65153818-27b3-4f19-8fd1-d7d698168f20
Copy the code
conclusion
This article implements a simple API gateway using reverse proxy technology in conjunction with registry CONSUL. The reverse proxy toolkit provided by Golang makes the whole implementation process relatively simple. The products used in actual projects, such as Zuul and Nginx, also include traffic limiting, request filtering, and identity authentication. The gateway only implements the proxy of the request, with the emphasis on understanding its internal process and deepening understanding.
In this paper, the reference
- Principle and implementation of HTTP proxy
- Golang ReverseProxy
- This article source @github
This article is first published in my wechat public account [Xi Yi Ang bar], welcome to scan code attention!