Envoy 源码分析(二) --------buffer

Envoy 源码分析(二) ——–buffer

Envoy的buffer实现基于Libevent的evbuffer,在evbuffer的基础上做了一些简单的封装,如果对evbuffer不甚了解的话,可以查看下这个blog:https://blog.csdn.net/windeal3203/article/details/52864994

BufferFragmentImpl

这个类非常简单,保存了指向内存地址的指针,内存的长度和释放内存的函数对象,调用的接口done来释放内存。

 const void* const data_;
 const size_t size_;
 const std::function<void(const void*, size_t, const BufferFragmentImpl*)> releasor_;

需要注意的是这个类是不可复制的,所有成员变量只有在构造函数时赋值之后不可更改。

OwnedImpl

这个类包含了一个evbuffer对象,并对相关的读写操作做了再次的封装:

 Event::Libevent::BufferPtr buffer_;

BufferPtr 是在common里提到过的用unique_ptr封装的C类指针:

typedef CSmartPtr<evbuffer, evbuffer_free> BufferPtr;
struct RawSlice {
  void* mem_ = nullptr;
  size_t len_ = 0;
};

这里的RawSlice结构体和Libevent中的evbuffer_iovec结构体完全相同,并且和Linux的iovec也相同

struct evbuffer_iovec {
    /** The start of the extent of memory. */
    void *iov_base;
    /** The length of the extent of memory. */
    size_t iov_len;
};

首先看下三个add重载

void add(const void* data, uint64_t size) override;
void add(const std::string& data) override;
void add(const Instance& data) override;

这个三个函数都是通过evbuffer_add函数向evbuffer添加数据,这里会发生复制行为,所以add之后可以析构之前的存储。

void addBufferFragment(BufferFragment& fragment) override;

这个函数内部调用的evbuffer_add_reference,这里是通过引用向evbuffer添加数据,没有复制行为发生,需要传进回调函数在内存失效时析构内存。在使用期间要保证内存的有效生命周期。
这里着重看下read和write函数,实现了对socket的读写操作,用到的系统调用是:readv和writev 分块读和集中写:

int OwnedImpl::read(int fd, uint64_t max_length) {
  if (max_length == 0) {
    return 0;
  }

  constexpr uint64_t MaxSlices = 2;
  RawSlice slices[MaxSlices];
  //获取足够存储max_length数量的内存块
  const uint64_t num_slices = reserve(max_length, slices, MaxSlices);
  struct iovec iov[num_slices];
  uint64_t num_slices_to_read = 0;
  uint64_t num_bytes_to_read = 0;
  //设置RawSlice和iovec对应,以传递readv调用
  for (; num_slices_to_read < num_slices && num_bytes_to_read < max_length; num_slices_to_read++) {
    iov[num_slices_to_read].iov_base = slices[num_slices_to_read].mem_;
    const size_t slice_length = std::min(slices[num_slices_to_read].len_,
                                         static_cast(max_length - num_bytes_to_read));
    iov[num_slices_to_read].iov_len = slice_length;
    num_bytes_to_read += slice_length;
  }
  ASSERT(num_slices_to_read <= MaxSlices);
  ASSERT(num_bytes_to_read <= max_length);
  //调用readv读取
  auto& os_syscalls = Api::OsSysCallsSingleton::get();
  const ssize_t rc = os_syscalls.readv(fd, iov, static_cast<int>(num_slices_to_read));
  if (rc < 0) {
    return rc;
  }
  uint64_t num_slices_to_commit = 0;
  uint64_t bytes_to_commit = rc;
  ASSERT(bytes_to_commit <= max_length);
  //根据读取到的实际数据量调整slices数组
  while (bytes_to_commit != 0) {
    slices[num_slices_to_commit].len_ =
        std::min(slices[num_slices_to_commit].len_, static_cast(bytes_to_commit));
    ASSERT(bytes_to_commit >= slices[num_slices_to_commit].len_);
    bytes_to_commit -= slices[num_slices_to_commit].len_;
    num_slices_to_commit++;
  }
  ASSERT(num_slices_to_commit <= num_slices);
  //将读取数据添加进缓存区
  commit(slices, num_slices_to_commit);
  return rc;
}


int OwnedImpl::write(int fd) {
  constexpr uint64_t MaxSlices = 16;
  RawSlice slices[MaxSlices];
  //返回evbuffer内存存储的指针数组
  const uint64_t num_slices = std::min(getRawSlices(slices, MaxSlices), MaxSlices);
  struct iovec iov[num_slices];
  uint64_t num_slices_to_write = 0;
  //根据evbuffer的数据量调整iovec数组
  for (uint64_t i = 0; i < num_slices; i++) {
    if (slices[i].mem_ != nullptr && slices[i].len_ != 0) {
      iov[num_slices_to_write].iov_base = slices[i].mem_;
      iov[num_slices_to_write].iov_len = slices[i].len_;
      num_slices_to_write++;
    }
  }
  if (num_slices_to_write == 0) {
    return 0;
  }
  //调用writev写数据
  auto& os_syscalls = Api::OsSysCallsSingleton::get();
  const ssize_t rc = os_syscalls.writev(fd, iov, num_slices_to_write);
  //若写数据成功,从缓冲区删除发送成功的数据
  if (rc > 0) {
    drain(static_cast(rc));
  }
  return static_cast<int>(rc);
}

其他的函数都很简单,只要知道evbuffer的接口就很容易看懂实现。

WatermarkBuffer

WatermarkBuffer是对OwnedImpl的再次封装,添加了两个水位检测变量和回调,当OwnedImpl中evbuffer的数据量低于低水位时会回调低水位回调函数,高水位回调于此类似。

  std::function<void()> below_low_watermark_;
  std::function<void()> above_high_watermark_;

  // Used for enforcing buffer limits (off by default). If these are set to non-zero by a call to
  // setWatermarks() the watermark callbacks will be called as described above.
  uint32_t high_watermark_{0};
  uint32_t low_watermark_{0};

ZeroCopyInputStreamImpl

ZeroCopyInputStreamImpl内部包含了一个Buffer::InstancePtr buffer_,其功能类似于一个迭代器,提供了对buffer_的一种访问方式,

 uint64_t position_{0};
 uint64_t byte_count_{0};

position指向evbuffer的evbuffer_chain的相对起始位置,
byte_count 表示当前Next过的所有byte数量。

你可能感兴趣的:(ServerMesh)