how can I reduce cpu usage in a golang tcp server? - c++

I try to implement a golang tcp server, and I found the concurrency is satisfied for me, but the CPU usage is too high(concurrency is 15W+/s, but the CPU usage is about 800% in a 24 cores linux machine). At the same time, a C++ tcp server is only about 200% usage with a similar concurrency(with libevent).
The following code is the demo of golang:
func main() {
listen, err := net.Listen("tcp", "0.0.0.0:17379")
if err != nil {
fmt.Errorf(err.Error())
}
go acceptClient(listen)
var channel2 = make(chan bool)
<-channel2
}
func acceptClient(listen net.Listener) {
for {
sock, err := listen.Accept()
if err != nil {
fmt.Errorf(err.Error())
}
tcp := sock.(*net.TCPConn)
tcp.SetNoDelay(true)
var channel = make(chan bool, 10)
go read(channel, sock.(*net.TCPConn))
go write(channel, sock.(*net.TCPConn))
}
}
func read(channel chan bool, sock *net.TCPConn) {
count := 0
for {
var buf = make([]byte, 1024)
n, err := sock.Read(buf)
if err != nil {
close(channel)
sock.CloseRead()
return
}
count += n
x := count / 58
count = count % 58
for i := 0; i < x; i++ {
channel <- true
}
}
}
func write(channel chan bool, sock *net.TCPConn) {
buf := []byte("+OK\r\n")
defer func() {
sock.CloseWrite()
recover()
}()
for {
_, ok := <-channel
if !ok {
return
}
_, writeError := sock.Write(buf)
if writeError != nil {
return
}
}
}
And I test this tcp server by the redis-benchmark with multi-clients:
redis-benchmark -h 10.100.45.2 -p 17379 -n 1000 -q script load "redis.call('set','aaa','aaa')"
I also analyzed my golang code by the pprof, it is said CPU cost a lot of time on syscall:
enter image description here

I don't think parallelise the read and write with channel will provide you better performance in this case. You should try to do less memory allocation and less syscall (The write function may do a lot of syscalls)
Can you try this version?
package main
import (
"bytes"
"fmt"
"net"
)
func main() {
listen, err := net.Listen("tcp", "0.0.0.0:17379")
if err != nil {
fmt.Errorf(err.Error())
}
acceptClient(listen)
}
func acceptClient(listen net.Listener) {
for {
sock, err := listen.Accept()
if err != nil {
fmt.Errorf(err.Error())
}
tcp := sock.(*net.TCPConn)
tcp.SetNoDelay(true)
go handleConn(tcp) // less go routine creation but no concurrent read/write on the same conn
}
}
var respPattern = []byte("+OK\r\n")
// just one goroutine per conn
func handleConn(sock *net.TCPConn) {
count := 0
buf := make([]byte, 4098) // Do not create a new buffer each time & increase the buff size
defer sock.Close()
for {
n, err := sock.Read(buf)
if err != nil {
return
}
count += n
x := count / 58
count = count % 58
resp := bytes.Repeat(respPattern, x) // can be optimize
_, writeError := sock.Write(resp) // do less syscall
if writeError != nil {
return
}
}
}

Related

Problem with testing cli prompts interactively, scanner is not waiting for user input

I try to write a test to verify cli prompts, emulate user inputs in response to some program outputs.
How to make scanner.Scan wait for the rest of the writes?
What I have so far:
b := &bytes.Buffer{}
fmt.Fprint(b, "0")
go func() {
time.Sleep(1 * time.Second)
for i := 1; i < 4; i++ {
fmt.Fprint(b, i)
time.Sleep(1 * time.Second)
}
}()
scanner := bufio.NewScanner(b)
for scanner.Scan() {
log.Print(scanner.Text())
}
if err := scanner.Err(); err != nil {
log.Println("problem while scanning:", err)
}
Expected result is: 0123
Actual result is: 0
I tried a version with io.Pipe
r, w := io.Pipe()
fmt.Fprint(w, "0")
go func() {
time.Sleep(1 * time.Second)
for i := 1; i < 4; i++ {
fmt.Fprint(w, i)
time.Sleep(1 * time.Second)
}
}()
scanner := bufio.NewScanner(r)
for scanner.Scan() {
log.Print(scanner.Text())
}
if err := scanner.Err(); err != nil {
log.Println("problem while scanning:", err)
}
result: fatal error: all goroutines are asleep - deadlock!
When using a pipe, writes and reads are synchronous. A Write cannot complete without a matching Read. Move the first write into the goroutine. And Close the writing end of the pipe to allow the scanner to stop scanning.
r, w := io.Pipe()
go func() {
defer w.Close()
fmt.Fprint(w, "0")
time.Sleep(1 * time.Second)
for i := 1; i < 4; i++ {
fmt.Fprint(w, i)
time.Sleep(1 * time.Second)
}
}()
scanner := bufio.NewScanner(r)
for scanner.Scan() {
log.Print(scanner.Text())
}
if err := scanner.Err(); err != nil {
log.Println("problem while scanning:", err)
}

