YARN Dispatcher的原理和应用

AsyncDispatcher Architecture

AsyncDisptcher

AsyncDispatcher Example

Enum AgentEventType

public enum AgentEventType {
    REGISTER,
    UNREGISTER,
    EXPIRED,
    HEARTBEAT,
    JOBLAUNCH,
    JOBKILL
}

AgentEvent

class AgentEvent extends AbstractEvent<AgentEventType> {
    private Agent agent;
    public AgentEvent(AgentEventType type, Agent agent){
        super(type);
        this.agent = agent;
    }

    public Agent getAgent() {
        return agent;
    }
}

JobEvent

class JobEvent extends AgentEvent{
    private int jobId;
    JobEvent(AgentEventType type, Agent agent, int jobId){
        super(type, agent);
        this.jobId = jobId;
    }

    public int getJobId() {
        return jobId;
    }
}
class Agent implements EventHandler<AgentEvent>{
    private final int id;
    private Set<Long> runningJobs = new HashSet<Long>();
    public Agent(int id) {
        this.id = id;
    }


    // hashCode and equals method must be implemented  if the object is used in HashMap as key or HashSet.
    @Override
    public int hashCode() {
        return id;
    }

    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        } else if (obj == null) {
            return false;
        } else if (this.getClass() != obj.getClass()) {
            return false;
        } else {
            Agent other = (Agent) obj;
            return this.id == other.id;
        }
    }

    public int getId() {
        return id;
    }

    @Override
    public String toString() {
        return "Agent: " + id;
    }

    @Override
    public void handle(AgentEvent agentEvent) {
        switch (agentEvent.getType()) {
            case  REGISTER:
                System.out.println("receive register event in agent");
                break;
            case UNREGISTER:
                break;

            case EXPIRED:
                break;
            case JOBLAUNCH:
                JobEvent jobEvent = (JobEvent)agentEvent;
                System.out.println("agent:" +  agentEvent.getAgent()
                    +" receive JOBLAUNCH event, jobId :" + jobEvent.getJobId());

                break;
            case JOBKILL:
                break;
        }
    }
}

AgentManager

class AgentManager extends AbstractService implements EventHandler<AgentEvent>{
    private static final Log LOG = LogFactory.getLog(AgentManager.class);
    private int expireInterval;
    private int monitorInterval;
    private Thread checkerThread;
    private boolean stopped = false;
    private Map<Agent, Long> running = new HashMap<>();

    public AgentManager() {
        super("AgentManager");
    }

    @Override
    protected void serviceInit(Configuration conf) throws Exception {
        super.serviceInit(conf);
        int expireIntvl = conf.getInt("com.baidu.dscheduler.agent.expire.ms",
                1000 * 60 * 10);
        //need some sanity check
        this.expireInterval = expireIntvl;
        this.monitorInterval = expireInterval / 3;
        this.checkerThread = new Thread(new PingChecker());
        this.checkerThread.setName("Ping Checker");
    }

    @Override
    protected void serviceStart() throws Exception {
        super.serviceStart();
        this.checkerThread.start();
    }

    @Override
    protected void serviceStop() throws Exception {
        stopped = true;
        super.serviceStop();
        checkerThread.interrupt();
    }

    @Override
    public void handle(AgentEvent agentEvent) {
        switch (agentEvent.getType()) {
            case  REGISTER:
                System.out.println("receive register request in agent manager.");
                if (!running.containsKey(agentEvent.getAgent())) {
                    running.putIfAbsent(agentEvent.getAgent(), System.currentTimeMillis());
                    agentEvent.getAgent().handle(agentEvent);
                }
                break;
            case UNREGISTER:
                if (running.containsKey(agentEvent.getAgent())) {
                    agentEvent.getAgent().handle(agentEvent);
                    running.remove(agentEvent.getAgent());
                }
                break;
            case EXPIRED:
                break;
            case HEARTBEAT:
                if (running.containsKey(agentEvent.getAgent())) {
                    Agent agent = agentEvent.getAgent();
                    running.put(agent, System.currentTimeMillis());
                    agentEvent.getAgent().handle(agentEvent);
                }
                break;
            case JOBLAUNCH:
            case JOBKILL:
                JobEvent jobEvent = (JobEvent)agentEvent;
                System.out.println(" agent master receive "
                        +" receive "+ jobEvent.getType()
                        +", agent: "+ agentEvent.getAgent()
                        + ", jobId :" + jobEvent.getJobId());
                if (running.containsKey(agentEvent.getAgent())) {
                    Agent agent = agentEvent.getAgent();
                    running.put(agent, System.currentTimeMillis());
                    agentEvent.getAgent().handle(jobEvent);
                }

        }

    }

    private class PingChecker implements Runnable {
        private PingChecker() {
        }

