代码优化~关注核心逻辑,封装常用规则
业务背景
活用函数式编程,可减少很多代码,提高开发效率
函数式编程简单入门,很早写的,比较简单,感兴趣可以看看
函数式编程介绍跳转
具体会以实战中的每个case带着大家去体会这种函数式编程的便捷。
api接口通用逻辑 ~ 供给型函数式接口案例 supplier
`api请求通用操作
- 打印日志
- 请求底层方法
因此可抽象成
package com.dgut.edu.cn.springbootxiaozheng.controller;
import com.dgut.edu.cn.springbootxiaozheng.function.ExSupplier;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
/**
* @description:
* @author: hongbin.zheng
* @create: 2022-09-14 18:05
**/
@Slf4j
public class OperateTemplateUtil {
public static <T> T operate(ExSupplier<T ,Exception> operate, Logger logger,String eventPrefix, String method,long logSlowRespMs, Object... reqParam) throws Exception {
long beginTime = System.currentTimeMillis();
Exception ex = null;
try {
return operate.get();
} catch (Exception e) {
ex = e;
if (e instanceof Exception) {
throw e;
}
throw new RuntimeException("错误信息");
} finally {
// 一个请求记录一条响应日志,查询请求不记录响应内容
// TODO 通用打印日志防范
logReadReq(logger, beginTime, eventPrefix, method, ex,
logSlowRespMs, reqParam);
}
}
/**
* 记录OSP读请求的日志
* @param logger VenusLogger实例
* @param beginTime 请求开始处理时间(System.currentTimeMillis())
* @param eventPrefix 事件名的业务前缀(可能是业务模块相关的前缀,比如寻仓管理:“locate.admin.”)
* @param method osp方法名,将添加到eventName和message,方便按方法统计和检索
* @param ex 请求处理过程中抛出的异常,可能为null
* @param logSlowRespMs 慢响应阈值,单位毫秒,通常是配置值,比如500ms
* @param reqParam 请求的入参
*/
public static void logReadReq(Logger logger, long beginTime, String eventPrefix, String method, Exception ex,
long logSlowRespMs, Object... reqParam) {
long cost = System.currentTimeMillis() - beginTime;
if (ex != null) {
// 出现非osp异常,打印ERROR日志
logger.error("********");
return;
}
if (logger.isDebugEnabled()) {
// 读请求,出入参情况打印到debug日志
logger.info("********");
return;
}
}
}
应用层
private String EVENT_NAME_PREFIX = "CallOtherInterfaceServiceImpl";
protected <T> T operate(ExSupplier<T, Exception> operate, String method, Object... req) throws Exception {
return OperateTemplateUtil.operate(operate, getLogger(), EVENT_NAME_PREFIX, method,
6000L, req);
}
private Logger getLogger() {
return LoggerFactory.getLogger("call");
}
public String getAppealCount(String req) throws Exception {
return operate(() -> call(req), "queryAppealCount");
}
接口重试代码 ~ 供给型函数式接口案例 supplier
说明:在请求外部接口的时候,常常会加上重试的逻辑。如果现在对接N个接口,就要写N份重试的代码逻辑,那能否只关注业务逻辑,不关心重试的逻辑呢
正常重试的代码
public String callWithRetry(String userName, Integer retryTime) {
String eventName = "CallOtherInterfaceServiceImpl_call";
String result = "";
do {
try {
// TODO 调用外部接口
result = "调用外部接口, 获取用户信息成功,当前用户信息:aa";
// 如果调用成功,没抛出异常,则结束
break;
} catch (Exception e) {
// 如果重试次数大于0,则要重试
if (retryTime > 0) {
log.info(eventName, userName, "重试中......");
retryTime--;
} else {
log.info(eventName, userName, "重试后依旧失败,抛出异常......");
throw e;
}
}
} while (retryTime > 0);
System.out.println("============");
System.out.println(result);
System.out.println("============");
return result;
}
controller代码
@RequestMapping("/callWithRetry")
@ResponseBody
public String callWithRetry() {
String username = "xiaozheng";
return callOtherInterfaceService.callWithRetry(username, 3);
}
问题:每个接口的实现类都需要完成自己的重试逻辑,写起来就会很冗余 \color{red}问题:每个接口的实现类都需要完成自己的重试逻辑,写起来就会很冗余 问题:每个接口的实现类都需要完成自己的重试逻辑,写起来就会很冗余
代码优化,把每个接口的调用看成是一个提供型函数,这样就能统一
函数式方法定义
package com.dgut.edu.cn.springbootxiaozheng.function;
/**
* hongbin.zheng
* @param <T>
* @param <E>
*/
@FunctionalInterface
public interface ExSupplier<T, E extends Throwable> {
/**
*
* @return
* @throws E
*/
T get() throws E;
}
重试工具类
package com.dgut.edu.cn.springbootxiaozheng.utils;
import com.dgut.edu.cn.springbootxiaozheng.function.ExSupplier;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import java.util.Map;
import java.util.Objects;
/**
* @description:
* @author: hongbin.zheng
* @create: 2022-09-14 14:23
**/
@Slf4j
public class RetryUtils {
private static final int defaultRetryTimes = 2;
public static <T, R, E extends Throwable> T doWithRetry(ExSupplier<T, E> supplier, String eventName, R logObj)
throws E {
return doWithRetry(supplier, defaultRetryTimes, eventName, logObj);
}
public static <T, R, E extends Throwable> T doWithRetry(ExSupplier<T, E> supplier, int times, String eventName,
R logObj) throws E {
try {
return supplier.get();
} catch (Throwable e) {
if (times > 0) {
log.warn(eventName, getInfoMap(logObj, times, eventName, e), "正在重试中");
Thread.yield(); // 让当前线程的优先级降低,减少可能存在的细微竞争
return doWithRetry(supplier, --times, eventName, logObj);
} else {
log.error(eventName, getInfoMap(logObj, times, eventName, e),"重试失败");
throw e;
}
}
}
private static Map<String, Object> getInfoMap(Object logObj, int times, String eventName, Throwable e) {
Map<String, Object> infoMap = Maps.newHashMap();
infoMap.put("logObj", logObj);
infoMap.put("retryTimes", times);
infoMap.put("eventName", eventName);
infoMap.put("msg", e.getMessage());
return infoMap;
}
}
工具类使用
public String call(String userName) {
String eventName = "CallOtherInterfaceServiceImpl_call";
String s = RetryUtils.doWithRetry(() -> "获取用户信息成功,当前用户信息:aa", eventName, userName);
System.out.println("============");
System.out.println(s);
System.out.println("============");
return s;
}
controller 测试
@RequestMapping("/call")
@ResponseBody
public String call() {
String username = "xiaozheng";
return callOtherInterfaceService.call(username);
}
这样就完成了
批量插入 兜底 单个插入 ~ consumer 消费型函数式接口 案例
消费型函数式接口定义
package com.dgut.edu.cn.springbootxiaozheng.function;
import java.util.Objects;
/**
* @see java.util.function.Consumer
*/
@FunctionalInterface
public interface ExConsumer<T> {
void accept(T t) throws Exception;
default ExConsumer<T> andThen(ExConsumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> {
accept(t);
after.accept(t);
};
}
}
工具类
/**
* 批量处理降级为单条处理
* @param param
* @param eventName
* @param batchProcess
* @param singleProcess
* @param <T>
* @return 处理成功数据
*/
public static <T> List<T> demoteProcessTry(List<T> param,String eventName, ExConsumer<List<T>> batchProcess, ExConsumer<T> singleProcess){
//处理成功的对象
List<T> successEntityList ;
try {
batchProcess.accept(param);
successEntityList = param;
}catch (Exception e){
log.error(eventName,param,"批量处理失败,改为单条处理");
successEntityList= Lists.newArrayListWithCapacity(param.size());
for (T t : param) {
try {
singleProcess.accept(t);
successEntityList.add(t);
}catch (Exception ex){
log.error(eventName,param,"单条处理失败");
}
}
}
return successEntityList;
}
mapper类
package com.dgut.edu.cn.springbootxiaozheng.mapper;
import com.dgut.edu.cn.springbootxiaozheng.domain.UserVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @description:
* @author: hongbin.zheng
* @create: 2022-09-14 17:34
**/
@Mapper
public interface UserVoMapper {
int insert(UserVo userVo);
int batchInsert(@Param("list") List<UserVo> userVoList);
}
测试类
@Autowired
UserVoMapper userVoMapper;
@RequestMapping("insert")
@ResponseBody
public String insert() {
List<UserVo> userVoList = UserVo.getUserVoList();
String eventName = "insert";
RetryUtils.demoteProcessTry(userVoList, eventName, list -> userVoMapper.batchInsert(list),
vo -> userVoMapper.insert(vo));
return "success";
}
总结
知道的越多,不知道的越多,希望对你有帮助! \color{red}知道的越多,不知道的越多,希望对你有帮助! 知道的越多,不知道的越多,希望对你有帮助!