dist_sender.go
kv/transport.go
rpc/context.go
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资源.