JAVA实现多线程处理批量发送短信、APP推送

本文介绍如何使用Java进行多线程处理,实现高效地批量发送短信和APP推送任务,涉及到并发控制与数据库操作。
摘要由CSDN通过智能技术生成
/**
	 * 推送消息 APP、短信
	 * @param message
	 * @throws Exception
	 */
	public void sendMsg(Message message) throws Exception{
		try {
			logger.info("send message start...");
			long startTime = System.currentTimeMillis();
			BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(20000);
			ThreadPoolExecutor executors = new ThreadPoolExecutor(5, 6, 60000, TimeUnit.SECONDS, queue);
			
			//要推送的用户总数
			int count = filterPhonesCount(message);
			logger.info("message all count=>{}",count);
			//初始每个线程处理的用户数量
			final int eveLength = 2000;
			//计算处理所有用户需要的线程数量
			int eveBlocks = count / eveLength + (count % eveLength != 0 ? 1 : 0);
			logger.info("need thread's count=>{}",eveBlocks);
			//线程计数器
			CountDownLatch doneSignal = new CountDownLatch(eveBlocks);
			
			//开启线程处理
			int doneCount = 0;
			for (int page = 0; page < eveBlocks; page++) { /* blocks太大可以再细分重新调度 */
				MessageSendThread ms = new MessageSendThread(messageDao,message,page + 1,eveLength,doneSignal);
				executors.execute(ms);
				//logger.info("start thread =>{}",page+1);
				doneCount++;
			}
			doneSignal.await();//等待所有计数器线程执行完
			long endTime = System.currentTimeMillis();
			logger.info("send message all thread ends!time(s)=>{}",(startTime-endTime)/1000);
			logger.info("all thread count=>{}",doneCount);
		} catch (Exception e) {
			logger.error("send message error=>{}",e);
		}
	}

  

package com.bankhui.center.business.service.message;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.impl.cookie.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.bankhui.center.business.dao.message.MessageDao;
import com.bankhui.center.business.entity.message.Message;
import com.bankhui.center.common.utils.DateUtil;
import com.bankhui.center.common.utils.SmsUtils;
import com.bankhui.center.jpush.JPushClient;
import com.bankhui.center.jpush.JPushScheduleClient;

/**
 * 系统消息推送线程(处理 block数据块)
 */
public class MessageSendThread implements Runnable{
	
	private final Logger logger = LoggerFactory.getLogger(MessageSendThread.class);

	private Integer currentIndex;//当前索引
	private Integer rows;//处理数据条数
	private CountDownLatch doneSignal;//处理线程条数
	private Message message;//消息实体
	private MessageDao messageDao;//DAO

	public MessageSendThread(MessageDao messageDao,Message message,Integer currentIndex,Integer rows, CountDownLatch doneSignal) {
		this.message = message;
		this.messageDao = messageDao;
		this.currentIndex = currentIndex;
		this.rows = rows;
		this.doneSignal = doneSignal;
	}
	
	
	@Override
	public void run() {
		try {
			/**
			 * ---------1.查询当前的block范围内的发送的手机号=>筛选目标客户群手机号---------
			 */
			Map<String,Object> smsDataMap = filterPhones(message,currentIndex,rows);
			if(MapUtils.isEmpty(smsDataMap)|| null == smsDataMap.get("jgAlias") 
					||StringUtils.isBlank(smsDataMap.get("jgAlias").toString())){
				logger.debug("push param is null,caurse by target customers is nothing");
				throw new RuntimeException();
			}
			logger.info("type of target customers=>{}", message.getReceiverGroupType());
			logger.info(" result of filter target customers=>{}", smsDataMap);
			 
			/**
			 *  ---------2.批量发送消息---------
			 *  TODO://((-?)\d{1,11}\,?){1,n}  n个线程分批发送
			 */
			if("0".equals(message.getType())){//短信发送
				sendBatch(smsDataMap.get("phone").toString(),message);
			}
			if("1".equals(message.getType())){//APP推送
				if("0".equals(message.getMethod())){//实时发送
					sendNormal(smsDataMap);
				}
				if("1".equals(message.getMethod())){//定时发送
					sendDelay(smsDataMap);
				}
			 }
		} catch (Exception e) {
			logger.error("send message thread exception=>{}{}{}{}",message,currentIndex,rows,e);
		}finally{
			doneSignal.countDown();//工人完成工作,计数器减一
		}
	}
	
	/**
	 * APP实时推送
	 * @param smsDataMap
	 */
	private void sendNormal(Map<String,Object> smsDataMap) {
		 //0为全部发送
		if("0".equals(message.getReceiverGroupType())){
			JPushClient.appSendAll(message.getTitle(), message.getContent(), message.getId().toString(), StringUtils.isBlank(message.getLink())?"0":"1", message.getLink());
		}else{
			String[] jgAlias = smsDataMap.get("jgAlias").toString().split(",");
			for(String jgAlia:jgAlias){
				JPushClient.appSend(message.getTitle(), message.getContent(), jgAlia, message.getId().toString(), StringUtils.isBlank(message.getLink())?"0":"1", message.getLink());
			}
		}
	}

