• 消息队列


    消息中间件是一个完备的、易于使用的消息队列系统,替代现有cm/transfer所有的功能,力求解决当前社区提交系统难运维、不通用等弊病,提供一个全流程支持、功能完善、性能可扩展、运维方便、可靠的消息队列及整套提交系统解决方案。 开发代号是NMQ

    长期以来,社区几大产品线(贴吧、空间、知道、ks等)都独立维护着自己的提交系统。虽然产品逻辑和规模不同,但从实现上来讲,面对的问题相近,解决的思 路相似,维护重复、运维分散、人力浪费。为避免重复工作和促进提交集群收敛,我们希望完成一个大社区范畴下消息中间件的统一解决方案。

    • nmq是一套包含服务、运维平台的整体解决方案。

    • 模型上,nmq 推的模型和cm/transfer一样。topic等同于cm,pusher等同于transfer。

    • 除了推模式之外,nmq还支持拉的模型。

    • nmq自身支持提交消息的按产品线、按业务、按性能拆分。cm里用于标示命令号的cmd_no被扩展为product、topic、cmd。可以按product、topic将产品线和业务进行拆分。如果单机性能出现瓶颈,可以将同一个topic拆 分为不同的子topic。

    • nmq不分配自增id;nmq没有和业务相关的字段填充和字段检查的功能。

    • nmq自身支持发送消息时的并发、时序。

    • 专门为nmq设计开发的mqp运维平台让运维上更方便。

    • pusher 支持transfer的延时发送功能,只需简单配置 sending_delay_time

    topic:按业务划分的消息序列,逻辑上的概念。产品线可以根据自己的业务特点划分,可大 可小。一般情况下,大产品线按各个子业务划分为不同的topic(例如:空间可以划分为博客、相册、用户等topic);小产品线可以整体作为一个 topic,如果业务复杂也可以划分为不同的topic。
    子topic: 按性能划分的消息序列,实体上的概念。topic可以按partition-key的自定义规则,拆分为若干个子topic,每个子topic内的消息是严格保证时序的。
    partition-key:消息划分或归属的依据,用于topic拆分子topic、竞争 消费时保证时序、多IDC时主IDC的归属。消息需自己指定partition-key,且只能有一个partition-key。如果不拆分子 topic或不关心时序,可以不指定partition-key。
    竞争模式:同一个模块部署很多机器,但所有机器竞争消费同一份消息,比如c模块+mysql 主从模式下,c模块多机部署,但由于不同机器上的c模块都更新同一个主库,因此每条消息只需要被一个机器上的模块消费即可,不需要每条消息都分发给所有机 器;竞争模式,为以后的主流更新模式。
    多主模式:同竞争模式不同,同一个模块的各个机器,都需要消费全量消息;一般模块内维护全量数据副本的场景下使用;大产品线的专有实现模块,一般是这种模式。

    单击放大查看

    上游模块向nmqproxy发送消息,消息经过消息中间件的nmqproxy转发到对应的topic server模块,topicserver将消息序列化到磁盘上,pusher扫描并读取到最新的消息,然后发送给下游模块。 除了主流的推模型外,nmq也支持“拉”模式,在拉模式下,模块所有的信息都保存在模块本地,模块通过nmq提供的pulllib同nmq保持心跳。

    1.接入

    1.1怎么接入nmq

    产品线之前接入cm/transfer有2种方式:
    c/c++通过nshead+mcpack、php通过ral等方式。 产品线接入nmq与接入cm/transfer一般没有太大变化,一般来说只需要加入以下字段:

    • _product(产品线名称,7个字节以内)

    • _topic(产品线业务名称,7个字节以内)

    • _cmd(需要做的事,与之前cmd_no含义类似,只是_cmd为string,例如"addblog"等)

    1.2注意项

    nmq不再具有cm的id分配功能。解决方法,产品线调idalloc(arch或者space的idalloc)模块进行id分配,然后再提交到nmq;因此接入前先判断是否需要id分配功能。
    下游转发不支持mysql.so和comdb.so。因此如果之前的业务是transfer下游直接是mysql或者comdb的话,需要产品线自行开发下游c或者php模块,实现更新db和comdb功能。

    2.提交请求和响应

    请求

    向nmqproxy提交消息,使用nshead+mcpack2协议,同时兼任mcpack1格式

    	//请求包
    	{
    	_product: "space",
    	_topic: "album",
    	_cmd: "album_add",
    	_partition_key: "10001", //可选
    	_msg_unique_key:12333234, //可选
    	//	消息自定义的内容
    	}
    									

    响应

    响应包中含_error_no和_error_msg字段,表明发送是否成功。注意,响应包中不含消息的原有字段和nmqproxy填充的字段。

    	//响应包
    	{
    	_error_no: (int32)0,
    	_error_msg: "OK",
    	}
    									

    3.字段和规范

    字段名约束:一个下划线开头的字段为保留字段。应用不能使用一个下划线开头的字段名。
    字段值约束:只能使用大小写字母、数字、和下划线。
    必须填的字段:_product、_topic、_cmd。
    长度限制(含''):product字段8个字符,topic字段8个字符,cmd字段16个字符。

    • product是产品线的唯一标示。

    • topic是业务划分的消息序列的表示,详见"名词解释"。

    • cmd是用有意义的字符串表示的命令号。

    • 可选填的字段:_idc。长度限制(含'').product字段8个字符

    【二期才支持】idc是机房的标示,用于多IDC提交。 
    可选填的字段:_partition_key,_msg_unique_key。长度无限制。 
    partition_key用于拆分子topic、多IDC转发,一般使用用户id、吧id作为partition_key。 
    【暂不支持】msg_unique_key用于提交去重,建议使用随机性和区分度非常好的uuid。

    4.配置实例

    1.nmqproxy的ip是用资源定位发布的,因此需要上游模块配置上nmqproxy在zk的服务器和路径。
    注:整个集群公用一组nmqproxy,所以使用的nmqproxy的资源定位是一样。
    2.在nmqproxy中配置本应用的消息的路由信息。
    修改nmqproxy/conf/nmqproxy.conf文件,在[topics]中增加:

    #每个topic的配置
    [.@topic]
    #产品线名称
    product : tieba
    #topic名称
    topic : post
    #是否启用,1或0。可选,默认为1
    enable : 1
    #是否走多IDC提交流程,1或0。可选,默认为0
    multi_idc : 0
    #拆分子topic的个数。可选,默认为1,表示不拆分
    sub_topic_num : 1
    #topic server的url,名称需要和ubclient配置里面的名称的一致,拆分时,自动在后面补0/1…
    topic_server_name_prefix : topic-tieba-post-commit
    pusher_server_name_prefix : pusher-tieba-post
    									

    product和topic是和提交消息里的字段保持一致。 
    最后的topic_server_name_prefix是topic_group的名称,需要和nmqproxy/conf/ubclient.conf保持一致的。
    3.topic server的配置不需要修改

    一个后端模块,想从nmq接受消息,对rd来说,只需要提供该模块的2个配置文件即可。
    建议该模块的模块命名为$(product)_$(module),那么需要下面2个配置文件: 

    • module_$(product)_$(module).conf

    • machine_$(product)_$(module).conf

    模块文件命名规范: 以下游名字来命名,比如下游叫abc模块,如果abc模块只用了某个product的数据,建议名为module_$(product)_abc.conf.如果abc是个很大的系统,用到很多模块的数据,允许module_abc.conf

    module_$(product)_$(module).conf主要配置该模块的模块级配置,包括发送协议、接受的消息类型、并发时序控制、字段复制等等。
    machine_$(product)_$(module).conf主要配置该模块的后端机器具体访问设置,包括ip、端口、超时等配置。
    目前支持本地配置,也支持资源定位(zk/webfoot),该配置文件,同ubclient的配置文件基本一致,但在针对多主模块的配置时,针对每个机器增加了identifer和flag字段。另外多主模式的模块,不支持资源定位方式。

    module_$(product)_$(module).conf + 竞争模式+ mcpack协议

    下面是pusher的下游是c模块,mcpack接收的范例配置, 参考space_follow点击下载

    [modules]
    [.@module]
    #模块名,需要跟ubclient配置里面的名称一致;
    name : space_follow
    #是否启用 1启用 0停用 默认0
    flag : 1
    #是竞争模式还是多主模式;0:竞争,1:多主
    sending_type : 0
    #转发协议配置;
    sending_protocol : mcpack
    #发送窗口大小,需要比线程数大;
    sending_window_size : 10
    #发送线程数。竞争模式为竞争线程数,多主模式下,为每个机器的并发线程数
    sending_thread_num : 8
    #出错后重试的时间间隔(单位MS)
    sending_retry_time: 500
    
    #命令过滤规则 ,接受的命令配置;
    # *表示通配,只能出现在字符串的最后,不能出现在中间
    [..msg_filter]
    @filter:space.follow.*
    
    #时序控制规则,支持按照某个partition key的时序支持
    [..sequence_control]
    #制定mutex key,必须是整形数类型的字段;
    mutex_key : qid
    
    #指定对于 没有PK的命令,是否强制串行处理;
    #1:是:该命令必须等待所有靠前的命令都执行完成,再执行。该命令执行前,靠后的命令也不能执行。(主要用于存在批量命令时,保证时序)。
    #0:否:该命令完全无序£??到了就执行。
    force_sequence_when_no_mutex_key : 1
    
    
    
    #支持任意模块自定义配置、so自定义配置
    [..ext_config]
    
    [..mcpack]
    #发送模式,0:nshead+mcpack, 1:ubrpc
    send_type : 0
    
    #mcpack字段复制
    #在消息中间件中,内部填充字段都是以"_"开头,比如_trans_id, _log_id等,为了兼容,可以在so中将这些字段复制改名;
    [...@mcpack_key_copy]
    from : _transid
    to : trans_id
    
    [...@mcpack_key_copy]
    from : _log_id
    to : log_id
    									

    machine_$(product)_$(module).conf + mcpack协议

    下面是pusher的下游是c模块,mcpack接收的范例配置, 参考space_follow点击下载

    [UbClientConfig]
    [.UbClient]
    
    [..@Service]
    Name  : space_follow
    ConnectAll :  0
    DefaultConnectTimeOut  :  300
    DefaultReadTimeOut  :  1000
    DefaultWriteTimeOut  :  1000
    DefaultMaxConnect  :  10
    #DefaultRetry  :  5
    #LONG / SHORT
    DefaultConnectType  :  SHORT
    #DefaultLinger  :  0
    #ReqBuf  :  100
    #ResBuf  :  100
    #DefaultAsyncWaitingNum  :  100
    [...CurrStrategy]
    ClassName  :  UbClientStrategy
    [...CurrHealthy]
    ClassName  :  UbClientHealthyChecker
    
    [...@Server]
    IP : 10.32.52.30
    Port : 29003 
    [...@Server]
    IP : 10.32.52.31
    Port : 29003 
    									

    module_$(product)_$(module).conf + 竞争模式 + 本地配置

    下面是pusher的下游是php模块,http接收的范例配置. 参考lbs_attent 点击下载

    [modules]
    #下面是一个模块的配置
    
    [.@module]
    #模块名,需要跟ubclient配置里面的名称一致;
    name : lbs_attent
    #是否启用 1启用 0停用
    flag : 1 
    #是竞争模式还是多主模式;0:竞争,1:多主
    sending_type : 0 
    #转发协议配置;
    sending_protocol : http
    #发送窗口大小,需要比线程数大;
    sending_window_size : 10
    #发送线程数。竞争模式为竞争线程数,多主模式下,为每个机器的并发线程数
    sending_thread_num : 8
    #出错后重试的时间间隔(单位MS)
    sending_retry_time: 500 
    
    #命令过滤规则 ,接受的命令配置;
    # *表示通配,只能出现在字符串的最后,不能出现在中间
    [..msg_filter]
    @filter : promo.atten.*
    #@filter : promo.*
    
    #时序控制规则,支持按照某个partition key的时序支持
    [..sequence_control]
    #制定mutex key,必须是整形数类型的字段;
    mutex_key : qid
    
    #指定对于 没有PK的命令,是否强制串行处理;
    #1:是:该命令必须等待所有靠前的命令都执行完成,再执行。该命令执行前,靠后的命令也不能执行。(主要用于存在批量命令时,保证时序)。
    #0:否:该命令完全无序,到了就执行。
    force_sequence_when_no_mutex_key : 1
    
    
    #支持任意模块自定义配置、so自定义配置                                                                                                      
    [..ext_config]
    custom_key : sample
    
    [..http]
    #第二部分
    #用户自定义配置内容
    [...default_conf]
    
    #重试次数,默认为-1,一直重试; 0:不重试;
    max_retry_times : -1
    
    #是否将提交的mcpack作为post数据发送,1发送0不发送,缺省1,0的情况下只往后端发url
    send_pack : 1 
    
    #发送mcpack的方式:0:二进制模式直接post, 1:转化成text,使用post $key=$pack_str 的方式发送;
    # 2: 转换成json,使用post $key=$pack_str 的方式发送;默认为0模式; 
    send_pack_type : 0 
    
    #在send_pack_type=1/2的时候,配置post的key;默认"data”;
    send_pack_key : data
    
    #server 冗余均衡策略: 0:random,对多个ip进行轮询;1:master-slave,只在出错时切换到下一个ip;
    #默认0;
    server_redundancy_policy : 0 
    
    #uri的模版,其中{{}}里面的字段将从mcpack里面获取替换; 支持req_download.uid这种多级的字段;
    uri : /promov1/commit/attention?mod=commit&transid={{_transid}}
    
    #http method, 0:get, 1:post, default: 1
    #http_method : 0
    
    #过滤器,会判断mcpack中的uid字段的值%2后是否为1,只有为1时才发送
    #目前只支持"=" 和 "%" 这两种;
    #filter : {{user_id}}%2=1
    
    #http header, 多个header用\r\n隔开;
    http_header : User-Agent: NuSOAP/0.6.6
    charset=UTF-8
    
    
    #命令号相关配置
    #针对每个不同的命令号,可以重设上面的uri/http_method/filter/send_pack/http_header,覆盖上面的默认配置;
    #[...space_photo_add]
    #uri : /so/test22?service=Commit&pid=test&tk=test&transid={{_transid}}
    #filter : {{is_xxx}}=1
    
    #[...space_post_add]
    #send_pack : 1
    #uri : /so/test33?service=Commit&pid=test&tk=test&transid={{_transid}}
    #filter : {{uid}}%2=1
    
    #mcpack字段复制
    #在消息中间件中,内部填充字段都是以"_"开头,比如_transid, _log_id等,为了兼容,可以在so中将这些字段复制改名;
    #[...@mcpack_key_copy]
    #from : _transid
    #to : trans_id
    									

    machine_$(product)_$(module).conf + 竞争模式 + 本地配置

    下面是pusher的下游是php模块,http接收的范例配置. 参考lbs_attent 点击下载

    [UbClientConfig]                                                                                                                           
    [.UbClient]
    
    #竞争的例子(不用zk时)
    
    [..@Service]
    Name  : lbs_attent
    ConnectAll :  0
    DefaultConnectTimeOut  :  300 
    DefaultReadTimeOut  :  1000
    DefaultWriteTimeOut  :  1000
    DefaultMaxConnect  :  10  
    #DefaultRetry  :  5
    #LONG / SHORT
    DefaultConnectType  :  SHORT
    #DefaultLinger  :  0
    #ReqBuf  :  100
    #ResBuf  :  100
    #DefaultAsyncWaitingNum  :  100
    #声明将要使用的策略类及属性
    [...CurrStrategy]
    ClassName  :  UbClientStrategy
    [...CurrHealthy]
    ClassName  :  UbClientHealthyChecker
    [...@Server]
    IP : 10.40.71.100
    Port : 8016
    [...@Server]
    IP : 10.40.71.101
    Port : 8016
    									

    如果协议使用mcpack和http,但默认so不能满足的功能,请联系nmq维护者。 如果使用其他的转发协议如comdb、memcache,请联系nmq维护者,如果不是通用的需求可能需要产品线自己来开发。 自定义so需要实现若干个回调函数,定义在talk_ext.h中。

    /*
     * @brief 初始化操作,成功返回0,失败返回-1
     *
     * @param server 服务器配置
     * @param [out] handle_out 自定义处理的句柄,如果有的话
     * @param [in] module_conf 自定义处理的命令文件
     *
     * @return 成功返回0, 失败返回-1
     */
    typedef int (*np_init_f)(talk_svr_t * server, void **handle_out, comcfg::Configure *module_conf);
    /*
     * @brief 发送命令逻辑
     *
     * @param server 服务器配置
     * @param handle 自定义处理的句柄,如果有的话
     * @param logdi  logid
     * @param req_head  同步请求头
     * @param buf 数据
     * @param buf_len  数据的长度
     *
     * @return 成功返回0,失败返回-1, 命令格式错误返回1
     */
    typedef int (*np_send_f)(
        talk_svr_t * server,
        void *handle,
        unsigned logdi,
        ub::UbClientManager *ubmgr,
        const char *mod_name,
        void *buf,
        unsigned buf_len);
    /*
     * @brief 析构操作
     *
     * @param server  server信息
     * @param handle 自定义处理的句柄,如果有的话
     *
     * @return 成功返回0,失败返回-1
     */
    typedef int (*np_free_f)(talk_svr_t * server, void **handle);
    
    /*
     * @brief 重设连接
     *
     * @param server server信息
     * @param handle    自定义句柄
     */
    typedef void (*np_reset_f)(talk_svr_t * server, void *handle);
  • 相关阅读:
    纸牌排序
    将年份转换成天干地支
    猜算式
    字符串的简单处理
    九宫格填数字
    扫雷
    嗨喽
    Input.GetAxis与Input.GetAxisRaw区别
    C#中(int)、int.Parse()、int.TryParse()和Convert.ToInt32()的区别
    开发游戏所需知识(知乎转载)
  • 原文地址:https://www.cnblogs.com/lushilin/p/6134803.html
Copyright © 2020-2023  润新知