package io.anyway.hera.httpclient; import io.anyway.hera.collector.MetricCollector; import io.anyway.hera.collector.MetricHandler; import io.anyway.hera.common.MetricQuota; import io.anyway.hera.common.BlockingStackTraceCollector; import io.anyway.hera.common.MetricUtils; import io.anyway.hera.service.NonMetricService; import io.anyway.hera.spring.BeanPostProcessorWrapper; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.http.HttpClientConnection; import org.apache.http.conn.ConnectionPoolTimeoutException; import org.apache.http.conn.ConnectionRequest; import org.apache.http.conn.routing.HttpRoute; import org.apache.http.pool.ConnPoolControl; import org.apache.http.pool.PoolStats; import org.springframework.cglib.proxy.InvocationHandler; import org.springframework.cglib.proxy.Proxy; import org.springframework.util.StringUtils; import java.lang.reflect.Method; import java.util.Collection; import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; /** * Created by yangzz on 17/1/4. */ @NonMetricService public class HttpClientPoolCollector implements BeanPostProcessorWrapper,MetricCollector { private Log logger= LogFactory.getLog(HttpClientPoolCollector.class); private MetricHandler handler; private BlockingStackTraceCollector blockingStackTraceCollector; private final Map<String,ConnPoolControl<HttpRoute>> pool= new LinkedHashMap<String,ConnPoolControl<HttpRoute>>(); public void setHandler(MetricHandler handler){ this.handler= handler; } public void setBlockingStackTraceCollector(BlockingStackTraceCollector blockingStackTraceCollector) { this.blockingStackTraceCollector = blockingStackTraceCollector; } @Override public boolean interest(Object bean) { return bean instanceof ConnPoolControl; } @Override public Object wrapBean(final Object bean, String appId, final String beanName) { Class<?> clazz= bean.getClass(); Class<?>[] interfaces= MetricUtils.getInterfaces(clazz,HttpClientStackTraceRepository.class); Object result= Proxy.newProxyInstance(clazz.getClassLoader(), interfaces, new InvocationHandler() { ConcurrentHashMap<HttpClientConnection,StackTraceElement[]> traceRepository= new ConcurrentHashMap<HttpClientConnection,StackTraceElement[]>(); @Override public Object invoke(Object o, Method method, Object[] args) throws Throwable { String methodName= method.getName(); if("requestConnection".equals(methodName)){ final ConnectionRequest delegate= (ConnectionRequest)method.invoke(bean,args); return new ConnectionRequest(){ @Override public boolean cancel() { return delegate.cancel(); } @Override public HttpClientConnection get(long l, TimeUnit timeUnit) throws InterruptedException, ExecutionException, ConnectionPoolTimeoutException { HttpClientConnection connection= delegate.get(l,timeUnit); traceRepository.put(connection,Thread.currentThread().getStackTrace()); return connection; } }; } else if("releaseConnection".equals(methodName)){ traceRepository.remove(args[0]); } else if("getBlockingStackTrace".equals(methodName)){ return traceRepository.values(); } return method.invoke(bean,args); } }); pool.put((!StringUtils.isEmpty(appId)?appId+":":"")+beanName,(ConnPoolControl<HttpRoute>)result); logger.info("metrics HttpClient pool:" +beanName); return result; } @Override public void destroyWrapper(String appId, String beanName) { pool.remove((!StringUtils.isEmpty(appId)?appId+":":"")+beanName); logger.info("remove HttpClient pool:" +beanName); } @Override public void doCollect() { for(Map.Entry<String,ConnPoolControl<HttpRoute>> each: pool.entrySet()){ Map<String,String> tags= new LinkedHashMap<String,String>(); Map<String,Object> props= new LinkedHashMap<String,Object>(); tags.put("name",each.getKey()); ConnPoolControl<HttpRoute> connPoolControl= each.getValue(); PoolStats stats= connPoolControl.getTotalStats(); props.put("available",stats.getAvailable()); props.put("leased",stats.getLeased()); props.put("max",stats.getMax()); props.put("pending",stats.getPending()); handler.handle(MetricQuota.HTTPCLIENT,tags,props); //资源已满打印堆栈 if(stats.getMax()==stats.getLeased()){ Collection<StackTraceElement[]> stackTraces= ((HttpClientStackTraceRepository)connPoolControl).getBlockingStackTrace(); blockingStackTraceCollector.collect(MetricQuota.HTTPCLIENT,each.getKey(),stackTraces); } } } }