View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.hadoop.hbase.ipc;
20  
21  import java.util.ArrayList;
22  import java.util.List;
23  import java.util.concurrent.BlockingQueue;
24  import java.util.concurrent.ThreadLocalRandom;
25  import java.util.concurrent.atomic.AtomicInteger;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.apache.hadoop.conf.Configuration;
30  import org.apache.hadoop.hbase.Abortable;
31  import org.apache.hadoop.hbase.HConstants;
32  import org.apache.hadoop.hbase.classification.InterfaceAudience;
33  import org.apache.hadoop.hbase.classification.InterfaceStability;
34  import org.apache.hadoop.util.StringUtils;
35  
36  import com.google.common.base.Preconditions;
37  import com.google.common.base.Strings;
38  
39  @InterfaceAudience.Private
40  @InterfaceStability.Evolving
41  public abstract class RpcExecutor {
42    private static final Log LOG = LogFactory.getLog(RpcExecutor.class);
43  
44    private final AtomicInteger activeHandlerCount = new AtomicInteger(0);
45    private final List<Thread> handlers;
46    private final int handlerCount;
47    private final String name;
48    private final AtomicInteger failedHandlerCount = new AtomicInteger(0);
49  
50    private boolean running;
51  
52    private Configuration conf = null;
53    private Abortable abortable = null;
54  
55    public RpcExecutor(final String name, final int handlerCount) {
56      this.handlers = new ArrayList<Thread>(handlerCount);
57      this.handlerCount = handlerCount;
58      this.name = Strings.nullToEmpty(name);
59    }
60  
61    public RpcExecutor(final String name, final int handlerCount, final Configuration conf,
62        final Abortable abortable) {
63      this(name, handlerCount);
64      this.conf = conf;
65      this.abortable = abortable;
66    }
67  
68    public void start(final int port) {
69      running = true;
70      startHandlers(port);
71    }
72  
73    public void stop() {
74      running = false;
75      for (Thread handler : handlers) {
76        handler.interrupt();
77      }
78    }
79  
80    public int getActiveHandlerCount() {
81      return activeHandlerCount.get();
82    }
83  
84    /** Returns the length of the pending queue */
85    public abstract int getQueueLength();
86  
87    /** Add the request to the executor queue */
88    public abstract void dispatch(final CallRunner callTask) throws InterruptedException;
89  
90    /** Returns the list of request queues */
91    protected abstract List<BlockingQueue<CallRunner>> getQueues();
92  
93    protected void startHandlers(final int port) {
94      List<BlockingQueue<CallRunner>> callQueues = getQueues();
95      startHandlers(null, handlerCount, callQueues, 0, callQueues.size(), port);
96    }
97  
98    protected void startHandlers(final String nameSuffix, final int numHandlers,
99        final List<BlockingQueue<CallRunner>> callQueues,
100       final int qindex, final int qsize, final int port) {
101     final String threadPrefix = name + Strings.nullToEmpty(nameSuffix);
102     for (int i = 0; i < numHandlers; i++) {
103       final int index = qindex + (i % qsize);
104       Thread t = new Thread(new Runnable() {
105         @Override
106         public void run() {
107           consumerLoop(callQueues.get(index));
108         }
109       });
110       t.setDaemon(true);
111       t.setName(threadPrefix + "RpcServer.handler=" + handlers.size() +
112         ",queue=" + index + ",port=" + port);
113       t.start();
114       LOG.debug(threadPrefix + " Start Handler index=" + handlers.size() + " queue=" + index);
115       handlers.add(t);
116     }
117   }
118 
119   protected void consumerLoop(final BlockingQueue<CallRunner> myQueue) {
120     boolean interrupted = false;
121     double handlerFailureThreshhold =
122         conf == null ? 1.0 : conf.getDouble(HConstants.REGION_SERVER_HANDLER_ABORT_ON_ERROR_PERCENT,
123           HConstants.DEFAULT_REGION_SERVER_HANDLER_ABORT_ON_ERROR_PERCENT);
124     try {
125       while (running) {
126         try {
127           CallRunner task = myQueue.take();
128           try {
129             activeHandlerCount.incrementAndGet();
130             task.run();
131           } catch (Throwable e) {
132             if (e instanceof Error) {
133               int failedCount = failedHandlerCount.incrementAndGet();
134               if (handlerFailureThreshhold >= 0
135                   && failedCount > handlerCount * handlerFailureThreshhold) {
136                 String message =
137                     "Number of failed RpcServer handler exceeded threshhold "
138                         + handlerFailureThreshhold + "  with failed reason: "
139                         + StringUtils.stringifyException(e);
140                 if (abortable != null) {
141                   abortable.abort(message, e);
142                 } else {
143                   LOG.error("Received " + StringUtils.stringifyException(e)
144                     + " but not aborting due to abortable being null");
145                   throw e;
146                 }
147               } else {
148                 LOG.warn("RpcServer handler threads encountered errors "
149                     + StringUtils.stringifyException(e));
150               }
151             } else {
152               LOG.warn("RpcServer handler threads encountered exceptions "
153                   + StringUtils.stringifyException(e));
154             }
155           } finally {
156             activeHandlerCount.decrementAndGet();
157           }
158         } catch (InterruptedException e) {
159           interrupted = true;
160         }
161       }
162     } finally {
163       if (interrupted) {
164         Thread.currentThread().interrupt();
165       }
166     }
167   }
168 
169   public static abstract class QueueBalancer {
170     /**
171      * @return the index of the next queue to which a request should be inserted
172      */
173     public abstract int getNextQueue();
174   }
175 
176   public static QueueBalancer getBalancer(int queueSize) {
177     Preconditions.checkArgument(queueSize > 0, "Queue size is <= 0, must be at least 1");
178     if (queueSize == 1) {
179       return ONE_QUEUE;
180     } else {
181       return new RandomQueueBalancer(queueSize);
182     }
183   }
184 
185   /**
186    * All requests go to the first queue, at index 0
187    */
188   private static QueueBalancer ONE_QUEUE = new QueueBalancer() {
189 
190     @Override
191     public int getNextQueue() {
192       return 0;
193     }
194   };
195 
196   /**
197    * Queue balancer that just randomly selects a queue in the range [0, num queues).
198    */
199   private static class RandomQueueBalancer extends QueueBalancer {
200     private final int queueSize;
201 
202     public RandomQueueBalancer(int queueSize) {
203       this.queueSize = queueSize;
204     }
205 
206     public int getNextQueue() {
207       return ThreadLocalRandom.current().nextInt(queueSize);
208     }
209   }
210 }