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 */
018 package org.apache.hadoop.hdfs.shortcircuit;
019
020 import java.io.IOException;
021 import java.net.InetSocketAddress;
022 import java.util.concurrent.TimeUnit;
023
024 import org.apache.commons.io.IOUtils;
025 import org.apache.commons.logging.Log;
026 import org.apache.commons.logging.LogFactory;
027 import org.apache.hadoop.HadoopIllegalArgumentException;
028 import org.apache.hadoop.hdfs.DFSClient;
029 import org.apache.hadoop.hdfs.DFSClient.Conf;
030 import org.apache.hadoop.hdfs.DFSConfigKeys;
031 import org.apache.hadoop.net.unix.DomainSocket;
032
033 import com.google.common.base.Preconditions;
034 import com.google.common.cache.Cache;
035 import com.google.common.cache.CacheBuilder;
036 import org.apache.hadoop.util.PerformanceAdvisory;
037
038 public 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 }