When I use shell it returns ok,
but when I use golang it returns err: context deadline exceeded.
Why?
package main
import (
"context"
"fmt"
"go.etcd.io/etcd/clientv3"
"time"
)
func main() {
cli,err := clientv3.New(clientv3.Config{
Endpoints: []string{"http://192.168.11.34:2379"},
DialTimeout: 5*time.Second,
})
if err != nil{
panic(err)
}
fmt.Println("connect etcd success!")
defer cli.Close()
ctx,cancel := context.WithTimeout(context.Background(),time.Second*5)
_,err = cli.Put(ctx,"mac","leave")
defer cancel()
if err !=nil{
fmt.Println("put etcd err:",err.Error())
return
}
}
connect etcd success!
{"level":"warn","ts":"2021-02-08T17:10:46.490+0800","caller":"clientv3/retry_interceptor.go:62","msg":"retrying of unary invoker failed","target":"endpoint://client-5b14f6c4-2395-4a15-9ebb-cadf5985fb06/192.168.11.34:2379","attempt":0,"error":"rpc error: code = DeadlineExceeded desc = latest balancer error: all SubConns are in TransientFailure, latest connection error: connection error: desc = \"transport: Error while dialing dial tcp 192.168.11.34:2379: connect: connection refused\""}
put etcd err: context deadline exceeded
Creation of client does not guarantee that the connection is established.
It might get delayed till the first request that actually fails.
Please make sure that your server is reachable.
Increase your timeout:
DialTimeout: 5*time.Second
Clearly this time is being exceeded if you are receiving a context deadline exceeded error.
Deferring the cancel() means that the context won't be canceled until after your Println and return (during function tear-down). If the Put() call is on the edge of the timeout period that will cause the error to be reported. But calling cancel() directly after the Put() call seems to also have issues (see my recent question)
Related
I am currently studying network programming in Go.
Currently, a timeout test using the deadline of the context is in progress by myself.
package socket
import (
"context"
"net"
"syscall"
"testing"
"time"
)
func TestDialContext(t *testing.T) {
deadline := time.Now().Add(time.Second * 5)
ctx, cancelFunc := context.WithDeadline(context.Background(), deadline)
defer cancelFunc()
var dialer net.Dialer
dialer.Control = func(_, _ string, _ syscall.RawConn) error {
time.Sleep(4 * time.Second)
return nil
}
conn, err := dialer.DialContext(ctx, "tcp", "10.0.0.0:http")
if err == nil {
conn.Close()
t.Fatal("expected no connection")
}
if nErr, ok := err.(net.Error); ok && nErr.Timeout() {
t.Logf("expected timeout error: %v", err)
} else {
t.Errorf("expected timeout error; actual: %v", err)
}
if ctx.Err() != context.DeadlineExceeded {
t.Errorf("expected deadline exceeded; actual: %v", ctx.Err())
}
}
As you can see from the code, I tried to connect using a context with a 5-second deadline.
In addition, by using the dialer's control function when requested, the control function is called immediately after being connected (actually right before being connected), so that a nil error is returned after 4 seconds.
By my intent, this test should fail. The reason is that the control function returns a nil error before the deadline of 5 seconds is reached.
But the test succeeds as below.
=== RUN TestDialContext
dial_context_test.go:27: expected timeout error: dial tcp 10.0.0.0:80: i/o timeout
--- PASS: TestDialContext (5.01s)
PASS
What exactly is the purpose of the control function? Is the error returned by the control function unrelated to the error returned by the net.Dial() function? Is the test using the control function like the code I wrote meaningless?
Thanks for reading this long question!
What exactly is the purpose of the control function?
To give the dialing code a chance to call functions on the socket's underlying file descriptor — e.g. one of the syscall.Setsockopt functions — after the socket is created and before the connection is established.
Is the error returned by the control function unrelated to the error returned by the net.Dial() function?
If the control function returns an error, then dialing fails with that error. If the control function returns a nil error, then dialing continues as usual.
Is the test using the control function like the code I wrote meaningless?
Pretty much. Your control function sleeps 4 seconds but otherwise doesn't do anything.
I deployed DgraphAlpha and DgraphZero in docker. I am connecting to Dgraph as described in the documentation.
func newClient() *dgo.Dgraph {
d, err := grpc.Dial("localhost:9080", grpc.WithInsecure())
if err != nil {
log.Fatal(err)
}
return dgo.NewDgraphClient(
api.NewDgraphClient(d),
)
}
And the client is created successfully, but when I try to search
txn := i.dgraphClient.NewReadOnlyTxn()
defer txn.Discard(context.Background())
dgraphQuery := "search here"
response, err := txn.Query(context.Background(), dgraphQuery)
if err != nil {
// error here
}
I get an error:
rpc error: code = Unavailable desc = connection closed before server preface received
This error does not always occur, at unexpected moments, and because of this it is difficult for me to determine its root. Has anyone encountered something similar? What could be the problem?
Beside other transitory causes, a common cause of this error is the server running with TLS enabled and the client attempting to connect without TLS.
Make sure you correctly configured TLS options on the client:
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{myCertificate},
RootCAs: myCAPool,
}
tlsOpt := grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig))
conn, err := grpc.DialContext(ctx, "<connection_string>", tlsOpt)
Make also sure you are actually using client certificates on the client connection.
It may be some issue with timing. This may happens more often in the first requests? Do you have any log on the Dgraph side?
Consider:
Use dial option WithBlock() to ensure you have a connection
Use DialContext and use a context with timeout to avoid wait a lot
Be aware that Insecure dial option is deprecated
Deprecated: use WithTransportCredentials and insecure.NewCredentials() instead.
About the error:
https://grpc.github.io/grpc/core/md_doc_statuscodes.html
Status code 14
This seems a transient error.
You may retry after some time. There are some retriers that can be used on this case like:
https://pkg.go.dev/github.com/eapache/go-resiliency/retrier
Using gorilla/websocket I dial the binance websocket endpoint, which succeeds without error. After setting the pong handler on the connection, I write a ping control message and wait for a pong to arrive at the pong handler, which never seems to happen. I use a channel, a context with timeout and a select block to check if the pong arrived.
The code:
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/gorilla/websocket"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
conn, resp, err := websocket.DefaultDialer.DialContext(ctx, "wss://stream.binance.com:9443/ws", nil)
if resp != nil {
log.Printf("status: %s", resp.Status)
}
if err != nil {
panic(err)
}
pong := make(chan struct{})
conn.SetPongHandler(func(appData string) error {
log.Println(appData)
pong <- struct{}{}
return nil
})
if err := conn.WriteControl(websocket.PingMessage, []byte("Hello, world!"), time.Now().Add(5*time.Second)); err != nil {
panic(err)
}
select {
case <-ctx.Done():
panic(fmt.Errorf("pong wait: %w", ctx.Err()))
case <-pong:
}
}
Output:
$ go run ./cmd/ping
2022/02/07 20:01:23 status: 101 Switching Protocols
panic: pong wait: context deadline exceeded
goroutine 1 [running]:
main.main()
/workspaces/yatgo/cmd/ping/ping.go:39 +0x2ba
exit status 2
As per rfc6455, section-5.5.2:
5.5.2. Ping
The Ping frame contains an opcode of 0x9.
A Ping frame MAY include "Application data".
Upon receipt of a Ping frame, an endpoint MUST send a Pong frame in
response, unless it already received a Close frame. It SHOULD
respond with Pong frame as soon as is practical. Pong frames are
discussed in Section 5.5.3.
An endpoint MAY send a Ping frame any time after the connection is
established and before the connection is closed.
NOTE: A Ping frame may serve either as a keepalive or as a means to
verify that the remote endpoint is still responsive.
I kind off expected this to work. Binance websocket API limits doc does mentions ping messages:
WebSocket connections have a limit of 5 incoming messages per second. A message is considered:
A PING frame
So I wonder:
Is something wrong with my code?
Or is binance not respecting RFC6455?
The Gorilla Websocket documentation says:
The application must read the connection to process close, ping and pong messages sent from the peer. If the application is not otherwise interested in messages from the peer, then the application should start a goroutine to read and discard messages from the peer.
Fix the application by starting a goroutine to read the connection before the select statement:
go func() {
defer cancel()
for {
if _, _, err := conn.NextReader(); err != nil {
fmt.Println(err)
return
}
}
}()
select {
⋮
This is a fix for the application shown in the question. If your actual application reads data from the connection in a loop, then you should not add the goroutine shown here. The application should use one read loop to handle control and data messages.
I'm trying to start an HTTP server in Go, and when the server is started, a message should be printed, in case of an error, an error message should be printed.
Given the following code:
const (
HTTPServerPort = ":4000"
)
func main() {
var httpServerError = make(chan error)
var waitGroup sync.WaitGroup
setupHTTPHandlers()
waitGroup.Add(1)
go func() {
defer waitGroup.Done()
httpServerError <- http.ListenAndServe(HTTPServerPort, nil)
}()
if <-httpServerError != nil {
fmt.Println("The Logging API service could not be started.", <-httpServerError)
} else {
fmt.Println("Logging API Service running # http://localhost" + HTTPServerPort)
}
waitGroup.Wait()
}
When I do start the application, I don't see anything printed to the console, where I would like to see:
Logging API Service running # http://localhost:4000
When I change the port to an invalid one, the following output is printed to the console:
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan receive]:
main.main()
...app.go:45 +0x107
exit status 2
Could anyone point me in the right direction so that I know what I'm doing wrong with this implementation?
You can't do this unless you change the logic in your code or use Listen and Serve separately. Because ListenAndServe is a blocking function. If there something unexpected happens, it will return you an error. Provided it is not, it will keep blocking running the server. There is neither an event that is triggered whenever a server is started.
Let's run Listen and Serve separately then.
l, err := net.Listen("tcp", ":8080")
if err != nil {
// handle error
}
// Signal that server is open for business.
if err := http.Serve(l, rootHandler); err != nil {
// handle error
}
See https://stackoverflow.com/a/44598343/4792552.
P.S. net.Listen doesn't block because it runs in background. In other means, it simply spawns a socket connection in OS level and returns you with the details/ID of it. Thus, you use that ID to proxy orders to that socket.
The issue is that your if statement will always read from the httpServerError channel. However the only time something writes to that is if the server fails.
Try a select statement:
select{
case err := <-httpServerError
fmt.Println("The Logging API service could not be started.", err)
default:
fmt.Println("Logging API Service running # http://localhost" + HTTPServerPort
}
The default case will be ran if the channel does not have anything on it.
Notice this does not read from the channel twice like your example. Once you read a value from a channel, its gone. Think of it as a queue.
I have a client who try to connect to a server.
I need to be able to terminate the client and abort this Dial attempt. Is this possible ? How could I do that ?
The timeout is apparently longer than 30s since the test blocks until the 30s elapse without a failure of the Dial call.
Can we specify a timeout ourself ?
The net.Dialer has Timeout and Deadline fields, and also can use a context with DialContext which allows for timeout and cancelation.
You can refer to DialTimeout to see how to setup the basic Dialer:
func DialTimeout(network, address string, timeout time.Duration) (Conn, error) {
d := Dialer{Timeout: timeout}
return d.Dial(network, address)
}
And an example with a context.Context:
var d Dialer
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
return d.DialContext(ctx, network, address)