001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hdfs.shortcircuit; 019 020import java.io.IOException; 021import java.net.InetSocketAddress; 022import java.util.concurrent.TimeUnit; 023 024import org.apache.commons.io.IOUtils; 025import org.apache.commons.logging.Log; 026import org.apache.commons.logging.LogFactory; 027import org.apache.hadoop.HadoopIllegalArgumentException; 028import org.apache.hadoop.hdfs.DFSClient; 029import org.apache.hadoop.hdfs.DFSClient.Conf; 030import org.apache.hadoop.hdfs.DFSConfigKeys; 031import org.apache.hadoop.net.unix.DomainSocket; 032 033import com.google.common.base.Preconditions; 034import com.google.common.cache.Cache; 035import com.google.common.cache.CacheBuilder; 036 037public class DomainSocketFactory { 038 private static final Log LOG = LogFactory.getLog(DomainSocketFactory.class); 039 040 public enum PathState { 041 UNUSABLE(false, false), 042 SHORT_CIRCUIT_DISABLED(true, false), 043 VALID(true, true); 044 045 PathState(boolean usableForDataTransfer, boolean usableForShortCircuit) { 046 this.usableForDataTransfer = usableForDataTransfer; 047 this.usableForShortCircuit = usableForShortCircuit; 048 } 049 050 public boolean getUsableForDataTransfer() { 051 return usableForDataTransfer; 052 } 053 054 public boolean getUsableForShortCircuit() { 055 return usableForShortCircuit; 056 } 057 058 private final boolean usableForDataTransfer; 059 private final boolean usableForShortCircuit; 060 } 061 062 public static class PathInfo { 063 private final static PathInfo NOT_CONFIGURED = 064 new PathInfo("", PathState.UNUSABLE); 065 066 final private String path; 067 final private PathState state; 068 069 PathInfo(String path, PathState state) { 070 this.path = path; 071 this.state = state; 072 } 073 074 public String getPath() { 075 return path; 076 } 077 078 public PathState getPathState() { 079 return state; 080 } 081 082 @Override 083 public String toString() { 084 return new StringBuilder().append("PathInfo{path=").append(path). 085 append(", state=").append(state).append("}").toString(); 086 } 087 } 088 089 /** 090 * Information about domain socket paths. 091 */ 092 final Cache<String, PathState> pathMap = 093 CacheBuilder.newBuilder() 094 .expireAfterWrite(10, TimeUnit.MINUTES) 095 .build(); 096 097 public DomainSocketFactory(Conf conf) { 098 final String feature; 099 if (conf.isShortCircuitLocalReads() && (!conf.isUseLegacyBlockReaderLocal())) { 100 feature = "The short-circuit local reads feature"; 101 } else if (conf.isDomainSocketDataTraffic()) { 102 feature = "UNIX domain socket data traffic"; 103 } else { 104 feature = null; 105 } 106 107 if (feature == null) { 108 LOG.debug("Both short-circuit local reads and UNIX domain socket are disabled."); 109 } else { 110 if (conf.getDomainSocketPath().isEmpty()) { 111 throw new HadoopIllegalArgumentException(feature + " is enabled but " 112 + DFSConfigKeys.DFS_DOMAIN_SOCKET_PATH_KEY + " is not set."); 113 } else if (DomainSocket.getLoadingFailureReason() != null) { 114 LOG.warn(feature + " cannot be used because " 115 + DomainSocket.getLoadingFailureReason()); 116 } else { 117 LOG.debug(feature + " is enabled."); 118 } 119 } 120 } 121 122 /** 123 * Get information about a domain socket path. 124 * 125 * @param addr The inet address to use. 126 * @param conf The client configuration. 127 * 128 * @return Information about the socket path. 129 */ 130 public PathInfo getPathInfo(InetSocketAddress addr, DFSClient.Conf conf) { 131 // If there is no domain socket path configured, we can't use domain 132 // sockets. 133 if (conf.getDomainSocketPath().isEmpty()) return PathInfo.NOT_CONFIGURED; 134 // If we can't do anything with the domain socket, don't create it. 135 if (!conf.isDomainSocketDataTraffic() && 136 (!conf.isShortCircuitLocalReads() || conf.isUseLegacyBlockReaderLocal())) { 137 return PathInfo.NOT_CONFIGURED; 138 } 139 // If the DomainSocket code is not loaded, we can't create 140 // DomainSocket objects. 141 if (DomainSocket.getLoadingFailureReason() != null) { 142 return PathInfo.NOT_CONFIGURED; 143 } 144 // UNIX domain sockets can only be used to talk to local peers 145 if (!DFSClient.isLocalAddress(addr)) return PathInfo.NOT_CONFIGURED; 146 String escapedPath = DomainSocket.getEffectivePath( 147 conf.getDomainSocketPath(), addr.getPort()); 148 PathState status = pathMap.getIfPresent(escapedPath); 149 if (status == null) { 150 return new PathInfo(escapedPath, PathState.VALID); 151 } else { 152 return new PathInfo(escapedPath, status); 153 } 154 } 155 156 public DomainSocket createSocket(PathInfo info, int socketTimeout) { 157 Preconditions.checkArgument(info.getPathState() != PathState.UNUSABLE); 158 boolean success = false; 159 DomainSocket sock = null; 160 try { 161 sock = DomainSocket.connect(info.getPath()); 162 sock.setAttribute(DomainSocket.RECEIVE_TIMEOUT, socketTimeout); 163 success = true; 164 } catch (IOException e) { 165 LOG.warn("error creating DomainSocket", e); 166 // fall through 167 } finally { 168 if (!success) { 169 if (sock != null) { 170 IOUtils.closeQuietly(sock); 171 } 172 pathMap.put(info.getPath(), PathState.UNUSABLE); 173 sock = null; 174 } 175 } 176 return sock; 177 } 178 179 public void disableShortCircuitForPath(String path) { 180 pathMap.put(path, PathState.SHORT_CIRCUIT_DISABLED); 181 } 182 183 public void disableDomainSocketPath(String path) { 184 pathMap.put(path, PathState.UNUSABLE); 185 } 186}