Created
November 1, 2017 01:25
-
-
Save sdhjl2000/b7830daeb483e762fac42ac903f9b51e to your computer and use it in GitHub Desktop.
aop
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 拦截redisclient,可以自动把压测流量导入影子库。 | |
*/ | |
@Configuration | |
@ConditionalOnResource(resources = "/autoconfig/perftest.properties") | |
@ConditionalOnClass({RedisClient.class, TransmittableThreadLocal.class}) | |
public static class RedisClientConfigurer { | |
private volatile Class<?> lastJedisConnectionProxyClass; | |
private static final Logger logger = LoggerFactory.getLogger(RedisClientConfigurer.class); | |
@Bean | |
public SpecifiedBeanPostProcessor perfTestRedisClientPostProcessor() { | |
return new SpecifiedBeanPostProcessor<RedisClient>() { | |
@Override | |
public int getOrder() { | |
return -2; | |
} | |
@Override | |
public Class<RedisClient> getBeanType() { | |
return RedisClient.class; | |
} | |
@Override | |
public Object postProcessBeforeInitialization(RedisClient bean, String beanName) throws BeansException { | |
return bean; | |
} | |
@Override | |
public Object postProcessAfterInitialization(RedisClient bean, String beanName) throws BeansException { | |
try { | |
RedisClient rawbean= (RedisClient) AopTargetUtils.getTarget(bean); | |
Field field=ReflectionUtils.findField(rawbean.getClass(), "jedisPool"); | |
ReflectionUtils.makeAccessible(field); | |
Pool<Jedis> jedisPool= (Pool<Jedis>)ReflectionUtils.getField(field,rawbean); | |
ProxyFactory factory = new ProxyFactory(); | |
factory.addAdvice(new JedisPoolInterceptor()); | |
factory.setTarget(jedisPool); | |
Pool<Jedis> newJedisPool = (Pool<Jedis>) factory.getProxy(); | |
if (lastJedisConnectionProxyClass != null && lastJedisConnectionProxyClass != newJedisPool.getClass()) { | |
logger.error("newJedisPool is not same,this is spring's bug,please upgrade spring-boot's version to 1.3.8.RELEASE or higher! {},{},{},{}", new Object[]{getClass().getClassLoader(), newJedisPool.getClass().getClassLoader(), lastJedisConnectionProxyClass, newJedisPool.getClass()});//如果抱这个错,则有perm区内存溢出的风险,需要关注 | |
} | |
lastJedisConnectionProxyClass = newJedisPool.getClass(); | |
ReflectionUtils.setField(field,rawbean,newJedisPool); | |
return bean; | |
}catch (Exception ex){ | |
logger.error(ex.getMessage(),ex); | |
return bean; | |
} | |
} | |
}; | |
} | |
} | |
@Configuration | |
@ConditionalOnResource(resources = "/autoconfig/perftest.properties") | |
@ConditionalOnClass({TransmittableThreadLocal.class, Jedis.class,Aspect.class}) | |
public static class JedisPerfConfiguration{ | |
@Bean | |
public JedisPerfAspect getJedisPerfAspect(){ | |
return new JedisPerfAspect(); | |
} | |
} | |
package cn.com.duibaboot.ext.autoconfigure.perftest; | |
/** | |
* Created by hujinliang on 2017/10/27. | |
*/ | |
import java.util.List; | |
import com.google.common.collect.ImmutableList; | |
import org.aopalliance.intercept.MethodInterceptor; | |
import org.aopalliance.intercept.MethodInvocation; | |
import redis.clients.jedis.Jedis; | |
/** | |
* {@link MethodInterceptor} | |
*/ | |
public class JedisMethodInterceptor implements MethodInterceptor { | |
public static final Integer PERFDB_INDEX=2; | |
private static final List<String> notInterceptorJedisMethodList = ImmutableList.<String>builder().add("get").add("hget") | |
.add("hgetAll").add("hmget").add("mget") | |
.add("hlen").add("llen").add("strlen") | |
.add("bitcount").add("zcount").add("pfcount") | |
.add("scan").add("hscan").add("sscan").add("zscan") | |
.add("keys").add("hkeys").add("ttl") | |
.build().asList(); | |
JedisMethodInterceptor() { | |
} | |
@Override | |
public Object invoke(MethodInvocation invocation) throws Throwable { | |
String methodName = invocation.getMethod().getName(); | |
//忽略Object基类中的方法 | |
if (methodName.equals("toString") || methodName.equals("hashCode") || methodName.equals("equals")) { | |
return invocation.proceed(); | |
} | |
if (PerfTestContext.isCurrentInPerfTestMode() && !notInterceptorJedisMethodList.contains(methodName)) { | |
((Jedis)invocation.getThis()).select(PERFDB_INDEX); | |
} | |
return invocation.proceed(); | |
} | |
} | |
package cn.com.duibaboot.ext.autoconfigure.perftest; | |
import java.util.List; | |
import java.util.concurrent.ConcurrentHashMap; | |
import cn.com.duiba.wolf.redis.RedisClient; | |
import com.alibaba.ttl.TransmittableThreadLocal; | |
import com.google.common.collect.ImmutableList; | |
import org.aopalliance.intercept.MethodInterceptor; | |
import org.aopalliance.intercept.MethodInvocation; | |
import org.aspectj.lang.ProceedingJoinPoint; | |
import org.aspectj.lang.annotation.Around; | |
import org.aspectj.lang.annotation.Aspect; | |
import org.aspectj.lang.reflect.MethodSignature; | |
import org.slf4j.Logger; | |
import org.slf4j.LoggerFactory; | |
import org.springframework.aop.framework.ProxyFactory; | |
import org.springframework.data.redis.connection.RedisConnection; | |
import org.springframework.data.redis.connection.jedis.JedisConnection; | |
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; | |
import redis.clients.jedis.Jedis; | |
/** | |
* 加入AOP,spring-data-reids执行影子库 | |
*/ | |
public class JedisPoolInterceptor implements MethodInterceptor{ | |
private static final Logger logger = LoggerFactory.getLogger(JedisPoolInterceptor.class); | |
@Override | |
public Object invoke(MethodInvocation invocation) throws Throwable { | |
String methodName = invocation.getMethod().getName(); | |
//忽略Object基类中的方法 | |
if (methodName.equals("getResource")) { | |
Jedis jedis=(Jedis) invocation.proceed(); | |
ProxyFactory factory = new ProxyFactory(); | |
factory.addAdvice(new JedisMethodInterceptor()); | |
factory.setTarget(jedis); | |
Object newJedis = factory.getProxy(); | |
return newJedis; | |
} | |
return invocation.proceed(); | |
} | |
} | |
package cn.com.duibaboot.ext.autoconfigure.perftest; | |
import java.util.List; | |
import java.util.concurrent.ConcurrentHashMap; | |
import com.alibaba.ttl.TransmittableThreadLocal; | |
import com.google.common.collect.ImmutableList; | |
import org.aopalliance.intercept.MethodInterceptor; | |
import org.aopalliance.intercept.MethodInvocation; | |
import org.aspectj.lang.ProceedingJoinPoint; | |
import org.aspectj.lang.annotation.Around; | |
import org.aspectj.lang.annotation.Aspect; | |
import org.aspectj.lang.reflect.MethodSignature; | |
import org.slf4j.Logger; | |
import org.slf4j.LoggerFactory; | |
import org.springframework.aop.framework.ProxyFactory; | |
import org.springframework.beans.BeanUtils; | |
import org.springframework.data.redis.connection.RedisConnection; | |
import org.springframework.data.redis.connection.jedis.JedisConnection; | |
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; | |
/** | |
* 加入AOP,spring-data-reids执行影子库 | |
*/ | |
@Aspect | |
public class JedisPerfAspect { | |
private static final List<String> notInterceptorConectionMethodList = ImmutableList.<String>builder().add("get").add("hGet") | |
.add("hGetAll").add("hMGet").add("mGet") | |
.add("hLen").add("lLen").add("strLen") | |
.add("bitCount").add("zCount") | |
.add("scan").add("hScan").add("sScan").add("zScan") | |
.add("keys").add("hKeys") | |
.build().asList(); | |
private static final Logger logger = LoggerFactory.getLogger(JedisPerfAspect.class); | |
public static ConcurrentHashMap<String, JedisConnectionFactory> shadeDataSource = new ConcurrentHashMap<>(); | |
private final TransmittableThreadLocal<RedisConnection> shadeConnection = new TransmittableThreadLocal<>(); | |
private volatile Class<?> lastJedisConnectionProxyClass; | |
@Around("execution(* org.springframework.data.redis.connection.jedis.JedisConnectionFactory.getConnection(..))") | |
public Object springDataConnectionJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable { | |
MethodSignature signature = (MethodSignature) joinPoint.getSignature(); | |
String methodName = signature.getMethod().getName(); | |
if ("getConnection".equals(methodName)) { | |
JedisConnectionFactory jedisConnectionFactory = (JedisConnectionFactory) joinPoint.getTarget(); | |
JedisConnection connection = (JedisConnection) joinPoint.proceed(); | |
ProxyFactory factory = new ProxyFactory(); | |
factory.addAdvice(new JedisConnectionMethodInterceptor()); | |
factory.setTarget(connection); | |
JedisConnection newConnection = (JedisConnection) factory.getProxy();//getClass().getClassLoader() | |
if (lastJedisConnectionProxyClass != null && lastJedisConnectionProxyClass != newConnection.getClass()) { | |
logger.error("JedisConnectionProxyClass is not same,this is spring's bug,please upgrade spring-boot's version to 1.3.8.RELEASE or higher! {},{},{},{}", new Object[]{getClass().getClassLoader(), newConnection.getClass().getClassLoader(), lastJedisConnectionProxyClass, newConnection.getClass()});//如果抱这个错,则有perm区内存溢出的风险,需要关注 | |
} | |
lastJedisConnectionProxyClass = newConnection.getClass(); | |
if (PerfTestContext.isCurrentInPerfTestMode()) { | |
String dbKey= jedisConnectionFactory.getHostName() + jedisConnectionFactory.getPort(); | |
if(!shadeDataSource.containsKey(dbKey)){ | |
JedisConnectionFactory shadeConnectionFactory = new JedisConnectionFactory(); | |
BeanUtils.copyProperties(jedisConnectionFactory, shadeConnectionFactory); | |
shadeConnectionFactory.afterPropertiesSet(); | |
shadeConnectionFactory.setDatabase(JedisMethodInterceptor.PERFDB_INDEX); | |
shadeDataSource.put(dbKey,shadeConnectionFactory); | |
} | |
shadeConnection.set(shadeDataSource.get(dbKey).getConnection()); | |
} | |
return newConnection; | |
} | |
return joinPoint.proceed(); | |
} | |
/** | |
* {@link MethodInterceptor} | |
*/ | |
private class JedisConnectionMethodInterceptor implements MethodInterceptor { | |
JedisConnectionMethodInterceptor() { | |
} | |
@Override | |
public Object invoke(MethodInvocation invocation) throws Throwable { | |
String methodName = invocation.getMethod().getName(); | |
if (methodName.equals("isPipelined") || methodName.equals("openPipeline") || methodName.equals("isQueueing") | |
|| methodName.equals("isClosed") | |
|| methodName.equals("close") | |
|| methodName.equals("closePipeline")) { | |
return invocation.proceed(); | |
} else if (methodName.equals("getNativeConnection")) { | |
Object nativeConnection = invocation.proceed(); | |
ProxyFactory factory = new ProxyFactory(); | |
factory.addAdvice(new JedisMethodInterceptor()); | |
factory.setTarget(nativeConnection); | |
Object newNativeConnection = factory.getProxy();//getClass().getClassLoader() | |
return newNativeConnection; | |
} | |
return interceptorRedisConnectionMethods(invocation); | |
} | |
} | |
private Object interceptorRedisConnectionMethods(MethodInvocation invocation) throws Throwable { | |
String methodName = invocation.getMethod().getName(); | |
//忽略Object基类中的方法 | |
if (methodName.equals("toString") || methodName.equals("hashCode") || methodName.equals("equals")) { | |
return invocation.proceed(); | |
} | |
Object value = null; | |
if (PerfTestContext.isCurrentInPerfTestMode() && !notInterceptorConectionMethodList.contains(methodName)) { | |
value = invocation.getMethod().invoke(shadeConnection.get(), invocation.getArguments()); | |
} else { | |
value = invocation.proceed(); | |
} | |
return value; | |
} | |
} | |
package cn.com.duibaboot.ext.autoconfigure.perftest; | |
import static cn.com.duibaboot.ext.autoconfigure.perftest.PerfTestAutoConfiguration.TransmittableThreadLocalHolder.threadLocal2PressureTest; | |
import java.util.Properties; | |
import org.apache.ibatis.executor.Executor; | |
import org.apache.ibatis.mapping.MappedStatement; | |
import org.apache.ibatis.plugin.Interceptor; | |
import org.apache.ibatis.plugin.Intercepts; | |
import org.apache.ibatis.plugin.Invocation; | |
import org.apache.ibatis.plugin.Plugin; | |
import org.apache.ibatis.plugin.Signature; | |
import org.apache.ibatis.session.ResultHandler; | |
import org.apache.ibatis.session.RowBounds; | |
import org.slf4j.Logger; | |
import org.slf4j.LoggerFactory; | |
/** | |
* mybatis拦截器,压测时读流量走主库,写流量走影子库 | |
* Created by hujinliang on 2017/9/8. | |
*/ | |
@Intercepts({ @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, | |
RowBounds.class, ResultHandler.class }) }) | |
public class MybatisResetPerfTestInterceptor implements Interceptor { | |
private Logger logger = LoggerFactory.getLogger(this.getClass()); | |
@Override | |
public Object intercept(Invocation invocation) throws Throwable { | |
Boolean val = threadLocal2PressureTest.get(); | |
if (val != null) { | |
threadLocal2PressureTest.remove();//删除压测标记,让后续读请求走主库 | |
} | |
Object o = invocation.proceed(); | |
if (val != null) { | |
threadLocal2PressureTest.set(val);//恢复压测标记 | |
} | |
return o; | |
} | |
@Override | |
public Object plugin(Object target) { | |
return Plugin.wrap(target, this); | |
} | |
@Override | |
public void setProperties(Properties properties) { | |
} | |
} | |
public class AopTargetUtils { | |
/** | |
* 获取 目标对象 | |
* @param proxy 代理对象 | |
* @throws Exception | |
*/ | |
public static Object getTarget(Object proxy) throws Exception { | |
if(!AopUtils.isAopProxy(proxy)) { | |
return proxy;//不是代理对象 | |
} | |
if(AopUtils.isJdkDynamicProxy(proxy)) { | |
return getJdkDynamicProxyTargetObject(proxy); | |
} else { //cglib | |
return getCglibProxyTargetObject(proxy); | |
} | |
} | |
private static Object getCglibProxyTargetObject(Object proxy) throws Exception { | |
Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0"); | |
h.setAccessible(true); | |
Object dynamicAdvisedInterceptor = h.get(proxy); | |
Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised"); | |
advised.setAccessible(true); | |
return ((AdvisedSupport)advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget(); | |
} | |
private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception { | |
Field h = proxy.getClass().getSuperclass().getDeclaredField("h"); | |
h.setAccessible(true); | |
AopProxy aopProxy = (AopProxy) h.get(proxy); | |
Field advised = aopProxy.getClass().getDeclaredField("advised"); | |
advised.setAccessible(true); | |
return ((AdvisedSupport)advised.get(aopProxy)).getTargetSource().getTarget(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment