当前位置:网站首页>How do the general bottom buried points do?

How do the general bottom buried points do?

2020-11-06 01:15:50 Yin Jihuan

It takes time to monitor the database in the program , Want to automatically transfer link tracking information in the underlying framework , These needs often come across , Especially when building the infrastructure .

There is only one core goal , That is to encapsulate it at the bottom , It doesn't need to be cared by the upper users . Today, I'd like to talk to you about how to deal with the common embedded point method of the underlying extension .

The framework has its own extension point

If you use a framework when you design it , It is very convenient to reserve extension points . such as Mybatis Interceptor , We can deal with... In the interceptor Sql monitor , rewrite .

Like Ali's Sentinel frame , Can pass SPI To expand Slot, Adjust the order of arrangement , Add custom Slot To realize current limiting alarm, etc .

The quality of open source frameworks varies , There's something better designed in the early days , There are plenty of extension points , Convenient for users . There are also some that are less comprehensive , You need to extend it when you use it , Found no extension point found , For scenarios where the framework itself does not provide extension points , Please go on to the following .

Modify source code

If the framework has no extension points , The most direct way is to modify the source code of the open source framework to expand the functions you want , The usual way is to clone the source code into its own private repository , And then modify , test , Repackage for use .

Like we used XXL-JOB Do task scheduling , Also changed some code , The configuration information of monitoring notification is extended in the interface , By default, only email is supported , It can be extended to mobile phones , Nails, etc .

The bad thing about modifying the source code is that it needs to be aligned with the version of the original framework , If it's not aligned , It's OK to change it . Misalignment means that something has been fixed bug And some new features , Can't use . To align , You need to constantly merge new versions of the code into your own branches .

There are many other companies , It's an open source version , Build your own version of the company , The follow-up is to follow the internal use requirements to expand and maintain , It doesn't synchronize with the community version , If you have a dedicated team to do this , It's also a way .

File with the same name is covered

The way to change the source code needs to synchronize the new version of the code frequently , Sometimes you just want to modify a class , For example, some operations at the bottom are monitored by buried points , If the framework itself does not provide extension points, you can only change the source code to achieve .

In fact, there is a way to take advantage of it , It is to create as like as two peas you want to modify in the project , Package name + The categories are the same , The same is true for method names , You can change the implementation of the method . So you can cover jar The class in the package , It has something to do with the order in which the classes are loaded , Load your own definition first .

The advantage of this approach is that you don't have to synchronize new versions of code often , If you're using an updated version of the framework , As long as the package name and class name remain unchanged , You only cover that class , New features and fixed bug No impact .

Cut in the face

Facets are very useful when doing a lot of uniform processing , The same is true for the scene where the bottom buried point is made .

For example, we need to focus on Mongodb All operations are monitored at the buried point , You can modify MongoDB Driver source code , You can create a file with the same name to overlay , There are many ways , Find a fit , And the most important thing to achieve the requirements .

With Spring In the operation Mongodb To illustrate , stay Spring Data Mongodb China will MongoTemplate To operate Mongodb. The easiest way is to go straight to MongoTemplate Class to be buried , So that all operations can be monitored .

Cut directly to with a section MongoTemplate In all ways , And then bury it , It's easy .

  
  1. @Aspect
  2. public class MongoTemplateAspect {
  3. @Pointcut("execution(* org.springframework.data.mongodb.core.MongoTemplate.*(..))")
  4. public void pointcut() {}
  5. @Around("pointcut()")
  6. public Object around(final ProceedingJoinPoint pjp) throws Throwable {
  7. String callMethod = pjp.getSignature().getDeclaringType().getSimpleName() + "." + pjp.getSignature().getName();
  8. Map<String, Object> data = new HashMap<>();
  9. data.put("params", JsonUtils.toJson(pjp.getArgs()));
  10. return CatTransactionManager.newTransaction(() -> {
  11. try {
  12. return pjp.proceed();
  13. } catch (Throwable throwable) {
  14. throw new RuntimeException(throwable);
  15. }
  16. }, "Mongo", callMethod, data);
  17. }
  18. }

And such as , You also want to monitor Redis dependent ,Redis It's also with Spring Integrated framework , Then there is RedisTemplate This class , The same can be done with facets .

