最代码官方的gravatar头像
最代码官方 2014-05-25 23:27:57

java中使用FIFO队列:java.util.Queue实现多台服务器发邮件的代码

最近由于注册用户的增多,qq企业邮箱发送邮件会被封禁账号导致注册后面的用户收不到周总结,所以紧急开发了一套多账号,多服务器发送邮件的程序。

大概的设计思路如下:

java中使用FIFO队列:java.util.Queue实现多台服务器发邮件的代码

1.服务器可以无限扩展,但由于qq企业邮箱是限定域名,所以要想多服务器还得有多域名,多账号也不行。

2.最核心的代码是FIFO分配指定长度的邮件列表这一步,通过java线程同步Queue实现。

3.发送失败的要加到重试队列继续发送。

4.经测试每分钟发一封邮件,qq企业邮箱最大可以发送7000封邮件,这样最代码2个账号service@javaniu.com和service@zuidaima.com就可以发送到15000个邮件,该方案绝对是性价比超高的,目前市面上每封邮件1分钱,那15000也得150元人民币,如果你每周呢?

5.可以指定某个用户id来只发送某个范围的邮件列表

6.可以指定clear参数来清空所有邮件列表,不过该参数慎用,避免多发送邮件。

下面分享下核心代码,有思路和代码做参考,我想大家肯定可以自己搭建起来了:

分配邮件端核心java代码:

private final static Map<Long, Queue<User>> map = new HashMap<Long, Queue<User>>();

// 申请发邮件的邮箱地址,每次根据mailTaskCount配置来取个数
	public @ResponseBody
	JSONObject apply_mails(
			@PathVariable("id") Long id,
			@RequestParam(value = "start_date", required = false) String startDate,
			@RequestParam(value = "end_date", required = false) String endDate,
			@RequestParam(value = "clear", required = false) String clear,
			@RequestParam(value = "user_id", required = false) Long user_id)
			throws IOException, TemplateException {
		JSONObject json = new JSONObject();
		String error = "";
		if (StringUtils.isBlank(startDate) || StringUtils.isBlank(endDate)) {
			error = "参数非法,startDate和endDate";
			json.put("error", error);
			return json;
		}
		final Task task = taskService.findOneById(id);
		if (task.getType() != ModuleConstants.TASK_TYPE_WEEK_SUMMARY) {
			error = "任务" + id + "不是周总结";
			json.put("error", error);
			return json;
		}
		Queue<User> allUsers = null;
		List<User> users = new ArrayList<User>();
		synchronized (map) {//注意线程安全
			if (StringUtils.isNotBlank(clear)) {// clear
				map.put(id, null);
				error = "任务清空成功";
				json.put("error", error);
				return json;
			}
			allUsers = map.get(id);
			if (allUsers == null) {
				allUsers = new LinkedList<User>();
				if (user_id == null) {
					user_id = 0l;
				}
				Page<User> page = userService
						.findAllReceiveMailUsersGreaterThanId(user_id, 1,
								Integer.MAX_VALUE);
				allUsers.addAll(page.getContent());
				map.put(id, allUsers);
			}
			int size = allUsers.size();
			if (size == 0) {
				error = "任务" + id + "执行完毕!";
				json.put("error", error);
				return json;
			}

			int i = 0;
			while (i < mailTaskCount) {
				User _user = allUsers.poll();
				if (_user == null) {
					break;
				}
				users.add(_user);
				i++;
			}
		}

		File templateFile = new File(deployPath + "/apply_week_summary_" + id
				+ ".html");
		String templdate = "";
		if (templateFile.exists()) {
			templdate = FileUtils.readFileToString(templateFile, "utf-8");
		} else {
			List<Long> weekSummaryShareIds = findIds(startDate, endDate);
			ModelMap model = new ModelMap();
			Collection<Project> weekSummaryShares = projectService
					.findAllByIds(weekSummaryShareIds);
			Configuration configuration = freemarkerConfig.getConfiguration();
			configuration.setDirectoryForTemplateLoading(new File(deployPath
					+ "/WEB-INF/template/"));
			Template template = configuration
					.getTemplate("apply_week_summary.htm");
			Page<Rank> weekRankUsers = rankService.findAllByType(
					ModuleConstants.RANK_TYPE_USER_CONTRIBUTE_WEEK,
					ParamConstants.DEFAULT_PAGE,
					ParamConstants.DEFAULT_TOP_LESS_COUNT,
					ModuleConstants.RANKSORT);
			model.put("weekRankUsers", weekRankUsers);

			Page<Rank> monthRankUsers = rankService.findAllByType(
					ModuleConstants.RANK_TYPE_USER_CONTRIBUTE_MONTH,
					ParamConstants.DEFAULT_PAGE,
					ParamConstants.DEFAULT_TOP_LESS_COUNT,
					ModuleConstants.RANKSORT);
			model.put("monthRankUsers", monthRankUsers);

			model.put("task", task);
			model.put("weekSummaryShares", weekSummaryShares);

			Sort sort = new Sort(Direction.DESC, "secondSort");
			Page<Project> downloadProjects = projectService.findAllByType(
					ModuleConstants.PROJECT_TYPE_SHARE_CODE, 1,
					ParamConstants.DEFAULT_TOP_COUNT, sort);
			model.put("downloadProjects", downloadProjects);

			sort = new Sort(Direction.DESC, "thirdSort");
			Page<Project> commentProjects = projectService.findAllByType(
					ModuleConstants.PROJECT_TYPE_SHARE_CODE, 1,
					ParamConstants.DEFAULT_TOP_COUNT, sort);
			model.put("commentProjects", commentProjects);

			StringWriter stringWriter = new StringWriter();
			stringWriter.flush();
			stringWriter.close();
			template.process(model, stringWriter);
			FileUtils.writeStringToFile(templateFile, stringWriter.toString(),
					"utf-8");
			templdate = stringWriter.toString();
		}

		json.put("template", templdate);
		JSONArray usersJson = new JSONArray();
		for (User user : users) {
			JSONObject userJson = new JSONObject();
			userJson.put("id", user.getId());
			userJson.put("uuid", user.getUuid());
			String code = Security.encryptUnsubscribeWeeklyEmail(user.getId()
					+ "_" + new Date().getTime());
			userJson.put("unsubscribe_weekly_email_code", code);
			userJson.put("email", user.getEmail());
			usersJson.add(userJson);
		}
		json.put("users", usersJson);
		json.put("error", error);
		json.put("title", task.getTitle());
		return json;
	}

