Jason Pan

好客若干优化点

黄杰 / 2020-06-28


QueueEntry 可以用 Protobuf 定义封装

几类函数

  1. set/get类
  2. 序列化与反序列化
  3. 状态判断IsXXX
  4. 枚举与描述的转换
  5. 内部函数,如根据几个字段拼出队列名

前三类都可以直接使用Protobuf的类来实现

枚举与描述的转换

关于枚举和描述的转换,Protobuf也是支持的。使用Protobuf自带的Message例子:

message Person {
  required string name = 1;
  required int32 id = 2;
  optional string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }
  message PhoneNumber {
    required string number = 1;
    optional PhoneType type = 2 [default = HOME];
  }

  repeated PhoneNumber phones = 4;
};

实际打印枚举对应的字符串:

  std::cout << Person_PhoneType_MOBILE << std::endl; 
  std::cout << Person_PhoneType_Name(Person_PhoneType_MOBILE) << std::endl; 

以若干字段作为参数的内部函数

因为Protobuf产生的类被标记为final,无法被继承,所以如果要使用成员函数做一些操作,需要使用其他的函数。

可以命名为PersonHelper(?)

string concatenateNameEmail(const proto::Person &person) { ... }

几种 Key

 QueueIndex + "_" + GroupId + "_" + SendRuleQueueName
 QueueIndex + "_" + GroupId + "_" + SendRuleQueueName + "_status"
 QueueIndex + "_" + GroupId + "_" + SendRuleQueueName + "_estimated"

使用Protobuf压缩

input stream 是用来解析的。 output stream 是序列化产生的。

简单的单例实现

static Singleton& get() {
  static Singleton instance;
  return instance;
}

另外一个实现“

https://www.theimpossiblecode.com/blog/c11-generic-singleton-pattern/

.inl文件的作用

#include "src/store_mgr_v2/common/store_conn_mgt_timer.inl"

StoreConnMgtTimer

start() 具体的逻辑为:

不是线程安全的。当多个线程中同时start可能造成多new定时器和处理函数指针。

但其实这种只会在main中启动一次的也可以不用考虑线程安全的问题。

可以比较Envoy项目中的 Envoy::Event::TimerEnvoy::Event::Scheduler

Envoy::Event::Timer

时钟

using SystemTime = std::chrono::time_point<std::chrono::system_clock>;
using MonotonicTime = std::chrono::time_point<std::chrono::steady_clock>;

StoreClientMgt

负责管理与存储的连接。功能:

获取IP信息的函数可以单独拎出去。

ServerInfo的内容。

StoreClientMgt 管理一个cluster,包括一批服务发现类型相同的服务器信息(由StoreServerMgtInfo决定的)。

模板类型参数 StoreClsName 是用来。

继承自RefCounter和使用StoreSmartPtr的目的,都是为了使用智能指针,可以使用shared_ptr完全替代。

DummyQueueQueueInterface 的关系,后者就是个接口/纯虚类,前者是其一种实现。封装了Redis的相关操作。

DummyQueue 保存RedisContext的上下文信息。使用DummyQueue的对象进行redis操作,应当加锁。

类似的还有 TglogClientCdbClient 。这几个类为了使用StoreClientMgt管理,需要提供 set_store_idxset_version 两个函数,以及单个ServerInfo的存储。

几个相关类的关系:

typedef Smart_Ptr_T <TglogClient> TglogClient_Ptr; 
typedef StoreClientMgt<TglogClient_Ptr, TglogClient> TglogClientMgt; 
typedef StoreConnMgtTimer<TglogClientMgt> TglogClientMgtTimer; 
typedef StorePtrMgt<TglogClient_Ptr, TglogClientMgt> TglogStorePtrMgt; 

#define PSGT_TglogClient_Mgt Singleton_T<TglogClientMgt>::getInstance()
#define PSGT_TglogClient_Mgt_Timer Singleton_T<TglogClientMgtTimer>::getInstance()

TglogClientMgt 是为了管理几个服务地址的所有连接的池子。 TglogClientMgtTimer 是为了通过 TglogClientMgt 来定时刷新连接的定时器。 TglogStorePtrMgt 是为了析构时释放连接的。

ClientMgt 是管理一个

Q: 是否一定需要使用模板来定义TglogClientMgt? 看起来几个Client实现都是继承自相同的类,都实现了几个固定的参数,是否可以用类来替换?

配置散落各处

开发方面,会造成需要多个线程去分别更新不同的配置。

运维方面,会给运维工作带来困扰,有的要管理端操作下发配置,有的有修改启动参数重启进程,有的则要下发配置文件。

与IDIP的关系

因为IDIP支持异步处理(返回中带有请求中的req_id),因此发送IDIP请求的线程池和处理IDIP返回的线程池是独立的(其实也有关系)。

使用单例维护连接池管理对象

StoreClientMgt

以CDB连接的管理来说明几种对象/名称的含义

【优化】有两个类模板是这么写的:

template <typename StoreSmartPtr, typename StoreClsName>
class StoreClientMgt {
  public:
   StoreClientMgt();
};

template <typename StoreSmartPtr, typename StoreSmartPtrMgt>
class StorePtrMgt {
 public:
  StorePtrMgt(StoreSmartPtr ptr);
};

根据上边的关系可以得知,StoreSmartPtr是StoreClsName的智能指针。所以

第一个类模板可以简化为:

template <typename StoreClsName>
class StoreClientMgt {
 public:
  typedef Smart_Ptr_T<StoreClsName> StoreSmartPtr;
  StoreClientMgt();
};

也就是StoreClientMgt变成只需要接受一个形参类型的类模板,因此第二个类也可以进行简化:

template <typename StoreClsName>
class StorePtrMgt {
 public:
  typedef Smart_Ptr_T<StoreClsName> StoreSmartPtr;
  typedef StoreClientMgt<StoreClsName> StoreSmartPtrMgt;

  StorePtrMgt(StoreSmartPtr ptr);
};

定义新类型使用使用 usingtypedef 的区别

using SchedulerPtr = std::unique_ptr;

There is no difference between creating a type alias using typedef and creating a type alias using using. However, using is more powerful since one it can be templated (creating what’s called an alias template).

typedef int A; // ok
using B = int; // ok
template <class T>
typedef std::pair<T, T> TT; // error 
template <class T>
using TT = std::pair<T, T>; // ok

On the other hand, typedef allows you to declare multiple types at once (not that you would do this often):

// declares I to be an alias for int, 
// P to be an alias for int*, 
// and F to be an alias for int (*)() 
typedef int I, *P, (*F)(); 

typename的使用

template <class T>
string GetRealIpFromHeaderXff(T &headers) {
  string ret;
  typename T::iterator it;
  it = headers.find("X-Forwarded-For")) == headers.end();
  ...

在模板中使用

返回成员变量引用:

const int &QueueEntry::get_retry_count() const {

    return retry_count_;

}