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