想象一个具有大量不同路由的 Web 服务。其中一些会触发发送给用户的交易电子邮件。初始化邮件程序实例似乎很奇怪,例如github.com/aws/aws-sdk-go/service/sns每次请求想要发送某些东西时使用的东西。
相反,我假设有一个邮件程序实例,并且一切都发生在一个单独的频道上,amessage被发布到该频道。
例子
我创建了一个简单的例子来说明这个问题。全局Mailer实例配置一次,Index处理程序请求一个通道并传递一个Message.
package main
import (
"fmt"
"log"
"net/http"
"os"
)
// Message is the custom type used to pass the channel
type Message struct {
To string
Subject string
Body string
}
// Mailer is responsible to send out emails
type Mailer struct{}
// send sends out the email
func (m *Mailer) send(message Message) {
fmt.Printf("Sending email to:`%s`\nSubject: %s\n%s\n\n", message.To, message.Subject, message.Body)
}
// Messages returns the channel to which messages can be passed
func (m *Mailer) Messages() chan<- Message {
cm := make(chan Message)
go func() {
msg := <-cm
m.send(msg)
close(cm)
}()
return cm
}
// mailer is a global var in this example, would probably be part of some
// sort of app context that's accessible from any handler.
//
// Note the mailer is NOT handler-scoped.
var mailer = Mailer{} // would this be thread-safe?
// Index handler
func Index(w http.ResponseWriter, r *http.Request) {
m := Message{"email@example.com", fmt.Sprintf("visited `%s`", r.URL.Path[1:]), "Lorem ipsum"}
mailer.Messages() <- m
fmt.Fprintf(w, "Sent out email with subject line `%s`\n", m.Subject)
}
func main() {
http.HandleFunc("/", Index)
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
if err := http.ListenAndServe(":"+port, nil); err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
输出
访问http://localhost:8080/hello-world将呈现…
发送主题为“visited `hello-world”的电子邮件
...并记录
发送电子邮件到`email@example.com`:
访问了`hello-world`
Lorem ipsum
问题
这是正确的方法吗?
它是线程安全的——如果不是,如何让它成为线程安全的?
子衿沉夜
相关分类