2021SC@SDUSC BRPC源码分析(十一)Selective Channel

2021SC@SDUSC BRPC源码分析(十一)Selective Channel

class SelectiveChannel : public ChannelBase/*non-copyable*/ {
public:
    typedef SocketId ChannelHandle;

    SelectiveChannel();
    ~SelectiveChannel();

    int Init(const char* load_balancer_name, const ChannelOptions* options);

    int AddChannel(ChannelBase* sub_channel, ChannelHandle* handle);

    void RemoveAndDestroyChannel(ChannelHandle handle);

    void CallMethod(const google::protobuf::MethodDescriptor* method,
                    google::protobuf::RpcController* controller,
                    const google::protobuf::Message* request,
                    google::protobuf::Message* response,
                    google::protobuf::Closure* done);

    bool initialized() const;

    void Describe(std::ostream& os, const DescribeOptions& options) const;

private:
    int CheckHealth();
    
    Channel _chan;
};

}

组合通道将流量分割为子通道,即schan。schan的主要目的是在服务器组之间实现负载平衡。selectivecchannel是一个完整的功能通道:同步和异步RPC。可在异步调用后立即删除。
-可取消call_id(取消所有子调用)
-超时。
由于schan的设计目标,它有一个单独的重试和备份请求层。即当schan无法访问子通道时,它可能重试另一个通道。schan中的子通道共享被访问服务器的信息,并尽可能避免重新尝试被访问的服务器。
当一个schan发送一个备份请求时,它调用一个子通道。由于子通道也可以是组合通道,所以"backup requests"只可能是"backup requests".。
当前选择通道要求requestCallMethod在RPC结束之前是有效的。其他渠道则不然。如果你在用selectivecchannel做异步调用,确保requestdone中被拥有和删除。


int SelectiveChannel::Init(const char* lb_name, const ChannelOptions* options) {    
    GlobalInitializeOrDie();
 
    schan::ChannelBalancer* lb = new (std::nothrow) schan::ChannelBalancer;
    if (NULL == lb) {
        LOG(FATAL) << "Fail to new ChannelBalancer";
        return -1;
    }
    if (lb->Init(lb_name) != 0) {
        LOG(ERROR) << "Fail to init lb";
        delete lb;
        return -1;
    }
    _chan._lb.reset(lb);
    _chan._serialize_request = PassSerializeRequest;
    if (options) {
        _chan._options = *options;
        _chan._options.connection_type = CONNECTION_TYPE_UNKNOWN;
        _chan._options.succeed_without_server = true;
        _chan._options.auth = NULL;
    }
    _chan._options.protocol = PROTOCOL_UNKNOWN;
    return 0;
}
  • 强制命名服务注册。
  • 修改一些字段,使其与schan的行为一致。
    • 在使用前初始化一个schan。’ load_balancer_name’是在brpc/channel.h中列出的负载均衡算法的名称,如果’ options’是NULL,使用默认选项。

int ChannelBalancer::AddChannel(ChannelBase* sub_channel,
                                SelectiveChannel::ChannelHandle* handle) {
    BAIDU_SCOPED_LOCK(_mutex);
    if (_chan_map.find(sub_channel) != _chan_map.end()) {
        LOG(ERROR) << "Duplicated sub_channel=" << sub_channel;
        return -1;
    }
    SubChannel* sub_chan = new (std::nothrow) SubChannel;
    if (sub_chan == NULL) {
        LOG(FATAL) << "Fail to to new SubChannel";
        return -1;
    }
            
    SocketUniquePtr ptr;
    CHECK_EQ(0, Socket::Address(sock_id, &ptr));
    if (!AddServer(ServerId(sock_id))) {
        LOG(ERROR) << "Duplicated sub_channel=" << sub_channel;
        ptr->SetFailed();
        return -1;
    }
    _chan_map[sub_channel]= ptr.release();  
    return 0;
}
  • 回收socket时,sub_chan将被删除。
  • 添加引用。
  • 添加一个子通道,它将与schan一起删除或通过RemoveAndDestroyChannel显式删除。在成功时,handle被设置为移除的键。与pchan不同,schan可以在任何时候添加通道。成功返回0,否则返回-1。

void ChannelBalancer::RemoveAndDestroyChannel(SelectiveChannel::ChannelHandle handle) {
    if (!RemoveServer(ServerId(handle))) {
        return;
    }
    SocketUniquePtr ptr;
    const int rc = Socket::AddressFailedAsWell(handle, &ptr);
    if (rc >= 0) {
        SubChannel* sub = static_cast<SubChannel*>(ptr->user());
        {
            BAIDU_SCOPED_LOCK(_mutex);
            CHECK_EQ(1UL, _chan_map.erase(sub->chan));
        }
        {
            SocketUniquePtr ptr2(ptr.get());
        }
        if (rc == 0) {
            ptr->ReleaseAdditionalReference();
        }
    }
}
  • 获取废弃ptr并且删除并销毁与’ handle’关联的子通道。

