分治模式
作为一个最终的例子(也许你已经厌倦了代码而想要探讨下抽象的概念),让我们做一些超级计算机的事情;
超级计算机应用是一个典型的并行处理模式,我们有
一个通风口,作为生成可并行处理的任务;
一组处理任务的工作者;
一个工作池,用来收集工作者完成的任务;
实际上,工作者可能运行在很高的性能机器上;例如使用GPU处理较难的数学运算;下边是通风口
它产生100个任务;每个消息都告诉工作者休息几毫秒;
// Task ventilator
// Binds PUSH socket to tcp://localhost:5557
// Sends batch of tasks to workers via that socket
#include "zhelpers.h"
int main (void)
{
void *context = zmq_ctx_new ();
// Socket to send messages on
void *sender = zmq_socket (context, ZMQ_PUSH);
zmq_bind (sender, "tcp://*:5557");
// Socket to send start of batch message on
void *sink = zmq_socket (context, ZMQ_PUSH);
zmq_connect (sink, "tcp://localhost:5558");
printf ("Press Enter when the workers are ready: ");
getchar ();
printf ("Sending tasks to workers…\n");
// The first message is "0" and signals start of batch
s_send (sink, "0");
// Initialize random number generator
srandom ((unsigned) time (NULL));
// Send 100 tasks
int task_nbr;
int total_msec = 0; // Total expected cost in msecs
for (task_nbr = 0; task_nbr < 100; task_nbr++) {
int workload;
// Random workload from 1 to 100msecs
workload = randof (100) + 1;
total_msec += workload;
char string [10];
sprintf (string, "%d", workload);
s_send (sender, string);
}
printf ("Total expected cost: %d msec\n", total_msec);
zmq_close (sink);
zmq_close (sender);
zmq_ctx_destroy (context);
return 0;
}
下面是工作者程序,它接受一个消息,然后休眠一会,然后就报告任务完成了;
// Task worker
// Connects PULL socket to tcp://localhost:5557
// Collects workloads from ventilator via that socket
// Connects PUSH socket to tcp://localhost:5558
// Sends results to sink via that socket
#include "zhelpers.h"
int main (void)
{
// Socket to receive messages on
void *context = zmq_ctx_new ();
void *receiver = zmq_socket (context, ZMQ_PULL);
zmq_connect (receiver, "tcp://localhost:5557");
// Socket to send messages to
void *sender = zmq_socket (context, ZMQ_PUSH);
zmq_connect (sender, "tcp://localhost:5558");
// Process tasks forever
while (1) {
char *string = s_recv (receiver);
printf ("%s.", string); // Show progress
fflush (stdout);
s_sleep (atoi (string)); // Do the work
free (string);
s_send (sender, ""); // Send results to sink
}
zmq_close (receiver);
zmq_close (sender);
zmq_ctx_destroy (context);
return 0;
}
下面是工作池程序,它收集到100个任务,然后计算出总计任务完成耗时,
// Task sink
// Binds PULL socket to tcp://localhost:5558
// Collects results from workers via that socket
#include "zhelpers.h"
int main (void)
{
// Prepare our context and socket
void *context = zmq_ctx_new ();
void *receiver = zmq_socket (context, ZMQ_PULL);
zmq_bind (receiver, "tcp://*:5558");
// Wait for start of batch
char *string = s_recv (receiver);
free (string);
// Start our clock now
int64_t start_time = s_clock ();
// Process 100 confirmations
int task_nbr;
for (task_nbr = 0; task_nbr < 100; task_nbr++) {
char *string = s_recv (receiver);
free (string);
if ((task_nbr / 10) * 10 == task_nbr)
printf (":");
else
printf (".");
fflush (stdout);
}
// Calculate and report duration of batch
printf ("Total elapsed time: %d msec\n",
(int) (s_clock () - start_time));
zmq_close (receiver);
zmq_ctx_destroy (context);
return 0;
}
平均耗时为5秒;我们分别启动1个 ,2 个,4个工作者,获取的性能结果如下所示:
1 个工作者: 总计耗时: 5034 毫秒.
2 个工作者: 总计耗时: 2421 毫秒.
4 个工作者: 总计耗时: 1018 毫秒.
让我们仔细的看下这些代码:
工作者上接通风口,下连工作池;这就意味着你可以随意添加工作者;如果工作者绑定
到一个端点上,当你添加一个工作者时,你可能需要多个端点,并且需要修改通风口和工作池;
因此,通风口和工作池一个稳定的部件,工作者是动态的;
我们须同步所有的工作者和批次启动,在zeromq中这是一个常见的事情,没有其他好的办法;
zmq_connect会消耗点时间,因此当一组工作者连接到通风口时,第一个连接成功的工作者会获取一堆消息;
如果你不进行同步批次启动,系统就不会并行处理;试着删除通风口的等待看看会发生什么事情;
通风口的推送套接字会分发任务给工作者(假定批次启动后,所有的工作者都已经连接上)
这就叫负载均衡,
工作池的推送套接字会均匀的收集工作者的任务,这焦作公平排队;
管道线模式展现了“slow joiner”,导致了推送套接字不能负载均衡;如果你使用了
PUSH和PULL,则你的某个工作者可能获取消息比其他的多;这是因为PULL套接字比其他的要参与的快,
所以会收集到更多的消息;如果你像要均衡负载,则需要查看第三节的负载均衡模式-- 高级请求响应模式
转载于:https://my.oschina.net/u/2413521/blog/546557