应用程序的邮件系统之类的东西是否应该在一个单独的频道中运行,如本例所示?

想象一个具有大量不同路由的 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


问题

这是正确的方法吗?

它是线程安全的——如果不是,如何让它成为线程安全的?


宝慕林4294392
浏览 167回答 1
1回答

子衿沉夜

在这个例子中,你并没有真正做任何事情,但是通过通道传递消息总是安全的——通道是语言中的基本并发原语之一。你让自己对竞争条件的可能性持开放态度,这取决于send实际最终做什么。处理此问题的另一种方法是send从单个通道接收。type Mailer struct{&nbsp; &nbsp; Messages chan Message}func (m *Mailer) send() {&nbsp; &nbsp; for message := range m.Messages {&nbsp; &nbsp; &nbsp; &nbsp; fmt.Printf("Sending email to:`%s`\nSubject: %s\n%s\n\n", message.To, message.Subject, message.Body)&nbsp; &nbsp; }}var mailer *Mailerfunc Index(w http.ResponseWriter, r *http.Request) {&nbsp; &nbsp; m := Message{"email@example.com", fmt.Sprintf("visited `%s`", r.URL.Path[1:]), "Lorem ipsum"}&nbsp; &nbsp; mailer.Messages <- m&nbsp; &nbsp; fmt.Fprintf(w, "Sent out email with subject line `%s`\n", m.Subject)}func main() {&nbsp; &nbsp; mailer = &Mailer{&nbsp; &nbsp; &nbsp; &nbsp; // buffer up to 100 message to be sent before blocking&nbsp; &nbsp; &nbsp; &nbsp; Messages: make(chan Message, 100),&nbsp; &nbsp; }&nbsp; &nbsp; // start the mailer send loop&nbsp; &nbsp; go mailer.send()&nbsp; &nbsp; ...
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go