一个复杂的系统,可能外部一个web请求,调用到服务端之后,会变成多个请求,可能是再次请求外部,也可能是请求外部的DB,这时候就面临一个问题,就是一个请求,如何不被重复发送,例如根据userId在数据库查询用户信息,这个操作,可能会由于新人改代码,明明线程内已经请求过一次了,还会再继续请求,这个时候就多了一次网络开销。
这种问题如何避免呢?也可能有答案,就是通过review代码的形式,之前已经获取的用户信息中,放在一个变量中,把这个变量不断的传递下去,先从这个变量中获取数据,如果变量中没有,则从远端(例如数据库端)获取这个数据,但是这样有个问题,就是这个变量会冗余的作为方法体的参数,看起来有点不爽。
所以,基于此,我觉得可以尝试用ThreadLocal来缓存一个线程中以及调用过的方法的返回结果。这样的话,非常复杂的系统,再也不用反复review代码去看有哪些调用是被重复搞的,也不用再通过上下文去传递变量了。
设计图如下:
上代码(目前还是一个初级版本 https://github.com/iamzhongyong/RMICache ):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
package rmicache;
import java.util.HashMap;
import java.util.Map;
/** * 缓存信息的持有
*
*/
public class RMICacheHolder {
private RMICacheHolder() {}
private static RMICacheHolder rmiCacheHolder = new RMICacheHolder();
/** 缓存的实体对象, map结构,一个线程体内,可能缓存多个方法的对象 */
private ThreadLocal<Map<String /*方法的签名信息*/,Object/*线程缓存的对象*/>> cacheEntry = new ThreadLocal<Map<String,Object>>(){
@Override
protected Map<String, Object> initialValue() {
return new HashMap<String,Object>();
}
};
/**构造单例*/
public static RMICacheHolder newInstance(){
return rmiCacheHolder;
}
/**根据方法签名,获取缓存对象,如果没有,则通过callback的形式来放入到缓存中*/
public Object getEntry(String methedSign,RMICacheCallback callback){
Map<String,Object> cacheObject = RMICacheHolder.newInstance().cacheEntry.get();
Object cacheValue = cacheObject.get(methedSign);
if(null == cacheValue){
cacheValue = callback.RMIGet();
cacheObject.put(methedSign, cacheValue);
}
return cacheValue;
}
/**根据方法的签名,获取方法的缓存对象*/
public Object getEntry(String methodSign){
Map<String,Object> cacheObject = RMICacheHolder.newInstance().cacheEntry.get();
return cacheObject.get(methodSign);
}
/**缓存之中,放入数据*/
public void putEntry(String methodSign,Object obj){
Map<String,Object> cacheObject = RMICacheHolder.newInstance().cacheEntry.get();
cacheObject.put(methodSign, obj);
}
/**清理线程缓存中的数据,由于现在大多数都是基于线程池的使用,所以这不清理操作必须存在*/
public void clearThreadLocal(){
RMICacheHolder.newInstance().cacheEntry.set( new HashMap<String,Object>());
}
} |
下面一个是一个callback的接口定义:
1
2
3
4
5
6
7
8
9
10
|
package rmicache;
public interface RMICacheCallback {
/**
* 远程获取这个结果
* @return
*/
public Object RMIGet();
} |
结合一个例子使用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
package test;
import rmicache.RMICacheCallback;
import rmicache.RMICacheHolder;
/** * 辅助进行测试的方法
*
*/
public class BizService {
/**
* 假设这个数据的获取非常消耗时间,在一个线程处理过程中,由于逻辑复杂,不能保证这个方法被人调用多次
* 如果系统对于高并发和响应时间有很高的要求,那么多一个耗时的调用,是非常致命的。
*/
public UserDO getUserDOByID(Long userId){
try {
Thread.sleep( 1000 );
} catch (InterruptedException e) {
}
UserDO user = new UserDO();
user.setName( "iamzhongyong_" +Thread.currentThread().getName());
user.setAge( 18 );
return user;
}
/**
* 远程的包装类
*/
public UserDO getUserDOByIdWraper( final Long userId){
return (UserDO) RMICacheHolder.newInstance().getEntry( "getUserDOByID" , new RMICacheCallback() {
public Object RMIGet() {
return getUserDOByID(userId);
}
});
}
} |
测试类:
1
2
3
4
5
6
|
直接调用远程,方法调用三次,耗时: 3004
三次包装类调用,方法调用三次,有缓存,耗时: 1010
线程数据清理,调用一次包装类,耗时: 1000
异步方法调用三次,一次远程,一次缓存,耗时: 2001
异步方法调用三次,全部是远程,耗时: 3001
线程缓存清理后,异步方法调用三次,一次远程,一次缓存,耗时: 2000
|
相关推荐
客户端与服务端的代码 RMI全名叫做 remote method invocation
远程方法调用 -- Remote Method Invocation-
bazel项目中,通过使用远程缓存bazel_remote_cache 服务,进行编译加速。 受限于单服务的访问和读写能力受限,进行优化升级。 通过使用nginx uri一致性哈希,将请求分发到10台缓存服务器上,总容量提升10倍,经过...
JDK9-JSE- Java Remote Method Invocation API Guide-2
JDK10-Java Platform, Standard Edition ,Java Remote Method Invocation API Guide-2
Java Platform, Standard Edition Java Remote Method Invocation API Guide Release 20-3
Laravel开发-laravel-remote-content-cache 为从远程服务器检索的内容提供Laravel 5缓存框架。
全球领先的半导体及解决方案供应商瑞萨电子于8月6日—8日在深圳举办的2014年工业计算机及嵌入式系统展首日,宣布推出Remote I/O组件的参考设计解决方案。该方案使用了工业以太网通信SoC产品R-IN32M3系列,适用于智能...
Remote Remote Remote Remote Remote
remote-method- guesser ( rmg )是用Java编写的命令行实用程序,可用于识别Java RMI端点上的安全漏洞。 当前,支持以下操作: 列出可用的绑定名称及其对应的接口类名称 列出代码库位置(如果由远程服务器公开...
JDK16-java-remote-method-invocation-api-guide
JDK17-java-remote-method-invocation-api-guide
JDK12-java-remote-method-invocation-api-guide
JDK19-java-remote-method-invocation-api-guide
JDK13-java-remote-method-invocation-api-guide
JDK14-java-remote-method-invocation-api-guide
JDK15-java-remote-method-invocation-api-guide
JDK18-java-remote-method-invocation-api-guide
Remote Method Invocation是一种机制,能够让在某个 Java 虚拟机上的对象调用另一个 Java 虚拟机中的对象上的方法。 编写一个RMI的步骤 定义一个远程接口,此接口需要继承java.rmi.Remote 开发远程接口的实现类 创建...
远程组件 在运行时从URL加载React组件。 目录 渲染道具 React钩 创建一个远程组件 远程组件入门套件 使用Webpack创建一个远程组件 创建React App(CRA) 使用Next.js进行服务器端渲染 getServerSideProps 从Next...