workflow支持线程绑定CPU吗?或者支持不同类型任务分配不同的线程池吗?
iamysx opened this issue · comments
独立线程池可以做的。你可以创建自己的Executor(对于一个线程池)和自己的ExecQueue(对应一个计算队列。你可能只需要一个队列),然后用自己的Executor和ExecQueue创建go task或thread task:
#include "workflow/Executor.h"
#include "workflow/WFTaskFactory.h"
int main()
{
Executor executor;
executor.init(10); // 10个线程
ExecQueue queue;
queue.init();
WFGoTask *task = WFTaskFactory::create_to_task(&queue, &executor, f, 1, 2, 3);
....
// 程序退出之前,释放Executor和queue
Executor.deinit();
queue.deinit();
}
使用Executor和queue创建thread task或go task的接口在:
workflow/src/factory/WFTaskFactory.h
Line 462 in 9c018ea
workflow/src/factory/WFTaskFactory.h
Line 361 in 9c018ea
至于绑定CPU的想法,我们基本放弃了。意义不大。
不过,你确定需要多个线程池吗?我们计算队列名就是用来解决多种计算任务公平性的问题。
目前我使用的是WFGlobalSettings来初始化线程池,主要框架是处理http请求,但现在遇到的问题是有些http请求中涉及redis,sql等操作,这些操作执行效率较低导致阻赛到所有线程用完,因此其它的http请求无法响应,因此考虑的方案看是否能够用多个线程池的方式,不涉及到redis,sql的http请求使用一个线程池,另外涉及到的使用一个线程池,或者针对这种场景你有更好的解决方案吗?
或者是否可以针对redis,sql的操作创建一个线程池来执行,而不影响其它的http请求的方式?
目前我使用的是WFGlobalSettings来初始化线程池,主要框架是处理http请求,但现在遇到的问题是有些http请求中涉及redis,sql等操作,这些操作执行效率较低导致阻赛到所有线程用完,因此其它的http请求无法响应,因此考虑的方案看是否能够用多个线程池的方式,不涉及到redis,sql的http请求使用一个线程池,另外涉及到的使用一个线程池,或者针对这种场景你有更好的解决方案吗?
你是说你想要网络任务用不同的线程池?我们的网络应用都是全程无等待的,不应该有线程用完的情况,可能你对我们框架的使用有一些问题。
你可以贴一下你的用法,我们看看应该怎么改。
目前使用方式:
HTTP请求-->handler处理,handler处理涉及到另外的http任务,也可能涉及到redis,mysql查询。
存在的问题在并发测试时存在HTTP请求超时无响应
之前的redis,mysql在handler处理中是通过串行方式,也未使用create_redis_task,create_mysql_task任务方式,因此可能存在的问题点是查询慢导致线程阻塞,也可能是由于handler处理中涉及到另外对其它的http请求响应慢导致。
redis使用的redis++,如redisConnectUnix(),建立了连接池
mysql使用的soci,也建立了连接池,但未使用workflow内置任务,因为我们代码进行了封装一层所以贴出来也不太好看出来。
如果像你说的你们线程应该都不存在等待的话,那内部向其它模块的http请求任务即便响应慢那应该也不会阻塞吧,只是如果都慢的话也可能出现客户端请求的超时未响应,或者就是在内部redis,mysql查询上有阻塞了。
另外redis,mysql存在集群的方式,workflow的内置任务可以适配集群吗?
我们的redis任务确实是支持集群模式的,但mysql集群是什么意思?
另外redis,mysql存在集群的方式,workflow的内置任务可以适配集群吗?
我觉得你可能最后先看一下我们文档,感觉你对我们的使用方法好像理解有点问题。
你可以看一下我们http proxy这个示例,用于展示无阻塞的server是怎么实现的:https://github.com/sogou/workflow/blob/master/docs/tutorial-05-http_proxy.md
就是按照上面文档使用的,http方面没什么问题,只是目前的确是redis,mysql没有按照内置任务方式使用,我看create_mysql_task,create_redis_task这种内置使用方式的话是不是就不需要自己创建连接池了?
就是按照上面文档使用的,http方面没什么问题,只是目前的确是redis,mysql没有按照内置任务方式使用,我看create_mysql_task,create_redis_task这种内置使用方式的话是不是就不需要自己创建连接池了?
是的啊,redis和http一样,都是使用同一套网络线程池。
还是那个问题,你不贴你的伪代码,我现在连你是在写client还是server都不知道……
//redis初始化
RedisCluster *g_redis_cluster = NULL;
bool redis_cluter_init(const cache_config &x_config)
{
ConnectionOptions connection_options;
ConnectionPoolOptions pool_options;
try
{
g_redis_cluster = new RedisCluster(connection_options, pool_options);
}
catch (const ReplyError &err)
{
log_info_g("RedisHandler-- ReplyError:%s \n", err.what());
return false;
}
........
return true;
}
//redis查询
cache_redis::hget(REDIS_HKEY_HASH, key, hash_value);
//redis调用
int cache_redis::hget(const char *key, const char *field, std::string &value)
{
if (g_config.REDIS_MODE == "redis-cluster")
{
try
{
auto val = g_redis_cluster->hget(key, field);
if (val)
{
}
return ret;
}
catch (const std::exception &e)
{
return -1;
}
}
redisReply *replay = nullptr;
do
{
auto context = g_redis_pool->get();
replay = (redisReply *)redisCommand(context->m_connection, "hget %s %s", key, field);
if (replay->type != REDIS_REPLY_STRING)
break;
value.assign(replay->str, replay->len);
} while (0);
if (nullptr != replay)
{
freeReplyObject(replay);
}
return ret;
}
这是redis初始化调用查询的伪代码,如果改成create_redis_task的方式的话应该需要一些工作。
static int url_req(WFHttpTask *task, context)
{
ParallelWork *pwork = Workflow::create_parallel_work(_parallel_work_callback);
pwork->set_context(context);
do
{
auto next1 = invoke_java::create_http_task();
if (nullptr == next)
{
break;
}
SeriesWork *series = Workflow::create_series_work(next, nullptr);
pwork->add_series(series);
} while (0);
do
{
auto next2 = invoke_java::create_http_task( context);
if (nullptr == next)
{
break;
}
SeriesWork *series = Workflow::create_series_work(next, nullptr);
pwork->add_series(series);
} while (0);
//redis查询
cache_redis::hget(REDIS_HKEY_HASH, key, hash_value);
if (pwork->size() <= 0)
{
pwork->dismiss();
}
else
{
series_of(task)->push_back(pwork);
}
return 0;
}
这是现在http请求收到后的处理伪代码,如有两个http内部请求next1,next2,再加一个redis查询操作,现在的redis操作没有使用内置的create_redis_task的方式,这种方式的确就是redis会阻塞线程了,现在的问题点就是在这里,如果想要优化考虑的两种方式,一是redis任务做成内置任务方式,另外一种是否能够使用上面你最开始介绍的Executor的方式,把redis或者mysql任务做成任务队列,单独的用线程池来执行。
是的啊,如果你这个url_req是运行在我们的 process或callback里,那就是占用这我们一个 handler线程在等待redis任务了。最最简单的方法,把cache_redis::hget这改成WFTaskFactory::create_redis_task,然后先push_back这个task。这个改掉非常简单啊。
等待一个非本框架的异步任务,可以使用counter,文档在:https://github.com/sogou/workflow/blob/master/docs/about-counter.md#server%E4%B8%8E%E5%85%B6%E5%AE%83%E5%BC%82%E6%AD%A5%E5%BC%95%E6%93%8E%E7%BB%93%E5%90%88%E4%BD%BF%E7%94%A8
但是你现在这个redis任务连异步都不是,那就只能在搞个线程池来模拟异步了。
我们这个redis支持多种部署模式,redis-single,redis-sentinel,redis-cluster,redis-uds,这些模式在workflow中支持吗?
我看试例没有涉及到模式这种设置的地方
TEST(redis_unittest, WFRedisTask1)
{
std::mutex mutex;
std::condition_variable cond;
bool done = false;
WFRedisServer server(__redis_process);
EXPECT_TRUE(server.start("127.0.0.1", 6677) == 0) << "server start failed";
test_client("redis://:testpass@127.0.0.1:6677/6", mutex, cond, done);
std::unique_lock<std::mutex> lock(mutex);
while (!done)
cond.wait(lock);
lock.unlock();
server.stop();
}
我们这个redis支持多种部署模式,redis-single,redis-sentinel,redis-cluster,redis-uds,这些模式在workflow中支持吗? 我看试例没有涉及到模式这种设置的地方
TEST(redis_unittest, WFRedisTask1) { std::mutex mutex; std::condition_variable cond; bool done = false; WFRedisServer server(__redis_process); EXPECT_TRUE(server.start("127.0.0.1", 6677) == 0) << "server start failed";
test_client("redis://:testpass@127.0.0.1:6677/6", mutex, cond, done); std::unique_lock<std::mutex> lock(mutex); while (!done) cond.wait(lock); lock.unlock(); server.stop();
}
你不是用我们的WFRedisServer啊,就是我们的WFTaskFactory::create_redis_task();
你先用我们的tutorial-02试一试能不能都支持?
不过UDS确实没有支持,要做也不难。
https://github.com/sogou/workflow/blob/master/docs/tutorial-02-redis_cli.md
用这个tutorial里的代码试一下。不要看test目录啊。
@iamysx 如果你无需使用到计算任务,你用我们的计算任务线程来等待你的redis client同步任务也是可以的。直接调用create_go_task,在函数里去调用你的hget。这个go task可以push_back到series里。
如果你用这个方法,可以在global settings里,修改compute thread的线程数。这个数默认是计算机上的cpu核心数。反正你不是用来计算了,那改成多少看你需要了。
补充一下,我们的http/redis/mysql这些内置协议都支持UDS的client。用一个upstream指向本地UDS地址就可以了。