        public void run() {
            while (!AgentManager.this.stopped && !Thread.currentThread().isInterrupted()) {


                synchronized (AgentManager.this) {
                    Iterator<Map.Entry<Agent, Long>> iterator = AgentManager.this.running.entrySet().iterator();
                    long currentTime = System.currentTimeMillis();

                    while (true) {
                        if (!iterator.hasNext()) {
                            break;
                        }

                        Map.Entry<Agent, Long> entry = (Map.Entry) iterator.next();
                        if (currentTime > ((Long) entry.getValue()).longValue() + (long) AgentManager.this.expireInterval) {
                            iterator.remove();
                            //AgentManager.this.expire(entry.getKey());
                            AgentManager.LOG.info("Expired:" + entry.getKey().toString() + " Timed out after " + AgentManager.this.expireInterval / 1000 + " secs");
                        }
                    }
                }

                try {
                    Thread.sleep((long) AgentManager.this.monitorInterval);
                    continue;
                } catch (InterruptedException e) {
                    AgentManager.this.LOG.info( AgentManager.this.getName() + " thread interrupted");
                }
            }
        }
    }
}
public class AsyncDispatcherTest {
    @Test
    public void asyncDispatcherTest1() {
        AgentManager agentManager = new AgentManager();
        Configuration conf = new Configuration();
        AsyncDispatcher dispatcher = new AsyncDispatcher();
        dispatcher.init(conf);
        dispatcher.register(AgentEventType.class, agentManager );
        dispatcher.start();
        dispatcher.getEventHandler().handle(
                new AgentEvent(AgentEventType.REGISTER, new Agent(1)));
        System.out.println("send register request in asyncDispatcherTest1");

        dispatcher.getEventHandler().handle(
                new JobEvent(AgentEventType.JOBLAUNCH, new Agent(1), 2));
        System.out.println("send job launch request in asyncDispatcherTest1");
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        dispatcher.stop();
    }
}

output

send register request in asyncDispatcherTest1
send job launch request in asyncDispatcherTest1
receive register request in agent manager.
receive register event in agent
 agent master receive  receive JOBLAUNCH, agent: Agent: 1, jobId :2
agent:Agent: 1 receive JOBLAUNCH event, jobId :2

AsyncDispatcher Code Analysis

AsyncDispatcher is a service and implements Dispatcher interface.

public class AsyncDispatcher extends AbstractService implements Dispatcher {

Dispatcher

public interface Dispatcher {

  EventHandler getEventHandler();

  void register(Class<? extends Enum> eventType, EventHandler handler);

}

Fields of AsyncDispatcher

 private final BlockingQueue<Event> eventQueue; // used to store events.
   private volatile boolean stopped = false;
  private final EventHandler handlerInstance = new GenericEventHandler();
  private Thread eventHandlingThread;
  protected final Map<Class<? extends Enum>, EventHandler> eventDispatchers;

Constructors of AsyncDispatcher

public AsyncDispatcher() {
    this(new LinkedBlockingQueue<Event>());
  }

  public AsyncDispatcher(BlockingQueue<Event> eventQueue) {
    super("Dispatcher");
    this.eventQueue = eventQueue;
    this.eventDispatchers = new HashMap<Class<? extends Enum>, EventHandler>();
  }

AsyncDispatcher.serviceInit

AsyncDispatcher is a service, so it needs to implement serviceInit, serviceStart, serviceStop methods.

 @Override
  protected void serviceInit(Configuration conf) throws Exception {
    this.exitOnDispatchException =
        conf.getBoolean(Dispatcher.DISPATCHER_EXIT_ON_ERROR_KEY,
          Dispatcher.DEFAULT_DISPATCHER_EXIT_ON_ERROR);
    super.serviceInit(conf);
  }
@Override
  protected void serviceStart() throws Exception {
    //start all the components
    super.serviceStart();
    eventHandlingThread = new Thread(createThread());
    eventHandlingThread.setName("AsyncDispatcher event handler");
    eventHandlingThread.start();
  }

AsyncDispatcher.serviceStop

@Override
  protected void serviceStop() throws Exception {
    if (drainEventsOnStop) {
      blockNewEvents = true;
      LOG.info("AsyncDispatcher is draining to stop, igonring any new events.");
      long endTime = System.currentTimeMillis() + getConfig()
          .getLong(YarnConfiguration.DISPATCHER_DRAIN_EVENTS_TIMEOUT,
              YarnConfiguration.DEFAULT_DISPATCHER_DRAIN_EVENTS_TIMEOUT);

      synchronized (waitForDrained) {
        while (!drained && eventHandlingThread != null
            && eventHandlingThread.isAlive()
            && System.currentTimeMillis() < endTime) {
          waitForDrained.wait(1000);
          LOG.info("Waiting for AsyncDispatcher to drain. Thread state is :" +
              eventHandlingThread.getState());
        }
      }
    }
    stopped = true;
    if (eventHandlingThread != null) {
      eventHandlingThread.interrupt();
      try {
        eventHandlingThread.join();
      } catch (InterruptedException ie) {
        LOG.warn("Interrupted Exception while stopping", ie);
      }
    }

    // stop all the components
    super.serviceStop();
  }

AsyncDispatcher.register, it can register several handler to the same event type.

@SuppressWarnings("unchecked")
  @Override
  public void register(Class<? extends Enum> eventType,
      EventHandler handler) {
    /* check to see if we have a listener registered */
    EventHandler<Event> registeredHandler = (EventHandler<Event>)
    eventDispatchers.get(eventType);
    LOG.info("Registering " + eventType + " for " + handler.getClass());
    if (registeredHandler == null) {
      eventDispatchers.put(eventType, handler);
    } else if (!(registeredHandler instanceof MultiListenerHandler)){
      /* for multiple listeners of an event add the multiple listener handler */
      MultiListenerHandler multiHandler = new MultiListenerHandler();
      multiHandler.addHandler(registeredHandler);
      multiHandler.addHandler(handler);
      eventDispatchers.put(eventType, multiHandler);
    } else {
      /* already a multilistener, just add to it */
      MultiListenerHandler multiHandler
      = (MultiListenerHandler) registeredHandler;
      multiHandler.addHandler(handler);
    }
  }
static class MultiListenerHandler implements EventHandler<Event> {
    List<EventHandler<Event>> listofHandlers;

