上篇文章写了简单的生产者消费者实现,这篇生产者不做改变,对消费者如何消费做了升级,不再是上篇单一的实现,而是根据不同的入参执行相应的消费逻辑。
1.先定义一个消息基类,里面参数看你需要,可以为失败重试做准备
@Data
public class BaseAsyncDto {
/**
* 重试次数
*/
private Integer retryTimes=0;
/**
* 下次重试时间
*/
private Date nextRetryTimes;
}
2.定义实现消息入参体(两个类不同的属性做区分)
@Data
public class SmsSend1Dto extends BaseAsyncDto implements Serializable {
private static final long serialVersionUID = -4882464111301842049L;
private Integer id;
/**手机号码*/
private String phoneNo;
}
@Data
public class SmsSend2Dto extends BaseAsyncDto implements Serializable {
private static final long serialVersionUID = 8436613771808998029L;
/**手机号码*/
private String phone;
private String name;
}
3.定义回调结果,里面包含入参消息基类,为后续失败重试做准备
@Data
public class InvokeResult implements Serializable {
private boolean success;
private boolean doRetry;
private BaseAsyncDto dto;
}
4.定义处理类基类和对应上述两个不同消息体不同处理handler
public interface BaseHandler {
/**
* 功能描述: 该handler是否能处理请求
*/
boolean canHandle(Object obj);
/**
* 功能描述: 处理过程
*/
Callable<InvokeResult> doHandle(Object obj);
}
两个实现类
@Component("smsSend1Handler")
public class SmsSend1Handler implements BaseHandler {
private static final Logger logger = LogManager.getLogger(SmsSend1Handler.class);
@Override
public boolean canHandle(Object obj) {
return obj instanceof SmsSend1Dto;
}
@Override
public Callable<InvokeResult> doHandle(Object obj) {
Callable<InvokeResult> res=new Callable<InvokeResult>() {
@Override
public InvokeResult call() throws Exception {
System.out.println("走handler1的逻辑");
SmsSend1Dto record = (SmsSend1Dto) obj;
// 返回值
InvokeResult result = new InvokeResult();
result.setDoRetry(false);
result.setDto(record);
result.setSuccess(true);
try {
//进行处理逻辑
System.out.println("走handler1的逻辑"+ JSONUtil.toJsonStr(record));
}catch (Exception e){
logger.error(e.getMessage());
onFailure(record, e.getMessage());
result.setSuccess(false);
return result;
}
return result;
}
};
return res;
}
/**
* 功能描述: 失败处理
*/
private void onFailure(SmsSend1Dto record, String errorInfo) {
//失败处理
}
}
第二个doHandle里用了lamda表达式,写法更为简单干净,效果一样
@Component("smsSend2Handler")
public class SmsSend2Handler implements BaseHandler {
private static final Logger logger = LogManager.getLogger(SmsSend2Handler.class);
@Override
public boolean canHandle(Object obj) {
return obj instanceof SmsSend2Dto;
}
@Override
public Callable<InvokeResult> doHandle(Object obj) {
return () -> {
SmsSend2Dto record = (SmsSend2Dto) obj;
// 返回值
InvokeResult result = new InvokeResult();
result.setDoRetry(false);
result.setDto(record);
result.setSuccess(true);
try {
//进行处理逻辑
System.out.println("走handler2的逻辑"+ JSONUtil.toJsonStr(record));
}catch (Exception e){
logger.error(e.getMessage());
onFailure(record, e.getMessage());
result.setSuccess(false);
return result;
}
return result;
};
}
/**
* 功能描述: 失败处理
*/
private void onFailure(SmsSend2Dto record, String errorInfo) {
//失败处理
}
}
5.改造我们的ConsumerService
@Component
public class ConsumerService {
private static final Logger logger = LogManager.getLogger(TestService.class);
@Autowired
private List<BaseHandler> handlerList;
private ExecutorService threadpool = new ThreadPoolExecutor(0, 8,
60L, TimeUnit.SECONDS, new LinkedBlockingDeque<>());
/**
* 消费mq
*
*/
@JmsListener(destination = "ActiveMQQueue")
public void mainListen(String message) throws Exception {
System.out.println("主流程消费:"+message);
// 创建任务
List<Callable<InvokeResult>> callableList= createCallable(message);
// 任务执行及异常处理
if (callableList.size()>0){
runAndExceptionDeal(callableList);
}
}
public List<Callable<InvokeResult>> createCallable(String message){
List<Callable<InvokeResult>> callableList = new ArrayList<>();
// 创建任务
Object obj;
try {
//因为@type会报错,所以加一行
ParserConfig.getGlobalInstance().addAccept("com.*.*.dto.");
obj = JSON.parse(message);
// 从数据库读出来的json字符串需要转两次,第一次会转成不带转义符的string,第二次转成的对象
//可忽略,这是把消息存数据库,需要多处理一遍
if(obj instanceof String) {
obj = JSON.parse((String)obj);
}
} catch (Exception e) {
logger.error("JSON解析失败:" + message);
e.printStackTrace();
return callableList;
}
for (BaseHandler handler : handlerList) {
if (handler.canHandle(obj)) {
BaseAsyncDtodto = (BaseAsyncDto)obj;
callableList.add(handler.doHandle(dto));
//记录异步执行记录
}
}
return callableList;
}
public void runAndExceptionDeal(List<Callable<InvokeResult>> callableList){
// 任务执行及异常处理
try {
List<Future<InvokeResult>> futureList = threadpool.invokeAll(callableList);
for (Future<InvokeResult> future : futureList) {
InvokeResult invokeResult = future.get();
if(invokeResult == null) {
logger.error("异步任务执行失败,无法重试");
}
if(!invokeResult.isSuccess()) {
System.out.println("异步任务执行失败,走失败重试");
}
}
} catch (Exception e) {
logger.error("unknown exception has been catch!");
}
}
}
最后生产者生产的时候要把发送对象转成string哦
@RestController
@RequestMapping("/test")
public class ProviderController {
//注入存放消息的队列,用于下列方法一
@Autowired
private Queue queue;
//注入springboot封装的工具类
@Autowired
private JmsMessagingTemplate jmsMessagingTemplate;
@RequestMapping("/send")
public void send(String name) {
SmsSend1Dto dto=new SmsSend1Dto();
dto.setId(1);
dto.setPhoneNo("1768333");
jmsMessagingTemplate.convertAndSend(queue, JSONObject.toJSONString(dto, SerializerFeature.WriteClassName));
}
}
好了,简单的handler实现就完成了,里面还有一些重试啊,记录啊什么的没写进去,不过根据大家的需要自己实现就行了