be based on Template Class to bury some , It's relatively upper level , If you want to monitor at the bottom , That is to say Connection This floor ,Template The operations are based on Connection To achieve .

Again, we can replace... With sections Connection Related implementation , You can get a slice, for example Connection Methods , Then replace Connection The object of monitoring is the object with buried point monitoring .

  
  1. @Aspect
  2. public class RedisAspect {
  3. @Pointcut("target(org.springframework.data.redis.connection.RedisConnectionFactory)")
  4. public void connectionFactory() {}
  5. @Pointcut("execution(org.springframework.data.redis.connection.RedisConnection *.getConnection(..))")
  6. public void getConnection() {}
  7. @Pointcut("execution(org.springframework.data.redis.connection.RedisClusterConnection *.getClusterConnection(..))")
  8. public void getClusterConnection() {}
  9. @Around("getConnection() && connectionFactory()")
  10. public Object aroundGetConnection(final ProceedingJoinPoint pjp) throws Throwable {
  11. RedisConnection connection = (RedisConnection) pjp.proceed();
  12. return new CatMonitorRedisConnection(connection);
  13. }
  14. @Around("getClusterConnection() && connectionFactory()")
  15. public Object aroundGetClusterConnection(final ProceedingJoinPoint pjp) throws Throwable {
  16. RedisClusterConnection clusterConnection = (RedisClusterConnection) pjp.proceed();
  17. return new CatMonitorRedisClusterConnection(clusterConnection);
  18. }
  19. }

CatMonitorRedisConnection Medium to primary RedisConnection Be enhanced , It doesn't affect the original RedisConnection The function of .

  
  1. public class CatMonitorRedisConnection implements RedisConnection {
  2. private final RedisConnection connection;
  3. private CatMonitorHelper catMonitorHelper;
  4. public CatMonitorRedisConnection(RedisConnection connection) {
  5. this.connection = connection;
  6. this.catMonitorHelper = new CatMonitorHelper();
  7. }
  8. @Override
  9. public byte[] get(byte[] key) {
  10. return catMonitorHelper.execute(RedisCommand.GET, key, () -> connection.get(key));
  11. }
  12. }

Java Agent

Java Agent You can change the bytecode of a loaded class at run time , We can add code logic that we need to monitor . There is no need to modify the original code , Zero invasive .

You can see it in a lot of great open source frameworks Java Agent Application , image APM frame SkyWalking, Asynchronously passing context transmittable-thread-local etc. .

Java Agent Compared with other ways , There is still a certain threshold , After all, it is not a technical point that is often used in daily development . If you want to understand this extension , You can take a look at the source code of some open source frameworks that have been used , I'll know how to use it . Post a paragraph below transmittable-thread-local In the thread pool to expand the code bar , The main thing is to make use of javassist Operation bytecode .

  
  1. try {
  2. final CtMethod afterExecute = clazz.getDeclaredMethod("afterExecute", new CtClass[]{runnableClass, throwableClass});
  3. // unwrap runnable if IsAutoWrapper
  4. String code = "$1 = com.alibaba.ttl.threadpool.agent.internal.transformlet.impl.Utils.unwrapIfIsAutoWrapper($1);";
  5. logger.info("insert code before method " + signatureOfMethod(afterExecute) + " of class " + afterExecute.getDeclaringClass().getName() + ": " + code);
  6. afterExecute.insertBefore(code);
  7. modified = true;
  8. } catch (NotFoundException e) {
  9. // clazz does not override afterExecute method, do nothing.
  10. }

About author : Yin Jihuan , Simple technology enthusiasts ,《Spring Cloud Microservices - Full stack technology and case analysis 》, 《Spring Cloud Microservices introduction Actual combat and advanced 》 author , official account Ape world Originator .

I have compiled a complete set of learning materials , Those who are interested can search through wechat 「 Ape world 」, Reply key 「 Learning materials 」 Get what I've sorted out Spring Cloud,Spring Cloud Alibaba,Sharding-JDBC Sub database and sub table , Task scheduling framework XXL-JOB,MongoDB, Reptiles and other related information .

版权声明
本文为[Yin Jihuan]所创,转载请带上原文链接,感谢