Go Smtp ve RabbitMQ ile Email servisi
Tags: go, rabbitmq, smtp, fiber
Published: 2021-12-03 13:23:51

Selamlar Ben Tayfur Kaya yeni **Golang** öğreniyorum ve size öğrenirken yaşadığım zorlukları ve avantajları bu projede göstermek istiyorum.
Bugün go dili ile yazmış olduğum basit bir email service inceleyeceğiz.
kullandığım teknolojiler RabbitMQ, Smtp, fiber ve Gorm 
Github Reposuna gitmek istiyorsanız [Github](https://github.com/Tayfur/Email-Service)
# Stmp
Golang ile basit bir sekilde gönderici olan mail adresine giriş yaparak istediğimiz email adresine mesajı html template olarak göndereceğiz


### Html nasıl parse edilir ?

{% gist https://gist.github.com/Tayfur/45a27ab426876126a490f5826a81a14a.js %}
```go
 template.ParseFiles("./Templates/FeatureNotification.html")
```
yukarıda html dosyasını parse ediyoruz.
ve aşağıdaki gibi execute ediyoruz buradaki subject kısmı ise gönderilecek mailin title yazıyoruz..

```go
templateBuffer.Write([]byte(fmt.Sprintf("Subject: Lates Features \n%s\n\n", mimeHeaders)))
	t.Execute(&templateBuffer, nil)
```

### Smtp ile mail göndermek
{% gist https://gist.github.com/Tayfur/262abbac1a6c34c87a40a9ab88b53110.js %}

sendMail fonksiyonu hangi consumerden triggerlandıysa ilgili htmli parse ediyoruz
```go
	switch tamplate {
	case "WelcomeQueue":
		body = parseHtml.WelcomeTemplate()
		break
	case "WeeklyReportQueue":
		body = parseHtml.WeeklyReportTemplate()
		break
	case "FeatureNotificationQueue":
		body = parseHtml.FeatureNotificationTemplate()
		break
	}
```

```go
err := smtp.SendMail(smtpHost+":"+smtpPort, auth, senderMail, to, []byte(body))
```
yukaradaki kod bloğunda mail adresimize authentication oluyoruz ve oldugumuz email adresinden **to** göndereceğimiz mail adresini içeriyor. **msg**  kısmı ise gönderilecek olan mesajı içeriyor ama biz orada bir mesaj yerine html template göndereceğiz.

# RabbitMQ
diyebilirsinizki yukarıda basit yazıyordu ne gerek var rabbitMQ kullanmaya!
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/de8czqszvyzpi1sxyuli.png)
Evet var çünkü gönderecek oldugumuz mailleri **Message queue**
ya göndermemiz gerekiyor,  yaptıgımız app belki databaseden aldığımız mail adreslerine email atarken yarısında app crashe olabilir ve biz hangi mail adreslerine mail gönderildi hangisine gönderilmedi bunu bilemeyiz.

 Birden fazla email templatemiz olabilir mesela welcome, verify, Feature Notification gibi farkettiginiz gibi bazıları **Bulk**(toplu gönderilen) mail bazıları **Transaction**(tekli ) mail.

Biz her template icin bir **Consume** ve **Publish** oluşturacağız böylelikle mail adresleri ilk önce queue gidicek sonra Consume"a iletilecek ve oradan templatine göre smtp ile maili göndereceğiz =)


### Publish

RabbitMQ bağlantısı oluşturuyoruz
```go
connectRabbitMQ, err := amqp.Dial(amqpServerURL)

```
consume ile haberleşmek için bir channel oluşturuyoruz
```go
channelRabbitMQ, err := connectRabbitMQ.Channel()


```
yeni bir mesaj oluşturup bunu queue gönderiyoruz ama burdaki en önemli nokta verilen queue name"i çok önemli bu fonksiyonun çağrıldığı yerden alıyor ve mesajı o queue ismine göre gönderiyor bu sayede consume ederken her template icin farklı queue den onları alabileceğiz
```go
	message := amqp.Publishing{
		ContentType: "text/plain",
		Body:        msg,
	}

	// mesaji publish eder.
	if err := channelRabbitMQ.Publish(
		"",// exchange
		queueName,// queue name
		false,
		false,
		message,
	); err != nil {
		return err
	}

```


{% gist https://gist.github.com/Tayfur/333e8375acd98a191c966eea7ed366c3.js %}

### Consume
buradaki işlemlerde aynı sayılır ama bir farkla =)
queue deki mesajları okuyup her mesaj için bir sendMail fonksiyonunu trigger ediyor
```go
	forever := make(chan bool)
	go func() {
		for message := range messages {
			to := []string{string(message.Body)}
			//queue'dan gelen mesaji mail.Send fonksiyonuna gönder
			mail.SendMail(to, queueName)
			log.Printf(" > Received message: %s\n", message.Body)
		}
	}()

	<-forever
```

{% gist https://gist.github.com/Tayfur/c7d3444d637981ab0650a0235fa43695.js %}

### Bulk Mail & Transaction Mail
Yukarıda bu ikisinden bahsetmiştik genel anlamda toplu gönderilen maillere Bulk ,ve sadece tekli işlemler için gönderilen maillere Transaction mail denir

```go
	db, err := db.ConnectDB()
	if err != nil {
		log.Fatal(err)
		panic(err)
	}
	var User []models.User
	db.Find(&User)

```
yukarıda diye bağlanıp dbdeki User ların hepsini arrayin içine alıyoruz
Aşağıda is User arrayini for döngüsüne alıp her user"ın maili için []byte a dönüştürüp bunu queue gönderiyoruz burada []byte a dönüştürmemizin sebebi RabbitMQ channeli byte ile haberleşmesi

```go
	for _, t := range User {
		var to []byte
		to = []byte(t.Email)
		rabbitmq.Publisher(to, template)
	}

```
{% gist https://gist.github.com/Tayfur/226226b767bc90a0b5065e563af64c83.js %}

Transaction maillerinde ise farklı farklı işlemler yapılabilir ama biz basit bir halde fiber endpointinden gelen mail adresine email göndereceğiz  

{% gist https://gist.github.com/Tayfur/c45cdf9eb8c489acf515021551f994e4.js %}


# Main
Main fonksiyonu içinde goroutine ile bütün consumeleri dinliyoruz bunlar hep açık kalıyor queue herhangi bir mail girdiğinde direkt bunları işleme gönderecek
```go
WelcomeQueue := "WelcomeQueue"
	WeeklyReportQueue := "WeeklyReportQueue"
	FeatureNotificationQueue := "FeatureNotificationQueue"
	// consumelari dinliyoruz
	go rabbitmq.Consume(WeeklyReportQueue)
	go rabbitmq.Consume(FeatureNotificationQueue)
	go rabbitmq.Consume(WelcomeQueue)

```
Dediğim gibi Mailleri gönderirken fiber endpointleri trigger edicek aslında daha complex hale getirilebilir örneğin eğer diye yeni bir kullanıcı eklendiyse o indexteki maile welcomeTemplati göndermek gibi ama şimdi daha basit bir şekilde handle ediyoruz

```go
	app := fiber.New()
	app.Use(
		logger.New(), // add simple logger
	)

	app.Get("/FeatureNotification", func(c *fiber.Ctx) error {
		go logic.BulkMail(FeatureNotificationQueue)
		return c.SendString("Feature notification Sended")
	})
	app.Get("/WelcomeNotification", func(c *fiber.Ctx) error {
		msg := []byte(c.Query("msg"))
		logic.Transactional(msg,WelcomeQueue)
		return c.SendString("Welcome Sended to "+string(msg))
	})
```
{% gist https://gist.github.com/Tayfur/98466fb6e313c769566112435a080f52.js %}

##conclusion
Bence **go** yazması çok keyifli bir dil ve kolayca service oluşturabiliyorsunuz eğer sizde benim gibi başka yazılım dillerinden geldiyseniz variables'a önem verin çünkü verileri çok defa convert etmem gerekti
umarım yazımı beğenmişsinizdir, yazım yanlışları için özür dilerim elimden geldiğince anlaşılır yazmaya çalıştım