    public MultiListenerHandler() {
      listofHandlers = new ArrayList<EventHandler<Event>>();
    }

    @Override
    public void handle(Event event) {
      for (EventHandler<Event> handler: listofHandlers) {
        handler.handle(event);
      }
    }

    void addHandler(EventHandler<Event> handler) {
      listofHandlers.add(handler);
    }

  }

The process of dispatcher.getEventHandler().handle( new AgentEvent(AgentEventType.REGISTER, new Agent(1)));

AsyncDispatcher.GenericEventHandler

@Override
  public EventHandler getEventHandler() {
    return handlerInstance;
  }

  class GenericEventHandler implements EventHandler<Event> {
    public void handle(Event event) {
      if (blockNewEvents) {
        return;
      }
      drained = false;

      /* all this method does is enqueue all the events onto the queue */
      int qSize = eventQueue.size();
      if (qSize != 0 && qSize % 1000 == 0
          && lastEventQueueSizeLogged != qSize) {
        lastEventQueueSizeLogged = qSize;
        LOG.info("Size of event-queue is " + qSize);
      }
      int remCapacity = eventQueue.remainingCapacity();
      if (remCapacity < 1000) {
        LOG.warn("Very low remaining capacity in the event-queue: "
            + remCapacity);
      }
      try {
        eventQueue.put(event);
      } catch (InterruptedException e) {
        if (!stopped) {
          LOG.warn("AsyncDispatcher thread interrupted", e);
        }
        // Need to reset drained flag to true if event queue is empty,
        // otherwise dispatcher will hang on stop.
        drained = eventQueue.isEmpty();
        throw new YarnRuntimeException(e);
      }
    };
  }

AsyncDispatcher.createThread

Runnable createThread() {
    return new Runnable() {
      @Override
      public void run() {
        while (!stopped && !Thread.currentThread().isInterrupted()) {
          drained = eventQueue.isEmpty();
          // blockNewEvents is only set when dispatcher is draining to stop,
          // adding this check is to avoid the overhead of acquiring the lock
          // and calling notify every time in the normal run of the loop.
          if (blockNewEvents) {
            synchronized (waitForDrained) {
              if (drained) {
                waitForDrained.notify();
              }
            }
          }
          Event event;
          try {
            event = eventQueue.take();
          } catch(InterruptedException ie) {
            if (!stopped) {
              LOG.warn("AsyncDispatcher thread interrupted", ie);
            }
            return;
          }
          if (event != null) {
            dispatch(event);
          }
        }
      }
    };
  }

AsyncDispatcher.dispatch

protected void dispatch(Event event) {

    Class<? extends Enum> type = event.getType().getDeclaringClass();

    try{
      EventHandler handler = eventDispatchers.get(type);
      if(handler != null) {
        handler.handle(event);
      } else {
        throw new Exception("No handler for registered for " + type);
      }
    } catch (Throwable t) {
      //TODO Maybe log the state of the queue
      LOG.fatal("Error in dispatcher thread", t);
      // If serviceStop is called, we should exit this thread gracefully.
      if (exitOnDispatchException
          && (ShutdownHookManager.get().isShutdownInProgress()) == false
          && stopped == false) {
        Thread shutDownThread = new Thread(createShutDownThread());
        shutDownThread.setName("AsyncDispatcher ShutDown handler");
        shutDownThread.start();
      }
    }
  }

Thinking

Advantages and disadvantages of AsyncDispatcher

  • reduce the wait time.
    function call will block the caller.
  • flexibility.
    You can add additional handler of the event.

Disadvantages of AsyncDispatcher

  • event dispatch is a single thread, it will block when dealing event, so the system will slow down when add too much event to one asynchronous dispatcher. You must use the dispatcher carefully. Sometimes, you can use several asynchronous dispatcher at different service.
  • *
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值