备机在启动时会调用RequestXLogStreaming函数(这一步在哪里调用在前面的博客中已经描述过),这个函数会将GUC参数中的primary_conninfo信息保存在WalRcvData中(WalRcvData是由WalRcvShmemInit函数在共享内存中初始化的),也会保存WAL日志的复制起始LSN及时间线信息,然后通过SendPostmasterSignal(PMSIGNAL_START_WALRECEIVER)通知Postmaster进程启动WalReceiver进程,WalReciver进程负责和主机建立连接关系。(摘抄自张树杰 PostgreSQL技术内幕 事务处理深度探索)
在这里插入图片描述
首先我们看一下WAL 接收器的六个状态字段,RequestXLogStreaming函数只有在WALRCV_STOPPED状态下,RequestXLogStreaming函数才会去发送PMSIGNAL_START_WALRECEIVER通知Postmaster进程启动WalReceiver进程,只有在WALRCV_WAITING状态下,才能直接调用SetLatch来唤醒wal reciver的latch。

typedef enum {
	WALRCV_STOPPED,				/* stopped and mustn't start up again */
	WALRCV_STARTING,			/* launched, but the process hasn't initialized yet */
	WALRCV_STREAMING,			/* walreceiver is streaming */
	WALRCV_WAITING,				/* stopped streaming, waiting for orders */
	WALRCV_RESTARTING,			/* asked to restart streaming */
	WALRCV_STOPPING				/* requested to stop, but still running */
} WalRcvState;

void RequestXLogStreaming(TimeLineID tli, XLogRecPtr recptr, const char *conninfo, const char *slotname, bool create_temp_slot)函数的参数“recptr”表示流应该开始的位置; “conninfo”是要使用的 libpq 连接字符串; “slotname”是可选的,要获取的复制槽的名称;“create_temp_slot”表示在没有给出“slotname”时创建一个临时槽。WAL 接收器不直接加载用于连接到主节点的 GUC 参数,而是依赖此例程的调用者向下传递的值。 因此,任何新参数的添加都应通过此代码路径进行。

	WalRcvData *walrcv = WalRcv;
	bool		launch = false;
	pg_time_t	now = (pg_time_t) time(NULL);
	Latch	   *latch;

	/* We always start at the beginning of the segment. That prevents a broken segment (i.e., with no records in the first half of a segment) from being created by XLOG streaming, which might cause trouble later on if the segment is e.g archived. 我们总是从段的开头开始。 这可以防止 XLOG 流创建损坏的段(即,段的前半部分没有记录),如果段被存档,这可能会在以后造成麻烦。 */
	if (XLogSegmentOffset(recptr, wal_segment_size) != 0) recptr -= XLogSegmentOffset(recptr, wal_segment_size);
	SpinLockAcquire(&walrcv->mutex); // 获取锁

	/* It better be stopped if we try to restart it 只有在停止状态和等待状态才能调用 */
	Assert(walrcv->walRcvState == WALRCV_STOPPED ||  walrcv->walRcvState == WALRCV_WAITING);

	if (conninfo != NULL) strlcpy((char *) walrcv->conninfo, conninfo, MAXCONNINFO);
	else walrcv->conninfo[0] = '\0';

	/* Use configured replication slot if present, and ignore the value of create_temp_slot as the slot name should be persistent.  Otherwise, use create_temp_slot to determine whether this WAL receiver should create a temporary slot by itself and use it, or not. 如果存在,请使用配置的复制槽,并忽略 create_temp_slot 的值,因为槽名称应该是持久的。 否则,使用 create_temp_slot 来确定这个 WAL 接收器是否应该自己创建一个临时槽并使用它,或者不 */
	if (slotname != NULL && slotname[0] != '\0'){
		strlcpy((char *) walrcv->slotname, slotname, NAMEDATALEN);
		walrcv->is_temp_slot = false;
	}else{
		walrcv->slotname[0] = '\0';
		walrcv->is_temp_slot = create_temp_slot;
	}

	if (walrcv->walRcvState == WALRCV_STOPPED) {
		launch = true;
		walrcv->walRcvState = WALRCV_STARTING;
	}
	else
		walrcv->walRcvState = WALRCV_RESTARTING;
	walrcv->startTime = now;

	/* If this is the first startup of walreceiver (on this timeline), initialize flushedUpto and latestChunkStart to the starting point. 如果这是 walreceiver 的第一次启动(在此时间轴上),请将 flushedUpto 和 latestChunkStart 初始化为起点。 */
	if (walrcv->receiveStart == 0 || walrcv->receivedTLI != tli) {
		walrcv->flushedUpto = recptr;
		walrcv->receivedTLI = tli;
		walrcv->latestChunkStart = recptr;
	}
	walrcv->receiveStart = recptr;
	walrcv->receiveStartTLI = tli;
	latch = walrcv->latch;
	SpinLockRelease(&walrcv->mutex);

	if (launch) SendPostmasterSignal(PMSIGNAL_START_WALRECEIVER);
	else if (latch) SetLatch(latch);
}