Odoo中文社区可以通过以下三个域名访问:shine-it.net , odoocn.org,odoo.net.cn

原论坛用户的基本信息和发帖这里都予以保留,请注意:原论坛用户无需重新注册新用户,但是您的密码需要重置

开发人员可以登录gitter讨论组: http://gitter.im/odoo-china/Talk, 需要github账号

如果您登录系统碰到问题,请在微信公众号留言:

OpenERP 负载平衡



  • OpenERP 7.0 带来了许多新特性,架构上也有许多改进。其中可配置 worker 参数,可使 OpenERP 运行在多进程模式,突破GIL的限制,有效利用了现代多核CPU的性能。但默认情况下,OpenERP 只能运行于一台服务器,对于提供SAAS服务或并发很大的情况下,单台服务器的性能是有限的。本文介绍实现 OpenERP 负载平衡的方法和原理。<br /><br />一、架构<br /><br />    ┌────────────────────────────────┐<br />    │                          Nginx                        │<br />    └────────────────────────────────┘<br />            /                      |                      <br />    ┌────────┐    ┌────────┐    ┌────────┐<br />    │OE Server │    │ OE Server │  │ OE Server │<br />    └────────┘    └────────┘    └────────┘<br />            &nbsp;                   |                        /<br />    ┌────────────────────────────────┐<br />    │                      Redis Server                  │<br />    └────────────────────────────────┘<br /><br />注:实现负载平衡的关键点在于 cache 和 session 共享。<br /><br />二、Web 服务器配置<br /><br />WEB 服务器选择 Nginx + upstream 配置,可参考 “使用Nginx Upstream 部署 OpenERP ” http://my.oschina.net/wangbuke/blog/67450 。<br /><br />默认情况下,nginx 采用轮询的方式,将请求分发到多个 OE Server 里。建议改为 ip_hash 方式,如:<br /><br />    upstream bakend {<br />        ip_hash;<br />        server 192.168.0.11:8069;<br />        server 192.168.0.12:8069;<br />    }<br /><br />三、OpenERP 的 Session 和 Cache 处理<br /><br />3.1 OpenERP Web Session 处理<br /><br />OpenERP 中的Session 处理默认用FilesystemSessionStore,使用文件系统存储用户 session  。如 openerp/addons/web/http.py<br /><br />class Root(object):<br />  <br />    def init(self):<br />  <br />        self.session_store = werkzeug.contrib.sessions.FilesystemSessionStore(path)<br />        self.session_lock = threading.Lock()<br /><br />那么我们只要将OpenERP 中的SessionStore,改为 RedisSessionStore,RedisSessionStore 可参考https://gist.github.com/1451947 。<br /><br />修改方法,可以直接修改 http.py 文件。<br />或是和我一样,重写一个库,重载 session_context 方法,这样可以不修改OpenERP的源文件,方便以后升级。<br /><br />3.2 OpenERP LRU Cache 处理<br /><br />openerp/tools/cache.py 中 ormcache 和 ormcache_multi 是 OpenERP 中非常重要的缓存类。OpenERP ORM 大部分的方法调用都会经过 @tools.ormcache 或 @ormcache_multi 修饰。经过修饰后,结果会被缓存,这个缓存是存放于内存中。 这个就是OE在加载一次数据后,第二次会明显快很多的原因。还有,通过web 界面翻译OE术语不能实时生效,也是因为缓存没有更新。<br /><br />可以修改ormcache 和 ormcache_multi 类,以使用 redis 缓存。关键代码如下:<br /><br />    def lookup(self, self2, cr, *args):<br />        key = args[self.skiparg-2:]<br />        key = '%s:%s' % (self.method.name, str(key))<br />        #key = md5(key).hexdigest()<br />        hash_name = self.db_key_template % cr.dbname<br />        value = self.redis.hget(hash_name, key)<br />        if value:<br />            self.stat_hit += 1<br />            return loads(value)<br />        else:<br />            self.stat_miss += 1<br />            value = self.method(self2, cr, *args)<br />            self.redis.hset(hash_name, key, dumps(value, HIGHEST_PROTOCOL))<br />            self.redis.expire(hash_name, self.timeout)<br />            return value<br /><br />缓存的值使用 cPickle 序列化后,将每个键值对存放于 redis 的 哈希表中。<br /><br />3.3 auth_openid 模块<br /><br />auth_openid模块也使用文件系统存储用户登录凭证。如:<br /><br />class OpenIDController(openerp.addons.web.http.Controller):<br /><br />    _store = filestore.FileOpenIDStore(_storedir)<br /><br />如果您启用了这个模块,那么这里也需要修改为存储在redis中。如果没有启用此模块,则无需理会。<br /><br />相关实现可参考,https://github.com/bbangert/openid-redis/blob/master/openidredis/init.py<br /><br />四、OpenERP Cron 处理<br /><br />默认情况下,每个OpenERP Server 实例都会运行一个 cron 进程任务。这里建议只允许一个实例运行CRON。把OpenERP 7.0 的配置参数 max_cron_threads 设置为0 ,即可禁止cron。相关代码如下:<br /><br />    def process_spawn(self):<br />        while len(self.workers_http) < self.population:<br />            self.worker_spawn(WorkerHTTP, self.workers_http)<br />        while len(self.workers_cron) < config['max_cron_threads']:<br />            self.worker_spawn(WorkerCron, self.workers_cron)<br /><br />五、OpenERP Module RegistryManager 处理<br /><br />OpenERP Module Registry 主要负责管理OE的对象。一般是安装或更新的模块时候,会根据定义来更新数据库。 在OE多进程模式下,OE会自动管理 Module Registry ,相关的更新信息会存放在数据库里。RegistryManager  会检测是否有更新,如有更新将会自动清除缓存并重新载入。相关代码如下:<br /><br />    @classmethod<br />    def setup_multi_process_signaling(cls, cr):<br />        if not openerp.multi_process:<br />            return<br /><br />    @classmethod<br />    def check_registry_signaling(cls, db_name):<br />        if openerp.multi_process and db_name in cls.registries:<br /><br />这里,实际上无需做改动,上面只是说明情况。只需让OE运行在多进程模式即可(也就是配置 worker 参数)。<br /><br />六、完成!<br /><br />经过以上几个步骤,可以让OpenERP 运行于多台服务器,通过Redis 分布式缓存处理相关的 Cache 和 Session,从而实现 OpenERP 负载平衡。<br /><br />注:<br />1、本文仅讨论 OpenERP 负载平衡部署方式,并不涉及 Postgresql 和 Redis 的负载平衡,相应的方法请自行搜索。<br />2、鉴于OpenERP SA 官方已不再维护 GTK 客户端,并没有对GTK客户端的情况进行完整测试。<br /><br /><br />



  • OpenERP 7.0 带来了许多新特性,架构上也有许多改进。其中可配置 worker 参数,可使 OpenERP 运行在多进程模式,突破GIL的限制,有效利用了现代多核CPU的性能。但默认情况下,OpenERP 只能运行于一台服务器,对于提供SAAS服务或并发很大的情况下,单台服务器的性能是有限的。本文介绍实现 OpenERP 负载平衡的方法和原理。<br /><br />一、架构<br /><br />    ┌────────────────────────────────┐<br />    │                          Nginx                        │<br />    └────────────────────────────────┘<br />            /                      |                      <br />    ┌────────┐    ┌────────┐    ┌────────┐<br />    │OE Server │    │ OE Server │  │ OE Server │<br />    └────────┘    └────────┘    └────────┘<br />            &nbsp;                   |                        /<br />    ┌────────────────────────────────┐<br />    │                      Redis Server                  │<br />    └────────────────────────────────┘<br /><br />注:实现负载平衡的关键点在于 cache 和 session 共享。<br /><br />二、Web 服务器配置<br /><br />WEB 服务器选择 Nginx + upstream 配置,可参考 “使用Nginx Upstream 部署 OpenERP ” http://my.oschina.net/wangbuke/blog/67450 。<br /><br />默认情况下,nginx 采用轮询的方式,将请求分发到多个 OE Server 里。建议改为 ip_hash 方式,如:<br /><br />    upstream bakend {<br />        ip_hash;<br />        server 192.168.0.11:8069;<br />        server 192.168.0.12:8069;<br />    }<br /><br />三、OpenERP 的 Session 和 Cache 处理<br /><br />3.1 OpenERP Web Session 处理<br /><br />OpenERP 中的Session 处理默认用FilesystemSessionStore,使用文件系统存储用户 session  。如 openerp/addons/web/http.py<br /><br />class Root(object):<br />  <br />    def init(self):<br />  <br />        self.session_store = werkzeug.contrib.sessions.FilesystemSessionStore(path)<br />        self.session_lock = threading.Lock()<br /><br />那么我们只要将OpenERP 中的SessionStore,改为 RedisSessionStore,RedisSessionStore 可参考https://gist.github.com/1451947 。<br /><br />修改方法,可以直接修改 http.py 文件。<br />或是和我一样,重写一个库,重载 session_context 方法,这样可以不修改OpenERP的源文件,方便以后升级。<br /><br />3.2 OpenERP LRU Cache 处理<br /><br />openerp/tools/cache.py 中 ormcache 和 ormcache_multi 是 OpenERP 中非常重要的缓存类。OpenERP ORM 大部分的方法调用都会经过 @tools.ormcache 或 @ormcache_multi 修饰。经过修饰后,结果会被缓存,这个缓存是存放于内存中。 这个就是OE在加载一次数据后,第二次会明显快很多的原因。还有,通过web 界面翻译OE术语不能实时生效,也是因为缓存没有更新。<br /><br />可以修改ormcache 和 ormcache_multi 类,以使用 redis 缓存。关键代码如下:<br /><br />    def lookup(self, self2, cr, *args):<br />        key = args[self.skiparg-2:]<br />        key = '%s:%s' % (self.method.name, str(key))<br />        #key = md5(key).hexdigest()<br />        hash_name = self.db_key_template % cr.dbname<br />        value = self.redis.hget(hash_name, key)<br />        if value:<br />            self.stat_hit += 1<br />            return loads(value)<br />        else:<br />            self.stat_miss += 1<br />            value = self.method(self2, cr, *args)<br />            self.redis.hset(hash_name, key, dumps(value, HIGHEST_PROTOCOL))<br />            self.redis.expire(hash_name, self.timeout)<br />            return value<br /><br />缓存的值使用 cPickle 序列化后,将每个键值对存放于 redis 的 哈希表中。<br /><br />3.3 auth_openid 模块<br /><br />auth_openid模块也使用文件系统存储用户登录凭证。如:<br /><br />class OpenIDController(openerp.addons.web.http.Controller):<br /><br />    _store = filestore.FileOpenIDStore(_storedir)<br /><br />如果您启用了这个模块,那么这里也需要修改为存储在redis中。如果没有启用此模块,则无需理会。<br /><br />相关实现可参考,https://github.com/bbangert/openid-redis/blob/master/openidredis/init.py<br /><br />四、OpenERP Cron 处理<br /><br />默认情况下,每个OpenERP Server 实例都会运行一个 cron 进程任务。这里建议只允许一个实例运行CRON。把OpenERP 7.0 的配置参数 max_cron_threads 设置为0 ,即可禁止cron。相关代码如下:<br /><br />    def process_spawn(self):<br />        while len(self.workers_http) < self.population:<br />            self.worker_spawn(WorkerHTTP, self.workers_http)<br />        while len(self.workers_cron) < config['max_cron_threads']:<br />            self.worker_spawn(WorkerCron, self.workers_cron)<br /><br />五、OpenERP Module RegistryManager 处理<br /><br />OpenERP Module Registry 主要负责管理OE的对象。一般是安装或更新的模块时候,会根据定义来更新数据库。 在OE多进程模式下,OE会自动管理 Module Registry ,相关的更新信息会存放在数据库里。RegistryManager  会检测是否有更新,如有更新将会自动清除缓存并重新载入。相关代码如下:<br /><br />    @classmethod<br />    def setup_multi_process_signaling(cls, cr):<br />        if not openerp.multi_process:<br />            return<br /><br />    @classmethod<br />    def check_registry_signaling(cls, db_name):<br />        if openerp.multi_process and db_name in cls.registries:<br /><br />这里,实际上无需做改动,上面只是说明情况。只需让OE运行在多进程模式即可(也就是配置 worker 参数)。<br /><br />六、完成!<br /><br />经过以上几个步骤,可以让OpenERP 运行于多台服务器,通过Redis 分布式缓存处理相关的 Cache 和 Session,从而实现 OpenERP 负载平衡。<br /><br />注:<br />1、本文仅讨论 OpenERP 负载平衡部署方式,并不涉及 Postgresql 和 Redis 的负载平衡,相应的方法请自行搜索。<br />2、鉴于OpenERP SA 官方已不再维护 GTK 客户端,并没有对GTK客户端的情况进行完整测试。<br /><br /><br />



  • 这个必须顶起来.... 太强大的 book 了...


  • 管理员

    前排,占坐,感谢buke分享如此强大的文章!



  • 强人。<br />buke分享,必定是重量级的



  • 很厉害的呢,这种最适合弄个分支然后提交给官方合并。<br /><br />我觉得普通企业用恐怕需求不大吧,因为 OE 这类应用的瓶颈还是在 DB 上,快来人写 PostgreSQL 负载均衡和高可用文章



  • [quote author=oldrev link=topic=5703.msg14029#msg14029 date=1358770532]<br />很厉害的呢,这种最适合弄个分支然后提交给官方合并。<br /><br />我觉得普通企业用恐怕需求不大吧,因为 OE 这类应用的瓶颈还是在 DB 上,快来人写 PostgreSQL 负载均衡和高可用文章<br />[/quote]<br /><br /> ;D , OpenERP s.a.  don't bird me , I don't bird OpenERP s.a.  <br /><br />1、OpenERP 官方从6.0 开始做SAAS, 他们不缺这个技术。<br />2、此文不对应用场景下结论,需不需要做负载平衡,这个大家自行根据实际情况判断。<br />3、PostgreSQL  的历史和口碑远胜于OE,PostgreSQL 负载均衡方案早就很成熟了,比如pgpool-II 等。<br /><br />SO ....  祝玩的开心 ~<br /><br />



  • 转载到和公司logo更贴近的地方去了。<br /><br />这篇文章是cc协议吧?



  • 好文,谢谢buke分享!如果运营SaaS模式,这个办法很好。<br /><br />我有个问题请教下:<br />如果日订单平均1万单(对于某些较大的电商,这是完全可能的),一年下来就超过300万单。<br />就OE和PostgreSQL而言,这个数据量的处理会有哪些瓶颈呢,有什么办法克服此瓶颈吗?



  • [quote author=Jeff link=topic=5703.msg14033#msg14033 date=1358780186]<br />转载到和公司logo更贴近的地方去了。<br /><br />这篇文章是cc协议吧?<br />[/quote]<br /><br /><br />是,谢谢转载,欢迎转载 ~



  • [quote author=NewZN link=topic=5703.msg14034#msg14034 date=1358782739]<br />好文,谢谢buke分享!如果运营SaaS模式,这个办法很好。<br /><br />我有个问题请教下:<br />如果日订单平均1万单(对于某些较大的电商,这是完全可能的),一年下来就超过300万单。<br />就OE和PostgreSQL而言,这个数据量的处理会有哪些瓶颈呢,有什么办法克服此瓶颈吗?<br />[/quote]<br /><br /><br />一切不给出详细数据来谈优化和性能,都是耍流氓。<br /><br />我这里也就纸上谈兵,空谈几句。。。<br /><br />先说说优化原则:<br />1、[b]不要过早优化[/b]。能通过升级硬件就升级,能多机负载就搞多机。<br />2、[b]空间换时间[/b]。除了不合理的或错误的方法外,所有的优化原理上都是以空间换时间,木有之一。<br /><br />需求分析:<br />首先,日均1万单,那峰值加一个数量级: 10万单/天<br />按照电商行业高峰期算半天:10万单/12小时 = 23单/秒<br />看起来很简单嘛,嘿嘿 ~ 且慢!订单处理就是OE的核心,可以说订单处理基本上把OE都搞了个底朝天,对数据库来说这里基本还都是事务操作,懂不懂就要锁行甚至锁表。。。<br /><br />此外,正常运作起来的ERP,用户少不了查询、统计、报表。。。。这些也是一大块<br /><br />按经验,这里最有可能的瓶颈就是数据库<br /><br />数据库可能的优化:<br />1、换 ORACLE <br />2、请一个DBA<br />3、如果以上2条都没办法,先那么就考虑 PG 集群吧<br /><br />PGPOOL-II = 负载平衡 + 读写分离 。这个配置的好,服务器不是太差的话,对付每秒几百单的需求应该都可以了<br /><br />4、如果读写分离也搞不定,那就做sharding ,横切竖切,除了整一个DBA回来外,当然OpenERP 的DB路由、表结构都得改改了<br /><br />5、还是搞不定? 尼玛这估计都到每秒几千单了,电商当中你都是 TOP 10 了吧,还在折腾OE? 风投的钱不花白不花啊<br /><br /><br />OE Server 部分可能的优化<br />前端优化的好,可以减少很多DB请求,思路也是一样,空间换时间<br /><br />1、多机负载<br />2、缓存!缓存!缓存!一切能缓存的都给缓存了<br />3、尽可能减少实时计算<br />  类似OE 的 funcion field 统统 store = True <br />  OE 的 kanban view 等,把结果缓存,或多建几个冗余表放在数据库里<br />  OE的列表查询,宁可翻页,也不能让客户选择分页数。<br />  一些统计分组查询之类,比如月报表、周报表、日报表,放在固定时间跑批,然后放数据库或缓存<br />  报表尽量在客户端渲染,或者生成之后然后缓存<br /><br />现在能想到的大概就这么了。。。<br /><br />总结:<br />在合理优化的前提下,使用多机负载方法,我认为是可以满足您的需求的。<br /><br />以上罗罗嗦嗦,如果不当之处还请大家斧正。



  • oe 6, 6.1也可以和gunicorn一起 集成,跨CPU,支持多进程模式(多个worker,不过这个参数是在gunicorn中配置),<br />从代码变迁可以看出,oe官方真是说干就干啊,oe7版本就直接借用了 gunicorn 的设计<br />附件中是,oe6.1和gunicorn配置,详细细节<br /><br />谢谢 wangbuke的好文



  • 不好意思,附件多穿了个



  • Thanks for your sharing



  • OE 对于数据库好象没有做 读写分离一样... <br /><br />不知道 pgpool 怎么样玩... <br /><br />



  • 对OE来说,PGPool就是透明的,不需要关心细节,PGPool 会自己处理读写分离。<br /><br />PGPool会将需要复制的SQL发到Master数据库,不需要复制的SQL符合条件的情况下将可能被分发到Slave数据库以达到负载均衡的效果。<br /><br />只需要配置好 PGPool ,然后将OE数据库连接改为连接PGPool即可。<br /><br /><br />



  • PostgresStreamReplication+PGPool-II+HA集群最佳实践<br />



  • 这个必须要关注...<br /><br />太强大的步科妹纸了....


  • 管理员

    谢谢buke分享



  • 太强了,先收藏!


登录后回复
 

与 Odoo 中文社区 的连接断开,我们正在尝试重连,请耐心等待