sogou / workflow

C++ Parallel Computing and Asynchronous Networking Framework

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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的接口在:


/* Create 'Go' task on user's executor and execution queue. */

至于绑定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地址就可以了。