项目文件夹

文件
2026-07-13 13:29:29 +08:00

492 行
17 KiB
C++

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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.
#include "butil/containers/flat_map.h"
#include "brpc/log.h"
#include "brpc/load_balancer.h"
#include "brpc/details/naming_service_thread.h"
#include "brpc/partition_channel.h"
#include "brpc/global.h"
namespace brpc {
// ================= PartitionChannelBase ====================
// Base of PartitionChannel and DynamicPartitionChannel.
class PartitionChannelBase : public ParallelChannel,
public NamingServiceWatcher {
public:
PartitionChannelBase();
~PartitionChannelBase();
int Init(int num_partition_kinds,
PartitionParser* partition_parser,
const char* load_balancer_name,
const PartitionChannelOptions* options);
int partition_count() const { return channel_count(); }
size_t AddServersInBatch(const std::vector<ServerId>& servers);
size_t RemoveServersInBatch(const std::vector<ServerId>& servers);
private:
bool initialized() const { return _parser != NULL; }
void PartitionServersIntoTemps(const std::vector<ServerId>& servers);
void OnAddedServers(const std::vector<ServerId>& servers);
void OnRemovedServers(const std::vector<ServerId>& servers);
struct SubChannel : public Channel {
SharedLoadBalancer* lb() { return _lb.get(); }
std::vector<ServerId> tmp;
};
SubChannel* _subs;
PartitionParser* _parser;
};
PartitionChannelBase::PartitionChannelBase()
: _subs(NULL)
, _parser(NULL) {
}
PartitionChannelBase::~PartitionChannelBase() {
delete [] _subs;
_subs = NULL;
}
int PartitionChannelBase::Init(int num_partition_kinds,
PartitionParser* partition_parser,
const char* load_balancer_name,
const PartitionChannelOptions* options_in) {
if (num_partition_kinds <= 0) {
LOG(ERROR) << "Parameter[num_partition_kinds] must be positive";
return -1;
}
if (NULL == partition_parser) {
LOG(ERROR) << "Parameter[partition_parser] must be non-NULL";
return -1;
}
PartitionChannelOptions options;
if (options_in) {
options = *options_in;
}
options.succeed_without_server = true;
options.log_succeed_without_server = false;
_subs = new (std::nothrow) SubChannel[num_partition_kinds];
if (NULL == _subs) {
LOG(ERROR) << "Fail to new Channels[" << num_partition_kinds << "]";
return -1;
}
for (int i = 0; i < num_partition_kinds; ++i) {
if (_subs[i].Init("list://", load_balancer_name, &options) != 0) {
LOG(ERROR) << "Fail to init sub channel[" << i << "]";
return -1;
}
}
for (int i = 0; i < num_partition_kinds; ++i) {
if (AddChannel(&_subs[i], DOESNT_OWN_CHANNEL,
options.call_mapper.get(),
options.response_merger.get()) != 0) {
LOG(ERROR) << "Fail to add sub channel[" << i << "]";
return -1;
}
}
ParallelChannelOptions pchan_options;
pchan_options.timeout_ms = options.timeout_ms;
pchan_options.fail_limit = options.fail_limit;
if (ParallelChannel::Init(&pchan_options) != 0) {
LOG(ERROR) << "Fail to init PartitionChannel as ParallelChannel";
return -1;
}
// Must be last one because it's the marker of initialized().
_parser = partition_parser;
return 0;
}
void PartitionChannelBase::PartitionServersIntoTemps(
const std::vector<ServerId>& servers) {
for (int i = 0; i < partition_count(); ++i) {
_subs[i].tmp.clear();
}
for (size_t i = 0; i < servers.size(); ++i) {
Partition part;
if (!_parser->ParseFromTag(servers[i].tag, &part)) {
LOG(ERROR) << "Fail to parse " << servers[i].tag;
continue;
}
if (part.num_partition_kinds != partition_count()) {
// Not belonging to this channel.
continue;
}
if (part.index < 0 || part.index >= partition_count()) {
LOG(ERROR) << "Invalid index=" << part.index << " in tag=`"
<< servers[i].tag << "'";
continue;
}
if (_subs[part.index].tmp.capacity() == 0) {
_subs[part.index].tmp.reserve(16);
}
_subs[part.index].tmp.push_back(servers[i]);
}
}
size_t PartitionChannelBase::AddServersInBatch(
const std::vector<ServerId>& servers) {
PartitionServersIntoTemps(servers);
size_t ntotal = 0;
for (int i = 0; i < partition_count(); ++i) {
if (!_subs[i].tmp.empty()) {
size_t n = _subs[i].lb()->AddServersInBatch(_subs[i].tmp);
ntotal += n;
RPC_VLOG << "Added " << n << " servers to channel[" << i << "]";
}
}
return ntotal;
}
size_t PartitionChannelBase::RemoveServersInBatch(
const std::vector<ServerId>& servers) {
PartitionServersIntoTemps(servers);
size_t ntotal = 0;
for (int i = 0; i < partition_count(); ++i) {
if (!_subs[i].tmp.empty()) {
size_t n = _subs[i].lb()->RemoveServersInBatch(_subs[i].tmp);
ntotal += n;
RPC_VLOG << "Removed " << n << " servers from channel[" << i << "]";
}
}
return ntotal;
}
void PartitionChannelBase::OnAddedServers(
const std::vector<ServerId>& servers) {
AddServersInBatch(servers);
}
void PartitionChannelBase::OnRemovedServers(
const std::vector<ServerId>& servers) {
RemoveServersInBatch(servers);
}
// ================= PartitionChannel ====================
PartitionChannelOptions::PartitionChannelOptions()
: ChannelOptions(), fail_limit(-1) {
}
PartitionChannel::PartitionChannel()
: _pchan(NULL)
, _parser(NULL) {
}
PartitionChannel::~PartitionChannel() {
if (_nsthread_ptr) {
if (_pchan) {
_nsthread_ptr->RemoveWatcher(_pchan);
}
_nsthread_ptr.reset();
}
delete _pchan;
_pchan = NULL;
delete _parser;
_parser = NULL;
}
int PartitionChannel::Init(int num_partition_kinds,
PartitionParser* partition_parser,
const char* ns_url,
const char* load_balancer_name,
const PartitionChannelOptions* options_in) {
// Force naming services to register.
GlobalInitializeOrDie();
if (num_partition_kinds == 0) {
LOG(ERROR) << "Parameter[num_partition_kinds] must be positive";
return -1;
}
if (NULL == partition_parser) {
LOG(ERROR) << "Parameter[partition_parser] must be non-NULL";
return -1;
}
GetNamingServiceThreadOptions ns_opt;
if (options_in) {
ns_opt.succeed_without_server = options_in->succeed_without_server;
}
if (GetNamingServiceThread(&_nsthread_ptr, ns_url, &ns_opt) != 0) {
LOG(ERROR) << "Fail to get NamingServiceThread";
return -1;
}
_pchan = new (std::nothrow) PartitionChannelBase;
if (NULL == _pchan) {
LOG(ERROR) << "Fail to new PartitionChannelBase";
return -1;
}
if (_pchan->Init(num_partition_kinds, partition_parser,
load_balancer_name, options_in) != 0) {
LOG(ERROR) << "Fail to init PartitionChannelBase";
return -1;
}
if (_nsthread_ptr->AddWatcher(
_pchan, (options_in ? options_in->ns_filter : NULL)) != 0) {
LOG(ERROR) << "Fail to add PartitionChannelBase as watcher";
return -1;
}
// Must be last one because it's the marker of initialized().
_parser = partition_parser;
return 0;
}
void PartitionChannel::CallMethod(
const google::protobuf::MethodDescriptor* method,
google::protobuf::RpcController* controller,
const google::protobuf::Message* request,
google::protobuf::Message* response,
google::protobuf::Closure* done) {
if (_pchan != NULL) {
_pchan->CallMethod(method, controller, request, response, done);
} else {
Controller* cntl = static_cast<Controller*>(controller);
cntl->SetFailed(EINVAL, "PartitionChannel=%p is not initialized yet",
this);
// This is a branch only entered by wrongly-used RPC, just call done
// in-place. See comments in channel.cpp on deadlock concerns.
if (done) {
done->Run();
}
}
}
int PartitionChannel::partition_count() const {
return _pchan ? _pchan->partition_count() : 0;
}
int PartitionChannel::CheckHealth() {
if (_pchan == NULL) {
return -1;
}
return static_cast<ChannelBase*>(_pchan)->CheckHealth();
}
// ================= DynamicPartitionChannel ====================
class DynamicPartitionChannel::Partitioner : public NamingServiceWatcher {
public:
void PartitionServersIntoTemps(const std::vector<ServerId>& servers) {
for (PartChanMap::const_iterator it = _part_chan_map.begin();
it != _part_chan_map.end(); ++it) {
it->second->tmp.clear();
}
for (size_t i = 0; i < servers.size(); ++i) {
Partition part;
if (!_parser->ParseFromTag(servers[i].tag, &part)) {
LOG(ERROR) << "Fail to parse " << servers[i].tag;
continue;
}
if (part.num_partition_kinds <= 0) {
LOG(ERROR) << "Invalid num_partition_kinds=" << part.num_partition_kinds
<< " in tag=`" << servers[i].tag << "'";
continue;
}
if (part.index < 0 || part.index >= part.num_partition_kinds) {
LOG(ERROR) << "Invalid index=" << part.index << " in tag=`"
<< servers[i].tag << "'";
continue;
}
SubPartitionChannel** ppchan = _part_chan_map.seek(part.num_partition_kinds);
SubPartitionChannel* pchan = NULL;
if (ppchan == NULL) {
pchan = new (std::nothrow) SubPartitionChannel;
if (pchan == NULL) {
LOG(ERROR) << "Fail to new SubPartitionChannel";
continue;
}
if (pchan->Init(part.num_partition_kinds, _parser,
_load_balancer_name.c_str(), &_options) != 0) {
LOG(ERROR) << "Fail to init SubPartitionChannel=#"
<< part.num_partition_kinds;
delete pchan;
continue;
}
if (_schan->AddChannel(pchan, &pchan->handle) != 0) {
LOG(ERROR) << "Fail to add SubPartitionChannel=#"
<< part.num_partition_kinds;
delete pchan;
continue;
}
_part_chan_map[part.num_partition_kinds] = pchan;
RPC_VLOG << "Added partition=" << part.num_partition_kinds;
} else {
pchan = *ppchan;
CHECK_EQ(part.num_partition_kinds, pchan->partition_count());
}
if (pchan->tmp.capacity() == 0) {
pchan->tmp.reserve(16);
}
pchan->tmp.push_back(servers[i]);
}
}
void OnAddedServers(const std::vector<ServerId>& servers) {
PartitionServersIntoTemps(servers);
for (PartChanMap::const_iterator it = _part_chan_map.begin();
it != _part_chan_map.end(); ++it) {
if (!it->second->tmp.empty()) {
size_t n = it->second->AddServersInBatch(it->second->tmp);
it->second->num_servers += n;
RPC_VLOG << "Added " << n << " servers to partition="
<< it->first;
}
}
}
void OnRemovedServers(const std::vector<ServerId>& servers) {
PartitionServersIntoTemps(servers);
std::vector<int> erased_parts;
for (PartChanMap::const_iterator it = _part_chan_map.begin();
it != _part_chan_map.end(); ++it) {
SubPartitionChannel* partchan = it->second;
if (!partchan->tmp.empty()) {
size_t n = partchan->RemoveServersInBatch(partchan->tmp);
partchan->num_servers -= n;
RPC_VLOG << "Removed " << n << " servers from partition="
<< it->first;
if (partchan->num_servers <= 0) {
CHECK_EQ(0, partchan->num_servers);
const int npart = partchan->partition_count();
_schan->RemoveAndDestroyChannel(partchan->handle);
// NOTE: Don't touch partchan again!
RPC_VLOG << "Removed partition=" << npart;
erased_parts.push_back(it->first);
}
}
}
for (size_t i = 0; i < erased_parts.size(); ++i) {
CHECK_EQ(1UL, _part_chan_map.erase(erased_parts[i]));
}
}
Partitioner()
: _schan(NULL)
, _parser(NULL)
{}
~Partitioner() {
// Do nothing. _schan deletes all sub channels.
}
int Init(SelectiveChannel* schan,
PartitionParser* parser,
const char* load_balancer_name,
const PartitionChannelOptions* options) {
_schan = schan;
_parser = parser;
_load_balancer_name = load_balancer_name;
if (options) {
_options = *options;
}
return 0;
}
private:
struct SubPartitionChannel : public PartitionChannelBase {
SubPartitionChannel() : num_servers(0) {}
int num_servers;
SelectiveChannel::ChannelHandle handle; // uninitialized
std::vector<ServerId> tmp;
};
typedef butil::FlatMap<int, SubPartitionChannel*> PartChanMap;
PartChanMap _part_chan_map;
SelectiveChannel* _schan;
PartitionParser* _parser;
std::string _load_balancer_name;
PartitionChannelOptions _options;
};
DynamicPartitionChannel::DynamicPartitionChannel()
: _partitioner(NULL)
, _parser(NULL) {
}
DynamicPartitionChannel::~DynamicPartitionChannel() {
if (_nsthread_ptr) {
if (_partitioner) {
_nsthread_ptr->RemoveWatcher(_partitioner);
}
_nsthread_ptr.reset();
}
delete _partitioner;
_partitioner = NULL;
delete _parser;
_parser = NULL;
}
int DynamicPartitionChannel::Init(
PartitionParser* partition_parser,
const char* ns_url,
const char* load_balancer_name,
const PartitionChannelOptions* options_in) {
GlobalInitializeOrDie();
if (NULL == partition_parser) {
LOG(ERROR) << "Parameter[partition_parser] must be non-NULL";
return -1;
}
GetNamingServiceThreadOptions ns_opt;
if (options_in) {
ns_opt.succeed_without_server = options_in->succeed_without_server;
}
if (GetNamingServiceThread(&_nsthread_ptr, ns_url, &ns_opt) != 0) {
LOG(ERROR) << "Fail to get NamingServiceThread";
return -1;
}
if (_schan.Init("_dynpart", options_in) != 0) {
LOG(ERROR) << "Fail to init _schan";
return -1;
}
_partitioner = new (std::nothrow) Partitioner;
if (NULL == _partitioner) {
LOG(ERROR) << "Fail to new Partitioner";
return -1;
}
if (_partitioner->Init(&_schan, partition_parser,
load_balancer_name, options_in) != 0) {
LOG(ERROR) << "Fail to init Partitioner";
return -1;
}
if (_nsthread_ptr->AddWatcher(
_partitioner, (options_in ? options_in->ns_filter : NULL)) != 0) {
LOG(ERROR) << "Fail to add Partitioner as watcher";
return -1;
}
// Must be last one because it's the marker of initialized().
_parser = partition_parser;
return 0;
}
void DynamicPartitionChannel::CallMethod(
const google::protobuf::MethodDescriptor* method,
google::protobuf::RpcController* controller,
const google::protobuf::Message* request,
google::protobuf::Message* response,
google::protobuf::Closure* done) {
_schan.CallMethod(method, controller, request, response, done);
}
} // namespace brpc