游戏开发:服务器端网络代理设计(network proxy)

不仅是游戏项目,从软件设计层面看,当服务位于网络远端时,为与客户端之间的传输层连接提供一定的网络质量优化支持是常见的设计要求。

实现一个前后端的网络代理层,目标是优化直连链路的网络质量,支持缓冲重连。这里记录下思路;

代理层实现:

  1. 定制分配规则对服务器端机器做负载均衡;
  2. 客户端和服务器端业务连接的断线重连;
  3. 支持全双工通信协议类型的扩展;
  4. 适配双端数据序列化协议;
  5. 偏好热更新

方案是:将 client <一> server 的通信链路修饰为 client <一> netproxy <一> server

核心实现是:对传输层连接进行封装,使支持业务层的个性化设定;代理层为每个通信链路建立两个连接,使用一组connection(connpair)来代理前后端的数据通信工作;

我们将client与netproxy的连接称为downconn,netproxy与server的连接称为upconn;

typedef struct connpair {
  	struct downconn * down;
  	struct upconn * up;
} conpair;

static bool
longforward(uint64_t id, const char * tag, void * dest, void * src) {
  // ...	
  while (true) {
      	int readbyte = src.read(buffer);
        if (readbyte > 0) {
          	int err = dest.write(buffer, readbyte);
          	if (err) break;
        }
    }
  	src.close(); dest.close();
}

1. 负载均衡的实现

typedef struct host {
  	char * name;
  	char * addr;
  	int weight;
  	char ** client; // ["127.0.0.1:8888", ...]
} host;

typedef struct servermgr {
  	struct host * hosts;
} servermgr;

struct rulebalance {
};

代理层维护后端服务器群负载信息(权重、当前承压情况等),每个连接通过特定规则计算出相匹配的目标后端机器。后端服务器群通过配置给出,提供reload接口支持动态修改和扩展服务器群;

2. 断线重连的实现

当connpair捕获到某一传输层连接断开,会执行冻结操作,将proxy conn标记为冻结,保留pair中另一连接和业务状态数据(使连接健康方对断线无感知),超时释放。当client在超时时间内重新建立连接,代理层恢复connpair状态,替换失效连接,补发丢失数据;

// network proxy conn
typedef struct proxyconn {
	int fd; // socket
	struct config *Config;
  
	char * error;
	bool handshaked; // handshake finish flag
	bool frozen;
  
	int id; // unique ID
	int handshakes; // handshakes count

	bool reused;// reused conn
} proxyconn;

3. 对通信协议类型扩展友好

代理层驱动跟协议类型解耦。封装通信协议提供松耦合的API,通过上下文的方式传入协议对象;

function driver.onaccpet(conn)
  	--[[
  		...
 		conn:read()
  		conn:write()
  	--]]
end

function driver.serve(listener)
    while true do
        local conn = listener.accept()
        coroutine_fork(driver.onaccpet, conn)
    end
end
posted @ 2024-06-03 23:05  linxx-  阅读(15)  评论(0编辑  收藏  举报