/** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.hbase.regionserver; import static org.apache.hadoop.hbase.util.Bytes.getBytes; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HRegionLocation; import org.apache.hadoop.hbase.KeyValue; import org.apache.hadoop.hbase.NotServingRegionException; import org.apache.hadoop.hbase.client.Get; import org.apache.hadoop.hbase.client.HTable; import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.protobuf.ProtobufUtil; import org.apache.hadoop.hbase.thrift.ThriftServerRunner; import org.apache.hadoop.hbase.thrift.ThriftUtilities; import org.apache.hadoop.hbase.thrift.generated.IOError; import org.apache.hadoop.hbase.thrift.generated.TRowResult; /** * HRegionThriftServer - this class starts up a Thrift server in the same * JVM where the RegionServer is running. It inherits most of the * functionality from the standard ThriftServer. This is good because * we can maintain compatibility with applications that use the * standard Thrift interface. For performance reasons, we can override * methods to directly invoke calls into the HRegionServer and avoid the hop. * <p> * This can be enabled with <i>hbase.regionserver.export.thrift</i> set to true. */ @InterfaceAudience.Private public class HRegionThriftServer extends Thread { public static final Log LOG = LogFactory.getLog(HRegionThriftServer.class); private final HRegionServer rs; private final ThriftServerRunner serverRunner; /** * Create an instance of the glue object that connects the * RegionServer with the standard ThriftServer implementation */ HRegionThriftServer(HRegionServer regionServer, Configuration conf) throws IOException { super("Region Thrift Server"); this.rs = regionServer; this.serverRunner = new ThriftServerRunner(conf, new HBaseHandlerRegion(conf)); } /** * Stop ThriftServer */ void shutdown() { serverRunner.shutdown(); } @Override public void run() { serverRunner.run(); } /** * Inherit the Handler from the standard ThriftServerRunner. This allows us * to use the default implementation for most calls. We override certain calls * for performance reasons */ private class HBaseHandlerRegion extends ThriftServerRunner.HBaseHandler { /** * Whether requests should be redirected to other RegionServers if the * specified region is not hosted by this RegionServer. */ private boolean redirect; HBaseHandlerRegion(final Configuration conf) throws IOException { super(conf); initialize(conf); } /** * Read and initialize config parameters */ private void initialize(Configuration conf) { this.redirect = conf.getBoolean("hbase.regionserver.thrift.redirect", false); } // TODO: Override more methods to short-circuit for performance /** * Get a record. Short-circuit to get better performance. */ @Override public List<TRowResult> getRowWithColumnsTs(ByteBuffer tableName, ByteBuffer rowb, List<ByteBuffer> columns, long timestamp, Map<ByteBuffer, ByteBuffer> attributes) throws IOError { try { byte[] row = getBytes(rowb); HTable table = getTable(getBytes(tableName)); HRegionLocation location = table.getRegionLocation(row, false); byte[] regionName = location.getRegionInfo().getRegionName(); if (columns == null) { Get get = new Get(row); get.setTimeRange(Long.MIN_VALUE, timestamp); Result result = ProtobufUtil.get(rs, regionName, get); return ThriftUtilities.rowResultFromHBase(result); } Get get = new Get(row); for(ByteBuffer column : columns) { byte [][] famAndQf = KeyValue.parseColumn(getBytes(column)); if (famAndQf.length == 1) { get.addFamily(famAndQf[0]); } else { get.addColumn(famAndQf[0], famAndQf[1]); } } get.setTimeRange(Long.MIN_VALUE, timestamp); Result result = ProtobufUtil.get(rs, regionName, get); return ThriftUtilities.rowResultFromHBase(result); } catch (NotServingRegionException e) { if (!redirect) { LOG.warn(e.getMessage(), e); throw new IOError(e.getMessage()); } LOG.debug("ThriftServer redirecting getRowWithColumnsTs"); return super.getRowWithColumnsTs(tableName, rowb, columns, timestamp, attributes); } catch (IOException e) { LOG.warn(e.getMessage(), e); throw new IOError(e.getMessage()); } } } }