Envoy源码分析之Dispatcher

Dispatcher机制

​ Envoy和Nginx一样都是基于事件驱动的架构,这种架构的核心就是事件循环(EventLoop)。业界目前典型的几种事件循环实现主要有Libevent、Libev、Libuv、Boost.Asio等,也可以完全基于Linux系统调用epoll来实现。Envoy选择在Libevent的基础上进行了封装,实现了自己的事件循环机制,在Envoy中被称为Dispatcher,一个Dispatcher对象就是一个事件分发器,就如同它的名字一样。Dispatcher是Envoy的核心,可以说Envoy中绝大部分的能力都是构建在Dispatcher的基础上。所以理解Dispatcher机制是掌握Envoy的一个很重要的前提。

​ 在Envoy中Dispatcher不仅仅提供了网络事件分发、定时器、信号处理等基本的事件循环能力,还在事件循环的基础上实现任务执行队列、DeferredDelet等,这两个功能为Envoy中很多组件提供了必不可少的基础能力。比如借助DeferredDelet实现了安全的对象析构,通过任务执行队列实现Thread Local机制等等。

Libevent事件封装

​ Envoy在Libevent的基础上进行了封装最为重要的一个原因就是因为Libevent本身是C开发的,很多Libevent暴露出来的结构需要自己来管理内存的分配和释放,这对于现代化的C++来说显然是无法接受的,因此Envoy借助了C++的RAII机制将这些结构封装起来,自动管理内存资源的释放。接下来我们看下Envoy是如何进行封装的。

template <class T, void (*deleter)(T*)>
class CSmartPtr : public std::unique_ptr<T, void (*)(T*)> {
   
public:
  CSmartPtr() : std::unique_ptr<T, void (*)(T*)>(nullptr, deleter) {
   }
  CSmartPtr(T* object) : std::unique_ptr<T, void (*)(T*)>(object, deleter) {
   }
};

​ Envoy通过继承unique_ptr自定义了一个CSmartPtr,通过继承拥有了unqiue_ptr自动管理内存释放的能力,离开作用域后自动释放内存。借助CSmartPtr,Envoy将Libevent中的event_base包装成BasePtr,将evconnlistener包装成ListenerPtr。其中event_base就是事件循环,一个event_base就是一个事件循环,可以拥有多个事件循环,Envoy内部就是每一个worker线程都会有一个事件循环,也就是最常见的one loop per thread模型。

using BasePtr = CSmartPtr<event_base, event_base_free>;
using ListenerPtr = CSmartPtr<evconnlistener, evconnlistener_free>;

​ 在Libevent中无论是定时器到期、收到信号、还是文件可读写等都是事件,统一使用event类型来表示,Envoy中则将event作为ImplBase的成员,然后让所有的事件类型的对象都继承ImplBase,从而实现了事件的抽象。同时也借助了RAII机制自动实现了事件资源的释放。

class ImplBase {
   
protected:
  ~ImplBase();
	
  event raw_event_;
};

ImplBase::~ImplBase() {
   
  // Derived classes are assumed to have already assigned the raw event in the constructor.
  event_del(&raw_event_);
}

​ 通过继承ImplBase基类可以拥有event事件成员,但是每一种事件表现出的具体行为是不一样的,比如说信号事件,需要有信号注册的能力,定时器事件则需要可以开启或者关闭定时的能力,文件事件则需要能够开启某些事件状态的监听。为此Envoy为每一种事件类型都抽象了对应的接口,例如文件事件接口。

class FileEvent {
   
public:
  virtual ~FileEvent() = default;
  // 激活指定事件,会自动触发对应事件的callback
  virtual void activate(uint32_t events) PURE;
  // 开启指定事件状态的监听
  virtual void setEnabled(uint32_t events) PURE;
};

​ 有了事件基类和对应的接口类后,让我们来看下Envoy如何来实现一个文件事件对象。

// 通过继承ImplBase拥有了event成员
class FileEventImpl : public FileEvent, ImplBase {
   
public:
  FileEventImpl(DispatcherImpl& dispatcher, int fd, FileReadyCb cb, 
                FileTriggerType trigger,
                uint32_t events);

  // Event::FileEvent
  // 实现了文件事件的接口,通过这个接口可
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值