用golang写了个统计各单位报送的信息数量的微服务
代码很乱,bug很多,将就着看吧。参考了很多网上代码,只能说声感谢了。
//cjl.ZongHeInfo.1.0
//目的:对各部门报上来的信息数量进行排名
//思路:预计一年信息量不超过100M,全部存入全局变量GlobalInfoDoc中,以方便排序,统计
//在协程中每5分钟将GlobalInfoDoc用json编码后存入文件中。因此,退出程序前应先手动保存(一定程度上可考虑用signal),避免5分钟内的数据丢失
//重要:生成的json备份文件不能用notepad编辑,要保存为UTF-8 NO BOM package main import (
"io/ioutil"
"math"
"net/url"
"strconv" //"encoding/base64"
"encoding/json"
"fmt"
"log"
"sort"
"strings" "sync" "net/http"
"os"
"os/exec"
"time"
) var (
DEPA = []string{"XX部", "XXX部", "X1部", "X2部", "XX公司", "XX公司", "XX厂", "XXX厂", "XX中心"}
GlobalInfoDoc TInfoDoc //保存了所有报上来的信息,其实就相当于一个文本文件
GlobalConf = make(map[string]string) //配置文件
GlobalManuscripts = "" //约稿要求,直接放投稿页面的placeholder中了 ) type TInfoDoc struct {
Infos []TInfo //属性名一定要大写,血的教训
sync.RWMutex //http是多线程的,加上锁
} type TInfo struct {
Time int64 `json:"name,omitempty"` //`时间`
Title string `json:"title,omitempty"` //`标题`
Content string `json:"Content,omitempty"` //`内容`
Department string `json:"Department,omitempty"` //`单位`
Who string `json:"Who,omitempty"` //`报送人`
Tel string `json:"Tel,omitempty"` //`电话`
Ip string `json:"IP,omitempty"` //IP
} //原计划利用cookie保存,后来认为用不上,把相关代码注释掉了
type TUser struct {
Name string
Tel string
} //用于对所报的信息按单位名称排序
type TInfoRanks []TInfoRank type TInfoRank struct {
Key string
Value int
} func main() {
//打开文件,若文件不存在则生成
year, _, _ := time.Now().Date()
fname := "info" + strconv.Itoa(year)
f, _ := os.OpenFile(fname, os.O_RDWR|os.O_CREATE, )
//将文件内容反序列化到全局变量infoDoc
out, _ := ioutil.ReadAll(f)
//fmt.Println(string(out))
json.Unmarshal(out, &GlobalInfoDoc)
//这里不能用defer f.Close(),因为main函数不会结束
f.Close()
//fmt.Println(infoDoc)
http.HandleFunc("/Add", Add)
http.HandleFunc("/Add2", Add2)
http.HandleFunc("/List", List)
http.HandleFunc("/save", save)
http.HandleFunc("/conf", conf)
http.HandleFunc("/yg", Manuscripts)
http.HandleFunc("/", Index)
exec.Command("cmd", "/c", "C:\\Progra~2\\Google\\Chrome\\Application\\chrome.exe", "http://localhost:8000/Add").Run()
go savefile()
fmt.Println("因5分钟才保存一次文件,所以退出程序前请访问/save以防止最近5分钟提交的信息丢失")
fmt.Println("请访问/conf更新配置文件的allowip")
http.ListenAndServe(":8000", nil) } func checkErr(err error) {
if err != nil {
log.Println(err)
}
} func Index(w http.ResponseWriter, r *http.Request) {
s := `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
a:link,a:visited{
text-decoration:none; /*超链接无下划线*/
color:red;
}
a:hover{
text-decoration:underline; /*鼠标放上去有下划线*/
}
</style>
</head>
<body>
<a href="http://192.168.0.239:8000/Add" target="_blank">信息报送</a>
</body>
</html>
`
fmt.Fprintf(w, s)
} func Add(w http.ResponseWriter, r *http.Request) { //模板
tpl := `<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
input{
width:500px;
}
textarea{
width:500px;
}
.labelspan{
display:inline-block;
width:80px;
}
.input2{
width:410px;
} </style> </head>
<body onload="javascript:init()"> <form action="Add2" method="post" onsubmit="return check()">
<fieldset>
<legend><a href="yg">_</a>信息汇报<a href="List">_</a></legend>
标题: <input id="Title" name="Title" type="text" size="" required="required"><br/>
内容: <textarea id="Content" name="Content" rows="" placeholder="{{Manuscripts}}" required="required"></textarea><br/>
正文限制(-1000字):<span id="txtNum"></span><br/>
部门(单位): <select id="Department" name="Department" required="required">
<option value="">请选择</option>
{{OPTIONS}}
</select><br>
<span class="labelspan">报 送 人:</span> <input id="Who" name="Who" class="input2" value="" type="text" size="" required="required"><br/>
<span class="labelspan">联系电话:</span> <input id="Tel" name="Tel" class="input2" value="" type="text" size="" required="required"><br/>
<input type="submit" value="提交">
</fieldset>
</form>
{{SORTLIST}}
<script>
var txt = document.getElementById("Content");
var txtNum = document.getElementById("txtNum");
txt.addEventListener("keyup", function(){
txtNum.textContent = txt.value.length;
})
</script>
<script>
function check(){
var title = document.getElementById("Title").value;
var Content = document.getElementById("Content").value;
var Department = document.getElementById("Department").selectedIndex;
var Who = document.getElementById("Who").value;
var Tel = document.getElementById("Tel").value;
if(title == null || title == '' ||title.length <){
alert("请完善标题!");
return false;
}else if(Content.length <||Content.length >){
alert("正文限制100至1000字!");
return false;
}else if(Department == ){
alert("请选择部门!");
return false;
}else if(Who.length <){
alert("请填写正确的姓名!");
return false;
}else if(Tel.length <){
alert("请填写联系电话!");
return false;
}
return true;
}
</script>
<script>
function init(){
self.moveTo(, );
self.resizeTo(screen.width, screen.height);}
</script>
</body>
</html>` //读cookie 注意:直接读来自用户的数据不安全
/*
var User TUser
u, err := r.Cookie("u")
if err == nil {
uu, _ := base64.StdEncoding.DecodeString(u.Value)
json.Unmarshal(uu, &User)
}
//fmt.Println(User)
tpl = strings.Replace(tpl, `{{WHO}}`, string(User.Name), -1)
tpl = strings.Replace(tpl, `{{TEL}}`, string(User.Tel), -1)
*/
//选项
var options strings.Builder
for _, v := range DEPA {
fmt.Fprintln(&options, `<option value="`, v, `">`, v, `</option>`)
}
//fmt.Println(options.String()) //统计
year, month, _ := time.Now().Date()
//统计各部门稿件数量的map
//本月
thisMonthFirstDay := time.Date(year, month, , , , , , time.Local) //本月第一天
nextMonthFirstDay := thisMonthFirstDay.AddDate(, , ) //下月第一天
_ThisMonth := periodInfos(thisMonthFirstDay, nextMonthFirstDay) //上月
lastMonthFirstDay := thisMonthFirstDay.AddDate(, -, ) //上月第一天
_LastMonth := periodInfos(lastMonthFirstDay, thisMonthFirstDay) //本年
thisYearFirstDay := time.Date(year, , , , , , , time.Local) //本年第一天
nextYearLastDay := thisYearFirstDay.AddDate(, , ) //明年第一天
_ThisYear := periodInfos(thisYearFirstDay, nextYearLastDay) //将map转切片,用sort.Slice排序后输出
var sortlist strings.Builder
s_ThisMonth := sortByValue(_ThisMonth)
s_LastMonth := sortByValue(_LastMonth)
s_thisYear := sortByValue(_ThisYear)
//本月ol
fmt.Fprintln(&sortlist, `<table><tr><th>本月排序</th><th>上月排序</th><th>本年排序</th><tr><td>`)
fmt.Fprintln(&sortlist, `<ol>`)
for _, v := range s_ThisMonth {
fmt.Fprintln(&sortlist, `<li>`, v.Key, v.Value, `篇`, `</li>`)
}
//上月ol
fmt.Fprintln(&sortlist, `</ol></td><td><ol>`)
for _, v := range s_LastMonth {
fmt.Fprintln(&sortlist, `<li>`, v.Key, v.Value, `篇`, `</li>`)
}
//本年ol
fmt.Fprintln(&sortlist, `</ol></td><td><ol>`)
for _, v := range s_thisYear {
fmt.Fprintln(&sortlist, `<li>`, v.Key, v.Value, `篇`, `</li>`)
}
fmt.Fprintln(&sortlist, `</ol></td></tr></table>`) //替换模板
tpl = strings.Replace(tpl, `{{OPTIONS}}`, options.String(), -)
tpl = strings.Replace(tpl, `{{SORTLIST}}`, sortlist.String(), -)
tpl = strings.Replace(tpl, `{{Manuscripts}}`, GlobalManuscripts, -) //模板输出
fmt.Fprintln(w, tpl) } //对报送的稿件数量排序
func sortByValue(m map[string]int) TInfoRanks { pl := make(TInfoRanks, len(m))
i :=
for k, v := range m {
pl[i] = TInfoRank{k, v}
i++
}
sort.Slice(pl, func(i, j int) bool {
flag := false
if pl[i].Value > pl[j].Value {
flag = true
} else if pl[i].Value == pl[j].Value {
if pl[i].Key < pl[j].Key {
flag = true
}
}
return flag
})
return pl
} func Add2(w http.ResponseWriter, r *http.Request) {
//r.ParseForm()
if r.Method == "POST" {
info := TInfo{}
info.Time = time.Now().Unix()
info.Title = safeFilter(r.PostFormValue("Title"))
info.Content = safeFilter(r.PostFormValue("Content"))
info.Department = safeFilter(r.PostFormValue("Department"))
info.Who = safeFilter(r.PostFormValue("Who"))
info.Tel = safeFilter(r.PostFormValue("Tel"))
ip := r.RemoteAddr
info.Ip = ip[:strings.LastIndex(ip, ":")]
//在全局InfoDoc的Info切片后追加info
GlobalInfoDoc.Lock()
GlobalInfoDoc.Infos = append(GlobalInfoDoc.Infos, info)
GlobalInfoDoc.Unlock() //设置cookie
//base64
/*
u := TUser{}
u.Name = info.Who
u.Tel = info.Tel
us, _ := json.Marshal(u)
uu := base64.StdEncoding.EncodeToString(us)
cookieu := http.Cookie{Name: "u", Value: uu, Path: "/", MaxAge: 86400 * 180}
http.SetCookie(w, &cookieu)
*/
//重定向,避免用户重复提交
http.Redirect(w, r, "Add", http.StatusFound)
return
}
//不允许GET访问
fmt.Fprintln(w, "what are you 弄啥哩!")
} func List(w http.ResponseWriter, r *http.Request) {
ip := r.RemoteAddr
ip = ip[:strings.LastIndex(ip, ":")]
allowip := GlobalConf["allowip"]
if !strings.Contains(allowip, ip) {
fmt.Fprintln(w, "not allow")
return
}
tpl := `
<html><head>
<meta charset="UTF-8">
<title></title>
<style type="text/css">
body{background-color:#f0f0f0;}
table{
width:1000px;
table-layout:fixed;/* 只有定义了表格的布局算法为fixed,下面td的定义才能起作用。 */
}
td{
width:%;
min-width:300px;
word-break:keep-all;/* 不换行 */
white-space:nowrap;/* 不换行 */
overflow:hidden;/* 内容超出宽度时隐藏超出部分的内容 */
text-overflow:ellipsis;/* 当对象内文本溢出时显示省略标记(...) ;需与overflow:hidden;一起使用*/
}
ul{
border:1px solid #;
}
li{
word-break:break-all;
}
.ctx{background-color:#f0f0f9;}
</style>
</head>
<body>
{{TB}}
{{PAGENAV}}
</body></html>` //获取用户输入url参数?p=1中的页码1
uri, _ := url.Parse(r.RequestURI)
urlParam := uri.RawQuery
uri_m, _ := url.ParseQuery(urlParam)
curpage := //设当前页为1
p_uri_int := //用户提供的页码,默认为1
uri_m_p, ok := uri_m["p"]
if ok {
p_uri_int, _ = strconv.Atoi(uri_m_p[])
}
perpage := //每页1000条
numall := len(GlobalInfoDoc.Infos)
if numall <= {
fmt.Fprintln(w, "无数据")
return
} //总信息数
maxpage := int(math.Ceil(float64(numall) / float64(perpage))) //末页 if p_uri_int <= {
curpage =
} else if p_uri_int > maxpage {
curpage = maxpage
} else {
curpage = p_uri_int
}
pagenav := `<a href=List>首页</a> <a href=List?p=` + strconv.Itoa(curpage-) + `>上一页</a> <a href=List?p=` + strconv.Itoa(curpage+) + `>下一页</a> <a href=List?p=` + strconv.Itoa(maxpage) + `>尾页</a>` //以ul>li方式输出表格
var sb strings.Builder
//逆序输出1000条,最新的稿件显示在最上面
start1000 := numall - (curpage-)*
end1000 := numall - curpage* + //如:从1001到2000,而不是1000到2000,所以加1
if end1000 < {
end1000 = //逆序输出,最后一条也就是第一条
}
for i := start1000; i >= end1000; i-- {
info := GlobalInfoDoc.Infos[i-] //第一条对应的索引为0,所以减1
time1 := time.Unix(int64(), ).Format("2006-01-02 15:04:05")
fmt.Fprintln(&sb, `<ul>`)
fmt.Fprintln(&sb, `<li>`, `ID:`, i, `</li>`)
fmt.Fprintln(&sb, `<li>`, time1, `</li>`)
fmt.Fprintln(&sb, `<li>`, info.Title, `</li>`)
fmt.Fprintln(&sb, `<li class="ctx">`, info.Content, `</li>`)
fmt.Fprintln(&sb, `<li>`, info.Department, `</li>`)
fmt.Fprintln(&sb, `<li>`, info.Who, `</li>`)
fmt.Fprintln(&sb, `<li>`, info.Tel, `</li>`)
fmt.Fprintln(&sb, `<li>`, info.Ip, `</li>`)
fmt.Fprintln(&sb, `</ul>`)
} //替换模板
tpl = strings.Replace(tpl, `{{TB}}`, sb.String(), -)
tpl = strings.Replace(tpl, `{{PAGENAV}}`, pagenav, -)
fmt.Fprintln(w, tpl)
} func save(w http.ResponseWriter, r *http.Request) {
savefile()
} //保存GlobalInfoDoc,每5分钟保存一次
func savefile() {
t1 := time.Tick( * time.Second)
for {
select {
case <-t1:
//fmt.Println("t1定时器")
year, _, _ := time.Now().Date()
fname := "info" + strconv.Itoa(year)
//文件不存在则创建
_, _ = os.OpenFile(fname, os.O_RDWR|os.O_CREATE, )
out, _ := json.Marshal(GlobalInfoDoc)
ioutil.WriteFile(fname, out, os.ModeExclusive)
}
} } //安全过滤字符串
func safeFilter(s string) string {
s = strings.Replace(s, `=`, `=`, -)
s = strings.Replace(s, `'`, `’`, -1)
s = strings.Replace(s, `"`, `”`, -1)
s = strings.Replace(s, `<`, `〈`, -)
s = strings.Replace(s, `>`, `〉`, -)
s = strings.Replace(s, string(byte()), `<br>`, -)
return s
} //统计一段时间内的稿件数量排名
func periodInfos(t1, t2 time.Time) map[string]int {
m := make(map[string]int, len(DEPA))
for _, v := range DEPA {
m[v] =
}
//更新投稿数量
for _, info := range GlobalInfoDoc.Infos {
dp := strings.TrimSpace(info.Department) //这里将字符串转[]byte后发现前后有空格,asii为32
if _, ok := m[dp]; ok {
if info.Time >= t1.Unix() && info.Time < t2.Unix() {
m[dp]++
}
} }
return m
} //读配置文件
func conf(w http.ResponseWriter, r *http.Request) {
//配置文件中每行的格式类似:a=1
//将a=1解析到map中,k为等号左边的a,v为等号右边的1
var m = make(map[string]string, )
fname := "conf.txt"
//文件不存在则创建
f, _ := os.OpenFile(fname, os.O_RDWR|os.O_CREATE, )
defer f.Close()
if si, _ := f.Stat(); si.Size() == {
f.WriteString("allowip=[::1],192.168.3.4,192.168.2.4")
}
b, _ := ioutil.ReadFile(fname)
c := strings.Split(string(b), "\n")
for _, v := range c { if v != "" {
d := strings.Split(v, "=")
m[d[]] = d[]
}
}
var l *sync.Mutex
l = new(sync.Mutex)
l.Lock()
defer l.Unlock()
GlobalConf = m
} //设置约稿内容
func Manuscripts(w http.ResponseWriter, r *http.Request) {
ip := r.RemoteAddr
ip = ip[:strings.LastIndex(ip, ":")]
allowip := GlobalConf["allowip"]
if !strings.Contains(allowip, ip) {
fmt.Fprintln(w, "not allow")
return
}
//输入录入页面
if r.Method == "GET" {
tpl := `
<html>
<head>
<meta charset="utf-8">
<title></title></head>
<body>
<form action="" method="POST">
<textarea id="yg" name="yg" rows= cols= value="" >{{Manuscripts}}</textarea>
<input type="submit" value="提交">
</form>
</body>
</html>
`
//替换模板
tpl = strings.Replace(tpl, `{{Manuscripts}}`, GlobalManuscripts, -)
fmt.Fprintln(w, tpl)
return
} else if r.Method == "POST" {
s := r.PostFormValue("yg")
var l *sync.Mutex
l = new(sync.Mutex)
l.Lock()
defer l.Unlock()
GlobalManuscripts = s
//同时将约稿内容保存为yg.txt 如果用ioutil.WriteFile,则os.ModeAppend是无效的
//outil.WriteFile("yg.txt", []byte(s), os.ModeAppend)
fl, err := os.OpenFile("yg.txt", os.O_APPEND|os.O_CREATE, )
if err != nil {
return
}
defer fl.Close()
fl.Write([]byte(s))
//重定向,避免用户重复提交
http.Redirect(w, r, "Add", http.StatusFound)
return
} else {
fmt.Fprintln(w, "what are you 弄啥哩!")
} }
用golang写了个统计各单位报送的信息数量的微服务的更多相关文章
- 如何加速golang写业务的开发速度
如何加速golang写业务的开发速度 不要忌讳panic golang写业务代码经常会被吐槽,写业务太慢了,其中最大的吐槽点就是,处理各种error太麻烦了.一个项目中,会有30%或者更多的是在处理e ...
- 使用golang写一个redis-cli
使用golang写一个redis-cli 0. redis通信协议 redis的客户端(redis-cli)和服务端(redis-server)的通信是建立在tcp连接之上, 两者之间数据传输的编码解 ...
- Golang写文件的坑
Golang写文件一般使用os.OpenFile返回文件指针的Write方法或者WriteString或者WriteAt方法,但是在使用这三个方法时候经常会遇到写入的内容和实际内容有出入,因为这几个函 ...
- github上用golang写的项目
1.moby/moby docker的新马甲 2.kubernetes/kubernetes 分布式容器管理 3.grafana/grafana 一个可视化面板,有漂亮的仪表盘,多种数据来源,适合做系 ...
- [goa]golang微服务框架学习--安装使用
当项目逐渐变大之后,服务增多,开发人员增加,单纯的使用go来写服务会遇到风格不统一,开发效率上的问题. 之前研究go的微服务架构go-kit最让人头疼的就是定义服务之后,还要写很多重复的框架代码, ...
- Golang微服务实践
背景 在之前的文章<漫谈微服务>我已经简单的介绍过微服务,微服务特性是轻量级跨平台和跨语言的服务,也列举了比较了集中微服务通信的手段的利弊,本文将通过RPC通信的方式实现一个增删查Redi ...
- 【GoLang】go 微服务框架 && Web框架学习资料
参考资料: 通过beego快速创建一个Restful风格API项目及API文档自动化: http://www.cnblogs.com/huligong1234/p/4707282.html Go 语 ...
- 【GoLang】GoLang 微服务、开源库等参考资料
参考资料: GoLang书籍: https://github.com/dariubs/GoBooksGo名库: https://github.com/Unknwon/go-rock-libraries ...
- 【GoLang】golang 微服务框架 go-kit
golang-Microservice Go kit - A toolkit for microservices kubernetes go-kit_百度搜索 Peter Bourgon谈使用Go和& ...
随机推荐
- Windows10文件目录下添加 Shift+右键打开管理员Powershell窗口
背景(可略过) 目前在调试 Python 程序,遇到了一个问题:当程序中包含多线程时,使用 IDLE 运行是不会执行多线程的语句的,在网上一顿搜罗了解到这种情况可以换成在命令行下执行.好像用 PyCh ...
- 详解k8s原生的集群监控方案(Heapster+InfluxDB+Grafana) - kubernetes
1.浅析监控方案 heapster是一个监控计算.存储.网络等集群资源的工具,以k8s内置的cAdvisor作为数据源收集集群信息,并汇总出有价值的性能数据(Metrics):cpu.内存.netwo ...
- 《深入理解 JVM 虚拟机》 --- 看书笔记
1.JVM 内存溢出 1.堆溢出:堆要不断的创建对象,如果避免了垃圾回收来清除这些对象,就会产生JVM内存溢出.一般手段是通过内存映像分析工具对Dump出来的堆转储快照进行分析,分清楚到底是内存泄露还 ...
- 聊聊Mysql索引和redis跳表
摘要 面试时,交流有关mysql索引问题时,发现有些人能够涛涛不绝的说出B+树和B树,平衡二叉树的区别,却说不出B+树和hash索引的区别.这种一看就知道是死记硬背,没有理解索引的本质.本文旨在剖析这 ...
- FragmentTabHostTopDemo【FragmentTabHost固定宽度且居中】
版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 使用FragmentTabHost实现顶部选项卡(居中且宽度非全屏)展现. 备注:该Demo主要是演示FragmentTabHost ...
- HandlerInterceptor里@Autowired对象为空的解决方法
That's because Spring isn't managing your PagePopulationInterceptor instance. You are creating it yo ...
- Asp.Net Core微服务再体验
ASP.Net Core的基本配置 .在VS中调试的时候有很多修改Web应用运行端口的方法.但是在开发.调试微服务应用的时候可能需要同时在不同端口上开启多个服务器的实例,因此下面主要看看如何通过命令行 ...
- 爸爸又给Spring MVC生了个弟弟叫Spring WebFlux
情景引入 很早之前,Java就火起来了,是因为它善于开发和处理网络方面的应用. Java有一个爱好,就是喜欢制定规范标准,但自己又不善于去实现. 反倒是一些服务提供商使用它的规范标准来制造应用服务器而 ...
- SpringCloud系列——Bus 消息总线
前言 SpringCloud Bus使用轻量级消息代理将分布式系统的节点连接起来.然后可以使用此代理广播状态更改(例如配置更改)或其他管理指令.本文结合RabbitMQ+GitHub的Webhook实 ...
- Docker的使用
Ubuntu16.04+ 在Ubuntu系统中安装较为简单,官方提供了脚本供我们进行安装. sudo apt install curl curl -fsSL get.docker.com -o get ...