This is a creation in Article, where the information may have evolved or changed.
Original Address
Reprint please specify the original and translation address
In the second part, we will:
- Set up Go Project
- Write our first micro-service
- Using the gorilla component to provide a JSON response to an HTTP request
We start with a micro-service foundation and then build on our Docker swarm
Introduced
For internal or external requests, a JSON response over HTTP is not the only option. But here we will focus on this approach. When an internal request or external request is also a system, it is a good choice to use RPC and binary information formats as a request, such as protocol buffers. Go has built-in RPC support, Grpc worth studying. However, let's start with the built-in HTTP package and Gorilla Web Tookit.
Many useful frameworks (security, tracking) rely on HTTP header information to pass the state of the request, which is also the advantage of using HTTP. We will also see the relevant ID and OAuth header information in our blog post. While other protocols also support similar features, many frameworks are actually developed on HTTP, so I try to use them to make our integrations more straightforward.
Create a Go Project
If you are already familiar with go development, you can skip this paragraph.
I think go's work area needs a little time to get to know him. I am used to using the root directory of my project as the root directory of my workspace. But the way to organize a workspace is a bit odd, and it's about how the go compiler looks for source code and dependencies.
Recommended reading:
Official documents
Go path and workspace
Installing the SDK
Before we write the code, we need to install the go SDK. You can refer to the official documentation
1. Create a root directory
All commands are based on OS X or Linux development environment. If you are developing on Windows, please adjust these commands.
mkdir ~/goworkspacecd goworkspaceexport GOPATH=`pwd`
Here we create a root directory and let the environment variable Gopath point to this file. This root directory will contain all the go codes and third-party libraries. I suggest you add Gopath to the. Bash_profile, so you don't have to set it every time.
2. Create a folder and file for the first project
Now we are in the root directory and do the following:
mkdir -p src/github.com/callistaenterprise
If you want to follow the code yourself, do the following:
cd src/github.com/callistaenterprisemkdir -p goblog/accountservicecd goblog/accountservicetouch main.gomkdir service
Or you can clone this git repository directly and go to the P2 branch. In the folder Src/github.com/callistaenterprise, perform the
git clone https://github.com/callistaenterprise/goblog.gitcd gobloggit checkout P2
Now we can start.
Create service-main.go
The main function is the same as in other languages, the access point of the program. Let's write some code to run it.
package mainimport ( "fmt" ) var appName = "accountservice"func main() { fmt.Printf("Starting %v\n", appName)}
Now, run it. Make sure you are under this folder:
$GOPATH/src/github.com/callistaenterprise/goblog/accountservice
> go run *.goStarting accountservice>
Complete, this code is printed and then exited, now let's add the HTTP terminal
Create an HTTP Service
Make the project cleaner and we'll put all the HTTP services into the service folder
Start the HTTP service
Create a file Webserve.go in the/services folder
package serviceimport ( "net/http" "log")func StartWebServer(port string) { log.Println("Starting HTTP service at " + port) err := http.ListenAndServe(":" + port, nil) // Goroutine will block here if err != nil { log.Println("An error occured starting HTTP listener at port " + port) log.Println("Error: " + err.Error()) }}
We use the built-in Net/http package to execute the Listenandserve, which opens the HTTP service on the specified port.
Update Main.go use a write-dead port to request the Startwebserver function
package mainimport ( "fmt" "github.com/callistaenterprise/goblog/accountservice/service" // NEW)var appName = "accountservice"func main() { fmt.Printf("Starting %v\n", appName) service.StartWebServer("6767") // NEW}
To run the program:
> go run *.goStarting accountservice2017/01/30 19:36:00 Starting HTTP service at 6767
Now we have a simple HTTP service listening on port 6767, curl this service
curl http://localhost:6767404 page not found
404 is what we anticipate, because we haven't defined any routes
Stop this service-ctrl+c
Join the first route
We're going to start doing things, we define the first route. In the service folder, create the Routes.go
Package Serviceimport "Net/http"//defines a single route, e.g. a human readable name, HTTP method and the//PA Ttern the function that would execute when the route is Called.type route struct {Name string Method str ing Pattern string handlerfunc http. handlerfunc}//defines the type Routes which is just an array (slice) of the Route structs.type Routes []route//Initialize ou R Routesvar routes = routes{route{"Getaccount",//Name "GET", HTTP method "/accounts/{accountid}",//Route PA Ttern func (w http. Responsewriter, R *http. Request) {W.header (). Set ("Content-type", "Application/json; Charset=utf-8 ") W.write ([]byte (" {\ "result\": \ "Ok\"} ")},},}
Above the code, we define the path/accounts/{accountsid}, after which we can curl this path. Gorilla also supports complex routing including regular matching, schemes, methods, queries, header information values, and so on. So not limited to path and path parameters
Now we're going to return a JSON message:
{"result":"OK"}
We now need some boilerplate code to start the gorilla route. In the service folder, create the Router.go
package serviceimport ( "github.com/gorilla/mux")// Function that returns a pointer to a mux.Router we can use as a handler.func NewRouter() *mux.Router { // Create an instance of the Gorilla router router := mux.NewRouter().StrictSlash(true) // Iterate over the routes we declared in routes.go and attach them to the router instance for _, route := range routes { // Attach each route, uses a Builder-like pattern to set each route up. router.Methods(route.Method). Path(route.Pattern). Name(route.Name). Handler(route.HandlerFunc) } return router}
Introducing Dependencies
In import we define a dependency Github.com/gorilla/mux.
In order for the above files to compile and run, we need to use go get to get the defined package
> go get
The Go tool will download all the source code. The code is then stored in the local file of the $gopath/src/github.com/gorilla/mux. and compile the binary file into your static link.
Summary
Now, look again at Webserver.go and add the following code:
func StartWebServer(port string) { r := NewRouter() // NEW http.Handle("/", r) // NEW
This code is the route we just wrote to join HTTP. The handle. Let's Run the code:
> go run *.goStarting accountservice2017/01/31 15:15:57 Starting HTTP service at 6767
Curl, this address.
> curl http://localhost:6767/accounts/10000 {"result":"OK"}
Our first service is ready!
Resource consumption and efficiency
As we explore the resource consumption and efficiency of go-based microservices, we do a base test. I developed a simple Gatling test that would request Accounts/{accountid} with a Get method. If you check out this part of the code, you can see the test in the Goblog/loadtest folder.
Run Tests
If you want to test yourself, make sure that Accountservice is running in your localhost. And you have checked out branch P2. You need to have Jave runtime and Apache Maven.
Go to the folder/goblog/loadtest to execute the following command
> mvn gatling:execute -Dusers=1000 -Dduration=30 -DbaseUrl=http://localhost:6767
This will start the test, and the input parameter is
- User: Number of concurrent users simulated
- Duration: Number of seconds for test duration
- BASEURL: We test the underlying path of the service. When we use Docker swarm, BaseURL will become swarm's public IP.
At the end of the test, the results are written to terminal, and a nice HTML report is stored in the target/gatling/results/
Results
After that, our base test will be in Docker swarm, and now my 2014-year old Mac is still running on load.
The following is memory consumption, which is displayed in the Task Manager of OS X
1.2MB, it's not bad. Let's test 1K req/s with Gatling. Remember this is a very simple program that returns only hard code strings
The 1k req/s allows Accountservice to consume approximately 28MB of memory. This is still one-tenth when the spring boot app is turned on. It will be interesting to see how this number changes when we add some real processing functions into it.
Performance and CPU Usage
1k req/s consumes about 8% of the single core
It is unclear how Gatling handles millisecond latencies, but the average latency is 0ms. There is a request to use 11ms. Overall, accountservice performance is quite good, can handle 745 req/s.
Next..
In the next section, we want to make our accountserive do something useful. We want to add a simple database. We will also try JSON serialization. Also check how these affect consumption and performance.