注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

widebright的个人空间

// 编程和生活

 
 
 

日志

 
 

freeDiameter源码阅读之消息路由  

2013-07-29 16:27:42|  分类: 程序设计 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

Diameter Base Protocol
http://tools.ietf.org/html/rfc6733

里面定义详细的消息的路由的处理过程,定义了Relay Agents ,Proxy Agents ,Redirect Agents等功能节点。
6. Diameter Message Processing 则给出的整个消息的路由处理过程。

freeDiameter里面并没有完整的实现这几个agent的功能,不过他有定义forward和 route out的extension接口。可以根据需要自己进行复杂的功能,比如负载均很等。其他处理是按照文档的消息路由处理过程来进行的。




libproto.h
============
看看routing相关的函数和结构

/* The extracted list items have the following structure: */
struct rtd_candidate {
struct fd_list chain; /* link in the list returned by the previous fct */
DiamId_t diamid; /* the diameter Id of the peer */
size_t diamidlen; /* cached size of the diamid string */
DiamId_t realm; /* the diameter realm of the peer */
size_t realmlen; /* cached size of realm */
int score; /* the current routing score for this peer, see fd_rt_out_register definition for details */
};


/* Add a peer to the candidates list. */
int fd_rtd_candidate_add(struct rt_data * rtd, DiamId_t peerid, size_t peeridlen, DiamId_t realm, size_t realmlen);

/* Remove a peer from the candidates (if it is found). The search is case-insensitive. */
void fd_rtd_candidate_del(struct rt_data * rtd, uint8_t * id, size_t idsz);


libcore.h
=========
可以看到freeDiameter里面基于callback的 forword 和 out的路由的接口的定义,可以利用这个接口自己慈宁宫一复杂的路由功能。
比如extensions里面的rt_load_balance rt_redirect 等模块。可以结合这些模块的代码和下面的这写接口的注释。
其中注释里面的路由处理过程写的很清楚了。

/*============================================================*/
/* ROUTING */
/*============================================================*/

/* This file contains the definitions of types and functions involved in the routing decisions in freeDiameter,
* and that can be called by extensions.
*
* Three different type of messages must be distinguished:
* - Messages received, and the peer is final recipient (IN messages)
* - Messages received, and the peer is not final recipient (FWD messages)
* - Message is locally generated (OUT messages)
*
* There are three global message queues (in queues.c) and also peers-specific queues (in struct fd_peer).
*
* (*) IN messages processing details:
* - the message is received from the remote peer, a FDEVP_CNX_MSG_RECV event is generated for the peer.
* - the PSM thread parses the buffer, does some verifications, handles non routable messages (fd_msg_is_routable)
* - routable messages are queued in the fd_g_incoming global queue.
* - a thread (routing-in) picks the message and takes the decision if it is handled locally or forwarded,
* based on local capabilities (registered by extensions with fd_disp_app_support).
* - If the message is handled locally, it is queued in fd_g_local.
* - Another thread (dispatch.c) will handle this message and pass it to registered callbacks (see fd_disp_register in libfreeDiameter.h).
*
* (*) FWD messages details:
* - The process is the same as for IN messages, until the routing-in threads makes its decision that the message is not handled locally.
* - If the local peer does not relay message, an error DIAMETER_APPLICATION_UNSUPPORTED is returned.
* - All callbacks registered with fd_rt_fwd_register are called for the message (see bellow).
* - these callbacks will typically do proxying work. Note that adding the route-record is handled by the daemon.
* - Once all callbacks have been called, the message is queued in the global fd_g_outgoing queue.
* - The remaining processing is the same as for OUT messages, as described bellow.
*
* (*) OUT messages details:
* - The message are picked from fd_g_outgoing (they are queued there as result of forwarding process or call to fd_msg_send.)
* - The (routing-out) thread builds a list of possible destinations for the message, as follow:
* - create a list of all known peers in the "OPEN" state.
* - remove from that list all peers that are in a Route-Record AVP of the message, to avoid routing loops.
* - remove also all peers that have previously replied an error message for this message.
* - If the list is empty, create an error UNABLE_TO_DELIVER (note: should we trig dynamic discovery here???) and reply.
* - Otherwise, call all callbacks registered by function fd_rt_out_register, with the list of peers and the message.
* - Order the resulting list of peers by score (see bellow), and sent the message to the peer with highest (positive) score.
* - in case the peer is no longer in the "OPEN" state, send the message to the second peer in the list.
* - if no peer is in OPEN state anymore, restart the process of creating the list.
* - Once a peer has been selected, the message is queued into that peer's outgoing queue.
*
* The following functions allow an extension to register or remove a callback as described above.
*/

/********** Forwarding callbacks: for Proxy operations ***********/

/* Handle to registered callback */
struct fd_rt_fwd_hdl;

