背景:项目要求单接口TPS为1w,为了达到这个目标,需要尽量减少请求的响应时间。
如果处理每次请求,都需要和数据库进行交互,那么必然会很耗时:
耗时主要发生在:
- 与数据库建立连接;
- 数据库查询数据;
- 数据的网络传输。
而实际上,很多数据都是偏向于读,而非写。即,每次都去数据库拉取数据的操作,实际上是不必须的。我们要做的,只是把数据缓存下来,下次用的时候,直接读取缓存就可以了,从而避免了重复与DB交互。
常用的缓存,分为公共缓存和本地缓存:
- 如果
key分布均匀
的话,可以考虑使用公共缓存,常用的是Redis;- 如果
key分布不均匀
的话,则需要考虑使用本地缓存了,当然在设计时,要考虑机器的内存。
扩容、或者增加机器的性能,是提高接口TPS最直接的方式。
比如,原本只有1台机器处理来的请求,现在把机器扩到8台,则理想情况下:
扩容前:TPS = 100
扩容后:TPS = 8 x 100
当然,这是理想情况,实际上请求需要经过ngx进行分流,中间按理是有损耗。
当然,这样非常考验ngx分发的能力,ngx也有可能会成为性能瓶颈!
如果,一个接口对性能的要求特别高,那么对于那些不必实时处理的逻辑,可以事前先处理好,放在缓存中,需要的时候,直接去取就可以了。
- 简化校验:比如参数的校验,只要非空,不校验合法(reason:业务会校验);
- 非必要的操作:不保留;
- 非实时的操作:提前处理。
ArrayList<Integer> l1 = new ArrayList<>();
// 推荐
for (int i = 0; i < l1.size(); i++) {
}
// 不推荐
for (Integer integer : l1) {
}
不建议新建一个集合,然后将合法的元素进行赋值,当然,是否也需要考虑对应的场景呢?
Iterator<Integer> iterator1 = l1.iterator();
while (iterator1.hasNext()) {
Integer next = iterator1.next();
// 移除条件
if (next.equals(1)) {
iterator1.remove();
}
}
java自带的线程池其实已经有对应的方法了
logger.info("启动定时任务,查询正在进行的实验,每10秒执行一次,即配置信息最长十秒后生效!");
Long waitTime = 10 * 1000L;
int availableProcessor = Runtime.getRuntime().availableProcessors();
// 任务执行完成,休眠10s重新执行对应的方法
ScheduledExecutorService service = Executors.newScheduledThreadPool(availableProcessor);
service.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
process();
}
},0,waitTime,TimeUnit.MILLISECONDS);
反面教材:
while (true) {
try {
// 要执行的业务
.....
// 执行完,休眠10s
Thread.sleep(waitTime);
} catch (Exception e) {
logger.error("定时任务失败", e);
}
}
根据性能看,第三种借助反射的效率最高。
/**
* 执行:10000 x 200 耗时:10711
* 根据属性,获取get方法
* @param ob 对象
* @param name 属性名
* @return
* @throws Exception
*/
public static Object getGetMethod(Object ob , String name)throws Exception{
Method[] m = ob.getClass().getMethods();
for(int i = 0;i < m.length;i++){
if(("get"+name).toLowerCase().equals(m[i].getName().toLowerCase())){
return m[i].invoke(ob);
}
}
return null;
}
/**
* 执行 10000 x 200 耗时:5015
* @param ob
* @param name
* @return
*/
public static Object getGetMethod2(Object ob , String name) {
try{
// 获取字段
Field field = ob.getClass().getDeclaredField(name);
PropertyDescriptor pd = new PropertyDescriptor(field.getName(), ob.getClass());
// 获取get方法
Method method = pd.getReadMethod();
return ReflectionUtils.invokeMethod(method, ob);
} catch (Exception e){
System.out.println("error");
}
return null;
}
/**
* 执行 10000 x 200 耗时 38ms
* @param object
* @return
*/
public static Map<String, Object> getFieldNameAndValue(Object object) {
Map<String, Object> map = new HashMap<>();
Class clazz = object.getClass();
ArrayList<Field> fieldList = new ArrayList<>();
while (clazz != null) {
fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
clazz = clazz.getSuperclass();
}
Field[] fields = new Field[fieldList.size()];
fieldList.toArray(fields);
try {
for (Field field : fields) {
field.setAccessible(true);
map.put(field.getName(), field.get(object));
}
} catch (Exception e) {
e.printStackTrace();
}
return map;
}