    /**
     * APP定时推送
     * @param smsDataMap
     */
	private void sendDelay(Map<String,Object> smsDataMap) {
		 //0为全部发送
		if("0".equals(message.getReceiverGroupType())){
			JPushScheduleClient.createSingleSchedule(
					DateUtil.formatDateToStr("yyyy-MM-dd HH:mm:ss", message.getExpectTime()), 
					message.getTitle(), 
					message.getContent(),  
					message.getId().toString(), 
					StringUtils.isBlank(message.getLink())?"0":"1", 
					message.getLink());
		}else{
			String[] jgAlias = smsDataMap.get("jgAlias").toString().split(",");
			JPushScheduleClient.createSingleSchedule(
					Arrays.asList(jgAlias),
					DateUtil.formatDateToStr("yyyy-MM-dd HH:mm:ss", message.getExpectTime()), 
					message.getTitle(), 
					message.getContent(),  
					message.getId().toString(), 
					StringUtils.isBlank(message.getLink())?"0":"1", 
					message.getLink());
		}
	}


	
	
	/**
	 * 批量发送消息
	 * @param smsDataList
	 * @param message
	 */
	private void sendBatch(String smsDataListStr,Message message){
		try {
			//批量发送方法使用异步发送
			if(!message.getContent().contains("退订回T")){
				message.setContent(message.getContent()+"退订回T");
			}
			SmsUtils.batchExecuteTask(smsDataListStr, message.getContent());
			//短信测试方法
			//SmsUtils.batchExecuteTask(smsDataListStr, message.getContent(),true);
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("批量发送消息异常=>{}{}",smsDataListStr,e);
		}
	}
	

}
 1 /**
 2      * 批量发送消息
 3      * @param smsDataList
 4      * @param message
 5      */
 6     private void sendBatch(String smsDataListStr,Message message){
 7         try {
 8             //批量发送方法使用异步发送
 9             if(!message.getContent().contains("退订回T")){
10                 message.setContent(message.getContent()+"退订回T");
11             }
12             SmsUtils.batchExecuteTask(smsDataListStr, message.getContent());
13             //短信测试方法
14             //SmsUtils.batchExecuteTask(smsDataListStr, message.getContent(),true);
15         } catch (Exception e) {
16             e.printStackTrace();
17             logger.error("批量发送消息异常=>{}{}",smsDataListStr,e);
18         }
19     }
 1 public static String sendSmsCL(String mobile, String content,String urlStr,String un, String pw, String rd) {
 2            // 创建StringBuffer对象用来操作字符串
 3             StringBuffer sb = new StringBuffer(urlStr+"?");
 4             // 用户账号
 5             sb.append("un="+un);
 6 
 7             //用户密码
 8             sb.append("&pw="+pw);
 9 
10             // 是否需要状态报告,0表示不需要,1表示需要
11             sb.append("&rd="+rd);
12 
13             // 向StringBuffer追加手机号码
14             sb.append("&phone="+mobile);
15 
16             // 返回发送结果
17             String inputline;
18             BufferedReader in = null;
19             InputStreamReader isr = null;
20             try {
21                 // 向StringBuffer追加消息内容转URL标准码
22                 sb.append("&msg="+URLEncoder.encode(content,"UTF8"));
23                 // 创建url对象
24                 URL url = new URL(sb.toString());
25 
26                 // 打开url连接
27                 HttpURLConnection connection = (HttpURLConnection) url.openConnection();
28 
29                 // 设置url请求方式 ‘get’ 或者 ‘post’
30                 connection.setRequestMethod("POST");
31                 isr = new InputStreamReader(url.openStream());
32                 // 发送
33                 in = new BufferedReader(isr);
34                 inputline = in.readLine();
35                 if(inputline.contains(",0")){
36                     logger.info("手机号:【{}】发送短信成功", mobile);
37                 }else{
38                     logger.info("手机号:【{}】发送短信失败,errorMsg is:{}", mobile,inputline);
39                 }
40                 // 输出结果
41                 return inputline;
42             } catch (Exception e) {
43                 logger.error("发送短信请求异常:{}", e.getMessage());
44                 return e.getMessage();
45             } finally{
46                 if(null != isr){
47                     try {
48                         isr.close();
49                     } catch (IOException e) {
50                         logger.error("关闭流异常:{}", e.getMessage());
51                     }
52                 }
53                 if(null != in){
54                     try {
55                         in.close();
56                     } catch (IOException e) {
57                         logger.error("关闭流异常:{}", e.getMessage());
58                     }
59                 }
60             }
61 
62     }
package com.bankhui.center.jpush;

import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The entrance of JPush API library.
 *
 */
public class JPushClient extends BaseClient {
    private static Logger logger = LoggerFactory.getLogger(JPushClient.class);
    //在极光注册上传应用的 appKey 和 masterSecret
    private static final String appKeyStr ="******************";必填,
    
    private static final String masterSecretStr = "******************";//必填,每个应用都对应一个masterSecret
    
    private static JPushClient jpush = null;

    /*
     * 保存离线的时长。秒为单位。最多支持10天(864000秒)。
     * 0 表示该消息不保存离线。即:用户在线马上发出,当前不在线用户将不会收到此消息。
     * 此参数不设置则表示默认,默认为保存1天的离线消息(86400秒
     */
    private static long timeToLive =  
  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值