Commit c21f564e authored by Peter Cheng's avatar Peter Cheng

系統開發完成

parent 3493e490
No related merge requests found
env/env\.go
# counter
佇列管理員
## 資料夾結構
beans 用來裝Call API後之ResultObject
controllers Restful API呼叫用控制器
dev_env Docker-Compose環境產生設定
env 系統設定
route 系統路由設定
services 系統任務執行主程式
main.go 主程式
## 程式運行原理
本系統會透過多行程啟動一個Restful API與一個與Messae Quete溝通,並查看任務且執行任務之服務
當使用者透過呼叫本系統Restful API時,系統會將需要操作的事務記錄到Message Quete去,當使用者收到HTTP 200之後,本系統會每隔二秒自己去查看需要執行的任務,並執行他
本系統透過nsq做為Message Quete
目前它己有一個現成的寄信服務,其他可以依自己做擴充,要修改Resful API,就自己在Route/Web.go裡面設定路由,然後將對應的程式放到Controllers去,要開發供MQ執行的任務,就將程式放到Services去,並修改./main.go的runService內容
本來有想過這隻程式只做佇列排程就好,最後在透過gRPC或是CURL去呼叫其他的Restful API,這樣會比較乾淨,只是透過外部呼叫效能不會比同一個系統裡面執行還好
如果連nsq都不想自己架的人,可以自己安裝Docker與Docker Compose,自己到./dev_env資料夾下打docker-compose up -d --build,nsq會自己架好
## 必須套件
本程式透過Google Protobuf 3產生所需之ResultObject,然Proto 3之後官方不支持Custom Tags,所以還需要多安裝一個寫入retags的套件
git clone https://github.com/qianlnk/protobuf.git $GOPATH/src/github.com/golang/protobuf
go install $GOPATH/src/github.com/golang/protobuf/protoc-gen-go
還有與nsq溝通用之套件
go get -u -v github.com/bitly/go-nsq
及Restful Framework
go get -u -v github.com/gin-gonic/gin
## 程式操作流程
1. 將./env/env.swp檔名改成env.go
2. 修改./env/env.go並設定您的SMTP Server與Message Quete Server
3. 到./beans底下,運行protoc --go_out=plugins=grpc+retag:. *.proto
4. go run main.go
\ No newline at end of file
// Code generated by protoc-gen-go.
// source: quete.proto
// DO NOT EDIT!
package beans
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
type Quete struct {
UseService string `protobuf:"bytes,1,opt,name=UseService" json:"UseService"`
UseParams string `protobuf:"bytes,2,opt,name=UseParams" json:"UseParams"`
}
func (m *Quete) Reset() { *m = Quete{} }
func (m *Quete) String() string { return proto.CompactTextString(m) }
func (*Quete) ProtoMessage() {}
func (*Quete) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{0} }
func (m *Quete) GetUseService() string {
if m != nil {
return m.UseService
}
return ""
}
func (m *Quete) GetUseParams() string {
if m != nil {
return m.UseParams
}
return ""
}
func init() {
proto.RegisterType((*Quete)(nil), "beans.Quete")
}
func init() { proto.RegisterFile("quete.proto", fileDescriptor1) }
var fileDescriptor1 = []byte{
// 98 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2e, 0x2c, 0x4d, 0x2d,
0x49, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x4d, 0x4a, 0x4d, 0xcc, 0x2b, 0x56, 0x72,
0xe5, 0x62, 0x0d, 0x04, 0x89, 0x0a, 0xc9, 0x71, 0x71, 0x85, 0x16, 0xa7, 0x06, 0xa7, 0x16, 0x95,
0x65, 0x26, 0xa7, 0x4a, 0x30, 0x2a, 0x30, 0x6a, 0x70, 0x06, 0x21, 0x89, 0x08, 0xc9, 0x70, 0x71,
0x86, 0x16, 0xa7, 0x06, 0x24, 0x16, 0x25, 0xe6, 0x16, 0x4b, 0x30, 0x81, 0xa5, 0x11, 0x02, 0x49,
0x6c, 0x60, 0x43, 0x8d, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x19, 0xca, 0xf5, 0x19, 0x63, 0x00,
0x00, 0x00,
}
syntax="proto3";
package beans;
message Quete {
string UseService = 1; //`json:"UseService"`
string UseParams = 2; //`json:"UseParams"`
}
\ No newline at end of file
// Code generated by protoc-gen-go.
// source: sendMail.proto
// DO NOT EDIT!
package beans
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
type SendMail struct {
To []string ` protobuf:"bytes,1,rep,name=To" json:"To,omitempty" form:"to"`
Subject string ` protobuf:"bytes,2,opt,name=Subject" json:"Subject,omitempty" form:"subject"`
Content string ` protobuf:"bytes,3,opt,name=Content" json:"Content,omitempty" form:"content"`
}
func (m *SendMail) Reset() { *m = SendMail{} }
func (m *SendMail) String() string { return proto.CompactTextString(m) }
func (*SendMail) ProtoMessage() {}
func (*SendMail) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{0} }
func (m *SendMail) GetTo() []string {
if m != nil {
return m.To
}
return nil
}
func (m *SendMail) GetSubject() string {
if m != nil {
return m.Subject
}
return ""
}
func (m *SendMail) GetContent() string {
if m != nil {
return m.Content
}
return ""
}
func init() {
proto.RegisterType((*SendMail)(nil), "beans.SendMail")
}
func init() { proto.RegisterFile("sendMail.proto", fileDescriptor2) }
var fileDescriptor2 = []byte{
// 109 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2b, 0x4e, 0xcd, 0x4b,
0xf1, 0x4d, 0xcc, 0xcc, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x4d, 0x4a, 0x4d, 0xcc,
0x2b, 0x56, 0xf2, 0xe3, 0xe2, 0x08, 0x86, 0x4a, 0x08, 0xf1, 0x71, 0x31, 0x85, 0xe4, 0x4b, 0x30,
0x2a, 0x30, 0x6b, 0x70, 0x06, 0x31, 0x85, 0xe4, 0x0b, 0x49, 0x70, 0xb1, 0x07, 0x97, 0x26, 0x65,
0xa5, 0x26, 0x97, 0x48, 0x30, 0x29, 0x30, 0x6a, 0x70, 0x06, 0xc1, 0xb8, 0x20, 0x19, 0xe7, 0xfc,
0xbc, 0x92, 0xd4, 0xbc, 0x12, 0x09, 0x66, 0x88, 0x0c, 0x94, 0x9b, 0xc4, 0x06, 0x36, 0xdd, 0x18,
0x10, 0x00, 0x00, 0xff, 0xff, 0x69, 0x4f, 0x3a, 0xdc, 0x6f, 0x00, 0x00, 0x00,
}
syntax = "proto3";
package beans;
message SendMail {
repeated string To = 1; //`form:"to"`
string Subject = 2; //`form:"subject"`
string Content = 3; //`form:"content"`
}
\ No newline at end of file
package controllers
import "github.com/gin-gonic/gin"
//Home 首頁
func Home(response *gin.Context) {
response.String(200, "")
}
package controllers
import (
"encoding/json"
"fmt"
"log"
"net/http"
"../beans"
"../env"
"github.com/bitly/go-nsq"
"github.com/gin-gonic/gin"
)
var cfg = env.GetEnv()
func postMessage(message string) {
config := nsq.NewConfig()
w, _ := nsq.NewProducer(cfg.Message.Post.Address, config)
err := w.Publish("Mail", []byte(message))
if err != nil {
fmt.Println(err)
}
w.Stop()
}
//SendMail 寄信用API
func SendMail(c *gin.Context) {
sendMail := &beans.SendMail{}
err := c.BindJSON(sendMail)
if err != nil {
log.Println(err)
}
json, _ := json.Marshal(sendMail)
quete := "SendMail</UseService>" + string(json)
postMessage(quete)
c.JSON(http.StatusOK, gin.H{"status": "true"})
}
version: '2'
services:
nsqlookupd:
image: 'nsqio/nsq'
container_name: 'nsqlookupd'
command: /nsqlookupd
networks:
server:
ipv4_address: 10.1.0.4
ports:
- '0.0.0.0:4160:4160'
- '0.0.0.0:4161:4161'
restart: always
nsqd:
image: 'nsqio/nsq'
container_name: 'nsqd'
command: /nsqd -data-path=/data --lookupd-tcp-address=nsqlookupd:4160 --broadcast-address=127.0.0.1
volumes:
- './nsq:/data'
depends_on:
- nsqlookupd
networks:
server:
ipv4_address: 10.1.0.3
ports:
- '0.0.0.0:4150:4150'
- '0.0.0.0:4151:4151'
restart: always
nsadmin:
image: 'nsqio/nsq'
container_name: 'nsqadmin'
command: /nsqadmin --lookupd-http-address=nsqlookupd:4161
depends_on:
- nsqlookupd
networks:
server:
ipv4_address: 10.1.0.2
ports:
- '0.0.0.0:4171:4171'
restart: always
networks:
server:
driver: bridge
ipam:
config:
- subnet: 10.1.0.0/16
gateway: 10.1.0.1
\ No newline at end of file
package env
//Config 系統參數
type Config struct {
Env string
TimeFormat string
Message struct {
Post struct {
Address string
}
Received struct {
Address string
}
Topic string
Channel string
}
Mail struct {
Host string
User string
Password string
Port string
}
}
var cfg = &Config{}
func init() {
cfg.Env = "release"
cfg.TimeFormat = "2006-01-02 15:04:05"
cfg.Message.Post.Address = "127.0.0.1:4150"
cfg.Message.Received.Address = "127.0.0.1:4161"
cfg.Message.Topic = "Mail"
cfg.Message.Channel = "SendMail"
cfg.Mail.Host = ""
cfg.Mail.User = ""
cfg.Mail.Password = ""
cfg.Mail.Port = ""
}
//GetEnv 取得環境參數
func GetEnv() *Config {
return cfg
}
package main
import (
"encoding/json"
"log"
"strings"
"time"
"./services"
"./beans"
"./env"
"./route"
"github.com/bitly/go-nsq"
)
var cfg = env.GetEnv()
func main() {
services.Create("INFO", "存入Elasticsearch")
upStream := make(chan time.Time)
go func() {
webService()
upStream <- time.Now()
}()
go func() {
messageService()
upStream <- time.Now()
}()
<-upStream
}
//webService Restful API服務
func webService() {
api := route.API()
api.Run(":8805")
}
//messageService Message Quete服務
func messageService() {
host := cfg.Message.Received.Address
InitConsumer(cfg.Message.Topic, cfg.Message.Channel, host)
}
//InitConsumer 初始化消費者
func InitConsumer(topic string, channel string, host string) bool {
interval := time.Second * 2
upStream := make(chan int, 1)
config := nsq.NewConfig()
config.LookupdPollInterval = interval
query, err := nsq.NewConsumer("Mail", "SendMail", config)
if err != nil {
log.Panic(err)
}
query.AddHandler(nsq.HandlerFunc(func(message *nsq.Message) error {
chat := string(message.Body)
quete := getQuete(chat)
runServices(quete)
return nil
}))
if err = query.ConnectToNSQLookupd(host); err != nil {
panic(err)
}
<-upStream
return true
}
func getQuete(message string) []string {
quete := strings.Split(message, "</UseService>")
return quete
}
func runServices(quete []string) {
useService := quete[0]
params := []byte(quete[1])
switch useService {
case "SendMail":
sendMail := &beans.SendMail{}
json.Unmarshal(params, sendMail)
services.SendMail(sendMail)
}
}
package route
import (
"../controllers"
"../env"
"github.com/gin-gonic/gin"
)
var cfg = env.GetEnv()
//API Restful路由
func API() *gin.Engine {
gin.SetMode(cfg.Env)
route := gin.Default()
route.Any("/", controllers.Home)
route.POST("/Mail/SendMail", controllers.SendMail)
return route
}
package services
import (
"net/smtp"
"../beans"
"../env"
)
//SendMail 寄發通知郵件
func SendMail(params *beans.SendMail) error {
cfg := env.GetEnv()
host := cfg.Mail.Host + ":" + cfg.Mail.Port
auth := smtp.PlainAuth("", cfg.Mail.User, cfg.Mail.Password, cfg.Mail.Host)
message := []byte(
"Subject: " + params.GetSubject() + "\r\n" +
"To: " + params.GetTo()[0] + "\r\n" +
"From: " + cfg.Mail.User + "\r\n" +
"Content-Type: text/plain; charset=UTF-8" + "\r\n" +
"\r\n" +
params.GetContent() + "\r\n" +
"\r\n",
)
err := smtp.SendMail(host, auth, cfg.Mail.User, params.GetTo(), message)
return err
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment