树莓派网站容灾:利用DNSPod,Google App Engine和Github

背景介绍 把网站托管在树莓派上后如果家里停电或是宽带故障,会造成网站中断。本文提供一个免费的解决方案(前提是你需要有自己的一个域名,并由DNSPod解析) DNSPod 首先需要在DNSPod里设置好需要failover的域名CNAME:比如hugozhu.myalert.info 其中默认指向pi.myalert.info, 这是一个域名的A Record,会由运行在树莓派上的脚本来更新动态IP,国外则指向github。当停电时我们需要自动把`默认`这条纪录修改成github。 使用下面命令获得相应CNAME的domain_id: curl -k https://dnsapi.cn/Domain.List -d "login_email=xxx&login_password=xxx" 使用下面命令获得相应CNAME的record_id: curl -k https://dnsapi.cn/Record.List -d "login_email=xxx&login_password=xxx&domain_id=xxx" Google App Engine 切换DNS脚本 package dnspod import ( "io/ioutil" "net/http" "net/url" "strings" ) const ( login_email = "<your_login_email>" login_password = "<your_login_password>" format = "json" domain_id = "<domain_id>" record_id = "<record_id>" sub_domain = "<your_subdomain>" record_type = "CNAME" record_line = "默认" ttl = "600" ) func Update(client *http.Client, cname string) string { body := url. [Read More]

使用Goroutine和Channel实现按键超时交互

背景介绍 前面的文章(见参考链接)已经介绍了如何使用按键作为树莓派的输入。在实际应用中可以通过按下按键循环显示预先设定的脚本输出到显示屏幕,需求如下: 如果按键不被触动,则定时5秒执行脚本获取最新内容显示; 因为不同的脚本获取内容速度会不一样,我们要求如果超过500ms脚本还未返回,需要在屏幕上显示“loading…”这样的过渡内容,如果脚本在500ms内返回,则不显示。 使用Goroutine和Channel可以很方便的实现这个需求。 代码 var screen_chan chan int var switch_chan = make(chan bool) func main() { //a goroutine: 检查按键是否被按 go func() { last_time := time.Now().UnixNano() / 1000000 btn_pushed := 0 total_mode := 3 for msg := range WiringPiISR(PIN_GPIO_6, INT_EDGE_FALLING) { if msg > -1 { n := time.Now().UnixNano() / 1000000 delta := n - last_time if delta > 300 { //如果两次按键变化的间隔时间<300ms,是因为接触信号不稳定可以忽略掉 last_time = n btn_pushed++ screen_chan <- btn_pushed % total_mode } } } }() //a goroutine: 根据管道消息刷新屏幕 go loop_update_display() //选择确实的屏幕内容脚本编号 screen_chan <- 0 //a goroutine: 定时5s向管道发送更新屏幕内容的信号 ticker := time. [Read More]

Go语言内存模型

名词定义 执行体 - Go里的Goroutine或Java中的Thread 背景介绍 内存模型的目的是为了定义清楚变量的读写在不同执行体里的可见性。理解内存模型在并发编程中非常重要,因为代码的执行顺序和书写的逻辑顺序并不会完全一致,甚至在编译期间编译器也有可能重排代码以最优化CPU执行, 另外还因为有CPU缓存的存在,内存的数据不一定会及时更新,这样对内存中的同一个变量读和写也不一定和期望一样。 和Java的内存模型规范类似,Go语言也有一个内存模型,相对JMM来说,Go的内存模型比较简单,Go的并发模型是基于CSP(Communicating Sequential Process)的,不同的Goroutine通过一种叫Channel的数据结构来通信;Java的并发模型则基于多线程和共享内存,有较多的概念(violatie, lock, final, construct, thread, atomic等)和场景,当然java.util.concurrent并发工具包大大简化了Java并发编程。 Go内存模型规范了在什么条件下一个Goroutine对某个变量的修改一定对其它Goroutine可见。 Happens Before 在一个单独的Goroutine里,对变量的读写和代码的书写顺序一致。比如以下的代码: package main import ( "log" ) var a, b, c int func main() { a = 1 b = 2 c = a + 2 log.Println(a, b, c) } 尽管在编译期和执行期,编译器和CPU都有可能重排代码,比如,先执行b=2,再执行a=1,但c=a+2是保证在a=1后执行的。这样最后的执行结果一定是1 2 3,不会是1 2 2。但下面的代码则可能会输出0 0 0,1 2 2, 0 2 3 (b=2比a=1先执行), 1 2 3等各种可能。 package main import ( "log" ) var a, b, c int func main() { go func() { a = 1 b = 2 }() go func() { c = a + 2 }() log. [Read More]

在Raspberry Pi上使用Google Channel服务搭建实时应用

前面提到了有关个人网站的实时在线人数问题,本文要讨论的是如何自己来实现一个这样的统计服务。因为网站也同时部署在Github上,海外用户访问Github镜像网站的访问日志Pi是拿不到的,这怎么办? Google Channel Service Google Channel Service允许应用和GAE (Google App Engine) 保持一个长连接,允许应用实时发送消息给JavaScript客户端,而不用让客户端用效率很低的定时轮询获取新消息。这个服务是允许有多个发布者和多个订阅者,也能创建多个主题来关联发布者和订阅者。 使用这个服务分两步: 客户端请求服务器端(部署在GAE上)获取一个Channel的Token: 客户端根据Channel Token和服务器建立长连接,并开始接收消息,这时其它的客户端(或服务器端)可以想这个通道发送消息 在线人数统计实现 在页面上部署beacon 通常的网站流量统计是依赖部署在页面上的beacon(Javascript或图片标签)来实现的,这样做的好处是可以直接过滤掉一些机器流量,并且可以将日志集中存储在日志收集服务器上,和网站分离开。 于是可以利用GAE实现一个简单的Beacon服务,这里采用Go语言来实现,用Java或Python也是可以的。 package counter import ( "encoding/base64" "fmt" "time" "net/http" "appengine" "appengine/channel" ) var GIF []byte const ( TOPIC = "counter" ) func init() { GIF, _ = base64.StdEncoding.DecodeString("R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7") http.HandleFunc("/beacon.gif", handler) http.HandleFunc("/new_token", handler_new_token) } func handler(w http.ResponseWriter, r *http.Request) { context := appengine.NewContext(r) now := time.Now() expire := now.AddDate(30, 0, 0) zcookie, _ := r. [Read More]