github.com/cloudwego/kitex@v0.9.0/pkg/generic/binary_test/generic_test.go (about)

     1  /*
     2   * Copyright 2021 CloudWeGo Authors
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package test
    18  
    19  import (
    20  	"context"
    21  	"encoding/binary"
    22  	"net"
    23  	"runtime"
    24  	"runtime/debug"
    25  	"strings"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/apache/thrift/lib/go/thrift"
    30  
    31  	"github.com/cloudwego/kitex/client"
    32  	"github.com/cloudwego/kitex/client/callopt"
    33  	"github.com/cloudwego/kitex/client/genericclient"
    34  	kt "github.com/cloudwego/kitex/internal/mocks/thrift"
    35  	"github.com/cloudwego/kitex/internal/test"
    36  	"github.com/cloudwego/kitex/pkg/generic"
    37  	"github.com/cloudwego/kitex/pkg/utils"
    38  	"github.com/cloudwego/kitex/server"
    39  )
    40  
    41  var addr = test.GetLocalAddress()
    42  
    43  func TestRun(t *testing.T) {
    44  	t.Run("RawThriftBinary", rawThriftBinary)
    45  	t.Run("RawThriftBinaryError", rawThriftBinaryError)
    46  	t.Run("RawThriftBinaryMockReq", rawThriftBinaryMockReq)
    47  	t.Run("RawThriftBinary2NormalServer", rawThriftBinary2NormalServer)
    48  }
    49  
    50  func rawThriftBinary(t *testing.T) {
    51  	svr := initRawThriftBinaryServer(new(GenericServiceImpl))
    52  	defer svr.Stop()
    53  
    54  	cli := initRawThriftBinaryClient()
    55  
    56  	method := "myMethod"
    57  	buf := genBinaryReqBuf(method)
    58  
    59  	resp, err := cli.GenericCall(context.Background(), method, buf, callopt.WithRPCTimeout(1*time.Second))
    60  	test.Assert(t, err == nil, err)
    61  	respBuf, ok := resp.([]byte)
    62  	test.Assert(t, ok)
    63  	test.Assert(t, string(respBuf[12+len(method):]) == respMsg)
    64  }
    65  
    66  func rawThriftBinaryError(t *testing.T) {
    67  	svr := initRawThriftBinaryServer(new(GenericServiceErrorImpl))
    68  	defer svr.Stop()
    69  
    70  	cli := initRawThriftBinaryClient()
    71  
    72  	method := "myMethod"
    73  	buf := genBinaryReqBuf(method)
    74  
    75  	_, err := cli.GenericCall(context.Background(), method, buf, callopt.WithRPCTimeout(100*time.Second))
    76  	test.Assert(t, err != nil)
    77  	test.Assert(t, strings.Contains(err.Error(), errResp), err.Error())
    78  }
    79  
    80  func rawThriftBinaryMockReq(t *testing.T) {
    81  	svr := initRawThriftBinaryServer(new(GenericServiceMockImpl))
    82  	defer svr.Stop()
    83  
    84  	cli := initRawThriftBinaryClient()
    85  
    86  	req := kt.NewMockReq()
    87  	req.Msg = reqMsg
    88  	strMap := make(map[string]string)
    89  	strMap["aa"] = "aa"
    90  	strMap["bb"] = "bb"
    91  	req.StrMap = strMap
    92  	args := kt.NewMockTestArgs()
    93  	args.Req = req
    94  
    95  	// encode
    96  	rc := utils.NewThriftMessageCodec()
    97  	buf, err := rc.Encode("Test", thrift.CALL, 100, args)
    98  	test.Assert(t, err == nil, err)
    99  
   100  	resp, err := cli.GenericCall(context.Background(), "Test", buf)
   101  	test.Assert(t, err == nil, err)
   102  
   103  	// decode
   104  	buf = resp.([]byte)
   105  	var result kt.MockTestResult
   106  	method, seqID, err := rc.Decode(buf, &result)
   107  	test.Assert(t, err == nil, err)
   108  	test.Assert(t, method == "Test", method)
   109  	test.Assert(t, seqID != 100, seqID)
   110  	test.Assert(t, *result.Success == respMsg)
   111  
   112  	seqID2, err2 := generic.GetSeqID(buf)
   113  	test.Assert(t, err2 == nil, err2)
   114  	test.Assert(t, seqID2 == seqID, seqID2)
   115  }
   116  
   117  func rawThriftBinary2NormalServer(t *testing.T) {
   118  	svr := initMockServer(new(MockImpl))
   119  	defer svr.Stop()
   120  
   121  	cli := initRawThriftBinaryClient()
   122  
   123  	req := kt.NewMockReq()
   124  	req.Msg = reqMsg
   125  	strMap := make(map[string]string)
   126  	strMap["aa"] = "aa"
   127  	strMap["bb"] = "bb"
   128  	req.StrMap = strMap
   129  	args := kt.NewMockTestArgs()
   130  	args.Req = req
   131  
   132  	// encode
   133  	rc := utils.NewThriftMessageCodec()
   134  	buf, err := rc.Encode("Test", thrift.CALL, 100, args)
   135  	test.Assert(t, err == nil, err)
   136  
   137  	resp, err := cli.GenericCall(context.Background(), "Test", buf, callopt.WithRPCTimeout(100*time.Second))
   138  	test.Assert(t, err == nil, err)
   139  
   140  	// decode
   141  	buf = resp.([]byte)
   142  	var result kt.MockTestResult
   143  	method, seqID, err := rc.Decode(buf, &result)
   144  	test.Assert(t, err == nil, err)
   145  	test.Assert(t, method == "Test", method)
   146  	// seqID会在kitex中覆盖,避免TTHeader和Payload codec 不一致问题
   147  	test.Assert(t, seqID != 100, seqID)
   148  	test.Assert(t, *result.Success == respMsg)
   149  }
   150  
   151  func initRawThriftBinaryClient() genericclient.Client {
   152  	g := generic.BinaryThriftGeneric()
   153  	cli := newGenericClient("destServiceName", g, addr)
   154  	return cli
   155  }
   156  
   157  func initRawThriftBinaryServer(handler generic.Service) server.Server {
   158  	addr, _ := net.ResolveTCPAddr("tcp", addr)
   159  	g := generic.BinaryThriftGeneric()
   160  	svr := newGenericServer(g, addr, handler)
   161  	return svr
   162  }
   163  
   164  func initMockServer(handler kt.Mock) server.Server {
   165  	tcpAddr, _ := net.ResolveTCPAddr("tcp", addr)
   166  	svr := NewMockServer(handler, tcpAddr)
   167  	return svr
   168  }
   169  
   170  func genBinaryReqBuf(method string) []byte {
   171  	idx := 0
   172  	buf := make([]byte, 12+len(method)+len(reqMsg))
   173  	binary.BigEndian.PutUint32(buf, thrift.VERSION_1)
   174  	idx += 4
   175  	binary.BigEndian.PutUint32(buf[idx:idx+4], uint32(len(method)))
   176  	idx += 4
   177  	copy(buf[idx:idx+len(method)], method)
   178  	idx += len(method)
   179  	binary.BigEndian.PutUint32(buf[idx:idx+4], 100)
   180  	idx += 4
   181  	copy(buf[idx:idx+len(reqMsg)], reqMsg)
   182  	return buf
   183  }
   184  
   185  func TestBinaryThriftGenericClientClose(t *testing.T) {
   186  	debug.SetGCPercent(-1)
   187  	defer debug.SetGCPercent(100)
   188  
   189  	var ms runtime.MemStats
   190  	runtime.ReadMemStats(&ms)
   191  
   192  	t.Logf("Before new clients, allocation: %f Mb, Number of allocation: %d\n", mb(ms.HeapAlloc), ms.HeapObjects)
   193  
   194  	cliCnt := 10000
   195  	clis := make([]genericclient.Client, cliCnt)
   196  	for i := 0; i < cliCnt; i++ {
   197  		g := generic.BinaryThriftGeneric()
   198  		clis[i] = newGenericClient("destServiceName", g, addr, client.WithShortConnection())
   199  	}
   200  
   201  	runtime.ReadMemStats(&ms)
   202  	preHeapAlloc, preHeapObjects := mb(ms.HeapAlloc), ms.HeapObjects
   203  	t.Logf("After new clients, allocation: %f Mb, Number of allocation: %d\n", preHeapAlloc, preHeapObjects)
   204  
   205  	for _, cli := range clis {
   206  		_ = cli.Close()
   207  	}
   208  	runtime.GC()
   209  	runtime.ReadMemStats(&ms)
   210  	afterGCHeapAlloc, afterGCHeapObjects := mb(ms.HeapAlloc), ms.HeapObjects
   211  	t.Logf("After close clients and GC be executed, allocation: %f Mb, Number of allocation: %d\n", afterGCHeapAlloc, afterGCHeapObjects)
   212  	test.Assert(t, afterGCHeapAlloc < preHeapAlloc && afterGCHeapObjects < preHeapObjects)
   213  
   214  	// Trigger the finalizer of kclient be executed
   215  	time.Sleep(200 * time.Millisecond) // ensure the finalizer be executed
   216  	runtime.GC()
   217  	runtime.ReadMemStats(&ms)
   218  	secondGCHeapAlloc, secondGCHeapObjects := mb(ms.HeapAlloc), ms.HeapObjects
   219  	t.Logf("After second GC, allocation: %f Mb, Number of allocation: %d\n", secondGCHeapAlloc, secondGCHeapObjects)
   220  	test.Assert(t, secondGCHeapAlloc/2 < afterGCHeapAlloc && secondGCHeapObjects/2 < afterGCHeapObjects)
   221  }
   222  
   223  func TestBinaryThriftGenericClientFinalizer(t *testing.T) {
   224  	debug.SetGCPercent(-1)
   225  	defer debug.SetGCPercent(100)
   226  
   227  	var ms runtime.MemStats
   228  	runtime.ReadMemStats(&ms)
   229  	t.Logf("Before new clients, allocation: %f Mb, Number of allocation: %d\n", mb(ms.HeapAlloc), ms.HeapObjects)
   230  
   231  	cliCnt := 10000
   232  	clis := make([]genericclient.Client, cliCnt)
   233  	for i := 0; i < cliCnt; i++ {
   234  		g := generic.BinaryThriftGeneric()
   235  		clis[i] = newGenericClient("destServiceName", g, addr, client.WithShortConnection())
   236  	}
   237  
   238  	runtime.ReadMemStats(&ms)
   239  	t.Logf("After new clients, allocation: %f Mb, Number of allocation: %d\n", mb(ms.HeapAlloc), ms.HeapObjects)
   240  
   241  	runtime.GC()
   242  	runtime.ReadMemStats(&ms)
   243  	firstGCHeapAlloc, firstGCHeapObjects := mb(ms.HeapAlloc), ms.HeapObjects
   244  	t.Logf("After first GC, allocation: %f Mb, Number of allocation: %d\n", firstGCHeapAlloc, firstGCHeapObjects)
   245  
   246  	// Trigger the finalizer of generic client be executed
   247  	time.Sleep(200 * time.Millisecond) // ensure the finalizer be executed
   248  	runtime.GC()
   249  	runtime.ReadMemStats(&ms)
   250  	secondGCHeapAlloc, secondGCHeapObjects := mb(ms.HeapAlloc), ms.HeapObjects
   251  	t.Logf("After second GC, allocation: %f Mb, Number of allocation: %d\n", secondGCHeapAlloc, secondGCHeapObjects)
   252  	test.Assert(t, secondGCHeapAlloc < firstGCHeapAlloc && secondGCHeapObjects < firstGCHeapObjects)
   253  
   254  	// Trigger the finalizer of kClient be executed
   255  	time.Sleep(200 * time.Millisecond) // ensure the finalizer be executed
   256  	runtime.GC()
   257  	runtime.ReadMemStats(&ms)
   258  	thirdGCHeapAlloc, thirdGCHeapObjects := mb(ms.HeapAlloc), ms.HeapObjects
   259  	t.Logf("After third GC, allocation: %f Mb, Number of allocation: %d\n", thirdGCHeapAlloc, thirdGCHeapObjects)
   260  	test.Assert(t, thirdGCHeapAlloc < secondGCHeapAlloc/2 && thirdGCHeapObjects < secondGCHeapObjects/2)
   261  }
   262  
   263  func mb(byteSize uint64) float32 {
   264  	return float32(byteSize) / float32(1024*1024)
   265  }