I'm using a channel to pass messages from an HTTP handler:
package server
import (
"bytes"
"errors"
"io/ioutil"
"log"
"net/http"
)
type Server struct {}
func (s Server) Listen() chan interface{} {
ch := make(chan interface{})
http.HandleFunc("/", handle(ch))
go http.ListenAndServe(":8080", nil)
return ch
}
func handle(ch chan interface{}) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
b, err := ioutil.ReadAll(r.Body)
defer r.Body.Close()
if err != nil {
ch <- errors.New(string(500))
return
}
w.Write([]byte("Hello World"))
log.Print("about to pass to handler channel")
ch <- bytes.NewBuffer(b)
log.Print("passed to handler channel")
}
}
When I make a request to the server running on port 8080, the thread blocks on this line:
ch <- bytes.NewBuffer(b)
Why is this happening? If you notice, I'm running the listener in a goroutine. I also figured that HTTP handles happen in a separate thread. If I delete the above line, the thread becomes unblocked and the program works as expected. What am I doing wrong?
To clarify, I want to be able to pass the body of a POST request to a channel. Help.
UPDATE:
I'm reading from the channel on the main thread:
listenerChan := n.Listen()
go SendRequest("POST", "http://localhost:8080", []byte("hello"))
for listenedMsg := range listenerChan {
log.Print("listened message>>>> ", listenedMsg)
}
But the thread still blocks on the same line. For clarification, there is nothing wrong with how im sending the request. If I remove the channel send line above, the thread doesnt block.
Because the channel is unbuffered, the send operation blocks until there's someone who is ready to receive from them. Making the channel buffered will only defer the blocking, so you always need some reading goroutine.
Update to your update: the control flow of the program would go like this:
Server starts listening
main sends the request and waits for the response
Server receives the request and tries to write to the channel
main reads from the channel
4 may happen only after 2, which is blocked by 3 which is blocked because 4 is not happening yet. A classical deadlock.
I think #bereal gave a good explanation about using an unbuffered or synchronous channel.
Another way to make things work is to make the channel buffered by changing the line that creates the channel to:
ch := make(chan interface{}, 1) // added the 1
This will prevent the function from being blocked.
I added missing parts in your code and run it, everything works well. I don't see any block. Here's the code:
package main
import (
"bytes"
"errors"
"io/ioutil"
"log"
"net/http"
"time"
)
type Server struct{}
func (s *Server) Listen() chan interface{} {
ch := make(chan interface{})
http.HandleFunc("/", handle(ch))
go http.ListenAndServe(":8080", nil)
return ch
}
func handle(ch chan interface{}) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
b, err := ioutil.ReadAll(r.Body)
defer r.Body.Close()
if err != nil {
ch <- errors.New(string(500))
return
}
w.Write([]byte("Hello World"))
log.Print("about to pass to handler channel")
ch <- bytes.NewBuffer(b)
log.Print("passed to handler channel")
}
}
// SendRequest send request
func SendRequest(method string, url string, data []byte) {
tr := &http.Transport{
MaxIdleConns: 10,
IdleConnTimeout: 30 * time.Second,
DisableCompression: true,
}
client := &http.Client{Transport: tr}
reader := bytes.NewReader(data)
req, err := http.NewRequest(method, url, reader)
if err != nil {
panic(err)
}
client.Do(req)
}
func main() {
n := new(Server)
listenerChan := n.Listen()
go SendRequest("POST", "http://localhost:8080", []byte("hello"))
for listenedMsg := range listenerChan {
log.Print("listened message>>>> ", listenedMsg)
}
}
And the output are:
2018/06/28 17:22:10 about to pass to handler channel
2018/06/28 17:22:10 passed to handler channel
2018/06/28 17:22:10 listened message>>>> hello
Related
I get this error, regardless of which way round I set the content type and write the status code. I can't really figure out why...
It seems to me like a very mundane task - I just want to set the Content-Type and the http status code. The server does actually work, it serves the web page just fine, but it logs that message to the terminal every time I request that endpoint/path.
Error
http: superfluous response.WriteHeader call from main.indexHandler (server.go:49)
Code
package main
import (
"context"
"log"
"net/http"
"os"
"os/signal"
"time"
"github.com/gorilla/mux"
)
func main() {
r := mux.NewRouter()
fs := http.FileServer(http.Dir("./static"))
r.PathPrefix("/assets/").Handler(http.StripPrefix("/assets/", fs))
r.HandleFunc("/", indexHandler).Methods("GET")
server := &http.Server{
Addr: "0.0.0.0:8080",
Handler: r,
}
go func() {
if err := server.ListenAndServe(); err != nil {
log.Fatal("Unable to start the server")
}
}()
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
<-c
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
log.Fatal("Unable to gracefully shutdown the server")
}
}
func indexHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html")
w.WriteHeader(http.StatusOK)
http.ServeFile(w, r, "./static/index.html")
}
In
func indexHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html")
w.WriteHeader(http.StatusOK)
http.ServeFile(w, r, "./static/index.html")
}
You don't need the w.WriteHeader call. http.ServeFile will set the status based on its success in serving the file.
If you want to understand how this error message comes to be, look at the implementation of the WriteHeader method and what it does if the header is already written.
If I use a chan anywhere on main or func home, the application runs, but it doesn't really work. No errors thrown, however, it won't work.
If I remove the channels references, it goes back to working.
Either by using a chan in a struct or a global channel the application stops working.
In a GET request, it returns h.Message from func home
by adding any channel in the code, GET request wont return the message.
https://play.golang.org/p/-ZVcLhZRRRG
package main
import (
"fmt"
"net/http"
"github.com/gorilla/websocket"
// _ "github.com/go-sql-driver/mysql"
)
type WMessage struct {
Message string `json:"message"`
ch chan string
}
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool {
return true
},
}
var Chann chan string
func (h *WMessage) home(w http.ResponseWriter, r *http.Request) {
h.Message = "hey this is the message from home"
fmt.Fprintln(w, h.Message)
fmt.Println(<-h.ch)
}
func main() {
hom := &WMessage{}
hom.ch = make(chan string)
hom.ch <- "message sent"
http.HandleFunc("/", hom.home)
err := http.ListenAndServe(":3000", nil)
if err != nil {
fmt.Println("there was an error -> ", err)
}
}
Unbuffered channels are blocking in nature, i.e. writing to channel (hom.ch <- "message sent") and reading from channel (fmt.Println(<-h.ch)) block the go-routine.
In your case, http server is not running because hom.ch <- "message sent" blocks the execution. That's why GET request is not working.
One simple solution can be to make it buffered channel instead.
I'm writing a TCP Server and Client in Go, just as a working example to get familiar with this language. I want to write a server, let's call it MyServer, which does the following - it has a backend TCP Server, which listens for incoming messages, but it also has a Client which allows him to send other messages, independently on the received once. However, I don't know how to tell MyServer to listen "in the background", without blocking the main thread. Here is the code for my TCPServer:
package main
import (
"fmt"
"net"
"os"
)
func main() {
startListener()
doOtherThins()
}
func startListener() {
listener, err := net.Listen("tcp", "localhost:9998")
if err != nil {
fmt.Println("Error listening:", err.Error())
os.Exit(1)
}
defer listener.Close()
fmt.Println("Listening on " + "localhost:9998")
for {
// Listen for an incoming connection.
conn, err := listener.Accept()
if err != nil {
fmt.Println("Error during accepting: ", err.Error())
os.Exit(1)
}
go handleConnection(conn)
}
}
func handleConnection(conn net.Conn) {
buf := make([]byte, 1024)
_, err := conn.Read(buf)
if err != nil {
fmt.Println("Error reading:", err.Error())
}
conn.Write([]byte("Message correctly received."))
conn.Close()
}
Function startListener() blocks the main function, so the function doOtherThins() (which I want to independently send packets to other servers) is never triggered as long as the server is listening. I tried to change the main function and use the goroutine:
func main() {
go startListener()
doOtherThins()
}
But then the server is not listening for the incoming packets (it just triggers doOtherThins() and ends main()).
Is it possible to spin the listener in the background, to allow the main thread do also other operations?
Your last example should do what you want, the issue is that the main thread ends before you can do anything. There's 2 solutions start doOtherThins() on another goroutine and then call startListener() which blocks but the other goroutine is already running:
func main() {
go doOtherThins()
startListener()
}
Or use waitGroups to wait until the code ends before exiting.
Here is a cleaner way of achieving this using channels.
package main
import (
"net/http"
"fmt"
)
func main() {
// Create a channel to synchronize goroutines
finish := make(chan bool)
server8001 := http.NewServeMux()
server8001.HandleFunc("/foo", foo8001)
server8001.HandleFunc("/bar", bar8001)
go func() {
http.ListenAndServe(":8001", server8001)
}()
go func() {
//do other things in a separate routine
fmt.Println("doing some work")
// you can also start a new server on a different port here
}()
// do other things in the main routine
<-finish //wait for all the routines to finish
}
func foo8001(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Listening on 8001: foo "))
}
func bar8001(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Listening on 8001: bar "))
}
adding another variation of Anuruddha answer
package main
import (
"io"
"net/http"
"os"
"time"
)
func main() {
server8001 := http.NewServeMux()
server8001.HandleFunc("/foo", foo8001)
server8001.HandleFunc("/bar", bar8001)
unblock(func() error {
return http.ListenAndServe(":8001", server8001)
})//forgot err check, must be done!
res, err := http.Get("http://0.0.0.0:8001/foo")
if err != nil {
panic(err)
}
defer res.Body.Close()
io.Copy(os.Stdout, res.Body)
os.Exit(0)
}
func unblock(h func() error) error {
w := make(chan error)
go func() {
w <- h()
}()
select {
case err := <-w:
return err
case <-time.After(time.Millisecond * 50):
return nil
}
}
func foo8001(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Listening on 8001: foo "))
}
func bar8001(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Listening on 8001: bar "))
}
I'm building a server in go using gorilla multiplexer library found in https://github.com/gorilla/mux.
The problem is, I want it to gracefully shutdown when I'm using Ctrl+C, or when there is a specific API call, for example "/shutdown".
I already know that in Go 1.8, graceful shutdown is already implemented. But how to combine it with gorilla multiplexer? Also, how to combine it with SIGINT signal?
Can anyone show me how to do it?
Channel can be used to capture shutdown request through API call (/shutdown) or interrupt signal (Ctrl+C).
Embed http.Server into a custom struct, so we can call http Server.Shutdown later
Add channel field (shutdownReq) for passing shutdown request from API call /shutdown
Register http handlers including /shutdown in gorilla/mux's router, then assign the router to http.Server.Handler
Register os.Interrupt/syscall.SIGINT, syscall.SIGTERM handler
Use select to capture shutdown request through API call or interrupt signal
Perform clean shutdown by calling Server.Shutdown
Below is the example code:
package main
import (
"context"
"log"
"net/http"
"sync/atomic"
"syscall"
"time"
"os"
"os/signal"
"github.com/gorilla/mux"
)
type myServer struct {
http.Server
shutdownReq chan bool
reqCount uint32
}
func NewServer() *myServer {
//create server
s := &myServer{
Server: http.Server{
Addr: ":8080",
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
},
shutdownReq: make(chan bool),
}
router := mux.NewRouter()
//register handlers
router.HandleFunc("/", s.RootHandler)
router.HandleFunc("/shutdown", s.ShutdownHandler)
//set http server handler
s.Handler = router
return s
}
func (s *myServer) WaitShutdown() {
irqSig := make(chan os.Signal, 1)
signal.Notify(irqSig, syscall.SIGINT, syscall.SIGTERM)
//Wait interrupt or shutdown request through /shutdown
select {
case sig := <-irqSig:
log.Printf("Shutdown request (signal: %v)", sig)
case sig := <-s.shutdownReq:
log.Printf("Shutdown request (/shutdown %v)", sig)
}
log.Printf("Stoping http server ...")
//Create shutdown context with 10 second timeout
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
//shutdown the server
err := s.Shutdown(ctx)
if err != nil {
log.Printf("Shutdown request error: %v", err)
}
}
func (s *myServer) RootHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello Gorilla MUX!\n"))
}
func (s *myServer) ShutdownHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Shutdown server"))
//Do nothing if shutdown request already issued
//if s.reqCount == 0 then set to 1, return true otherwise false
if !atomic.CompareAndSwapUint32(&s.reqCount, 0, 1) {
log.Printf("Shutdown through API call in progress...")
return
}
go func() {
s.shutdownReq <- true
}()
}
func main() {
//Start the server
server := NewServer()
done := make(chan bool)
go func() {
err := server.ListenAndServe()
if err != nil {
log.Printf("Listen and serve: %v", err)
}
done <- true
}()
//wait shutdown
server.WaitShutdown()
<-done
log.Printf("DONE!")
}
Note: Please watch this issue which is related to gracefull shutdown.
I am using the Mux library from Gorilla Web Toolkit along with the bundled Go http server.
The problem is that in my application the HTTP server is only one component and it is required to stop and start at my discretion.
When I call http.ListenAndServe(fmt.Sprintf(":%d", service.Port()), service.router) it blocks and I cannot seem to stop the server from running.
I am aware this has been a problem in the past, is that still the case? Are there any new solutions?
Regarding graceful shutdown (introduced in Go 1.8), a bit more concrete example:
package main
import (
"context"
"io"
"log"
"net/http"
"sync"
"time"
)
func startHttpServer(wg *sync.WaitGroup) *http.Server {
srv := &http.Server{Addr: ":8080"}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "hello world\n")
})
go func() {
defer wg.Done() // let main know we are done cleaning up
// always returns error. ErrServerClosed on graceful close
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
// unexpected error. port in use?
log.Fatalf("ListenAndServe(): %v", err)
}
}()
// returning reference so caller can call Shutdown()
return srv
}
func main() {
log.Printf("main: starting HTTP server")
httpServerExitDone := &sync.WaitGroup{}
httpServerExitDone.Add(1)
srv := startHttpServer(httpServerExitDone)
log.Printf("main: serving for 10 seconds")
time.Sleep(10 * time.Second)
log.Printf("main: stopping HTTP server")
// now close the server gracefully ("shutdown")
// timeout could be given with a proper context
// (in real world you shouldn't use TODO()).
if err := srv.Shutdown(context.TODO()); err != nil {
panic(err) // failure/timeout shutting down the server gracefully
}
// wait for goroutine started in startHttpServer() to stop
httpServerExitDone.Wait()
log.Printf("main: done. exiting")
}
As mentioned in yo.ian.g's answer. Go 1.8 has included this functionality in the standard lib.
Minimal example for for Go 1.8+:
server := &http.Server{Addr: ":8080", Handler: handler}
go func() {
if err := server.ListenAndServe(); err != nil {
// handle err
}
}()
// Setting up signal capturing
stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt)
// Waiting for SIGINT (kill -2)
<-stop
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
// handle err
}
// Wait for ListenAndServe goroutine to close.
You can kill the server gracefully using kill -2 <pid>
Original Answer - Pre Go 1.8 :
Building on Uvelichitel's answer.
You can create your own version of ListenAndServe which returns an io.Closer and does not block.
func ListenAndServeWithClose(addr string, handler http.Handler) (io.Closer,error) {
var (
listener net.Listener
srvCloser io.Closer
err error
)
srv := &http.Server{Addr: addr, Handler: handler}
if addr == "" {
addr = ":http"
}
listener, err = net.Listen("tcp", addr)
if err != nil {
return nil, err
}
go func() {
err := srv.Serve(tcpKeepAliveListener{listener.(*net.TCPListener)})
if err != nil {
log.Println("HTTP Server Error - ", err)
}
}()
srvCloser = listener
return srvCloser, nil
}
Full code available here.
The HTTP Server will close with the error
accept tcp [::]:8080: use of closed network connection
Go 1.8 will include graceful and forceful shutdown, available via Server::Shutdown(context.Context) and Server::Close() respectively.
go func() {
httpError := srv.ListenAndServe(address, handler)
if httpError != nil {
log.Println("While serving HTTP: ", httpError)
}
}()
srv.Shutdown(context)
The relevant commit can be found here
You can construct net.Listener
l, err := net.Listen("tcp", fmt.Sprintf(":%d", service.Port()))
if err != nil {
log.Fatal(err)
}
which you can Close()
go func(){
//...
l.Close()
}()
and http.Serve() on it
http.Serve(l, service.router)
Since none of the previous answers say why you can't do it if you use http.ListenAndServe(), I went into the v1.8 http source code and here is what it says:
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
As you can see the http.ListenAndServe function does not return the server variable. This means you cannot get to 'server' to use the Shutdown command. Therefore, you need to create your own 'server' instance instead of using this function for the graceful shutdown to be implemented.
You can close the server by closing its context.
type ServeReqs func(ctx context.Context, cfg Config, deps ReqHandlersDependencies) error
var ServeReqsImpl = func(ctx context.Context, cfg Config, deps ReqHandlersDependencies) error {
http.Handle(pingRoute, decorateHttpRes(pingHandlerImpl(deps.pingRouteResponseMessage), addJsonHeader()))
server := &http.Server{Addr: fmt.Sprintf(":%d", cfg.port), Handler: nil}
go func() {
<-ctx.Done()
fmt.Println("Shutting down the HTTP server...")
server.Shutdown(ctx)
}()
err := server.ListenAndServeTLS(
cfg.certificatePemFilePath,
cfg.certificatePemPrivKeyFilePath,
)
// Shutting down the server is not something bad ffs Go...
if err == http.ErrServerClosed {
return nil
}
return err
}
And whenever you are ready to close it, call:
ctx, closeServer := context.WithCancel(context.Background())
err := ServeReqs(ctx, etc)
closeServer()
It is possible to solve this using a context.Context using a net.ListenConfig. In my case, I didn't want to use a sync.WaitGroup or http.Server's Shutdown() call, and instead rely on a context.Context (which was closed with a signal).
import (
"context"
"http"
"net"
"net/http/pprof"
)
func myListen(ctx context.Context, cancel context.CancelFunc) error {
lc := net.ListenConfig{}
ln, err := lc.Listen(ctx, "tcp4", "127.0.0.1:6060")
if err != nil {
// wrap the err or log why the listen failed
return err
}
mux := http.NewServeMux()
mux.Handle("/debug/pprof/", pprof.Index)
mux.Handle("/debug/pprof/cmdline", pprof.CmdLine)
mux.Handle("/debug/pprof/profile", pprof.Profile)
mux.Handle("/debug/pprof/symbol", pprof.Symbol)
mux.Handle("/debug/pprof/trace", pprof.Trace)
go func() {
if err := http.Serve(l, mux); err != nil {
cancel()
// log why we shut down the context
return err
}
}()
// If you want something semi-synchronous, sleep here for a fraction of a second
return nil
}
Reproducible example when you do not want your main server to be run in a separate goroutine:
main.go:
package main
import (
"context"
"log"
"net/http"
"os"
"os/signal"
"sync"
"time"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) {
// wait for 10 seconds before sending OK
time.Sleep(10 * time.Second)
_, _ = w.Write([]byte("OK\n"))
})
server := &http.Server{Addr: ":3333", Handler: nil}
// Creating a waiting group that waits until the graceful shutdown procedure is done
var wg sync.WaitGroup
wg.Add(1)
// This goroutine is running in parallels to the main one
go func() {
// creating a channel to listen for signals, like SIGINT
stop := make(chan os.Signal, 1)
// subscribing to interruption signals
signal.Notify(stop, os.Interrupt)
// this blocks until the signal is received
<-stop
// initiating the shutdown
err := server.Shutdown(context.Background())
// can't do much here except for logging any errors
if err != nil {
log.Printf("error during shutdown: %v\n", err)
}
// notifying the main goroutine that we are done
wg.Done()
}()
log.Println("listening on port 3333...")
err := server.ListenAndServe()
if err == http.ErrServerClosed { // graceful shutdown
log.Println("commencing server shutdown...")
wg.Wait()
log.Println("server was gracefully shut down.")
} else if err != nil {
log.Printf("server error: %v\n", err)
}
}
Open two terminals. In the first run the app, in the second one run curl localhost:3333, then quickly switch to the first one and try to stop the app with CTRL+C
The output should be:
2021/03/12 13:39:49 listening on port 3333...
2021/03/12 13:39:50 user initiated a request
2021/03/12 13:39:54 commencing server shutdown...
2021/03/12 13:40:00 user request is fulfilled
2021/03/12 13:40:01 server was gracefully shut down.
There exists a module which implements (graceful) stopping of Go HTTP servers:
https://github.com/pseidemann/finish
This removes the need of the boilerplate presented in the other answers.
What I've done for such cases where the application is just the server and performing no other function is install an http.HandleFunc for a pattern like /shutdown. Something like
http.HandleFunc("/shutdown", func(w http.ResponseWriter, r *http.Request) {
if <credentials check passes> {
// - Turn on mechanism to reject incoming requests.
// - Block until "in-flight" requests complete.
// - Release resources, both internal and external.
// - Perform all other cleanup procedures thought necessary
// for this to be called a "graceful shutdown".
fmt.Fprint(w, "Goodbye!\n")
os.Exit(0)
}
})
It does not require 1.8. But if 1.8 is available, then that solution can be embedded here instead of the os.Exit(0) call if desirable, I believe.
The code to perform all of that cleanup work is left as an exercise for the reader.
Extra credit if you can say where that cleanup code might be most reasonably be placed, for I would not recommend doing it here, and how this endpoint hit should cause the invocation that code.
More extra credit if you can say where that os.exit(0) call (or whatever process exit you choose to use), given here for illustrative purposes only, would be most reasonably placed.
Yet even more extra credit if you can explain why this mechanism of HTTP server process signaling should be considered above all other such mechanisms thought workable in this case.