利用LUA协程实现FUTURE模式

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,高可用系列 2核4GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 1. Future模式: 参见http://www.cnblogs.com/zhiranok/archive/2011/03/26/Future_Pattern.html 使用future的好处是即利用了异步的并行能力,又保证主逻辑串行执行,保持简单。

1. Future模式:

参见http://www.cnblogs.com/zhiranok/archive/2011/03/26/Future_Pattern.html

使用future的好处是即利用了异步的并行能力,又保证主逻辑串行执行,保持简单。

2. Lua 协程

sina Timyang 的介绍 http://timyang.net/lua/lua-coroutine/

lua coroutine 通过create创建一个伪线程,该“线程”通过yield可以挂起自己,通过调用resume可以使该“线程”从挂起位置继续执行。

3. LUA coroutine 实现 Future

假设有如下应用场景:

1. 用户登录系统,需要将用户数据从Mysql中获取用户数据,然后在LUA中实例化user_t对象。

2. 用户登录事件由C++触发,将uid参数传递给lua

3. lua 并不存在mysql接口,必须委托c++完成mysql操作,而且lua state必须被单线程操作,顾我们期望LUA不能被阻塞,在单个user从mysql 载入数据

  时其他user应该能够继续接受请求

故我们设计了如下解决方案:

1. lua中的user_t对象每个实例拥有两个主要数据,

  a. request_cache,在user未初始化完成时该uid的请求将被缓存起来(我们将请求封装成function)。

      b. coroutine ,该协程尝试将request_cache中的所有请求执行完毕,当出现如下情况该协程为挂起自己

    (1)request_cache 为空,挂起等待新的请求

    (2)需要执行mysql时挂起,等待mysql执行完毕被唤醒。

示例代码:

     

 1 user_t = {}
2 user_t.__index = user_t
3
4 function user_t:new()
5 local funjc = function() print("TODO exe all request in request_cache") end
6 local ret =
7 {
8 ["request_cache"] = {},
9 ["coroutine_obj"] = coroutine.create(funjc),
10 }
11 setmetatable(ret, self)
12 return ret
13 end

 

2. C++ 封装异步调用Mysql的接口,注册接口到LUA

1. future_t 用于LUA和C++传递数据

1 class future_t
2 {
3 public:
    void   set_result(const string& v_) { m_result = v_;   }
4 string get_result() const { return m_result; }
5 private:
6 string m_result;
7 };

2. async_load_data_from_db 用于异步执行mysql操作

 1 void async_load_data_from_db(future_t* ret_)
2 {
3 //! post another thread, async exe load data from db
4 thread.post(boost::bind(do_load_data_from_db, ret_));
5 }
6
7 void do_load_data_from_db(future_t* ret_)
8 {
9 //! TODO exe sql opertion
10 lua_pcall("resume_routine")
11 }

 

lua 调用C++的接口async_load_data_from_db,async_load_data_from_db 将请求post另外的线程,执行mysql请求,将请求结果赋值到future中,调用lua的resume函数唤醒
lua协程继续执行

3. LUA 示例代码

 1 user_t = {}
2 user_t.__index = user_t
3
4 function user_t:new(uid_)
5 local ret =
6 {
7 ["uid"] = uid_,
8 ["request_cache"] = {},
9 ["coroutine_obj"] = true,
10 ["runing_flag"] = true,
11 }
12 setmetatable(ret, self)
13
14 local func = function()
15 while true == runing_flag
16 if 0 == #ret.request_cache
17 then
18 coroutine.yield()
19 else
20 local todo_func = ret.request_cache[1]
21 local tmp = {}
22 for k = 2, #ret.request_cache
23 do
24 table.insert(tmp, ret.request_cache[k])
25 end
26 ret.request_cache = tmp
27 todo_func()
28 end
29 end
30 end
31 ret.coroutine_obj = coroutine.create(func)
32 return ret
33 end
34
35 function user_t:init()
36 local func = function()
37 local future = future_t:new()
38 async_load_data_from_db(future)
39 coroutine.yield()
40 print("user_t:init ok", self.uid, future:get_result())
41 future:delete()
42 end
43 table.insert(self.request_cache, func)
44 coroutine.resume(self.coroutine_obj)
45 end
46
47 function user_t:resume_routine()
48 coroutine.resume(self.coroutine_obj)
49 end
50
51 local test_user = user_t:new(1122334)
52
53 function user_login()
54 return test_user:init()
55 end
56
57 function resume_routine()
58 return test_user:resume_routine()
59 end

4. 注意事项:

尽管一个lua state是串行执行的,使用lua coroutine时仍然要注意数据一致性,比如在coroutine执行时使用了全局变量,yield挂起后全局变量有可能被修改了,

所以协程适合于例子中的user_t对象,各个user是互不干扰的,相同的user请求会被单个协程串行化。

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。   相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情: https://www.aliyun.com/product/rds/mysql 
目录
相关文章
|
C# Python
[√]lua 协程
[√]lua 协程
207 1
|
NoSQL Redis
Redis下Lua脚本的复制模式
假设我们的Redis选择了主从架构, 和AOF持久化方式。我们执行一条写命令时, 该条命令会被发送到从服务器, 和追加到AOF文件中。当我们执行的不是一条命令, 而是Lua脚本时, 默认情况下, 整个Lua脚本的内容会进行复制, 但是存在一些特殊情况。
2432 0
Redis下Lua脚本的复制模式
Lua语法(四)——协程
Lua语法(四)——协程
206 0
|
存储 NoSQL Redis
Tair的发展问题之在Redis集群模式下,Lua脚本操作key面临什么问题,如何解决
Tair的发展问题之在Redis集群模式下,Lua脚本操作key面临什么问题,如何解决
|
存储 缓存 NoSQL
一文讲透 Redis 事务 (事务模式 VS Lua 脚本)
先说结论: Redis 的事务模式具备如下特点: - 保证隔离性; - 无法保证持久性; - 具备了一定的原子性,但不支持回滚; - 一致性的概念有分歧,假设在一致性的核心是约束的语意下,Redis 的事务可以保证一致性。 但 Lua 脚本更具备实用场景,它是另一种形式的事务,他具备一定的原子性,但脚本报错的情况下,事务并不会回滚。Lua 脚本可以保证隔离性,而且可以完美的支持**后面的步骤依赖前面步骤的结果**。
一文讲透 Redis 事务 (事务模式 VS Lua 脚本)
|
NoSQL Redis
RedisTemplate执行lua脚本在Redis集群模式下报错EvalSha is not supported in cluster environment.
RedisTemplate执行lua脚本在Redis集群模式下报错EvalSha is not supported in cluster environment.
510 0
【Lua】协程(Coroutine)
【Lua】协程(Coroutine)
166 0