/* Message direction for the callback */
enum fd_rt_fwd_dir {
RT_FWD_REQ = 1, /* The callback will be called on forwarded requests only */
RT_FWD_ALL = 2, /* The callback will be called on all forwarded messages (requests and answers )*/
RT_FWD_ANS = 3 /* The callback will be called on answers and errors only */
};

/*
* FUNCTION: fd_rt_fwd_register
*
* PARAMETERS:
* rt_fwd_cb : The callback function to register (see prototype bellow).
* cbdata : Pointer to pass to the callback when it is called. The data is opaque to the daemon.
* dir : One of the RT_FWD_* directions defined above.
* handler : On success, a handler to the registered callback is stored here.
* This handler will be used to unregister the cb.
*
* DESCRIPTION:
* Register a new callback for forwarded messages. See explanations above.
* Note that there is no guaranteed order for the callbacks calls.
*
* RETURN VALUE:
* 0 : The callback is registered.
* EINVAL : A parameter is invalid.
* ENOMEM : Not enough memory to complete the operation
*/
int fd_rt_fwd_register ( int (*rt_fwd_cb)(void * cbdata, struct msg ** msg), void * cbdata, enum fd_rt_fwd_dir dir, struct fd_rt_fwd_hdl ** handler );
/*
* CALLBACK: rt_fwd_cb
*
* PARAMETERS:
* data : pointer to some data that was passed when the callback was registered (optional).
* msg : The message that is being forwarded.
*
* DESCRIPTION:
* This callback is called when a message is forwarded to another peer. It may for example add a Proxy-Info AVP.
* The callback may also choose to handle the message in a more complex form. In that case, it must set *msg = NULL
* and handle it differently. In such case, the forwarding thread will stop processing this message.
*
* RETURN VALUE:
* 0 : Operation complete.
* !0 : An error occurred -- will result in daemon's termination.
*/

/*
* FUNCTION: fd_rt_fwd_unregister
*
* PARAMETERS:
* handler : The handler of the callback that must be unregistered.
* cbdata : Will receive the data registered with the callback, that can be freed if needed.
*
* DESCRIPTION:
* Removes a callback from the list of registered callbacks.
*
* RETURN VALUE:
* 0 : The callback is unregistered.
* EINVAL : A parameter is invalid.
*/
int fd_rt_fwd_unregister ( struct fd_rt_fwd_hdl * handler, void ** cbdata );


/********** Out callbacks: for next hop routing decision operations ***********/

/* Handle to registered callback */
struct fd_rt_out_hdl;

enum fd_rt_out_score {
FD_SCORE_NO_DELIVERY = -70, /* We should not send this message to this candidate */
FD_SCORE_SENT_REDIRECT = -60, /* If this peer previously sent a Redirect indication that applies to this message */
FD_SCORE_INI = -2, /* All candidates are initialized with this value */
FD_SCORE_LOAD_BALANCE = 1, /* Use this to differentiate between several peers with the same score */
FD_SCORE_DEFAULT = 5, /* The peer is a default route for all messages */
FD_SCORE_DEFAULT_REALM = 10, /* The peer is a default route for this realm */
FD_SCORE_REALM = 15, /* The peer belongs to Destination-Realm of the message */
FD_SCORE_REDIR_HOST = 25, /* If there is a redirect rule with ALL_HOST for these message and peer */
FD_SCORE_REDIR_APP = 30, /* If there is a redirect rule with ALL_APPLICATION for these message and peer */
FD_SCORE_REDIR_REALM = 35, /* If there is a redirect rule with ALL_REALM for these message and peer */
FD_SCORE_REDIR_REALM_APP = 40, /* If there is a redirect rule with REALM_AND_APPLICATION for these message and peer */
FD_SCORE_REDIR_USER = 45, /* If there is a redirect rule with ALL_USER for these message and peer */
FD_SCORE_REDIR_SESSION = 50, /* If there is a redirect rule with ALL_SESSION for these message and peer */
FD_SCORE_REDIR_ONCE = 55, /* If there is a redirect rule with DONT_CACHE for these message and peer */
FD_SCORE_FINALDEST = 100 /* If the peer is the final recipient of the message (i.e. matching Destination-Host), it receives a big score. */
};

