fastpath源码的一些对象

本文介绍了 FastPath 插件的主要对象及其作用,包括客服(Agent)、客服管理类(AgentManager)、客服会话(AgentSession)、技能组(Workgroup)、技能组管理类(WorkgroupManager)等,并详细解释了路由分配逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

fastpath在前一章有做了一些介绍,是openfire提供的一种可以实现客服功能的插件。

这里把自己查看源码看到一些主要几个类来说明一下这个fastpath的主要对象。

1.agent

这是对于的客服的实体对象,主要就是记录一些基本的属性,比如:agent的昵称,jid等

public class Agent {

	private static final Logger Log = LoggerFactory.getLogger(Agent.class);

    private static final String LOAD_AGENT =
            "SELECT name, agentJID, maxchats FROM fpAgent WHERE agentID=?";
    private static final String SAVE_AGENT =
            "UPDATE fpAgent SET name=?, agentJID=?, maxchats=? WHERE agentID=?";

    /**
     * The agent session created when the agent joined the service.
     */
    private AgentSession session;

    /**
     * The ceiling on the maximumn number of chats the agent should handle.
     */
    protected int maxChats = 0;

    /**
     * Nickname of the agent.
     */
    private String nickname;

    /**
     * Custom properties for the agent.
     */
    private JiveLiveProperties properties;

    /**
     * The id of the agent.
     */
    private long id;

    /**
     * The XMPP address of the Agent.
     */
    private JID agentJID;

2.agentManager

这个类很好理解就是对agent这个实体类的管理类,openfire中的一些实体对象一样整个代码主要是用一些sql语句实现了基本的增删改查。

public class AgentManager {

	private static final Logger Log = LoggerFactory.getLogger(AgentManager.class);

    private static final String LOAD_AGENTS =
            "SELECT agentID FROM fpAgent";
    private static final String INSERT_AGENT =
            "INSERT INTO fpAgent (agentID, agentJID, name, maxchats, minchats) VALUES (?,?,?,?,?)";
    private static final String DELETE_AGENT =
            "DELETE FROM fpAgent WHERE agentID=?";
    private static final String DELETE_AGENT_PROPS =
            "DELETE FROM fpAgentProp WHERE ownerID=?";

3.agentSession

这个对象顾名思义agent的会话实体。从类的属性可以看出,这个实体是对应一个agent对象,包含了这个agent在各个技能组的聊天会话的集合,加入的技能组集合,以及这个agent的最大的聊天室,出席包的状态等都在这个类中定义。这个是在后面操作中比较常用的类。以及一些事件的监听类

public class AgentSession {

	private static final Logger Log = LoggerFactory.getLogger(AgentSession.class);

    private static final FastDateFormat UTC_FORMAT = FastDateFormat.getInstance("yyyyMMdd'T'HH:mm:ss", TimeZone.getTimeZone("GMT+0"));

    private Presence presence;
    private Collection<Workgroup> workgroups = new ConcurrentLinkedQueue<Workgroup>();
    private Offer offer;
    /**
     * Flag that indicates if the agent requested to get information of agents of all the workgroups
     * where the agent has joined.
     */
    private boolean requestedAgentInfo = false;
    private Map<Workgroup, Queue<ChatInfo>> chatInfos = new ConcurrentHashMap<Workgroup, Queue<ChatInfo>>();
    /**
     * By default maxChats has a value of -1 which means that #getMaxChats will return
     * the max number of chats per agent defined in the workgroup. The agent may overwrite
     * the default value but the new value will not be persisted for future sessions.
     */
    private int maxChats;
    private Agent agent;  private WorkgroupPresence workgroupPresenceHandler;
    private WorkgroupIQHandler workgroupIqHandler;
    private MessageHandler messageHandler;


4.workgroup

这个是技能组的实体对象,这个对象和之前的agent对象有点相同也是包含了基本的一些属性,如:最大聊天数,这是对于技能组而言,offer超时时间等。这里涉及到队列的实体,一个技能组可以包含多个队列,根据队列的等级来显示路由的优先级。提供了创建队列的方法,队列会涉及到路由的实现。这个也是比较主要的类,在后面会经常使用。

public class Workgroup {

	private static final Logger Log = LoggerFactory.getLogger(Workgroup.class);
    private String description = null;
    private Date creationDate;
    private Date modDate;
    private long offerTimeout = -1;
    private long requestTimeout = -1;
    private int maxChats;
    private int minChats;


    private Map<Long, RequestQueue> queues = new HashMap<Long, RequestQueue>();

    /**
     * Custom properties for the workgroup.
     */
    private JiveLiveProperties properties;

