程序说明:最近公司服务号搞发红包活动,活动那个火啊(一帮见钱眼开的家伙,当然我也是^&^)但是服务器太烂(单台小企业服务器只支持php5.2),并发量和php性能都不够,结果导致很多红包没发出去,于是就想前端用php把用户的openid和红包的存到redis里,然后后端用golang在服务器里并行发,事实证明too young too simple,由于golang协程是并行的(几乎同一时间),所以导致订单号都是一样的,没办法暂时还没想到解决方案(有解决方案的兄弟可以私信我),只能折中,每隔三秒自动一个个的发,经过两天的奋战终于大功告成!
注意:请go build后再运行,速度跟go run差?倍,反正速差非常多,代码阅读对像,有Golang基础的兄弟
package main
import (
"bytes"
"crypto/md5"
"crypto/tls"
"crypto/x509"
"encoding/hex"
"encoding/xml"
"fmt"
"github.com/garyburd/redigo/redis"
"io/ioutil"
"log"
"math/rand"
"net/http"
"os"
"runtime"
"strconv"
"strings"
"time"
)
type Bonus struct {
XMLName xml.Name `xml:"xml"`
Act_name string `xml:"act_name"`
Client_ip string `xml:"client_ip"`
Mch_billno string `xml:"mch_billno"`
Mch_id string `xml:"mch_id"`
Nonce_str string `xml:"nonce_str"`
Re_openid string `xml:"re_openid"`
Remark string `xml:"remark"`
Send_name string `xml:"send_name"`
Total_amount int `xml:"total_amount"`
Total_num int `xml:"total_num"`
Wishing string `xml:"wishing"`
Wxappid string `xml:"wxappid"`
Sign string `xml:"sign"`
}
const (
WECHATCERTPATH = "D:/apiclient_cert.pem" //客户端证书存放绝对路径
WECHATKEYPATH = "D:/apiclient_key.pem" //客户端私匙存放绝对路径
WECHATCAPATH = "D:/wwwroot/rootca.pem" //服务端证书存放绝对路径
WECHATURL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack" //微信红包发送链接
REDISURL = "127.0.0.1:6379" //redis地址
ENDTIME = "201603221657" //红包活动结束时间
OPENIDS = "openids" //中奖用户redis openid列表名
BONUSPRICEPOSTFIX = "_bonusprice" //redis get对应用户红包价格的后缀 例:set openid+BONUSPRICEPOSTFIX price
//微信红包内容XML设置
ACT_NAME = "" //活动名
CLIENT_IP = "" //服务器IP
MCH_ID = "1231911402" //商户号
REMARK = ""
SEND_NAME = ""
TOTAL_NUM = 1 //红包个数
WISHING = ""
WXAPPID = ""
KEY = ""
)
var _tlsConfig *tls.Config
func getTLSConfig() (*tls.Config, error) {
if _tlsConfig != nil {
return _tlsConfig, nil
}
// load cert
cert, err := tls.LoadX509KeyPair(WECHATCERTPATH, WECHATKEYPATH)
if err != nil {
fmt.Println("load wechat keys fail", err)
return nil, err
}
// load root ca
caData, err := ioutil.ReadFile(WECHATCAPATH)
if err != nil {
fmt.Println("read wechat ca fail", err)
return nil, err
}
pool := x509.NewCertPool()
pool.AppendCertsFromPEM(caData)
_tlsConfig = &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: pool,
}
return _tlsConfig, nil
}
func SecurePost(url string, xmlContent []byte) (*http.Response, error) {
tlsConfig, err := getTLSConfig()
if err != nil {
return nil, err
}
tr := &http.Transport{TLSClientConfig: tlsConfig}
client := &http.Client{Transport: tr}
return client.Post(
url,
"text/xml",
bytes.NewBuffer(xmlContent))
}
func bonus(oid string, price int) {
openid := oid //openid
total_amount := price * 100 //金额
//生成随机数并md5加密随机数
rand.Seed(time.Now().UnixNano())
md5Ctx1 := md5.New()
md5Ctx1.Write([]byte(strconv.Itoa(rand.Intn(1000))))
nonce_str := hex.EncodeToString(md5Ctx1.Sum(nil)) //随机数
//订单号
mch_billno := MCH_ID + time.Now().Format("20060102") + strconv.FormatInt(time.Now().Unix(), 10)
// 生成签名
s1 := "act_name=" + ACT_NAME + "&client_ip=" + CLIENT_IP + "&mch_billno=" + mch_billno + "&mch_id=" + MCH_ID + "&nonce_str=" + nonce_str + "&re_openid=" + openid + "&remark=" + REMARK + "&send_name=" + SEND_NAME + "&total_amount=" + strconv.Itoa(total_amount) + "&total_num=" + strconv.Itoa(TOTAL_NUM) + "&wishing=" + WISHING + "&wxappid=" + WXAPPID + "&key=" + KEY
md5Ctx2 := md5.New()
md5Ctx2.Write([]byte(s1))
s1 = hex.EncodeToString(md5Ctx2.Sum(nil))
sign := strings.ToUpper(s1) //签名
v := &Bonus{
Act_name: ACT_NAME,
Client_ip: CLIENT_IP,
Mch_billno: mch_billno,
Mch_id: MCH_ID,
Nonce_str: nonce_str,
Remark: REMARK,
Send_name: SEND_NAME,
Re_openid: openid,
Total_amount: total_amount,
Total_num: TOTAL_NUM,
Wishing: WISHING,
Wxappid: WXAPPID,
Sign: sign}
output, err := xml.MarshalIndent(v, " ", " ")
if err != nil {
fmt.Printf("error: %v\n", err)
}
// os.Stdout.Write(output)
//POST数据
SecurePost(WECHATURL, output)
}
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
ticker := time.NewTicker(3 * time.Second)
for {
if time.Now().Format("200601021504") >= ENDTIME {
log.Printf("over")
break
ticker.Stop()
os.Exit(1)
} else {
<-ticker.C
//redis
redisDataBase, _ := redis.Dial("tcp", REDISURL)
openidsLen, _ := redis.Int(redisDataBase.Do("llen", OPENIDS))
if openidsLen > 0 {
log.Printf("Start Bonus")
openid, _ := redis.String(redisDataBase.Do("lpop", OPENIDS))
log.Printf(openid)
//获取红包价格
bonusPrice, _ := redis.Int(redisDataBase.Do("get", openid+BONUSPRICEPOSTFIX))
redisDataBase.Do("del", openid+BONUSPRICEPOSTFIX)
redisDataBase.Close()
bonus(openid, bonusPrice)
log.Printf("End Bonus")
} else {
log.Printf("Openids Is Empty")
redisDataBase.Close()
}
}
}
}
请发表评论