一个refine/refactor的例子

来源:互联网 发布:意念移物软件 编辑:程序博客网 时间:2024/06/12 16:45

我觉得refine比refactor的幅度大,refactor主要是等价替换,而refine有时候直接寻找更好的方案。

Server在管理Client的BufferTime,即决定什么时候应该发包的这部分逻辑,最初是这么写的:

u_int32_t Connection::PerformIO(){    u_int32_t ret = PErrorCode::Success;        ChunkPacketFarm* cfarm = rtmp.GetChunkFarm();    MessagePacketFarm* mfarm = rtmp.GetMessageFarm();    // buffer is full, async write will flush it.    if(!cfarm->CanWrite()){        return ret;    }        int client_buffer_time = 10 * 1000;        // if stream farm is NULL, initialize it and do metadata.    if(stream_farm == NULL){        if((ret = EnQueueMetaData()) != PErrorCode::Success){            return ret;        }        // enqueue the pre-load data ASAP.        int64_t current_time = fire_time = client_start_time = Utility::GetCurrentTime();        if((ret = EnQueueMessages(current_time, client_buffer_time)) != PErrorCode::Success){            return ret;        }                // the abs time used to sync with client.        fire_time = Utility::GetCurrentTime() + client_buffer_time;                Singleton::Timer()->AddEvent(TimerEvent::EventIO, this, 0);    }    // if we are transfering audio/video, do it    else{        // we have changed the original position of current time.        int64_t current_time = Utility::GetCurrentTime() + client_buffer_time;                if(fire_time <= current_time){            // TODO: analysis the right queue size.            int queue_interval_time = 10000;                        if((ret = EnQueueMessages(current_time, queue_interval_time)) != PErrorCode::Success){                return ret;            }        }                Singleton::Timer()->AddEvent(TimerEvent::EventIO, this, fire_time - client_buffer_time);    }    // when enqueue messages, send out all of them.    bool all_sent_out = false;    while(!all_sent_out){        if((ret = mfarm->SendMessage(all_sent_out)) != PErrorCode::Success){            return ret;        }    }        return ret;}u_int32_t Connection::EnQueueMessages(int64_t current_time, int buffer_time){    u_int32_t ret = PErrorCode::Success;        Message* msg = NULL;    FLVTag* tag = NULL;        fire_time = current_time + buffer_time;        for(;;){        if((ret = EnQueueTag(&tag, &msg)) != PErrorCode::Success){            return ret;        }            if(tag != NULL && tag->tag_header.timestamp + client_start_time >= fire_time){            break;        }    }    return ret;}

即第一次我们先发送10秒的包,让client能减少延迟。然后每隔10秒发送一次包。很不直观,而且很麻烦,refine如下:

u_int32_t Connection::PerformIO(){    u_int32_t ret = PErrorCode::Success;        ChunkPacketFarm* cfarm = rtmp.GetChunkFarm();    MessagePacketFarm* mfarm = rtmp.GetMessageFarm();        // buffer is full, wait for async write flush it.    if(!cfarm->CanWrite()){        return ret;    }        // if stream farm is NULL, initialize it and do metadata.    if(stream_farm == NULL){        if((ret = EnQueueMetaData()) != PErrorCode::Success){            return ret;        }        // to notify the buffer time manager we begin stream.        buffer_time.StreamBegin();    }    // enqueue the pakckets ASAP.    if((ret = EnQueueMessages()) != PErrorCode::Success){        return ret;    }            Singleton::Timer()->AddEvent(TimerEvent::EventIO, this, buffer_time.GetFireTime());    // when enqueue messages, send out all of them.    bool all_sent_out = false;    while(!all_sent_out){        if((ret = mfarm->SendMessage(all_sent_out)) != PErrorCode::Success){            return ret;        }    }        return ret;}u_int32_t Connection::EnQueueMessages(){    u_int32_t ret = PErrorCode::Success;        Message* msg = NULL;    FLVTag* tag = NULL;    int64_t timestamp = buffer_time.GetTimesamp();        if(timestamp == -1){        return ret;    }        for(;;){        if((ret = EnQueueTag(&tag, &msg)) != PErrorCode::Success){            return ret;        }            if(tag != NULL && msg != NULL && tag->tag_header.timestamp > timestamp){            break;        }    }    return ret;}void BufferTimeManager::StreamBegin(){    client_start_time = Utility::GetCurrentTime();}int64_t BufferTimeManager::GetFireTime(){    return fire_time;}int64_t BufferTimeManager::GetTimesamp(){    int64_t current_time = Utility::GetCurrentTime();    // we are not ready to enqueue more messages.    if(current_time < fire_time){        return -1;    }    // move fire time to next position.    // the fire time and current time is used for timer_farm to calc the event active time.    fire_time = current_time + pulse_time;    // while the preload time is used to send the messages ASAP.        int preload_time = client_buffer_time + pulse_time;    return current_time - client_start_time + preload_time;}

我们extract出了一个新类BufferTimeManager,第一次发送流时告诉它记录当前时间,然后就EnQueueMessage(它从BufferTimeManager获取当前最大的时间戳GetTimestamp),发送完毕后下次发包的时间是GetFireTime。

Refine之前,很难理解在做什么;Refine之后,有点像自然语言,把buffertime聚集在一起,抽象出来时就会迫使我们去分析到底buffertime是什么,提供什么样的API,迫使代码接近逻辑上的本来抽象。

后来始终觉得这个缓冲区算法太复杂,随着功能增加,这个算法变成如下这样样子:

int64_t GetTimesamp(){    int64_t current_time = Utility::GetCurrentTime();    // we are not ready to enqueue more messages.    if(current_time < fire_time){        return -1;    }    // pause or low bandwidth delay.    // only when event_abs_time is 0, that is the event is active by async write,    // we think there exists a delay of socket buffer full.    if((client_pauseraw || client_paused) || (fire_time > 0 && has_delay)){        delay_time += current_time - fire_time;    }    // move fire time to next position.    // the fire time and current time is used for timer_farm to calc the event active time.    fire_time = current_time + pulse_time;    // if paused, we return not-ready(-1) when we update the fire_time.    if(client_paused){        delay_time += pulse_time; // the pulse time is the message time, we consume it when pause.        RtmpTrace("pulse time coming, client is paused. "\            "pulse=%d, delay=%ld, buffer=%d, seek=%ld, current=%ld, fire=%ld",             pulse_time, delay_time, client_buffer_time, seek_time, current_time, fire_time);        return -1;    }    // main message timestamp algorithm    int preload_time = client_buffer_time + pulse_time;    int64_t absolute_auto_growth_time = current_time - client_start_time;    int64_t preload_cache_time = absolute_auto_growth_time + preload_time;    int64_t conti_delay_time = preload_cache_time - delay_time;    int64_t message_time = seek_time + conti_delay_time;        RtmpTrace("client=%d buffer time manager report. "\        "message=%ld, seek=%ld, current=%ld, start=%ld, buffer=%d, pulse=%d, delay=%ld, fire=%ld",         connection->GetFD(), message_time, seek_time, current_time, client_start_time, client_buffer_time,         pulse_time, delay_time, fire_time);    return message_time;}
揪心啊,这么复杂的东西,别人实在很难看懂,我不能写这样的东西。以真实数据看看这个算法在做什么:


发现其实每次返回的值是一个序列!马上灵光一闪,简化算法如下:

int64_t GetTimesamp(){    int64_t current_time = Utility::GetCurrentTime();    // we are not ready to enqueue more messages.    if(current_time < fire_time){        return -1;    }    // move fire time to next position.    // the fire time and current time is used for timer_farm to calc the event active time.    fire_time = current_time + pulse_time;    // only not paused, we consume a pulse time of messages.    if(!client_paused){        current_message_time += pulse_time;    }        RtmpTrace("client=%d buffer time manager report. "\        "message=%ld, current=%ld, buffer=%d, pulse=%d, fire=%ld",         connection->GetFD(), current_message_time, current_time, client_buffer_time,         pulse_time, fire_time);    return current_message_time;}

恩,这个很简单,但和实际的场景一致。异步socket在没有发完数据之前都会继续发送,每次进到这个函数时,说明数据发完了,不管延迟还是什么的,都是发完了,然后再发一个pulse的包,其实就可以了。

再进一步,发觉if(paused)其实可以换算为speed,当paused时speed为0,正常时为1.0,pauseraw时加速为2.0,然后加速后需要一个衰减,改进如下:

#define FasterPulse 1000#define NormalPulse 5000#define FasterSpeed 2.0#define NormalSpeed 1.0#define ZeroSpeed 0int64_t BufferTimeManager::GetCurrentTime(){    int64_t current_time = Utility::GetCurrentTime();    // we are not ready to enqueue more messages.    if(current_time < fire_time){        return -1;    }    // move fire time to next position.    // the fire time and current time is used for timer_farm to calc the event active time.    fire_time = current_time + pulse_time;    // we can control the buffer by the speed. for example:    // pauseraw: write faster, speed > 1    // paused: write paused, speed = 0.    message_time += pulse_time * speed;    // if high speed, use attenuation to slow it down.    if(speed > NormalSpeed){        speed -= Attenuation;        speed = RtmpMax(NormalSpeed, speed);    }        RtmpTrace("client=%d paused=%d buffer time manager report. "\        "message=%ld, current=%ld, buffer=%d, pulse=%d, speed=%0.1f, fire=%ld delay=%ld",         connection->GetFD(), client_paused, message_time, current_time, buffer_time, pulse_time, speed, fire_time,         current_time - start_time - message_time + buffer_time + pulse_time);    return message_time;}

这样,控制speed就能控制发包(填充缓冲区)速度,控制pulse就能控制发包频率,控制attenuation能控制高速时的速度衰减。简化的原因是pulse和message的time没有关系,不应该关联到一起。简化过后这样的代码是可读的。

pulse:脉冲,发包的频率。
speed:速度,发包的速度。
attenuation:衰减,高速时的速度衰减。