dist_sender.go

func (ds *DistSender) sendToReplicas(
	ctx context.Context,
	ba roachpb.BatchRequest,
	opts SendOptions,
	rangeID roachpb.RangeID,
	replicas ReplicaSlice,
	nodeDialer *nodedialer.Dialer,
	cachedLeaseHolder roachpb.ReplicaDescriptor,
	withCommit bool,
) (*roachpb.BatchResponse, error) {
	...
	br, err := transport.SendNext(ctx, ba)
	...
}

kv/transport.go

// SendNext invokes the specified RPC on the supplied client when the
// client is ready. On success, the reply is sent on the channel;
// otherwise an error is sent.
func (gt *grpcTransport) SendNext(
	ctx context.Context, ba roachpb.BatchRequest,
) (*roachpb.BatchResponse, error) {
  ...
	client := gt.orderedClients[gt.clientIndex]
	ctx, iface, err := gt.NextInternalClient(ctx)
	...
	reply, err := gt.sendBatch(ctx, client.replica.NodeID, iface, ba)
	...
}

rpc/context.go

func (a internalClientAdapter) Batch(
	ctx context.Context, ba *roachpb.BatchRequest, _ ...grpc.CallOption,
) (*roachpb.BatchResponse, error) {
	return a.InternalServer.Batch(ctx, ba)
}

server/node.go

func (n *Node) Batch(
	ctx context.Context, args *roachpb.BatchRequest,
) (*roachpb.BatchResponse, error) {
  ...
	br, err := n.batchInternal(ctx, args)
  ...
}

func (n *Node) batchInternal(
	ctx context.Context, args *roachpb.BatchRequest,
) (*roachpb.BatchResponse, error) {
	...
	  br, pErr = n.stores.Send(ctx, *args)
	...
}

storage/stores.go

func (ls *Stores) Send(
	ctx context.Context, ba roachpb.BatchRequest,
) (*roachpb.BatchResponse, *roachpb.Error) {
	...
	store, err := ls.GetStore(ba.Replica.StoreID)
	...
	br, pErr := store.Send(ctx, ba)
	...
}

storage/store.go

func (s *Store) Send(
	ctx context.Context, ba roachpb.BatchRequest,
) (br *roachpb.BatchResponse, pErr *roachpb.Error) {
 ...
 br, pErr = repl.Send(ctx, ba)
 ...
}

storage/replica.go

func (r *Replica) Send(
	ctx context.Context, ba roachpb.BatchRequest,
) (*roachpb.BatchResponse, *roachpb.Error) {
	return r.sendWithRangeID(ctx, r.RangeID, &ba)
}

判断客户端与Server端是否为同一个节点 rpc/context.go

func IsLocal(iface roachpb.InternalClient) bool {
	_, ok := iface.(internalClientAdapter)
	return ok // internalClientAdapter is used for local connections.
}

直接调用Server端的对应函数.避免rpc走网络传输

func (a internalClientAdapter) Batch(
	ctx context.Context, ba *roachpb.BatchRequest, _ ...grpc.CallOption,
) (*roachpb.BatchResponse, error) {
	return a.InternalServer.Batch(ctx, ba)
}

InternalServer即Node类

context.go

// SetLocalInternalServer sets the context's local internal batch server.
func (ctx *Context) SetLocalInternalServer(internalServer roachpb.InternalServer) {
	ctx.localInternalClient = internalClientAdapter{internalServer}
}

server.go

func (s *Server) Start(ctx context.Context) error {
	...
	s.rpcContext.SetLocalInternalServer(s.node)
	...
}

dist_sender.go

ds.transportFactory = GRPCTransportFactory

transport_regular.go

// GRPCTransportFactory is the default TransportFactory, using GRPC.
func GRPCTransportFactory(
	opts SendOptions, nodeDialer *nodedialer.Dialer, replicas ReplicaSlice,
) (Transport, error) {
	return grpcTransportFactoryImpl(opts, nodeDialer, replicas)
}

transport.go

func grpcTransportFactoryImpl(
	opts SendOptions, nodeDialer *nodedialer.Dialer, replicas ReplicaSlice,
) (Transport, error) {
clients := make([]batchClient, 0, len(replicas))
	for _, replica := range replicas {
		healthy := nodeDialer.ConnHealth(replica.NodeID) == nil    // 核心代码
		clients = append(clients, batchClient{ 
			replica: replica.ReplicaDescriptor,
			healthy: healthy,
		})
	}
}

node_dialer.go

func (n *Dialer) ConnHealth(nodeID roachpb.NodeID) error {
	if n == nil || n.resolver == nil {
		return errors.New("no node dialer configured")
	}
	if !n.getBreaker(nodeID).Ready() {
		return circuit.ErrBreakerOpen
	}
	addr, err := n.resolver(nodeID)
	if err != nil {
		return err
	}
	// TODO(bdarnell): GRPCDial should detect local addresses and return
	// a dummy connection instead of requiring callers to do this check.
	if n.rpcContext.GetLocalInternalClientForAddr(addr.String()) != nil {  // 核心代码,本地
		// The local client is always considered healthy.
		return nil
	}
	conn := n.rpcContext.GRPCDial(addr.String())
	return conn.Health()
}

即客户端与Server端是同一个Node时,不走gRPC调用.

cockroach BatchRequest请求 结论: 1).如果使用gRpc执行请求,无法避免序列化和反序列化.  2). 由于crdb增加了客户端与Server是同一节点的逻辑,不使用gRPC,直接调用node.go中的Batch方法,此时不存在pb的序列化和反序列化.

3). 采用pb进行序列化与反序列化,对于bytes类型编解码存在两次内存复制的操作,因此与使用unsafe.Pointer指针复制C层byte[]相比,更消耗CPU资源.