在测试过程中发现了james一个不那么友好的地方,略微做一些修改
在测试james的过程中,我尝试向general-subscribe@james.apache.org(james的一个邮件列表)发送邮件,结果邮件始终无法送达,debug之后发现是apache服务器拒绝了命令
拒绝的是 helo命令和echo 命令会传输一个 hostname,默认情况取得是主机名称,因此给拒绝了。
修改 mailetcontainer.xml中的RemoteDelivery,添加一个属性 heloName,最终结果如下:
1 2 3 4 5 6 7 8 9 10 11 12
| <processor state="relay" enableJmx="true"> <mailet match="All" class="RemoteDelivery"> <outgoingQueue>outgoing</outgoingQueue> <delayTime>5000, 100000, 500000</delayTime> <maxRetries>3</maxRetries> <maxDnsProblemRetries>0</maxDnsProblemRetries> <deliveryThreads>10</deliveryThreads> <sendpartial>true</sendpartial> <bounceProcessor>bounces</bounceProcessor> <heloName>smtp.domain.com</heloName> </mailet> </processor>
|
本以为这样就可以了,继续测试发现出现了另一个错误
Client host rejected: cannot find your reverse hostname
查了一下该问题无解,因为要解决该问题,需要给出口ip做反向域名解析,绑定到heloName上,然而这是一项收费服务,而且还很贵。
邮件发不出去没关系,但是消息提醒需要有啊,在测试过程中,我确实收到过一次错误邮件,但是邮件内容只有以下信息有用
Error message:
Too many retries failure. Bouncing after 3 retries.
意思是重试了三次依然失败,但是没有错误原因,这就很不方便。因此我准备改造一下
投递出错的处理在DeliveryRunnable的第153行 handleTemporaryFailure
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @VisibleForTesting void attemptDelivery(Mail mail) throws MailQueue.MailQueueException { ExecutionResult executionResult = mailDelivrer.deliver(mail); switch (executionResult.getExecutionState()) { case SUCCESS: outgoingMailsMetric.increment(); configuration.getOnSuccess() .ifPresent(Throwing.consumer(onSuccess -> mailetContext.sendMail(mail, onSuccess))); break; case TEMPORARY_FAILURE: handleTemporaryFailure(mail, executionResult); break; case PERMANENT_FAILURE: handlePermanentFailure(mail, executionResult); break; } }
|
里面负责判断重试次数,如果达标,则交给Bouncer处理,Bouncer会修改邮件状态,然后转给别的处理器处理。默认配置下最终转给了DSNBounce
研究了一下代码,在Bouncer的第62行,就已经把真正有用的错误堆栈信息给处理掉了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public void bounce(Mail mail, Exception ex) { if (!mail.hasSender()) { LOGGER.debug("Null Sender: no bounce will be generated for {}", mail.getName()); } else { if (configuration.getBounceProcessor() != null) { computeErrorCode(ex).ifPresent(mail::setAttribute); mail.setAttribute(new Attribute(DELIVERY_ERROR, AttributeValue.of(getErrorMsg(ex)))); try { mailetContext.sendMail(mail, configuration.getBounceProcessor()); } catch (MessagingException e) { LOGGER.warn("Exception re-inserting failed mail: ", e); } } else { bounceWithMailetContext(mail, ex); } } }
|
那就只有改这个类,但是这个类是直接在RemoteDelivery里写死的new方法创建,没办法动态注入
1 2 3 4 5 6 7 8 9 10 11 12 13
| public void init() throws MessagingException { configuration = new RemoteDeliveryConfiguration(getMailetConfig(), domainList); queue = queueFactory.createQueue(configuration.getOutGoingQueueName()); deliveryRunnable = new DeliveryRunnable(queue, configuration, dnsServer, metricFactory, getMailetContext(), new Bouncer(configuration, getMailetContext())); if (startThreads == ThreadState.START_THREADS) { deliveryRunnable.start(); } }
|
james大概是不希望这里能够自由扩展,但是没关系,反正是有源码的,改一下重新打包就是,修改成如下代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| private void handleTemporaryFailure(Mail mail, ExecutionResult executionResult) throws MailQueue.MailQueueException { if (!mail.getState().equals(Mail.ERROR)) { mail.setState(Mail.ERROR); DeliveryRetriesHelper.initRetries(mail); mail.setLastUpdated(dateSupplier.get()); } mail.setAttribute(new Attribute(IS_DELIVERY_PERMANENT_ERROR, AttributeValue.of(false))); int retries = DeliveryRetriesHelper.retrieveRetries(mail);
if (retries < configuration.getMaxRetries()) { reAttemptDelivery(mail, retries); } else { LOGGER.debug("Bouncing message {} after {} retries", mail.getName(), retries); bouncer.bounce(mail, new Exception("Too many retries failure. Bouncing after " + retries + " retries.\n" + executionResult.getException().map(e -> { if (e instanceof MessagingException) { return e.getMessage() + "\n" + ((MessagingException) e).getNextException().getMessage(); } return e.getMessage(); }).orElse(""), executionResult.getException().orElse(null))); } }
|
尝试打包后失败,原因是代码格式未通过校验。相关错误是import的位置不对和代码缩减使用了tab,处理一下就行能顺利打包了。
如果之前已经打过包的,只需要在server/mailet/mailets和要使用的构建版本打两次包就行
我这里用的docker,所以需要的是server/apps/jpa-app/target下的jib-image.tar,我还顺便改了一下image的标签