1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.util;
20
21 import java.io.IOException;
22 import java.util.Map;
23 import java.util.concurrent.ConcurrentHashMap;
24 import java.util.concurrent.locks.Lock;
25
26 import org.apache.hadoop.hbase.classification.InterfaceAudience;
27 import org.apache.hadoop.conf.Configuration;
28 import org.apache.hadoop.hbase.Chore;
29 import org.apache.hadoop.hbase.Stoppable;
30 import org.apache.hadoop.hbase.TableName;
31 import org.apache.hadoop.hbase.client.Admin;
32 import org.apache.hadoop.hbase.client.HConnection;
33 import org.apache.hadoop.hbase.client.HConnectionManager;
34 import org.apache.hadoop.hbase.client.HTableInterface;
35 import org.apache.hadoop.hbase.client.RegionLocator;
36 import org.apache.hadoop.hbase.security.User;
37 import org.apache.hadoop.hbase.security.UserProvider;
38 import org.apache.hadoop.security.UserGroupInformation;
39 import org.apache.log4j.Logger;
40
41
42
43
44
45
46
47 @InterfaceAudience.Private
48 public class ConnectionCache {
49 private static Logger LOG = Logger.getLogger(ConnectionCache.class);
50
51 private final Map<String, ConnectionInfo>
52 connections = new ConcurrentHashMap<String, ConnectionInfo>();
53 private final KeyLocker<String> locker = new KeyLocker<String>();
54 private final String realUserName;
55 private final UserGroupInformation realUser;
56 private final UserProvider userProvider;
57 private final Configuration conf;
58
59 private final ThreadLocal<String> effectiveUserNames =
60 new ThreadLocal<String>() {
61 protected String initialValue() {
62 return realUserName;
63 }
64 };
65
66 public ConnectionCache(final Configuration conf,
67 final UserProvider userProvider,
68 final int cleanInterval, final int maxIdleTime) throws IOException {
69 Stoppable stoppable = new Stoppable() {
70 private volatile boolean isStopped = false;
71 @Override public void stop(String why) { isStopped = true;}
72 @Override public boolean isStopped() {return isStopped;}
73 };
74
75 Chore cleaner = new Chore("ConnectionCleaner", cleanInterval, stoppable) {
76 @Override
77 protected void chore() {
78 for (Map.Entry<String, ConnectionInfo> entry: connections.entrySet()) {
79 ConnectionInfo connInfo = entry.getValue();
80 if (connInfo.timedOut(maxIdleTime)) {
81 if (connInfo.admin != null) {
82 try {
83 connInfo.admin.close();
84 } catch (Throwable t) {
85 LOG.info("Got exception in closing idle admin", t);
86 }
87 }
88 try {
89 connInfo.connection.close();
90 } catch (Throwable t) {
91 LOG.info("Got exception in closing idle connection", t);
92 }
93 }
94 }
95 }
96 };
97
98 Threads.setDaemonThreadRunning(cleaner.getThread());
99 this.realUser = userProvider.getCurrent().getUGI();
100 this.realUserName = realUser.getShortUserName();
101 this.userProvider = userProvider;
102 this.conf = conf;
103 }
104
105
106
107
108 public void setEffectiveUser(String user) {
109 effectiveUserNames.set(user);
110 }
111
112
113
114
115 public String getEffectiveUser() {
116 return effectiveUserNames.get();
117 }
118
119
120
121
122
123 public Admin getAdmin() throws IOException {
124 ConnectionInfo connInfo = getCurrentConnection();
125 if (connInfo.admin == null) {
126 Lock lock = locker.acquireLock(getEffectiveUser());
127 try {
128 if (connInfo.admin == null) {
129 connInfo.admin = connInfo.connection.getAdmin();
130 }
131 } finally {
132 lock.unlock();
133 }
134 }
135 return connInfo.admin;
136 }
137
138
139
140
141 public HTableInterface getTable(String tableName) throws IOException {
142 ConnectionInfo connInfo = getCurrentConnection();
143 return connInfo.connection.getTable(tableName);
144 }
145
146
147
148
149 public RegionLocator getRegionLocator(byte[] tableName) throws IOException {
150 return getCurrentConnection().connection.getRegionLocator(TableName.valueOf(tableName));
151 }
152
153
154
155
156
157 ConnectionInfo getCurrentConnection() throws IOException {
158 String userName = getEffectiveUser();
159 ConnectionInfo connInfo = connections.get(userName);
160 if (connInfo == null || !connInfo.updateAccessTime()) {
161 Lock lock = locker.acquireLock(userName);
162 try {
163 connInfo = connections.get(userName);
164 if (connInfo == null) {
165 UserGroupInformation ugi = realUser;
166 if (!userName.equals(realUserName)) {
167 ugi = UserGroupInformation.createProxyUser(userName, realUser);
168 }
169 User user = userProvider.create(ugi);
170 HConnection conn = HConnectionManager.createConnection(conf, user);
171 connInfo = new ConnectionInfo(conn, userName);
172 connections.put(userName, connInfo);
173 }
174 } finally {
175 lock.unlock();
176 }
177 }
178 return connInfo;
179 }
180
181 class ConnectionInfo {
182 final HConnection connection;
183 final String userName;
184
185 volatile Admin admin;
186 private long lastAccessTime;
187 private boolean closed;
188
189 ConnectionInfo(HConnection conn, String user) {
190 lastAccessTime = EnvironmentEdgeManager.currentTime();
191 connection = conn;
192 closed = false;
193 userName = user;
194 }
195
196 synchronized boolean updateAccessTime() {
197 if (closed) {
198 return false;
199 }
200 if (connection.isAborted() || connection.isClosed()) {
201 LOG.info("Unexpected: cached HConnection is aborted/closed, removed from cache");
202 connections.remove(userName);
203 return false;
204 }
205 lastAccessTime = EnvironmentEdgeManager.currentTime();
206 return true;
207 }
208
209 synchronized boolean timedOut(int maxIdleTime) {
210 long timeoutTime = lastAccessTime + maxIdleTime;
211 if (EnvironmentEdgeManager.currentTime() > timeoutTime) {
212 connections.remove(userName);
213 closed = true;
214 }
215 return false;
216 }
217 }
218 }