发送邮件端核心java代码:

public static void main(String[] args) {
		if (args.length < 3) {
			System.out
					.println("需要3个参数:\n第一个是task id\n第二个是开始时间,如:2014-05-05\n第三个是结束时间,如:2014-05-09\n第四个参数user_id\n第五个参数是clear,如true");
			return;
		}

		String task_id = args[0];
		String start_date = args[1];
		String end_date = args[2];
		String user_id = "0";
		if (args.length >= 4) {
			user_id = args[3];
		}
		String clear = "";
		if (args.length >= 5) {
			clear = args[4];
		}
		String url = String.format(APPLY_EMAIL_URL, task_id, start_date,
				end_date, user_id, clear);
		while (true) {
			String response = request(url);
			JSONObject json = JSONObject.fromObject(response);
			String error = json.getString("error");
			if (StringUtils.isNotBlank(error)) {
				System.out.println("发送邮件结束:error:" + error);
				break;
			}

			JSONArray usersJson = json.getJSONArray("users");
			if (usersJson.size() == 0) {
				System.out.println("发送邮件结束");
				break;
			}
			String template = json.getString("template");
			String title = json.getString("title");
			int i = 1;
			try {
				List<JSONObject> failed1 = new ArrayList<JSONObject>();
				for (Object _userJson : usersJson) {
					send(_userJson, title, template, i, failed1,
							usersJson.size());
					i++;
				}
				if (failed1.size() == 0) {
					System.out.println("发送邮件结束一批");
					continue;
				}

				System.out.println("第一次发送失败的邮箱列表" + failed1);
				Thread.sleep(1 * 60 * 60 * 1000);
				List<JSONObject> failed2 = new ArrayList<JSONObject>();
				i = 1;
				for (Object userJson : failed1) {
					send(userJson, title, template, i, failed2, failed1.size());
					i++;
				}
				if (failed2.size() == 0) {
					System.out.println("failed1发送邮件结束一批");
					continue;
				}

				System.out.println("第二次发送失败的邮箱列表" + failed2);
				Thread.sleep(1 * 60 * 60 * 1000);
				List<JSONObject> failed3 = new ArrayList<JSONObject>();
				i = 1;
				for (Object userJson : failed2) {
					send(userJson, title, template, i, failed3, failed2.size());
					i++;
				}

				if (failed3.size() == 0) {
					System.out.println("failed2发送邮件结束一批");
					continue;
				}

				System.out.println("第三次发送失败的邮箱列表" + failed3);
				Thread.sleep(1 * 60 * 60 * 1000);
				List<JSONObject> failed4 = new ArrayList<JSONObject>();
				i = 1;
				for (Object userJson : failed3) {
					send(userJson, title, template, i, failed4, failed3.size());
					i++;
				}
				System.out.println("最终发送失败的邮箱列表:" + failed4);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

线上正式环境运行截图:

java中使用FIFO队列:java.util.Queue实现多台服务器发邮件的代码


打赏

最代码最近下载分享源代码列表最近下载
最代码最近浏览分享源代码列表最近浏览
1358849392  LV21 2023年6月9日
浪里格朗  LV4 2023年1月31日
xuexizhuanyong23  LV16 2022年7月3日
1143011510  LV15 2021年7月14日
nini444 2021年5月25日
暂无贡献等级
李涛11111  LV8 2021年4月27日
喵小兵  LV10 2021年3月13日
dongzhan  LV12 2020年12月22日
gwck2020  LV1 2020年12月10日
Jason137  LV8 2020年12月6日
顶部 客服 微信二维码 底部
>扫描二维码关注最代码为好友扫描二维码关注最代码为好友