/*
* FUNCTION: fd_rt_out_register
*
* PARAMETERS:
* rt_out_cb : The callback function to register (see prototype bellow).
* cbdata : Pointer to pass to the callback when it is called. The data is opaque to the daemon.
* priority : Order for calling this callback. The callbacks are called in reverse priority order (higher priority = called sooner).
* handler : On success, a handler to the registered callback is stored here.
* This handler will be used to unregister the cb.
*
* DESCRIPTION:
* Register a new callback to handle OUT routing decisions. See explanations above.
*
* RETURN VALUE:
* 0 : The callback is registered.
* EINVAL : A parameter is invalid.
* ENOMEM : Not enough memory to complete the operation
*/
int fd_rt_out_register ( int (*rt_out_cb)(void * cbdata, struct msg * msg, struct fd_list * candidates), void * cbdata, int priority, struct fd_rt_out_hdl ** handler );
/*
* CALLBACK: rt_out_cb
*
* PARAMETERS:
* cbdata : pointer to some data that was registered with the callback.
* msg : The message that must be sent.
* list : The list of peers to which the message may be sent to, as returned by fd_rtd_candidate_extract
*
* DESCRIPTION:
* This callback must attribute a score (preferably from FD_SCORE_*) to each candidate peer in the list.
* Once all registered callbacks have been called, the message is sent to the candidate with the highest score.
* Note that each callback must *add* its locally-attributed score to the candidate current "score" parameter, not replace it!
* Note also that this callback must be re-entrant since it may be called by several threads at the same time
* (for different messages)
*
* RETURN VALUE:
* 0 : Operation complete.
* !0 : An error occurred.
*/

/*
* FUNCTION: fd_rt_out_unregister
*
* PARAMETERS:
* handler : The handler of the callback that must be unregistered.
* cbdata : Will receive the data registered with the callback, that can be freed if needed.
*
* DESCRIPTION:
* Removes a callback from the list of registered callbacks.
*
* RETURN VALUE:
* 0 : The callback is unregistered.
* EINVAL : A parameter is invalid.
*/
int fd_rt_out_unregister ( struct fd_rt_out_hdl * handler, void ** cbdata );








routing_dispatch.c
==================
相关的函数实现大多都在这个文件里面

在前面的文章“freeDiameter源码阅读之消息队列和消息处理流程”里面已经大概review过这个文件的代码了。
再次过一下前面文章,然后这次主要关注路由接口的callback的调用的处理和消息的路由过程。

接收到消息的路由过程
====================
/* The ROUTING-IN message processing */
static int msg_rt_in(struct msg ** pmsg
(1) 本地的avp, 就直接本地处理了
is_dest_host
fd_fifo_post(fd_g_local, pmsg)
(2) Destination-Host avp 为空没有说设置并且是本地支持的app,就也还是本地处理
is_local_app
(3) 不是本地host,而且配置支持forwarding
if ((is_dest_host == NO) || (is_dest_realm == NO) )
if ( fd_g_config->cnf_flags.no_fwd)

// 遍历所有注册的 forward 接口。
for ( li = (is_req ? rt_fwd_list.next : rt_fwd_list.prev) ; *pmsg && (li != &rt_fwd_list) ; li = (is_req ? li->next : li->prev) ) {
如果其中有某一个 extension 把消息指针设置为NULL,就表示消息已经由 extension 自己处理了。 就不会再做第4步的转发了。

(4) 添加到发送出去的链表或者本地的
CHECK_FCT(fd_fifo_post(fd_g_outgoing, pmsg) );




往外发送消息的路由过程,包含forward 转发的
==========================================
/* The ROUTING-OUT message processing */
static int msg_rt_out(struct msg ** pmsg
(1) 对应answer消息的路由
fd_msg_answ_getq
fd_msg_source_get
先找到answer对应的request 消息 (freeDiameter应该在内存里面缓存有所有的request消息了,还没有answer的都在它那个队列里面)
然后根据 request 找到来源 peer,直接把answer发送过去。

(2) 根据libcore.h里面的注释处理
遍历所有正常工作的peer,排除掉那些已经在 Route-Records 里面的peer(应该处理的peer都加上 Route-Records了),这个排除掉之后可以编码重复处理的死循环路由。
上面的过程中使用下面这三个函数,建立一个 struct rt_data 结构的信息。
fd_rtd_init
fd_rtd_candidate_add
fd_rtd_candidate_del

(3) 对前面构建的struct rt_data ,初始化分值,然后遍历所有的 rt_out的extension 接口,调用所有扩展注册的函数。
for ( li = rt_out_list.prev ; li != &rt_out_list ; li = li->prev ) {
ret = (*rh->rt_out_cb)(rh->cbdata, *pmsg, candidates)

extension可以自己负责处理这个消息,并且把消息指针赋值为NULL,则跳过后续步骤直接返回。

(4) 对前面的处理分数,进行排序,只保留一个分数最高的,如果有多个同样高分数的,随机选择一个。
/* Order the candidate peers by score attributed by the callbacks */
CHECK_FCT( fd_rtd_candidate_reorder(candidates) )
把消息发往最终的路由结果得到peer去。
fd_peer_getbyid( c->diamid, c->diamidlen, 0, (void *)&peer
fd_out_send(pmsg, NULL, peer, 0





回头来看看rt_load_balance的代码,只是根据peer的负载,随机初始化各个peer的 route的分值,起到负载均衡的作用。
不过这个只是根据消息来做负载均衡的,可能并不是所有情况都试用。比如希望同一个sesion的消息被发送到同一个peer上去更好一些吧。




  评论这张
 
阅读(1017)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017