I want to stop the http server remotely (which I can do), but I also want to receive a message indicating that it has been stopped after it has been stopped. That is causing me some problems. The only solution that I could find is as shown below, which I do not consider ideal.
Can anyone provide a better solution. The problem is that the final message sent to the client is not getting through unless I use the goroutine as shown at the end "go func() {".
Code is as follows:
//*************
func stopServer(ohtWriter http.ResponseWriter, phtRequest *http.Request) {// Stop The Server
//*************
var iBytesSent int
var oOsError os.Error
var sErmes string
println("Stopping Server")
iBytesSent,oOsError = ohtWriter.Write([]byte("Message from server - server now stopped."))
if oOsError != nil {
sErmes = ". Error = " +oOsError.String()
} else {
sErmes = ". No error on write"
}
println("stopServer: Bytes sent = " +strconv.Itoa(iBytesSent) +sErmes)
ohtFlusher, tCanFlush := ohtWriter.(http.Flusher)
if tCanFlush {
ohtFlusher.Flush()
}
go func() {
time.Sleep(3e9)
os.Exit(0)
}()
}
Yeah I think without support from the http package, a graceful shutdown is not really possible. This is maybe a little bit less cringe-worthy, but will still slam closed any other concurrent requests in flight at the time of this request. Maybe try filing a feature request on the Go issue tracker. Better yet, open up the http package, and add a graceful shutdown method, and submit it.
Edit: I guess if you control all of the http.Handlers in your app, you could keep a count of in-flight requests (using appropriate thread synchronization), and modify the code below to a) refuse new connections once "shutdown" is called and b) wait for all in-flight requests to complete before shutting down...
package main
import (
"http"
"os"
"io"
"log"
"strconv"
)
func main() {
http.HandleFunc("/", ServeHTTP)
http.ListenAndServe(":8081", nil)
}
const responseString = "Shutting down\n"
func ServeHTTP(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.Header().Set("Content-Length", strconv.Itoa(len(responseString)))
io.WriteString(w, responseString)
f, canFlush := w.(http.Flusher)
if canFlush {
f.Flush()
}
conn, _, err := w.(http.Hijacker).Hijack()
if err != nil {
log.Fatalf("error while shutting down: %v", err)
}
conn.Close()
log.Println("Shutting down")
os.Exit(0)
}
Haven't tried it yet, but using http.ServerConn directly might work.
Here's a simple way that's good enough for local development.
http://www.sergiotapia.me/how-to-stop-your-go-http-server/
package main
import (
"net/http"
"os"
"github.com/bmizerany/pat"
)
var mux = pat.New()
func main() {
mux.Get("/kill", http.HandlerFunc(kill))
http.Handle("/", mux)
http.ListenAndServe(":8080", nil)
}
func kill(w http.ResponseWriter, r *http.Request) {
os.Exit(0)
}
Related
I am developing an application in Go which uses the net/http package to make a number of external http requests/calls which I would like to monitor the http calls' latency, response times, status code etc.
As there are a number of endpoints which my applications calls, and there could be more that will be added in the future, I would like to consider a solution which will obtain the metrics for all such external http calls.
Any suggestions, guidance or examples on this is much appreciated.
I am thinking of some way in which a middleware/wrapper is added onto the Do/Raw call within the net/http package which I can create and modify a copy of but I'm not exactly sure how to do that.
Here's a very (!) basic example that abstracts http.Client's Do method with a Prometheus Counter that Inc's on every Do and labels the Counter with the host's name, the request method and the response code:
package main
import (
"log"
"net/http"
"strconv"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
type Client struct {
Client *http.Client
Counter *prometheus.CounterVec
}
func NewClient(counter *prometheus.CounterVec) *Client {
return &Client{
Client: &http.Client{},
Counter: counter,
}
}
func (c *Client) Do(rqst *http.Request) (*http.Response, error) {
resp, err := c.Client.Do(rqst)
host := rqst.URL.Host
code := strconv.Itoa(resp.StatusCode)
c.Counter.With(prometheus.Labels{
"host": host,
"response_code": code,
}).Inc()
return resp, err
}
func main() {
metric := prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "foo",
Help: "Number of HTTP requests.",
},
[]string{
"host",
"response_code",
},
)
prometheus.MustRegister(metric)
c := NewClient(metric)
go func() {
for {
method := http.MethodPost
url := "https://httpbin.org/post"
rqst, _ := http.NewRequest(method, url, nil)
_, _ = c.Do(rqst)
time.Sleep(15 * time.Second)
}
}()
log.Println("Starting server")
http.Handle("/metrics", promhttp.Handler())
log.Fatal(http.ListenAndServe(":8080", nil))
}
And:
# HELP foo Number of HTTP requests.
# TYPE foo counter
foo{host="httpbin.org",method="POST",response_code="200"} 6```
I'm trying to access the HackerNews API to practice Go.
Anytime I go to my localhost to try see an output from Firebase database (where the data is stored) I am met with a Google Accounts authentication form.
Any help on this would be appreciated. In my terminal I used curl to check if I was getting a response from the server. I got a 200 OK response with content.
I thought I might be missing a Firebase client library but I'm not sure if that is the issue right now.
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
response, err := http.Get("https://hacker-news.firebaseio.com/v0/item/8863.json")
if err != nil {
fmt.Printf("The http request failed with the error %s\n", err)
} else {
data, _ := ioutil.ReadAll(response.Body)
fmt.Fprintf(w, string(data))
}
}
func main() {
fmt.Println("Starting the applicaiton")
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
Aren't you suppose to add .json to URL (via https://github.com/HackerNews/API)?
e.g https://hacker-news.firebaseio.com/v0/item/8863.json
I am trying to turn off handling GET requests in golang.
I just want to handle POST.
Is it possible to do?
Reason for doing so is that i can see more and more memory being allocated by golang whenever i go to localhost:8080 and refresh page multiple times.
Here is my test code:
package main
import (
"fmt"
"net/http"
"encoding/json"
)
type test_struct struct {
Test string
}
var t test_struct
func handlePOST(rw http.ResponseWriter, req *http.Request) {
switch req.Method {
case "POST":
decoder := json.NewDecoder(req.Body)
decoder.Decode(&t)
defer req.Body.Close()
fmt.Println(t.Test)
}
}
func main() {
http.HandleFunc("/", handlePOST)
http.ListenAndServe(":8080", nil)
}
You cannot not handle GET requests, Go's HTTP server (or rather its http.ServeMux) only allows you to specify a path pattern before dispatching the request to your handler. HTTP method related routing can only happen at the handler level.
Note that some external mux libraries allow you to register handlers to specific HTTP methods only, but the decision and routing based on that also happens in "hidden" handlers of those libraries.
What you're doing is the best: simply do nothing in the handler if the HTTP method is not the one you intend to handle, or even better: send back a http.StatusMethodNotAllowed error response:
func myHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Only POST is allowed", http.StatusMethodNotAllowed)
return
}
var t test_struct // Use local var not global, else it's a data race
decoder := json.NewDecoder(r.Body)
if err := decoder.Decode(&t); err != nil {
fmt.Println("Error decoding:", err)
}
fmt.Println(t.Test)
}
Is there any way to print something when the http server starts? For instance "Server is started at port 8080"
In Node (using Express), it would be like:
app.listen(8080, function() { console.log('Server started at port 8080') });
This is my code:
func main() {
http.HandleFunc("/", MyHandler)
http.ListenAndServe(":8080", nil)
}
Thanks.
Use Go's log package:
package main
import (
"net/http"
"log"
)
func main() {
addr := ":8080"
http.HandleFunc("/", MyHandler)
log.Println("listen on", addr)
log.Fatal( http.ListenAndServe(addr, nil) )
}
http.ListenAndServe opens the server port, and blocks forever waiting for clients. If it fails to open the port, the log.Fatal call will report the problem and exit the program.
You can't print a log message after ListenAndServe since it blocks and never returns, so basically you have two main options:
Print "Starting server on port...." and that's it - BUT if ListenAndServe could not start it returns an error, so unless there's some error or panic printed because of that, you can assume the server started.
Call ListenAndServe in a separate goroutine, and make sure there was no error returned and print "Server started..." etc.
I personally prefer the first approach.
To run ListenAndServe in a goroutine as mentioned by Not_a_Golfer, you can use an unbuffered, blocking channel to run it in a goroutine and also keep the server alive.
The following example creates a channel called done where <-done will keep the server alive as it waits for the goroutine to finish, which it won't in this case. Typically, the goroutine will tell the main function it is finished by executing done <- true.
package main
import (
"log"
"net/http"
)
func MyHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello World"))
}
func main() {
port := "8080"
http.HandleFunc("/", MyHandler)
done := make(chan bool)
go http.ListenAndServe(":"+port, nil)
log.Printf("Server started at port %v", port)
<-done
}
Here's a larger example that has the server verify it is operational, using Listen and Serve separately. The nice thing about doing it this way is you can capture an incorrect port easily.
package main
import (
"log"
"net"
"net/http"
"os"
)
func MyHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello World"))
}
func main() {
port := "8080"
http.HandleFunc("/", MyHandler)
listener, err := net.Listen("tcp", ":"+port)
if err != nil {
log.Fatal(err)
}
done := make(chan bool)
go http.Serve(listener, nil)
// Log server started
log.Printf("Server started at port %v", port)
// Attempt to connect
log.Printf("Fetching...")
res, err := http.Get("http://" + listener.Addr().String())
log.Printf("Received: %v, %v", res, err)
if err != nil {
log.Fatal(err)
}
res.Write(os.Stdout)
<-done
}
As i learned from golang docs, if i set runtime.GOMAXPROCS(8) with a cpu of 8 cores (intel i7), then start a goroutine of infinite-loop, other gorutines should not be blocked because there are engough threads and goprocs. But this is not true when using net/http package, an infinite-loop goroutine will block http server after a few invocations.
Can anyone help to explain why ?
If i comment the line of "go infinite loop", start client after server, client will output 1000 asterisks; but if i enable the goroutine, client will block after print a few asterisks
I have tried add runtime.LockOSThread() in the goroutine, it seems that doesn't work
My Environment: osx 10.10, go version go1.3.1 darwin/amd64
Server code:
package main
import (
"fmt"
"log"
"net/http"
"runtime"
)
func myHandler(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("hello"))
}
func infiniteloop() {
for {
}
}
func main() {
// set max procs for multi-thread executing
runtime.GOMAXPROCS(runtime.NumCPU())
// print GOMAXPROCS=8 on my computer
fmt.Println("GOMAXPROCS=", runtime.GOMAXPROCS(-1))
http.Handle("/", http.HandlerFunc(myHandler))
// uncomment below line cause server block after some requests
// go infiniteloop()
if err := http.ListenAndServe(":8280", nil); err != nil {
log.Fatal(err)
}
}
Client code:
package main
import (
"fmt"
"net/http"
)
func getOnce() {
if resp, err := http.Get("http://localhost:8280"); err != nil {
fmt.Println(err)
return
} else {
defer func() {
if err := resp.Body.Close(); err != nil {
fmt.Println(err)
}
}()
if resp.StatusCode != 200 {
fmt.Println("error codde:", resp.StatusCode)
return
} else {
fmt.Print("*")
}
}
}
func main() {
for i := 1; i < 1000; i++ {
getOnce()
if i%50 == 0 {
fmt.Println()
}
}
}
Now i know why such emtpy loop block other goroutines, but why runtime.LockOSThread() doesn't help either?
func infiniteloop() {
// add LockOSThread will not help
runtime.LockOSThread()
for {
}
}
As http://golang.org/pkg/runtime/#LockOSThread mentioned, the empty loop should be executed in an standalone thread, and other goroutines should not be impacted by the busy loop. What's wrong in my understanding?
The Go runtime's scheduler is not fully pre-emptive at this time. Go 1.2 improved matters by occasionally calling into the scheduler on function calls, but the infinite loops in your example have no function calls so this doesn't help.
With an actual body to your infinite loop handlers, you may see better behaviour. Alternatively, a manual call to runtime.Gosched may help in cases like this.
The scheduler might not be able to preempt such an empty "infinite" loop. The scheduler got better and better during the last release, maybe he should be good enough for such code; he definitely is good enough for real code. Just don't do such nonsense.