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