Unit-testing grpc functions in golang

I have created a function that utilizes the grpc package in golang. I don't know if it is relevant but the purpose is the communication with a GoBGP router over grpc. An example is the following function which prints all the peers (neighbors) of the router:
func (gc *Grpc) Peers(conn *grpc.ClientConn) error {
defer conn.Close()
c := pb.NewGobgpApiClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()
p := pb.ListPeerRequest{}
peer, err := c.ListPeer(ctx, &p)
if err != nil {
return err
}
for {
res, err := peer.Recv()
if err != nil {
return err
}
fmt.Println(res)
}
return nil
}
Now, I want to create unit tests for the function. To do so, I used google.golang.org/grpc/test/bufconn package, and initialized the following:
type server struct {
pb.UnimplementedGobgpApiServer
}
func (s *server) ListDefinedSet(in *pb.ListDefinedSetRequest, ls pb.GobgpApi_ListDefinedSetServer) error {
return nil
}
var lis *bufconn.Listener
const bufSize = 1024 * 1024
func init() {
lis = bufconn.Listen(bufSize)
s := grpc.NewServer()
pb.RegisterGobgpApiServer(s, &server{})
go func() {
if err := s.Serve(lis); err != nil {
fmt.Println("Server failed!")
}
}()
}
func bufDialer(context.Context, string) (net.Conn, error) {
return lis.Dial()
}
This way, I can run a unit-test creating a connection as follows:
ctx := context.Background()
conn, _ := grpc.DialContext(ctx, "bufnet", grpc.WithContextDialer(bufDialer), grpc.WithInsecure())
Peers(conn)
However, the problem is that the stream seems to be always empty and thus the peer.Recv()
always returns EOF. Is there any way to populate the stream with dummy data? If you have experience, is my methodology correct?

Testing with Gomock returns error: Expected call has already been called the max number of times

I am using Gomock https://godoc.org/github.com/golang/mock and mockgen
The Source code for this test is:
package sqs
import (
"fmt"
"log"
"os"
"runtime"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/sqs"
"github.com/aws/aws-sdk-go/service/sqs/sqsiface"
)
var sess *session.Session
var svc *sqs.SQS
var queueURL string
func init() {
// Setting the runtime to run with max CPUs available
runtime.GOMAXPROCS(runtime.NumCPU())
sess = session.Must(session.NewSessionWithOptions(session.Options{
SharedConfigState: session.SharedConfigEnable,
}))
svc = sqs.New(sess)
queueURL = os.Getenv("QUEUE_URL")
}
type Poller interface {
Poll(chan bool)
}
// NewPoller is a factory to create a Poller object
func NewPoller(msgr Messenger) Poller {
p := &poller{
m: msgr,
}
return p
}
type poller struct {
m Messenger
}
func (p *poller) Poll(done chan bool) {
sqsMsgCh := make(chan *sqs.Message, 100)
for {
messages, err := p.m.GetMessage()
if err != nil {
log.Printf("error when getting message")
if len(messages) == 0 {
// Stop the system
log.Printf("I am here")
done <- true
}
}
for _, msg := range messages {
sqsMsgCh <- msg
}
}
}
type Messenger interface {
GetMessage() ([]*sqs.Message, error)
}
func NewMessenger() Messenger {
return &messenger{
s: svc,
}
}
type messenger struct {
s sqsiface.SQSAPI
}
func (m *messenger) GetMessage() ([]*sqs.Message, error) {
result, err := m.s.ReceiveMessage(&sqs.ReceiveMessageInput{
AttributeNames: []*string{
aws.String(sqs.MessageSystemAttributeNameSentTimestamp),
},
MessageAttributeNames: []*string{
aws.String(sqs.QueueAttributeNameAll),
},
QueueUrl: aws.String(queueURL),
MaxNumberOfMessages: aws.Int64(10),
VisibilityTimeout: aws.Int64(36000), // 10 hours
WaitTimeSeconds: aws.Int64(0),
})
if err != nil {
fmt.Println("Error", err)
return nil, err
}
msgs := result.Messages
if len(msgs) == 0 {
fmt.Println("Received no messages")
return msgs, err
}
return msgs, nil
}
The test case for this Source file is here:
package sqs
import (
"errors"
"testing"
"path_to_the_mocks_package/mocks"
"github.com/golang/mock/gomock"
"github.com/aws/aws-sdk-go/service/sqs"
)
func TestPollWhenNoMessageOnQueue(t *testing.T) {
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
msgr := mocks.NewMockMessenger(mockCtrl)
mq := make([]*sqs.Message, 1)
err := errors.New("Mock Error")
// msgr.EXPECT().GetMessage().Return(mq, err) //.Times(1)
// msgr.GetMessage().Return(mq, err) //.Times(1)
msgr.EXPECT().GetMessage().Return(mq, err)
p := NewPoller(msgr)
done := make(chan bool)
go p.Poll(done)
<-done
t.Logf("Successfully done: %v", done)
}
When I run the tests I am getting the following error:
sqs\controller.go:150: Unexpected call to
*mocks.MockMessenger.GetMessage([]) at path_to_mocks_package/mocks/mock_messenger.go:38 because: Expected
call at path_to_sqs_package/sqs/sqs_test.go:35 has already been called
the max number of times. FAIL
If I write my own mock as follows the test case executes successfully:
type mockMessenger struct {
mock.Mock
}
func (m *mockMessenger) GetMessage() ([]*sqs.Message, error) {
msgs := make([]*sqs.Message, 0)
err := errors.New("Error")
return msgs, err
}
You are implicitly telling gomock that you only expect a single call.
msgr.EXPECT().GetMessage().Return(mq, err)
Adding a number of Times to the mock, allows you to return those values more than once.
msgr.EXPECT().GetMessage().Return(mq, err).AnyTimes()
For more details please read the gomock's AnyTimes documentation.

