数据服务开发经验
有状态服务或者说数据服务,上线遇到问题很棘手,回滚无济于事;而且数据加载通常都很慢,部署时间长;最终导致不敢修改代码,谨小慎微;服务质量也是能忍就忍,不愿意深度优化。在我负责顺风车LBS以来,感受愈加强烈;区别于无状态服务,数据服务的几个方面需要格外关注。(此处假设数据服务类似redis基于内存,数据量大到需要磁盘存储,关注点会有所不同。)
Ma' in wojel ba'ax ka lelo' u k'áat u ya'al
有状态服务或者说数据服务,上线遇到问题很棘手,回滚无济于事;而且数据加载通常都很慢,部署时间长;最终导致不敢修改代码,谨小慎微;服务质量也是能忍就忍,不愿意深度优化。在我负责顺风车LBS以来,感受愈加强烈;区别于无状态服务,数据服务的几个方面需要格外关注。(此处假设数据服务类似redis基于内存,数据量大到需要磁盘存储,关注点会有所不同。)
之前在组内分享过一次RocketMQ,虽然写了PPT,还是口述+黑板画图,现场分享效果更佳。RocketMQ架构清晰,只有NameServer、Broker、Producer和Consumer四个组件,通过共享commitlog,即使数十万topic,依然能保持高性能;是通过工程巧妙解决业务难题的典范。RocketMQ真正做到了“简单”,例如重复消费这种功能,完全抛给消费侧;通过构建逻辑队列,每个消息都会指定唯一队列,解决消息顺序的问题。
下面介绍整体架构和四个组件,然后分析一条消息从诞生到被消费的全过程,从中了解RocketMQ设计的精妙。
共享内存使用便捷,功能强大,但是会有几个显著的问题阻碍大家使用:
1、增删不当,造成内存泄漏;共享内存依赖一个全局的key,没有好的清理手段。
2、内存大小不好控制,动态管理则会引入更多复杂的情况;即使是加个header,一次性的动态大小,写起来也非常麻烦。
3、同样由于key的存在,共享内存管理容易混乱,万一冲突后果很严重。
3、语言和框架越来越强大,大多数人没有直接管理内存的经验,心里恐惧,不敢操作。
thrift结构体按照一定格式编码成字符串,供网络传输;通常上层会对解码和编码的调用做封装,把object编码为string,从string解码object:
boost::shared_ptr<TMemoryBuffer> mem_buffer(new TMemoryBuffer());
mem_buffer->resetBuffer(
reinterpret_cast<uint8_t*>(const_cast<char*>(buffer.c_str())),
buffer.size());
TBinaryProtocol protocol(mem_buffer);
data.read(&protocol);
boost::shared_ptr<TMemoryBuffer> mem_buffer(new TMemoryBuffer());
TBinaryProtocol protocol(mem_buffer);
data.write(&protocol);
buffer = mem_buffer->getBufferAsString();
spdlog_impl.h
: 用户接口
registry.h
: logger封装接口
using registry = registry_t<std::mutex>
class registry_t {
private:
std::unordered_map<std::string, std::shared_ptr<logger>> _loggers; // name -> logger
}