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 com.google.common.annotations.VisibleForTesting; 025import org.apache.commons.io.IOUtils; 026import org.apache.commons.logging.Log; 027import org.apache.commons.logging.LogFactory; 028import org.apache.hadoop.HadoopIllegalArgumentException; 029import org.apache.hadoop.hdfs.DFSClient; 030import org.apache.hadoop.hdfs.DFSClient.Conf; 031import org.apache.hadoop.hdfs.DFSConfigKeys; 032import org.apache.hadoop.net.unix.DomainSocket; 033 034import com.google.common.base.Preconditions; 035import com.google.common.cache.Cache; 036import com.google.common.cache.CacheBuilder; 037import org.apache.hadoop.util.PerformanceAdvisory; 038 039public class DomainSocketFactory { 040 private static final Log LOG = LogFactory.getLog(DomainSocketFactory.class); 041 042 public enum PathState { 043 UNUSABLE(false, false), 044 SHORT_CIRCUIT_DISABLED(true, false), 045 VALID(true, true); 046 047 PathState(boolean usableForDataTransfer, boolean usableForShortCircuit) { 048 this.usableForDataTransfer = usableForDataTransfer; 049 this.usableForShortCircuit = usableForShortCircuit; 050 } 051 052 public boolean getUsableForDataTransfer() { 053 return usableForDataTransfer; 054 } 055 056 public boolean getUsableForShortCircuit() { 057 return usableForShortCircuit; 058 } 059 060 private final boolean usableForDataTransfer; 061 private final boolean usableForShortCircuit; 062 } 063 064 public static class PathInfo { 065 private final static PathInfo NOT_CONFIGURED = 066 new PathInfo("", PathState.UNUSABLE); 067 068 final private String path; 069 final private PathState state; 070 071 PathInfo(String path, PathState state) { 072 this.path = path; 073 this.state = state; 074 } 075 076 public String getPath() { 077 return path; 078 } 079 080 public PathState getPathState() { 081 return state; 082 } 083 084 @Override 085 public String toString() { 086 return new StringBuilder().append("PathInfo{path=").append(path). 087 append(", state=").append(state).append("}").toString(); 088 } 089 } 090 091 /** 092 * Information about domain socket paths. 093 */ 094 final Cache<String, PathState> pathMap = 095 CacheBuilder.newBuilder() 096 .expireAfterWrite(10, TimeUnit.MINUTES) 097 .build(); 098 099 public DomainSocketFactory(Conf conf) { 100 final String feature; 101 if (conf.isShortCircuitLocalReads() && (!conf.isUseLegacyBlockReaderLocal())) { 102 feature = "The short-circuit local reads feature"; 103 } else if (conf.isDomainSocketDataTraffic()) { 104 feature = "UNIX domain socket data traffic"; 105 } else { 106 feature = null; 107 } 108 109 if (feature == null) { 110 PerformanceAdvisory.LOG.debug( 111 "Both short-circuit local reads and UNIX domain socket are disabled."); 112 } else { 113 if (conf.getDomainSocketPath().isEmpty()) { 114 throw new HadoopIllegalArgumentException(feature + " is enabled but " 115 + DFSConfigKeys.DFS_DOMAIN_SOCKET_PATH_KEY + " is not set."); 116 } else if (DomainSocket.getLoadingFailureReason() != null) { 117 LOG.warn(feature + " cannot be used because " 118 + DomainSocket.getLoadingFailureReason()); 119 } else { 120 LOG.debug(feature + " is enabled."); 121 } 122 } 123 } 124 125 /** 126 * Get information about a domain socket path. 127 * 128 * @param addr The inet address to use. 129 * @param conf The client configuration. 130 * 131 * @return Information about the socket path. 132 */ 133 public PathInfo getPathInfo(InetSocketAddress addr, DFSClient.Conf conf) { 134 // If there is no domain socket path configured, we can't use domain 135 // sockets. 136 if (conf.getDomainSocketPath().isEmpty()) return PathInfo.NOT_CONFIGURED; 137 // If we can't do anything with the domain socket, don't create it. 138 if (!conf.isDomainSocketDataTraffic() && 139 (!conf.isShortCircuitLocalReads() || conf.isUseLegacyBlockReaderLocal())) { 140 return PathInfo.NOT_CONFIGURED; 141 } 142 // If the DomainSocket code is not loaded, we can't create 143 // DomainSocket objects. 144 if (DomainSocket.getLoadingFailureReason() != null) { 145 return PathInfo.NOT_CONFIGURED; 146 } 147 // UNIX domain sockets can only be used to talk to local peers 148 if (!DFSClient.isLocalAddress(addr)) return PathInfo.NOT_CONFIGURED; 149 String escapedPath = DomainSocket.getEffectivePath( 150 conf.getDomainSocketPath(), addr.getPort()); 151 PathState status = pathMap.getIfPresent(escapedPath); 152 if (status == null) { 153 return new PathInfo(escapedPath, PathState.VALID); 154 } else { 155 return new PathInfo(escapedPath, status); 156 } 157 } 158 159 public DomainSocket createSocket(PathInfo info, int socketTimeout) { 160 Preconditions.checkArgument(info.getPathState() != PathState.UNUSABLE); 161 boolean success = false; 162 DomainSocket sock = null; 163 try { 164 sock = DomainSocket.connect(info.getPath()); 165 sock.setAttribute(DomainSocket.RECEIVE_TIMEOUT, socketTimeout); 166 success = true; 167 } catch (IOException e) { 168 LOG.warn("error creating DomainSocket", e); 169 // fall through 170 } finally { 171 if (!success) { 172 if (sock != null) { 173 IOUtils.closeQuietly(sock); 174 } 175 pathMap.put(info.getPath(), PathState.UNUSABLE); 176 sock = null; 177 } 178 } 179 return sock; 180 } 181 182 public void disableShortCircuitForPath(String path) { 183 pathMap.put(path, PathState.SHORT_CIRCUIT_DISABLED); 184 } 185 186 public void disableDomainSocketPath(String path) { 187 pathMap.put(path, PathState.UNUSABLE); 188 } 189 190 @VisibleForTesting 191 public void clearPathMap() { 192 pathMap.invalidateAll(); 193 } 194}