Full Code of smallnest/rpcx for AI

master d57f1f75012c cached
128 files
428.0 KB
142.6k tokens
1003 symbols
1 requests
Download .txt
Showing preview only (458K chars total). Download the full file or copy to clipboard to get everything.
Repository: smallnest/rpcx
Branch: master
Commit: d57f1f75012c
Files: 128
Total size: 428.0 KB

Directory structure:
gitextract_r3mw_qpx/

├── .github/
│   └── workflows/
│       └── go.yml
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── Makefile
├── README.md
├── _testutils/
│   ├── GoUnusedProtection__.go
│   ├── arith_service.go
│   ├── arith_service.proto
│   ├── thrift_arith_service-consts.go
│   ├── thrift_arith_service.go
│   └── thrift_arith_service.thrift
├── client/
│   ├── cache_client_builder.go
│   ├── circuit_breaker.go
│   ├── circuit_breaker_test.go
│   ├── client.go
│   ├── client_test.go
│   ├── connection.go
│   ├── connection_iouring.go
│   ├── connection_iouring_test.go
│   ├── connection_kcp.go
│   ├── connection_memu.go
│   ├── connection_nonkcp.go
│   ├── connection_nonquic.go
│   ├── connection_quic.go
│   ├── connection_rdma.go
│   ├── discovery.go
│   ├── dns_discovery.go
│   ├── failmode_enumer.go
│   ├── geo_utils.go
│   ├── hash_utils.go
│   ├── mdns_discovery.go
│   ├── mode.go
│   ├── multiple_servers_discovery.go
│   ├── oneclient.go
│   ├── oneclient_pool.go
│   ├── oneclient_pool_test.go
│   ├── peer2peer_discovery.go
│   ├── ping_utils.go
│   ├── plugin.go
│   ├── selectmode_enumer.go
│   ├── selector.go
│   ├── selector_test.go
│   ├── smooth_weighted_round_robin.go
│   ├── xclient.go
│   ├── xclient_pool.go
│   ├── xclient_pool_test.go
│   └── xclient_test.go
├── clientplugin/
│   └── req_rate_limiting_redis.go
├── codec/
│   ├── codec.go
│   ├── codec_test.go
│   └── testdata/
│       ├── GoUnusedProtection__.go
│       ├── gen.sh
│       ├── protobuf.pb.go
│       ├── protobuf.proto
│       ├── thrift_colorgroup-consts.go
│       ├── thrift_colorgroup.go
│       └── thrift_colorgroup.thrift
├── errors/
│   ├── error.go
│   └── error_test.go
├── go.mod
├── go.sum
├── log/
│   ├── default_logger.go
│   ├── dummy_logger.go
│   └── logger.go
├── protocol/
│   ├── compressor.go
│   ├── compressor_test.go
│   ├── message.go
│   ├── message_chan_test.go
│   ├── message_test.go
│   └── testdata/
│       ├── benchmark.pb.go
│       ├── benchmark.proto
│       └── gen.sh
├── reflection/
│   ├── server_reflection.go
│   └── server_reflection_test.go
├── server/
│   ├── context.go
│   ├── converter.go
│   ├── converter_test.go
│   ├── file_transfer.go
│   ├── gateway.go
│   ├── jsonrpc2.go
│   ├── jsonrpc2_wire.go
│   ├── kcp.go
│   ├── listener.go
│   ├── listener_linux.go
│   ├── listener_rdma.go
│   ├── listener_unix.go
│   ├── memconn.go
│   ├── option.go
│   ├── option_test.go
│   ├── options.go
│   ├── plugin.go
│   ├── plugin_test.go
│   ├── pool.go
│   ├── pool_test.go
│   ├── quic.go
│   ├── router.go
│   ├── server.go
│   ├── server_test.go
│   ├── service.go
│   ├── service_test.go
│   └── stream.go
├── serverplugin/
│   ├── alias.go
│   ├── blacklist.go
│   ├── mdns.go
│   ├── metrics.go
│   ├── rate_limiting.go
│   ├── redis.go
│   ├── registry_test.go
│   ├── req_rate_limiting.go
│   ├── req_rate_limiting_redis.go
│   ├── tee.go
│   └── whitelist.go
├── share/
│   ├── context.go
│   ├── context_test.go
│   ├── share.go
│   └── share_test.go
├── tool/
│   └── xgen/
│       ├── README.md
│       ├── parser/
│       │   └── parser.go
│       └── xgen.go
└── util/
    ├── buffer_pool.go
    ├── buffer_pool_test.go
    ├── compress.go
    ├── compress_test.go
    ├── converter.go
    ├── net.go
    └── net_test.go

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/go.yml
================================================
name: Go

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:

  build:
    name: Build
    runs-on: ubuntu-latest
    steps:

    - name: Set up Go 1.x
      uses: actions/setup-go@v2
      with:
        go-version: ^1.13

    - name: Check out code into the Go module directory
      uses: actions/checkout@v2

    - name: Get dependencies
      run: |
        go get -v -t -d ./...

    - name: Build
      run: go build -v ./...

    - name: Test
      run: go test -v ./...


================================================
FILE: .gitignore
================================================
# 
vendor/

# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
.glide/
.idea

cover.html

.history

================================================
FILE: .travis.yml
================================================
language: go

go:
  - 1.17.x

env:
  - GO111MODULE=on

before_script:
  - rm -f go.sum
  - go get -tags "quic kcp" -v github.com/smallnest/rpcx@HEAD
  - go get github.com/mattn/goveralls

script:
  - go test -v ./...
  - goveralls -service=travis-ci

notifications:
  email:
    recipients: smallnest@gmail.com
    on_success: change
    on_failure: always


================================================
FILE: CHANGELOG.md
================================================
# [rpcx](http://rpcx.io)

## 1.9.0
- unregister all services on close automatically
- add PostHTTPRequestPlugin 
- support io_uring
- add CacheDiscovery
- add Oneshot method for XClient
- support RDMA


## 1.8.0
- supports distributed rate limiter based on go-redis/redis-rate
- move zookeeper plugin to https://github.com/smallnest/rpcx-zookeepr
- move consul plugin to https://github.com/smallnest/rpcx-consul
- move redis plugin to https://github.com/smallnest/rpcx-redis
- move influxd/opentelemetry plugin to https://github.com/smallnest/rpcx-plugins
- you can write customized error, for example `{"code": 500, err: "internal error"}`
- server support the work pool by `WithPool`
- support to write services like `go std http router` style without reflect
- simplify async write for service
- improve performance


## 1.7.0
- move etcd support to github.com/rpcxio/rpcx-etcd
- Broken API: NewXXXDiscovery returns error instead of panic
- support AdvertiseAddr in FileTransfer
- support Auth for OneClientPool
- support Auth for XClientPool
- Broken API: add meta parameter for SendFile/DownloadFile 
- support streaming between server side and client side
- support DNS as service discovery
- support rpcx flow tracing
- support websocket as the transport like tcp,kcp and quic
- add CMuxPlugin to allow developing customzied services by using the same single port
- re-tag rpcx to make sure the version is less than 2 (for go module)
- support visit grpc services by rpcx clients: https://github.com/rpcxio/rpcxplus/tree/master/grpcx
- support configing grpc servicves in rpcx server side
- improve rpcx performance
- add Inform method in XClient
- add memory connection for unit tests
- supports opentelemetry

## 1.6.0 

- support reflection
- add kubernetes config example
- improve nacos support
- improve message.Encode performance
- re-register services in etcd v3
- avoid duplicated client creation
- add SelectNodePlugin that can interrupt the Select method
- support TcpCopy by TeePlugin
- support reuseport for http invoke
- return reply even in case of server errors
- Change two methods' name of client plugin!
- Broken API: add error parameter in `PreWriteResponse`(#486)
- Broken API: change ReadTimeout/WriteTimeout to IdleTimeout
- Support passing Deadline of client contexts to server side
- remove InprocessClient plugin
- use heartbeat/tcp_keepalive to avoid client hanging


## 1.5.0 

- support jsonrpc 2.0
- support CORS for jsonrpc 2.0
- support opentracing and opencensus
- upload/download files by streaming
- add Pool for XClient and OneClient
- remove rudp support
- add ConnCreated plugin. Yu can use it to set KCP UDPSession
- update client plugins. All plugin returns error instead of bool
- support ETCD 3.0 API
- support redis as registry
- support redis DB selection
- fix RegisterFunction issues
- add Filter for clients
- remove most of build tags such as etcd, zookeeper,consul,reuseport
- add Nacos as registry http://nacos.io
- support blacklist and whitlist

## 1.4.0

- Support utp and rudp
- Add OneClient to support invoke multile servicesby one client
- Finish compress feature
- Add more plugins for monitoring connection
- Support dynamic port allocation
- Use go module to manage dependencies
- Support shutdown graceful
- Add [rpcx-java](https://github.com/smallnest/rpcx-java) to support develop raw java services and clients
- Support thrift codec 
- Setup rpcx offcial site: http://rpcx.io
- Add Chinese document: http://cn.doc.rpcx.io or https://smallnest.gitbooks.io/go-rpc-programming-guide

## 1.3.1

- Add http gateway: https://github.com/rpcxio/rpcx-gateway
- Add direct http invoke
- Add bidirectional communication 
- Add xgen tool to generate codes for services automatically


fix bugs.

## 1.3.0

- Rewrite rpcx. It implements its protocol and won't implemented based on wrapper of go standard rpc lib
- Add go tags for pluggable plugins
- Add English document: https://github.com/smallnest/rpcx-programming
- Add rpcx 3.0 examples: https://github.com/rpcxio/rpcx-examples

rpcx 3.0 is not compatible with rpcx 2.0 and below

================================================
FILE: LICENSE
================================================
Copyright  2017  Chao yuepan

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

================================================
FILE: Makefile
================================================
WORKDIR=`pwd`

default: build

vet:
	go vet ./...

tools:
	go get github.com/golangci/golangci-lint/cmd/golangci-lint
	go get github.com/golang/lint/golint
	go get github.com/axw/gocov/gocov
	go get github.com/matm/gocov-html

golangci-lint:
	golangci-lint run -D errcheck --build-tags 'quic kcp'

lint:
	golint ./...

doc:
	godoc -http=:6060

deps:
	go list -f '{{ join .Deps  "\n"}}' ./... |grep "/" | grep -v "github.com/smallnest/rpcx"| grep "\." | sort |uniq

fmt:
	go fmt ./...

build:
	go build ./...

build-all:
	go build -tags "kcp quic" ./...

test:
	go test -race -tags "kcp quic" ./...

cover:
	gocov test -tags "kcp quic" ./... | gocov-html > cover.html
	open cover.html

check-libs:
	GIT_TERMINAL_PROMPT=1 GO111MODULE=on go list -m -u all | column -t

update-libs:
	GIT_TERMINAL_PROMPT=1 GO111MODULE=on go get -u -v ./...

mod-tidy:
	GIT_TERMINAL_PROMPT=1 GO111MODULE=on go mod tidy


================================================
FILE: README.md
================================================
- **stable branch**: v1.7.x
- **development branch**: master

<a href="https://rpcx.io/"><img height="160" src="http://rpcx.io/logos/rpcx-logo-text.png"></a>

Official site: [http://rpcx.io](http://rpcx.io/)

[![License](https://img.shields.io/:license-apache%202-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![GoDoc](https://godoc.org/github.com/smallnest/rpcx?status.png)](http://godoc.org/github.com/smallnest/rpcx)  [![github actions](https://github.com/smallnest/rpcx/actions)](https://github.com/smallnest/rpcx/actions/workflows/go.yml/badge.svg) [![Go Report Card](https://goreportcard.com/badge/github.com/smallnest/rpcx)](https://goreportcard.com/report/github.com/smallnest/rpcx) [![coveralls](https://coveralls.io/repos/smallnest/rpcx/badge.svg?branch=master&service=github)](https://coveralls.io/github/smallnest/rpcx?branch=master) [![QQ3群](https://img.shields.io/:QQ3群-953962236-blue.svg)](_documents/rpcx_dev_qq3.jpg) 

**Notice: etcd**

since rpcx 1.7.6, some plugins have been moved to the independent project:

- `etcd` plugin has been moved to [rpcx-etcd](https://github.com/rpcxio/rpcx-etcd)
- `zookeeper` plugin has been moved to [rpcx-zookeeper](https://github.com/rpcxio/rpcx-zookeeper)
- `consul` plugin has been moved to [rpcx-consul](https://github.com/rpcxio/rpcx-consul)
- `redis` plugin has been moved to [rpcx-redis](https://github.com/rpcxio/rpcx-redis)
- `influxdb` plugin has been moved to [rpcx-plugins](https://github.com/rpcxio/rpcx-plugins)
- `opentelemetry` plugin has been moved to [rpcx-plugins](https://github.com/rpcxio/rpcx-plugins)

## Announce

A tcpdump-like tool added: [rpcxdump](https://github.com/smallnest/rpcxdump)。 You can use it to debug communications between rpcx services and clients.

![](https://github.com/smallnest/rpcxdump/blob/master/snapshoot.png)


## Cross-Languages
you can use other programming languages besides Go to access rpcx services.

- **rpcx-gateway**: You can write clients in any programming languages to call rpcx services via [rpcx-gateway](https://github.com/rpcxio/rpcx-gateway)
- **http invoke**: you can use the same http requests to access rpcx gateway
- **Java Services/Clients**: You can use [rpcx-java](https://github.com/smallnest/rpcx-java) to implement/access rpcx services via raw protocol.
- **rust rpcx**: You can write rpcx services in rust by [rpcx-rs](https://github.com/smallnest/rpcx-rs)

> If you can write Go methods, you can also write rpc services. It is so easy to write rpc applications with rpcx.

## Installation

install the basic features:

`go get -v github.com/smallnest/rpcx/...`


If you want to use `quic`、`kcp` registry, use those tags to `go get` 、 `go build` or `go run`. For example, if you want to use all features, you can:

```sh
go get -v -tags "quic kcp" github.com/smallnest/rpcx/...
```

**_tags_**:
- **quic**: support quic transport
- **kcp**: support kcp transport

## Which companies are using rpcx?

<p float="left">
  <img style="padding-bottom: 20px;" src="https://user-images.githubusercontent.com/865763/102993220-b967f000-4557-11eb-9747-703a6cbb9fb1.png" width="200" />
  <img style="padding-bottom: 20px;" src="https://user-images.githubusercontent.com/865763/113414067-a8e4d280-93ee-11eb-9f42-1373d7e766c1.png" width="200" />
  <img style="padding-bottom: 20px;" src="https://user-images.githubusercontent.com/865763/102993433-267b8580-4558-11eb-9e45-4e1a86d61688.png" width="200" /> 
  <img style="padding-bottom: 20px;" src="https://user-images.githubusercontent.com/865763/102993530-4743db00-4558-11eb-9f76-1ee69e992b82.png" width="200" />
  <img style="padding-bottom: 20px;" src="https://user-images.githubusercontent.com/865763/102993612-722e2f00-4558-11eb-849a-3264c430aef9.png" width="200" />
  <img style="padding-bottom: 20px;" src="https://user-images.githubusercontent.com/865763/102993785-c20cf600-4558-11eb-82b9-27b801aca4ff.png" width="200" />
</p>

## Features
rpcx is a RPC framework like [Alibaba Dubbo](http://dubbo.io/) and [Weibo Motan](https://github.com/weibocom/motan).

**rpcx** is created for targets:
1. **Simple**: easy to learn, easy to develop, easy to integrate and easy to deploy
2. **Performance**: high performance (>= grpc-go)
3. **Cross-platform**: support _raw slice of bytes_, _JSON_, _Protobuf_ and _MessagePack_. Theoretically it can be used with java, php, python, c/c++, node.js, c# and other platforms
4. **Service discovery and service governance**: support zookeeper, etcd and consul.


It contains below features
- Support raw Go functions. There's no need to define proto files.
- Pluggable. Features can be extended such as service discovery, tracing.
- Support TCP, HTTP, [QUIC](https://en.wikipedia.org/wiki/QUIC) and [KCP](https://github.com/skywind3000/kcp)
- Support multiple codecs such as JSON, Protobuf, [MessagePack](https://msgpack.org/index.html) and raw bytes.
- Service discovery. Support peer2peer, configured peers, [zookeeper](https://zookeeper.apache.org), [etcd](https://github.com/coreos/etcd), [consul](https://www.consul.io) and [mDNS](https://en.wikipedia.org/wiki/Multicast_DNS).
- Fault tolerance:Failover, Failfast, Failtry.
- Load banlancing:support Random, RoundRobin, Consistent hashing, Weighted, network quality and Geography.
- Support Compression.
- Support passing metadata.
- Support Authorization.
- Support heartbeat and one-way request.
- Other features: metrics, log, timeout, alias, circuit breaker.
- Support bidirectional communication.
- Support access via HTTP so you can write clients in any programming languages.
- Support API gateway.
- Support backup request, forking and broadcast.


rpcx uses a binary protocol and platform-independent, which means you can develop services in other languages such as Java, python, nodejs, and you can use other prorgramming languages to invoke services developed in Go.

There is a UI manager: [rpcx-ui](https://github.com/smallnest/rpcx-ui).

## Performance

Test results show rpcx has better performance than other rpc framework except standard rpc lib.


The benchmark code is at [rpcx-benchmark](https://github.com/rpcx-ecosystem/rpcx-benchmark).

**Listen to others, but test by yourself**.

**_Test Environment_**

- **CPU**: Intel(R) Xeon(R) CPU E5-2630 v3 @ 2.40GHz, 32 cores
- **Memory**: 32G
- **Go**: 1.9.0
- **OS**: CentOS 7 / 3.10.0-229.el7.x86_64

**_Use_**
- protobuf
- the client and the server on the same server
- 581 bytes payload
- 500/2000/5000 concurrent clients
- mock processing time: 0ms, 10ms and 30ms

**_Test Result_**

### mock 0ms process time

<table><tr><th>Throughputs</th><th>Mean Latency</th><th>P99 Latency</th></tr><tr><td width="30%"><img src="http://colobu.com/2018/01/31/benchmark-2018-spring-of-popular-rpc-frameworks/p0-throughput.png"></td><td width="30%"><img src="http://colobu.com/2018/01/31/benchmark-2018-spring-of-popular-rpc-frameworks/p0-latency.png"></td><td width="30%"><img src="http://colobu.com/2018/01/31/benchmark-2018-spring-of-popular-rpc-frameworks/p0-p99.png"></td></tr></table>


### mock 10ms process time

<table><tr><th>Throughputs</th><th>Mean Latency</th><th>P99 Latency</th></tr><tr><td width="30%"><img src="http://colobu.com/2018/01/31/benchmark-2018-spring-of-popular-rpc-frameworks/p10-throughput.png"></td><td width="30%"><img src="http://colobu.com/2018/01/31/benchmark-2018-spring-of-popular-rpc-frameworks/p10-latency.png"></td><td width="30%"><img src="http://colobu.com/2018/01/31/benchmark-2018-spring-of-popular-rpc-frameworks/p10-p99.png"></td></tr></table>


### mock 30ms process time

<table><tr><th>Throughputs</th><th>Mean Latency</th><th>P99 Latency</th></tr><tr><td width="30%"><img src="http://colobu.com/2018/01/31/benchmark-2018-spring-of-popular-rpc-frameworks/p30-throughput.png"></td><td width="30%"><img src="http://colobu.com/2018/01/31/benchmark-2018-spring-of-popular-rpc-frameworks/p30-latency.png"></td><td width="30%"><img src="http://colobu.com/2018/01/31/benchmark-2018-spring-of-popular-rpc-frameworks/p30-p99.png"></td></tr></table>


## Examples

You can find all examples at [rpcxio/rpcx-examples](https://github.com/rpcxio/rpcx-examples).

The below is a simple example.


**Server**

```go
    // define example.Arith
    ……

    s := server.NewServer()
	s.RegisterName("Arith", new(example.Arith), "")
	s.Serve("tcp", addr)

```


**Client**

```go
    // prepare requests
    ……

    d, err := client.NewPeer2PeerDiscovery("tcp@"+addr, "")
	xclient := client.NewXClient("Arith", client.Failtry, client.RandomSelect, d, client.DefaultOption)
	defer xclient.Close()
	err = xclient.Call(context.Background(), "Mul", args, reply, nil)
```

## Contributors

<a href="https://github.com/smallnest/rpcx/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=smallnest/rpcx" />
</a>


## Contribute

see [contributors](https://github.com/smallnest/rpcx/graphs/contributors).

Welcome to contribute:
- submit issues or requirements
- send PRs
- write projects to use rpcx
- write tutorials or articles to introduce rpcx

## License

Apache License, Version 2.0 


================================================
FILE: _testutils/GoUnusedProtection__.go
================================================
// Code generated by Thrift Compiler (0.14.0). DO NOT EDIT.

package testutils

var GoUnusedProtection__ int


================================================
FILE: _testutils/arith_service.go
================================================
// Code generated by protoc-gen-gogo.
// source: arith_service.proto
// DO NOT EDIT!

/*
	Package client is a generated protocol buffer package.

	It is generated from these files:
		arith_service.proto

	It has these top-level messages:
		ProtoArgs
		ProtoReply
*/
package testutils

import proto "github.com/gogo/protobuf/proto"
import fmt "fmt"
import math "math"

import io "io"

// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf

// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package

type ProtoArgs struct {
	A int32 `protobuf:"varint,1,opt,name=A,proto3" json:"A,omitempty"`
	B int32 `protobuf:"varint,2,opt,name=B,proto3" json:"B,omitempty"`
}

func (m *ProtoArgs) Reset()                    { *m = ProtoArgs{} }
func (m *ProtoArgs) String() string            { return proto.CompactTextString(m) }
func (*ProtoArgs) ProtoMessage()               {}
func (*ProtoArgs) Descriptor() ([]byte, []int) { return fileDescriptorArithService, []int{0} }

func (m *ProtoArgs) GetA() int32 {
	if m != nil {
		return m.A
	}
	return 0
}

func (m *ProtoArgs) GetB() int32 {
	if m != nil {
		return m.B
	}
	return 0
}

type ProtoReply struct {
	C int32 `protobuf:"varint,1,opt,name=C,proto3" json:"C,omitempty"`
}

func (m *ProtoReply) Reset()                    { *m = ProtoReply{} }
func (m *ProtoReply) String() string            { return proto.CompactTextString(m) }
func (*ProtoReply) ProtoMessage()               {}
func (*ProtoReply) Descriptor() ([]byte, []int) { return fileDescriptorArithService, []int{1} }

func (m *ProtoReply) GetC() int32 {
	if m != nil {
		return m.C
	}
	return 0
}

func init() {
	proto.RegisterType((*ProtoArgs)(nil), "client.ProtoArgs")
	proto.RegisterType((*ProtoReply)(nil), "client.ProtoReply")
}
func (m *ProtoArgs) Marshal() (dAtA []byte, err error) {
	size := m.Size()
	dAtA = make([]byte, size)
	n, err := m.MarshalTo(dAtA)
	if err != nil {
		return nil, err
	}
	return dAtA[:n], nil
}

func (m *ProtoArgs) MarshalTo(dAtA []byte) (int, error) {
	var i int
	_ = i
	var l int
	_ = l
	if m.A != 0 {
		dAtA[i] = 0x8
		i++
		i = encodeVarintArithService(dAtA, i, uint64(m.A))
	}
	if m.B != 0 {
		dAtA[i] = 0x10
		i++
		i = encodeVarintArithService(dAtA, i, uint64(m.B))
	}
	return i, nil
}

func (m *ProtoReply) Marshal() (dAtA []byte, err error) {
	size := m.Size()
	dAtA = make([]byte, size)
	n, err := m.MarshalTo(dAtA)
	if err != nil {
		return nil, err
	}
	return dAtA[:n], nil
}

func (m *ProtoReply) MarshalTo(dAtA []byte) (int, error) {
	var i int
	_ = i
	var l int
	_ = l
	if m.C != 0 {
		dAtA[i] = 0x8
		i++
		i = encodeVarintArithService(dAtA, i, uint64(m.C))
	}
	return i, nil
}

func encodeFixed64ArithService(dAtA []byte, offset int, v uint64) int {
	dAtA[offset] = uint8(v)
	dAtA[offset+1] = uint8(v >> 8)
	dAtA[offset+2] = uint8(v >> 16)
	dAtA[offset+3] = uint8(v >> 24)
	dAtA[offset+4] = uint8(v >> 32)
	dAtA[offset+5] = uint8(v >> 40)
	dAtA[offset+6] = uint8(v >> 48)
	dAtA[offset+7] = uint8(v >> 56)
	return offset + 8
}
func encodeFixed32ArithService(dAtA []byte, offset int, v uint32) int {
	dAtA[offset] = uint8(v)
	dAtA[offset+1] = uint8(v >> 8)
	dAtA[offset+2] = uint8(v >> 16)
	dAtA[offset+3] = uint8(v >> 24)
	return offset + 4
}
func encodeVarintArithService(dAtA []byte, offset int, v uint64) int {
	for v >= 1<<7 {
		dAtA[offset] = uint8(v&0x7f | 0x80)
		v >>= 7
		offset++
	}
	dAtA[offset] = uint8(v)
	return offset + 1
}
func (m *ProtoArgs) Size() (n int) {
	var l int
	_ = l
	if m.A != 0 {
		n += 1 + sovArithService(uint64(m.A))
	}
	if m.B != 0 {
		n += 1 + sovArithService(uint64(m.B))
	}
	return n
}

func (m *ProtoReply) Size() (n int) {
	var l int
	_ = l
	if m.C != 0 {
		n += 1 + sovArithService(uint64(m.C))
	}
	return n
}

func sovArithService(x uint64) (n int) {
	for {
		n++
		x >>= 7
		if x == 0 {
			break
		}
	}
	return n
}
func sozArithService(x uint64) (n int) {
	return sovArithService(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *ProtoArgs) Unmarshal(dAtA []byte) error {
	l := len(dAtA)
	iNdEx := 0
	for iNdEx < l {
		preIndex := iNdEx
		var wire uint64
		for shift := uint(0); ; shift += 7 {
			if shift >= 64 {
				return ErrIntOverflowArithService
			}
			if iNdEx >= l {
				return io.ErrUnexpectedEOF
			}
			b := dAtA[iNdEx]
			iNdEx++
			wire |= (uint64(b) & 0x7F) << shift
			if b < 0x80 {
				break
			}
		}
		fieldNum := int32(wire >> 3)
		wireType := int(wire & 0x7)
		if wireType == 4 {
			return fmt.Errorf("proto: ProtoArgs: wiretype end group for non-group")
		}
		if fieldNum <= 0 {
			return fmt.Errorf("proto: ProtoArgs: illegal tag %d (wire type %d)", fieldNum, wire)
		}
		switch fieldNum {
		case 1:
			if wireType != 0 {
				return fmt.Errorf("proto: wrong wireType = %d for field A", wireType)
			}
			m.A = 0
			for shift := uint(0); ; shift += 7 {
				if shift >= 64 {
					return ErrIntOverflowArithService
				}
				if iNdEx >= l {
					return io.ErrUnexpectedEOF
				}
				b := dAtA[iNdEx]
				iNdEx++
				m.A |= (int32(b) & 0x7F) << shift
				if b < 0x80 {
					break
				}
			}
		case 2:
			if wireType != 0 {
				return fmt.Errorf("proto: wrong wireType = %d for field B", wireType)
			}
			m.B = 0
			for shift := uint(0); ; shift += 7 {
				if shift >= 64 {
					return ErrIntOverflowArithService
				}
				if iNdEx >= l {
					return io.ErrUnexpectedEOF
				}
				b := dAtA[iNdEx]
				iNdEx++
				m.B |= (int32(b) & 0x7F) << shift
				if b < 0x80 {
					break
				}
			}
		default:
			iNdEx = preIndex
			skippy, err := skipArithService(dAtA[iNdEx:])
			if err != nil {
				return err
			}
			if skippy < 0 {
				return ErrInvalidLengthArithService
			}
			if (iNdEx + skippy) > l {
				return io.ErrUnexpectedEOF
			}
			iNdEx += skippy
		}
	}

	if iNdEx > l {
		return io.ErrUnexpectedEOF
	}
	return nil
}
func (m *ProtoReply) Unmarshal(dAtA []byte) error {
	l := len(dAtA)
	iNdEx := 0
	for iNdEx < l {
		preIndex := iNdEx
		var wire uint64
		for shift := uint(0); ; shift += 7 {
			if shift >= 64 {
				return ErrIntOverflowArithService
			}
			if iNdEx >= l {
				return io.ErrUnexpectedEOF
			}
			b := dAtA[iNdEx]
			iNdEx++
			wire |= (uint64(b) & 0x7F) << shift
			if b < 0x80 {
				break
			}
		}
		fieldNum := int32(wire >> 3)
		wireType := int(wire & 0x7)
		if wireType == 4 {
			return fmt.Errorf("proto: ProtoReply: wiretype end group for non-group")
		}
		if fieldNum <= 0 {
			return fmt.Errorf("proto: ProtoReply: illegal tag %d (wire type %d)", fieldNum, wire)
		}
		switch fieldNum {
		case 1:
			if wireType != 0 {
				return fmt.Errorf("proto: wrong wireType = %d for field C", wireType)
			}
			m.C = 0
			for shift := uint(0); ; shift += 7 {
				if shift >= 64 {
					return ErrIntOverflowArithService
				}
				if iNdEx >= l {
					return io.ErrUnexpectedEOF
				}
				b := dAtA[iNdEx]
				iNdEx++
				m.C |= (int32(b) & 0x7F) << shift
				if b < 0x80 {
					break
				}
			}
		default:
			iNdEx = preIndex
			skippy, err := skipArithService(dAtA[iNdEx:])
			if err != nil {
				return err
			}
			if skippy < 0 {
				return ErrInvalidLengthArithService
			}
			if (iNdEx + skippy) > l {
				return io.ErrUnexpectedEOF
			}
			iNdEx += skippy
		}
	}

	if iNdEx > l {
		return io.ErrUnexpectedEOF
	}
	return nil
}
func skipArithService(dAtA []byte) (n int, err error) {
	l := len(dAtA)
	iNdEx := 0
	for iNdEx < l {
		var wire uint64
		for shift := uint(0); ; shift += 7 {
			if shift >= 64 {
				return 0, ErrIntOverflowArithService
			}
			if iNdEx >= l {
				return 0, io.ErrUnexpectedEOF
			}
			b := dAtA[iNdEx]
			iNdEx++
			wire |= (uint64(b) & 0x7F) << shift
			if b < 0x80 {
				break
			}
		}
		wireType := int(wire & 0x7)
		switch wireType {
		case 0:
			for shift := uint(0); ; shift += 7 {
				if shift >= 64 {
					return 0, ErrIntOverflowArithService
				}
				if iNdEx >= l {
					return 0, io.ErrUnexpectedEOF
				}
				iNdEx++
				if dAtA[iNdEx-1] < 0x80 {
					break
				}
			}
			return iNdEx, nil
		case 1:
			iNdEx += 8
			return iNdEx, nil
		case 2:
			var length int
			for shift := uint(0); ; shift += 7 {
				if shift >= 64 {
					return 0, ErrIntOverflowArithService
				}
				if iNdEx >= l {
					return 0, io.ErrUnexpectedEOF
				}
				b := dAtA[iNdEx]
				iNdEx++
				length |= (int(b) & 0x7F) << shift
				if b < 0x80 {
					break
				}
			}
			iNdEx += length
			if length < 0 {
				return 0, ErrInvalidLengthArithService
			}
			return iNdEx, nil
		case 3:
			for {
				var innerWire uint64
				var start int = iNdEx
				for shift := uint(0); ; shift += 7 {
					if shift >= 64 {
						return 0, ErrIntOverflowArithService
					}
					if iNdEx >= l {
						return 0, io.ErrUnexpectedEOF
					}
					b := dAtA[iNdEx]
					iNdEx++
					innerWire |= (uint64(b) & 0x7F) << shift
					if b < 0x80 {
						break
					}
				}
				innerWireType := int(innerWire & 0x7)
				if innerWireType == 4 {
					break
				}
				next, err := skipArithService(dAtA[start:])
				if err != nil {
					return 0, err
				}
				iNdEx = start + next
			}
			return iNdEx, nil
		case 4:
			return iNdEx, nil
		case 5:
			iNdEx += 4
			return iNdEx, nil
		default:
			return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
		}
	}
	panic("unreachable")
}

var (
	ErrInvalidLengthArithService = fmt.Errorf("proto: negative length found during unmarshaling")
	ErrIntOverflowArithService   = fmt.Errorf("proto: integer overflow")
)

func init() { proto.RegisterFile("arith_service.proto", fileDescriptorArithService) }

var fileDescriptorArithService = []byte{
	// 126 bytes of a gzipped FileDescriptorProto
	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4e, 0x2c, 0xca, 0x2c,
	0xc9, 0x88, 0x2f, 0x4e, 0x2d, 0x2a, 0xcb, 0x4c, 0x4e, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17,
	0x62, 0x4b, 0xce, 0xc9, 0x4c, 0xcd, 0x2b, 0x51, 0x52, 0xe7, 0xe2, 0x0c, 0x00, 0x09, 0x38, 0x16,
	0xa5, 0x17, 0x0b, 0xf1, 0x70, 0x31, 0x3a, 0x4a, 0x30, 0x2a, 0x30, 0x6a, 0xb0, 0x06, 0x31, 0x3a,
	0x82, 0x78, 0x4e, 0x12, 0x4c, 0x10, 0x9e, 0x93, 0x92, 0x14, 0x17, 0x17, 0x58, 0x61, 0x50, 0x6a,
	0x41, 0x4e, 0x25, 0x48, 0xce, 0x19, 0xa6, 0xd2, 0xd9, 0x49, 0xe0, 0xc4, 0x23, 0x39, 0xc6, 0x0b,
	0x8f, 0xe4, 0x18, 0x1f, 0x3c, 0x92, 0x63, 0x9c, 0xf0, 0x58, 0x8e, 0x21, 0x89, 0x0d, 0x6c, 0x8b,
	0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0xfa, 0x7e, 0x25, 0x8d, 0x7c, 0x00, 0x00, 0x00,
}


================================================
FILE: _testutils/arith_service.proto
================================================
// protoc --gogofaster_out=. arith_service.proto
// mv arith_service.pb.go arith_service_test.go 
syntax = "proto3";

package client;

message ProtoArgs { 
    int32 A = 1;
    int32 B = 2;
}

message ProtoReply { 
    int32 C = 1;
}


================================================
FILE: _testutils/thrift_arith_service-consts.go
================================================
// Code generated by Thrift Compiler (0.14.0). DO NOT EDIT.

package testutils

import (
	"bytes"
	"context"
	"fmt"
	"github.com/apache/thrift/lib/go/thrift"
	"time"
)

// (needed to ensure safety because of naive import list construction.)
var _ = thrift.ZERO
var _ = fmt.Printf
var _ = context.Background
var _ = time.Now
var _ = bytes.Equal

func init() {
}


================================================
FILE: _testutils/thrift_arith_service.go
================================================
// Code generated by Thrift Compiler (0.14.0). DO NOT EDIT.

package testutils

import (
	"bytes"
	"context"
	"fmt"
	"github.com/apache/thrift/lib/go/thrift"
	"time"
)

// (needed to ensure safety because of naive import list construction.)
var _ = thrift.ZERO
var _ = fmt.Printf
var _ = context.Background
var _ = time.Now
var _ = bytes.Equal

// Attributes:
//  - A
//  - B
type ThriftArgs_ struct {
	A int32 `thrift:"a,1" db:"a" json:"a"`
	B int32 `thrift:"b,2" db:"b" json:"b"`
}

func NewThriftArgs_() *ThriftArgs_ {
	return &ThriftArgs_{}
}

func (p *ThriftArgs_) GetA() int32 {
	return p.A
}

func (p *ThriftArgs_) GetB() int32 {
	return p.B
}
func (p *ThriftArgs_) Read(ctx context.Context, iprot thrift.TProtocol) error {
	if _, err := iprot.ReadStructBegin(ctx); err != nil {
		return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err)
	}

	for {
		_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx)
		if err != nil {
			return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err)
		}
		if fieldTypeId == thrift.STOP {
			break
		}
		switch fieldId {
		case 1:
			if fieldTypeId == thrift.I32 {
				if err := p.ReadField1(ctx, iprot); err != nil {
					return err
				}
			} else {
				if err := iprot.Skip(ctx, fieldTypeId); err != nil {
					return err
				}
			}
		case 2:
			if fieldTypeId == thrift.I32 {
				if err := p.ReadField2(ctx, iprot); err != nil {
					return err
				}
			} else {
				if err := iprot.Skip(ctx, fieldTypeId); err != nil {
					return err
				}
			}
		default:
			if err := iprot.Skip(ctx, fieldTypeId); err != nil {
				return err
			}
		}
		if err := iprot.ReadFieldEnd(ctx); err != nil {
			return err
		}
	}
	if err := iprot.ReadStructEnd(ctx); err != nil {
		return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err)
	}
	return nil
}

func (p *ThriftArgs_) ReadField1(ctx context.Context, iprot thrift.TProtocol) error {
	if v, err := iprot.ReadI32(ctx); err != nil {
		return thrift.PrependError("error reading field 1: ", err)
	} else {
		p.A = v
	}
	return nil
}

func (p *ThriftArgs_) ReadField2(ctx context.Context, iprot thrift.TProtocol) error {
	if v, err := iprot.ReadI32(ctx); err != nil {
		return thrift.PrependError("error reading field 2: ", err)
	} else {
		p.B = v
	}
	return nil
}

func (p *ThriftArgs_) Write(ctx context.Context, oprot thrift.TProtocol) error {
	if err := oprot.WriteStructBegin(ctx, "ThriftArgs"); err != nil {
		return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err)
	}
	if p != nil {
		if err := p.writeField1(ctx, oprot); err != nil {
			return err
		}
		if err := p.writeField2(ctx, oprot); err != nil {
			return err
		}
	}
	if err := oprot.WriteFieldStop(ctx); err != nil {
		return thrift.PrependError("write field stop error: ", err)
	}
	if err := oprot.WriteStructEnd(ctx); err != nil {
		return thrift.PrependError("write struct stop error: ", err)
	}
	return nil
}

func (p *ThriftArgs_) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) {
	if err := oprot.WriteFieldBegin(ctx, "a", thrift.I32, 1); err != nil {
		return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:a: ", p), err)
	}
	if err := oprot.WriteI32(ctx, int32(p.A)); err != nil {
		return thrift.PrependError(fmt.Sprintf("%T.a (1) field write error: ", p), err)
	}
	if err := oprot.WriteFieldEnd(ctx); err != nil {
		return thrift.PrependError(fmt.Sprintf("%T write field end error 1:a: ", p), err)
	}
	return err
}

func (p *ThriftArgs_) writeField2(ctx context.Context, oprot thrift.TProtocol) (err error) {
	if err := oprot.WriteFieldBegin(ctx, "b", thrift.I32, 2); err != nil {
		return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:b: ", p), err)
	}
	if err := oprot.WriteI32(ctx, int32(p.B)); err != nil {
		return thrift.PrependError(fmt.Sprintf("%T.b (2) field write error: ", p), err)
	}
	if err := oprot.WriteFieldEnd(ctx); err != nil {
		return thrift.PrependError(fmt.Sprintf("%T write field end error 2:b: ", p), err)
	}
	return err
}

func (p *ThriftArgs_) Equals(other *ThriftArgs_) bool {
	if p == other {
		return true
	} else if p == nil || other == nil {
		return false
	}
	if p.A != other.A {
		return false
	}
	if p.B != other.B {
		return false
	}
	return true
}

func (p *ThriftArgs_) String() string {
	if p == nil {
		return "<nil>"
	}
	return fmt.Sprintf("ThriftArgs_(%+v)", *p)
}

// Attributes:
//  - C
type ThriftReply struct {
	C int32 `thrift:"c,1" db:"c" json:"c"`
}

func NewThriftReply() *ThriftReply {
	return &ThriftReply{}
}

func (p *ThriftReply) GetC() int32 {
	return p.C
}
func (p *ThriftReply) Read(ctx context.Context, iprot thrift.TProtocol) error {
	if _, err := iprot.ReadStructBegin(ctx); err != nil {
		return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err)
	}

	for {
		_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx)
		if err != nil {
			return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err)
		}
		if fieldTypeId == thrift.STOP {
			break
		}
		switch fieldId {
		case 1:
			if fieldTypeId == thrift.I32 {
				if err := p.ReadField1(ctx, iprot); err != nil {
					return err
				}
			} else {
				if err := iprot.Skip(ctx, fieldTypeId); err != nil {
					return err
				}
			}
		default:
			if err := iprot.Skip(ctx, fieldTypeId); err != nil {
				return err
			}
		}
		if err := iprot.ReadFieldEnd(ctx); err != nil {
			return err
		}
	}
	if err := iprot.ReadStructEnd(ctx); err != nil {
		return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err)
	}
	return nil
}

func (p *ThriftReply) ReadField1(ctx context.Context, iprot thrift.TProtocol) error {
	if v, err := iprot.ReadI32(ctx); err != nil {
		return thrift.PrependError("error reading field 1: ", err)
	} else {
		p.C = v
	}
	return nil
}

func (p *ThriftReply) Write(ctx context.Context, oprot thrift.TProtocol) error {
	if err := oprot.WriteStructBegin(ctx, "ThriftReply"); err != nil {
		return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err)
	}
	if p != nil {
		if err := p.writeField1(ctx, oprot); err != nil {
			return err
		}
	}
	if err := oprot.WriteFieldStop(ctx); err != nil {
		return thrift.PrependError("write field stop error: ", err)
	}
	if err := oprot.WriteStructEnd(ctx); err != nil {
		return thrift.PrependError("write struct stop error: ", err)
	}
	return nil
}

func (p *ThriftReply) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) {
	if err := oprot.WriteFieldBegin(ctx, "c", thrift.I32, 1); err != nil {
		return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:c: ", p), err)
	}
	if err := oprot.WriteI32(ctx, int32(p.C)); err != nil {
		return thrift.PrependError(fmt.Sprintf("%T.c (1) field write error: ", p), err)
	}
	if err := oprot.WriteFieldEnd(ctx); err != nil {
		return thrift.PrependError(fmt.Sprintf("%T write field end error 1:c: ", p), err)
	}
	return err
}

func (p *ThriftReply) Equals(other *ThriftReply) bool {
	if p == other {
		return true
	} else if p == nil || other == nil {
		return false
	}
	if p.C != other.C {
		return false
	}
	return true
}

func (p *ThriftReply) String() string {
	if p == nil {
		return "<nil>"
	}
	return fmt.Sprintf("ThriftReply(%+v)", *p)
}


================================================
FILE: _testutils/thrift_arith_service.thrift
================================================
struct ThriftArgs
{
    1: i32 a,
    2: i32 b,
}


struct ThriftReply
{
    1: i32 c,
}

================================================
FILE: client/cache_client_builder.go
================================================
package client

import "sync"

// CacheClientBuilder defines builder interface to generate RPCCient.
type CacheClientBuilder interface {
	SetCachedClient(client RPCClient, k, servicePath, serviceMethod string)
	FindCachedClient(k, servicePath, serviceMethod string) RPCClient
	DeleteCachedClient(client RPCClient, k, servicePath, serviceMethod string)
	GenerateClient(k, servicePath, serviceMethod string) (client RPCClient, err error)
}

// RegisterCacheClientBuilder(network string, builder CacheClientBuilder)

var cacheClientBuildersMutex sync.RWMutex
var cacheClientBuilders = make(map[string]CacheClientBuilder)

func RegisterCacheClientBuilder(network string, builder CacheClientBuilder) {
	cacheClientBuildersMutex.Lock()
	defer cacheClientBuildersMutex.Unlock()

	cacheClientBuilders[network] = builder
}

func getCacheClientBuilder(network string) (CacheClientBuilder, bool) {
	cacheClientBuildersMutex.RLock()
	defer cacheClientBuildersMutex.RUnlock()

	builder, ok := cacheClientBuilders[network]
	return builder, ok
}


================================================
FILE: client/circuit_breaker.go
================================================
package client

import (
	"errors"
	"sync/atomic"
	"time"
)

var (
	ErrBreakerOpen    = errors.New("breaker open")
	ErrBreakerTimeout = errors.New("breaker time out")
)

// ConsecCircuitBreaker is window sliding CircuitBreaker with failure threshold.
type ConsecCircuitBreaker struct {
	lastFailureTime int64

	failures         uint64
	failureThreshold uint64
	window           time.Duration
}

// NewConsecCircuitBreaker returns a new ConsecCircuitBreaker.
func NewConsecCircuitBreaker(failureThreshold uint64, window time.Duration) *ConsecCircuitBreaker {
	return &ConsecCircuitBreaker{
		failureThreshold: failureThreshold,
		window:           window,
	}
}

// Call Circuit function
func (cb *ConsecCircuitBreaker) Call(fn func() error, d time.Duration) error {
	var err error

	if !cb.ready() {
		return ErrBreakerOpen
	}

	if d == 0 {
		err = fn()
	} else {
		c := make(chan error, 1)
		go func() {
			c <- fn()
			close(c)
		}()

		t := time.NewTimer(d)
		select {
		case e := <-c:
			err = e
		case <-t.C:
			err = ErrBreakerTimeout
		}
		t.Stop()
	}

	if err == nil {
		cb.success()
	} else {
		cb.fail()
	}

	return err
}

func (cb *ConsecCircuitBreaker) ready() bool {
	lastFailureTime := time.Unix(0, atomic.LoadInt64(&cb.lastFailureTime))
	if time.Since(lastFailureTime) > cb.window {
		cb.reset()
		return true
	}

	failures := atomic.LoadUint64(&cb.failures)
	return failures < cb.failureThreshold
}

func (cb *ConsecCircuitBreaker) success() {
	cb.reset()
}

func (cb *ConsecCircuitBreaker) fail() {
	atomic.AddUint64(&cb.failures, 1)
	atomic.StoreInt64(&cb.lastFailureTime, time.Now().UnixNano())
}

func (cb *ConsecCircuitBreaker) Success() {
	cb.success()
}

func (cb *ConsecCircuitBreaker) Fail() {
	cb.fail()
}

func (cb *ConsecCircuitBreaker) Ready() bool {
	return cb.ready()
}

func (cb *ConsecCircuitBreaker) reset() {
	atomic.StoreUint64(&cb.failures, 0)
	atomic.StoreInt64(&cb.lastFailureTime, time.Now().UnixNano())
}


================================================
FILE: client/circuit_breaker_test.go
================================================
package client

import (
	"errors"
	"math/rand"
	"testing"
	"time"
)

func TestConsecCircuitBreaker(t *testing.T) {
	count := -1
	fn := func() error {
		count++
		if count >= 5 && count < 10 {
			return nil
		}

		return errors.New("test error")
	}

	cb := NewConsecCircuitBreaker(5, 100*time.Millisecond)

	for i := 0; i < 25; i++ {
		err := cb.Call(fn, 200*time.Millisecond)
		switch {
		case i < 5:
			if err.Error() != "test error" {
				t.Fatalf("expect %v, got %v", "test error", err)
			}
		case i >= 5 && i < 10:
			if err != ErrBreakerOpen {
				t.Fatalf("expect %v, got %v", ErrBreakerOpen, err)
			}
		case i >= 10 && i < 15:
			if err != nil {
				t.Fatalf("expect success, got %v", err)
			}
		case i >= 15 && i < 20:
			if err.Error() != "test error" {
				t.Fatalf("expect %v, got %v", "test error", err)
			}
		case i >= 20 && i < 25:
			if err != ErrBreakerOpen {
				t.Fatalf("expect %v, got %v", ErrBreakerOpen, err)
			}
		}

		if i == 9 { // expired
			time.Sleep(150 * time.Millisecond)
		}

	}

}

func TestCircuitBreakerRace(t *testing.T) {
	cb := NewConsecCircuitBreaker(2, 50*time.Millisecond)
	routines := 100
	loop := 100000

	fn := func() error {
		if rand.Intn(2) == 1 {
			return nil
		}
		return errors.New("test error")
	}

	for r := 0; r < routines; r++ {
		go func() {
			for i := 0; i < loop; i++ {
				cb.Call(fn, 100*time.Millisecond)
			}
		}()
	}
}


================================================
FILE: client/client.go
================================================
package client

import (
	"bufio"
	"context"
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"net"
	"net/url"
	"strconv"
	"strings"
	"sync"
	"time"

	circuit "github.com/rubyist/circuitbreaker"
	"github.com/smallnest/rpcx/log"
	"github.com/smallnest/rpcx/protocol"
	"github.com/smallnest/rpcx/share"
)

const (
	XVersion           = "X-RPCX-Version"
	XMessageType       = "X-RPCX-MesssageType"
	XHeartbeat         = "X-RPCX-Heartbeat"
	XOneway            = "X-RPCX-Oneway"
	XMessageStatusType = "X-RPCX-MessageStatusType"
	XSerializeType     = "X-RPCX-SerializeType"
	XMessageID         = "X-RPCX-MessageID"
	XServicePath       = "X-RPCX-ServicePath"
	XServiceMethod     = "X-RPCX-ServiceMethod"
	XMeta              = "X-RPCX-Meta"
	XErrorMessage      = "X-RPCX-ErrorMessage"
)

// ServiceError is an error from server.
type ServiceError interface {
	Error() string
	IsServiceError() bool
}

// NewServiceError creates a ServiceError with the error message.
func NewServiceError(s string) ServiceError {
	return strErr(s)
}

// ClientErrorFunc is a function to create a customized error.
var ClientErrorFunc func(res *protocol.Message, e string) ServiceError

type strErr string

func (s strErr) Error() string {
	return string(s)
}

func (s strErr) IsServiceError() bool {
	return true
}

// DefaultOption is a common option configuration for client.
var DefaultOption = Option{
	Retries:             3,
	RPCPath:             share.DefaultRPCPath,
	ConnectTimeout:      time.Second,
	SerializeType:       protocol.MsgPack,
	CompressType:        protocol.None,
	BackupLatency:       10 * time.Millisecond,
	MaxWaitForHeartbeat: 30 * time.Second,
	TCPKeepAlivePeriod:  time.Minute,
	BidirectionalBlock:  false,
	TimeToDisallow:      time.Minute,
}

// Breaker is a CircuitBreaker interface.
type Breaker interface {
	Call(func() error, time.Duration) error
	Fail()
	Success()
	Ready() bool
}

// CircuitBreaker is a default circuit breaker (RateBreaker(0.95, 100)).
var CircuitBreaker Breaker = circuit.NewRateBreaker(0.95, 100)

// ErrShutdown connection is closed.
var (
	ErrShutdown         = errors.New("connection is shut down")
	ErrUnsupportedCodec = errors.New("unsupported codec")
)

const (
	// ReaderBuffsize is used for bufio reader.
	ReaderBuffsize = 16 * 1024
	// WriterBuffsize is used for bufio writer.
	WriterBuffsize = 16 * 1024
)

type seqKey struct{}

// RPCClient is interface that defines one client to call one server.
type RPCClient interface {
	Connect(network, address string) error
	Go(ctx context.Context, servicePath, serviceMethod string, args interface{}, reply interface{}, done chan *Call) *Call
	Call(ctx context.Context, servicePath, serviceMethod string, args interface{}, reply interface{}) error
	SendRaw(ctx context.Context, r *protocol.Message) (map[string]string, []byte, error)
	Close() error
	RemoteAddr() string

	RegisterServerMessageChan(ch chan<- *protocol.Message)
	UnregisterServerMessageChan()

	IsClosing() bool
	IsShutdown() bool

	GetConn() net.Conn
}

// Client represents a RPC client.
type Client struct {
	option Option

	Conn net.Conn
	r    *bufio.Reader
	// w    *bufio.Writer

	mutex        sync.Mutex // protects following
	seq          uint64
	pending      map[uint64]*Call
	closing      bool // user has called Close
	shutdown     bool // server has told us to stop
	pluginClosed bool // the plugin has been called

	Plugins PluginContainer

	ServerMessageChan chan<- *protocol.Message
}

// NewClient returns a new Client with the option.
func NewClient(option Option) *Client {
	return &Client{
		option: option,
	}
}

// RemoteAddr returns the remote address.
func (client *Client) RemoteAddr() string {
	return client.Conn.RemoteAddr().String()
}

// GetConn returns the underlying conn.
func (client *Client) GetConn() net.Conn {
	return client.Conn
}

// Option contains all options for creating clients.
type Option struct {
	// Group is used to select the services in the same group. Services set group info in their meta.
	// If it is empty, clients will ignore group.
	Group string

	// Retries retries to send
	Retries int
	// RetryInterval is the interval between retries
	RetryInterval time.Duration
	// Time to disallow the bad server not to be selected
	TimeToDisallow time.Duration

	// TLSConfig for tcp and quic
	TLSConfig *tls.Config
	// kcp.BlockCrypt
	Block interface{}
	// RPCPath for http connection
	RPCPath string
	// ConnectTimeout sets timeout for dialing
	ConnectTimeout time.Duration
	// IdleTimeout sets max idle time for underlying net.Conns
	IdleTimeout time.Duration

	// BackupLatency is used for Failbackup mode. rpcx will sends another request if the first response doesn't return in BackupLatency time.
	BackupLatency time.Duration

	// Breaker is used to config CircuitBreaker
	GenBreaker func() Breaker

	SerializeType protocol.SerializeType
	CompressType  protocol.CompressType

	// send heartbeat message to service and check responses
	Heartbeat bool
	// interval for heartbeat
	HeartbeatInterval   time.Duration
	MaxWaitForHeartbeat time.Duration

	// TCPKeepAlive, if it is zero we don't set keepalive
	TCPKeepAlivePeriod time.Duration
	// bidirectional mode, if true serverMessageChan will block to wait message for consume. default false.
	BidirectionalBlock bool

	// alaways use the selected server until it is bad
	Sticky bool

	// not call server message handler
	NilCallServerMessageHandler func(msg *protocol.Message)
}

// Call represents an active RPC.
type Call struct {
	ServicePath   string            // The name of the service and method to call.
	ServiceMethod string            // The name of the service and method to call.
	Metadata      map[string]string // metadata
	ResMetadata   map[string]string
	Args          interface{} // The argument to the function (*struct).
	Reply         interface{} // The reply from the function (*struct).
	Error         error       // After completion, the error status.
	Done          chan *Call  // Strobes when call is complete.
	Raw           bool        // raw message or not
}

func (call *Call) done() {
	if call.Done == nil { // Oneshot
		return
	}

	select {
	case call.Done <- call:
		// ok
	default:
		log.Debug("rpc: discarding Call reply due to insufficient Done chan capacity")

	}
}

// RegisterServerMessageChan registers the channel that receives server requests.
func (client *Client) RegisterServerMessageChan(ch chan<- *protocol.Message) {
	client.ServerMessageChan = ch
}

// UnregisterServerMessageChan removes ServerMessageChan.
func (client *Client) UnregisterServerMessageChan() {
	client.ServerMessageChan = nil
}

// IsClosing client is closing or not.
func (client *Client) IsClosing() bool {
	client.mutex.Lock()
	defer client.mutex.Unlock()
	return client.closing
}

// IsShutdown client is shutdown or not.
func (client *Client) IsShutdown() bool {
	client.mutex.Lock()
	defer client.mutex.Unlock()
	return client.shutdown
}

// Go invokes the function asynchronously. It returns the Call structure representing
// the invocation. The done channel will signal when the call is complete by returning
// the same Call object. If done is nil, Go will allocate a new channel.
// If non-nil, done must be buffered or Go will deliberately crash.
func (client *Client) Go(ctx context.Context, servicePath, serviceMethod string, args interface{}, reply interface{}, done chan *Call) *Call {
	call := new(Call)
	call.ServicePath = servicePath
	call.ServiceMethod = serviceMethod
	meta := ctx.Value(share.ReqMetaDataKey)
	if meta != nil { // copy meta in context to meta in requests
		call.Metadata = meta.(map[string]string)
	}

	if !share.IsShareContext(ctx) {
		ctx = share.NewContext(ctx)
	}

	call.Args = args
	call.Reply = reply

	// // we allow done is nil
	// if done == nil {
	// 	done = make(chan *Call, 10) // buffered.
	// } else {
	// 	// If caller passes done != nil, it must arrange that
	// 	// done has enough buffer for the number of simultaneous
	// 	// RPCs that will be using that channel. If the channel
	// 	// is totally unbuffered, it's best not to run at all.
	// 	if cap(done) == 0 {
	// 		log.Panic("rpc: done channel is unbuffered")
	// 	}
	// }

	call.Done = done

	if share.Trace {
		log.Debugf("client.Go send request for %s.%s, args: %+v in case of client call", servicePath, serviceMethod, args)
	}

	go client.send(ctx, call)

	return call
}

// Call invokes the named function, waits for it to complete, and returns its error status.
func (client *Client) Call(ctx context.Context, servicePath, serviceMethod string, args interface{}, reply interface{}) error {
	return client.call(ctx, servicePath, serviceMethod, args, reply)
}

func (client *Client) call(ctx context.Context, servicePath, serviceMethod string, args interface{}, reply interface{}) error {
	seq := new(uint64)

	if sharedCtx, ok := ctx.(*share.Context); ok {
		sharedCtx.SetValue(seqKey{}, seq)
	} else {
		ctx = context.WithValue(ctx, seqKey{}, seq)
	}

	if share.Trace {
		log.Debugf("client.call for %s.%s, args: %+v in case of client call", servicePath, serviceMethod, args)
		defer func() {
			log.Debugf("client.call done for %s.%s, args: %+v in case of client call", servicePath, serviceMethod, args)
		}()
	}

	Done := client.Go(ctx, servicePath, serviceMethod, args, reply, make(chan *Call, 10)).Done

	var err error
	select {
	case <-ctx.Done(): // cancel by context
		client.mutex.Lock()
		call := client.pending[*seq]
		delete(client.pending, *seq)
		client.mutex.Unlock()
		if call != nil {
			call.Error = ctx.Err()
			call.done()
		}

		return ctx.Err()
	case call := <-Done:
		err = call.Error
		meta := ctx.Value(share.ResMetaDataKey)
		if meta != nil && len(call.ResMetadata) > 0 {
			resMeta := meta.(map[string]string)
			locker, ok := ctx.Value(share.ContextTagsLock).(*sync.Mutex)
			if ok {

				locker.Lock()
				for k, v := range call.ResMetadata {
					resMeta[k] = v
				}
				resMeta[share.ServerAddress] = client.Conn.RemoteAddr().String()
				locker.Unlock()

			} else {
				for k, v := range call.ResMetadata {
					resMeta[k] = v
				}
				resMeta[share.ServerAddress] = client.Conn.RemoteAddr().String()
			}
		}
	}

	return err
}

// SendRaw sends raw messages. You don't care args and replies.
func (client *Client) SendRaw(ctx context.Context, r *protocol.Message) (map[string]string, []byte, error) {
	if sharedCtx, ok := ctx.(*share.Context); ok {
		sharedCtx.SetValue(seqKey{}, r.Seq())
	} else {
		ctx = context.WithValue(ctx, seqKey{}, r.Seq())
	}

	call := new(Call)
	call.Raw = true
	call.ServicePath = r.ServicePath
	call.ServiceMethod = r.ServiceMethod
	meta := ctx.Value(share.ReqMetaDataKey)

	rmeta := make(map[string]string)

	// copy meta to rmeta
	if meta != nil {
		for k, v := range meta.(map[string]string) {
			rmeta[k] = v
		}
	}
	// copy r.Metadata to rmeta
	if r.Metadata != nil {
		for k, v := range r.Metadata {
			rmeta[k] = v
		}
	}

	if meta != nil { // copy meta in context to meta in requests
		call.Metadata = rmeta
	}
	r.Metadata = rmeta

	if !share.IsShareContext(ctx) {
		ctx = share.NewContext(ctx)
	}

	done := make(chan *Call, 10)
	call.Done = done

	seq := r.Seq()
	client.mutex.Lock()
	if client.pending == nil {
		client.pending = make(map[uint64]*Call)
	}
	client.pending[seq] = call
	client.mutex.Unlock()

	data := r.EncodeSlicePointer()
	_, err := client.Conn.Write(*data)
	protocol.PutData(data)

	if err != nil {
		client.mutex.Lock()
		call = client.pending[seq]
		delete(client.pending, seq)
		client.mutex.Unlock()
		if call != nil {
			call.Error = err
			call.done()
		}
		return nil, nil, err
	}
	if r.IsOneway() {
		client.mutex.Lock()
		call = client.pending[seq]
		delete(client.pending, seq)
		client.mutex.Unlock()
		if call != nil {
			call.done()
		}
		return nil, nil, nil
	}

	var m map[string]string
	var payload []byte

	select {
	case <-ctx.Done(): // cancel by context
		client.mutex.Lock()
		call := client.pending[seq]
		delete(client.pending, seq)
		client.mutex.Unlock()
		if call != nil {
			call.Error = ctx.Err()
			call.done()
		}

		return nil, nil, ctx.Err()
	case call := <-done:
		err = call.Error
		m = call.ResMetadata
		if call.Reply != nil {
			payload = call.Reply.([]byte)
		}
	}

	return m, payload, err
}

func convertRes2Raw(res *protocol.Message) (map[string]string, []byte, error) {
	m := make(map[string]string)
	m[XVersion] = strconv.Itoa(int(res.Version()))
	if res.IsHeartbeat() {
		m[XHeartbeat] = "true"
	}
	if res.IsOneway() {
		m[XOneway] = "true"
	}
	if res.MessageStatusType() == protocol.Error {
		m[XMessageStatusType] = "Error"
	} else {
		m[XMessageStatusType] = "Normal"
	}

	// if res.CompressType() == protocol.Gzip {
	// 	m["Content-Encoding"] = "gzip"
	// }

	m[XMeta] = urlencode(res.Metadata)
	m[XSerializeType] = strconv.Itoa(int(res.SerializeType()))
	m[XMessageID] = strconv.FormatUint(res.Seq(), 10)
	m[XServicePath] = res.ServicePath
	m[XServiceMethod] = res.ServiceMethod

	return m, res.Payload, nil
}

func urlencode(data map[string]string) string {
	if len(data) == 0 {
		return ""
	}
	var buf strings.Builder
	first := true
	for k, v := range data {
		if !first {
			buf.WriteByte('&')
		}
		buf.WriteString(url.QueryEscape(k))
		buf.WriteByte('=')
		buf.WriteString(url.QueryEscape(v))
		first = false
	}
	return buf.String()
}

func (client *Client) send(ctx context.Context, call *Call) {
	// Register this call.
	client.mutex.Lock()
	if client.shutdown || client.closing {
		call.Error = ErrShutdown
		client.mutex.Unlock()
		call.done()
		return
	}

	isHeartbeat := call.ServicePath == "" && call.ServiceMethod == ""
	serializeType := client.option.SerializeType
	if isHeartbeat {
		serializeType = protocol.MsgPack
	}
	codec := share.Codecs[serializeType]
	if codec == nil {
		call.Error = ErrUnsupportedCodec
		client.mutex.Unlock()
		call.done()
		return
	}

	if client.pending == nil {
		client.pending = make(map[uint64]*Call)
	}

	seq := client.seq
	client.seq++
	client.pending[seq] = call
	client.mutex.Unlock()

	if cseq, ok := ctx.Value(seqKey{}).(*uint64); ok {
		*cseq = seq
	}

	// req := protocol.NewMessage()
	req := protocol.NewMessage()
	req.SetMessageType(protocol.Request)
	req.SetSeq(seq)
	if call.Reply == nil {
		req.SetOneway(true)
	}

	// heartbeat, and use default SerializeType (msgpack)
	if isHeartbeat {
		req.SetHeartbeat(true)
		req.SetSerializeType(protocol.MsgPack)
	} else {
		req.SetSerializeType(client.option.SerializeType)
	}

	if call.Metadata != nil {
		req.Metadata = call.Metadata
	}

	req.ServicePath = call.ServicePath
	req.ServiceMethod = call.ServiceMethod

	data, err := codec.Encode(call.Args)
	if err != nil {
		client.mutex.Lock()
		delete(client.pending, seq)
		client.mutex.Unlock()
		call.Error = err
		call.done()
		return
	}
	if len(data) > 1024 && client.option.CompressType != protocol.None {
		req.SetCompressType(client.option.CompressType)
	}

	req.Payload = data

	if client.Plugins != nil {
		_ = client.Plugins.DoClientBeforeEncode(req)
	}

	if share.Trace {
		log.Debugf("client.send for %s.%s, args: %+v in case of client call", call.ServicePath, call.ServiceMethod, call.Args)
	}
	allData := req.EncodeSlicePointer()
	_, err = client.Conn.Write(*allData)
	protocol.PutData(allData)
	if share.Trace {
		log.Debugf("client.sent for %s.%s, args: %+v in case of client call", call.ServicePath, call.ServiceMethod, call.Args)
	}

	if err != nil {
		if e, ok := err.(*net.OpError); ok {
			if e.Err != nil {
				err = fmt.Errorf("net.OpError: %s", e.Err.Error())
			} else {
				err = errors.New("net.OpError")
			}

		}
		client.mutex.Lock()
		call = client.pending[seq]
		delete(client.pending, seq)
		client.mutex.Unlock()
		if call != nil {
			call.Error = err
			call.done()
		}

		return
	}

	isOneway := req.IsOneway()

	if isOneway {
		client.mutex.Lock()
		call = client.pending[seq]
		delete(client.pending, seq)
		client.mutex.Unlock()
		if call != nil {
			call.done()
		}
	}

	if client.option.IdleTimeout != 0 {
		_ = client.Conn.SetDeadline(time.Now().Add(client.option.IdleTimeout))
	}
}

func (client *Client) input() {
	var err error

	for err == nil {
		res := protocol.NewMessage()
		if client.option.IdleTimeout != 0 {
			_ = client.Conn.SetDeadline(time.Now().Add(client.option.IdleTimeout))
		}

		err = res.Decode(client.r)
		if err != nil {
			break
		}
		if client.Plugins != nil {
			_ = client.Plugins.DoClientAfterDecode(res)
		}

		seq := res.Seq()
		var call *Call
		isServerMessage := (res.MessageType() == protocol.Request && !res.IsHeartbeat() && res.IsOneway())
		if !isServerMessage {
			client.mutex.Lock()
			call = client.pending[seq]
			delete(client.pending, seq)
			client.mutex.Unlock()
		}

		if share.Trace {
			log.Debugf("client.input received %v", res)
		}

		switch {
		case call == nil:
			if isServerMessage {
				if client.ServerMessageChan != nil {
					client.handleServerRequest(res)
				} else if client.option.NilCallServerMessageHandler != nil {
					client.option.NilCallServerMessageHandler(res)
				}
				continue
			}
		case res.MessageStatusType() == protocol.Error:
			// We've got an error response. Give this to the request
			if len(res.Metadata) > 0 {
				call.ResMetadata = res.Metadata

				// convert server error to a customized error, which implements ServerError interface
				if ClientErrorFunc != nil {
					call.Error = ClientErrorFunc(res, res.Metadata[protocol.ServiceError])
				} else {
					call.Error = strErr(res.Metadata[protocol.ServiceError])
				}

			}

			if call.Raw {
				call.Metadata, call.Reply, _ = convertRes2Raw(res)
				call.Metadata[XErrorMessage] = call.Error.Error()
				call.ResMetadata = res.Metadata
			} else if len(res.Payload) > 0 {
				data := res.Payload
				codec := share.Codecs[res.SerializeType()]
				if codec != nil {
					_ = codec.Decode(data, call.Reply)
				}
			}
			call.done()
		default:
			if call.Raw {
				call.Metadata, call.Reply, _ = convertRes2Raw(res)
				call.ResMetadata = res.Metadata
			} else {
				data := res.Payload
				if len(data) > 0 {
					codec := share.Codecs[res.SerializeType()]
					if codec == nil {
						call.Error = strErr(ErrUnsupportedCodec.Error())
					} else {
						err = codec.Decode(data, call.Reply)
						if err != nil {
							call.Error = strErr(err.Error())
						}
					}
				}
				if len(res.Metadata) > 0 {
					call.ResMetadata = res.Metadata
				}

			}

			call.done()
		}
	}
	// Terminate pending calls.

	if client.ServerMessageChan != nil {
		req := protocol.NewMessage()
		req.SetMessageType(protocol.Request)
		req.SetMessageStatusType(protocol.Error)
		if req.Metadata == nil {
			req.Metadata = make(map[string]string)
			if err != nil {
				req.Metadata[protocol.ServiceError] = err.Error()
			}
		}
		req.Metadata["server"] = client.Conn.RemoteAddr().String()
		client.handleServerRequest(req)
	}

	client.mutex.Lock()
	if !client.pluginClosed {
		if client.Plugins != nil {
			client.Plugins.DoClientConnectionClose(client.Conn)
		}
		client.pluginClosed = true
	}
	client.Conn.Close()
	client.shutdown = true
	closing := client.closing
	if e, ok := err.(*net.OpError); ok {
		if e.Addr != nil || e.Err != nil {
			err = fmt.Errorf("net.OpError: %s", e.Err.Error())
		} else {
			err = errors.New("net.OpError")
		}

	}
	if err == io.EOF {
		if closing {
			err = ErrShutdown
		} else {
			err = io.ErrUnexpectedEOF
		}
	}
	for _, call := range client.pending {
		call.Error = err
		call.done()
	}

	client.mutex.Unlock()

	if err != nil && !closing {
		log.Errorf("rpcx: client protocol error: %v", err)
	}
}

func (client *Client) handleServerRequest(msg *protocol.Message) {
	defer func() {
		if r := recover(); r != nil {
			log.Errorf("ServerMessageChan may be closed so client remove it. Please add it again if you want to handle server requests. error is %v", r)
			client.ServerMessageChan = nil
		}
	}()

	serverMessageChan := client.ServerMessageChan
	if serverMessageChan != nil {
		if client.option.BidirectionalBlock {
			serverMessageChan <- msg
		} else {
			select {
			case serverMessageChan <- msg:
			default:
				log.Warnf("ServerMessageChan may be full so the server request %d has been dropped", msg.Seq())
			}
		}
	}
}

func (client *Client) heartbeat() {
	t := time.NewTicker(client.option.HeartbeatInterval)

	if client.option.MaxWaitForHeartbeat == 0 {
		client.option.MaxWaitForHeartbeat = 30 * time.Second
	}

	for range t.C {
		if client.IsShutdown() || client.IsClosing() {
			t.Stop()
			return
		}

		request := time.Now().UnixNano()
		reply := int64(0)
		ctx, cancel := context.WithTimeout(context.Background(), client.option.MaxWaitForHeartbeat)
		err := client.Call(ctx, "", "", &request, &reply)
		abnormal := false
		if ctx.Err() != nil {
			log.Warnf("failed to heartbeat to %s, context err: %v", client.Conn.RemoteAddr().String(), ctx.Err())
			abnormal = true
		}
		cancel()
		if err != nil {
			log.Warnf("failed to heartbeat to %s: %v", client.Conn.RemoteAddr().String(), err)
			abnormal = true
		}

		if reply != request {
			log.Warnf("reply %d in heartbeat to %s is different from request %d", reply, client.Conn.RemoteAddr().String(), request)
		}

		if abnormal {
			client.Close()
		}
	}
}

// Close calls the underlying connection's Close method. If the connection is already
// shutting down, ErrShutdown is returned.
func (client *Client) Close() error {
	client.mutex.Lock()

	for seq, call := range client.pending {
		delete(client.pending, seq)
		if call != nil {
			call.Error = ErrShutdown
			call.done()
		}
	}

	var err error
	if !client.pluginClosed {
		if client.Plugins != nil {
			client.Plugins.DoClientConnectionClose(client.Conn)
		}

		client.pluginClosed = true
		err = client.Conn.Close()
	}

	if client.closing || client.shutdown {
		client.mutex.Unlock()
		return ErrShutdown
	}

	client.closing = true
	client.mutex.Unlock()
	return err
}


================================================
FILE: client/client_test.go
================================================
package client

import (
	"context"
	"fmt"
	"math/rand"
	"net"
	"sync"
	"testing"
	"time"

	testutils "github.com/smallnest/rpcx/_testutils"
	"github.com/smallnest/rpcx/protocol"
	"github.com/smallnest/rpcx/server"
)

type Args struct {
	A int
	B int
}

type Reply struct {
	C int
}

type Arith int

func (t *Arith) Mul(ctx context.Context, args *Args, reply *Reply) error {
	reply.C = args.A * args.B
	return nil
}

type PBArith int

func (t *PBArith) Mul(ctx context.Context, args *testutils.ProtoArgs, reply *testutils.ProtoReply) error {
	reply.C = args.A * args.B
	return nil
}

func (t *Arith) ThriftMul(ctx context.Context, args *testutils.ThriftArgs_, reply *testutils.ThriftReply) error {
	reply.C = args.A * args.B
	return nil
}

type Bidirectional struct {
	*server.Server
}

func (t *Bidirectional) Mul(ctx context.Context, args *Args, reply *Reply) error {
	conn := ctx.Value(server.RemoteConnContextKey).(net.Conn)
	reply.C = args.A * args.B
	t.SendMessage(conn, "test_service_path", "test_service_method", nil, []byte("abcde"))
	return nil
}

func TestClient_IT(t *testing.T) {
	s := server.NewServer()
	_ = s.RegisterName("Arith", new(Arith), "")
	_ = s.RegisterName("PBArith", new(PBArith), "")
	go func() {
		_ = s.Serve("tcp", "127.0.0.1:0")
	}()
	defer s.Close()
	time.Sleep(500 * time.Millisecond)

	addr := s.Address().String()

	client := &Client{
		option: DefaultOption,
	}

	err := client.Connect("tcp", addr)
	if err != nil {
		t.Fatalf("failed to connect: %v", err)
	}
	defer client.Close()

	args := &Args{
		A: 10,
		B: 20,
	}

	reply := &Reply{}
	err = client.Call(context.Background(), "Arith", "Mul", args, reply)
	if err != nil {
		t.Fatalf("failed to call: %v", err)
	}

	if reply.C != 200 {
		t.Fatalf("expect 200 but got %d", reply.C)
	}

	err = client.Call(context.Background(), "Arith", "Add", args, reply)
	if err == nil {
		t.Fatal("expect an error but got nil")
	}

	client.option.SerializeType = protocol.MsgPack
	reply = &Reply{}
	err = client.Call(context.Background(), "Arith", "Mul", args, reply)
	if err != nil {
		t.Fatalf("failed to call: %v", err)
	}

	if reply.C != 200 {
		t.Fatalf("expect 200 but got %d", reply.C)
	}

	client.option.SerializeType = protocol.ProtoBuffer

	pbArgs := &testutils.ProtoArgs{
		A: 10,
		B: 20,
	}
	pbReply := &testutils.ProtoReply{}
	err = client.Call(context.Background(), "PBArith", "Mul", pbArgs, pbReply)
	if err != nil {
		t.Fatalf("failed to call: %v", err)
	}

	if pbReply.C != 200 {
		t.Fatalf("expect 200 but got %d", pbReply.C)
	}
}

func TestClient_IT_Concurrency(t *testing.T) {
	s := server.NewServer()
	_ = s.RegisterName("PBArith", new(PBArith), "")
	go func() {
		_ = s.Serve("tcp", "127.0.0.1:0")
	}()
	defer s.Close()
	time.Sleep(500 * time.Millisecond)

	addr := s.Address().String()

	client := &Client{
		option: DefaultOption,
	}

	err := client.Connect("tcp", addr)
	if err != nil {
		t.Fatalf("failed to connect: %v", err)
	}
	defer client.Close()

	var wg sync.WaitGroup
	wg.Add(100)
	for i := 0; i < 100; i++ {
		i := i
		go testSendRaw(t, client, uint64(i), rand.Int31(), rand.Int31(), &wg)
	}
	wg.Wait()

}

func testSendRaw(t *testing.T, client *Client, seq uint64, x, y int32, wg *sync.WaitGroup) {
	defer wg.Done()
	rpcxReq := protocol.NewMessage()
	rpcxReq.SetMessageType(protocol.Request)
	rpcxReq.SetSeq(seq)
	rpcxReq.ServicePath = "PBArith"
	rpcxReq.ServiceMethod = "Mul"
	rpcxReq.SetSerializeType(protocol.ProtoBuffer)
	rpcxReq.SetOneway(false)

	pbArgs := &testutils.ProtoArgs{
		A: x,
		B: y,
	}
	data, _ := pbArgs.Marshal()
	rpcxReq.Payload = data
	_, reply, err := client.SendRaw(context.Background(), rpcxReq)
	if err != nil {
		t.Errorf("failed to call SendRaw: %v", err)
		return
	}

	pbReply := &testutils.ProtoReply{}
	err = pbReply.Unmarshal(reply)
	if err != nil {
		t.Errorf("failed to unmarshal reply: %v", err)
		return
	}

	if pbReply.C != x*y {
		t.Errorf("expect %d but got %d", x*y, pbReply.C)
		return
	}
}

func TestClient_Res_Reset(t *testing.T) {
	var res = protocol.NewMessage()
	res.Payload = []byte{1, 2, 3, 4, 5, 6, 7, 8}
	data := res.Payload
	res.Reset()

	if len(data) == 0 {
		t.Fatalf("data has been set to empty after response has been reset: %v", data)
	}
}

func TestClient_Bidirectional(t *testing.T) {
	s := server.NewServer()
	_ = s.RegisterName("Bidirectional", &Bidirectional{Server: s}, "")
	go func() {
		_ = s.Serve("tcp", "127.0.0.1:0")
	}()
	defer s.Close()
	time.Sleep(500 * time.Millisecond)

	addr := s.Address().String()

	opt := DefaultOption

	var receive string

	opt.NilCallServerMessageHandler = func(msg *protocol.Message) {
		fmt.Printf("receive msg from server: %s\n", msg.Payload)
		receive = string(msg.Payload)
	}
	client := &Client{
		option: opt,
	}

	err := client.Connect("tcp", addr)
	if err != nil {
		t.Fatalf("failed to connect: %v", err)
	}
	defer client.Close()

	args := &Args{
		A: 10,
		B: 20,
	}
	reply := &Reply{}
	err = client.Call(context.Background(), "Bidirectional", "Mul", args, reply)
	if err != nil {
		t.Fatalf("failed to call: %v", err)
	}
	if receive != "abcde" {
		t.Fatalf("expect abcde but got %s", receive)
	}
	if reply.C != 200 {
		t.Fatalf("expect 200 but got %d", reply.C)
	}

}


================================================
FILE: client/connection.go
================================================
package client

import (
	"bufio"
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"time"

	"github.com/smallnest/rpcx/log"
	"github.com/smallnest/rpcx/share"
	"golang.org/x/net/websocket"
)

type ConnFactoryFn func(c *Client, network, address string) (net.Conn, error)

var ConnFactories = make(map[string]ConnFactoryFn)

func init() {
	ConnFactories["http"] = newDirectHTTPConn
	ConnFactories["kcp"] = newDirectKCPConn
	ConnFactories["quic"] = newDirectQuicConn
	ConnFactories["unix"] = newDirectConn
	ConnFactories["memu"] = newMemuConn
	ConnFactories["iouring"] = newIOUringConn
}

// Connect connects the server via specified network.
func (client *Client) Connect(network, address string) error {
	var conn net.Conn
	var err error

	switch network {
	case "http":
		conn, err = newDirectHTTPConn(client, network, address)
	case "ws", "wss":
		conn, err = newDirectWSConn(client, network, address)
	default:
		fn := ConnFactories[network]
		if fn != nil {
			conn, err = fn(client, network, address)
		} else {
			conn, err = newDirectConn(client, network, address)
		}
	}

	if err == nil && conn != nil {
		if tc, ok := conn.(*net.TCPConn); ok && client.option.TCPKeepAlivePeriod > 0 {
			_ = tc.SetKeepAlive(true)
			_ = tc.SetKeepAlivePeriod(client.option.TCPKeepAlivePeriod)
		}

		if client.option.IdleTimeout != 0 {
			_ = conn.SetDeadline(time.Now().Add(client.option.IdleTimeout))
		}

		if client.Plugins != nil {
			conn, err = client.Plugins.DoConnCreated(conn)
			if err != nil {
				return err
			}
		}

		client.Conn = conn
		client.r = bufio.NewReaderSize(conn, ReaderBuffsize)
		// c.w = bufio.NewWriterSize(conn, WriterBuffsize)

		// start reading and writing since connected
		go client.input()

		if client.option.Heartbeat && client.option.HeartbeatInterval > 0 {
			go client.heartbeat()
		}

	}

	if err != nil && client.Plugins != nil {
		client.Plugins.DoConnCreateFailed(network, address)
	}

	return err
}

func newDirectConn(c *Client, network, address string) (net.Conn, error) {
	var conn net.Conn
	var tlsConn *tls.Conn
	var err error

	if c == nil {
		err = fmt.Errorf("nil client")
		return nil, err
	}

	if c.option.TLSConfig != nil {
		dialer := &net.Dialer{
			Timeout: c.option.ConnectTimeout,
		}
		tlsConn, err = tls.DialWithDialer(dialer, network, address, c.option.TLSConfig)
		// or conn:= tls.Client(netConn, &config)
		conn = net.Conn(tlsConn)
	} else {
		conn, err = net.DialTimeout(network, address, c.option.ConnectTimeout)
	}

	if err != nil {
		log.Warnf("failed to dial server: %v", err)
		return nil, err
	}

	return conn, nil
}

var connected = "200 Connected to rpcx"

func newDirectHTTPConn(c *Client, network, address string) (net.Conn, error) {
	if c == nil {
		return nil, errors.New("empty client")
	}
	path := c.option.RPCPath
	if path == "" {
		path = share.DefaultRPCPath
	}

	var conn net.Conn
	var tlsConn *tls.Conn
	var err error

	if c.option.TLSConfig != nil {
		dialer := &net.Dialer{
			Timeout: c.option.ConnectTimeout,
		}
		tlsConn, err = tls.DialWithDialer(dialer, "tcp", address, c.option.TLSConfig)
		// or conn:= tls.Client(netConn, &config)

		conn = net.Conn(tlsConn)
	} else {
		conn, err = net.DialTimeout("tcp", address, c.option.ConnectTimeout)
	}
	if err != nil {
		log.Errorf("failed to dial server: %v", err)
		return nil, err
	}

	_, err = io.WriteString(conn, "CONNECT "+path+" HTTP/1.0\n\n")
	if err != nil {
		// Dial() success but Write() failed here, close the successfully
		// created conn before return.
		conn.Close()

		log.Errorf("failed to make CONNECT: %v", err)
		return nil, err
	}

	// Require successful HTTP response
	// before switching to RPC protocol.
	resp, err := http.ReadResponse(bufio.NewReader(conn), &http.Request{Method: "CONNECT"})
	if err == nil && resp.Status == connected {
		return conn, nil
	}
	if err == nil {
		log.Errorf("unexpected HTTP response: %v", err)
		err = errors.New("unexpected HTTP response: " + resp.Status)
	}
	conn.Close()
	return nil, &net.OpError{
		Op:   "dial-http",
		Net:  network + " " + address,
		Addr: nil,
		Err:  err,
	}
}

func newDirectWSConn(c *Client, network, address string) (net.Conn, error) {
	if c == nil {
		return nil, errors.New("empty client")
	}
	path := c.option.RPCPath
	if path == "" {
		path = share.DefaultRPCPath
	}

	var conn net.Conn
	var err error

	// url := "ws://localhost:12345/ws"

	var url, origin string
	if network == "ws" {
		url = fmt.Sprintf("ws://%s%s", address, path)
		origin = fmt.Sprintf("http://%s", address)
	} else {
		url = fmt.Sprintf("wss://%s%s", address, path)
		origin = fmt.Sprintf("https://%s", address)
	}

	if c.option.TLSConfig != nil {
		config, erri := websocket.NewConfig(url, origin)
		if erri != nil {
			return nil, erri
		}
		config.TlsConfig = c.option.TLSConfig
		conn, err = websocket.DialConfig(config)
	} else {
		conn, err = websocket.Dial(url, "", origin)
	}

	return conn, err
}


================================================
FILE: client/connection_iouring.go
================================================
package client

import (
	"net"
)

// experimental
func newIOUringConn(c *Client, network, address string) (net.Conn, error) {
	return newDirectConn(c, "tcp", address)
}


================================================
FILE: client/connection_iouring_test.go
================================================
//go:build linux
// +build linux

package client

// func TestXClient_IOUring(t *testing.T) {
// 	s := server.NewServer()
// 	s.RegisterName("Arith", new(Arith), "")
// 	go s.Serve("iouring", "127.0.0.1:8972")
// 	defer s.Close()
// 	time.Sleep(500 * time.Millisecond)

// 	addr := s.Address().String()

// 	d, err := NewPeer2PeerDiscovery("iouring@"+addr, "desc=a test service")
// 	if err != nil {
// 		t.Fatalf("failed to NewPeer2PeerDiscovery: %v", err)
// 	}

// 	xclient := NewXClient("Arith", Failtry, RandomSelect, d, DefaultOption)

// 	defer xclient.Close()

// 	args := &Args{
// 		A: 10,
// 		B: 20,
// 	}

// 	reply := &Reply{}
// 	err = xclient.Call(context.Background(), "Mul", args, reply)
// 	if err != nil {
// 		t.Fatalf("failed to call: %v", err)
// 	}

// 	if reply.C != 200 {
// 		t.Fatalf("expect 200 but got %d", reply.C)
// 	}
// }


================================================
FILE: client/connection_kcp.go
================================================
// +build kcp

package client

import (
	"net"

	kcp "github.com/xtaci/kcp-go"
)

func newDirectKCPConn(c *Client, network, address string) (net.Conn, error) {
	return kcp.DialWithOptions(address, c.option.Block.(kcp.BlockCrypt), 10, 3)
}


================================================
FILE: client/connection_memu.go
================================================
package client

import (
	"net"

	"github.com/akutz/memconn"
)

func newMemuConn(c *Client, network, address string) (net.Conn, error) {
	return memconn.Dial(network, address)
}


================================================
FILE: client/connection_nonkcp.go
================================================
// +build !kcp

package client

import (
	"errors"
	"net"
)

func newDirectKCPConn(c *Client, network, address string) (net.Conn, error) {
	return nil, errors.New("kcp unsupported")
}


================================================
FILE: client/connection_nonquic.go
================================================
//go:build !quic
// +build !quic

package client

import (
	"errors"
	"net"
)

func newDirectQuicConn(c *Client, network, address string) (net.Conn, error) {
	return nil, errors.New("quic unsupported")
}


================================================
FILE: client/connection_quic.go
================================================
//go:build quic
// +build quic

package client

import (
	"crypto/tls"
	"net"

	"github.com/quic-go/quic-go"
	"github.com/smallnest/quick"
)

func newDirectQuicConn(c *Client, network, address string) (net.Conn, error) {
	tlsConf := c.option.TLSConfig
	if tlsConf == nil {
		tlsConf = &tls.Config{InsecureSkipVerify: true}
	}

	if len(tlsConf.NextProtos) == 0 {
		tlsConf.NextProtos = []string{"rpcx"}
	}

	quicConfig := &quic.Config{}

	return quick.Dial(address, tlsConf, quicConfig)
}


================================================
FILE: client/connection_rdma.go
================================================
//go:build rdma
// +build rdma

package client

import (
	"errors"
	"net"

	"github.com/smallnest/rsocket"
)

func init() {
	ConnFactories["rdma"] = newRDMAConn
}

func newRDMAConn(c *Client, network, address string) (net.Conn, error) {
	if network != "rdma" {
		return nil, errors.New("network is not rdma")
	}

	return rsocket.DialTCP(address)
}


================================================
FILE: client/discovery.go
================================================
package client

import (
	"encoding/json"
	"os"
	"path/filepath"
	"sync"
)

// ServiceDiscoveryFilter can be used to filter services with customized logics.
// Servers can register its services but clients can use the customized filter to select some services.
// It returns true if ServiceDiscovery wants to use this service, otherwise it returns false.
type ServiceDiscoveryFilter func(kvp *KVPair) bool

// ServiceDiscovery defines ServiceDiscovery of zookeeper, etcd and consul
type ServiceDiscovery interface {
	GetServices() []*KVPair       // return all services in the registry
	WatchService() chan []*KVPair // watch the change of services, it's a golang channel
	RemoveWatcher(ch chan []*KVPair)
	Clone(servicePath string) (ServiceDiscovery, error)
	SetFilter(ServiceDiscoveryFilter) // set customized filter to filter services
	Close()
}

type cachedServiceDiscovery struct {
	threshold  int
	cachedFile string
	cached     []*KVPair

	chansLock sync.RWMutex
	chans     map[chan []*KVPair]chan []*KVPair

	ServiceDiscovery
}

// CacheDiscovery caches the services in a file, it will return the cached services if the number of services is greater than threshold.
// It is very useful when the register center is lost.
func CacheDiscovery(threshold int, cachedFile string, discovery ServiceDiscovery) ServiceDiscovery {
	if cachedFile == "" {
		cachedFile = ".cache/discovery.json"
	}

	cachedFileDir := filepath.Dir(cachedFile)

	if _, err := os.Stat(cachedFileDir); os.IsNotExist(err) {
		// 目录不存在,创建目录
		if err := os.MkdirAll(cachedFileDir, os.ModePerm); err != nil {
			panic(err)
		}
	}

	return &cachedServiceDiscovery{
		threshold:        threshold,
		cachedFile:       cachedFile,
		ServiceDiscovery: discovery,
		chans:            make(map[chan []*KVPair]chan []*KVPair),
	}
}

func (cd *cachedServiceDiscovery) GetServices() []*KVPair {
	kvPairs := cd.ServiceDiscovery.GetServices()

	n := len(kvPairs)
	if n > cd.threshold {
		if n > len(cd.cached) { // strictly we should compare the content of the cached file, but only compare the length for performance
			cd.cached = kvPairs
			cd.storeCached(kvPairs)
		}

		return kvPairs
	}

	if len(cd.cached) == 0 {
		cd.loadCached()
	}

	return cd.cached
}

func (cd *cachedServiceDiscovery) WatchService() chan []*KVPair {
	ch := cd.ServiceDiscovery.WatchService()

	cachedCh := make(chan []*KVPair, 10)
	cd.chansLock.Lock()
	cd.chans[cachedCh] = ch
	cd.chansLock.Unlock()

	go func() {
		defer recover()

		for {
			kvPairs, ok := <-ch
			if !ok {
				close(cachedCh)
				return
			}

			n := len(kvPairs)
			if n > len(cd.cached) {
				cd.cached = kvPairs
				cd.storeCached(kvPairs)
			}

			cachedCh <- kvPairs
		}
	}()

	return cachedCh
}

func (cd *cachedServiceDiscovery) RemoveWatcher(ch chan []*KVPair) {
	cd.chansLock.Lock()
	origin := cd.chans[ch]
	delete(cd.chans, ch)
	cd.chansLock.Unlock()

	if origin != nil {
		cd.ServiceDiscovery.RemoveWatcher(origin)
	}

}

func (cd *cachedServiceDiscovery) storeCached(kvPairs []*KVPair) {
	data, _ := json.Marshal(kvPairs)
	os.WriteFile(cd.cachedFile, data, 0644)
}

func (cd *cachedServiceDiscovery) loadCached() (kvPairs []*KVPair) {
	data, err := os.ReadFile(cd.cachedFile)
	if err != nil || len(data) == 0 {
		return
	}

	json.Unmarshal(data, &kvPairs)

	return kvPairs
}


================================================
FILE: client/dns_discovery.go
================================================
package client

import (
	"fmt"
	"net"
	"sort"
	"sync"
	"time"

	"github.com/smallnest/rpcx/log"
)

// DNSDiscovery is based on DNS a record.
// You must set port and network info when you create the DNSDiscovery.
type DNSDiscovery struct {
	domain  string
	network string
	port    int
	d       time.Duration

	pairsMu sync.RWMutex
	pairs   []*KVPair
	chans   []chan []*KVPair

	mu sync.Mutex

	filter ServiceDiscoveryFilter

	stopCh chan struct{}
}

// NewDNSDiscovery returns a new DNSDiscovery.
func NewDNSDiscovery(domain string, network string, port int, d time.Duration) (*DNSDiscovery, error) {
	discovery := &DNSDiscovery{domain: domain, network: network, port: port, d: d}
	discovery.lookup()
	go discovery.watch()
	return discovery, nil
}

// Clone clones this ServiceDiscovery with new servicePath.
func (d *DNSDiscovery) Clone(servicePath string) (ServiceDiscovery, error) {
	return NewDNSDiscovery(d.domain, d.network, d.port, d.d)
}

// SetFilter sets the filer.
func (d *DNSDiscovery) SetFilter(filter ServiceDiscoveryFilter) {
	d.filter = filter
}

// GetServices returns the static server
func (d *DNSDiscovery) GetServices() []*KVPair {
	d.pairsMu.RLock()
	defer d.pairsMu.RUnlock()
	return d.pairs
}

// WatchService returns a nil chan.
func (d *DNSDiscovery) WatchService() chan []*KVPair {
	d.mu.Lock()
	defer d.mu.Unlock()

	ch := make(chan []*KVPair, 10)
	d.chans = append(d.chans, ch)
	return ch
}

func (d *DNSDiscovery) RemoveWatcher(ch chan []*KVPair) {
	d.mu.Lock()
	defer d.mu.Unlock()

	var chans []chan []*KVPair
	for _, c := range d.chans {
		if c == ch {
			continue
		}

		chans = append(chans, c)
	}

	d.chans = chans
}

func (d *DNSDiscovery) lookup() {
	var pairs []*KVPair // latest servers

	ips, err := net.LookupIP(d.domain)
	if err != nil {
		log.Errorf("failed to lookup %s: %v", d.domain, err)
		return
	}

	for _, ip := range ips {
		pair := &KVPair{Key: fmt.Sprintf("%s@%s:%d", d.network, ip.String(), d.port)}
		if d.filter != nil && !d.filter(pair) {
			continue
		}
		pairs = append(pairs, pair)
	}

	if len(pairs) > 0 {
		sort.Slice(pairs, func(i, j int) bool {
			return pairs[i].Key < pairs[j].Key
		})
	}

	d.pairsMu.Lock()
	d.pairs = pairs
	d.pairsMu.Unlock()

	d.mu.Lock()
	for _, ch := range d.chans {
		ch := ch
		go func() {
			defer func() {
				recover()
			}()
			select {
			case ch <- pairs:
			case <-time.After(time.Minute):
				log.Warn("chan is full and new change has been dropped")
			}
		}()
	}
	d.mu.Unlock()
}

func (d *DNSDiscovery) watch() {
	tick := time.NewTicker(d.d)
	defer tick.Stop()

	for {
		select {
		case <-d.stopCh:
			return
		case <-tick.C:
			d.lookup()
		}
	}
}

func (d *DNSDiscovery) Close() {
	close(d.stopCh)
}


================================================
FILE: client/failmode_enumer.go
================================================
// Code generated by "enumer -type=FailMode"; DO NOT EDIT.

package client

import (
	"fmt"
)

const _FailModeName = "FailoverFailfastFailtryFailbackup"

var _FailModeIndex = [...]uint8{0, 8, 16, 23, 33}

func (i FailMode) String() string {
	if i < 0 || i >= FailMode(len(_FailModeIndex)-1) {
		return fmt.Sprintf("FailMode(%d)", i)
	}
	return _FailModeName[_FailModeIndex[i]:_FailModeIndex[i+1]]
}

var _FailModeValues = []FailMode{0, 1, 2, 3}

var _FailModeNameToValueMap = map[string]FailMode{
	_FailModeName[0:8]:   0,
	_FailModeName[8:16]:  1,
	_FailModeName[16:23]: 2,
	_FailModeName[23:33]: 3,
}

// FailModeString retrieves an enum value from the enum constants string name.
// Throws an error if the param is not part of the enum.
func FailModeString(s string) (FailMode, error) {
	if val, ok := _FailModeNameToValueMap[s]; ok {
		return val, nil
	}
	return 0, fmt.Errorf("%s does not belong to FailMode values", s)
}

// FailModeValues returns all values of the enum
func FailModeValues() []FailMode {
	return _FailModeValues
}

// IsAFailMode returns "true" if the value is listed in the enum definition. "false" otherwise
func (i FailMode) IsAFailMode() bool {
	for _, v := range _FailModeValues {
		if i == v {
			return true
		}
	}
	return false
}


================================================
FILE: client/geo_utils.go
================================================
package client

import (
	"math"
)

// https://gist.github.com/cdipaolo/d3f8db3848278b49db68
func getDistanceFrom(lat1, lon1, lat2, lon2 float64) float64 {
	var la1, lo1, la2, lo2, r float64
	la1 = lat1 * math.Pi / 180
	lo1 = lon1 * math.Pi / 180
	la2 = lat2 * math.Pi / 180
	lo2 = lon2 * math.Pi / 180

	r = 6378100 // Earth radius in METERS

	// calculate
	h := hsin(la2-la1) + math.Cos(la1)*math.Cos(la2)*hsin(lo2-lo1)

	return 2 * r * math.Asin(math.Sqrt(h))
}

func hsin(theta float64) float64 {
	return math.Pow(math.Sin(theta/2), 2)
}


================================================
FILE: client/hash_utils.go
================================================
package client

import (
	"fmt"
	"hash/fnv"
)

// Hash consistently chooses a hash bucket number in the range [0, numBuckets) for the given key. numBuckets must be >= 1.
func Hash(key uint64, buckets int32) int32 {
	if buckets <= 0 {
		buckets = 1
	}

	var b, j int64

	for j < int64(buckets) {
		b = j
		key = key*2862933555777941757 + 1
		j = int64(float64(b+1) * (float64(int64(1)<<31) / float64((key>>33)+1)))
	}

	return int32(b)
}

// HashString get a hash value of a string
func HashString(s string) uint64 {
	h := fnv.New64a()
	h.Write([]byte(s))
	return h.Sum64()
}

// HashServiceAndArgs define a hash function
type HashServiceAndArgs func(len int, options ...interface{}) int

// ConsistentFunction define a hash function
// Return service address, like "tcp@127.0.0.1:8970"
type ConsistentAddrStrFunction func(options ...interface{}) string

func genKey(options ...interface{}) uint64 {
	keyString := ""
	for _, opt := range options {
		keyString = keyString + "/" + toString(opt)
	}

	return HashString(keyString)
}

// JumpConsistentHash selects a server by serviceMethod and args
func JumpConsistentHash(len int, options ...interface{}) int {
	return int(Hash(genKey(options...), int32(len)))
}

func toString(obj interface{}) string {
	return fmt.Sprintf("%v", obj)
}


================================================
FILE: client/mdns_discovery.go
================================================
package client

import (
	"context"
	"encoding/json"
	"net/url"
	"sync"
	"time"

	"github.com/grandcat/zeroconf"
	"github.com/smallnest/rpcx/log"
)

type serviceMeta struct {
	Service        string `json:"service,omitempty"`
	Meta           string `json:"meta,omitempty"`
	ServiceAddress string `json:"service_address,omitempty"`
}

// MDNSDiscovery is a mdns service discovery.
// It always returns the registered servers in mdns.
type MDNSDiscovery struct {
	Timeout       time.Duration
	WatchInterval time.Duration
	domain        string
	service       string
	pairsMu       sync.RWMutex
	pairs         []*KVPair
	chans         []chan []*KVPair

	mu sync.Mutex

	filter ServiceDiscoveryFilter

	stopCh chan struct{}
}

// NewMDNSDiscovery returns a new MDNSDiscovery.
// If domain is empty, use "local." in default.
func NewMDNSDiscovery(service string, timeout time.Duration, watchInterval time.Duration, domain string) (*MDNSDiscovery, error) {
	if domain == "" {
		domain = "local."
	}
	d := &MDNSDiscovery{service: service, Timeout: timeout, WatchInterval: watchInterval, domain: domain}
	d.stopCh = make(chan struct{})

	var err error
	d.pairsMu.Lock()
	d.pairs, err = d.browse()
	d.pairsMu.Unlock()
	if err != nil {
		log.Warnf("failed to browse services: %v", err)
	}
	go d.watch()
	return d, nil
}

// Clone clones this ServiceDiscovery with new servicePath.
func (d *MDNSDiscovery) Clone(servicePath string) (ServiceDiscovery, error) {
	return NewMDNSDiscovery(servicePath, d.Timeout, d.WatchInterval, d.domain)
}

// SetFilter sets the filer.
func (d *MDNSDiscovery) SetFilter(filter ServiceDiscoveryFilter) {
	d.filter = filter
}

// GetServices returns the servers
func (d *MDNSDiscovery) GetServices() []*KVPair {
	d.pairsMu.RLock()
	defer d.pairsMu.RUnlock()

	return d.pairs
}

// WatchService returns a nil chan.
func (d *MDNSDiscovery) WatchService() chan []*KVPair {
	d.mu.Lock()
	defer d.mu.Unlock()

	ch := make(chan []*KVPair, 10)
	d.chans = append(d.chans, ch)
	return ch
}

func (d *MDNSDiscovery) RemoveWatcher(ch chan []*KVPair) {
	d.mu.Lock()
	defer d.mu.Unlock()

	var chans []chan []*KVPair
	for _, c := range d.chans {
		if c == ch {
			continue
		}

		chans = append(chans, c)
	}

	d.chans = chans
}

func (d *MDNSDiscovery) watch() {
	t := time.NewTicker(d.WatchInterval)

	for {
		select {
		case <-d.stopCh:
			t.Stop()
			log.Info("discovery has been closed")
			return
		case <-t.C:
			pairs, err := d.browse()
			if err == nil {
				d.pairsMu.Lock()
				d.pairs = pairs
				d.pairsMu.Unlock()

				d.mu.Lock()
				for _, ch := range d.chans {
					ch := ch
					go func() {
						defer func() {
							recover()
						}()
						select {
						case ch <- pairs:
						case <-time.After(time.Minute):
							log.Warn("chan is full and new change has ben dropped")
						}
					}()
				}
				d.mu.Unlock()
			}
		}
	}
}

func (d *MDNSDiscovery) browse() ([]*KVPair, error) {
	resolver, err := zeroconf.NewResolver(nil)
	if err != nil {
		log.Warnf("Failed to initialize resolver: %v", err)
		return nil, err
	}
	entries := make(chan *zeroconf.ServiceEntry)

	var totalServices []*KVPair
	var services []*serviceMeta

	done := make(chan struct{})
	go func(results <-chan *zeroconf.ServiceEntry) {
		for entry := range entries {
			s, _ := url.QueryUnescape(entry.Text[0])
			err := json.Unmarshal([]byte(s), &services)
			if err != nil {
				log.Warnf("Failed to browse: %v", err)
				continue
			}

			for _, sm := range services {

				pair := &KVPair{
					Key:   sm.ServiceAddress,
					Value: sm.Meta,
				}
				if d.filter != nil && !d.filter(pair) {
					continue
				}
				totalServices = append(totalServices, pair)
			}
		}

		close(done)
	}(entries)

	ctx, cancel := context.WithTimeout(context.Background(), d.Timeout)
	defer cancel()
	err = resolver.Browse(ctx, "_rpcxservices", d.domain, entries)
	if err != nil {
		log.Warnf("Failed to browse: %v", err)
	}

	<-done
	return totalServices, nil
}

func (d *MDNSDiscovery) Close() {
	close(d.stopCh)
}


================================================
FILE: client/mode.go
================================================
package client

// FailMode decides how clients action when clients fail to invoke services
type FailMode int

const (
	// Failover selects another server automaticaly
	Failover FailMode = iota
	// Failfast returns error immediately
	Failfast
	// Failtry use current client again
	Failtry
	// Failbackup select another server if the first server doesn't respond in specified time and use the fast response.
	Failbackup
)

// SelectMode defines the algorithm of selecting a services from candidates.
type SelectMode int

const (
	// RandomSelect is selecting randomly
	RandomSelect SelectMode = iota
	// RoundRobin is selecting by round robin
	RoundRobin
	// WeightedRoundRobin is selecting by weighted round robin
	WeightedRoundRobin
	// WeightedICMP is selecting by weighted Ping time
	WeightedICMP
	// ConsistentHash is selecting by hashing
	ConsistentHash
	// Closest is selecting the closest server
	Closest

	// SelectByUser is selecting by implementation of users
	SelectByUser = 1000
)


================================================
FILE: client/multiple_servers_discovery.go
================================================
package client

import (
	"sync"
	"time"

	"github.com/smallnest/rpcx/log"
)

// MultipleServersDiscovery is a multiple servers service discovery.
// It always returns the current servers and users can change servers dynamically.
type MultipleServersDiscovery struct {
	pairsMu sync.RWMutex
	pairs   []*KVPair
	chans   []chan []*KVPair

	mu sync.Mutex
}

// NewMultipleServersDiscovery returns a new MultipleServersDiscovery.
func NewMultipleServersDiscovery(pairs []*KVPair) (*MultipleServersDiscovery, error) {
	return &MultipleServersDiscovery{
		pairs: pairs,
	}, nil
}

// Clone clones this ServiceDiscovery with new servicePath.
func (d *MultipleServersDiscovery) Clone(servicePath string) (ServiceDiscovery, error) {
	return d, nil
}

// SetFilter sets the filer.
func (d *MultipleServersDiscovery) SetFilter(filter ServiceDiscoveryFilter) {
}

// GetServices returns the configured server
func (d *MultipleServersDiscovery) GetServices() []*KVPair {
	d.pairsMu.RLock()
	defer d.pairsMu.RUnlock()

	return d.pairs
}

// WatchService returns a nil chan.
func (d *MultipleServersDiscovery) WatchService() chan []*KVPair {
	d.mu.Lock()
	defer d.mu.Unlock()

	ch := make(chan []*KVPair, 10)
	d.chans = append(d.chans, ch)
	return ch
}

func (d *MultipleServersDiscovery) RemoveWatcher(ch chan []*KVPair) {
	d.mu.Lock()
	defer d.mu.Unlock()

	var chans []chan []*KVPair
	for _, c := range d.chans {
		if c == ch {
			continue
		}

		chans = append(chans, c)
	}

	d.chans = chans
}

// Update is used to update servers at runtime.
func (d *MultipleServersDiscovery) Update(pairs []*KVPair) {
	d.mu.Lock()
	defer d.mu.Unlock()

	for _, ch := range d.chans {
		ch := ch
		go func() {
			defer func() {
				recover()
			}()
			select {
			case ch <- pairs:
			case <-time.After(time.Minute):
				log.Warn("chan is full and new change has been dropped")
			}
		}()
	}

	d.pairsMu.Lock()
	d.pairs = pairs
	d.pairsMu.Unlock()
}

func (d *MultipleServersDiscovery) Close() {
}


================================================
FILE: client/oneclient.go
================================================
package client

import (
	"context"
	"fmt"
	"io"
	"net"
	"sync"

	"github.com/smallnest/rpcx/share"

	multierror "github.com/hashicorp/go-multierror"
	"github.com/smallnest/rpcx/protocol"
)

// OneClient wraps servicesPath and XClients.
// Users can use a shared oneclient to access multiple services.
type OneClient struct {
	xclients map[string]XClient
	mu       sync.RWMutex

	failMode   FailMode
	selectMode SelectMode
	discovery  ServiceDiscovery
	option     Option

	selectors map[string]Selector
	Plugins   PluginContainer
	latitude  float64
	longitude float64
	auth      string

	serverMessageChan chan<- *protocol.Message
}

// NewOneClient creates a OneClient that supports service discovery and service governance.
func NewOneClient(failMode FailMode, selectMode SelectMode, discovery ServiceDiscovery, option Option) *OneClient {
	return &OneClient{
		failMode:   failMode,
		selectMode: selectMode,
		discovery:  discovery,
		option:     option,
		xclients:   make(map[string]XClient),
		selectors:  make(map[string]Selector),
	}
}

// NewBidirectionalOneClient creates a new xclient that can receive notifications from servers.
func NewBidirectionalOneClient(failMode FailMode, selectMode SelectMode, discovery ServiceDiscovery, option Option, serverMessageChan chan<- *protocol.Message) *OneClient {
	return &OneClient{
		failMode:          failMode,
		selectMode:        selectMode,
		discovery:         discovery,
		option:            option,
		xclients:          make(map[string]XClient),
		selectors:         make(map[string]Selector),
		serverMessageChan: serverMessageChan,
	}
}

// SetSelector sets customized selector by users.
func (c *OneClient) SetSelector(servicePath string, s Selector) {
	c.mu.Lock()
	c.selectors[servicePath] = s
	if xclient, ok := c.xclients[servicePath]; ok {
		xclient.SetSelector(s)
	}
	c.mu.Unlock()
}

// SetPlugins sets client's plugins.
func (c *OneClient) SetPlugins(plugins PluginContainer) {
	c.Plugins = plugins
	c.mu.RLock()
	for _, v := range c.xclients {
		v.SetPlugins(plugins)
	}
	c.mu.RUnlock()
}

func (c *OneClient) GetPlugins() PluginContainer {
	return c.Plugins
}

// ConfigGeoSelector sets location of client's latitude and longitude,
// and use newGeoSelector.
func (c *OneClient) ConfigGeoSelector(latitude, longitude float64) {
	c.selectMode = Closest
	c.latitude = latitude
	c.longitude = longitude

	c.mu.RLock()
	for _, v := range c.xclients {
		v.ConfigGeoSelector(latitude, longitude)
	}
	c.mu.RUnlock()
}

// Auth sets s token for Authentication.
func (c *OneClient) Auth(auth string) {
	c.auth = auth
	c.mu.RLock()
	for _, v := range c.xclients {
		v.Auth(auth)
	}
	c.mu.RUnlock()
}

// Go invokes the function asynchronously. It returns the Call structure representing the invocation. The done channel will signal when the call is complete by returning the same Call object. If done is nil, Go will allocate a new channel. If non-nil, done must be buffered or Go will deliberately crash.
// It does not use FailMode.
func (c *OneClient) Go(ctx context.Context, servicePath string, serviceMethod string, args interface{}, reply interface{}, done chan *Call) (*Call, error) {
	c.mu.RLock()
	xclient := c.xclients[servicePath]
	c.mu.RUnlock()

	if xclient == nil {
		var err error
		c.mu.Lock()
		xclient = c.xclients[servicePath]
		if xclient == nil {
			xclient, err = c.newXClient(servicePath)
			c.xclients[servicePath] = xclient
		}
		c.mu.Unlock()
		if err != nil {
			return nil, err
		}
	}

	return xclient.Go(ctx, serviceMethod, args, reply, done)
}

func (c *OneClient) newXClient(servicePath string) (xclient XClient, err error) {
	defer func() {
		if r := recover(); r != nil {
			if e, ok := r.(error); ok {
				err = e
			} else {
				err = fmt.Errorf("%v", r)
			}
		}
	}()

	d, err := c.discovery.Clone(servicePath)
	if err != nil {
		return nil, err
	}

	if c.serverMessageChan == nil {
		xclient = NewXClient(servicePath, c.failMode, c.selectMode, d, c.option)
	} else {
		xclient = NewBidirectionalXClient(servicePath, c.failMode, c.selectMode, d, c.option, c.serverMessageChan)
	}

	if c.Plugins != nil {
		xclient.SetPlugins(c.Plugins)
	}

	if s, ok := c.selectors[servicePath]; ok {
		xclient.SetSelector(s)
	}

	if c.selectMode == Closest {
		xclient.ConfigGeoSelector(c.latitude, c.longitude)
	}

	if c.auth != "" {
		xclient.Auth(c.auth)
	}

	return xclient, err
}

// Call invokes the named function, waits for it to complete, and returns its error status.
// It handles errors base on FailMode.
func (c *OneClient) Call(ctx context.Context, servicePath string, serviceMethod string, args interface{}, reply interface{}) error {
	c.mu.RLock()
	xclient := c.xclients[servicePath]
	c.mu.RUnlock()

	if xclient == nil {
		var err error
		c.mu.Lock()
		xclient = c.xclients[servicePath]
		if xclient == nil {
			xclient, err = c.newXClient(servicePath)
			c.xclients[servicePath] = xclient
		}
		c.mu.Unlock()
		if err != nil {
			return err
		}
	}

	return xclient.Call(ctx, serviceMethod, args, reply)
}

func (c *OneClient) SendRaw(ctx context.Context, r *protocol.Message) (map[string]string, []byte, error) {
	servicePath := r.ServicePath

	c.mu.RLock()
	xclient := c.xclients[servicePath]
	c.mu.RUnlock()

	if xclient == nil {
		var err error
		c.mu.Lock()
		xclient = c.xclients[servicePath]
		if xclient == nil {
			xclient, err = c.newXClient(servicePath)
			c.xclients[servicePath] = xclient
		}
		c.mu.Unlock()

		if err != nil {
			return nil, nil, err
		}
	}

	return xclient.SendRaw(ctx, r)
}

// Broadcast sends requests to all servers and Success only when all servers return OK.
// FailMode and SelectMode are meanless for this method.
// Please set timeout to avoid hanging.
func (c *OneClient) Broadcast(ctx context.Context, servicePath string, serviceMethod string, args interface{}, reply interface{}) error {
	c.mu.RLock()
	xclient := c.xclients[servicePath]
	c.mu.RUnlock()

	if xclient == nil {
		var err error
		c.mu.Lock()
		xclient = c.xclients[servicePath]
		if xclient == nil {
			xclient, err = c.newXClient(servicePath)
			c.xclients[servicePath] = xclient
		}
		c.mu.Unlock()
		if err != nil {
			return err
		}
	}

	return xclient.Broadcast(ctx, serviceMethod, args, reply)
}

// Fork sends requests to all servers and Success once one server returns OK.
// FailMode and SelectMode are meanless for this method.
func (c *OneClient) Fork(ctx context.Context, servicePath string, serviceMethod string, args interface{}, reply interface{}) error {
	c.mu.RLock()
	xclient := c.xclients[servicePath]
	c.mu.RUnlock()

	if xclient == nil {
		var err error
		c.mu.Lock()
		xclient = c.xclients[servicePath]
		if xclient == nil {
			xclient, err = c.newXClient(servicePath)
			c.xclients[servicePath] = xclient
		}
		c.mu.Unlock()
		if err != nil {
			return err
		}
	}

	return xclient.Fork(ctx, serviceMethod, args, reply)
}

func (c *OneClient) SendFile(ctx context.Context, fileName string, rateInBytesPerSecond int64, meta map[string]string) error {
	c.mu.RLock()
	xclient := c.xclients[share.SendFileServiceName]
	c.mu.RUnlock()

	if xclient == nil {
		var err error
		c.mu.Lock()
		xclient = c.xclients[share.SendFileServiceName]
		if xclient == nil {
			xclient, err = c.newXClient(share.SendFileServiceName)
			c.xclients[share.SendFileServiceName] = xclient
		}
		c.mu.Unlock()
		if err != nil {
			return err
		}
	}

	return xclient.SendFile(ctx, fileName, rateInBytesPerSecond, meta)
}

func (c *OneClient) DownloadFile(ctx context.Context, requestFileName string, saveTo io.Writer, meta map[string]string) error {
	c.mu.RLock()
	xclient := c.xclients[share.SendFileServiceName]
	c.mu.RUnlock()

	if xclient == nil {
		var err error
		c.mu.Lock()
		xclient = c.xclients[share.SendFileServiceName]
		if xclient == nil {
			xclient, err = c.newXClient(share.SendFileServiceName)
			c.xclients[share.SendFileServiceName] = xclient
		}
		c.mu.Unlock()
		if err != nil {
			return err
		}
	}

	return xclient.DownloadFile(ctx, requestFileName, saveTo, meta)
}

func (c *OneClient) Stream(ctx context.Context, meta map[string]string) (net.Conn, error) {
	c.mu.RLock()
	xclient := c.xclients[share.StreamServiceName]
	c.mu.RUnlock()

	if xclient == nil {
		var err error
		c.mu.Lock()
		xclient = c.xclients[share.StreamServiceName]
		if xclient == nil {
			xclient, err = c.newXClient(share.StreamServiceName)
			c.xclients[share.StreamServiceName] = xclient
		}
		c.mu.Unlock()
		if err != nil {
			return nil, err
		}
	}

	return xclient.Stream(ctx, meta)
}

// Close closes all xclients and its underlying connections to services.
func (c *OneClient) Close() error {
	var result error

	c.mu.RLock()
	for _, v := range c.xclients {
		err := v.Close()
		if err != nil {
			result = multierror.Append(result, err)
		}
	}
	c.mu.RUnlock()

	return result
}


================================================
FILE: client/oneclient_pool.go
================================================
package client

import (
	"sync/atomic"

	"github.com/smallnest/rpcx/protocol"
)

// OneClientPool is a oneclient pool with fixed size.
// It uses roundrobin algorithm to call its xclients.
// All oneclients share the same configurations such as ServiceDiscovery and serverMessageChan.
type OneClientPool struct {
	count      uint64
	index      uint64
	oneclients []*OneClient
	auth       string
	Plugins    PluginContainer

	failMode          FailMode
	selectMode        SelectMode
	discovery         ServiceDiscovery
	option            Option
	serverMessageChan chan<- *protocol.Message
}

// NewOneClientPool creates a fixed size OneClient pool.
func NewOneClientPool(count int, failMode FailMode, selectMode SelectMode, discovery ServiceDiscovery, option Option) *OneClientPool {
	pool := &OneClientPool{
		count:      uint64(count),
		oneclients: make([]*OneClient, count),
		failMode:   failMode,
		selectMode: selectMode,
		discovery:  discovery,
		option:     option,
	}

	for i := 0; i < count; i++ {
		oneclient := NewOneClient(failMode, selectMode, discovery, option)
		pool.oneclients[i] = oneclient
	}
	return pool
}

// NewBidirectionalOneClientPool creates a BidirectionalOneClient pool with fixed size.
func NewBidirectionalOneClientPool(count int, failMode FailMode, selectMode SelectMode, discovery ServiceDiscovery, option Option, serverMessageChan chan<- *protocol.Message) *OneClientPool {
	pool := &OneClientPool{
		count:             uint64(count),
		oneclients:        make([]*OneClient, count),
		failMode:          failMode,
		selectMode:        selectMode,
		discovery:         discovery,
		option:            option,
		serverMessageChan: serverMessageChan,
	}

	for i := 0; i < count; i++ {
		oneclient := NewBidirectionalOneClient(failMode, selectMode, discovery, option, serverMessageChan)
		pool.oneclients[i] = oneclient
	}
	return pool
}

// Auth sets s token for Authentication.
func (p *OneClientPool) Auth(auth string) {
	p.auth = auth

	for _, v := range p.oneclients {
		v.Auth(auth)
	}
}

// SetPlugins sets client's plugins.
func (p *OneClientPool) SetPlugins(plugins PluginContainer) {
	p.Plugins = plugins

	for _, v := range p.oneclients {
		v.SetPlugins(plugins)
	}
}

// GetPlugins returns client's plugins.
func (p *OneClientPool) GetPlugins() PluginContainer {
	return p.Plugins
}

// Get returns a OneClient.
// It does not remove this OneClient from its cache so you don't need to put it back.
// Don't close this OneClient because maybe other goroutines are using this OneClient.
func (p *OneClientPool) Get() *OneClient {
	i := atomic.AddUint64(&p.index, 1)
	picked := int(i % p.count)
	return p.oneclients[picked]
}

// Close this pool.
// Please make sure it won't be used any more.
func (p *OneClientPool) Close() {
	for _, c := range p.oneclients {
		c.Close()
	}
	p.oneclients = nil
}


================================================
FILE: client/oneclient_pool_test.go
================================================
package client

import (
	"testing"
)

func TestOneClientPool_SetPlugins(t *testing.T) {
	// Create a simple discovery
	pairs := []*KVPair{
		{Key: "tcp@127.0.0.1:8972", Value: ""},
	}
	discovery, err := NewMultipleServersDiscovery(pairs)
	if err != nil {
		t.Fatalf("failed to create discovery: %v", err)
	}
	defer discovery.Close()

	// Create a pool
	pool := NewOneClientPool(3, Failtry, RandomSelect, discovery, DefaultOption)
	defer pool.Close()

	// Create plugins
	plugins := NewPluginContainer()
	tp := &testPlugin{name: "test-plugin"}
	plugins.Add(tp)

	// Test SetPlugins
	pool.SetPlugins(plugins)

	// Verify plugins are set on pool
	if pool.GetPlugins() == nil {
		t.Error("plugins should not be nil after SetPlugins")
	}
	if pool.GetPlugins() != plugins {
		t.Error("pool plugins should be the same as the set plugins")
	}

	// Verify plugins are set on all oneclients
	for i := 0; i < 3; i++ {
		oneclient := pool.Get()
		if oneclient.GetPlugins() == nil {
			t.Errorf("oneclient %d plugins should not be nil", i)
		}
		if oneclient.GetPlugins() != plugins {
			t.Errorf("oneclient %d plugins should be the same as the set plugins", i)
		}
	}
}

func TestOneClientPool_GetPlugins(t *testing.T) {
	// Create a simple discovery
	pairs := []*KVPair{
		{Key: "tcp@127.0.0.1:8972", Value: ""},
	}
	discovery, err := NewMultipleServersDiscovery(pairs)
	if err != nil {
		t.Fatalf("failed to create discovery: %v", err)
	}
	defer discovery.Close()

	// Create a pool
	pool := NewOneClientPool(2, Failtry, RandomSelect, discovery, DefaultOption)
	defer pool.Close()

	// Initially, plugins should be nil
	if pool.GetPlugins() != nil {
		t.Error("plugins should be nil initially")
	}

	// Create and set plugins
	plugins := NewPluginContainer()
	tp := &testPlugin{name: "test-plugin"}
	plugins.Add(tp)
	pool.SetPlugins(plugins)

	// Verify GetPlugins returns the correct plugins
	retrievedPlugins := pool.GetPlugins()
	if retrievedPlugins == nil {
		t.Error("plugins should not be nil after SetPlugins")
	}
	if retrievedPlugins != plugins {
		t.Error("GetPlugins should return the same plugins as SetPlugins")
	}

	// Verify plugins contain the test plugin
	allPlugins := retrievedPlugins.All()
	if len(allPlugins) != 1 {
		t.Errorf("expected 1 plugin, got %d", len(allPlugins))
	}
	if p, ok := allPlugins[0].(*testPlugin); !ok || p.name != "test-plugin" {
		t.Error("plugin should be the test plugin")
	}
}

func TestOneClientPool_SetPlugins_Concurrent(t *testing.T) {
	// Create a simple discovery
	pairs := []*KVPair{
		{Key: "tcp@127.0.0.1:8972", Value: ""},
	}
	discovery, err := NewMultipleServersDiscovery(pairs)
	if err != nil {
		t.Fatalf("failed to create discovery: %v", err)
	}
	defer discovery.Close()

	// Create a pool
	pool := NewOneClientPool(5, Failtry, RandomSelect, discovery, DefaultOption)
	defer pool.Close()

	// Test concurrent SetPlugins calls
	done := make(chan bool, 10)
	for i := 0; i < 10; i++ {
		go func(id int) {
			plugins := NewPluginContainer()
			tp := &testPlugin{name: "test-plugin"}
			plugins.Add(tp)
			pool.SetPlugins(plugins)
			done <- true
		}(i)
	}

	// Wait for all goroutines to complete
	for i := 0; i < 10; i++ {
		<-done
	}

	// Verify plugins are set
	if pool.GetPlugins() == nil {
		t.Error("plugins should not be nil after concurrent SetPlugins")
	}
}



================================================
FILE: client/peer2peer_discovery.go
================================================
package client

// Peer2PeerDiscovery is a peer-to-peer service discovery.
// It always returns the static server.
type Peer2PeerDiscovery struct {
	server   string
	metadata string
}

// NewPeer2PeerDiscovery returns a new Peer2PeerDiscovery.
func NewPeer2PeerDiscovery(server, metadata string) (*Peer2PeerDiscovery, error) {
	return &Peer2PeerDiscovery{server: server, metadata: metadata}, nil
}

// Clone clones this ServiceDiscovery with new servicePath.
func (d *Peer2PeerDiscovery) Clone(servicePath string) (ServiceDiscovery, error) {
	return d, nil
}

// SetFilter sets the filer.
func (d *Peer2PeerDiscovery) SetFilter(filter ServiceDiscoveryFilter) {

}

// GetServices returns the static server
func (d *Peer2PeerDiscovery) GetServices() []*KVPair {
	return []*KVPair{&KVPair{Key: d.server, Value: d.metadata}}
}

// WatchService returns a nil chan.
func (d *Peer2PeerDiscovery) WatchService() chan []*KVPair {
	return nil
}

func (d *Peer2PeerDiscovery) RemoveWatcher(ch chan []*KVPair) {}

func (d *Peer2PeerDiscovery) Close() {

}


================================================
FILE: client/ping_utils.go
================================================
package client

import (
	"context"
	"net"
	"strings"
	"time"

	ping "github.com/go-ping/ping"
)

// weightedICMPSelector selects servers with ping result.
type weightedICMPSelector struct {
	servers []*Weighted
	wrs     *weightedRoundRobinSelector
}

func newWeightedICMPSelector(servers map[string]string) Selector {
	ss := createICMPWeighted(servers)
	wicmps := weightedICMPSelector{
		servers: ss,
		wrs:     &weightedRoundRobinSelector{servers: ss},
	}
	wicmps.wrs.servers = ss
	wicmps.wrs.buildRing()
	return &wicmps
}

func (s *weightedICMPSelector) Select(ctx context.Context, servicePath, serviceMethod string, args interface{}) string {
	return s.wrs.Select(ctx, servicePath, serviceMethod, args)
}

func (s *weightedICMPSelector) UpdateServer(servers map[string]string) {
	ss := createICMPWeighted(servers)
	s.wrs.servers = ss
	s.servers = ss
	s.wrs.buildRing()
}

func createICMPWeighted(servers map[string]string) []*Weighted {
	var ss = make([]*Weighted, 0, len(servers))
	for k := range servers {
		w := &Weighted{Server: k, Weight: 1, EffectiveWeight: 1}
		server := strings.Split(k, "@")
		host, _, _ := net.SplitHostPort(server[1])
		rtt, _ := Ping(host)
		rtt = CalculateWeight(rtt)
		w.Weight = rtt
		w.EffectiveWeight = rtt
		ss = append(ss, w)
	}
	return ss
}

// Ping gets network traffic by ICMP
func Ping(host string) (rtt int, err error) {
	rtt = 1000 // default and timeout is 1000 ms

	pinger, err := ping.NewPinger(host)
	if err != nil {
		return rtt, err
	}
	pinger.Count = 3
	pinger.Timeout = 3 * time.Second
	err = pinger.Run()
	if err != nil {
		return rtt, err
	}
	stats := pinger.Statistics()
	// ping failed
	if len(stats.Rtts) == 0 {
		return rtt, err
	}
	rtt = int(stats.AvgRtt) / 1e6

	return rtt, err
}

// CalculateWeight converts the rtt to weighted by:
//  1. weight=191 if t <= 10
//  2. weight=201 -t if 10 < t <=200
//  3. weight=1 if 200 < t < 1000
//  4. weight = 0 if t >= 1000
//
// It means servers that ping time t < 10 will be preferred
// and servers won't be selected if t > 1000.
// It is hard coded based on Ops experience.
func CalculateWeight(rtt int) int {
	switch {
	case rtt >= 0 && rtt <= 10:
		return 191
	case rtt > 10 && rtt <= 200:
		return 201 - rtt
	case rtt > 100 && rtt < 1000:
		return 1
	default:
		return 0
	}
}


================================================
FILE: client/plugin.go
================================================
package client

import (
	"context"
	"net"

	"github.com/smallnest/rpcx/protocol"
)

// pluginContainer implements PluginContainer interface.
type pluginContainer struct {
	plugins []Plugin
}

func NewPluginContainer() PluginContainer {
	return &pluginContainer{}
}

// Plugin is the client plugin interface.
type Plugin interface{}

// Add adds a plugin.
func (p *pluginContainer) Add(plugin Plugin) {
	p.plugins = append(p.plugins, plugin)
}

// Remove removes a plugin by its name.
func (p *pluginContainer) Remove(plugin Plugin) {
	if p.plugins == nil {
		return
	}

	var plugins []Plugin
	for _, pp := range p.plugins {
		if pp != plugin {
			plugins = append(plugins, pp)
		}
	}

	p.plugins = plugins
}

// All returns all plugins
func (p *pluginContainer) All() []Plugin {
	return p.plugins
}

// DoPreCall executes before call
func (p *pluginContainer) DoPreCall(ctx context.Context, servicePath, serviceMethod string, args interface{}) error {
	for i := range p.plugins {
		if plugin, ok := p.plugins[i].(PreCallPlugin); ok {
			err := plugin.PreCall(ctx, servicePath, serviceMethod, args)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

// DoPostCall executes after call
func (p *pluginContainer) DoPostCall(ctx context.Context, servicePath, serviceMethod string, args interface{}, reply interface{}, err error) error {
	for i := range p.plugins {
		if plugin, ok := p.plugins[i].(PostCallPlugin); ok {
			err = plugin.PostCall(ctx, servicePath, serviceMethod, args, reply, err)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

// DoConnCreated is called in case of client connection created.
func (p *pluginContainer) DoConnCreated(conn net.Conn) (net.Conn, error) {
	var err error
	for i := range p.plugins {
		if plugin, ok := p.plugins[i].(ConnCreatedPlugin); ok {
			conn, err = plugin.ConnCreated(conn)
			if err != nil {
				return conn, err
			}
		}
	}
	return conn, nil
}

// DoConnCreateFailed is called in case of client connection create failed.
func (p *pluginContainer) DoConnCreateFailed(network, address string) {
	for i := range p.plugins {
		if plugin, ok := p.plugins[i].(ConnCreateFailedPlugin); ok {
			plugin.ConnCreateFailed(network, address)
		}
	}
}

// DoClientConnected is called in case of connected.
func (p *pluginContainer) DoClientConnected(conn net.Conn) (net.Conn, error) {
	var err error
	for i := range p.plugins {
		if plugin, ok := p.plugins[i].(ClientConnectedPlugin); ok {
			conn, err = plugin.ClientConnected(conn)
			if err != nil {
				return conn, err
			}
		}
	}
	return conn, nil
}

// DoClientConnectionClose is called in case of connection close.
func (p *pluginContainer) DoClientConnectionClose(conn net.Conn) error {
	var err error
	for i := range p.plugins {
		if plugin, ok := p.plugins[i].(ClientConnectionClosePlugin); ok {
			err = plugin.ClientConnectionClose(conn)
			if err != nil {
				return err
			}
		}
	}
	return err
}

// DoClientBeforeEncode is called when requests are encoded and sent.
func (p *pluginContainer) DoClientBeforeEncode(req *protocol.Message) error {
	var err error
	for i := range p.plugins {
		if plugin, ok := p.plugins[i].(ClientBeforeEncodePlugin); ok {
			err = plugin.ClientBeforeEncode(req)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

// DoClientAfterDecode is called when requests are decoded and received.
func (p *pluginContainer) DoClientAfterDecode(req *protocol.Message) error {
	var err error
	for i := range p.plugins {
		if plugin, ok := p.plugins[i].(ClientAfterDecodePlugin); ok {
			err = plugin.ClientAfterDecode(req)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

// DoWrapSelect is called when select a node.
func (p *pluginContainer) DoWrapSelect(fn SelectFunc) SelectFunc {
	rt := fn
	for i := range p.plugins {
		if pn, ok := p.plugins[i].(SelectNodePlugin); ok {
			rt = pn.WrapSelect(rt)
		}
	}

	return rt
}

type (
	// PreCallPlugin is invoked before the client calls a server.
	PreCallPlugin interface {
		PreCall(ctx context.Context, servicePath, serviceMethod string, args interface{}) error
	}

	// PostCallPlugin is invoked after the client calls a server.
	PostCallPlugin interface {
		PostCall(ctx context.Context, servicePath, serviceMethod string, args interface{}, reply interface{}, err error) error
	}

	// ConnCreatedPlugin is invoked when the client connection has created.
	ConnCreatedPlugin interface {
		ConnCreated(net.Conn) (net.Conn, error)
	}

	ConnCreateFailedPlugin interface {
		ConnCreateFailed(network, address string)
	}

	// ClientConnectedPlugin is invoked when the client has connected the server.
	ClientConnectedPlugin interface {
		ClientConnected(net.Conn) (net.Conn, error)
	}

	// ClientConnectionClosePlugin is invoked when the connection is closing.
	ClientConnectionClosePlugin interface {
		ClientConnectionClose(net.Conn) error
	}

	// ClientBeforeEncodePlugin is invoked when the message is encoded and sent.
	ClientBeforeEncodePlugin interface {
		ClientBeforeEncode(*protocol.Message) error
	}

	// ClientAfterDecodePlugin is invoked when the message is decoded.
	ClientAfterDecodePlugin interface {
		ClientAfterDecode(*protocol.Message) error
	}

	// SelectNodePlugin can interrupt selecting of xclient and add customized logics such as skipping some nodes.
	SelectNodePlugin interface {
		WrapSelect(SelectFunc) SelectFunc
	}

	// PluginContainer represents a plugin container that defines all methods to manage plugins.
	// And it also defines all extension points.
	PluginContainer interface {
		Add(plugin Plugin)
		Remove(plugin Plugin)
		All() []Plugin

		DoConnCreated(net.Conn) (net.Conn, error)
		DoConnCreateFailed(network, address string)
		DoClientConnected(net.Conn) (net.Conn, error)
		DoClientConnectionClose(net.Conn) error

		DoPreCall(ctx context.Context, servicePath, serviceMethod string, args interface{}) error
		DoPostCall(ctx context.Context, servicePath, serviceMethod string, args interface{}, reply interface{}, err error) error

		DoClientBeforeEncode(*protocol.Message) error
		DoClientAfterDecode(*protocol.Message) error

		DoWrapSelect(SelectFunc) SelectFunc
	}
)


================================================
FILE: client/selectmode_enumer.go
================================================
// Code generated by "enumer -type=SelectMode"; DO NOT EDIT.

package client

import (
	"fmt"
)

const _SelectModeName = "RandomSelectRoundRobinWeightedRoundRobinWeightedICMPConsistentHashClosest"

var _SelectModeIndex = [...]uint8{0, 12, 22, 40, 52, 66, 73}

func (i SelectMode) String() string {
	if i < 0 || i >= SelectMode(len(_SelectModeIndex)-1) {
		return fmt.Sprintf("SelectMode(%d)", i)
	}
	return _SelectModeName[_SelectModeIndex[i]:_SelectModeIndex[i+1]]
}

var _SelectModeValues = []SelectMode{0, 1, 2, 3, 4, 5}

var _SelectModeNameToValueMap = map[string]SelectMode{
	_SelectModeName[0:12]:  0,
	_SelectModeName[12:22]: 1,
	_SelectModeName[22:40]: 2,
	_SelectModeName[40:52]: 3,
	_SelectModeName[52:66]: 4,
	_SelectModeName[66:73]: 5,
}

// SelectModeString retrieves an enum value from the enum constants string name.
// Throws an error if the param is not part of the enum.
func SelectModeString(s string) (SelectMode, error) {
	if val, ok := _SelectModeNameToValueMap[s]; ok {
		return val, nil
	}
	return 0, fmt.Errorf("%s does not belong to SelectMode values", s)
}

// SelectModeValues returns all values of the enum
func SelectModeValues() []SelectMode {
	return _SelectModeValues
}

// IsASelectMode returns "true" if the value is listed in the enum definition. "false" otherwise
func (i SelectMode) IsASelectMode() bool {
	for _, v := range _SelectModeValues {
		if i == v {
			return true
		}
	}
	return false
}


================================================
FILE: client/selector.go
================================================
package client

import (
	"container/ring"
	"context"
	"math"
	"math/rand"
	"net/url"
	"sort"
	"strconv"
	"time"

	"github.com/edwingeng/doublejump"

	"github.com/valyala/fastrand"
)

type SelectFunc func(ctx context.Context, servicePath, serviceMethod string, args interface{}) string

// Selector defines selector that selects one service from candidates.
type Selector interface {
	Select(ctx context.Context, servicePath, serviceMethod string, args interface{}) string // SelectFunc
	UpdateServer(servers map[string]string)
}

func newSelector(selectMode SelectMode, servers map[string]string) Selector {
	switch selectMode {
	case RandomSelect:
		return newRandomSelector(servers)
	case RoundRobin:
		return newRoundRobinSelector(servers)
	case WeightedRoundRobin:
		return newWeightedRoundRobinSelector(servers)
	case WeightedICMP:
		return newWeightedICMPSelector(servers)
	case ConsistentHash:
		return newConsistentHashSelector(servers)
	case SelectByUser:
		return nil
	default:
		return newRandomSelector(servers)
	}
}

// randomSelector selects randomly.
type randomSelector struct {
	servers []string
}

func newRandomSelector(servers map[string]string) Selector {
	ss := make([]string, 0, len(servers))
	for k := range servers {
		ss = append(ss, k)
	}

	return &randomSelector{servers: ss}
}

func (s *randomSelector) Select(ctx context.Context, servicePath, serviceMethod string, args interface{}) string {
	ss := s.servers
	if len(ss) == 0 {
		return ""
	}
	i := fastrand.Uint32n(uint32(len(ss)))
	return ss[i]
}

func (s *randomSelector) UpdateServer(servers map[string]string) {
	ss := make([]string, 0, len(servers))
	for k := range servers {
		ss = append(ss, k)
	}

	s.servers = ss
}

// roundRobinSelector selects servers with roundrobin.
type roundRobinSelector struct {
	servers []string
	i       int
}

func newRoundRobinSelector(servers map[string]string) Selector {
	ss := make([]string, 0, len(servers))
	for k := range servers {
		ss = append(ss, k)
	}

	return &roundRobinSelector{servers: ss}
}

func (s *roundRobinSelector) Select(ctx context.Context, servicePath, serviceMethod string, args interface{}) string {
	ss := s.servers
	if len(ss) == 0 {
		return ""
	}
	i := s.i
	i = i % len(ss)
	s.i = i + 1

	return ss[i]
}

func (s *roundRobinSelector) UpdateServer(servers map[string]string) {
	ss := make([]string, 0, len(servers))
	for k := range servers {
		ss = append(ss, k)
	}

	s.servers = ss
}

// weightedRoundRobinSelector selects servers with weighted.
type weightedRoundRobinSelector struct {
	servers     []*Weighted
	totalWeight int
	rr          *ring.Ring
}

func newWeightedRoundRobinSelector(servers map[string]string) Selector {
	ss := createWeighted(servers)
	s := &weightedRoundRobinSelector{servers: ss}
	s.buildRing()
	return s
}

func (s *weightedRoundRobinSelector) Select(ctx context.Context, servicePath, serviceMethod string, args interface{}) string {
	ss := s.servers
	if len(ss) == 0 {
		return ""
	}
	val := s.rr.Value
	s.rr = s.rr.Next()
	return val.(*Weighted).Server

}

func (s *weightedRoundRobinSelector) UpdateServer(servers map[string]string) {
	newServer := newWeightedRoundRobinSelector(servers).(*weightedRoundRobinSelector)
	*s = *newServer
}

func (s *weightedRoundRobinSelector) buildRing() {
	s.totalWeight = 0
	for _, w := range s.servers {
		s.totalWeight += w.Weight
	}
	s.rr = ring.New(s.totalWeight)
	for i := 0; i < s.totalWeight; i++ {
		n := s.next()
		s.rr.Value = n
		s.rr = s.rr.Next()
	}
}
func (s *weightedRoundRobinSelector) next() *Weighted {
	if len(s.servers) == 0 {
		return nil
	}
	n := len(s.servers)
	if n == 0 {
		return nil
	}
	if n == 1 {
		return s.servers[0]
	}
	flag := 0
	m := 0
	for i := 0; i < n; i++ {
		s.servers[i].CurrentWeight += s.servers[i].Weight
		if s.servers[i].CurrentWeight > m {
			m = s.servers[i].CurrentWeight
			flag = i
		}
	}
	s.servers[flag].CurrentWeight -= s.totalWeight
	return s.servers[flag]
}
func createWeighted(servers map[string]string) []*Weighted {
	ss := make([]*Weighted, 0, len(servers))
	for k, metadata := range servers {
		w := &Weighted{Server: k, Weight: 1}

		if v, err := url.ParseQuery(metadata); err == nil {
			ww := v.Get("weight")
			if ww != "" {
				if weight, err := strconv.Atoi(ww); err == nil {
					w.Weight = weight
				}
			}
		}

		ss = append(ss, w)
	}

	return ss
}

type geoServer struct {
	Server    string
	Latitude  float64
	Longitude float64
}

// geoSelector selects servers based on location.
type geoSelector struct {
	servers   []*geoServer
	Latitude  float64
	Longitude float64
	r         *rand.Rand
}

func newGeoSelector(servers map[string]string, latitude, longitude float64) Selector {
	ss := createGeoServer(servers)
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	return &geoSelector{servers: ss, Latitude: latitude, Longitude: longitude, r: r}
}

func (s *geoSelector) Select(ctx context.Context, servicePath, serviceMethod string, args interface{}) string {
	if len(s.servers) == 0 {
		return ""
	}

	var server []string
	minNum := math.MaxFloat64
	for _, gs := range s.servers {
		d := getDistanceFrom(s.Latitude, s.Longitude, gs.Latitude, gs.Longitude)
		if d < minNum {
			server = []string{gs.Server}
			minNum = d
		} else if d == minNum {
			server = append(server, gs.Server)
		}
	}

	if len(server) == 1 {
		return server[0]
	}

	return server[s.r.Intn(len(server))]
}

func (s *geoSelector) UpdateServer(servers map[string]string) {
	ss := createGeoServer(servers)
	s.servers = ss
}

func createGeoServer(servers map[string]string) []*geoServer {
	geoServers := make([]*geoServer, 0, len(servers))

	for s, metadata := range servers {
		if v, err := url.ParseQuery(metadata); err == nil {
			latStr := v.Get("latitude")
			lonStr := v.Get("longitude")

			if latStr == "" || lonStr == "" {
				continue
			}

			lat, err := strconv.ParseFloat(latStr, 64)
			if err != nil {
				continue
			}
			lon, err := strconv.ParseFloat(lonStr, 64)
			if err != nil {
				continue
			}

			geoServers = append(geoServers, &geoServer{Server: s, Latitude: lat, Longitude: lon})

		}
	}

	return geoServers
}

// consistentHashSelector selects based on JumpConsistentHash.
type consistentHashSelector struct {
	h       *doublejump.Hash
	servers []string
}

func newConsistentHashSelector(servers map[string]string) Selector {
	h := doublejump.NewHash()
	ss := make([]string, 0, len(servers))
	for k := range servers {
		ss = append(ss, k)
		h.Add(k)
	}

	sort.Slice(ss, func(i, j int) bool { return ss[i] < ss[j] })
	return &consistentHashSelector{servers: ss, h: h}
}

func (s *consistentHashSelector) Select(ctx context.Context, servicePath, serviceMethod string, args interface{}) string {
	ss := s.servers
	if len(ss) == 0 {
		return ""
	}

	key := genKey(servicePath, serviceMethod, args)
	selected, _ := s.h.Get(key).(string)
	return selected
}

func (s *consistentHashSelector) UpdateServer(servers map[string]string) {
	ss := make([]string, 0, len(servers))
	for k := range servers {
		s.h.Add(k)
		ss = append(ss, k)
	}

	sort.Slice(ss, func(i, j int) bool { return ss[i] < ss[j] })

	for _, k := range s.servers {
		if _, exist := servers[k]; !exist { // remove
			s.h.Remove(k)
		}
	}
	s.servers = ss
}


================================================
FILE: client/selector_test.go
================================================
package client

import (
	"context"
	"testing"
)

func Test_consistentHashSelector_Select(t *testing.T) {
	servers := map[string]string{
		"tcp@192.168.1.16:9392": "",
		"tcp@192.168.1.16:9393": "",
	}
	s := newConsistentHashSelector(servers).(*consistentHashSelector)

	key := uint64(9280147620691907957)
	selected, _ := s.h.Get(key).(string)

	for i := 0; i < 10000; i++ {
		selected2, _ := s.h.Get(key).(string)
		if selected != selected2 {
			t.Errorf("expected %s but got %s", selected, selected2)
		}
	}
}

func Test_consistentHashSelector_UpdateServer(t *testing.T) {
	servers := map[string]string{
		"tcp@192.168.1.16:9392": "",
		"tcp@192.168.1.16:9393": "",
	}
	s := newConsistentHashSelector(servers).(*consistentHashSelector)
	if len(s.h.All()) != len(servers) {
		t.Errorf("NewSelector: expected %d server but got %d", len(servers), len(s.h.All()))
	}
	s.UpdateServer(servers)
	if len(s.h.All()) != len(servers) {
		t.Errorf("UpdateServer: expected %d server but got %d", len(servers), len(s.h.All()))
	}
}

func TestWeightedRoundRobinSelector_Select(t *testing.T) {
	calc := make(map[string]int)
	servers := make(map[string]string)
	servers["ServerA"] = "weight=4"
	servers["ServerB"] = "weight=2"
	servers["ServerC"] = "weight=1"
	weightSelector := newWeightedRoundRobinSelector(servers).(*weightedRoundRobinSelector)
	ctx := context.Background()
	for i := 0; i < 7; i++ {
		s := weightSelector.Select(ctx, "", "", nil)
		if _, ok := calc[s]; ok {
			calc[s]++
		} else {
			calc[s] = 1
		}
	}
	if calc["ServerA"] != 4 {
		t.Errorf("expected %d but got %d", 4, calc["ServerA"])
	}
	if calc["ServerB"] != 2 {
		t.Errorf("expected %d but got %d", 2, calc["ServerB"])
	}
	if calc["ServerC"] != 1 {
		t.Errorf("expected %d but got %d", 1, calc["ServerC"])
	}
}
func TestWeightedRoundRobinSelector_UpdateServer(t *testing.T) {
	calc := make(map[string]int)
	servers := make(map[string]string)
	servers["ServerA"] = "weight=4"
	servers["ServerB"] = "weight=2"
	servers["ServerC"] = "weight=1"
	weightSelector := newWeightedRoundRobinSelector(servers).(*weightedRoundRobinSelector)
	ctx := context.Background()
	servers["ServerA"] = "weight=5"
	weightSelector.UpdateServer(servers)
	for i := 0; i < 8; i++ {
		s := weightSelector.Select(ctx, "", "", nil)
		if _, ok := calc[s]; ok {
			calc[s]++
		} else {
			calc[s] = 1
		}
	}
	if calc["ServerA"] != 5 {
		t.Errorf("expected %d but got %d", 4, calc["ServerA"])
	}
	if calc["ServerB"] != 2 {
		t.Errorf("expected %d but got %d", 2, calc["ServerB"])
	}
	if calc["ServerC"] != 1 {
		t.Errorf("expected %d but got %d", 1, calc["ServerC"])
	}
}

func BenchmarkWeightedRoundRobinSelector_Select(b *testing.B) {
	servers := make(map[string]string)
	servers["ServerA"] = "weight=4"
	servers["ServerB"] = "weight=2"
	servers["ServerC"] = "weight=1"
	ctx := context.Background()
	weightSelector := newWeightedRoundRobinSelector(servers).(*weightedRoundRobinSelector)

	for i := 0; i < b.N; i++ {
		weightSelector.Select(ctx, "", "", nil)
	}
}

//
//func TestWeightedICMPSelector(t *testing.T) {
//	calc := make(map[string]int)
//	servers := make(map[string]string)
//	servers["@localhost:3333"] = ""
//	servers["@www.baidu.com:3334"] = ""
//	servers["@xxxx.xxxx:333"] = ""
//	s := newWeightedICMPSelector(servers)
//	ctx := context.Background()
//	for i := 0; i < 10; i++ {
//		host := s.Select(ctx, "", "", nil)
//		if _, ok := calc[host]; ok {
//			calc[host]++
//		} else {
//			calc[host] = 0
//		}
//	}
//	if len(calc) != 2 {
//		t.Errorf("expected %d but got %d", 2, len(servers))
//	}
//}
//func TestWeightedICMPSelector_UpdateServer(t *testing.T) {
//	calc := make(map[string]int)
//	servers := make(map[string]string)
//	servers["@localhost:3333"] = ""
//	servers["@www.baidu.com:3334"] = ""
//	servers["@xxxx.xxxx:333"] = ""
//	s := newWeightedICMPSelector(servers)
//	ctx := context.Background()
//	servers["@www.sina.com:3333"] = ""
//	s.UpdateServer(servers)
//	for i := 0; i < 10; i++ {
//		host := s.Select(ctx, "", "", nil)
//		if _, ok := calc[host]; ok {
//			calc[host]++
//		} else {
//			calc[host] = 0
//		}
//	}
//	if len(calc) != 3 {
//		t.Errorf("expected %d but got %d", 3, len(servers))
//	}
//}


================================================
FILE: client/smooth_weighted_round_robin.go
================================================
package client

// Weighted is a wrapped server with  weight
type Weighted struct {
	Server          string
	Weight          int
	CurrentWeight   int
	EffectiveWeight int
}


================================================
FILE: client/xclient.go
================================================
package client

import (
	"bufio"
	"context"
	"errors"
	"fmt"
	"io"
	"net"
	"net/url"
	"os"
	"reflect"
	"sort"
	"strings"
	"sync"
	"time"

	"github.com/juju/ratelimit"
	"golang.org/x/sync/singleflight"

	ex "github.com/smallnest/rpcx/errors"
	"github.com/smallnest/rpcx/log"
	"github.com/smallnest/rpcx/protocol"
	"github.com/smallnest/rpcx/share"
)

const (
	FileTransferBufferSize = 1024
)

var (
	// ErrXClientShutdown xclient is shutdown.
	ErrXClientShutdown = errors.New("xClient is shut down")
	// ErrXClientNoServer selector can't found one server.
	ErrXClientNoServer = errors.New("can not found any server")
	// ErrServerUnavailable selected server is unavailable.
	ErrServerUnavailable = errors.New("selected server is unavailable")
)

// Receipt represents the result of the service returned.
type Receipt struct {
	Address string
	Reply   interface{}
	Error   error
}

// XClient is an interface that used by client with service discovery and service governance.
// One XClient is used only for one service. You should create multiple XClient for multiple services.
type XClient interface {
	SetPlugins(plugins PluginContainer)
	GetPlugins() PluginContainer
	SetSelector(s Selector)
	ConfigGeoSelector(latitude, longitude float64)
	Auth(auth string)

	Go(ctx context.Context, serviceMethod string, args interface{}, reply interface{}, done chan *Call) (*Call, error)
	Call(ctx context.Context, serviceMethod string, args interface{}, reply interface{}) error
	Oneshot(ctx context.Context, serviceMethod string, args interface{}) error
	Broadcast(ctx context.Context, serviceMethod string, args interface{}, reply interface{}) error
	Fork(ctx context.Context, serviceMethod string, args interface{}, reply interface{}) error
	Inform(ctx context.Context, serviceMethod string, args interface{}, reply interface{}) ([]Receipt, error)
	SendRaw(ctx context.Context, r *protocol.Message) (map[string]string, []byte, error)
	SendFile(ctx context.Context, fileName string, rateInBytesPerSecond int64, meta map[string]string) error
	DownloadFile(ctx context.Context, requestFileName string, saveTo io.Writer, meta map[string]string) error
	Stream(ctx context.Context, meta map[string]string) (net.Conn, error)
	Close() error
}

// SetSelector sets customized selector by users.
func (c *xClient) SetSelector(s Selector) {
	c.mu.RLock()
	s.UpdateServer(c.servers)
	c.mu.RUnlock()

	c.selector = s
}

// KVPair contains a key and a string.
type KVPair struct {
	Key   string
	Value string
}

type xClient struct {
	failMode     FailMode
	selectMode   SelectMode
	cachedClient map[string]RPCClient
	breakers     sync.Map
	servicePath  string
	option       Option

	mu              sync.RWMutex
	servers         map[string]string
	unstableServers map[string]time.Time // 一些服务器重启,如果和它们建立链接,可能会耗费非常长的时间,这里记录袭来需要临时屏蔽
	discovery       ServiceDiscovery
	selector        Selector
	stickyRPCClient RPCClient
	stickyK         string

	slGroup singleflight.Group

	isShutdown bool

	// auth is a string for Authentication, for example, "Bearer mF_9.B5f-4.1JqM"
	auth string

	Plugins PluginContainer

	ch chan []*KVPair

	serverMessageChan chan<- *protocol.Message
}

// NewXClient creates a XClient that supports service discovery and service governance.
func NewXClient(servicePath string, failMode FailMode, selectMode SelectMode, discovery ServiceDiscovery, option Option) XClient {
	client := &xClient{
		failMode:        failMode,
		selectMode:      selectMode,
		discovery:       discovery,
		servicePath:     servicePath,
		cachedClient:    make(map[string]RPCClient),
		unstableServers: make(map[string]time.Time),
		option:          option,
	}

	pairs := discovery.GetServices()
	sort.Slice(pairs, func(i, j int) bool {
		return strings.Compare(pairs[i].Key, pairs[j].Key) <= 0
	})
	servers := make(map[string]string, len(pairs))
	for _, p := range pairs {
		servers[p.Key] = p.Value
	}
	filterByStateAndGroup(client.option.Group, servers)

	client.servers = servers
	if selectMode != Closest && selectMode != SelectByUser {
		client.selector = newSelector(selectMode, servers)
	}

	client.Plugins = &pluginContainer{}

	ch := client.discovery.WatchService()
	if ch != nil {
		client.ch = ch
		go client.watch(ch)
	}

	return client
}

// NewBidirectionalXClient creates a new xclient that can receive notifications from servers.
func NewBidirectionalXClient(servicePath string, failMode FailMode, selectMode SelectMode, discovery ServiceDiscovery, option Option, serverMessageChan chan<- *protocol.Message) XClient {
	client := &xClient{
		failMode:          failMode,
		selectMode:        selectMode,
		discovery:         discovery,
		servicePath:       servicePath,
		cachedClient:      make(map[string]RPCClient),
		option:            option,
		unstableServers:   make(map[string]time.Time),
		serverMessageChan: serverMessageChan,
	}

	pairs := discovery.GetServices()
	sort.Slice(pairs, func(i, j int) bool {
		return strings.Compare(pairs[i].Key, pairs[j].Key) <= 0
	})
	servers := make(map[string]string, len(pairs))
	for _, p := range pairs {
		servers[p.Key] = p.Value
	}
	filterByStateAndGroup(client.option.Group, servers)
	client.servers = servers
	if selectMode != Closest && selectMode != SelectByUser {
		client.selector = newSelector(selectMode, servers)
	}

	client.Plugins = &pluginContainer{}

	ch := client.discovery.WatchService()
	if ch != nil {
		client.ch = ch
		go client.watch(ch)
	}

	return client
}

// SetPlugins sets client's plugins.
func (c *xClient) SetPlugins(plugins PluginContainer) {
	c.Plugins = plugins
}

func (c *xClient) GetPlugins() PluginContainer {
	return c.Plugins
}

// ConfigGeoSelector sets location of client's latitude and longitude,
// and use newGeoSelector.
func (c *xClient) ConfigGeoSelector(latitude, longitude float64) {
	c.selector = newGeoSelector(c.servers, latitude, longitude)
	c.selectMode = Closest
}

// Auth sets s token for Authentication.
func (c *xClient) Auth(auth string) {
	c.auth = auth
}

// watch changes of service and update cached clients.
func (c *xClient) watch(ch chan []*KVPair) {
	for pairs := range ch {
		sort.Slice(pairs, func(i, j int) bool {
			return strings.Compare(pairs[i].Key, pairs[j].Key) <= 0
		})
		servers := make(map[string]string, len(pairs))
		for _, p := range pairs {
			servers[p.Key] = p.Value
		}
		c.mu.Lock()
		filterByStateAndGroup(c.option.Group, servers)
		c.servers = servers

		if c.selector != nil {
			c.selector.UpdateServer(servers)
		}

		c.mu.Unlock()
	}
}

func filterByStateAndGroup(group string, servers map[string]string) {
	for k, v := range servers {
		if values, err := url.ParseQuery(v); err == nil {
			if state := values.Get("state"); state == "inactive" {
				delete(servers, k)
			}
			groups := values["group"] // Directly access the map to get all values associated with "group" as a slice
			if group != "" {
				found := false
				for _, g := range groups {
					if group == g {
						found = true
						break // A matching group is found, stop the search
					}
				}
				if !found {
					delete(servers, k) // If no matching group is found, delete the corresponding server from the map
				}
			}
		}
	}
}

// selects a client from candidates base on c.selectMode
func (c *xClient) selectClient(ctx context.Context, servicePath, serviceMethod string, args interface{}) (string, RPCClient, error) {
	c.mu.Lock()

	if c.option.Sticky && c.stickyRPCClient != nil {
		if c.stickyRPCClient.IsClosing() || c.stickyRPCClient.IsShutdown() {
			c.stickyRPCClient = nil
		}
	}

	if c.option.Sticky && c.stickyRPCClient != nil {
		c.mu.Unlock()
		return c.stickyK, c.stickyRPCClient, nil
	}

	fn := c.selector.Select
	if c.Plugins != nil {
		fn = c.Plugins.DoWrapSelect(fn)
	}
	k := fn(ctx, servicePath, serviceMethod, args)
	c.mu.Unlock()

	if k == "" {
		return "", nil, ErrXClientNoServer
	}

	client, err := c.getCachedClient(k, servicePath, serviceMethod, args)

	if c.option.Sticky && client != nil {
		c.mu.Lock()
		safeCloseClient(c.stickyRPCClient)

		c.stickyK = k
		c.stickyRPCClient = client
		c.mu.Unlock()
	}

	return k, client, err
}

func safeCloseClient(client RPCClient) {
	if client == nil {
		return
	}

	defer func() {
		_ = recover()
	}()

	client.Close()
}

func (c *xClient) getCachedClient(k string, servicePath, serviceMethod string, _ interface{}) (client RPCClient, err error) {
	var needCallPlugin bool
	defer func() {
		if needCallPlugin {
			_, err = c.Plugins.DoClientConnected(client.GetConn())
		}
	}()

	if c.isShutdown {
		return nil, errors.New("this xclient is closed")
	}

	// if this client is broken
	breaker, ok := c.breakers.Load(k)
	if ok && !breaker.(Breaker).Ready() {
		return nil, ErrBreakerOpen
	}

	c.mu.Lock()
	defer c.mu.Unlock()

	client = c.findCachedClient(k, servicePath, serviceMethod)
	if client != nil {
		if !client.IsClosing() && !client.IsShutdown() {
			return client, nil
		}
		c.deleteCachedClient(client, k, servicePath, serviceMethod)
	}

	client = c.findCachedClient(k, servicePath, serviceMethod)

	if client == nil || client.IsShutdown() {
		generatedClient, err, _ := c.slGroup.Do(k, func() (interface{}, error) {
			return c.generateClient(k, servicePath, serviceMethod)
		})

		if err != nil {
			c.slGroup.Forget(k)
			return nil, err
		}

		client = generatedClient.(RPCClient)
		if c.Plugins != nil {
			needCallPlugin = true
		}

		client.RegisterServerMessageChan(c.serverMessageChan)

		c.setCachedClient(client, k, servicePath, serviceMethod)

		// forget k only when client is cached
		c.slGroup.Forget(k)
	}

	return client, nil
}

func (c *xClient) setCachedClient(client RPCClient, k, servicePath, serviceMethod string) {
	network, _ := splitNetworkAndAddress(k)
	if builder, ok := getCacheClientBuilder(network); ok {
		builder.SetCachedClient(client, k, servicePath, serviceMethod)
		return
	}

	c.cachedClient[k] = client
}

func (c *xClient) findCachedClient(k, servicePath, serviceMethod string) RPCClient {
	network, _ := splitNetworkAndAddress(k)
	if builder, ok := getCacheClientBuilder(network); ok {
		return builder.FindCachedClient(k, servicePath, serviceMethod)
	}

	return c.cachedClient[k]
}

func (c *xClient) deleteCachedClient(client RPCClient, k, servicePath, serviceMethod string) {
	network, _ := splitNetworkAndAddress(k)
	if builder, ok := getCacheClientBuilder(network); ok && client != nil {
		builder.DeleteCachedClient(client, k, servicePath, serviceMethod)
		client.Close()
		return
	}

	delete(c.cachedClient, k)
	if client != nil {
		client.Close()
	}
}

func (c *xClient) removeClient(k, servicePath, serviceMethod string, client RPCClient) {
	c.mu.Lock()
	if c.option.Sticky {
		c.stickyK = ""
		c.stickyRPCClient = nil
	}

	cl := c.findCachedClient(k, servicePath, serviceMethod)
	if cl == client {
		c.deleteCachedClient(client, k, servicePath, serviceMethod)
	}
	c.mu.Unlock()

	if client != nil {
		client.UnregisterServerMessageChan()
		client.Close()
	}
}

func (c *xClient) generateClient(k, servicePath, serviceMethod string) (client RPCClient, err error) {
	network, addr := splitNetworkAndAddress(k)
	if builder, ok := getCacheClientBuilder(network); ok && builder != nil {
		return builder.GenerateClient(k, servicePath, serviceMethod)
	}

	client = &Client{
		option:  c.option,
		Plugins: c.Plugins,
	}

	var breaker interface{}
	if c.option.GenBreaker != nil {
		breaker, _ = c.breakers.LoadOrStore(k, c.option.GenBreaker())
	}

	err = client.Connect(network, addr)
	if err != nil {
		if breaker != nil {
			breaker.(Breaker).Fail()
		}
		return nil, err
	}
	return client, err
}

func (c *xClient) getCachedClientWithoutLock(k, servicePath, serviceMethod string) (RPCClient, bool, error) {
	var needCallPlugin bool
	client := c.findCachedClient(k, servicePath, serviceMethod)
	if client != nil {
		if !client.IsClosing() && !client.IsShutdown() {
			return client, needCallPlugin, nil
		}
		c.deleteCachedClient(client, k, servicePath, serviceMethod)

		// double check
		client = c.findCachedClient(k, servicePath, serviceMethod)
	}

	if client == nil || client.IsShutdown() {
		generatedClient, err, _ := c.slGroup.Do(k, func() (interface{}, error) {
			return c.generateClient(k, servicePath, serviceMethod)
		})

		if err != nil {
			c.slGroup.Forget(k)
			return nil, needCallPlugin, err
		}

		client = generatedClient.(RPCClient)
		if c.Plugins != nil {
			needCallPlugin = true
		}

		client.RegisterServerMessageChan(c.serverMessageChan)

		c.setCachedClient(client, k, servicePath, serviceMethod)
		c.slGroup.Forget(k)
	}

	return client, needCallPlugin, nil
}

func splitNetworkAndAddress(server string) (string, string) {
	ss := strings.SplitN(server, "@", 2)
	if len(ss) == 1 {
		return "tcp", server
	}

	return ss[0], ss[1]
}

func setServerTimeout(ctx context.Context) context.Context {
	if deadline, ok := ctx.Deadline(); ok {
		metadata := ctx.Value(share.ReqMetaDataKey)
		if metadata == nil {
			metadata = map[string]string{}
			ctx = context.WithValue(ctx, share.ReqMetaDataKey, metadata)
		}
		m := metadata.(map[string]string)
		m[share.ServerTimeout] = fmt.Sprintf("%d", time.Until(deadline).Milliseconds())
	}

	return ctx
}

// Go invokes the function asynchronously. It returns the Call structure representing the invocation. The done channel will signal when the call is complete by returning the same Call object. If done is nil, Go will allocate a new channel. If non-nil, done must be buffered or Go will deliberately crash.
// It does not use FailMode.
func (c *xClient) Go(ctx context.Context, serviceMethod string, args interface{}, reply interface{}, done chan *Call) (*Call, error) {
	if c.isShutdown {
		return nil, ErrXClientShutdown
	}

	if c.auth != "" {
		metadata := ctx.Value(share.ReqMetaDataKey)
		if metadata == nil {
			metadata = map[string]string{}
			ctx = context.WithValue(ctx, share.ReqMetaDataKey, metadata)
		}
		m := metadata.(map[string]string)
		m[share.AuthKey] = c.auth
	}

	ctx = setServerTimeout(ctx)

	if share.Trace {
		log.Debugf("select a client for %s.%s, args: %+v in case of xclient Go", c.servicePath, serviceMethod, args)
	}
	_, client, err := c.selectClient(ctx, c.servicePath, serviceMethod, args)
	if err != nil {
		return nil, err
	}
	if share.Trace {
		log.Debugf("selected a client %s for %s.%s, args: %+v in case of xclient Go", client.RemoteAddr(), c.servicePath, serviceMethod, args)
	}

	if done == nil {
		done = make(chan *Call, 10)
	}

	return client.Go(ctx, c.servicePath, serviceMethod, args, reply, done), nil
}

// Call invokes the named function, waits for it to complete, and returns its error status.
// It handles errors base on FailMode.
func (c *xClient) Call(ctx context.Context, serviceMethod string, args interface{}, reply interface{}) error {
	if c.isShutdown {
		return ErrXClientShutdown
	}

	if c.auth != "" {
		metadata := ctx.Value(share.ReqMetaDataKey)
		if metadata == nil {
			metadata = map[string]string{}
			ctx = context.WithValue(ctx, share.ReqMetaDataKey, metadata)
		}
		m := metadata.(map[string]string)
		m[share.AuthKey] = c.auth
	}
	ctx = setServerTimeout(ctx)

	if share.Trace {
		log.Debugf("select a client for %s.%s, failMode: %v, args: %+v in case of xclient Call", c.servicePath, serviceMethod, c.failMode, args)
	}

	var err error
	k, client, err := c.selectClient(ctx, c.servicePath, serviceMethod, args)
	if err != nil {
		if c.failMode == Failfast || contextCanceled(err) {
			return err
		}
	}

	if share.Trace {
		if client != nil {
			log.Debugf("selected a client %s for %s.%s, failMode: %v, args: %+v in case of xclient Call", client.RemoteAddr(), c.servicePath, serviceMethod, c.failMode, args)
		} else {
			log.Debugf("selected a client %s for %s.%s, failMode: %v, args: %+v in case of xclient Call", "nil", c.servicePath, serviceMethod, c.failMode, args)
		}
	}

	var e error
	switch c.failMode {
	case Failtry:
		retries := c.option.Retries
		retryInterval := c.option.RetryInterval
		for retries >= 0 {
			retries--

			if client != nil {
				err = c.wrapCall(ctx, client, serviceMethod, args, reply)
				if err == nil {
					return nil
				}
				if contextCanceled(err) {
					return err
				}
				if e, ok := err.(ServiceError); ok && e.IsServiceError() {
					return err
				}
			}

			if uncoverError(err) {
				c.removeClient(k, c.servicePath, serviceMethod, client)
			}
			client, e = c.getCachedClient(k, c.servicePath, serviceMethod, args)
			time.Sleep(retryInterval)
		}
		if err == nil {
			err = e
		}
		return err
	case Failover:
		retries := c.option.Retries
		retryInterval := c.option.RetryInterval
		for retries >= 0 {
			retries--

			if client != nil {
				err = c.wrapCall(ctx, client, serviceMethod, args, reply)
				if err == nil {
					return nil
				}
				if contextCanceled(err) {
					return err
				}
				if e, ok := err.(ServiceError); ok && e.IsServiceError() {
					return err
				}
			}

			if uncoverError(err) {
				c.removeClient(k, c.servicePath, serviceMethod, client)
			}
			time.Sleep(retryInterval)
			// select another server
			k, client, e = c.selectClient(ctx, c.servicePath, serviceMethod, args)
		}

		if err == nil {
			err = e
		}
		return err
	case Failbackup:
		ctx, cancelFn := context.WithCancel(ctx)
		defer cancelFn()
		call1 := make(chan *Call, 10)
		call2 := make(chan *Call, 10)

		var reply1, reply2 interface{}

		if reply != nil {
			reply1 = reflect.New(reflect.ValueOf(reply).Elem().Type()).Interface()
			reply2 = reflect.New(reflect.ValueOf(reply).Elem().Type()).Interface()
		}

		_, err1 := c.Go(ctx, serviceMethod, args, reply1, call1)

		t := time.NewTimer(c.option.BackupLatency)
		select {
		case <-ctx.Done(): // cancel by context
			err = ctx.Err()
			return err
		case call := <-call1:
			err = call.Error
			if err == nil && reply != nil {
				reflect.ValueOf(reply).Elem().Set(reflect.ValueOf(reply1).Elem())
			}
			return err
		case <-t.C:

		}
		_, err2 := c.Go(ctx, serviceMethod, args, reply2, call2)
		if err2 != nil {
			if uncoverError(err2) {
				c.removeClient(k, c.servicePath, serviceMethod, client)
			}
			err = err1
			return err
		}

		select {
		case <-ctx.Done(): // cancel by context
			err = ctx.Err()
		case call := <-call1:
			err = call.Error
			if err == nil && reply != nil && reply1 != nil {
				reflect.ValueOf(reply).Elem().Set(reflect.ValueOf(reply1).Elem())
			}
		case call := <-call2:
			err = call.Error
			if err == nil && reply != nil && reply2 != nil {
				reflect.ValueOf(reply).Elem().Set(reflect.ValueOf(reply2).Elem())
			}
		}

		return err
	default: // Failfast
		err = c.wrapCall(ctx, client, serviceMethod, args, reply)
		if err != nil {
			if uncoverError(err) {
				c.removeClient(k, c.servicePath, serviceMethod, client)
			}
		}

		return err
	}
}

// Oneshot invokes the named function, ** DOEST NOT ** wait for it to complete, and returns immediately.
func (c *xClient) Oneshot(ctx context.Context, serviceMethod string, args interface{}) error {
	if c.isShutdown {
		return ErrXClientShutdown
	}

	if c.auth != "" {
		metadata := ctx.Value(share.ReqMetaDataKey)
		if metadata == nil {
			metadata = map[string]string{}
			ctx = context.WithValue(ctx, share.ReqMetaDataKey, metadata)
		}
		m := metadata.(map[string]string)
		m[share.AuthKey] = c.auth
	}

	ctx = setServerTimeout(ctx)

	if share.Trace {
		log.Debugf("select a client for %s.%s, args: %+v in case of xclient Go", c.servicePath, serviceMethod, args)
	}
	_, client, err := c.selectClient(ctx, c.servicePath, serviceMethod, args)
	if err != nil {
		return err
	}
	if share.Trace {
		log.Debugf("selected a client %s for %s.%s, args: %+v in case of xclient Go", client.RemoteAddr(), c.servicePath, serviceMethod, args)
	}

	client.Go(ctx, c.servicePath, serviceMethod, args, nil, nil)

	return nil
}

func uncoverError(err error) bool {
	if e, ok := err.(ServiceError); ok && e.IsServiceError() {
		return false
	}

	if err == context.DeadlineExceeded {
		return false
	}

	if err == context.Canceled {
		return false
	}

	return true
}

func contextCanceled(err error) bool {
	if err == context.DeadlineExceeded {
		return true
	}

	if err == context.Canceled {
		return true
	}

	return false
}

func (c *xClient) SendRaw(ctx context.Context, r *protocol.Message) (map[string]string, []byte, error) {
	if c.isShutdown {
		return nil, nil, ErrXClientShutdown
	}

	if c.auth != "" {
		metadata := ctx.Value(share.ReqMetaDataKey)
		if metadata == nil {
			metadata = map[string]string{}
			ctx = context.WithValue(ctx, share.ReqMetaDataKey, metadata)
		}
		m := metadata.(map[string]string)
		m[share.AuthKey] = c.auth
	}

	ctx = setServerTimeout(ctx)

	if share.Trace {
		log.Debugf("select a client for %s.%s, failMode: %v, args: %+v in case of xclient SendRaw", r.ServicePath, r.ServiceMethod, c.failMode, r.Payload)
	}

	var err error
	k, client, err := c.selectClient(ctx, r.ServicePath, r.ServiceMethod, r.Payload)
	if err != nil {
		if c.failMode == Failfast {
			return nil, nil, err
		}
		if contextCanceled(err) {
			return nil, nil, err
		}
		if e, ok := err.(ServiceError); ok && e.IsServiceError() {
			return nil, nil, err
		}
	}

	if share.Trace {
		log.Debugf("selected a client %s for %s.%s, failMode: %v, args: %+v in case of xclient Call", client.RemoteAddr(), r.ServicePath, r.ServiceMethod, c.failMode, r.Payload)
	}

	var e error
	switch c.failMode {
	case Failtry:
		retries := c.option.Retries
		for retries >= 0 {
			retries--
			if client != nil {
				m, payload, err := c.wrapSendRaw(ctx, client, r)
				if err == nil {
					return m, payload, nil
				}
				if contextCanceled(err) {
					return nil, nil, err
				}
				if _, ok := err.(ServiceError); ok {
					return nil, nil, err
				}
			}

			if uncoverError(err) {
				c.removeClient(k, r.ServicePath, r.ServiceMethod, client)
			}
			client, e = c.getCachedClient(k, r.ServicePath, r.ServiceMethod, r.Payload)
		}

		if err == nil {
			err = e
		}
		return nil, nil, err
	case Failover:
		retries := c.option.Retries
		for retries >= 0 {
			retries--
			if client != nil {
				m, payload, err := c.wrapSendRaw(ctx, client, r)
				if err == nil {
					return m, payload, nil
				}
				if contextCanceled(err) {
					return nil, nil, err
				}
				if e, ok := err.(ServiceError); ok && e.IsServiceError() {
					return nil, nil, err
				}
			}

			if uncoverError(err) {
				c.removeClient(k, r.ServicePath, r.ServiceMethod, client)
			}
			// select another server
			k, client, e = c.selectClient(ctx, r.ServicePath, r.ServiceMethod, r.Payload)
		}

		if err == nil {
			err = e
		}
		return nil, nil, err

	default: // Failfast
		m, payload, err := c.wrapSendRaw(ctx, client, r)
		if err != nil {
			if uncoverError(err) {
				c.removeClient(k, r.ServicePath, r.ServiceMethod, client)
			}
		}

		return m, payload, nil
	}
}

func (c *xClient) wrapCall(ctx context.Context, client RPCClient, serviceMethod string, args interface{}, reply interface{}) error {
	if client == nil {
		return ErrServerUnavailable
	}

	if share.Trace {
		log.Debugf("call a client for %s.%s, args: %+v in case of xclient wrapCall", c.servicePath, serviceMethod, args)
	}

	if _, ok := ctx.(*share.Context); !ok {
		ctx = share.NewContext(ctx)
	}

	err := c.Plugins.DoPreCall(ctx, c.servicePath, serviceMethod, args)
	if err != nil {
		return err
	}
	err = client.Call(ctx, c.servicePath, serviceMethod, args, reply)
	c.Plugins.DoPostCall(ctx, c.servicePath, serviceMethod, args, reply, err)

	if share.Trace {
		log.Debugf("called a client for %s.%s, args: %+v, err: %v in case of xclient wrapCall", c.servicePath, serviceMethod, args, err)
	}

	return err
}

// wrapSendRaw wrap SendRaw to support client plugins
func (c *xClient) wrapSendRaw(ctx context.Context, client RPCClient, r *protocol.Message) (map[string]string, []byte, error) {
	if client == nil {
		return nil, nil, ErrServerUnavailable
	}

	if share.Trace {
		log.Debugf("call a client for %s.%s, args: %+v in case of xclient wrapSendRaw", c.servicePath, r.ServiceMethod, r.Payload)
	}

	ctx = share.NewContext(ctx)
	err := c.Plugins.DoPreCall(ctx, c.servicePath, r.ServiceMethod, r.Payload)
	if err != nil {
		return nil, nil, err
	}

	m, payload, err := client.SendRaw(ctx, r)
	c.Plugins.DoPostCall(ctx, c.servicePath, r.ServiceMethod, r.Payload, nil, err)

	if share.Trace {
		log.Debugf("called a client for %s.%s, args: %+v, err: %v in case of xclient wrapSendRaw", c.servicePath, r.ServiceMethod, r.Payload, err)
	}

	return m, payload, err
}

// Broadcast sends requests to all servers and Success only when all servers return OK.
// FailMode and SelectMode are meanless for this method.
// Please set timeout to avoid hanging.
func (c *xClient) Broadcast(ctx context.Context, serviceMethod string, args interface{}, reply interface{}) error {
	if c.isShutdown {
		return ErrXClientShutdown
	}

	if c.auth != "" {
		metadata := ctx.Value(share.ReqMetaDataKey)
		if metadata == nil {
			metadata = map[string]string{}
			ctx = context.WithValue(ctx, share.ReqMetaDataKey, metadata)
		}
		m := metadata.(map[string]string)
		m[share.AuthKey] = c.auth
	}

	var replyOnce sync.Once

	ctx = setServerTimeout(ctx)
	// add timeout after set server timeout, only prevent client hanging
	ctx, cancel := context.WithTimeout(ctx, time.Minute)
	defer cancel()
	callPlugins := make([]RPCClient, 0, len(c.servers))
	clients := make(map[string]RPCClient)
	c.mu.Lock()
	for k := range c.servers {
		client, needCallPlugin, err := c.getCachedClientWithoutLock(k, c.servicePath, serviceMethod)
		if err != nil {
			continue
		}
		clients[k] = client
		if needCallPlugin {
			callPlugins = append(callPlugins, client)
		}
	}
	c.mu.Unlock()

	for i := range callPlugins {
		if c.Plugins != nil {
			c.Plugins.DoClientConnected(callPlugins[i].GetConn())
		}
	}

	if len(clients) == 0 {
		return ErrXClientNoServer
	}

	err := &ex.MultiError{}
	l := len(clients)
	done := make(chan bool, l)
	for k, client := range clients {
		k := k
		client := client
		go func() {
			var clonedReply interface{}
			if reply != nil {
				clonedReply = reflect.New(reflect.ValueOf(reply).Elem().Type()).Interface()
			}

			e := c.wrapCall(ctx, client, serviceMethod, args, clonedReply)
			defer func() {
				done <- (e == nil)
			}()
			if e != nil {
				if uncoverError(e) {
					c.removeClient(k, c.servicePath, serviceMethod, client)
				}
				err.Append(e)
			}

			if e == nil && reply != nil && clonedReply != nil {
				replyOnce.Do(func() {
					reflect.ValueOf(reply).Elem().Set(reflect.ValueOf(clonedReply).Elem())
				})
			}
		}()
	}

check:
	for {
		select {
		case result := <-done:
			l--
			if l == 0 || !result { // all returns or some one returns an error
				break check
			}
		}
	}

	select {
	case <-ctx.Done():
		err.Append(errors.New(("timeout")))
	default:
	}

	return err.ErrorOrNil()
}

// Fork sends requests to all servers and Success once one server returns OK.
// FailMode and SelectMode are meanless for this method.
func (c *xClient) Fork(ctx context.Context, serviceMethod string, args interface{}, reply interface{}) error {
	if c.isShutdown {
		return ErrXClientShutdown
	}

	if c.auth != "" {
		metadata := ctx.Value(share.ReqMetaDataKey)
		if metadata == nil {
			metadata = map[string]string{}
			ctx = context.WithValue(ctx, share.ReqMetaDataKey, metadata)
		}
		m := metadata.(map[string]string)
		m[share.AuthKey] = c.auth
	}

	ctx = setServerTimeout(ctx)

	// add timeout after set server timeout, only prevent client hanging
	ctx, cancel := context.WithTimeout(ctx, time.Minute)
	defer cancel()
	callPlugins := make([]RPCClient, 0, len(c.servers))
	clients := make(map[string]RPCClient)
	c.mu.Lock()
	for k := range c.servers {
		client, needCallPlugin, err := c.getCachedClientWithoutLock(k, c.servicePath, serviceMethod)
		if err != nil {
			continue
		}
		clients[k] = client
		if needCallPlugin {
			callPlugins = append(callPlugins, client)
		}
	}
	c.mu.Unlock()

	for i := range callPlugins {
		if c.Plugins != nil {
			c.Plugins.DoClientConnected(callPlugins[i].GetConn())
		}
	}

	if len(clients) == 0 {
		return ErrXClientNoServer
	}

	var replyOnce sync.Once

	err := &ex.MultiError{}
	l := len(clients)
	done := make(chan bool, l)
	for k, client := range clients {
		k := k
		client := client
		go func() {
			var clonedReply interface{}
			if reply != nil {
				clonedReply = reflect.New(reflect.ValueOf(reply).Elem().Type()).Interface()
			}

			e := c.wrapCall(ctx, client, serviceMethod, args, clonedReply)
			if e == nil && reply != nil && clonedReply != nil {
				replyOnce.Do(func() {
					reflect.ValueOf(reply).Elem().Set(reflect.ValueOf(clonedReply).Elem())
				})
			}
			defer func() {
				done <- (e == nil)
			}()
			if e != nil {
				if uncoverError(e) {
					c.removeClient(k, c.servicePath, serviceMethod, client)
				}
				err.Append(e)
			}
		}()
	}

check:
	for {
		select {
		case result := <-done:
			l--
			if result {
				return nil
			}
			if l == 0 { // all returns or some one returns an error
				break check
			}
		}
	}

	select {
	case <-ctx.Done():
		err.Append(errors.New(("timeout")))
	default:
	}

	return err.ErrorOrNil()
}

// Inform sends requests to all servers and returns all results from services.
// FailMode and SelectMode are meanless for this method.
// Please set timeout to avoid hanging.
func (c *xClient) Inform(ctx context.Context, serviceMethod string, args interface{}, reply interface{}) ([]Receipt, error) {
	if c.isShutdown {
		return nil, ErrXClientShutdown
	}

	if c.auth != "" {
		metadata := ctx.Value(share.ReqMetaDataKey)
		if metadata == nil {
			metadata = map[string]string{}
			ctx = context.WithValue(ctx, share.ReqMetaDataKey, metadata)
		}
		m := metadata.(map[string]string)
		m[share.AuthKey] = c.auth
	}

	ctx = setServerTimeout(ctx)

	// add timeout after set server timeout, only prevent client hanging
	ctx, cancel := context.WithTimeout(ctx, time.Minute)
	defer cancel()
	callPlugins := make([]RPCClient, 0, len(c.servers))
	clients := make(map[string]RPCClient)
	c.mu.Lock()
	for k := range c.servers {
		client, needCallPlugin, err := c.getCachedClientWithoutLock(k, c.servicePath, serviceMethod)
		if err != nil {
			continue
		}
		clients[k] = client
		if needCallPlugin {
			callPlugins = append(callPlugins, client)
		}
	}
	c.mu.Unlock()

	for i := range callPlugins {
		if c.Plugins != nil {
			c.Plugins.DoClientConnected(callPlugins[i].GetConn())
		}
	}

	if len(clients) == 0 {
		return nil, ErrXClientNoServer
	}

	var receiptsLock sync.Mutex
	var receipts []Receipt

	var replyOnce sync.Once

	err := &ex.MultiError{}
	l := len(clients)
	done := make(chan bool, l)
	for k, client := range clients {
		k := k
		client := client
		go func() {
			var clonedReply interface{}
			if reply != nil {
				clonedReply = reflect.New(reflect.ValueOf(reply).Elem().Type()).Interface()
			}

			e := c.wrapCall(ctx, client, serviceMethod, args, clonedReply)
			defer func() {
				done <- (e == nil)
			}()
			if e != nil {
				if uncoverError(e) {
					c.removeClient(k, c.servicePath, serviceMethod, client)
				}
				err.Append(e)
			}
			if e == nil && reply != nil && clonedReply != nil {
				replyOnce.Do(func() {
					reflect.ValueOf(reply).Elem().Set(reflect.ValueOf(clonedReply).Elem())
				})
			}

			addr := k
			ss := strings.SplitN(k, "@", 2)
			if len(ss) == 2 {
				addr = ss[1]
			}
			receiptsLock.Lock()

			receipts = append(receipts, Receipt{
				Address: addr,
				Reply:   clonedReply,
				Error:   err,
			})
			receiptsLock.Unlock()
		}()
	}

check:
	for {
		select {
		case <-done:
			l--
			if l == 0 { // all returns or some one returns an error
				break check
			}
		}
	}

	select {
	case <-ctx.Done():
		err.Append(errors.New(("timeout")))
	default:
	}

	return receipts, err.ErrorOrNil()
}

// SendFile sends a local file to the server.
// fileName is the path of local file.
// rateInBytesPerSecond can limit bandwidth of sending,  0 means does not limit the bandwidth, unit is bytes / second.
func (c *xClient) SendFile(ctx context.Context, fileName string, rateInBytesPerSecond int64, meta map[string]string) error {
	file, err := os.Open(fileName)
	if err != nil {
		return err
	}

	defer file.Close()

	fi, err := os.Stat(fileName)
	if err != nil {
		return err
	}

	args := share.FileTransferArgs{
		FileName: fi.Name(),
		FileSize: fi.Size(),
		Meta:     meta,
	}

	ctx = setServerTimeout(ctx)

	reply := &share.FileTransferReply{}
	err = c.Call(ctx, "TransferFile", args, reply)
	if err != nil {
		return err
	}

	conn, err := net.DialTimeout("tcp", reply.Addr, c.option.ConnectTimeout)
	if err != nil {
		return err
	}

	defer conn.Close()

	_, err = conn.Write(reply.Token)
	if err != nil {
		return err
	}

	var tb *ratelimit.Bucket

	if rateInBytesPerSecond > 0 {
		tb = ratelimit.NewBucketWithRate(float64(rateInBytesPerSecond), rateInBytesPerSecond)
	}

	sendBuffer := make([]byte, FileTransferBufferSize)
loop:
	for {
		select {
		case <-ctx.Done():
			err = ctx.Err()
			break loop
		default:
			if tb != nil {
				tb.Wait(FileTransferBufferSize)
			}
			n, err := file.Read(sendBuffer)
			if err != nil {
				if err == io.EOF {
					return nil
				} else {
					return err
				}
			}
			if n == 0 {
				break loop
			}
			_, err = conn.Write(sendBuffer[:n])
			if err != nil {
				if err == io.EOF {
					return nil
				} else {
					return err
				}
			}
		}
	}

	return nil
}

func (c *xClient) DownloadFile(ctx context.Context, requestFileName string, saveTo io.Writer, meta map[string]string) error {
	ctx = setServerTimeout(ctx)

	args := share.DownloadFileArgs{
		FileName: requestFileName,
		Meta:     meta,
	}

	reply := &share.FileTransferReply{}
	err := c.Call(ctx, "DownloadFile", args, reply)
	if err != nil {
		return err
	}

	conn, err := net.DialTimeout("tcp", reply.Addr, c.option.ConnectTimeout)
	if err != nil {
		return err
	}

	defer conn.Close()

	_, err = conn.Write(reply.Token)
	if err != nil {
		return err
	}

	buf := make([]byte, FileTransferBufferSize)
	r := bufio.NewReader(conn)
loop:
	for {
		select {
		case <-ctx.Done():
			err = ctx.Err()
			break loop
		default:
			n, er := r.Read(buf)
			if n > 0 {
				_, ew := saveTo.Write(buf[0:n])
				if ew != nil {
					err = ew
					break loop
				}
			}
			if er != nil {
				if er != io.EOF {
					err = er
				}
				break loop
			}
		}
	}

	return err
}

// Close closes this client and its underlying connections to services.
func (c *xClient) Close() error {
	var errs []error
	c.mu.Lock()
	c.isShutdown = true
	for k, v := range c.cachedClient {
		e := v.Close()
		if e != nil {
			errs = append(errs, e)
		}

		delete(c.cachedClient, k)

	}
	c.mu.Unlock()

	go func() {
		defer func() {
			recover()
		}()

		c.discovery.RemoveWatcher(c.ch)
		close(c.ch)
	}()

	if len(errs) > 0 {
		return ex.NewMultiError(errs)
	}
	return nil
}

func (c *xClient) Stream(ctx context.Context, meta map[string]string) (net.Conn, error) {
	args := share.StreamServiceArgs{
		Meta: meta,
	}

	ctx = setServerTimeout(ctx)

	reply := &share.StreamServiceReply{}
	err := c.Call(ctx, "Stream", args, reply)
	if err != nil {
		return nil, err
	}

	conn, err := net.DialTimeout("tcp", reply.Addr, c.option.ConnectTimeout)
	if err != nil {
		return nil, err
	}

	_, err = conn.Write(reply.Token)
	if err != nil {
		conn.Close()
		return nil, err
	}

	return conn, nil
}


================================================
FILE: client/xclient_pool.go
================================================
package client

import (
	"sync"
	"sync/atomic"

	"github.com/smallnest/rpcx/protocol"
)

// XClientPool is a xclient pool with fixed size.
// It uses roundrobin algorithm to call its xclients.
// All xclients share the same configurations such as ServiceDiscovery and serverMessageChan.
type XClientPool struct {
	count    uint64
	index    uint64
	xclients []XClient
	mu       sync.RWMutex

	servicePath string
	failMode    FailMode
	selectMode  SelectMode
	discovery   ServiceDiscovery
	option      Option
	auth        string
	Plugins     PluginContainer

	serverMessageChan chan<- *protocol.Message
}

// NewXClientPool creates a fixed size XClient pool.
func NewXClientPool(count int, servicePath string, failMode FailMode, selectMode SelectMode, discovery ServiceDiscovery, option Option) *XClientPool {
	pool := &XClientPool{
		count:       uint64(count),
		xclients:    make([]XClient, count),
		servicePath: servicePath,
		failMode:    failMode,
		selectMode:  selectMode,
		discovery:   discovery,
		option:      option,
	}

	for i := 0; i < count; i++ {
		xclient := NewXClient(servicePath, failMode, selectMode, discovery, option)
		pool.xclients[i] = xclient
	}
	return pool
}

// NewBidirectionalXClientPool creates a BidirectionalXClient pool with fixed size.
func NewBidirectionalXClientPool(count int, servicePath string, failMode FailMode, selectMode SelectMode, discovery ServiceDiscovery, option Option, serverMessageChan chan<- *protocol.Message) *XClientPool {
	pool := &XClientPool{
		count:             uint64(count),
		xclients:          make([]XClient, count),
		servicePath:       servicePath,
		failMode:          failMode,
		selectMode:        selectMode,
		discovery:         discovery,
		option:            option,
		serverMessageChan: serverMessageChan,
	}

	for i := 0; i < count; i++ {
		xclient := NewBidirectionalXClient(servicePath, failMode, selectMode, discovery, option, serverMessageChan)
		pool.xclients[i] = xclient
	}
	return pool
}

// Auth sets s token for Authentication.
func (c *XClientPool) Auth(auth string) {
	c.auth = auth
	c.mu.RLock()
	for _, v := range c.xclients {
		v.Auth(auth)
	}
	c.mu.RUnlock()
}

// SetPlugins sets client's plugins.
func (p *XClientPool) SetPlugins(plugins PluginContainer) {
	p.Plugins = plugins
	p.mu.RLock()
	for _, v := range p.xclients {
		v.SetPlugins(plugins)
	}
	p.mu.RUnlock()
}

// GetPlugins returns client's plugins.
func (p *XClientPool) GetPlugins() PluginContainer {
	return p.Plugins
}

// Get returns a xclient.
// It does not remove this xclient from its cache so you don't need to put it back.
// Don't close this xclient because maybe other goroutines are using this xclient.
func (p *XClientPool) Get() XClient {
	i := atomic.AddUint64(&p.index, 1)
	picked := int(i % p.count)
	return p.xclients[picked]
}

// Close this pool.
// Please make sure it won't be used any more.
func (p *XClientPool) Close() {
	for _, c := range p.xclients {
		c.Close()
	}
	p.xclients = nil
}


================================================
FILE: client/xclient_pool_test.go
================================================
package client

import (
	"testing"
)

// testPlugin is a simple test plugin implementation
type testPlugin struct {
	name string
}

func TestXClientPool_SetPlugins(t *testing.T) {
	// Create a simple discovery
	pairs := []*KVPair{
		{Key: "tcp@127.0.0.1:8972", Value: ""},
	}
	discovery, err := NewMultipleServersDiscovery(pairs)
	if err != nil {
		t.Fatalf("failed to create discovery: %v", err)
	}
	defer discovery.Close()

	// Create a pool
	pool := NewXClientPool(3, "Arith", Failtry, RandomSelect, discovery, DefaultOption)
	defer pool.Close()

	// Create plugins
	plugins := NewPluginContainer()
	tp := &testPlugin{name: "test-plugin"}
	plugins.Add(tp)

	// Test SetPlugins
	pool.SetPlugins(plugins)

	// Verify plugins are set on pool
	if pool.GetPlugins() == nil {
		t.Error("plugins should not be nil after SetPlugins")
	}
	if pool.GetPlugins() != plugins {
		t.Error("pool plugins should be the same as the set plugins")
	}

	// Verify plugins are set on all xclients
	for i := 0; i < 3; i++ {
		xclient := pool.Get()
		if xclient.GetPlugins() == nil {
			t.Errorf("xclient %d plugins should not be nil", i)
		}
		if xclient.GetPlugins() != plugins {
			t.Errorf("xclient %d plugins should be the same as the set plugins", i)
		}
	}
}

func TestXClientPool_GetPlugins(t *testing.T) {
	// Create a simple discovery
	pairs := []*KVPair{
		{Key: "tcp@127.0.0.1:8972", Value: ""},
	}
	discovery, err := NewMultipleServersDiscovery(pairs)
	if err != nil {
		t.Fatalf("failed to create discovery: %v", err)
	}
	defer discovery.Close()

	// Create a pool
	pool := NewXClientPool(2, "Arith", Failtry, RandomSelect, discovery, DefaultOption)
	defer pool.Close()

	// Initially, plugins should be nil
	if pool.GetPlugins() != nil {
		t.Error("plugins should be nil initially")
	}

	// Create and set plugins
	plugins := NewPluginContainer()
	tp := &testPlugin{name: "test-plugin"}
	plugins.Add(tp)
	pool.SetPlugins(plugins)

	// Verify GetPlugins returns the correct plugins
	retrievedPlugins := pool.GetPlugins()
	if retrievedPlugins == nil {
		t.Error("plugins should not be nil after SetPlugins")
	}
	if retrievedPlugins != plugins {
		t.Error("GetPlugins should return the same plugins as SetPlugins")
	}

	// Verify plugins contain the test plugin
	allPlugins := retrievedPlugins.All()
	if len(allPlugins) != 1 {
		t.Errorf("expected 1 plugin, got %d", len(allPlugins))
	}
	if p, ok := allPlugins[0].(*testPlugin); !ok || p.name != "test-plugin" {
		t.Error("plugin should be the test plugin")
	}
}

func TestXClientPool_SetPlugins_Concurrent(t *testing.T) {
	// Create a simple discovery
	pairs := []*KVPair{
		{Key: "tcp@127.0.0.1:8972", Value: ""},
	}
	discovery, err := NewMultipleServersDiscovery(pairs)
	if err != nil {
		t.Fatalf("failed to create discovery: %v", err)
	}
	defer discovery.Close()

	// Create a pool
	pool := NewXClientPool(5, "Arith", Failtry, RandomSelect, discovery, DefaultOption)
	defer pool.Close()

	// Test concurrent SetPlugins calls
	done := make(chan bool, 10)
	for i := 0; i < 10; i++ {
		go func(id int) {
			plugins := NewPluginContainer()
			tp := &testPlugin{name: "test-plugin"}
			plugins.Add(tp)
			pool.SetPlugins(plugins)
			done <- true
		}(i)
	}

	// Wait for all goroutines to complete
	for i := 0; i < 10; i++ {
		<-done
	}

	// Verify plugins are set
	if pool.GetPlugins() == nil {
		t.Error("plugins should not be nil after concurrent SetPlugins")
	}
}



================================================
FILE: client/xclient_test.go
================================================
package client

import (
	"context"
	"errors"
	"testing"
	"time"

	"fmt"

	testutils "github.com/smallnest/rpcx/_testutils"
	"github.com/smallnest/rpcx/protocol"
	"github.com/smallnest/rpcx/server"
	"github.com/smallnest/rpcx/share"
)

func TestXClient_Thrift(t *testing.T) {
	s := server.NewServer()
	s.RegisterName("Arith", new(Arith), "")
	go s.Serve("tcp", "127.0.0.1:0")
	defer s.Close()
	time.Sleep(500 * time.Millisecond)

	addr := s.Address().String()

	opt := Option{
		Retries:        1,
		RPCPath:        share.DefaultRPCPath,
		ConnectTimeout: 10 * time.Second,
		SerializeType:  protocol.Thrift,
		CompressType:   protocol.None,
		BackupLatency:  10 * time.Millisecond,
	}

	d, err := NewPeer2PeerDiscovery("tcp@"+addr, "desc=a test service")
	if err != nil {
		t.Fatalf("failed to NewPeer2PeerDiscovery: %v", err)
	}

	xclient := NewXClient("Arith", Failtry, RandomSelect, d, opt)

	defer xclient.Close()

	args := testutils.ThriftArgs_{}
	args.A = 200
	args.B = 100

	reply := testutils.ThriftReply{}

	err = xclient.Call(context.Background(), "ThriftMul", &args, &reply)
	if err != nil {
		t.Fatalf("failed to call: %v", err)
	}

	fmt.Println(reply.C)
	if reply.C != 20000 {
		t.Fatalf("expect 20000 but got %d", reply.C)
	}
}

func TestXClient_IT(t *testing.T) {
	s := server.NewServer()
	s.RegisterName("Arith", new(Arith), "")
	go s.Serve("tcp", "127.0.0.1:0")
	defer s.Close()
	time.Sleep(500 * time.Millisecond)

	addr := s.Address().String()

	d, err := NewPeer2PeerDiscovery("tcp@"+addr, "desc=a test service")
	if err != nil {
		t.Fatalf("failed to NewPeer2PeerDiscovery: %v", err)
	}

	xclient := NewXClient("Arith", Failtry, RandomSelect, d, DefaultOption)

	defer xclient.Close()

	args := &Args{
		A: 10,
		B: 20,
	}

	reply := &Reply{}
	err = xclient.Call(context.Background(), "Mul", args, reply)
	if err != nil {
		t.Fatalf("failed to call: %v", err)
	}

	if reply.C != 200 {
		t.Fatalf("expect 200 but got %d", reply.C)
	}
}

func TestXClient_filterByStateAndGroup(t *testing.T) {
	servers := map[string]string{"a": "", "b": "state=inactive&ops=10", "c": "ops=20", "d": "group=test1&group=test&ops=20"}
	filterByStateAndGroup("test", servers)
	if _, ok := servers["b"]; ok {
		t.Error("has not remove inactive node")
	}
	if _, ok := servers["a"]; ok {
		t.Error("has not remove inactive node")
	}
	if _, ok := servers["c"]; ok {
		t.Error("has not remove inactive node")
	}
	if _, ok := servers["d"]; !ok {
		t.Error("node must be removed")
	}

	filterByStateAndGroup("test1", servers)

	if _, ok := servers["d"]; !ok {
		t.Error("node must be removed")
	}
}

func TestUncoverError(t *testing.T) {
	var e error = strErr("error")
	if uncoverError(e) {
		t.Fatalf("expect false but get true")
	}

	if uncoverError(context.DeadlineExceeded) {
		t.Fatalf("expect false but get true")
	}

	if uncoverError(context.Canceled) {
		t.Fatalf("expect false but get true")
	}

	e = errors.New("error")
	if !uncoverError(e) {
		t.Fatalf("expect true but get false")
	}
}


================================================
FILE: clientplugin/req_rate_limiting_redis.go
================================================
package clientplugin

import (
	"context"
	"time"

	"github.com/go-redis/redis_rate/v10"
	"github.com/redis/go-redis/v9"
	"github.com/smallnest/rpcx/client"
	"github.com/smallnest/rpcx/server"
)

var _ client.PreCallPlugin = (*RedisRateLimitingPlugin)(nil)

// RedisRateLimitingPlugin can limit requests per unit time
type RedisRateLimitingPlugin struct {
	addrs   []string
	limiter redis_rate.Limiter
	limit   redis_rate.Limit
}

// NewRedisRateLimitingPlugin creates a new RateLimitingPlugin
func NewRedisRateLimitingPlugin(addrs []string, rate int, burst int, period time.Duration) *RedisRateLimitingPlugin {
	limit := redis_rate.Limit{
		Rate:   rate,
		Burst:  burst,
		Period: period,
	}
	rdb := redis.NewClusterClient(&redis.ClusterOptions{
		Addrs: addrs,
	})

	limiter := redis_rate.NewLimiter(rdb)

	return &RedisRateLimitingPlugin{
		addrs:   addrs,
		limiter: *limiter,
		limit:   limit,
	}
}

// PreCall can limit request processing.
func (plugin *RedisRateLimitingPlugin) PreCall(ctx context.Context, servicePath, serviceMethod string, args interface{}) error {
	res, err := plugin.limiter.Allow(ctx, servicePath+"/"+serviceMethod, plugin.limit)
	if err != nil {
		return err
	}

	if res.Allowed > 0 {
		return nil
	}
	return server.ErrReqReachLimit
}


================================================
FILE: codec/codec.go
================================================
package codec

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"reflect"

	pb "google.golang.org/protobuf/proto"

	"github.com/apache/thrift/lib/go/thrift"
	"github.com/gogo/protobuf/proto"
	"github.com/tinylib/msgp/msgp"
	"github.com/vmihailenco/msgpack/v5"
)

// Codec defines the interface that decode/encode payload.
type Codec interface {
	Encode(i interface{}) ([]byte, error)
	Decode(data []byte, i interface{}) error
}

// ByteCodec uses raw slice pf bytes and don't encode/decode.
type ByteCodec struct{}

// Encode returns raw slice of bytes.
func (c ByteCodec) Encode(i interface{}) ([]byte, error) {
	if data, ok := i.([]byte); ok {
		return data, nil
	}
	if data, ok := i.(*[]byte); ok {
		return *data, nil
	}

	return nil, fmt.Errorf("%T is not a []byte", i)
}

// Decode returns raw slice of bytes.
func (c ByteCodec) Decode(data []byte, i interface{}) error {
	reflect.Indirect(reflect.ValueOf(i)).SetBytes(data)
	return nil
}

// JSONCodec uses json marshaler and unmarshaler.
type JSONCodec struct{}

// Encode encodes an object into slice of bytes.
func (c JSONCodec) Encode(i interface{}) ([]byte, error) {
	return json.Marshal(i)
}

// Decode decodes an object from slice of bytes.
func (c JSONCodec) Decode(data []byte, i interface{}) error {
	d := json.NewDecoder(bytes.NewBuffer(data))
	d.UseNumber()
	return d.Decode(i)
}

// PBCodec uses protobuf marshaler and unmarshaler.
type PBCodec struct{}

// Encode encodes an object into slice of bytes.
func (c PBCodec) Encode(i interface{}) ([]byte, error) {
	if m, ok := i.(proto.Marshaler); ok {
		return m.Marshal()
	}

	if m, ok := i.(pb.Message); ok {
		return pb.Marshal(m)
	}

	return nil, fmt.Errorf("%T is not a proto.Marshaler or pb.Message", i)
}

// Decode decodes an object from slice of bytes.
func (c PBCodec) Decode(data []byte, i interface{}) error {
	if m, ok := i.(proto.Unmarshaler); ok {
		return m.Unmarshal(data)
	}

	if m, ok := i.(pb.Message); ok {
		return pb.Unmarshal(data, m)
	}

	return fmt.Errorf("%T is not a proto.Unmarshaler  or pb.Message", i)
}

// MsgpackCodec uses messagepack marshaler and unmarshaler.
type MsgpackCodec struct{}

// Encode encodes an object into slice of bytes.
func (c MsgpackCodec) Encode(i interface{}) ([]byte, error) {
	if m, ok := i.(msgp.Marshaler); ok {
		return m.MarshalMsg(nil)
	}
	var buf bytes.Buffer
	enc := msgpack.NewEncoder(&buf)
	// enc.UseJSONTag(true)
	err := enc.Encode(i)
	return buf.Bytes(), err
}

// Decode decodes an object from slice of bytes.
func (c MsgpackCodec) Decode(data []byte, i interface{}) error {
	if m, ok := i.(msgp.Unmarshaler); ok {
		_, err := m.UnmarshalMsg(data)
		return err
	}
	dec := msgpack.NewDecoder(bytes.NewReader(data))
	// dec.UseJSONTag(true)
	err := dec.Decode(i)
	return err
}

type ThriftCodec struct{}

func (c ThriftCodec) Encode(i interface{}) ([]byte, error) {
	b := thrift.NewTMemoryBufferLen(1024)
	p := thrift.NewTBinaryProtocolFactoryConf(&thrift.TConfiguration{}).
		GetProtocol(b)
	t := &thrift.TSerializer{
		Transport: b,
		Protocol:  p,
	}
	t.Transport.Close()
	if msg, ok := i.(thrift.TStruct); ok {
		return t.Write(context.Background(), msg)
	}
	return nil, errors.New("type assertion failed")
}

func (c ThriftCodec) Decode(data []byte, i interface{}) error {
	t := thrift.NewTMemoryBufferLen(1024)
	p := thrift.NewTBinaryProtocolFactoryConf(&thrift.TConfiguration{}).
		GetProtocol(t)
	d := &thrift.TDeserializer{
		Transport: t,
		Protocol:  p,
	}
	d.Transport.Close()
	return d.Read(context.Background(), i.(thrift.TStruct), data)
}


================================================
FILE: codec/codec_test.go
================================================
package codec

import (
	"testing"

	"github.com/smallnest/rpcx/codec/testdata"
)

type ColorGroup struct {
	Id     int      `json:"id" xml:"id,attr" msg:"id"`
	Name   string   `json:"name" xml:"name" msg:"name"`
	Colors []string `json:"colors" xml:"colors" msg:"colors"`
}

var group = ColorGroup{
	Id:     1,
	Name:   "Reds",
	Colors: []string{"Crimson", "Red", "Ruby", "Maroon"},
}

func BenchmarkJSONCodec_Encode(b *testing.B) {
	var raw = make([]byte, 0, 1024)
	serializer := JSONCodec{}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		raw, _ = serializer.Encode(group)
	}
	b.ReportMetric(float64(len(raw)), "bytes")
}

func BenchmarkPBCodec_Encode(b *testing.B) {
	var raw = make([]byte, 0, 1024)
	serializer := PBCodec{}
	group := testdata.ProtoColorGroup{
		Id:     1,
		Name:   "Reds",
		Colors: []string{"Crimson", "Red", "Ruby", "Maroon"},
	}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		raw, _ = serializer.Encode(&group)
	}
	b.ReportMetric(float64(len(raw)), "bytes")
}

func BenchmarkMsgpackCodec_Encode(b *testing.B) {
	var raw = make([]byte, 0, 1024)
	serializer := MsgpackCodec{}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		raw, _ = serializer.Encode(group)
	}
	b.ReportMetric(float64(len(raw)), "bytes")
}

func BenchmarkThriftCodec_Encode(b *testing.B) {
	var bb = make([]byte, 0, 1024)
	serializer := ThriftCodec{}
	thriftColorGroup := testdata.ThriftColorGroup{
		ID:     1,
		Name:   "Reds",
		Colors: []string{"Crimson", "Red", "Ruby", "Maroon"},
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		bb, _ = serializer.Encode(&thriftColorGroup)
	}

	b.ReportMetric(float64(len(bb)), "bytes")
}

func BenchmarkJSONCodec_Decode(b *testing.B) {
	serializer := JSONCodec{}
	bytes, _ := serializer.Encode(group)
	result := ColorGroup{}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = serializer.Decode(bytes, &result)
	}
}

func BenchmarkPBCodec_Decode(b *testing.B) {
	serializer := PBCodec{}
	bytes, _ := serializer.Encode(group)
	result := ColorGroup{}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = serializer.Decode(bytes, &result)
	}
}

func BenchmarkMsgpackCodec_Decode(b *testing.B) {
	serializer := MsgpackCodec{}
	bytes, _ := serializer.Encode(group)
	result := ColorGroup{}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = serializer.Decode(bytes, &result)
	}
}

func BenchmarkThriftCodec_Decode(b *testing.B) {
	serializer := ThriftCodec{}
	thriftColorGroup := testdata.ThriftColorGroup{
		ID:     1,
		Name:   "Reds",
		Colors: []string{"Crimson", "Red", "Ruby", "Maroon"},
	}
	bytes, _ := serializer.Encode(&thriftColorGroup)
	result := testdata.ThriftColorGroup{}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = serializer.Decode(bytes, &result)
	}
}


================================================
FILE: codec/testdata/GoUnusedProtection__.go
================================================
// Code generated by Thrift Compiler (0.14.0). DO NOT EDIT.

package testdata

var GoUnusedProtection__ int;



================================================
FILE: codec/testdata/gen.sh
================================================
# generate .go files from IDL
protoc -I.  --go_out=. --go_opt=module="testdata" ./protobuf.proto

thrift -r -out ../ --gen go ./thrift_colorgroup.thrift

# # run benchmarks
# go test -bench=. -run=none

# # clean files
# rm -rf ./testdata/*.go

================================================
FILE: codec/testdata/protobuf.pb.go
================================================
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// 	protoc-gen-go v1.33.0
// 	protoc        v5.26.0
// source: protobuf.proto

package testdata

import (
	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
	reflect "reflect"
	sync "sync"
)

const (
	// Verify that this generated code is sufficiently up-to-date.
	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
	// Verify that runtime/protoimpl is sufficiently up-to-date.
	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)

type ProtoColorGroup struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	Id     int32    `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
	Name   string   `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
	Colors []string `protobuf:"bytes,3,rep,name=colors,proto3" json:"colors,omitempty"`
}

func (x *ProtoColorGroup) Reset() {
	*x = ProtoColorGroup{}
	if protoimpl.UnsafeEnabled {
		mi := &file_protobuf_proto_msgTypes[0]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

func (x *ProtoColorGroup) String() string {
	return protoimpl.X.MessageStringOf(x)
}

func (*ProtoColorGroup) ProtoMessage() {}

func (x *ProtoColorGroup) ProtoReflect() protoreflect.Message {
	mi := &file_protobuf_proto_msgTypes[0]
	if protoimpl.UnsafeEnabled && x != nil {
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		if ms.LoadMessageInfo() == nil {
			ms.StoreMessageInfo(mi)
		}
		return ms
	}
	return mi.MessageOf(x)
}

// Deprecated: Use ProtoColorGroup.ProtoReflect.Descriptor instead.
func (*ProtoColorGroup) Descriptor() ([]byte, []int) {
	return file_protobuf_proto_rawDescGZIP(), []int{0}
}

func (x *ProtoColorGroup) GetId() int32 {
	if x != nil {
		return x.Id
	}
	return 0
}

func (x *ProtoColorGroup) GetName() string {
	if x != nil {
		return x.Name
	}
	return ""
}

func (x *ProtoColorGroup) GetColors() []string {
	if x != nil {
		return x.Colors
	}
	return nil
}

var File_protobuf_proto protoreflect.FileDescriptor

var file_protobuf_proto_rawDesc = []byte{
	0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
	0x12, 0x08, 0x74, 0x65, 0x73, 0x74, 0x64, 0x61, 0x74, 0x61, 0x22, 0x4d, 0x0a, 0x0f, 0x50, 0x72,
	0x6f, 0x74, 0x6f, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x0e, 0x0a,
	0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a,
	0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d,
	0x65, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28,
	0x09, 0x52, 0x06, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x42, 0x0c, 0x5a, 0x0a, 0x2e, 0x2f, 0x74,
	0x65, 0x73, 0x74, 0x64, 0x61, 0x74, 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}

var (
	file_protobuf_proto_rawDescOnce sync.Once
	file_protobuf_proto_rawDescData = file_protobuf_proto_rawDesc
)

func file_protobuf_proto_rawDescGZIP() []byte {
	file_protobuf_proto_rawDescOnce.Do(func() {
		file_protobuf_proto_rawDescData = protoimpl.X.CompressGZIP(file_protobuf_proto_rawDescData)
	})
	return file_protobuf_proto_rawDescData
}

var file_protobuf_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_protobuf_proto_goTypes = []interface{}{
	(*ProtoColorGroup)(nil), // 0: testdata.ProtoColorGroup
}
var file_protobuf_proto_depIdxs = []int32{
	0, // [0:0] is the sub-list for method output_type
	0, // [0:0] is the sub-list for method input_type
	0, // [0:0] is the sub-list for extension type_name
	0, // [0:0] is the sub-list for extension extendee
	0, // [0:0] is the sub-list for field type_name
}

func init() { file_protobuf_proto_init() }
func file_protobuf_proto_init() {
	if File_protobuf_proto != nil {
		return
	}
	if !protoimpl.UnsafeEnabled {
		file_protobuf_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*ProtoColorGroup); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
	}
	type x struct{}
	out := protoimpl.TypeBuilder{
		File: protoimpl.DescBuilder{
			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
			RawDescriptor: file_protobuf_proto_rawDesc,
			NumEnums:      0,
			NumMessages:   1,
			NumExtensions: 0,
			NumServices:   0,
		},
		GoTypes:           file_protobuf_proto_goTypes,
		DependencyIndexes: file_protobuf_proto_depIdxs,
		MessageInfos:      file_protobuf_proto_msgTypes,
	}.Build()
	File_protobuf_proto = out.File
	file_protobuf_proto_rawDesc = nil
	file_protobuf_proto_goTypes = nil
	file_protobuf_proto_depIdxs = nil
}


================================================
FILE: codec/testdata/protobuf.proto
================================================
syntax = "proto3";

package testdata;

option go_package = "./testdata";

message ProtoColorGroup {
  int32 id = 1;
  string name = 2;
  repeated string colors = 3;
}

================================================
FILE: codec/testdata/thrift_colorgroup-consts.go
================================================
// Code generated by Thrift Compiler (0.14.0). DO NOT EDIT.

package testdata

import(
	"bytes"
	"context"
	"fmt"
	"time"
	"github.com/apache/thrift/lib/go/thrift"
)

// (needed to ensure safety because of naive import list construction.)
var _ = thrift.ZERO
var _ = fmt.Printf
var _ = context.Background
var _ = time.Now
var _ = bytes.Equal


func init() {
}



================================================
FILE: codec/testdata/thrift_colorgroup.go
================================================
// Code generated by Thrift Compiler (0.14.0). DO NOT EDIT.

package testdata

import(
	"bytes"
	"context"
	"fmt"
	"time"
	"github.com/apache/thrift/lib/go/thrift"
)

// (needed to ensure safety because of naive import list construction.)
var _ = thrift.ZERO
var _ = fmt.Printf
var _ = context.Background
var _ = time.Now
var _ = bytes.Equal

// Attributes:
//  - ID
//  - Name
//  - Colors
type ThriftColorGroup struct {
  ID int32 `thrift:"id,1" db:"id" json:"id"`
  Name string `thrift:"name,2" db:"name" json:"name"`
  Colors []string `thrift:"colors,3" db:"colors" json:"colors"`
}

func NewThriftColorGroup() *ThriftColorGroup {
  return &ThriftColorGroup{}
}


func (p *ThriftColorGroup) GetID() int32 {
  return p.ID
}

func (p *ThriftColorGroup) GetName() string {
  return p.Name
}

func (p *ThriftColorGroup) GetColors() []string {
  return p.Colors
}
func (p *ThriftColorGroup) Read(ctx context.Context, iprot thrift.TProtocol) error {
  if _, err := iprot.ReadStructBegin(ctx); err != nil {
    return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err)
  }


  for {
    _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx)
    if err != nil {
      return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err)
    }
    if fieldTypeId == thrift.STOP { break; }
    switch fieldId {
    case 1:
      if fieldTypeId == thrift.I32 {
        if err := p.ReadField1(ctx, iprot); err != nil {
          return err
        }
      } else {
        if err := iprot.Skip(ctx, fieldTypeId); err != nil {
          return err
        }
      }
    case 2:
      if fieldTypeId == thrift.STRING {
        if err := p.ReadField2(ctx, iprot); err != nil {
          return err
        }
      } else {
        if err := iprot.Skip(ctx, fieldTypeId); err != nil {
          return err
        }
      }
    case 3:
      if fieldTypeId == thrift.LIST {
        if err := p.ReadField3(ctx, iprot); err != nil {
          return err
        }
      } else {
        if err := iprot.Skip(ctx, fieldTypeId); err != nil {
          return err
        }
      }
    default:
      if err := iprot.Skip(ctx, fieldTypeId); err != nil {
        return err
      }
    }
    if err := iprot.ReadFieldEnd(ctx); err != nil {
      return err
    }
  }
  if err := iprot.ReadStructEnd(ctx); err != nil {
    return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err)
  }
  return nil
}

func (p *ThriftColorGroup)  ReadField1(ctx context.Context, iprot thrift.TProtocol) error {
  if v, err := iprot.ReadI32(ctx); err != nil {
  return thrift.PrependError("error reading field 1: ", err)
} else {
  p.ID = v
}
  return nil
}

func (p *ThriftColorGroup)  ReadField2(ctx context.Context, iprot thrift.TProtocol) error {
  if v, err := iprot.ReadString(ctx); err != nil {
  return thrift.PrependError("error reading field 2: ", err)
} else {
  p.Name = v
}
  return nil
}

func (p *ThriftColorGroup)  ReadField3(ctx context.Context, iprot thrift.TProtocol) error {
  _, size, err := iprot.ReadListBegin(ctx)
  if err != nil {
    return thrift.PrependError("error reading list begin: ", err)
  }
  tSlice := make([]string, 0, size)
  p.Colors =  tSlice
  for i := 0; i < size; i ++ {
var _elem0 string
    if v, err := iprot.ReadString(ctx); err != nil {
    return thrift.PrependError("error reading field 0: ", err)
} else {
    _elem0 = v
}
    p.Colors = append(p.Colors, _elem0)
  }
  if err := iprot.ReadListEnd(ctx); err != nil {
    return thrift.PrependError("error reading list end: ", err)
  }
  return nil
}

func (p *ThriftColorGroup) Write(ctx context.Context, oprot thrift.TProtocol) error {
  if err := oprot.WriteStructBegin(ctx, "ThriftColorGroup"); err != nil {
    return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) }
  if p != nil {
    if err := p.writeField1(ctx, oprot); err != nil { return err }
    if err := p.writeField2(ctx, oprot); err != nil { return err }
    if err := p.writeField3(ctx, oprot); err != nil { return err }
  }
  if err := oprot.WriteFieldStop(ctx); err != nil {
    return thrift.PrependError("write field stop error: ", err) }
  if err := oprot.WriteStructEnd(ctx); err != nil {
    return thrift.PrependError("write struct stop error: ", err) }
  return nil
}

func (p *ThriftColorGroup) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) {
  if err := oprot.WriteFieldBegin(ctx, "id", thrift.I32, 1); err != nil {
    return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:id: ", p), err) }
  if err := oprot.WriteI32(ctx, int32(p.ID)); err != nil {
  return thrift.PrependError(fmt.Sprintf("%T.id (1) field write error: ", p), err) }
  if err := oprot.WriteFieldEnd(ctx); err != nil {
    return thrift.PrependError(fmt.Sprintf("%T write field end error 1:id: ", p), err) }
  return err
}

func (p *ThriftColorGroup) writeField2(ctx context.Context, oprot thrift.TProtocol) (err error) {
  if err := oprot.WriteFieldBegin(ctx, "name", thrift.STRING, 2); err != nil {
    return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:name: ", p), err) }
  if err := oprot.WriteString(ctx, string(p.Name)); err != nil {
  return thrift.PrependError(fmt.Sprintf("%T.name (2) field write error: ", p), err) }
  if err := oprot.WriteFieldEnd(ctx); err != nil {
    return thrift.PrependError(fmt.Sprintf("%T write field end error 2:name: ", p), err) }
  return err
}

func (p *ThriftColorGroup) writeField3(ctx context.Context, oprot thrift.TProtocol) (err error) {
  if err := oprot.WriteFieldBegin(ctx, "colors", thrift.LIST, 3); err != nil {
    return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:colors: ", p), err) }
  if err := oprot.WriteListBegin(ctx, thrift.STRING, len(p.Colors)); err != nil {
    return thrift.PrependError("error writing list begin: ", err)
  }
  for _, v := range p.Colors {
    if err := oprot.WriteString(ctx, string(v)); err != nil {
    return thrift.PrependError(fmt.Sprintf("%T. (0) field write error: ", p), err) }
  }
  if err := oprot.WriteListEnd(ctx); err != nil {
    return thrift.PrependError("error writing list end: ", err)
  }
  if err := oprot.WriteFieldEnd(ctx); err != nil {
    return thrift.PrependError(fmt.Sprintf("%T write field end error 3:colors: ", p), err) }
  return err
}

func (p *ThriftColorGroup) Equals(other *ThriftColorGroup) bool {
  if p == other {
    return true
  } else if p == nil || other == nil {
    return false
  }
  if p.ID != other.ID { return false }
  if p.Name != other.Name { return false }
  if len(p.Colors) != len(other.Colors) { return false }
  for i, _tgt := range p.Colors {
    _src1 := other.Colors[i]
    if _tgt != _src1 { return false }
  }
  return true
}

func (p *ThriftColorGroup) String() string {
  if p == nil {
    return "<nil>"
  }
  return fmt.Sprintf("ThriftColorGroup(%+v)", *p)
}



================================================
FILE: codec/testdata/thrift_colorgroup.thrift
================================================
namespace go testdata

struct ThriftColorGroup {
  1: i32 id = 0,
  2: string name,
  3: list<string> colors,
}


================================================
FILE: errors/error.go
================================================
package errors

import (
	"fmt"
	"sync"
)

// MultiError holds multiple errors
type MultiError struct {
	Errors []error
	mu     sync.Mutex
}

// Error returns the message of the actual error
func (e *MultiError) Error() string {
	return fmt.Sprin
Download .txt
gitextract_r3mw_qpx/

├── .github/
│   └── workflows/
│       └── go.yml
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── Makefile
├── README.md
├── _testutils/
│   ├── GoUnusedProtection__.go
│   ├── arith_service.go
│   ├── arith_service.proto
│   ├── thrift_arith_service-consts.go
│   ├── thrift_arith_service.go
│   └── thrift_arith_service.thrift
├── client/
│   ├── cache_client_builder.go
│   ├── circuit_breaker.go
│   ├── circuit_breaker_test.go
│   ├── client.go
│   ├── client_test.go
│   ├── connection.go
│   ├── connection_iouring.go
│   ├── connection_iouring_test.go
│   ├── connection_kcp.go
│   ├── connection_memu.go
│   ├── connection_nonkcp.go
│   ├── connection_nonquic.go
│   ├── connection_quic.go
│   ├── connection_rdma.go
│   ├── discovery.go
│   ├── dns_discovery.go
│   ├── failmode_enumer.go
│   ├── geo_utils.go
│   ├── hash_utils.go
│   ├── mdns_discovery.go
│   ├── mode.go
│   ├── multiple_servers_discovery.go
│   ├── oneclient.go
│   ├── oneclient_pool.go
│   ├── oneclient_pool_test.go
│   ├── peer2peer_discovery.go
│   ├── ping_utils.go
│   ├── plugin.go
│   ├── selectmode_enumer.go
│   ├── selector.go
│   ├── selector_test.go
│   ├── smooth_weighted_round_robin.go
│   ├── xclient.go
│   ├── xclient_pool.go
│   ├── xclient_pool_test.go
│   └── xclient_test.go
├── clientplugin/
│   └── req_rate_limiting_redis.go
├── codec/
│   ├── codec.go
│   ├── codec_test.go
│   └── testdata/
│       ├── GoUnusedProtection__.go
│       ├── gen.sh
│       ├── protobuf.pb.go
│       ├── protobuf.proto
│       ├── thrift_colorgroup-consts.go
│       ├── thrift_colorgroup.go
│       └── thrift_colorgroup.thrift
├── errors/
│   ├── error.go
│   └── error_test.go
├── go.mod
├── go.sum
├── log/
│   ├── default_logger.go
│   ├── dummy_logger.go
│   └── logger.go
├── protocol/
│   ├── compressor.go
│   ├── compressor_test.go
│   ├── message.go
│   ├── message_chan_test.go
│   ├── message_test.go
│   └── testdata/
│       ├── benchmark.pb.go
│       ├── benchmark.proto
│       └── gen.sh
├── reflection/
│   ├── server_reflection.go
│   └── server_reflection_test.go
├── server/
│   ├── context.go
│   ├── converter.go
│   ├── converter_test.go
│   ├── file_transfer.go
│   ├── gateway.go
│   ├── jsonrpc2.go
│   ├── jsonrpc2_wire.go
│   ├── kcp.go
│   ├── listener.go
│   ├── listener_linux.go
│   ├── listener_rdma.go
│   ├── listener_unix.go
│   ├── memconn.go
│   ├── option.go
│   ├── option_test.go
│   ├── options.go
│   ├── plugin.go
│   ├── plugin_test.go
│   ├── pool.go
│   ├── pool_test.go
│   ├── quic.go
│   ├── router.go
│   ├── server.go
│   ├── server_test.go
│   ├── service.go
│   ├── service_test.go
│   └── stream.go
├── serverplugin/
│   ├── alias.go
│   ├── blacklist.go
│   ├── mdns.go
│   ├── metrics.go
│   ├── rate_limiting.go
│   ├── redis.go
│   ├── registry_test.go
│   ├── req_rate_limiting.go
│   ├── req_rate_limiting_redis.go
│   ├── tee.go
│   └── whitelist.go
├── share/
│   ├── context.go
│   ├── context_test.go
│   ├── share.go
│   └── share_test.go
├── tool/
│   └── xgen/
│       ├── README.md
│       ├── parser/
│       │   └── parser.go
│       └── xgen.go
└── util/
    ├── buffer_pool.go
    ├── buffer_pool_test.go
    ├── compress.go
    ├── compress_test.go
    ├── converter.go
    ├── net.go
    └── net_test.go
Download .txt
SYMBOL INDEX (1003 symbols across 108 files)

FILE: _testutils/arith_service.go
  constant _ (line 32) | _ = proto.GoGoProtoPackageIsVersion2
  type ProtoArgs (line 34) | type ProtoArgs struct
    method Reset (line 39) | func (m *ProtoArgs) Reset()                    { *m = ProtoArgs{} }
    method String (line 40) | func (m *ProtoArgs) String() string            { return proto.CompactT...
    method ProtoMessage (line 41) | func (*ProtoArgs) ProtoMessage()               {}
    method Descriptor (line 42) | func (*ProtoArgs) Descriptor() ([]byte, []int) { return fileDescriptor...
    method GetA (line 44) | func (m *ProtoArgs) GetA() int32 {
    method GetB (line 51) | func (m *ProtoArgs) GetB() int32 {
    method Marshal (line 78) | func (m *ProtoArgs) Marshal() (dAtA []byte, err error) {
    method MarshalTo (line 88) | func (m *ProtoArgs) MarshalTo(dAtA []byte) (int, error) {
    method Size (line 156) | func (m *ProtoArgs) Size() (n int) {
    method Unmarshal (line 190) | func (m *ProtoArgs) Unmarshal(dAtA []byte) error {
  type ProtoReply (line 58) | type ProtoReply struct
    method Reset (line 62) | func (m *ProtoReply) Reset()                    { *m = ProtoReply{} }
    method String (line 63) | func (m *ProtoReply) String() string            { return proto.Compact...
    method ProtoMessage (line 64) | func (*ProtoReply) ProtoMessage()               {}
    method Descriptor (line 65) | func (*ProtoReply) Descriptor() ([]byte, []int) { return fileDescripto...
    method GetC (line 67) | func (m *ProtoReply) GetC() int32 {
    method Marshal (line 106) | func (m *ProtoReply) Marshal() (dAtA []byte, err error) {
    method MarshalTo (line 116) | func (m *ProtoReply) MarshalTo(dAtA []byte) (int, error) {
    method Size (line 168) | func (m *ProtoReply) Size() (n int) {
    method Unmarshal (line 278) | func (m *ProtoReply) Unmarshal(dAtA []byte) error {
  function init (line 74) | func init() {
  function encodeFixed64ArithService (line 129) | func encodeFixed64ArithService(dAtA []byte, offset int, v uint64) int {
  function encodeFixed32ArithService (line 140) | func encodeFixed32ArithService(dAtA []byte, offset int, v uint32) int {
  function encodeVarintArithService (line 147) | func encodeVarintArithService(dAtA []byte, offset int, v uint64) int {
  function sovArithService (line 177) | func sovArithService(x uint64) (n int) {
  function sozArithService (line 187) | func sozArithService(x uint64) (n int) {
  function skipArithService (line 347) | func skipArithService(dAtA []byte) (n int, err error) {
  function init (line 452) | func init() { proto.RegisterFile("arith_service.proto", fileDescriptorAr...

FILE: _testutils/thrift_arith_service-consts.go
  function init (line 20) | func init() {

FILE: _testutils/thrift_arith_service.go
  type ThriftArgs_ (line 23) | type ThriftArgs_ struct
    method GetA (line 32) | func (p *ThriftArgs_) GetA() int32 {
    method GetB (line 36) | func (p *ThriftArgs_) GetB() int32 {
    method Read (line 39) | func (p *ThriftArgs_) Read(ctx context.Context, iprot thrift.TProtocol...
    method ReadField1 (line 88) | func (p *ThriftArgs_) ReadField1(ctx context.Context, iprot thrift.TPr...
    method ReadField2 (line 97) | func (p *ThriftArgs_) ReadField2(ctx context.Context, iprot thrift.TPr...
    method Write (line 106) | func (p *ThriftArgs_) Write(ctx context.Context, oprot thrift.TProtoco...
    method writeField1 (line 127) | func (p *ThriftArgs_) writeField1(ctx context.Context, oprot thrift.TP...
    method writeField2 (line 140) | func (p *ThriftArgs_) writeField2(ctx context.Context, oprot thrift.TP...
    method Equals (line 153) | func (p *ThriftArgs_) Equals(other *ThriftArgs_) bool {
    method String (line 168) | func (p *ThriftArgs_) String() string {
  function NewThriftArgs_ (line 28) | func NewThriftArgs_() *ThriftArgs_ {
  type ThriftReply (line 177) | type ThriftReply struct
    method GetC (line 185) | func (p *ThriftReply) GetC() int32 {
    method Read (line 188) | func (p *ThriftReply) Read(ctx context.Context, iprot thrift.TProtocol...
    method ReadField1 (line 227) | func (p *ThriftReply) ReadField1(ctx context.Context, iprot thrift.TPr...
    method Write (line 236) | func (p *ThriftReply) Write(ctx context.Context, oprot thrift.TProtoco...
    method writeField1 (line 254) | func (p *ThriftReply) writeField1(ctx context.Context, oprot thrift.TP...
    method Equals (line 267) | func (p *ThriftReply) Equals(other *ThriftReply) bool {
    method String (line 279) | func (p *ThriftReply) String() string {
  function NewThriftReply (line 181) | func NewThriftReply() *ThriftReply {

FILE: client/cache_client_builder.go
  type CacheClientBuilder (line 6) | type CacheClientBuilder interface
  function RegisterCacheClientBuilder (line 18) | func RegisterCacheClientBuilder(network string, builder CacheClientBuild...
  function getCacheClientBuilder (line 25) | func getCacheClientBuilder(network string) (CacheClientBuilder, bool) {

FILE: client/circuit_breaker.go
  type ConsecCircuitBreaker (line 15) | type ConsecCircuitBreaker struct
    method Call (line 32) | func (cb *ConsecCircuitBreaker) Call(fn func() error, d time.Duration)...
    method ready (line 67) | func (cb *ConsecCircuitBreaker) ready() bool {
    method success (line 78) | func (cb *ConsecCircuitBreaker) success() {
    method fail (line 82) | func (cb *ConsecCircuitBreaker) fail() {
    method Success (line 87) | func (cb *ConsecCircuitBreaker) Success() {
    method Fail (line 91) | func (cb *ConsecCircuitBreaker) Fail() {
    method Ready (line 95) | func (cb *ConsecCircuitBreaker) Ready() bool {
    method reset (line 99) | func (cb *ConsecCircuitBreaker) reset() {
  function NewConsecCircuitBreaker (line 24) | func NewConsecCircuitBreaker(failureThreshold uint64, window time.Durati...

FILE: client/circuit_breaker_test.go
  function TestConsecCircuitBreaker (line 10) | func TestConsecCircuitBreaker(t *testing.T) {
  function TestCircuitBreakerRace (line 56) | func TestCircuitBreakerRace(t *testing.T) {

FILE: client/client.go
  constant XVersion (line 24) | XVersion           = "X-RPCX-Version"
  constant XMessageType (line 25) | XMessageType       = "X-RPCX-MesssageType"
  constant XHeartbeat (line 26) | XHeartbeat         = "X-RPCX-Heartbeat"
  constant XOneway (line 27) | XOneway            = "X-RPCX-Oneway"
  constant XMessageStatusType (line 28) | XMessageStatusType = "X-RPCX-MessageStatusType"
  constant XSerializeType (line 29) | XSerializeType     = "X-RPCX-SerializeType"
  constant XMessageID (line 30) | XMessageID         = "X-RPCX-MessageID"
  constant XServicePath (line 31) | XServicePath       = "X-RPCX-ServicePath"
  constant XServiceMethod (line 32) | XServiceMethod     = "X-RPCX-ServiceMethod"
  constant XMeta (line 33) | XMeta              = "X-RPCX-Meta"
  constant XErrorMessage (line 34) | XErrorMessage      = "X-RPCX-ErrorMessage"
  type ServiceError (line 38) | type ServiceError interface
  function NewServiceError (line 44) | func NewServiceError(s string) ServiceError {
  type strErr (line 51) | type strErr
    method Error (line 53) | func (s strErr) Error() string {
    method IsServiceError (line 57) | func (s strErr) IsServiceError() bool {
  type Breaker (line 76) | type Breaker interface
  constant ReaderBuffsize (line 94) | ReaderBuffsize = 16 * 1024
  constant WriterBuffsize (line 96) | WriterBuffsize = 16 * 1024
  type seqKey (line 99) | type seqKey struct
  type RPCClient (line 102) | type RPCClient interface
  type Client (line 120) | type Client struct
    method RemoteAddr (line 147) | func (client *Client) RemoteAddr() string {
    method GetConn (line 152) | func (client *Client) GetConn() net.Conn {
    method RegisterServerMessageChan (line 235) | func (client *Client) RegisterServerMessageChan(ch chan<- *protocol.Me...
    method UnregisterServerMessageChan (line 240) | func (client *Client) UnregisterServerMessageChan() {
    method IsClosing (line 245) | func (client *Client) IsClosing() bool {
    method IsShutdown (line 252) | func (client *Client) IsShutdown() bool {
    method Go (line 262) | func (client *Client) Go(ctx context.Context, servicePath, serviceMeth...
    method Call (line 303) | func (client *Client) Call(ctx context.Context, servicePath, serviceMe...
    method call (line 307) | func (client *Client) call(ctx context.Context, servicePath, serviceMe...
    method SendRaw (line 366) | func (client *Client) SendRaw(ctx context.Context, r *protocol.Message...
    method send (line 512) | func (client *Client) send(ctx context.Context, call *Call) {
    method input (line 638) | func (client *Client) input() {
    method handleServerRequest (line 784) | func (client *Client) handleServerRequest(msg *protocol.Message) {
    method heartbeat (line 806) | func (client *Client) heartbeat() {
    method Close (line 846) | func (client *Client) Close() error {
  function NewClient (line 140) | func NewClient(option Option) *Client {
  type Option (line 157) | type Option struct
  type Call (line 208) | type Call struct
    method done (line 220) | func (call *Call) done() {
  function convertRes2Raw (line 466) | func convertRes2Raw(res *protocol.Message) (map[string]string, []byte, e...
  function urlencode (line 494) | func urlencode(data map[string]string) string {

FILE: client/client_test.go
  type Args (line 17) | type Args struct
  type Reply (line 22) | type Reply struct
  type Arith (line 26) | type Arith
    method Mul (line 28) | func (t *Arith) Mul(ctx context.Context, args *Args, reply *Reply) err...
    method ThriftMul (line 40) | func (t *Arith) ThriftMul(ctx context.Context, args *testutils.ThriftA...
  type PBArith (line 33) | type PBArith
    method Mul (line 35) | func (t *PBArith) Mul(ctx context.Context, args *testutils.ProtoArgs, ...
  type Bidirectional (line 45) | type Bidirectional struct
    method Mul (line 49) | func (t *Bidirectional) Mul(ctx context.Context, args *Args, reply *Re...
  function TestClient_IT (line 56) | func TestClient_IT(t *testing.T) {
  function TestClient_IT_Concurrency (line 126) | func TestClient_IT_Concurrency(t *testing.T) {
  function testSendRaw (line 157) | func testSendRaw(t *testing.T, client *Client, seq uint64, x, y int32, w...
  function TestClient_Res_Reset (line 192) | func TestClient_Res_Reset(t *testing.T) {
  function TestClient_Bidirectional (line 203) | func TestClient_Bidirectional(t *testing.T) {

FILE: client/connection.go
  type ConnFactoryFn (line 18) | type ConnFactoryFn
  function init (line 22) | func init() {
  method Connect (line 32) | func (client *Client) Connect(network, address string) error {
  function newDirectConn (line 87) | func newDirectConn(c *Client, network, address string) (net.Conn, error) {
  function newDirectHTTPConn (line 118) | func newDirectHTTPConn(c *Client, network, address string) (net.Conn, er...
  function newDirectWSConn (line 176) | func newDirectWSConn(c *Client, network, address string) (net.Conn, erro...

FILE: client/connection_iouring.go
  function newIOUringConn (line 8) | func newIOUringConn(c *Client, network, address string) (net.Conn, error) {

FILE: client/connection_kcp.go
  function newDirectKCPConn (line 11) | func newDirectKCPConn(c *Client, network, address string) (net.Conn, err...

FILE: client/connection_memu.go
  function newMemuConn (line 9) | func newMemuConn(c *Client, network, address string) (net.Conn, error) {

FILE: client/connection_nonkcp.go
  function newDirectKCPConn (line 10) | func newDirectKCPConn(c *Client, network, address string) (net.Conn, err...

FILE: client/connection_nonquic.go
  function newDirectQuicConn (line 11) | func newDirectQuicConn(c *Client, network, address string) (net.Conn, er...

FILE: client/connection_quic.go
  function newDirectQuicConn (line 14) | func newDirectQuicConn(c *Client, network, address string) (net.Conn, er...

FILE: client/connection_rdma.go
  function init (line 13) | func init() {
  function newRDMAConn (line 17) | func newRDMAConn(c *Client, network, address string) (net.Conn, error) {

FILE: client/discovery.go
  type ServiceDiscoveryFilter (line 13) | type ServiceDiscoveryFilter
  type ServiceDiscovery (line 16) | type ServiceDiscovery interface
  type cachedServiceDiscovery (line 25) | type cachedServiceDiscovery struct
    method GetServices (line 60) | func (cd *cachedServiceDiscovery) GetServices() []*KVPair {
    method WatchService (line 80) | func (cd *cachedServiceDiscovery) WatchService() chan []*KVPair {
    method RemoveWatcher (line 111) | func (cd *cachedServiceDiscovery) RemoveWatcher(ch chan []*KVPair) {
    method storeCached (line 123) | func (cd *cachedServiceDiscovery) storeCached(kvPairs []*KVPair) {
    method loadCached (line 128) | func (cd *cachedServiceDiscovery) loadCached() (kvPairs []*KVPair) {
  function CacheDiscovery (line 38) | func CacheDiscovery(threshold int, cachedFile string, discovery ServiceD...

FILE: client/dns_discovery.go
  type DNSDiscovery (line 15) | type DNSDiscovery struct
    method Clone (line 41) | func (d *DNSDiscovery) Clone(servicePath string) (ServiceDiscovery, er...
    method SetFilter (line 46) | func (d *DNSDiscovery) SetFilter(filter ServiceDiscoveryFilter) {
    method GetServices (line 51) | func (d *DNSDiscovery) GetServices() []*KVPair {
    method WatchService (line 58) | func (d *DNSDiscovery) WatchService() chan []*KVPair {
    method RemoveWatcher (line 67) | func (d *DNSDiscovery) RemoveWatcher(ch chan []*KVPair) {
    method lookup (line 83) | func (d *DNSDiscovery) lookup() {
    method watch (line 127) | func (d *DNSDiscovery) watch() {
    method Close (line 141) | func (d *DNSDiscovery) Close() {
  function NewDNSDiscovery (line 33) | func NewDNSDiscovery(domain string, network string, port int, d time.Dur...

FILE: client/failmode_enumer.go
  constant _FailModeName (line 9) | _FailModeName = "FailoverFailfastFailtryFailbackup"
  method String (line 13) | func (i FailMode) String() string {
  function FailModeString (line 31) | func FailModeString(s string) (FailMode, error) {
  function FailModeValues (line 39) | func FailModeValues() []FailMode {
  method IsAFailMode (line 44) | func (i FailMode) IsAFailMode() bool {

FILE: client/geo_utils.go
  function getDistanceFrom (line 8) | func getDistanceFrom(lat1, lon1, lat2, lon2 float64) float64 {
  function hsin (line 23) | func hsin(theta float64) float64 {

FILE: client/hash_utils.go
  function Hash (line 9) | func Hash(key uint64, buckets int32) int32 {
  function HashString (line 26) | func HashString(s string) uint64 {
  type HashServiceAndArgs (line 33) | type HashServiceAndArgs
  type ConsistentAddrStrFunction (line 37) | type ConsistentAddrStrFunction
  function genKey (line 39) | func genKey(options ...interface{}) uint64 {
  function JumpConsistentHash (line 49) | func JumpConsistentHash(len int, options ...interface{}) int {
  function toString (line 53) | func toString(obj interface{}) string {

FILE: client/mdns_discovery.go
  type serviceMeta (line 14) | type serviceMeta struct
  type MDNSDiscovery (line 22) | type MDNSDiscovery struct
    method Clone (line 59) | func (d *MDNSDiscovery) Clone(servicePath string) (ServiceDiscovery, e...
    method SetFilter (line 64) | func (d *MDNSDiscovery) SetFilter(filter ServiceDiscoveryFilter) {
    method GetServices (line 69) | func (d *MDNSDiscovery) GetServices() []*KVPair {
    method WatchService (line 77) | func (d *MDNSDiscovery) WatchService() chan []*KVPair {
    method RemoveWatcher (line 86) | func (d *MDNSDiscovery) RemoveWatcher(ch chan []*KVPair) {
    method watch (line 102) | func (d *MDNSDiscovery) watch() {
    method browse (line 138) | func (d *MDNSDiscovery) browse() ([]*KVPair, error) {
    method Close (line 186) | func (d *MDNSDiscovery) Close() {
  function NewMDNSDiscovery (line 40) | func NewMDNSDiscovery(service string, timeout time.Duration, watchInterv...

FILE: client/mode.go
  type FailMode (line 4) | type FailMode
  constant Failover (line 8) | Failover FailMode = iota
  constant Failfast (line 10) | Failfast
  constant Failtry (line 12) | Failtry
  constant Failbackup (line 14) | Failbackup
  type SelectMode (line 18) | type SelectMode
  constant RandomSelect (line 22) | RandomSelect SelectMode = iota
  constant RoundRobin (line 24) | RoundRobin
  constant WeightedRoundRobin (line 26) | WeightedRoundRobin
  constant WeightedICMP (line 28) | WeightedICMP
  constant ConsistentHash (line 30) | ConsistentHash
  constant Closest (line 32) | Closest
  constant SelectByUser (line 35) | SelectByUser = 1000

FILE: client/multiple_servers_discovery.go
  type MultipleServersDiscovery (line 12) | type MultipleServersDiscovery struct
    method Clone (line 28) | func (d *MultipleServersDiscovery) Clone(servicePath string) (ServiceD...
    method SetFilter (line 33) | func (d *MultipleServersDiscovery) SetFilter(filter ServiceDiscoveryFi...
    method GetServices (line 37) | func (d *MultipleServersDiscovery) GetServices() []*KVPair {
    method WatchService (line 45) | func (d *MultipleServersDiscovery) WatchService() chan []*KVPair {
    method RemoveWatcher (line 54) | func (d *MultipleServersDiscovery) RemoveWatcher(ch chan []*KVPair) {
    method Update (line 71) | func (d *MultipleServersDiscovery) Update(pairs []*KVPair) {
    method Close (line 94) | func (d *MultipleServersDiscovery) Close() {
  function NewMultipleServersDiscovery (line 21) | func NewMultipleServersDiscovery(pairs []*KVPair) (*MultipleServersDisco...

FILE: client/oneclient.go
  type OneClient (line 18) | type OneClient struct
    method SetSelector (line 62) | func (c *OneClient) SetSelector(servicePath string, s Selector) {
    method SetPlugins (line 72) | func (c *OneClient) SetPlugins(plugins PluginContainer) {
    method GetPlugins (line 81) | func (c *OneClient) GetPlugins() PluginContainer {
    method ConfigGeoSelector (line 87) | func (c *OneClient) ConfigGeoSelector(latitude, longitude float64) {
    method Auth (line 100) | func (c *OneClient) Auth(auth string) {
    method Go (line 111) | func (c *OneClient) Go(ctx context.Context, servicePath string, servic...
    method newXClient (line 133) | func (c *OneClient) newXClient(servicePath string) (xclient XClient, e...
    method Call (line 176) | func (c *OneClient) Call(ctx context.Context, servicePath string, serv...
    method SendRaw (line 198) | func (c *OneClient) SendRaw(ctx context.Context, r *protocol.Message) ...
    method Broadcast (line 226) | func (c *OneClient) Broadcast(ctx context.Context, servicePath string,...
    method Fork (line 250) | func (c *OneClient) Fork(ctx context.Context, servicePath string, serv...
    method SendFile (line 272) | func (c *OneClient) SendFile(ctx context.Context, fileName string, rat...
    method DownloadFile (line 294) | func (c *OneClient) DownloadFile(ctx context.Context, requestFileName ...
    method Stream (line 316) | func (c *OneClient) Stream(ctx context.Context, meta map[string]string...
    method Close (line 339) | func (c *OneClient) Close() error {
  function NewOneClient (line 37) | func NewOneClient(failMode FailMode, selectMode SelectMode, discovery Se...
  function NewBidirectionalOneClient (line 49) | func NewBidirectionalOneClient(failMode FailMode, selectMode SelectMode,...

FILE: client/oneclient_pool.go
  type OneClientPool (line 12) | type OneClientPool struct
    method Auth (line 64) | func (p *OneClientPool) Auth(auth string) {
    method SetPlugins (line 73) | func (p *OneClientPool) SetPlugins(plugins PluginContainer) {
    method GetPlugins (line 82) | func (p *OneClientPool) GetPlugins() PluginContainer {
    method Get (line 89) | func (p *OneClientPool) Get() *OneClient {
    method Close (line 97) | func (p *OneClientPool) Close() {
  function NewOneClientPool (line 27) | func NewOneClientPool(count int, failMode FailMode, selectMode SelectMod...
  function NewBidirectionalOneClientPool (line 45) | func NewBidirectionalOneClientPool(count int, failMode FailMode, selectM...

FILE: client/oneclient_pool_test.go
  function TestOneClientPool_SetPlugins (line 7) | func TestOneClientPool_SetPlugins(t *testing.T) {
  function TestOneClientPool_GetPlugins (line 50) | func TestOneClientPool_GetPlugins(t *testing.T) {
  function TestOneClientPool_SetPlugins_Concurrent (line 95) | func TestOneClientPool_SetPlugins_Concurrent(t *testing.T) {

FILE: client/peer2peer_discovery.go
  type Peer2PeerDiscovery (line 5) | type Peer2PeerDiscovery struct
    method Clone (line 16) | func (d *Peer2PeerDiscovery) Clone(servicePath string) (ServiceDiscove...
    method SetFilter (line 21) | func (d *Peer2PeerDiscovery) SetFilter(filter ServiceDiscoveryFilter) {
    method GetServices (line 26) | func (d *Peer2PeerDiscovery) GetServices() []*KVPair {
    method WatchService (line 31) | func (d *Peer2PeerDiscovery) WatchService() chan []*KVPair {
    method RemoveWatcher (line 35) | func (d *Peer2PeerDiscovery) RemoveWatcher(ch chan []*KVPair) {}
    method Close (line 37) | func (d *Peer2PeerDiscovery) Close() {
  function NewPeer2PeerDiscovery (line 11) | func NewPeer2PeerDiscovery(server, metadata string) (*Peer2PeerDiscovery...

FILE: client/ping_utils.go
  type weightedICMPSelector (line 13) | type weightedICMPSelector struct
    method Select (line 29) | func (s *weightedICMPSelector) Select(ctx context.Context, servicePath...
    method UpdateServer (line 33) | func (s *weightedICMPSelector) UpdateServer(servers map[string]string) {
  function newWeightedICMPSelector (line 18) | func newWeightedICMPSelector(servers map[string]string) Selector {
  function createICMPWeighted (line 40) | func createICMPWeighted(servers map[string]string) []*Weighted {
  function Ping (line 56) | func Ping(host string) (rtt int, err error) {
  function CalculateWeight (line 88) | func CalculateWeight(rtt int) int {

FILE: client/plugin.go
  type pluginContainer (line 11) | type pluginContainer struct
    method Add (line 23) | func (p *pluginContainer) Add(plugin Plugin) {
    method Remove (line 28) | func (p *pluginContainer) Remove(plugin Plugin) {
    method All (line 44) | func (p *pluginContainer) All() []Plugin {
    method DoPreCall (line 49) | func (p *pluginContainer) DoPreCall(ctx context.Context, servicePath, ...
    method DoPostCall (line 63) | func (p *pluginContainer) DoPostCall(ctx context.Context, servicePath,...
    method DoConnCreated (line 76) | func (p *pluginContainer) DoConnCreated(conn net.Conn) (net.Conn, erro...
    method DoConnCreateFailed (line 90) | func (p *pluginContainer) DoConnCreateFailed(network, address string) {
    method DoClientConnected (line 99) | func (p *pluginContainer) DoClientConnected(conn net.Conn) (net.Conn, ...
    method DoClientConnectionClose (line 113) | func (p *pluginContainer) DoClientConnectionClose(conn net.Conn) error {
    method DoClientBeforeEncode (line 127) | func (p *pluginContainer) DoClientBeforeEncode(req *protocol.Message) ...
    method DoClientAfterDecode (line 141) | func (p *pluginContainer) DoClientAfterDecode(req *protocol.Message) e...
    method DoWrapSelect (line 155) | func (p *pluginContainer) DoWrapSelect(fn SelectFunc) SelectFunc {
  function NewPluginContainer (line 15) | func NewPluginContainer() PluginContainer {
  type Plugin (line 20) | type Plugin interface
  type PreCallPlugin (line 168) | type PreCallPlugin interface
  type PostCallPlugin (line 173) | type PostCallPlugin interface
  type ConnCreatedPlugin (line 178) | type ConnCreatedPlugin interface
  type ConnCreateFailedPlugin (line 182) | type ConnCreateFailedPlugin interface
  type ClientConnectedPlugin (line 187) | type ClientConnectedPlugin interface
  type ClientConnectionClosePlugin (line 192) | type ClientConnectionClosePlugin interface
  type ClientBeforeEncodePlugin (line 197) | type ClientBeforeEncodePlugin interface
  type ClientAfterDecodePlugin (line 202) | type ClientAfterDecodePlugin interface
  type SelectNodePlugin (line 207) | type SelectNodePlugin interface
  type PluginContainer (line 213) | type PluginContainer interface

FILE: client/selectmode_enumer.go
  constant _SelectModeName (line 9) | _SelectModeName = "RandomSelectRoundRobinWeightedRoundRobinWeightedICMPC...
  method String (line 13) | func (i SelectMode) String() string {
  function SelectModeString (line 33) | func SelectModeString(s string) (SelectMode, error) {
  function SelectModeValues (line 41) | func SelectModeValues() []SelectMode {
  method IsASelectMode (line 46) | func (i SelectMode) IsASelectMode() bool {

FILE: client/selector.go
  type SelectFunc (line 18) | type SelectFunc
  type Selector (line 21) | type Selector interface
  function newSelector (line 26) | func newSelector(selectMode SelectMode, servers map[string]string) Selec...
  type randomSelector (line 46) | type randomSelector struct
    method Select (line 59) | func (s *randomSelector) Select(ctx context.Context, servicePath, serv...
    method UpdateServer (line 68) | func (s *randomSelector) UpdateServer(servers map[string]string) {
  function newRandomSelector (line 50) | func newRandomSelector(servers map[string]string) Selector {
  type roundRobinSelector (line 78) | type roundRobinSelector struct
    method Select (line 92) | func (s *roundRobinSelector) Select(ctx context.Context, servicePath, ...
    method UpdateServer (line 104) | func (s *roundRobinSelector) UpdateServer(servers map[string]string) {
  function newRoundRobinSelector (line 83) | func newRoundRobinSelector(servers map[string]string) Selector {
  type weightedRoundRobinSelector (line 114) | type weightedRoundRobinSelector struct
    method Select (line 127) | func (s *weightedRoundRobinSelector) Select(ctx context.Context, servi...
    method UpdateServer (line 138) | func (s *weightedRoundRobinSelector) UpdateServer(servers map[string]s...
    method buildRing (line 143) | func (s *weightedRoundRobinSelector) buildRing() {
    method next (line 155) | func (s *weightedRoundRobinSelector) next() *Weighted {
  function newWeightedRoundRobinSelector (line 120) | func newWeightedRoundRobinSelector(servers map[string]string) Selector {
  function createWeighted (line 178) | func createWeighted(servers map[string]string) []*Weighted {
  type geoServer (line 198) | type geoServer struct
  type geoSelector (line 205) | type geoSelector struct
    method Select (line 218) | func (s *geoSelector) Select(ctx context.Context, servicePath, service...
    method UpdateServer (line 242) | func (s *geoSelector) UpdateServer(servers map[string]string) {
  function newGeoSelector (line 212) | func newGeoSelector(servers map[string]string, latitude, longitude float...
  function createGeoServer (line 247) | func createGeoServer(servers map[string]string) []*geoServer {
  type consistentHashSelector (line 277) | type consistentHashSelector struct
    method Select (line 294) | func (s *consistentHashSelector) Select(ctx context.Context, servicePa...
    method UpdateServer (line 305) | func (s *consistentHashSelector) UpdateServer(servers map[string]strin...
  function newConsistentHashSelector (line 282) | func newConsistentHashSelector(servers map[string]string) Selector {

FILE: client/selector_test.go
  function Test_consistentHashSelector_Select (line 8) | func Test_consistentHashSelector_Select(t *testing.T) {
  function Test_consistentHashSelector_UpdateServer (line 26) | func Test_consistentHashSelector_UpdateServer(t *testing.T) {
  function TestWeightedRoundRobinSelector_Select (line 41) | func TestWeightedRoundRobinSelector_Select(t *testing.T) {
  function TestWeightedRoundRobinSelector_UpdateServer (line 67) | func TestWeightedRoundRobinSelector_UpdateServer(t *testing.T) {
  function BenchmarkWeightedRoundRobinSelector_Select (line 96) | func BenchmarkWeightedRoundRobinSelector_Select(b *testing.B) {

FILE: client/smooth_weighted_round_robin.go
  type Weighted (line 4) | type Weighted struct

FILE: client/xclient.go
  constant FileTransferBufferSize (line 28) | FileTransferBufferSize = 1024
  type Receipt (line 41) | type Receipt struct
  type XClient (line 49) | type XClient interface
  type KVPair (line 79) | type KVPair struct
  type xClient (line 84) | type xClient struct
    method SetSelector (line 70) | func (c *xClient) SetSelector(s Selector) {
    method SetPlugins (line 191) | func (c *xClient) SetPlugins(plugins PluginContainer) {
    method GetPlugins (line 195) | func (c *xClient) GetPlugins() PluginContainer {
    method ConfigGeoSelector (line 201) | func (c *xClient) ConfigGeoSelector(latitude, longitude float64) {
    method Auth (line 207) | func (c *xClient) Auth(auth string) {
    method watch (line 212) | func (c *xClient) watch(ch chan []*KVPair) {
    method selectClient (line 257) | func (c *xClient) selectClient(ctx context.Context, servicePath, servi...
    method getCachedClient (line 308) | func (c *xClient) getCachedClient(k string, servicePath, serviceMethod...
    method setCachedClient (line 365) | func (c *xClient) setCachedClient(client RPCClient, k, servicePath, se...
    method findCachedClient (line 375) | func (c *xClient) findCachedClient(k, servicePath, serviceMethod strin...
    method deleteCachedClient (line 384) | func (c *xClient) deleteCachedClient(client RPCClient, k, servicePath,...
    method removeClient (line 398) | func (c *xClient) removeClient(k, servicePath, serviceMethod string, c...
    method generateClient (line 417) | func (c *xClient) generateClient(k, servicePath, serviceMethod string)...
    method getCachedClientWithoutLock (line 443) | func (c *xClient) getCachedClientWithoutLock(k, servicePath, serviceMe...
    method Go (line 505) | func (c *xClient) Go(ctx context.Context, serviceMethod string, args i...
    method Call (line 542) | func (c *xClient) Call(ctx context.Context, serviceMethod string, args...
    method Oneshot (line 707) | func (c *xClient) Oneshot(ctx context.Context, serviceMethod string, a...
    method SendRaw (line 768) | func (c *xClient) SendRaw(ctx context.Context, r *protocol.Message) (m...
    method wrapCall (line 877) | func (c *xClient) wrapCall(ctx context.Context, client RPCClient, serv...
    method wrapSendRaw (line 905) | func (c *xClient) wrapSendRaw(ctx context.Context, client RPCClient, r...
    method Broadcast (line 933) | func (c *xClient) Broadcast(ctx context.Context, serviceMethod string,...
    method Fork (line 1032) | func (c *xClient) Fork(ctx context.Context, serviceMethod string, args...
    method Inform (line 1135) | func (c *xClient) Inform(ctx context.Context, serviceMethod string, ar...
    method SendFile (line 1252) | func (c *xClient) SendFile(ctx context.Context, fileName string, rateI...
    method DownloadFile (line 1333) | func (c *xClient) DownloadFile(ctx context.Context, requestFileName st...
    method Close (line 1389) | func (c *xClient) Close() error {
    method Stream (line 1419) | func (c *xClient) Stream(ctx context.Context, meta map[string]string) ...
  function NewXClient (line 115) | func NewXClient(servicePath string, failMode FailMode, selectMode Select...
  function NewBidirectionalXClient (line 153) | func NewBidirectionalXClient(servicePath string, failMode FailMode, sele...
  function filterByStateAndGroup (line 233) | func filterByStateAndGroup(group string, servers map[string]string) {
  function safeCloseClient (line 296) | func safeCloseClient(client RPCClient) {
  function splitNetworkAndAddress (line 480) | func splitNetworkAndAddress(server string) (string, string) {
  function setServerTimeout (line 489) | func setServerTimeout(ctx context.Context) context.Context {
  function uncoverError (line 740) | func uncoverError(err error) bool {
  function contextCanceled (line 756) | func contextCanceled(err error) bool {

FILE: client/xclient_pool.go
  type XClientPool (line 13) | type XClientPool struct
    method Auth (line 70) | func (c *XClientPool) Auth(auth string) {
    method SetPlugins (line 80) | func (p *XClientPool) SetPlugins(plugins PluginContainer) {
    method GetPlugins (line 90) | func (p *XClientPool) GetPlugins() PluginContainer {
    method Get (line 97) | func (p *XClientPool) Get() XClient {
    method Close (line 105) | func (p *XClientPool) Close() {
  function NewXClientPool (line 31) | func NewXClientPool(count int, servicePath string, failMode FailMode, se...
  function NewBidirectionalXClientPool (line 50) | func NewBidirectionalXClientPool(count int, servicePath string, failMode...

FILE: client/xclient_pool_test.go
  type testPlugin (line 8) | type testPlugin struct
  function TestXClientPool_SetPlugins (line 12) | func TestXClientPool_SetPlugins(t *testing.T) {
  function TestXClientPool_GetPlugins (line 55) | func TestXClientPool_GetPlugins(t *testing.T) {
  function TestXClientPool_SetPlugins_Concurrent (line 100) | func TestXClientPool_SetPlugins_Concurrent(t *testing.T) {

FILE: client/xclient_test.go
  function TestXClient_Thrift (line 17) | func TestXClient_Thrift(t *testing.T) {
  function TestXClient_IT (line 61) | func TestXClient_IT(t *testing.T) {
  function TestXClient_filterByStateAndGroup (line 95) | func TestXClient_filterByStateAndGroup(t *testing.T) {
  function TestUncoverError (line 118) | func TestUncoverError(t *testing.T) {

FILE: clientplugin/req_rate_limiting_redis.go
  type RedisRateLimitingPlugin (line 16) | type RedisRateLimitingPlugin struct
    method PreCall (line 43) | func (plugin *RedisRateLimitingPlugin) PreCall(ctx context.Context, se...
  function NewRedisRateLimitingPlugin (line 23) | func NewRedisRateLimitingPlugin(addrs []string, rate int, burst int, per...

FILE: codec/codec.go
  type Codec (line 20) | type Codec interface
  type ByteCodec (line 26) | type ByteCodec struct
    method Encode (line 29) | func (c ByteCodec) Encode(i interface{}) ([]byte, error) {
    method Decode (line 41) | func (c ByteCodec) Decode(data []byte, i interface{}) error {
  type JSONCodec (line 47) | type JSONCodec struct
    method Encode (line 50) | func (c JSONCodec) Encode(i interface{}) ([]byte, error) {
    method Decode (line 55) | func (c JSONCodec) Decode(data []byte, i interface{}) error {
  type PBCodec (line 62) | type PBCodec struct
    method Encode (line 65) | func (c PBCodec) Encode(i interface{}) ([]byte, error) {
    method Decode (line 78) | func (c PBCodec) Decode(data []byte, i interface{}) error {
  type MsgpackCodec (line 91) | type MsgpackCodec struct
    method Encode (line 94) | func (c MsgpackCodec) Encode(i interface{}) ([]byte, error) {
    method Decode (line 106) | func (c MsgpackCodec) Decode(data []byte, i interface{}) error {
  type ThriftCodec (line 117) | type ThriftCodec struct
    method Encode (line 119) | func (c ThriftCodec) Encode(i interface{}) ([]byte, error) {
    method Decode (line 134) | func (c ThriftCodec) Decode(data []byte, i interface{}) error {

FILE: codec/codec_test.go
  type ColorGroup (line 9) | type ColorGroup struct
  function BenchmarkJSONCodec_Encode (line 21) | func BenchmarkJSONCodec_Encode(b *testing.B) {
  function BenchmarkPBCodec_Encode (line 32) | func BenchmarkPBCodec_Encode(b *testing.B) {
  function BenchmarkMsgpackCodec_Encode (line 47) | func BenchmarkMsgpackCodec_Encode(b *testing.B) {
  function BenchmarkThriftCodec_Encode (line 58) | func BenchmarkThriftCodec_Encode(b *testing.B) {
  function BenchmarkJSONCodec_Decode (line 75) | func BenchmarkJSONCodec_Decode(b *testing.B) {
  function BenchmarkPBCodec_Decode (line 86) | func BenchmarkPBCodec_Decode(b *testing.B) {
  function BenchmarkMsgpackCodec_Decode (line 97) | func BenchmarkMsgpackCodec_Decode(b *testing.B) {
  function BenchmarkThriftCodec_Decode (line 108) | func BenchmarkThriftCodec_Decode(b *testing.B) {

FILE: codec/testdata/protobuf.pb.go
  constant _ (line 18) | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
  constant _ (line 20) | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
  type ProtoColorGroup (line 23) | type ProtoColorGroup struct
    method Reset (line 33) | func (x *ProtoColorGroup) Reset() {
    method String (line 42) | func (x *ProtoColorGroup) String() string {
    method ProtoMessage (line 46) | func (*ProtoColorGroup) ProtoMessage() {}
    method ProtoReflect (line 48) | func (x *ProtoColorGroup) ProtoReflect() protoreflect.Message {
    method Descriptor (line 61) | func (*ProtoColorGroup) Descriptor() ([]byte, []int) {
    method GetId (line 65) | func (x *ProtoColorGroup) GetId() int32 {
    method GetName (line 72) | func (x *ProtoColorGroup) GetName() string {
    method GetColors (line 79) | func (x *ProtoColorGroup) GetColors() []string {
  function file_protobuf_proto_rawDescGZIP (line 104) | func file_protobuf_proto_rawDescGZIP() []byte {
  function init (line 123) | func init() { file_protobuf_proto_init() }
  function file_protobuf_proto_init (line 124) | func file_protobuf_proto_init() {

FILE: codec/testdata/thrift_colorgroup-consts.go
  function init (line 21) | func init() {

FILE: codec/testdata/thrift_colorgroup.go
  type ThriftColorGroup (line 24) | type ThriftColorGroup struct
    method GetID (line 35) | func (p *ThriftColorGroup) GetID() int32 {
    method GetName (line 39) | func (p *ThriftColorGroup) GetName() string {
    method GetColors (line 43) | func (p *ThriftColorGroup) GetColors() []string {
    method Read (line 46) | func (p *ThriftColorGroup) Read(ctx context.Context, iprot thrift.TPro...
    method ReadField1 (line 104) | func (p *ThriftColorGroup)  ReadField1(ctx context.Context, iprot thri...
    method ReadField2 (line 113) | func (p *ThriftColorGroup)  ReadField2(ctx context.Context, iprot thri...
    method ReadField3 (line 122) | func (p *ThriftColorGroup)  ReadField3(ctx context.Context, iprot thri...
    method Write (line 144) | func (p *ThriftColorGroup) Write(ctx context.Context, oprot thrift.TPr...
    method writeField1 (line 159) | func (p *ThriftColorGroup) writeField1(ctx context.Context, oprot thri...
    method writeField2 (line 169) | func (p *ThriftColorGroup) writeField2(ctx context.Context, oprot thri...
    method writeField3 (line 179) | func (p *ThriftColorGroup) writeField3(ctx context.Context, oprot thri...
    method Equals (line 197) | func (p *ThriftColorGroup) Equals(other *ThriftColorGroup) bool {
    method String (line 213) | func (p *ThriftColorGroup) String() string {
  function NewThriftColorGroup (line 30) | func NewThriftColorGroup() *ThriftColorGroup {

FILE: errors/error.go
  type MultiError (line 9) | type MultiError struct
    method Error (line 15) | func (e *MultiError) Error() string {
    method Append (line 19) | func (e *MultiError) Append(err error) {
    method ErrorOrNil (line 25) | func (e *MultiError) ErrorOrNil() error {
  function NewMultiError (line 33) | func NewMultiError(errors []error) *MultiError {

FILE: errors/error_test.go
  function TestNewMultiError (line 11) | func TestNewMultiError(t *testing.T) {
  function TestMultiError_Append (line 20) | func TestMultiError_Append(t *testing.T) {
  function TestMultiError_Error (line 28) | func TestMultiError_Error(t *testing.T) {
  function TestMultiError_ErrorOrNil (line 36) | func TestMultiError_ErrorOrNil(t *testing.T) {

FILE: log/default_logger.go
  type Level (line 17) | type Level
  constant LvPanic (line 21) | LvPanic Level = iota
  constant LvFatal (line 22) | LvFatal
  constant LvError (line 23) | LvError
  constant LvWarn (line 24) | LvWarn
  constant LvInfo (line 25) | LvInfo
  constant LvDebug (line 26) | LvDebug
  constant LvMax (line 28) | LvMax
  type outputFn (line 31) | type outputFn
  function dropOutput (line 33) | func dropOutput(calldepth int, s string) error {
  type defaultLogger (line 37) | type defaultLogger struct
    method Debug (line 70) | func (l *defaultLogger) Debug(v ...interface{}) {
    method Debugf (line 74) | func (l *defaultLogger) Debugf(format string, v ...interface{}) {
    method Info (line 78) | func (l *defaultLogger) Info(v ...interface{}) {
    method Infof (line 82) | func (l *defaultLogger) Infof(format string, v ...interface{}) {
    method Warn (line 86) | func (l *defaultLogger) Warn(v ...interface{}) {
    method Warnf (line 90) | func (l *defaultLogger) Warnf(format string, v ...interface{}) {
    method Error (line 94) | func (l *defaultLogger) Error(v ...interface{}) {
    method Errorf (line 98) | func (l *defaultLogger) Errorf(format string, v ...interface{}) {
    method Fatal (line 102) | func (l *defaultLogger) Fatal(v ...interface{}) {
    method Fatalf (line 107) | func (l *defaultLogger) Fatalf(format string, v ...interface{}) {
    method Panic (line 112) | func (l *defaultLogger) Panic(v ...interface{}) {
    method Panicf (line 116) | func (l *defaultLogger) Panicf(format string, v ...interface{}) {
  function NewDefaultLogger (line 42) | func NewDefaultLogger(out io.Writer, prefix string, flag int, lv Level) ...
  function header (line 120) | func header(lvl, msg string) string {

FILE: log/dummy_logger.go
  type dummyLogger (line 3) | type dummyLogger struct
    method Debug (line 5) | func (l *dummyLogger) Debug(v ...interface{}) {
    method Debugf (line 8) | func (l *dummyLogger) Debugf(format string, v ...interface{}) {
    method Info (line 11) | func (l *dummyLogger) Info(v ...interface{}) {
    method Infof (line 14) | func (l *dummyLogger) Infof(format string, v ...interface{}) {
    method Warn (line 17) | func (l *dummyLogger) Warn(v ...interface{}) {
    method Warnf (line 20) | func (l *dummyLogger) Warnf(format string, v ...interface{}) {
    method Error (line 23) | func (l *dummyLogger) Error(v ...interface{}) {
    method Errorf (line 26) | func (l *dummyLogger) Errorf(format string, v ...interface{}) {
    method Fatal (line 29) | func (l *dummyLogger) Fatal(v ...interface{}) {
    method Fatalf (line 32) | func (l *dummyLogger) Fatalf(format string, v ...interface{}) {
    method Panic (line 35) | func (l *dummyLogger) Panic(v ...interface{}) {
    method Panicf (line 38) | func (l *dummyLogger) Panicf(format string, v ...interface{}) {

FILE: log/logger.go
  constant calldepth (line 9) | calldepth = 3
  type Logger (line 14) | type Logger interface
  function SetLogger (line 34) | func SetLogger(logger Logger) {
  function GetLogger (line 38) | func GetLogger() Logger {
  function SetDummyLogger (line 42) | func SetDummyLogger() {
  function Debug (line 46) | func Debug(v ...interface{}) {
  function Debugf (line 49) | func Debugf(format string, v ...interface{}) {
  function Info (line 53) | func Info(v ...interface{}) {
  function Infof (line 56) | func Infof(format string, v ...interface{}) {
  function Warn (line 60) | func Warn(v ...interface{}) {
  function Warnf (line 63) | func Warnf(format string, v ...interface{}) {
  function Error (line 67) | func Error(v ...interface{}) {
  function Errorf (line 70) | func Errorf(format string, v ...interface{}) {
  function Fatal (line 74) | func Fatal(v ...interface{}) {
  function Fatalf (line 77) | func Fatalf(format string, v ...interface{}) {
  function Panic (line 81) | func Panic(v ...interface{}) {
  function Panicf (line 84) | func Panicf(format string, v ...interface{}) {

FILE: protocol/compressor.go
  type Compressor (line 12) | type Compressor interface
  type GzipCompressor (line 18) | type GzipCompressor struct
    method Zip (line 21) | func (c GzipCompressor) Zip(data []byte) ([]byte, error) {
    method Unzip (line 25) | func (c GzipCompressor) Unzip(data []byte) ([]byte, error) {
  type RawDataCompressor (line 29) | type RawDataCompressor struct
    method Zip (line 32) | func (c RawDataCompressor) Zip(data []byte) ([]byte, error) {
    method Unzip (line 36) | func (c RawDataCompressor) Unzip(data []byte) ([]byte, error) {
  type SnappyCompressor (line 41) | type SnappyCompressor struct
    method Zip (line 44) | func (c *SnappyCompressor) Zip(data []byte) ([]byte, error) {
    method Unzip (line 64) | func (c *SnappyCompressor) Unzip(data []byte) ([]byte, error) {

FILE: protocol/compressor_test.go
  function newBenchmarkMessage (line 11) | func newBenchmarkMessage() *testdata.BenchmarkMessage {
  function BenchmarkGzipCompressor_Zip (line 38) | func BenchmarkGzipCompressor_Zip(b *testing.B) {
  function BenchmarkRawDataCompressor_Zip (line 50) | func BenchmarkRawDataCompressor_Zip(b *testing.B) {
  function BenchmarkSnappyCompressor_Zip (line 62) | func BenchmarkSnappyCompressor_Zip(b *testing.B) {
  function BenchmarkGzipCompressor_Unzip (line 74) | func BenchmarkGzipCompressor_Unzip(b *testing.B) {
  function BenchmarkRawDataCompressor_Unzip (line 87) | func BenchmarkRawDataCompressor_Unzip(b *testing.B) {
  function BenchmarkSnappyCompressor_Unzip (line 100) | func BenchmarkSnappyCompressor_Unzip(b *testing.B) {

FILE: protocol/message.go
  constant magicNumber (line 29) | magicNumber byte = 0x08
  function MagicNumber (line 32) | func MagicNumber() byte {
  constant ServiceError (line 47) | ServiceError = "__rpcx_error__"
  type MessageType (line 51) | type MessageType
  constant Request (line 55) | Request MessageType = iota
  constant Response (line 57) | Response
  type MessageStatusType (line 61) | type MessageStatusType
  constant Normal (line 65) | Normal MessageStatusType = iota
  constant Error (line 67) | Error
  type CompressType (line 71) | type CompressType
  constant None (line 75) | None CompressType = iota
  constant Gzip (line 77) | Gzip
  type SerializeType (line 81) | type SerializeType
  constant SerializeNone (line 85) | SerializeNone SerializeType = iota
  constant JSON (line 87) | JSON
  constant ProtoBuffer (line 89) | ProtoBuffer
  constant MsgPack (line 91) | MsgPack
  constant Thrift (line 94) | Thrift
  type Message (line 98) | type Message struct
    method Clone (line 216) | func (m Message) Clone() *Message {
    method Encode (line 227) | func (m Message) Encode() []byte {
    method EncodeSlicePointer (line 233) | func (m Message) EncodeSlicePointer() *[]byte {
    method WriteTo (line 291) | func (m Message) WriteTo(w io.Writer) (int64, error) {
    method Decode (line 416) | func (m *Message) Decode(r io.Reader) error {
    method Reset (line 513) | func (m *Message) Reset() {
  function NewMessage (line 108) | func NewMessage() *Message {
  type Header (line 119) | type Header
    method CheckMagicNumber (line 122) | func (h Header) CheckMagicNumber() bool {
    method Version (line 127) | func (h Header) Version() byte {
    method SetVersion (line 132) | func (h *Header) SetVersion(v byte) {
    method MessageType (line 137) | func (h Header) MessageType() MessageType {
    method SetMessageType (line 142) | func (h *Header) SetMessageType(mt MessageType) {
    method IsHeartbeat (line 147) | func (h Header) IsHeartbeat() bool {
    method SetHeartbeat (line 152) | func (h *Header) SetHeartbeat(hb bool) {
    method IsOneway (line 162) | func (h Header) IsOneway() bool {
    method SetOneway (line 167) | func (h *Header) SetOneway(oneway bool) {
    method CompressType (line 176) | func (h Header) CompressType() CompressType {
    method SetCompressType (line 181) | func (h *Header) SetCompressType(ct CompressType) {
    method MessageStatusType (line 186) | func (h Header) MessageStatusType() MessageStatusType {
    method SetMessageStatusType (line 191) | func (h *Header) SetMessageStatusType(mt MessageStatusType) {
    method SerializeType (line 196) | func (h Header) SerializeType() SerializeType {
    method SetSerializeType (line 201) | func (h *Header) SetSerializeType(st SerializeType) {
    method Seq (line 206) | func (h Header) Seq() uint64 {
    method SetSeq (line 211) | func (h *Header) SetSeq(seq uint64) {
  function PutData (line 286) | func PutData(data *[]byte) {
  function encodeMetadata (line 362) | func encodeMetadata(m map[string]string, bb *bytes.Buffer) {
  function decodeMetadata (line 377) | func decodeMetadata(l uint32, data []byte) (map[string]string, error) {
  function Read (line 406) | func Read(r io.Reader) (*Message, error) {
  function resetHeader (line 527) | func resetHeader(h *Header) {

FILE: protocol/message_chan_test.go
  function TestChanValue (line 11) | func TestChanValue(t *testing.T) {
  function TestChanPtr (line 35) | func TestChanPtr(t *testing.T) {
  function BenchmarkChanValue (line 60) | func BenchmarkChanValue(b *testing.B) {
  function BenchmarkChanPtr (line 78) | func BenchmarkChanPtr(b *testing.B) {

FILE: protocol/message_test.go
  function TestMessage (line 8) | func TestMessage(t *testing.T) {

FILE: protocol/testdata/benchmark.pb.go
  constant _ (line 18) | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
  constant _ (line 20) | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
  type BenchmarkMessage (line 23) | type BenchmarkMessage struct
    method Reset (line 70) | func (x *BenchmarkMessage) Reset() {
    method String (line 79) | func (x *BenchmarkMessage) String() string {
    method ProtoMessage (line 83) | func (*BenchmarkMessage) ProtoMessage() {}
    method ProtoReflect (line 85) | func (x *BenchmarkMessage) ProtoReflect() protoreflect.Message {
    method Descriptor (line 98) | func (*BenchmarkMessage) Descriptor() ([]byte, []int) {
    method GetField1 (line 102) | func (x *BenchmarkMessage) GetField1() string {
    method GetField9 (line 109) | func (x *BenchmarkMessage) GetField9() string {
    method GetField18 (line 116) | func (x *BenchmarkMessage) GetField18() string {
    method GetField80 (line 123) | func (x *BenchmarkMessage) GetField80() bool {
    method GetField81 (line 130) | func (x *BenchmarkMessage) GetField81() bool {
    method GetField2 (line 137) | func (x *BenchmarkMessage) GetField2() int32 {
    method GetField3 (line 144) | func (x *BenchmarkMessage) GetField3() int32 {
    method GetField280 (line 151) | func (x *BenchmarkMessage) GetField280() int32 {
    method GetField6 (line 158) | func (x *BenchmarkMessage) GetField6() int32 {
    method GetField22 (line 165) | func (x *BenchmarkMessage) GetField22() int64 {
    method GetField4 (line 172) | func (x *BenchmarkMessage) GetField4() string {
    method GetField5 (line 179) | func (x *BenchmarkMessage) GetField5() []uint64 {
    method GetField59 (line 186) | func (x *BenchmarkMessage) GetField59() bool {
    method GetField7 (line 193) | func (x *BenchmarkMessage) GetField7() string {
    method GetField16 (line 200) | func (x *BenchmarkMessage) GetField16() int32 {
    method GetField130 (line 207) | func (x *BenchmarkMessage) GetField130() int32 {
    method GetField12 (line 214) | func (x *BenchmarkMessage) GetField12() bool {
    method GetField17 (line 221) | func (x *BenchmarkMessage) GetField17() bool {
    method GetField13 (line 228) | func (x *BenchmarkMessage) GetField13() bool {
    method GetField14 (line 235) | func (x *BenchmarkMessage) GetField14() bool {
    method GetField104 (line 242) | func (x *BenchmarkMessage) GetField104() int32 {
    method GetField100 (line 249) | func (x *BenchmarkMessage) GetField100() int32 {
    method GetField101 (line 256) | func (x *BenchmarkMessage) GetField101() int32 {
    method GetField102 (line 263) | func (x *BenchmarkMessage) GetField102() string {
    method GetField103 (line 270) | func (x *BenchmarkMessage) GetField103() string {
    method GetField29 (line 277) | func (x *BenchmarkMessage) GetField29() int32 {
    method GetField30 (line 284) | func (x *BenchmarkMessage) GetField30() bool {
    method GetField60 (line 291) | func (x *BenchmarkMessage) GetField60() int32 {
    method GetField271 (line 298) | func (x *BenchmarkMessage) GetField271() int32 {
    method GetField272 (line 305) | func (x *BenchmarkMessage) GetField272() int32 {
    method GetField150 (line 312) | func (x *BenchmarkMessage) GetField150() int32 {
    method GetField23 (line 319) | func (x *BenchmarkMessage) GetField23() int32 {
    method GetField24 (line 326) | func (x *BenchmarkMessage) GetField24() bool {
    method GetField25 (line 333) | func (x *BenchmarkMessage) GetField25() int32 {
    method GetField78 (line 340) | func (x *BenchmarkMessage) GetField78() bool {
    method GetField67 (line 347) | func (x *BenchmarkMessage) GetField67() int32 {
    method GetField68 (line 354) | func (x *BenchmarkMessage) GetField68() int32 {
    method GetField128 (line 361) | func (x *BenchmarkMessage) GetField128() int32 {
    method GetField129 (line 368) | func (x *BenchmarkMessage) GetField129() string {
    method GetField131 (line 375) | func (x *BenchmarkMessage) GetField131() int32 {
  function file_benchmark_proto_rawDescGZIP (line 463) | func file_benchmark_proto_rawDescGZIP() []byte {
  function init (line 482) | func init() { file_benchmark_proto_init() }
  function file_benchmark_proto_init (line 483) | func file_benchmark_proto_init() {

FILE: reflection/server_reflection.go
  type ServiceInfo (line 24) | type ServiceInfo struct
    method String (line 52) | func (si ServiceInfo) String() string {
  type MethodInfo (line 31) | type MethodInfo struct
  type Reflection (line 59) | type Reflection struct
    method Register (line 69) | func (r *Reflection) Register(name string, rcvr interface{}, metadata ...
    method Unregister (line 146) | func (r *Reflection) Unregister(name string) error {
    method GetService (line 151) | func (r *Reflection) GetService(ctx context.Context, s string, reply *...
    method GetServices (line 161) | func (r *Reflection) GetServices(ctx context.Context, s string, reply ...
  function New (line 63) | func New() *Reflection {
  function generateTypeDefination (line 182) | func generateTypeDefination(name, pkg string, jsonValue string) string {
  function generateJSON (line 207) | func generateJSON(typ reflect.Type) string {
  function isExported (line 214) | func isExported(name string) bool {
  function isExportedOrBuiltinType (line 219) | func isExportedOrBuiltinType(t reflect.Type) bool {

FILE: reflection/server_reflection_test.go
  type PBArith (line 13) | type PBArith
    method Mul (line 15) | func (t *PBArith) Mul(ctx context.Context, args *testutils.ProtoArgs, ...
  function TestReflection_Register (line 20) | func TestReflection_Register(t *testing.T) {
  type Args (line 31) | type Args struct
  function Test_generateJSON (line 37) | func Test_generateJSON(t *testing.T) {

FILE: server/context.go
  type Context (line 12) | type Context struct
    method Get (line 26) | func (ctx *Context) Get(key interface{}) interface{} {
    method SetValue (line 31) | func (ctx *Context) SetValue(key, val interface{}) {
    method DeleteKey (line 39) | func (ctx *Context) DeleteKey(key interface{}) {
    method Payload (line 47) | func (ctx *Context) Payload() []byte {
    method Metadata (line 52) | func (ctx *Context) Metadata() map[string]string {
    method ServicePath (line 57) | func (ctx *Context) ServicePath() string {
    method ServiceMethod (line 62) | func (ctx *Context) ServiceMethod() string {
    method Bind (line 67) | func (ctx *Context) Bind(v interface{}) error {
    method Write (line 83) | func (ctx *Context) Write(v interface{}) error {
    method WriteError (line 140) | func (ctx *Context) WriteError(err error) error {
  function NewContext (line 21) | func NewContext(ctx *share.Context, conn net.Conn, req *protocol.Message...

FILE: server/converter.go
  constant XVersion (line 14) | XVersion           = "X-RPCX-Version"
  constant XMessageType (line 15) | XMessageType       = "X-RPCX-MessageType"
  constant XHeartbeat (line 16) | XHeartbeat         = "X-RPCX-Heartbeat"
  constant XOneway (line 17) | XOneway            = "X-RPCX-Oneway"
  constant XMessageStatusType (line 18) | XMessageStatusType = "X-RPCX-MessageStatusType"
  constant XSerializeType (line 19) | XSerializeType     = "X-RPCX-SerializeType"
  constant XCompressType (line 20) | XCompressType      = "X-RPCX-CompressType"
  constant XMessageID (line 21) | XMessageID         = "X-RPCX-MessageID"
  constant XServicePath (line 22) | XServicePath       = "X-RPCX-ServicePath"
  constant XServiceMethod (line 23) | XServiceMethod     = "X-RPCX-ServiceMethod"
  constant XMeta (line 24) | XMeta              = "X-RPCX-Meta"
  constant XErrorMessage (line 25) | XErrorMessage      = "X-RPCX-ErrorMessage"
  function HTTPRequest2RpcxRequest (line 29) | func HTTPRequest2RpcxRequest(r *http.Request) (*protocol.Message, error) {

FILE: server/converter_test.go
  function TestHTTPRequest2RpcxRequest (line 13) | func TestHTTPRequest2RpcxRequest(t *testing.T) {

FILE: server/file_transfer.go
  type FileTransferHandler (line 17) | type FileTransferHandler
  type DownloadFileHandler (line 20) | type DownloadFileHandler
  type tokenInfo (line 22) | type tokenInfo struct
  type downloadTokenInfo (line 27) | type downloadTokenInfo struct
  type FileTransfer (line 35) | type FileTransfer struct
    method Start (line 123) | func (s *FileTransfer) Start() error {
    method start (line 131) | func (s *FileTransfer) start() error {
    method Stop (line 210) | func (s *FileTransfer) Stop() error {
  type FileTransferService (line 49) | type FileTransferService struct
    method TransferFile (line 80) | func (s *FileTransferService) TransferFile(ctx context.Context, args *...
    method DownloadFile (line 101) | func (s *FileTransferService) DownloadFile(ctx context.Context, args *...
  function NewFileTransfer (line 54) | func NewFileTransfer(addr string, handler FileTransferHandler, downloadF...
  method EnableFileTransfer (line 72) | func (s *Server) EnableFileTransfer(serviceName string, fileTransfer *Fi...

FILE: server/gateway.go
  method startGateway (line 23) | func (s *Server) startGateway(network string, ln net.Listener) net.Liste...
  function http1Path (line 53) | func http1Path(prefix string) cmux.Matcher {
  function parseRequestLine (line 82) | func parseRequestLine(line string) (method, uri, proto string, ok bool) {
  function http1HeaderExist (line 92) | func http1HeaderExist(name string) cmux.Matcher {
  function matchHTTP1Field (line 105) | func matchHTTP1Field(r io.Reader, name string, matches func(string) bool...
  function rpcxPrefixByteMatcher (line 114) | func rpcxPrefixByteMatcher() cmux.Matcher {
  method startHTTP1APIGateway (line 123) | func (s *Server) startHTTP1APIGateway(ln net.Listener) {
  method closeHTTP1APIGateway (line 151) | func (s *Server) closeHTTP1APIGateway(ctx context.Context) error {
  method handleGatewayRequest (line 161) | func (s *Server) handleGatewayRequest(w http.ResponseWriter, r *http.Req...

FILE: server/jsonrpc2.go
  method jsonrpcHandler (line 20) | func (s *Server) jsonrpcHandler(w http.ResponseWriter, r *http.Request) {
  method handleJSONRPCRequest (line 54) | func (s *Server) handleJSONRPCRequest(ctx context.Context, r *jsonrpcReq...
  function writeResponse (line 140) | func writeResponse(w http.ResponseWriter, res *jsonrpcRespone) {
  function AllowAllCORSOptions (line 154) | func AllowAllCORSOptions() *CORSOptions {
  method SetCORS (line 178) | func (s *Server) SetCORS(options *CORSOptions) {
  method startJSONRPC2 (line 182) | func (s *Server) startJSONRPC2(ln net.Listener) {
  method closeJSONRPC2 (line 208) | func (s *Server) closeJSONRPC2(ctx context.Context) error {

FILE: server/jsonrpc2_wire.go
  constant CodeUnknownJSONRPCError (line 18) | CodeUnknownJSONRPCError = -32001
  constant CodeParseJSONRPCError (line 20) | CodeParseJSONRPCError = -32700
  constant CodeInvalidjsonrpcRequest (line 22) | CodeInvalidjsonrpcRequest = -32600
  constant CodeMethodNotFound (line 25) | CodeMethodNotFound = -32601
  constant CodeInvalidParams (line 28) | CodeInvalidParams = -32602
  constant CodeInternalJSONRPCError (line 30) | CodeInternalJSONRPCError = -32603
  type jsonrpcRequest (line 34) | type jsonrpcRequest struct
    method IsNotify (line 87) | func (r *jsonrpcRequest) IsNotify() bool {
  type jsonrpcRespone (line 51) | type jsonrpcRespone struct
  type JSONRPCError (line 63) | type JSONRPCError struct
    method JSONRPCError (line 91) | func (err *JSONRPCError) JSONRPCError() string {
  type VersionTag (line 76) | type VersionTag struct
    method MarshalJSON (line 98) | func (VersionTag) MarshalJSON() ([]byte, error) {
    method UnmarshalJSON (line 102) | func (VersionTag) UnmarshalJSON(data []byte) error {
  type ID (line 81) | type ID struct
    method String (line 116) | func (id *ID) String() string {
    method MarshalJSON (line 126) | func (id *ID) MarshalJSON() ([]byte, error) {
    method UnmarshalJSON (line 133) | func (id *ID) UnmarshalJSON(data []byte) error {

FILE: server/kcp.go
  function init (line 12) | func init() {
  function kcpMakeListener (line 16) | func kcpMakeListener(s *Server, address string) (ln net.Listener, err er...
  function WithBlockCrypt (line 25) | func WithBlockCrypt(bc kcp.BlockCrypt) OptionFn {

FILE: server/listener.go
  function init (line 12) | func init() {
  function RegisterMakeListener (line 22) | func RegisterMakeListener(network string, ml MakeListener) {
  type MakeListener (line 27) | type MakeListener
  method makeListener (line 31) | func (s *Server) makeListener(network, address string) (ln net.Listener,...
  function tcpMakeListener (line 44) | func tcpMakeListener(network string) MakeListener {

FILE: server/listener_linux.go
  function init (line 17) | func init() {
  function iouringMakeListener (line 24) | func iouringMakeListener(s *Server, address string) (ln net.Listener, er...
  type uringnetListener (line 48) | type uringnetListener struct
    method Close (line 53) | func (cl *uringnetListener) Close() error {
  type uringLogger (line 60) | type uringLogger struct
    method Log (line 64) | func (l *uringLogger) Log(keyvals ...interface{}) {

FILE: server/listener_rdma.go
  function init (line 14) | func init() {
  function rdmaMakeListener (line 18) | func rdmaMakeListener(s *Server, address string) (ln net.Listener, err e...

FILE: server/listener_unix.go
  function init (line 12) | func init() {
  function reuseportMakeListener (line 17) | func reuseportMakeListener(s *Server, address string) (ln net.Listener, ...
  function unixMakeListener (line 28) | func unixMakeListener(s *Server, address string) (ln net.Listener, err e...

FILE: server/memconn.go
  function init (line 9) | func init() {
  function memconnMakeListener (line 13) | func memconnMakeListener(s *Server, address string) (ln net.Listener, er...

FILE: server/option.go
  type OptionFn (line 11) | type OptionFn
  function WithTLSConfig (line 23) | func WithTLSConfig(cfg *tls.Config) OptionFn {
  function WithReadTimeout (line 30) | func WithReadTimeout(readTimeout time.Duration) OptionFn {
  function WithWriteTimeout (line 37) | func WithWriteTimeout(writeTimeout time.Duration) OptionFn {
  function WithPool (line 44) | func WithPool(maxWorkers, maxCapacity int, options ...pond.Option) Optio...
  function WithCustomPool (line 51) | func WithCustomPool(pool WorkerPool) OptionFn {
  function WithAsyncWrite (line 58) | func WithAsyncWrite() OptionFn {

FILE: server/option_test.go
  function TestOption (line 11) | func TestOption(t *testing.T) {

FILE: server/options.go
  function WithTCPKeepAlivePeriod (line 6) | func WithTCPKeepAlivePeriod(period time.Duration) OptionFn {

FILE: server/plugin.go
  type PluginContainer (line 16) | type PluginContainer interface
  type Plugin (line 48) | type Plugin interface
  type RegisterPlugin (line 52) | type RegisterPlugin interface
  type RegisterFunctionPlugin (line 58) | type RegisterFunctionPlugin interface
  type PostConnAcceptPlugin (line 65) | type PostConnAcceptPlugin interface
  type PostConnClosePlugin (line 70) | type PostConnClosePlugin interface
  type PreReadRequestPlugin (line 75) | type PreReadRequestPlugin interface
  type PostReadRequestPlugin (line 80) | type PostReadRequestPlugin interface
  type PostHTTPRequestPlugin (line 85) | type PostHTTPRequestPlugin interface
  type PreHandleRequestPlugin (line 90) | type PreHandleRequestPlugin interface
  type PreCallPlugin (line 94) | type PreCallPlugin interface
  type PostCallPlugin (line 98) | type PostCallPlugin interface
  type PreWriteResponsePlugin (line 103) | type PreWriteResponsePlugin interface
  type PostWriteResponsePlugin (line 108) | type PostWriteResponsePlugin interface
  type PreWriteRequestPlugin (line 113) | type PreWriteRequestPlugin interface
  type PostWriteRequestPlugin (line 118) | type PostWriteRequestPlugin interface
  type HeartbeatPlugin (line 123) | type HeartbeatPlugin interface
  type CMuxPlugin (line 127) | type CMuxPlugin interface
  type pluginContainer (line 133) | type pluginContainer struct
    method Add (line 138) | func (p *pluginContainer) Add(plugin Plugin) {
    method Remove (line 143) | func (p *pluginContainer) Remove(plugin Plugin) {
    method All (line 158) | func (p *pluginContainer) All() []Plugin {
    method DoRegister (line 163) | func (p *pluginContainer) DoRegister(name string, rcvr interface{}, me...
    method DoRegisterFunction (line 181) | func (p *pluginContainer) DoRegisterFunction(serviceName, fname string...
    method DoUnregister (line 199) | func (p *pluginContainer) DoUnregister(name string) error {
    method DoPostConnAccept (line 217) | func (p *pluginContainer) DoPostConnAccept(conn net.Conn) (net.Conn, b...
    method DoPostConnClose (line 232) | func (p *pluginContainer) DoPostConnClose(conn net.Conn) bool {
    method DoPreReadRequest (line 246) | func (p *pluginContainer) DoPreReadRequest(ctx context.Context) error {
    method DoPostReadRequest (line 260) | func (p *pluginContainer) DoPostReadRequest(ctx context.Context, r *pr...
    method DoPostHTTPRequest (line 274) | func (p *pluginContainer) DoPostHTTPRequest(ctx context.Context, r *ht...
    method DoPreHandleRequest (line 288) | func (p *pluginContainer) DoPreHandleRequest(ctx context.Context, r *p...
    method DoPreCall (line 302) | func (p *pluginContainer) DoPreCall(ctx context.Context, serviceName, ...
    method DoPostCall (line 317) | func (p *pluginContainer) DoPostCall(ctx context.Context, serviceName,...
    method DoPreWriteResponse (line 332) | func (p *pluginContainer) DoPreWriteResponse(ctx context.Context, req ...
    method DoPostWriteResponse (line 346) | func (p *pluginContainer) DoPostWriteResponse(ctx context.Context, req...
    method DoPreWriteRequest (line 360) | func (p *pluginContainer) DoPreWriteRequest(ctx context.Context) error {
    method DoPostWriteRequest (line 374) | func (p *pluginContainer) DoPostWriteRequest(ctx context.Context, r *p...
    method DoHeartbeatRequest (line 388) | func (p *pluginContainer) DoHeartbeatRequest(ctx context.Context, r *p...
    method MuxMatch (line 402) | func (p *pluginContainer) MuxMatch(m cmux.CMux) {

FILE: server/plugin_test.go
  type HeartbeatHandler (line 14) | type HeartbeatHandler struct
    method HeartbeatRequest (line 16) | func (h *HeartbeatHandler) HeartbeatRequest(ctx context.Context, req *...
  function TestPluginHeartbeat (line 23) | func TestPluginHeartbeat(t *testing.T) {

FILE: server/pool.go
  type Reset (line 9) | type Reset interface
  type typePools (line 28) | type typePools struct
    method Init (line 34) | func (p *typePools) Init(t reflect.Type) {
    method Put (line 44) | func (p *typePools) Put(t reflect.Type, x interface{}) {
    method Get (line 55) | func (p *typePools) Get(t reflect.Type) interface{} {

FILE: server/pool_test.go
  type Elem (line 10) | type Elem struct
    method Reset (line 15) | func (e Elem) Reset() {
  function TestPool (line 19) | func TestPool(t *testing.T) {

FILE: server/quic.go
  function init (line 13) | func init() {
  function quicMakeListener (line 17) | func quicMakeListener(s *Server, address string) (ln net.Listener, err e...

FILE: server/router.go
  method UpdateHandler (line 5) | func (s *Server) UpdateHandler(router map[string]Handler) {

FILE: server/server.go
  constant ReaderBuffsize (line 38) | ReaderBuffsize = 1024
  constant WriterBuffsize (line 40) | WriterBuffsize = 1024
  type contextKey (line 48) | type contextKey struct
    method String (line 52) | func (k *contextKey) String() string { return "rpcx context value " + ...
  type Handler (line 69) | type Handler
  type WorkerPool (line 71) | type WorkerPool interface
  type Server (line 79) | type Server struct
    method Address (line 158) | func (s *Server) Address() net.Addr {
    method AddHandler (line 167) | func (s *Server) AddHandler(servicePath, serviceMethod string, handler...
    method ActiveClientConn (line 172) | func (s *Server) ActiveClientConn() []net.Conn {
    method SendMessage (line 189) | func (s *Server) SendMessage(conn net.Conn, servicePath, serviceMethod...
    method getDoneChan (line 214) | func (s *Server) getDoneChan() <-chan struct{} {
    method Serve (line 220) | func (s *Server) Serve(network, address string) (err error) {
    method ServeListener (line 247) | func (s *Server) ServeListener(network string, ln net.Listener) (err e...
    method serveListener (line 264) | func (s *Server) serveListener(ln net.Listener) error {
    method serveByHTTP (line 332) | func (s *Server) serveByHTTP(ln net.Listener, rpcPath string) {
    method serveByWS (line 345) | func (s *Server) serveByWS(ln net.Listener, rpcPath string) {
    method sendResponse (line 358) | func (s *Server) sendResponse(ctx *share.Context, conn net.Conn, err e...
    method serveConn (line 394) | func (s *Server) serveConn(conn net.Conn) {
    method processOneRequest (line 525) | func (s *Server) processOneRequest(ctx *share.Context, req *protocol.M...
    method isShutdown (line 644) | func (s *Server) isShutdown() bool {
    method closeConn (line 648) | func (s *Server) closeConn(conn net.Conn) {
    method readRequest (line 658) | func (s *Server) readRequest(ctx context.Context, r io.Reader) (req *p...
    method auth (line 676) | func (s *Server) auth(ctx context.Context, req *protocol.Message) error {
    method handleRequest (line 685) | func (s *Server) handleRequest(ctx context.Context, req *protocol.Mess...
    method handleRequestForFunction (line 783) | func (s *Server) handleRequestForFunction(ctx context.Context, req *pr...
    method handleError (line 855) | func (s *Server) handleError(res *protocol.Message, err error) (*proto...
    method ServeHTTP (line 874) | func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    method ServeWS (line 895) | func (s *Server) ServeWS(conn *websocket.Conn) {
    method Close (line 905) | func (s *Server) Close() error {
    method RegisterOnShutdown (line 929) | func (s *Server) RegisterOnShutdown(f func(s *Server)) {
    method RegisterOnRestart (line 936) | func (s *Server) RegisterOnRestart(f func(s *Server)) {
    method Shutdown (line 951) | func (s *Server) Shutdown(ctx context.Context) error {
    method Restart (line 1024) | func (s *Server) Restart(ctx context.Context) error {
    method startProcess (line 1036) | func (s *Server) startProcess() (int, error) {
    method checkProcessMsg (line 1059) | func (s *Server) checkProcessMsg() bool {
    method closeDoneChanLocked (line 1065) | func (s *Server) closeDoneChanLocked() {
  function NewServer (line 134) | func NewServer(options ...OptionFn) *Server {
  function parseServerTimeout (line 624) | func parseServerTimeout(ctx *share.Context, req *protocol.Message) conte...
  function validIP4 (line 1078) | func validIP4(ipAddress string) bool {
  function validIP6 (line 1086) | func validIP6(ipAddress string) bool {

FILE: server/server_test.go
  type Args (line 18) | type Args struct
  type Reply (line 23) | type Reply struct
  type Arith (line 27) | type Arith
    method Mul (line 29) | func (t *Arith) Mul(ctx context.Context, args *Args, reply *Reply) err...
    method ThriftMul (line 34) | func (t *Arith) ThriftMul(ctx context.Context, args *testutils.ThriftA...
    method ConsumingOperation (line 39) | func (t *Arith) ConsumingOperation(ctx context.Context, args *testutil...
  function TestShutdownHook (line 45) | func TestShutdownHook(t *testing.T) {
  function TestHandleRequest (line 66) | func TestHandleRequest(t *testing.T) {
  function TestHandler (line 122) | func TestHandler(t *testing.T) {
  function Test_validIP6 (line 182) | func Test_validIP6(t *testing.T) {

FILE: server/service.go
  type RpcServiceInternalError (line 19) | type RpcServiceInternalError struct
    method Error (line 27) | func (e RpcServiceInternalError) Error() string {
    method String (line 32) | func (e RpcServiceInternalError) String() string {
  type methodType (line 43) | type methodType struct
  type functionType (line 51) | type functionType struct
  type service (line 58) | type service struct
    method call (line 371) | func (s *service) call(ctx context.Context, mtype *methodType, argv, r...
    method callForFunction (line 400) | func (s *service) callForFunction(ctx context.Context, ft *functionTyp...
  function isExported (line 66) | func isExported(name string) bool {
  function isExportedOrBuiltinType (line 71) | func isExportedOrBuiltinType(t reflect.Type) bool {
  method ListServices (line 80) | func (s *Server) ListServices() []string {
  method Register (line 101) | func (s *Server) Register(rcvr interface{}, metadata string) error {
  method RegisterName (line 111) | func (s *Server) RegisterName(name string, rcvr interface{}, metadata st...
  method RegisterFunction (line 128) | func (s *Server) RegisterFunction(servicePath string, fn interface{}, me...
  method RegisterFunctionName (line 139) | func (s *Server) RegisterFunctionName(servicePath string, name string, f...
  method register (line 148) | func (s *Server) register(rcvr interface{}, name string, useName bool) (...
  method registerFunction (line 191) | func (s *Server) registerFunction(servicePath string, fn interface{}, na...
  function suitableMethods (line 271) | func suitableMethods(typ reflect.Type, reportErr bool) map[string]*metho...
  method UnregisterAll (line 345) | func (s *Server) UnregisterAll() error {
  method unregisterAll (line 354) | func (s *Server) unregisterAll() error {

FILE: server/service_test.go
  function Test_isExported (line 11) | func Test_isExported(t *testing.T) {
  function Mul (line 27) | func Mul(ctx context.Context, args *Args, reply *Reply) error {
  function Test_isExportedOrBuiltinType (line 32) | func Test_isExportedOrBuiltinType(t *testing.T) {

FILE: server/stream.go
  type StreamHandler (line 20) | type StreamHandler
  type StreamAcceptor (line 24) | type StreamAcceptor
  type streamTokenInfo (line 26) | type streamTokenInfo struct
  type StreamService (line 34) | type StreamService struct
    method Stream (line 70) | func (s *StreamService) Stream(ctx context.Context, args *share.Stream...
    method Start (line 95) | func (s *StreamService) Start() error {
    method start (line 103) | func (s *StreamService) start() error {
    method Stop (line 176) | func (s *StreamService) Stop() error {
  function NewStreamService (line 48) | func NewStreamService(addr string, streamHandler StreamHandler, acceptor...
  method EnableStreamService (line 62) | func (s *Server) EnableStreamService(serviceName string, streamService *...

FILE: serverplugin/alias.go
  type aliasPair (line 11) | type aliasPair struct
  type AliasPlugin (line 16) | type AliasPlugin struct
    method Alias (line 23) | func (p *AliasPlugin) Alias(aliasServicePath, aliasServiceMethod strin...
    method PostReadRequest (line 43) | func (p *AliasPlugin) PostReadRequest(ctx context.Context, r *protocol...
    method PreWriteResponse (line 62) | func (p *AliasPlugin) PreWriteResponse(ctx context.Context, r *protoco...
  function NewAliasPlugin (line 35) | func NewAliasPlugin() *AliasPlugin {

FILE: serverplugin/blacklist.go
  type BlacklistPlugin (line 6) | type BlacklistPlugin struct
    method HandleConnAccept (line 12) | func (plugin *BlacklistPlugin) HandleConnAccept(conn net.Conn) (net.Co...

FILE: serverplugin/mdns.go
  type serviceMeta (line 19) | type serviceMeta struct
  type MDNSRegisterPlugin (line 26) | type MDNSRegisterPlugin struct
    method Start (line 60) | func (p *MDNSRegisterPlugin) Start() error {
    method Stop (line 110) | func (p *MDNSRegisterPlugin) Stop() error {
    method initMDNS (line 118) | func (p *MDNSRegisterPlugin) initMDNS() {
    method HandleConnAccept (line 145) | func (p *MDNSRegisterPlugin) HandleConnAccept(conn net.Conn) (net.Conn...
    method PreCall (line 153) | func (p *MDNSRegisterPlugin) PreCall(_ context.Context, _, _ string, a...
    method Register (line 162) | func (p *MDNSRegisterPlugin) Register(name string, rcvr interface{}, m...
    method RegisterFunction (line 187) | func (p *MDNSRegisterPlugin) RegisterFunction(serviceName, fname strin...
    method Unregister (line 191) | func (p *MDNSRegisterPlugin) Unregister(name string) (err error) {
  function NewMDNSRegisterPlugin (line 44) | func NewMDNSRegisterPlugin(serviceAddress string, port int, m metrics.Re...

FILE: serverplugin/metrics.go
  type MetricsPlugin (line 19) | type MetricsPlugin struct
    method withPrefix (line 29) | func (p *MetricsPlugin) withPrefix(m string) string {
    method Register (line 34) | func (p *MetricsPlugin) Register(name string, rcvr interface{}, metada...
    method HandleConnAccept (line 41) | func (p *MetricsPlugin) HandleConnAccept(conn net.Conn) (net.Conn, boo...
    method PreReadRequest (line 48) | func (p *MetricsPlugin) PreReadRequest(ctx context.Context) error {
    method PostReadRequest (line 53) | func (p *MetricsPlugin) PostReadRequest(ctx context.Context, r *protoc...
    method PostWriteResponse (line 66) | func (p *MetricsPlugin) PostWriteResponse(ctx context.Context, req *pr...
    method Log (line 94) | func (p *MetricsPlugin) Log(freq time.Duration, l metrics.Logger) {
    method Graphite (line 102) | func (p *MetricsPlugin) Graphite(freq time.Duration, prefix string, ad...
    method Exp (line 125) | func (p *MetricsPlugin) Exp() {
  function NewMetricsPlugin (line 25) | func NewMetricsPlugin(registry metrics.Registry) *MetricsPlugin {

FILE: serverplugin/rate_limiting.go
  type RateLimitingPlugin (line 11) | type RateLimitingPlugin struct
    method HandleConnAccept (line 28) | func (plugin *RateLimitingPlugin) HandleConnAccept(conn net.Conn) (net...
  function NewRateLimitingPlugin (line 18) | func NewRateLimitingPlugin(fillInterval time.Duration, capacity int64) *...

FILE: serverplugin/redis.go
  function init (line 20) | func init() {
  type RedisRegisterPlugin (line 25) | type RedisRegisterPlugin struct
    method Start (line 47) | func (p *RedisRegisterPlugin) Start() error {
    method Stop (line 124) | func (p *RedisRegisterPlugin) Stop() error {
    method HandleConnAccept (line 154) | func (p *RedisRegisterPlugin) HandleConnAccept(conn net.Conn) (net.Con...
    method PreCall (line 162) | func (p *RedisRegisterPlugin) PreCall(_ context.Context, _, _ string, ...
    method Register (line 171) | func (p *RedisRegisterPlugin) Register(name string, rcvr interface{}, ...
    method Unregister (line 218) | func (p *RedisRegisterPlugin) Unregister(name string) (err error) {

FILE: serverplugin/registry_test.go
  type Args (line 5) | type Args struct
  type Reply (line 10) | type Reply struct
  type Arith (line 14) | type Arith
    method Mul (line 16) | func (t *Arith) Mul(ctx context.Context, args *Args, reply *Reply) err...

FILE: serverplugin/req_rate_limiting.go
  type ReqRateLimitingPlugin (line 13) | type ReqRateLimitingPlugin struct
    method PostReadRequest (line 33) | func (plugin *ReqRateLimitingPlugin) PostReadRequest(ctx context.Conte...
  function NewReqRateLimitingPlugin (line 21) | func NewReqRateLimitingPlugin(fillInterval time.Duration, capacity int64...

FILE: serverplugin/req_rate_limiting_redis.go
  type RedisRateLimitingPlugin (line 16) | type RedisRateLimitingPlugin struct
    method PostReadRequest (line 43) | func (plugin *RedisRateLimitingPlugin) PostReadRequest(ctx context.Con...
  function NewRedisRateLimitingPlugin (line 23) | func NewRedisRateLimitingPlugin(addrs []string, rate int, burst int, per...

FILE: serverplugin/tee.go
  type TeeConnPlugin (line 9) | type TeeConnPlugin struct
    method Update (line 19) | func (plugin *TeeConnPlugin) Update(w io.Writer) {
    method HandleConnAccept (line 24) | func (plugin *TeeConnPlugin) HandleConnAccept(conn net.Conn) (net.Conn...
  function NewTeeConnPlugin (line 13) | func NewTeeConnPlugin(w io.Writer) *TeeConnPlugin {
  type teeConn (line 29) | type teeConn struct
    method Read (line 34) | func (t *teeConn) Read(p []byte) (n int, err error) {

FILE: serverplugin/whitelist.go
  type WhitelistPlugin (line 6) | type WhitelistPlugin struct
    method HandleConnAccept (line 12) | func (plugin *WhitelistPlugin) HandleConnAccept(conn net.Conn) (net.Co...

FILE: share/context.go
  type Context (line 13) | type Context struct
    method Lock (line 29) | func (c *Context) Lock() {
    method Unlock (line 33) | func (c *Context) Unlock() {
    method Value (line 37) | func (c *Context) Value(key interface{}) interface{} {
    method SetValue (line 50) | func (c *Context) SetValue(key, val interface{}) {
    method DeleteKey (line 61) | func (c *Context) DeleteKey(key interface{}) {
    method String (line 71) | func (c *Context) String() string {
  function NewContext (line 19) | func NewContext(ctx context.Context) *Context {
  function WithValue (line 75) | func WithValue(parent context.Context, key, val interface{}) *Context {
  function WithLocalValue (line 88) | func WithLocalValue(ctx *Context, key, val interface{}) *Context {
  function IsShareContext (line 105) | func IsShareContext(ctx context.Context) bool {

FILE: share/context_test.go
  function TestContext (line 15) | func TestContext(t *testing.T) {
  function TestWithValue (line 29) | func TestWithValue(t *testing.T) {
  function TestWithLocalValue (line 34) | func TestWithLocalValue(t *testing.T) {

FILE: share/share.go
  constant DefaultRPCPath (line 10) | DefaultRPCPath = "/_rpcx_"
  constant AuthKey (line 13) | AuthKey = "__AUTH"
  constant ServerAddress (line 16) | ServerAddress = "__ServerAddress"
  constant ServerTimeout (line 19) | ServerTimeout = "__ServerTimeout"
  constant SendFileServiceName (line 22) | SendFileServiceName = "_filetransfer"
  constant StreamServiceName (line 25) | StreamServiceName = "_streamservice"
  constant ContextTagsLock (line 28) | ContextTagsLock = "_tagsLock"
  constant isShareContext (line 30) | isShareContext = "_isShareContext"
  function RegisterCodec (line 48) | func RegisterCodec(t protocol.SerializeType, c codec.Codec) {
  type ContextKey (line 53) | type ContextKey
  type FileTransferArgs (line 62) | type FileTransferArgs struct
    method Clone (line 69) | func (args FileTransferArgs) Clone() *FileTransferArgs {
  type FileTransferReply (line 83) | type FileTransferReply struct
  type DownloadFileArgs (line 89) | type DownloadFileArgs struct
    method Clone (line 95) | func (args DownloadFileArgs) Clone() *DownloadFileArgs {
  type StreamServiceArgs (line 108) | type StreamServiceArgs struct
  type StreamServiceReply (line 113) | type StreamServiceReply struct

FILE: share/share_test.go
  type MockCodec (line 10) | type MockCodec struct
    method Encode (line 12) | func (codec MockCodec) Encode(i interface{}) ([]byte, error) {
    method Decode (line 16) | func (codec MockCodec) Decode(data []byte, i interface{}) error {
  function TestShare (line 20) | func TestShare(t *testing.T) {

FILE: tool/xgen/parser/parser.go
  type Parser (line 14) | type Parser struct
    method Parse (line 80) | func (p *Parser) Parse(fname string, isDir bool) error {
  type visitor (line 21) | type visitor struct
    method Visit (line 33) | func (v *visitor) Visit(n ast.Node) (w ast.Visitor) {
  function isExported (line 28) | func isExported(name string) bool {

FILE: tool/xgen/xgen.go
  function main (line 22) | func main() {
  function generate (line 77) | func generate(parsers []*parser.Parser) error {

FILE: util/buffer_pool.go
  type levelPool (line 8) | type levelPool struct
  function newLevelPool (line 13) | func newLevelPool(size int) *levelPool {
  type LimitedPool (line 25) | type LimitedPool struct
    method findPool (line 50) | func (p *LimitedPool) findPool(size int) *levelPool {
    method findPutPool (line 64) | func (p *LimitedPool) findPutPool(size int) *levelPool {
    method Get (line 82) | func (p *LimitedPool) Get(size int) *[]byte {
    method Put (line 93) | func (p *LimitedPool) Put(b *[]byte) {
  function NewLimitedPool (line 31) | func NewLimitedPool(minSize, maxSize int) *LimitedPool {

FILE: util/buffer_pool_test.go
  function TestLimitedPool_findPool (line 8) | func TestLimitedPool_findPool(t *testing.T) {
  function TestLimitedPool_findPutPool (line 41) | func TestLimitedPool_findPutPool(t *testing.T) {

FILE: util/compress.go
  function init (line 16) | func init() {
  function Unzip (line 29) | func Unzip(data []byte) ([]byte, error) {
  function Zip (line 50) | func Zip(data []byte) ([]byte, error) {

FILE: util/compress_test.go
  function TestZip (line 10) | func TestZip(t *testing.T) {
  function BenchmarkZip (line 28) | func BenchmarkZip(b *testing.B) {
  function BenchmarkUnzip (line 41) | func BenchmarkUnzip(b *testing.B) {
  function oldUnzip (line 59) | func oldUnzip(data []byte) ([]byte, error) {
  function BenchmarkUnzip_Old (line 88) | func BenchmarkUnzip_Old(b *testing.B) {

FILE: util/converter.go
  function SliceByteToString (line 7) | func SliceByteToString(b []byte) string {
  function StringToSliceByte (line 11) | func StringToSliceByte(s string) []byte {
  function CopyMeta (line 17) | func CopyMeta(src, dst map[string]string) {

FILE: util/net.go
  function GetFreePort (line 14) | func GetFreePort() (port int, err error) {
  function ParseRpcxAddress (line 25) | func ParseRpcxAddress(addr string) (network string, ip string, port int,...
  function ConvertMeta2Map (line 44) | func ConvertMeta2Map(meta string) map[string]string {
  function ConvertMap2String (line 62) | func ConvertMap2String(meta map[string]string) string {
  function ExternalIPV4 (line 83) | func ExternalIPV4() (string, error) {
  function ExternalIPV6 (line 121) | func ExternalIPV6() (string, error) {

FILE: util/net_test.go
  function TestGetFreePort (line 9) | func TestGetFreePort(t *testing.T) {
  function BenchmarkGetFreePort_Old (line 38) | func BenchmarkGetFreePort_Old(b *testing.B) {
  function BenchmarkGetFreePort_New (line 44) | func BenchmarkGetFreePort_New(b *testing.B) {
  function TestExternalIPV4 (line 50) | func TestExternalIPV4(t *testing.T) {
  function TestExternalIPV6 (line 62) | func TestExternalIPV6(t *testing.T) {
Condensed preview — 128 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (483K chars).
[
  {
    "path": ".github/workflows/go.yml",
    "chars": 512,
    "preview": "name: Go\n\non:\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\n\njobs:\n\n  build:\n    name: Build"
  },
  {
    "path": ".gitignore",
    "chars": 128,
    "preview": "# \nvendor/\n\n# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736\n.glide/\n.idea\n\ncover.html\n\n"
  },
  {
    "path": ".travis.yml",
    "chars": 357,
    "preview": "language: go\n\ngo:\n  - 1.17.x\n\nenv:\n  - GO111MODULE=on\n\nbefore_script:\n  - rm -f go.sum\n  - go get -tags \"quic kcp\" -v gi"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 4092,
    "preview": "# [rpcx](http://rpcx.io)\n\n## 1.9.0\n- unregister all services on close automatically\n- add PostHTTPRequestPlugin \n- suppo"
  },
  {
    "path": "LICENSE",
    "chars": 556,
    "preview": "Copyright  2017  Chao yuepan\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file "
  },
  {
    "path": "Makefile",
    "chars": 897,
    "preview": "WORKDIR=`pwd`\n\ndefault: build\n\nvet:\n\tgo vet ./...\n\ntools:\n\tgo get github.com/golangci/golangci-lint/cmd/golangci-lint\n\tg"
  },
  {
    "path": "README.md",
    "chars": 9058,
    "preview": "- **stable branch**: v1.7.x\n- **development branch**: master\n\n<a href=\"https://rpcx.io/\"><img height=\"160\" src=\"http://r"
  },
  {
    "path": "_testutils/GoUnusedProtection__.go",
    "chars": 114,
    "preview": "// Code generated by Thrift Compiler (0.14.0). DO NOT EDIT.\r\n\r\npackage testutils\r\n\r\nvar GoUnusedProtection__ int\r\n"
  },
  {
    "path": "_testutils/arith_service.go",
    "chars": 10575,
    "preview": "// Code generated by protoc-gen-gogo.\n// source: arith_service.proto\n// DO NOT EDIT!\n\n/*\n\tPackage client is a generated "
  },
  {
    "path": "_testutils/arith_service.proto",
    "chars": 234,
    "preview": "// protoc --gogofaster_out=. arith_service.proto\n// mv arith_service.pb.go arith_service_test.go \nsyntax = \"proto3\";\n\npa"
  },
  {
    "path": "_testutils/thrift_arith_service-consts.go",
    "chars": 382,
    "preview": "// Code generated by Thrift Compiler (0.14.0). DO NOT EDIT.\r\n\r\npackage testutils\r\n\r\nimport (\r\n\t\"bytes\"\r\n\t\"context\"\r\n\t\"fm"
  },
  {
    "path": "_testutils/thrift_arith_service.go",
    "chars": 7537,
    "preview": "// Code generated by Thrift Compiler (0.14.0). DO NOT EDIT.\r\n\r\npackage testutils\r\n\r\nimport (\r\n\t\"bytes\"\r\n\t\"context\"\r\n\t\"fm"
  },
  {
    "path": "_testutils/thrift_arith_service.thrift",
    "chars": 88,
    "preview": "struct ThriftArgs\n{\n    1: i32 a,\n    2: i32 b,\n}\n\n\nstruct ThriftReply\n{\n    1: i32 c,\n}"
  },
  {
    "path": "client/cache_client_builder.go",
    "chars": 1062,
    "preview": "package client\r\n\r\nimport \"sync\"\r\n\r\n// CacheClientBuilder defines builder interface to generate RPCCient.\r\ntype CacheClie"
  },
  {
    "path": "client/circuit_breaker.go",
    "chars": 1945,
    "preview": "package client\n\nimport (\n\t\"errors\"\n\t\"sync/atomic\"\n\t\"time\"\n)\n\nvar (\n\tErrBreakerOpen    = errors.New(\"breaker open\")\n\tErrB"
  },
  {
    "path": "client/circuit_breaker_test.go",
    "chars": 1389,
    "preview": "package client\n\nimport (\n\t\"errors\"\n\t\"math/rand\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestConsecCircuitBreaker(t *testing.T) {\n\tcou"
  },
  {
    "path": "client/client.go",
    "chars": 21787,
    "preview": "package client\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"strings"
  },
  {
    "path": "client/client_test.go",
    "chars": 5189,
    "preview": "package client\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"math/rand\"\n\t\"net\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\ttestutils \"github.com/smalln"
  },
  {
    "path": "client/connection.go",
    "chars": 4910,
    "preview": "package client\n\nimport (\n\t\"bufio\"\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/smallnes"
  },
  {
    "path": "client/connection_iouring.go",
    "chars": 170,
    "preview": "package client\n\nimport (\n\t\"net\"\n)\n\n// experimental\nfunc newIOUringConn(c *Client, network, address string) (net.Conn, er"
  },
  {
    "path": "client/connection_iouring_test.go",
    "chars": 857,
    "preview": "//go:build linux\n// +build linux\n\npackage client\n\n// func TestXClient_IOUring(t *testing.T) {\n// \ts := server.NewServer("
  },
  {
    "path": "client/connection_kcp.go",
    "chars": 239,
    "preview": "// +build kcp\n\npackage client\n\nimport (\n\t\"net\"\n\n\tkcp \"github.com/xtaci/kcp-go\"\n)\n\nfunc newDirectKCPConn(c *Client, netwo"
  },
  {
    "path": "client/connection_memu.go",
    "chars": 178,
    "preview": "package client\n\nimport (\n\t\"net\"\n\n\t\"github.com/akutz/memconn\"\n)\n\nfunc newMemuConn(c *Client, network, address string) (ne"
  },
  {
    "path": "client/connection_nonkcp.go",
    "chars": 184,
    "preview": "// +build !kcp\n\npackage client\n\nimport (\n\t\"errors\"\n\t\"net\"\n)\n\nfunc newDirectKCPConn(c *Client, network, address string) ("
  },
  {
    "path": "client/connection_nonquic.go",
    "chars": 204,
    "preview": "//go:build !quic\n// +build !quic\n\npackage client\n\nimport (\n\t\"errors\"\n\t\"net\"\n)\n\nfunc newDirectQuicConn(c *Client, network"
  },
  {
    "path": "client/connection_quic.go",
    "chars": 488,
    "preview": "//go:build quic\n// +build quic\n\npackage client\n\nimport (\n\t\"crypto/tls\"\n\t\"net\"\n\n\t\"github.com/quic-go/quic-go\"\n\t\"github.co"
  },
  {
    "path": "client/connection_rdma.go",
    "chars": 348,
    "preview": "//go:build rdma\n// +build rdma\n\npackage client\n\nimport (\n\t\"errors\"\n\t\"net\"\n\n\t\"github.com/smallnest/rsocket\"\n)\n\nfunc init("
  },
  {
    "path": "client/discovery.go",
    "chars": 3295,
    "preview": "package client\n\nimport (\n\t\"encoding/json\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"sync\"\n)\n\n// ServiceDiscoveryFilter can be used to fil"
  },
  {
    "path": "client/dns_discovery.go",
    "chars": 2705,
    "preview": "package client\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"sort\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/smallnest/rpcx/log\"\n)\n\n// DNSDiscovery is b"
  },
  {
    "path": "client/failmode_enumer.go",
    "chars": 1262,
    "preview": "// Code generated by \"enumer -type=FailMode\"; DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"fmt\"\n)\n\nconst _FailModeName = \"Fa"
  },
  {
    "path": "client/geo_utils.go",
    "chars": 542,
    "preview": "package client\n\nimport (\n\t\"math\"\n)\n\n// https://gist.github.com/cdipaolo/d3f8db3848278b49db68\nfunc getDistanceFrom(lat1, "
  },
  {
    "path": "client/hash_utils.go",
    "chars": 1284,
    "preview": "package client\n\nimport (\n\t\"fmt\"\n\t\"hash/fnv\"\n)\n\n// Hash consistently chooses a hash bucket number in the range [0, numBuc"
  },
  {
    "path": "client/mdns_discovery.go",
    "chars": 3998,
    "preview": "package client\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"net/url\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/grandcat/zeroconf\"\n\t\"githu"
  },
  {
    "path": "client/mode.go",
    "chars": 993,
    "preview": "package client\n\n// FailMode decides how clients action when clients fail to invoke services\ntype FailMode int\n\nconst (\n\t"
  },
  {
    "path": "client/multiple_servers_discovery.go",
    "chars": 1971,
    "preview": "package client\n\nimport (\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/smallnest/rpcx/log\"\n)\n\n// MultipleServersDiscovery is a multiple "
  },
  {
    "path": "client/oneclient.go",
    "chars": 8787,
    "preview": "package client\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"sync\"\n\n\t\"github.com/smallnest/rpcx/share\"\n\n\tmultierror \"github"
  },
  {
    "path": "client/oneclient_pool.go",
    "chars": 2841,
    "preview": "package client\n\nimport (\n\t\"sync/atomic\"\n\n\t\"github.com/smallnest/rpcx/protocol\"\n)\n\n// OneClientPool is a oneclient pool w"
  },
  {
    "path": "client/oneclient_pool_test.go",
    "chars": 3313,
    "preview": "package client\n\nimport (\n\t\"testing\"\n)\n\nfunc TestOneClientPool_SetPlugins(t *testing.T) {\n\t// Create a simple discovery\n\t"
  },
  {
    "path": "client/peer2peer_discovery.go",
    "chars": 1045,
    "preview": "package client\n\n// Peer2PeerDiscovery is a peer-to-peer service discovery.\n// It always returns the static server.\ntype "
  },
  {
    "path": "client/ping_utils.go",
    "chars": 2286,
    "preview": "package client\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"strings\"\n\t\"time\"\n\n\tping \"github.com/go-ping/ping\"\n)\n\n// weightedICMPSelecto"
  },
  {
    "path": "client/plugin.go",
    "chars": 6121,
    "preview": "package client\n\nimport (\n\t\"context\"\n\t\"net\"\n\n\t\"github.com/smallnest/rpcx/protocol\"\n)\n\n// pluginContainer implements Plugi"
  },
  {
    "path": "client/selectmode_enumer.go",
    "chars": 1435,
    "preview": "// Code generated by \"enumer -type=SelectMode\"; DO NOT EDIT.\n\npackage client\n\nimport (\n\t\"fmt\"\n)\n\nconst _SelectModeName ="
  },
  {
    "path": "client/selector.go",
    "chars": 7202,
    "preview": "package client\n\nimport (\n\t\"container/ring\"\n\t\"context\"\n\t\"math\"\n\t\"math/rand\"\n\t\"net/url\"\n\t\"sort\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"gith"
  },
  {
    "path": "client/selector_test.go",
    "chars": 4173,
    "preview": "package client\n\nimport (\n\t\"context\"\n\t\"testing\"\n)\n\nfunc Test_consistentHashSelector_Select(t *testing.T) {\n\tservers := ma"
  },
  {
    "path": "client/smooth_weighted_round_robin.go",
    "chars": 173,
    "preview": "package client\n\n// Weighted is a wrapped server with  weight\ntype Weighted struct {\n\tServer          string\n\tWeight     "
  },
  {
    "path": "client/xclient.go",
    "chars": 35189,
    "preview": "package client\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/url\"\n\t\"os\"\n\t\"reflect\"\n\t\"sort\"\n\t\"strings"
  },
  {
    "path": "client/xclient_pool.go",
    "chars": 2973,
    "preview": "package client\n\nimport (\n\t\"sync\"\n\t\"sync/atomic\"\n\n\t\"github.com/smallnest/rpcx/protocol\"\n)\n\n// XClientPool is a xclient po"
  },
  {
    "path": "client/xclient_pool_test.go",
    "chars": 3410,
    "preview": "package client\n\nimport (\n\t\"testing\"\n)\n\n// testPlugin is a simple test plugin implementation\ntype testPlugin struct {\n\tna"
  },
  {
    "path": "client/xclient_test.go",
    "chars": 2990,
    "preview": "package client\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"testing\"\n\t\"time\"\n\n\t\"fmt\"\n\n\ttestutils \"github.com/smallnest/rpcx/_testuti"
  },
  {
    "path": "clientplugin/req_rate_limiting_redis.go",
    "chars": 1266,
    "preview": "package clientplugin\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/go-redis/redis_rate/v10\"\n\t\"github.com/redis/go-redis/v9\""
  },
  {
    "path": "codec/codec.go",
    "chars": 3557,
    "preview": "package codec\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\n\tpb \"google.golang.org/protobu"
  },
  {
    "path": "codec/codec_test.go",
    "chars": 2696,
    "preview": "package codec\n\nimport (\n\t\"testing\"\n\n\t\"github.com/smallnest/rpcx/codec/testdata\"\n)\n\ntype ColorGroup struct {\n\tId     int "
  },
  {
    "path": "codec/testdata/GoUnusedProtection__.go",
    "chars": 116,
    "preview": "// Code generated by Thrift Compiler (0.14.0). DO NOT EDIT.\r\n\r\npackage testdata\r\n\r\nvar GoUnusedProtection__ int;\r\n\r\n"
  },
  {
    "path": "codec/testdata/gen.sh",
    "chars": 243,
    "preview": "# generate .go files from IDL\nprotoc -I.  --go_out=. --go_opt=module=\"testdata\" ./protobuf.proto\n\nthrift -r -out ../ --g"
  },
  {
    "path": "codec/testdata/protobuf.pb.go",
    "chars": 4769,
    "preview": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.33.0\n// \tprotoc        v5.26.0\n// sou"
  },
  {
    "path": "codec/testdata/protobuf.proto",
    "chars": 166,
    "preview": "syntax = \"proto3\";\n\npackage testdata;\n\noption go_package = \"./testdata\";\n\nmessage ProtoColorGroup {\n  int32 id = 1;\n  st"
  },
  {
    "path": "codec/testdata/thrift_colorgroup-consts.go",
    "chars": 384,
    "preview": "// Code generated by Thrift Compiler (0.14.0). DO NOT EDIT.\r\n\r\npackage testdata\r\n\r\nimport(\r\n\t\"bytes\"\r\n\t\"context\"\r\n\t\"fmt\""
  },
  {
    "path": "codec/testdata/thrift_colorgroup.go",
    "chars": 7087,
    "preview": "// Code generated by Thrift Compiler (0.14.0). DO NOT EDIT.\r\n\r\npackage testdata\r\n\r\nimport(\r\n\t\"bytes\"\r\n\t\"context\"\r\n\t\"fmt\""
  },
  {
    "path": "codec/testdata/thrift_colorgroup.thrift",
    "chars": 112,
    "preview": "namespace go testdata\n\nstruct ThriftColorGroup {\n  1: i32 id = 0,\n  2: string name,\n  3: list<string> colors,\n}\n"
  },
  {
    "path": "errors/error.go",
    "chars": 639,
    "preview": "package errors\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n)\n\n// MultiError holds multiple errors\ntype MultiError struct {\n\tErrors []error\n"
  },
  {
    "path": "errors/error_test.go",
    "chars": 1339,
    "preview": "package errors\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestNewMultiError(t "
  },
  {
    "path": "go.mod",
    "chars": 3384,
    "preview": "module github.com/smallnest/rpcx\n\ngo 1.24\n\nrequire (\n\tgithub.com/akutz/memconn v0.1.0\n\tgithub.com/alitto/pond v1.9.2\n\tgi"
  },
  {
    "path": "go.sum",
    "chars": 43292,
    "preview": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ngithub.com/BurntSushi/toml v0.3.1/go."
  },
  {
    "path": "log/default_logger.go",
    "chars": 2919,
    "preview": "package log\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"strconv\"\n\n\t\"github.com/fatih/color\"\n)\n\n// The default logger output at"
  },
  {
    "path": "log/dummy_logger.go",
    "chars": 743,
    "preview": "package log\n\ntype dummyLogger struct{}\n\nfunc (l *dummyLogger) Debug(v ...interface{}) {\n}\n\nfunc (l *dummyLogger) Debugf("
  },
  {
    "path": "log/logger.go",
    "chars": 1431,
    "preview": "package log\n\nimport (\n\t\"log\"\n\t\"os\"\n)\n\nconst (\n\tcalldepth = 3\n)\n\nvar l Logger = NewDefaultLogger(os.Stdout, \"\", log.LstdF"
  },
  {
    "path": "protocol/compressor.go",
    "chars": 1393,
    "preview": "package protocol\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\n\t\"github.com/golang/snappy\"\n\t\"github.com/smallnest/rpcx/util\"\n)\n\n// Compresso"
  },
  {
    "path": "protocol/compressor_test.go",
    "chars": 2756,
    "preview": "package protocol\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/smallnest/rpcx/codec\"\n\t\"github.com/smallnest/rpcx/protoco"
  },
  {
    "path": "protocol/message.go",
    "chars": 11662,
    "preview": "package protocol\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"runtime\"\n\n\t\"github.com/smallnest/rpcx/lo"
  },
  {
    "path": "protocol/message_chan_test.go",
    "chars": 2240,
    "preview": "package protocol\n\nimport (\n    \"fmt\"\n    \"strings\"\n    \"sync/atomic\"\n    \"testing\"\n    \"time\"\n)\n\nfunc TestChanValue(t *t"
  },
  {
    "path": "protocol/message_test.go",
    "chars": 1168,
    "preview": "package protocol\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n)\n\nfunc TestMessage(t *testing.T) {\n\treq := NewMessage()\n\treq.SetVersion("
  },
  {
    "path": "protocol/testdata/benchmark.pb.go",
    "chars": 18002,
    "preview": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.33.0\n// \tprotoc        v5.26.0\n// sou"
  },
  {
    "path": "protocol/testdata/benchmark.proto",
    "chars": 1031,
    "preview": "syntax = \"proto3\";\n\npackage testdata;\n\noption optimize_for = SPEED;\noption go_package = \"./testdata\";\n\n\nmessage Benchmar"
  },
  {
    "path": "protocol/testdata/gen.sh",
    "chars": 196,
    "preview": "# curl -O https://raw.githubusercontent.com/rpcxio/rpcx-benchmark/master/proto/benchmark.proto\n\n# generate .go files fro"
  },
  {
    "path": "reflection/server_reflection.go",
    "chars": 4749,
    "preview": "package reflection\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"strings\"\n\t\"text/template\"\n"
  },
  {
    "path": "reflection/server_reflection_test.go",
    "chars": 943,
    "preview": "package reflection\n\nimport (\n\t\"context\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/kr/pretty\"\n\ttestutils \"github.com/smallnest/"
  },
  {
    "path": "server/context.go",
    "chars": 3663,
    "preview": "package server\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\n\t\"github.com/smallnest/rpcx/protocol\"\n\t\"github.com/smallnest/rpcx/share\"\n)\n\n// C"
  },
  {
    "path": "server/converter.go",
    "chars": 2958,
    "preview": "package server\n\nimport (\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strconv\"\n\n\t\"github.com/smallnest/rpcx/protocol\"\n\t\"github.com/sma"
  },
  {
    "path": "server/converter_test.go",
    "chars": 1080,
    "preview": "package server\n\nimport (\n\t\"bytes\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/smallnest/rpcx/codec\"\n\t\"github.com/smallnest/rpcx"
  },
  {
    "path": "server/file_transfer.go",
    "chars": 4894,
    "preview": "package server\n\nimport (\n\t\"context\"\n\t\"crypto/rand\"\n\t\"io\"\n\t\"net\"\n\t\"sync\"\n\t\"time\"\n\n\tlru \"github.com/hashicorp/golang-lru\"\n"
  },
  {
    "path": "server/gateway.go",
    "chars": 6860,
    "preview": "package server\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\t\"time\"\n\n\t\""
  },
  {
    "path": "server/jsonrpc2.go",
    "chars": 4756,
    "preview": "package server\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\n\t\"github."
  },
  {
    "path": "server/jsonrpc2_wire.go",
    "chars": 4412,
    "preview": "// Copyright 2018 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license "
  },
  {
    "path": "server/kcp.go",
    "chars": 611,
    "preview": "// +build kcp\n\npackage server\n\nimport (\n\t\"errors\"\n\t\"net\"\n\n\tkcp \"github.com/xtaci/kcp-go\"\n)\n\nfunc init() {\n\tmakeListeners"
  },
  {
    "path": "server/listener.go",
    "chars": 1446,
    "preview": "package server\n\nimport (\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n)\n\nvar makeListeners = make(map[string]MakeListener)\n\nfun"
  },
  {
    "path": "server/listener_linux.go",
    "chars": 1407,
    "preview": "//go:build linux\n// +build linux\n\npackage server\n\nimport (\n\t\"net\"\n\t\"runtime\"\n\t\"time\"\n\n\turingnet \"github.com/godzie44/go-"
  },
  {
    "path": "server/listener_rdma.go",
    "chars": 602,
    "preview": "//go:build rdma\n// +build rdma\n\npackage server\n\nimport (\n\t\"net\"\n\t\"os\"\n\t\"strconv\"\n\n\t\"github.com/smallnest/rsocket\"\n)\n\nfun"
  },
  {
    "path": "server/listener_unix.go",
    "chars": 683,
    "preview": "//go:build !windows\n// +build !windows\n\npackage server\n\nimport (\n\t\"net\"\n\n\treuseport \"github.com/kavu/go_reuseport\"\n)\n\nfu"
  },
  {
    "path": "server/memconn.go",
    "chars": 251,
    "preview": "package server\n\nimport (\n\t\"net\"\n\n\t\"github.com/akutz/memconn\"\n)\n\nfunc init() {\n\tmakeListeners[\"memu\"] = memconnMakeListen"
  },
  {
    "path": "server/option.go",
    "chars": 1276,
    "preview": "package server\n\nimport (\n\t\"crypto/tls\"\n\t\"time\"\n\n\t\"github.com/alitto/pond\"\n)\n\n// OptionFn configures options of server.\nt"
  },
  {
    "path": "server/option_test.go",
    "chars": 558,
    "preview": "package server\n\nimport (\n\t\"crypto/tls\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestOption(t *t"
  },
  {
    "path": "server/options.go",
    "chars": 219,
    "preview": "package server\n\nimport \"time\"\n\n// WithTCPKeepAlivePeriod sets tcp keepalive period.\nfunc WithTCPKeepAlivePeriod(period t"
  },
  {
    "path": "server/plugin.go",
    "chars": 10758,
    "preview": "package server\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"net/http\"\n\n\t\"github.com/julienschmidt/httprouter\"\n\t\"github.com/smallnest/rp"
  },
  {
    "path": "server/plugin_test.go",
    "chars": 1767,
    "preview": "package server\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/smallnest/rpcx/client\"\n\t\"github.com/"
  },
  {
    "path": "server/pool.go",
    "chars": 1023,
    "preview": "package server\n\nimport (\n\t\"reflect\"\n\t\"sync\"\n)\n\n// Reset defines Reset method for pooled object.\ntype Reset interface {\n\t"
  },
  {
    "path": "server/pool_test.go",
    "chars": 489,
    "preview": "package server\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\ntype Elem struct {\n\t// magicNum"
  },
  {
    "path": "server/quic.go",
    "chars": 495,
    "preview": "//go:build quic\n// +build quic\n\npackage server\n\nimport (\n\t\"errors\"\n\t\"net\"\n\n\t\"github.com/smallnest/quick\"\n)\n\nfunc init() "
  },
  {
    "path": "server/router.go",
    "chars": 296,
    "preview": "package server\n\n// UpdateHandler 批量更新router\n// 服务器使用plugin热更时,批量替换特定接口\nfunc (s *Server) UpdateHandler(router map[string]"
  },
  {
    "path": "server/server.go",
    "chars": 26979,
    "preview": "package server\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"os\"\n\t\"os/exec\"\n\t\""
  },
  {
    "path": "server/server_test.go",
    "chars": 4284,
    "preview": "package server\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"net\"\n\t\"testing\"\n\t\"time\"\n\n\ttestutils \"github.com/sm"
  },
  {
    "path": "server/service.go",
    "chars": 12142,
    "preview": "package server\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"runtime\"\n\t\"strings\"\n\t\"sync\"\n\t\"unicode\"\n\t\"unicode/utf8\""
  },
  {
    "path": "server/service_test.go",
    "chars": 706,
    "preview": "package server\n\nimport (\n\t\"context\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc Test_isExported"
  },
  {
    "path": "server/stream.go",
    "chars": 4005,
    "preview": "package server\n\nimport (\n\t\"context\"\n\t\"crypto/rand\"\n\t\"errors\"\n\t\"io\"\n\t\"net\"\n\t\"sync\"\n\t\"time\"\n\n\tlru \"github.com/hashicorp/go"
  },
  {
    "path": "serverplugin/alias.go",
    "chars": 2148,
    "preview": "package serverplugin\n\nimport (\n\t\"context\"\n\n\t\"github.com/smallnest/rpcx/protocol\"\n)\n\nvar aliasAppliedKey = \"__aliasApplie"
  },
  {
    "path": "serverplugin/blacklist.go",
    "chars": 705,
    "preview": "package serverplugin\n\nimport \"net\"\n\n// BlacklistPlugin is a plugin that control only ip addresses in blacklist can **NOT"
  },
  {
    "path": "serverplugin/mdns.go",
    "chars": 5063,
    "preview": "package serverplugin\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/url\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings"
  },
  {
    "path": "serverplugin/metrics.go",
    "chars": 4087,
    "preview": "package serverplugin\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"time\"\n\n\t\"github.com/rcrowley/go-metrics\"\n\t\"github.com/rcrowley/go-met"
  },
  {
    "path": "serverplugin/rate_limiting.go",
    "chars": 745,
    "preview": "package serverplugin\n\nimport (\n\t\"net\"\n\t\"time\"\n\n\t\"github.com/juju/ratelimit\"\n)\n\n// RateLimitingPlugin can limit connectin"
  },
  {
    "path": "serverplugin/redis.go",
    "chars": 7026,
    "preview": "package serverplugin\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/url\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\tmetrics \"githu"
  },
  {
    "path": "serverplugin/registry_test.go",
    "chars": 238,
    "preview": "package serverplugin\n\nimport \"context\"\n\ntype Args struct {\n\tA int\n\tB int\n}\n\ntype Reply struct {\n\tC int\n}\n\ntype Arith int"
  },
  {
    "path": "serverplugin/req_rate_limiting.go",
    "chars": 1088,
    "preview": "package serverplugin\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/juju/ratelimit\"\n\t\"github.com/smallnest/rpcx/protocol\"\n\t\""
  },
  {
    "path": "serverplugin/req_rate_limiting_redis.go",
    "chars": 1273,
    "preview": "package serverplugin\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/go-redis/redis_rate/v10\"\n\t\"github.com/redis/go-redis/v9\""
  },
  {
    "path": "serverplugin/tee.go",
    "chars": 872,
    "preview": "package serverplugin\n\nimport (\n\t\"io\"\n\t\"net\"\n)\n\n// TeeConnPlugin is a plugin that copy requests from clients and send to "
  },
  {
    "path": "serverplugin/whitelist.go",
    "chars": 697,
    "preview": "package serverplugin\n\nimport \"net\"\n\n// WhitelistPlugin is a plugin that control only ip addresses in whitelist can acces"
  },
  {
    "path": "share/context.go",
    "chars": 2153,
    "preview": "package share\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"sync\"\n)\n\n// var _ context.Context = &Context{}\n\n// Context is a r"
  },
  {
    "path": "share/context_test.go",
    "chars": 1002,
    "preview": "package share\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nvar (\n\tTheAnswer   = \"Answer to "
  },
  {
    "path": "share/share.go",
    "chars": 3265,
    "preview": "package share\n\nimport (\n\t\"github.com/smallnest/rpcx/codec\"\n\t\"github.com/smallnest/rpcx/protocol\"\n)\n\nconst (\n\t// DefaultR"
  },
  {
    "path": "share/share_test.go",
    "chars": 533,
    "preview": "package share\n\nimport (\n\t\"testing\"\n\n\t\"github.com/smallnest/rpcx/protocol\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\ntype "
  },
  {
    "path": "tool/xgen/README.md",
    "chars": 1044,
    "preview": "# xgen\n\n`xgen` isn a tool that can help you generate a server stub for rpcx services.\n\nIt search structs in your specifi"
  },
  {
    "path": "tool/xgen/parser/parser.go",
    "chars": 1965,
    "preview": "// Package parser parses Go code and keeps track of all the types defined\n// and provides access to all the constants de"
  },
  {
    "path": "tool/xgen/xgen.go",
    "chars": 4743,
    "preview": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"go/build\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/smallnest/rpcx/tool/"
  },
  {
    "path": "util/buffer_pool.go",
    "chars": 1683,
    "preview": "package util\n\nimport (\n\t\"math\"\n\t\"sync\"\n)\n\ntype levelPool struct {\n\tsize int\n\tpool sync.Pool\n}\n\nfunc newLevelPool(size in"
  },
  {
    "path": "util/buffer_pool_test.go",
    "chars": 1320,
    "preview": "package util\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n)\n\nfunc TestLimitedPool_findPool(t *testing.T) {\n\tpool := NewLimitedPool(512, 4"
  },
  {
    "path": "util/compress.go",
    "chars": 1176,
    "preview": "package util\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"io\"\n\t\"sync\"\n)\n\nvar (\n\tspWriter sync.Pool\n\tspReader sync.Pool\n\tspBuffe"
  },
  {
    "path": "util/compress_test.go",
    "chars": 2440,
    "preview": "package util\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"io\"\n\t\"testing\"\n)\n\nfunc TestZip(t *testing.T) {\n\ts := \"%5B%7B%22servic"
  },
  {
    "path": "util/converter.go",
    "chars": 395,
    "preview": "package util\n\nimport (\n\t\"unsafe\"\n)\n\nfunc SliceByteToString(b []byte) string {\n\treturn *(*string)(unsafe.Pointer(&b))\n}\n\n"
  },
  {
    "path": "util/net.go",
    "chars": 3118,
    "preview": "package util\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/url\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// GetFreePort gets a free po"
  },
  {
    "path": "util/net_test.go",
    "chars": 1111,
    "preview": "package util\n\nimport (\n\t\"net\"\n\t\"strconv\"\n\t\"testing\"\n)\n\nfunc TestGetFreePort(t *testing.T) {\n\tfor i := 0; i < 1000; i++ {"
  }
]

About this extraction

This page contains the full source code of the smallnest/rpcx GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 128 files (428.0 KB), approximately 142.6k tokens, and a symbol index with 1003 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!