    private String workgroupName;
    private String displayName;
    private long id;

5.workgroupManager

这个也是对应的技能组的管理类,这个就比较复杂,涉及的地方比较多,


6.workgroupPresence

这个顾名思义管理发送给技能组的出席包,比如agent加入技能组就是个对应的技能组发送出席包。


7.RequestQueue

这个就是在前面提到的队列,这个实体类,基本属性主要是一些最大会话数,这里就比较涉及多。

用户请求的链表集合,通过集合来添加,删除等基本操作用户的排队请求,通过这个实现当前排队人数的推送等

private LinkedList<UserRequest> requests = new LinkedList<UserRequest>();
agentSession的集合对象,控制在本队列中的agent,比如agent退出等
private AgentSessionList activeAgents = new AgentSessionList();
路由的实现类
/**
     * Dispatcher for the queue.
     */
    private RoundRobinDispatcher dispatcher;

8.Request

这个是个抽象类,就是请求的实体类。

三个实现类:

UserRequest:这个就是上面的队列中的用户请求的实体类,就是用户加入排队的时候发送的包,包括了一些路由的要求比如:agent:优先的agent,rank:排的技能组等级,timeout:队列排队的时间,微秒为单位

TransferRequest:这个是后来实现转接的时候看到,是agent端转接给其他agent进行请求的一个请求类,这里的转接是用涉及到转接的对象,可以转接用户,队列,技能组都可以转接。smack包也提供了对应的方法。

InvitationRequest:这个就是邀请的请求,比如fastpath在路由成功后,邀请用户和agent加入自己创建的聊天室发送的包,就是这个类现实的。


9.RoundRobinDispatcher

这个也是上面一直提到的路由的实现类。整个的fastpath核心逻辑就是路由,就是这个类实现。该类是基于队列的,从构造方法可以看出通过定时器来扫描队列中的用户请求,来实现offer的分发给改队列中的agent,以现实路由功能。

public RoundRobinDispatcher(RequestQueue queue) {
        this.queue = queue;
        agentList = new LinkedList<AgentSession>();
        properties = new JiveLiveProperties("fpDispatcherProp", queue.getID());
        try {
            info = infoProvider.getDispatcherInfo(queue.getWorkgroup(), queue.getID());
        }
        catch (NotFoundException e) {
            Log.error("Queue ID " + queue.getID(), e);
        }
        // Recreate the agentSelector to use for selecting the best agent to receive the offer
        loadAgentSelector();

        // Fill the list of AgentSessions that are active in the queue.  Once the list has been
        // filled this dispatcher will be notified when new AgentSessions join the queue or leave
        // the queue
        fillAgentsList();
        Log.error("after fillAgentsList");
        TaskEngine.getInstance().scheduleAtFixedRate(new TimerTask() {
            @Override
			public void run() {
                checkForNewRequests();
            }
        }, 2000, 2000);
    }

最核心的方法就是分发通过之前的请求时间,来做超时控制,查找最合适的agent分发offer,等待offer是否被接收,要是接收就分发邀请的请求给两端加入聊天室从而实现了路由,如果没接收或者超时就继续循环,如果超时退出,就涉及到溢出,根据溢出的配置来对应的出来,比如取消请求告诉用户等:

public void dispatch(Offer offer) {
        // The time when the request should timeout
    	//        long timeoutTime = System.currentTimeMillis() + info.getRequestTimeout();
    	//这里修改,要是有输入超时时间,不用默认的
        final Request request = offer.getRequest();
        long timeoutTime = System.currentTimeMillis() + ((request.getMetaData().containsKey("timeout"))?Long.parseLong(request.getMetaData().get("timeout").get(0)):info.getRequestTimeout());
        boolean canBeInQueue = request instanceof UserRequest;
        Map<String,List<String>> map = request.getMetaData();
        String initialAgent = map.get("agent") == null || map.get("agent").isEmpty() ? null : map.get("agent").get(0);
        String ignoreAgent = map.get("ignore") == null || map.get("ignore").isEmpty() ? null : map.get("ignore").get(0);
        // Log debug trace
        Log.debug("RR - Dispatching request: " + request + " in queue: " + queue.getAddress());

        // Send the offer to the best agent. While the offer has not been accepted send it to the
        // next best agent. If there aren't any agent available then skip this section and proceed
        // to overflow the current request
        if (!agentList.isEmpty()) {
            for (long timeRemaining = timeoutTime - System.currentTimeMillis();
                 !offer.isAccepted() && timeRemaining > 0 && !offer.isCancelled();
                 timeRemaining = timeoutTime - System.currentTimeMillis()) {

                try {
                    AgentSession session = getBestNextAgent(initialAgent, ignoreAgent, offer);
                    Log.error("AgentSession:");
                    if (session == null && agentList.isEmpty()) {
                        // Stop looking for an agent since there are no more agent available
                    	Log.error("agentList.isEmpty():");
                         break;
                    }
                    else if (session == null || offer.isRejector(session)) {
                    	Log.error("offer.isRejector(session):");
                        initialAgent = null;
                        Thread.sleep(1000);
                    }
                    else {
                        // Recheck for changed maxchat setting
                        Workgroup workgroup = request.getWorkgroup();
                        if (session.getCurrentChats(workgroup) < session.getMaxChats(workgroup)) {
                            // Set the timeout of the offer based on the remaining time of the
                            // initial request and the default offer timeout
                            timeRemaining = timeoutTime - System.currentTimeMillis();
                            //设置offer超时时长,是怕offer超时时长超过request时长
                            offer.setTimeout(timeRemaining < info.getOfferTimeout() ?
                                    timeRemaining : info.getOfferTimeout());

                            // Make the offer and wait for a resolution to the offer
                            if (!request.sendOffer(session, queue)) {
                                // Log debug trace
                                Log.debug("RR - Offer for request: " + offer.getRequest() +
                                        " FAILED TO BE SENT to agent: " +
                                        session.getJID());
                                continue;
                            }
                            // Log debug trace
                            Log.debug("RR - Offer for request: " + offer.getRequest() + " SENT to agent: " +
                                    session.getJID());
                            
                            offer.waitForResolution();
                            // If the offer was accepted, we send out the invites
                            // and reset the offer
                            if (offer.isAccepted()) {
                                // Get the first agent that accepted the offer
                                AgentSession selectedAgent = offer.getAcceptedSessions().get(0);
                                // Log debug trace
                                Log.debug("RR - Agent: " + selectedAgent.getJID() +
                                        " ACCEPTED request: " +
                                        request);
                                // Create the room and send the invitations
                                offer.invite(selectedAgent);
                                
                                //发送监控消息 bywengwh
                                sendUmccWorkMsg(request,selectedAgent);
                                
                                // Notify the agents that accepted the offer that the offer process
                                // has finished
                                for (AgentSession agent : offer.getAcceptedSessions()) {
                                    agent.removeOffer(offer);
                                }
                                if (canBeInQueue) {
                                    // Remove the user from the queue since his request has
                                    // been accepted
                                    queue.removeRequest((UserRequest) request);
                                }
                            }
                        }
                        else {
                            // Log debug trace
                            Log.debug("RR - Selected agent: " + session.getJID() +
                                    " has reached max number of chats");
                        }
                    }
                }
                catch (Exception e) {
                    Log.error(e.getMessage(), e);
                }
            }
        }
        if (!offer.isAccepted() && !offer.isCancelled()) {
            // Calculate the maximum time limit for an unattended request before cancelling it
        	//modify by Condy 2014.03.09   
        	//TODO 未测试
        	long requestTimeOut=(request.getMetaData().containsKey("timeout"))?Long.parseLong(request.getMetaData().get("timeout").get(0)):info.getRequestTimeout();
            long limit = request.getCreationTime().getTime() +
                    (requestTimeOut * (getOverflowTimes() + 1));

            if (limit - System.currentTimeMillis() <= 0 || !canBeInQueue) {
                // Log debug trace
                Log.debug("RR - Cancelling request that maxed out overflow limit or cannot be queued: " + request);
                // Cancel the request if it has overflowed 'n' times
                request.cancel(Request.CancelType.AGENT_NOT_FOUND);
            }
            else {
                // Overflow if request timed out and was not dispatched and max number of overflows
                // has not been reached yet
                overflow(offer);
                // If there is no other queue to overflow then cancel the request
                if (!offer.isAccepted() && !offer.isCancelled()) {
                    // Log debug trace
                    Log.debug("RR - Cancelling request that didn't overflow: " + request);
                    request.cancel(Request.CancelType.AGENT_NOT_FOUND);
                }
            }
        }
    }

这是几个主要的类,对于整个fastpath插件类是很多的,功能很多,还等着大家多看源码去挖掘。其实在看源码的时候,可以观察他们的包的规划,类的命名,fastpath主要是分成:Provider,Handle,dispatcher,event等这几个结尾类。从查看源码明白他们的思路,可能不一定能做到每个类都读一遍,读清楚,但是对于他们的编写习惯有时候也可以借鉴,对自己以后编写也是个好处。虽然现在mvc是风靡java编程。这里就把fastpath做个总结。后面看看会接触到什么新的东西或者自己学一些java的一些知识的时候在贴出来。


转载于:https://my.oschina.net/u/1261308/blog/386619

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值