代码下载地址:http://www.zuidaima.com/share/1838230785625088.htm
原文:java中使用FIFO队列:java.util.Queue实现多台服务器发邮件的代码
最近由于zuidaima.com注册用户的增多,qq企业邮箱发送邮件会被封禁账号导致注册后面的用户收不到周总结,所以紧急开发了一套多账号,多服务器发送邮件的程序。
大概的设计思路如下:
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代码:
package com.zuidaima.mail; 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代码:
package com.zuidaima.mail; 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中,我们可以使用`java.util.Queue`接口及其实现类,如`LinkedList`或`ArrayDeque`来创建队列。队列在排队叫号系统中起到存储等待服务的客户和决定服务顺序的作用。 在模拟排队叫号系统时,我们通常会创建两...
在Java中,队列可以使用`java.util.Queue`接口及其实现类如`LinkedList`来创建。 2. **Java 面向对象编程**:此项目中,队列可能被实现为一个类,包含队列的基本操作,如添加元素(enqueue)、移除元素(dequeue)...
在Java中,`java.util`包提供了多种队列实现,包括: 1. **LinkedList**:`java.util.LinkedList` 是一个双向链表,同时实现了`Deque`接口,可以作为队列使用。插入和删除操作的时间复杂度为O(1),但在随机访问元素...
在Java编程语言中,`Queue`接口是集合框架的一部分,它代表了先进先出(FIFO)的数据结构,也就是我们通常所说的队列。队列是一种非常基础且实用的数据结构,广泛应用于多线程同步、任务调度、缓存管理等多个场景。...
在Java中实现FIFO队列,可以使用内置的`java.util.Queue`接口,如`LinkedList`或`ArrayDeque`类。`LinkedList`提供了`addLast()`方法添加元素到队尾,`removeFirst()`方法移除并返回队头的元素。`ArrayDeque`作为更...
1. 集合框架:Java.util包是Java集合框架的基础,包括List、Set、Queue和Map等接口,以及ArrayList、LinkedList、HashSet、HashMap等实现类。这些集合类为存储和操作对象提供了灵活的方式。例如,ArrayList实现了...
在Java中,我们可以使用`java.util.Queue`接口及其实现类,如`LinkedList`或`ArrayDeque`来创建队列。 接下来,我们需要创建两个线程类:一个是`CustomerThread`,代表等待叫号的客户,另一个是`ServiceThread`,...
本教程将深入探讨如何在Java中使用队列,特别是通过LinkedList实现。 ### 1. Java 队列接口 Java提供了多种队列实现,但它们都基于两个主要的接口:`Queue` 和 `Deque`。`Queue` 是基本的队列接口,而 `Deque`...
2. **实现Queue接口的类**:Java提供了多种实现Queue接口的类,如`ArrayDeque`(数组双端队列)、`LinkedList`(链表实现的队列)、`PriorityQueue`(优先级队列)等。每个实现类都有其特定的特性和用途,例如`...
在Java中,队列是一种线性数据结构,它遵循“先进先出”(FIFO)的原则,即最先添加到队列中的元素会最先被移除。队列在多线程、任务调度、数据缓冲等多个场景中发挥着重要作用。 在Eclipse这个流行的Java集成开发...
本文将详细介绍如何在Java中自定义一个队列类,并实现队列的基本操作。 #### 队列的基本操作 队列通常支持以下几种基本操作: 1. **enq(x)**:向队列插入一个值为`x`的元素; 2. **deq()**:从队列删除一个元素;...
在Java中,队列可以借助接口`java.util.Queue`和实现类如`LinkedList`来创建和操作。 队列的主要特点包括: 1. **先进先出**:元素的添加和移除都是按照严格的顺序进行,新添加的元素位于队尾,而最早加入的元素...
- **多线程并发**:Java的线程池(如`ThreadPoolExecutor`)使用队列来管理待执行的任务。 7. **优先级队列** 对于需要根据优先级处理元素的情况,可以使用`PriorityQueue`。它是一个基于最小堆的数据结构,支持...
总的来说,这个练习通过`Queue.java`和`Node.java`文件,旨在帮助学习者理解如何在Java中实现数据结构——队列,以及如何利用链表作为底层存储结构。这样的练习有助于提高对数据结构的理解,为编写更复杂、高效的...
类集框架是Java 2的一大亮点,它标准化了处理对象集合的方式,解决了早期Java中如Dictionary、Vector、Stack和Properties等类各自为政的问题。Collection接口作为框架的基础,定义了通用的集合操作,如添加元素、...
在Java中,`java.util.Queue`接口提供了队列操作的基本API,如`offer()`(添加元素)、`poll()`(移除并返回头元素)、`peek()`(查看但不移除头元素)等。 Java还提供了多种实现队列的类,如`LinkedList`、`...
在Java中,可以使用`LinkedHashMap`实现LRU,因为它的插入和删除操作具有O(1)的时间复杂度,并能按照访问顺序自动排序。 现在让我们看看Java中如何实现这些算法: 对于FIFO,可以创建一个`LinkedList`存储缓存中的...
- **数组(Array)**:Java中的数组是基本的数据结构,可以直接声明和使用。 - **链表(Linked List)**:`java.util.LinkedList`提供了链表的实现。 - **栈(Stack)**:`java.util.Stack`是一个继承自`Vector`的类,实现...
Java中可以使用LinkedList、ArrayList或PriorityQueue来实现队列,其中LinkedList和ArrayList适用于普通队列,而PriorityQueue则用于优先级队列。以下是一个使用LinkedList实现的队列示例: ```java import java....
在Java编程中,队列是一种线性数据结构,它遵循先进先出(FIFO)的原则。...通过阅读《Java队列》这篇博文,读者可以学习到如何在Java中正确地使用和实现队列,以及如何利用队列解决实际编程问题。