Function from golang c-library simply stops in C++

I am trying to setup a little server-client application.
For this purpose I created a network server and a network client in golang. I created a library from the client and I am calling the code in my C++ program, but when the program reaches the function call for the message function the program stops and never returns from the function.
I tried to send messages from golang to golang, which works fine.
Here is the go source code for the client:
package main
import (
"net"
"strconv"
"strings"
"fmt"
"C"
)
const (
StopCharacter = "\r\n\r\n"
)
//export GoMessage
func GoMessage(ip string, port int, message string) string {
address := strings.Join([]string{ip, strconv.Itoa(port)}, ":")
connection, err := net.Dial("tcp", address)
if err != nil {
fmt.Println(err)
}
defer connection.Close()
connection.Write([]byte(message))
connection.Write([]byte(StopCharacter))
buffer := make([]byte, 1024)
n, _ := connection.Read(buffer)
data := string(buffer[:n])
return data
}
func main() {
/*arg := os.Args[1]
var (
ip = "127.0.0.1"
port = 3333
)
buffer := SendMessage(ip, port, arg)
fmt.Println(buffer)
ToFile(buffer)*/
}
Here is the server code:
package main
import (
"bufio"
"io"
"fmt"
"net"
"strconv"
"strings"
"os"
)
const (
StopCharacter = "\r\n\r\n"
)
func ToFile(message string) {
f, err := os.Create("gout.txt")
defer f.Close()
if(err != nil) {
fmt.Println(err)
}
f.WriteString(message)
}
func SocketServer(port int) {
listen, err := net.Listen("tcp4", ":"+strconv.Itoa(port))
defer listen.Close()
if err != nil {
fmt.Println(err)
}
for {
connection, err := listen.Accept()
if err != nil {
fmt.Println(err)
continue
}
go handler(connection)
}
}
func handler(connection net.Conn) {
defer connection.Close()
var (
buffer = make([]byte, 1024)
r = bufio.NewReader(connection)
w = bufio.NewWriter(connection)
)
var data string = "";
ILOOP:
for {
n, err := r.Read(buffer)
data = string(buffer[:n])
switch err {
case io.EOF:
break ILOOP
case nil:
if isTransportOver(data) {
break ILOOP
}
default:
return
}
}
fmt.Println(data)
ToFile(data)
w.Write([]byte("Test123"))
w.Flush()
}
func isTransportOver(data string) (over bool) {
over = strings.HasSuffix(data, "\r\n\r\n")
return
}
func main() {
port := 3333
SocketServer(port)
}
and this is my C++ code:
#include <sstream>
#include <iostream>
#include <conio.h>
#include "goclient.h"
extern GoString GoMessage(GoString ip, GoInt port, GoString message);
std::string message(GoString message)
{
GoString ip;
ip.p = "127.0.0.1";
ip.n = 10;
GoString answer = GoMessage(ip, 3333, message);
return answer.p;
}
int main()
{
GoString msg;
msg.p = "hi";
msg.n = 2;
std::cout << message(msg);
_getch();
return 0;
}
I am doing this just for the sake of sience... I am aware that there are better solutions for this (like thrift).
I am completely out of ideas what to do to solve the problem.
I am using msvc btw.
Update:
First of all I tried to compile & run the program with minGW, which brought up a NUL string error:
Sorry can't copy from mingwshell)
Afterwards I tried to return from the GoMessage function direct after calling:
//export GoMessage
func GoMessage(ip string, port int, message string) string {
return "test"
address := strings.Join([]string{ip, strconv.Itoa(port)}, ":")
connection, err := net.Dial("tcp", address)
if err != nil {
fmt.Println(err)
}
defer connection.Close()
connection.Write([]byte(message))
connection.Write([]byte(StopCharacter))
buffer := make([]byte, 1024)
n, _ := connection.Read(buffer)
data := string(buffer[:n])
return data
}
Which lead to the same result on msvc and but a different output on minGW:
(Sorry can't copy from mingwshell)

Go webcrawler hangs after checking about 2000 urls

I have a program to check whether keywords are on a web page. But after checking 1000-3000 urls, it hangs. There is no output, it does not exit, and the number of tcp connections is zero. I don't know why there are no new connections.
Would you give me some advice how to debug it?
type requestReturn struct {
url string
status bool
}
var timeout = time.Duration(800 * time.Millisecond)
func checkUrls(urls []string, kws string, threadLimit int) []string {
limitChan := make(chan int, threadLimit)
ok := make(chan requestReturn, 1)
var result []string
i := 0
for ; i < threadLimit; i++ {
go func(u string) {
request(u, limitChan, ok, kws)
}(urls[i])
}
for o := range ok {
if o.status {
result = append(result, o.url)
log.Printf("success %s,remain %d", o.url, len(urls)-i)
} else {
log.Printf("fail %s,remain %d", o.url, len(urls)-i)
}
if i < len(urls) {
go func(u string) {
request(u, limitChan, ok, kws)
}(urls[i])
i++
}
}
close(limitChan)
return result
}
func dialTimeout(network, addr string) (net.Conn, error) {
return net.DialTimeout(network, addr, timeout)
}
func request(url string, threadLimit chan int, ok chan requestReturn, kws string) {
threadLimit <- 1
log.Printf("%s, start...", url)
//startTime := time.Now().UnixNano()
rr := requestReturn{url: url}
transport := http.Transport{
Dial: dialTimeout,
DisableKeepAlives: true,
}
client := http.Client{
Transport: &transport,
Timeout: time.Duration(15 * time.Second),
}
resp, e := client.Get(url)
if e != nil {
log.Printf("%q", e)
rr.status = false
return
}
if resp.StatusCode == 200 {
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Printf("%q", err)
rr.status = false
return
}
content := bytes.NewBuffer(body).String()
matched, err1 := regexp.MatchString(kws, content)
if err1 != nil {
log.Printf("%q", err1)
rr.status = false
} else if matched {
rr.status = true
log.Println(rr.url)
} else {
rr.status = false
}
} else {
rr.status = false
}
defer (func() {
resp.Body.Close()
ok <- rr
//processed := float32(time.Now().UnixNano()-startTime) / 1e9
//log.Printf("%s, status:%t,time:%.3fs", rr.url, rr.status, processed)
<-threadLimit
})()
}
You seem to be using two forms of concurrency control in this code, and both have problems.
You've got limitChan, which looks like it is being used as a semaphore (request sends a value at its start, and receives a value in a defer in that function). But checkUrls is also trying to make sure it only has threadLimit goroutines running at once (by spawning that number first up, and only spawning more when one reports its results on the ok channel). Only one of these should be necessary to limit the concurrency.
Both methods fail due to the way the defer is set up in request. There are a number of return statements that occur before defer, so it is possible for the function to complete without sending the result to the ok channel, and without freeing up its slot in limitChan. After a sufficient number of errors, checkUrls will stop spawning new goroutines and you'll see your hang.
The fix is to place the defer statement before any of the return statements so you know it will always be run. Something like this:
func request(url string, threadLimit chan int, ok chan requestReturn, kws string) {
threadLimit <- 1
rr := requestReturn{url: url}
var resp *http.Response
defer func() {
if resp != nil {
resp.Body.Close()
}
ok <- rr
<-threadLimit
}()
...
}