bool SelectiveChannel::initialized() const {
    return _chan._lb != NULL;
}

当Init() 运行成功返回true。


void SelectiveChannel::CallMethod(
    const google::protobuf::MethodDescriptor* method,
    google::protobuf::RpcController* controller_base,
    const google::protobuf::Message* request,
    google::protobuf::Message* response,
    google::protobuf::Closure* user_done) {
    Controller* cntl = static_cast<Controller*>(controller_base);
    if (!initialized()) {
        cntl->SetFailed(EINVAL, "SelectiveChannel=%p is not initialized yet",
                        this);
    }
    schan::Sender* sndr = new schan::Sender(cntl, request, response, user_done);
    cntl->_sender = sndr;
    cntl->add_flag(Controller::FLAGS_DESTROY_CID_IN_DONE);
    const CallId cid = cntl->call_id();
    _chan.CallMethod(method, cntl, request, response, sndr);
    if (user_done == NULL) {
        Join(cid);
        cntl->OnRPCEnd(butil::gettimeofday_us());
    }
}

通过子信道发送请求。Schan可能会根据重试或者备份请求设置重试其他子通道。


void SubDone::Run() {
    Controller* main_cntl = NULL;
    const int rc = bthread_id_lock(_cid, (void**)&main_cntl);
    if (rc != 0) {
        LOG(ERROR) << "Fail to lock correlation_id="
                   << _cid.value << ": " << berror(rc);
        return;
    }
    main_cntl->_remote_side = _cntl._remote_side;
    main_cntl->set_connection_type(_cntl.connection_type());
    Resource r;
    r.response = _cntl._response;
    r.sub_done = this;
    if (!_owner->PushFree(r)) {
        return;
    }
    const int saved_error = main_cntl->ErrorCode();
    
    if (_cntl.Failed()) {
        if (_cntl.ErrorCode() == ENODATA || _cntl.ErrorCode() == EHOSTDOWN) {
            Socket::SetFailed(_peer_id);  
        }
        main_cntl->SetFailed(_cntl._error_text);
        main_cntl->_error_code = _cntl._error_code;
    } else {
       ……
       略
       ……
    }

}
  • cid必须是有效的,因为schan在取消所有子调用之前不会dtor。
  • 复制get -but-settable字段,这些字段通常在RPC期间设置以反映细节。
  • connection_type可能在CallMethod中被更改。
  • LB找不到服务器。
  • 触发HC。

int Sender::IssueRPC(int64_t start_realtime_us) {
       ……
       略
       ……

    Resource r = PopFree();
    r.sub_done->_cid = _main_cntl->current_id();
    r.sub_done->_peer_id = sel_out.fake_sock->id();
    Controller* sub_cntl = &r.sub_done->_cntl;
    sub_cntl->_timeout_ms = -1;

    sub_cntl->set_connection_type(_main_cntl->connection_type());
    sub_cntl->set_type_of_service(_main_cntl->_tos);
    sub_cntl->set_request_compress_type(_main_cntl->request_compress_type());
    sub_cntl->set_log_id(_main_cntl->log_id());
    sub_cntl->set_request_code(_main_cntl->request_code());
    sub_cntl->request_attachment().append(_main_cntl->request_attachment());    
       ……
       略
       ……
    return 0;
}
  • 不对超时进行计数。因为已经在schan中管理超时了。如果超时发生,则通过ERPCTIMEDOUT取消子调用。
  • 继承_main_cntl的以下字段。
  • 将请求附件转发到subcall

class ChannelBalancer : public SharedLoadBalancer {
public:
    struct SelectOut {
        SelectOut() : need_feedback(false) {}

        ChannelBase* channel() {
            return static_cast<SubChannel*>(fake_sock->user())->chan;
        }
        
        SocketUniquePtr fake_sock;
        bool need_feedback;
    };
    
    ChannelBalancer() {}
    ~ChannelBalancer();
    int Init(const char* lb_name);
    int AddChannel(ChannelBase* sub_channel,
                   SelectiveChannel::ChannelHandle* handle);
    void RemoveAndDestroyChannel(SelectiveChannel::ChannelHandle handle);
    int SelectChannel(const LoadBalancer::SelectIn& in, SelectOut* out);
    int CheckHealth();
    void Describe(std::ostream& os, const DescribeOptions&);

private:
    butil::Mutex _mutex;
    ChannelToIdMap _chan_map;
};
  • SocketUsers为子通道的假套接字之间的负载均衡。
  • 查找重复的子通道。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值