当前位置: 首页 > article >正文

【brpc学习实践十一】session-local与thread-local应用与brpc抽象工厂模式实践

什么是session-local与thread-local

百度内的检索程序大量地使用了thread-local storage (缩写TLS),有些是为了缓存频繁访问的对象以避免反复创建,有些则是为了在全局函数间隐式地传递状态。你应当尽量避免后者,这样的函数难以测试,不设置thread-local变量甚至无法运行。session-local与thread-local是brpc解决thread-local问题的两种机制,实际上还有一种,可自行查阅官网文档。这两种机制其实就是提供了一个保存rpc请求过程中的用户关注数据的保存、处理接口。

session-local

session-local data与一次server端RPC绑定: 从进入service回调开始,到调用server端的done结束,不管该service是同步还是异步处理。 session-local data会尽量被重用,在server停止前不会被删除。

设置ServerOptions.session_local_data_factory后访问Controller.session_local_data()即可获得session-local数据。若没有设置,Controller.session_local_data()总是返回NULL。

若ServerOptions.reserved_session_local_data大于0,Server会在提供服务前就创建这么多个数据。

struct MySessionLocalData {
   
    MySessionLocalData() : x(123) {
   }
    int x;
};
 
class EchoServiceImpl : public example::EchoService {
   
public:
    ...
    void Echo(google::protobuf::RpcController* cntl_base,
              const example::EchoRequest* request,
              example::EchoResponse* response,
              google::protobuf::Closure* done) {
   
        ...
        brpc::Controller* cntl = static_cast<brpc::Controller*>(cntl_base);
 
        // Get the session-local data which is created by ServerOptions.session_local_data_factory
        // and reused between different RPC.
        MySessionLocalData* sd = static_cast<MySessionLocalData*>(cntl->session_local_data());
        if (sd == NULL) {
   
            cntl->SetFailed("Require ServerOptions.session_local_data_factory to be set with a correctly implemented instance");
            return;
        }
        ...
struct ServerOptions {
   
    ...
    // The factory to create/destroy data attached to each RPC session.
    // If this field is NULL, Controller::session_local_data() is always NULL.
    // NOT owned by Server and must be valid when Server is running.
    // Default: NULL
    const DataFactory* session_local_data_factory;
 
    // Prepare so many session-local data before server starts, so that calls
    // to Controller::session_local_data() get data directly rather than
    // calling session_local_data_factory->Create() at first time. Useful when
    // Create() is slow, otherwise the RPC session may be blocked by the
    // creation of data and not served within timeout.
    // Default: 0
    size_t reserved_session_local_data;
};

session_local_data_factory的类型为DataFactory,我们需要实现其中的CreateData和DestroyData。
brpc的DataFactory源码:

class DataFactory {
   
public:
    virtual ~DataFactory() {
   }

    // Implement this method to create a piece of data.
    // Notice that this method is const.
    // Returns the data, NULL on error.
    virtual void* CreateData() const = 0;

    // Implement this method to destroy a piece of data that was created
    // by Create().
    // Notice that this method is const.
    virtual void DestroyData(void*) const = 0;
};

实际上只是实现了一个抽象工厂的基类,对于产品族、具体工厂类都没有定义,这些是由具体业务去实现的。
注意:CreateData和DestroyData会被多个线程同时调用,**必须线程安全。**通常我们会在继承DataFactory的相关操作中添加锁保障线程安全。后面会有例子讲到。

class MySessionLocalDataFactory : public brpc::DataFactory {
   
public:
    void* CreateData() const {
   
        return new MySessionLocalData;
    }  
    void DestroyData(void* d) const {
   
        delete static_cast<MySessionLocalData*>(d);
    }  
};

MySessionLocalDataFactory g_session_local_data_factory;

int main(int argc, char* argv[]) {
   
    ...
 
    brpc::Server server;
    brpc::ServerOptions options;
    ...
    options.session_local_data_factory = &g_session_local_data_factory;
    ...

我们需要将具体产品类的对象指针传入DestroyData以便brpc能帮我们回收资源,这里的资源回收是无需我们人工干预的,brpc已经帮我们进行管理:
如下源码:
在server start的时候就会根据传入的tls参数来进行启动一个sever bthread,随后在server结束时我们传入的具体session local数据会被抽象DestroyData指针调用destroy掉。

 // Init _keytable_pool always. If the server was stopped before, the pool
    // should be destroyed in Join().
    _keytable_pool = new bthread_keytable_pool_t;
    if (bthread_keytable_pool_init(_keytable_pool) != 0) {
   
        LOG(ERROR) << "Fail to init _keytable_pool";
        delete _keytable_pool;
        _keytable_pool = NULL;
        return -1;
    }

    if (_options.thread_local_data_factory) {
   
        _tl_options.thread_local_data_factory = _options.thread_local_data_factory;
        if (bthread_key_create2(&_tl_options.<

http://www.kler.cn/a/154447.html

相关文章:

  • uniapp自动注册机制:easycom
  • 【网络安全面经】OSI七层模型每层都有什么协议
  • 关于强化学习的一份介绍
  • Python_爬虫3_Requests库网络爬虫实战(5个实例)
  • Java爬虫(HttpURLConnection)详解
  • Python学习从0到1 day26 第三阶段 Spark ④ 数据输出
  • Mybatis 分页查询的三种实现
  • css取消移动端长按元素背景色
  • OCR原理解析
  • Redis面试常见问题
  • Python基础学习快速入门
  • ESP32-Web-Server 实战编程-通过网页控制设备多个 GPIO
  • SpringBoot拦截器、过滤器、自定义注解、监听器、全局异常-使用详解
  • Vue3中定义变量是选择ref还是reactive?
  • 使用KubeSphere练习故障注入
  • SELinux refpolicy详解(4)
  • Layer Normalization是什么
  • Oauth2.0 学习
  • 180天Java从小白到就业-Day03-03Java位运算符、赋值运算符、数据交换的三种方式
  • P1 什么是链表 C语言简单易懂
  • Sql Server数据库跨机器完整恢复(源文件恢复)
  • QPrinter 是 Qt 框架中的一个类,用于与打印机进行交互,并提供打印功能
  • Linux 进程(三)
  • 每日一练:冒泡排序
  • 7、Jenkins+Nexus3+Docker+K8s实现CICD
  • 最小生成树算法