一个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:衰减,高速时的速度衰减。
- 一个refine/refactor的例子
- Refine! Refine! Refine!
- Refactor
- refactor
- 关于refactor的几个原则
- Code Refactor-命名的学问-读书笔记
- 重构(Refactor)的意义
- 使用Xcode的Refactor功能调整代码
- 一个触发器的例子
- LineDDA的一个例子
- 一个函数的例子
- 一个触发器的例子
- DirectX的一个例子
- 一个Hibernate的例子
- ACE的一个例子
- 一个分页的例子
- 过滤的一个例子
- JSTL的一个例子
- Group by与having理解
- java中String的比较
- jquery hover()事件函数
- Qt中的delete
- idhttp
- 一个refine/refactor的例子
- PhotoShop使用小技巧
- 运算符记忆
- 屏蔽页面右击查看功能
- PHP实现文件缓存转内存缓存
- Emacs Frame and buffer
- Data Guard相关参数
- Extjs-实用工具 桌面组件 Ext.Desktop
- 如何使用 Visual Studio .NET 创建安装程序包