服务计算——简单 web 服务与客户端开发实战

1、概述

利用 web 客户端调用远端服务是服务开发本实验的重要内容。其中,要点建立 API First 的开发理念,实现前后端分离,使得团队协作变得更有效率。

任务目标

  1. 选择合适的 API 风格,实现从接口或资源(领域)建模,到 API 设计的过程
  2. 使用 API 工具,编制 API 描述文件,编译生成服务器、客户端原型
  3. 使用 Github 建立一个组织,通过 API 文档,实现 客户端项目 与 RESTful 服务项目同步开发
  4. 使用 API 设计工具提供 Mock 服务,两个团队独立测试 API
  5. 使用 travis 测试相关模块

2、开发项目

项目地址

本次项目我主要负责数据库 boltdb 的接口以及数据库的建立。

boltdb 数据库

boltdb 数据库是使用 Go 语言编写的开源的键值对数据库,Github 地址如下:https://github.com/boltdb/bolt

boltdb 设计源于 LMDB,具有以下特点:

  • 直接使用 API 存取数据,没有查询语句;
  • 支持完全可序列化的 ACID 事务,这个特性比 LevelDB 强;
  • 数据保存在内存映射的文件里。没有 wal、线程压缩和垃圾回收;
  • 通过 COW 技术,可实现无锁的读写并发,但是无法实现无锁的写写并发,即读性能超高,但写性能一般,适合于读多写少的场景。

安装 boltdb 数据库

使用如下命令进行安装

go get github.com/boltdb/bolt

常见 API 操作

database.go 即为数据库操作的接口。
swapi 即为官方提供的接口

启动数据库
func Start(str string) {
	var err error
	dbName = str
	db, err = bolt.Open(dbName, 0666, &bolt.Options{Timeout: 1 * time.Second})
	if err != nil {
		log.Fatal(err)
		return
	}
}

其中 Open 的第一个参数为路径,如果数据库不存在则会创建数据库,第二个参数为文件操作,第三个参数为可选参数,可以配置只读和超时时间等。

停止数据库
func Stop(){
	if err := db.Close(); err != nil {
		log.Fatal(err)
	}
}
初始化数据库
func Init(str string) {
	if _,err := os.Open(str)  ; err == nil{
		log.Println("database is already exist . If you want to initialze it , please delete it and try again")
		return
	}
	Start(str)
	if err := db.Update(func(tx *bolt.Tx) error {
		tx.CreateBucket([]byte("users"))
		tx.CreateBucket([]byte("films"))
		tx.CreateBucket([]byte("people"))
		tx.CreateBucket([]byte("planets"))
		tx.CreateBucket([]byte("species"))
		tx.CreateBucket([]byte("starships"))
		tx.CreateBucket([]byte("vehicles"))
		return nil
	}); err != nil {
		log.Fatal(err)
	}
	Stop()
}
更新数据库
func Update(bucketName []byte, key []byte, value []byte) {
	if err := db.Update(func(tx *bolt.Tx) error {
		if err := tx.Bucket(bucketName).Put(key, value); err != nil {
			return err
		}
		return nil
	}); err != nil {
		log.Fatal(err)
	}
}

boltdb 数据库的读写事务操作可用 Update 来完成。在闭包 fun 中,在结束时返回 nil 来提交事务。

根据桶和键名得到数值
func GetValue(bucketName []byte, key []byte) string {
	var result []byte
	if err := db.View(func(tx *bolt.Tx) error {
		//value = tx.Bucket([]byte(bucketName)).Get(key)
		byteLen := len(tx.Bucket([]byte(bucketName)).Get(key))
		result = make([]byte, byteLen)
		copy(result[:], tx.Bucket([]byte(bucketName)).Get(key)[:])
		return nil
	}); err != nil {
		log.Fatal(err)
	}
	return string(result)
}

桶是数据库中键值对的集合。桶中所有的键必须是唯一的。可以使用 CreateBucket 来创建桶。

数据库的建立

main.go 即为数据库的建立,通过官方的 api,循环向 swapi 网站发送 get 请求获取数据,将数据存进数据库相应的 bucket 中的 key。具体实现如下:

package main

import (
	"encoding/json"

	"log"
	"strconv"

	"github.com/WebDevelopingHW12/server/database/database"
	"github.com/WebDevelopingHW12/server/database/swapi"
)

var dbName = "./database/test.db"

func main() {
	reSetDB := false
	if reSetDB {
		// initialize the database for the first time 
		// if it is already exist , do not initialize it again
		/*database.Init(dbName)*/
		database.Start(dbName)
		reSetDatabase()
	}else{
		database.Start(dbName)
		database.Stop()
	}
}

func reSetDatabase() {
	findFilm()
	findPerson()
	findPlanet()
	findSpecies()
	findVehicle()
	findStarship()
}

func findFilm() {
	c := swapi.DefaultClient
	invalidTime := 0
	for index := 1; ; index++ {
		jsonStr := dump(c.Film(index))
		indexStr := strconv.Itoa(index)
		if len(database.GetValue([]byte("films"), []byte(indexStr))) == 0 {
			if !putIntoDb([]byte("films"), []byte(indexStr), jsonStr) {
				invalidTime ++
				if invalidTime == 10{
					break
				}
			}else{
				invalidTime = 0
			}
		}else{
			log.Printf("films/%d is already exit", index)
		}
	}
}

func findPerson() {
	c := swapi.DefaultClient
	invalidTime := 0
	for index := 1; ; index++ {
		jsonStr := dump(c.Person(index))
		indexStr := strconv.Itoa(index)
		if len(database.GetValue([]byte("people"), []byte(indexStr))) == 0 {
			if !putIntoDb([]byte("people"), []byte(indexStr), jsonStr) {
				invalidTime ++
				if invalidTime == 10{
					break
				}
			}else{
				invalidTime = 0
			}
		}else{
			log.Printf("person/%d is already exit", index)
		}
	}
}

func findPlanet() {
	c := swapi.DefaultClient
	invalidTime := 0
	for index := 1; ; index++ {
		jsonStr := dump(c.Planet(index))
		indexStr := strconv.Itoa(index)
		if len(database.GetValue([]byte("planets"), []byte(indexStr))) == 0 {
			if !putIntoDb([]byte("planets"), []byte(indexStr), jsonStr) {
				invalidTime ++
				if invalidTime == 10{
					break
				}
			}else{
				invalidTime = 0
			}
		}else{
			log.Printf("planets/%d is already exit", index)
		}
	}
}

func findSpecies() {
	c := swapi.DefaultClient
	invalidTime := 0
	for index := 1; ; index++ {
		jsonStr := dump(c.Species(index))
		indexStr := strconv.Itoa(index)
		if len(database.GetValue([]byte("species"), []byte(indexStr))) == 0 {
			if !putIntoDb([]byte("species"), []byte(indexStr), jsonStr) {
				invalidTime ++
				if invalidTime == 10{
					break
				}
			}else{
				invalidTime = 0
			}
		}else{
			log.Printf("species/%d is already exit", index)
		}
	}
}

func findStarship() {
	c := swapi.DefaultClient
	invalidTime := 0
	for index := 1; ; index++ {
		jsonStr := dump(c.Starship(index))
		indexStr := strconv.Itoa(index)
		if len(database.GetValue([]byte("starships"), []byte(indexStr))) == 0 {
			if !putIntoDb([]byte("starships"), []byte(indexStr), jsonStr) {
				invalidTime ++
				if invalidTime == 10{
					break
				}
			}else{
				invalidTime = 0
			}
		}else{
			log.Printf("starships/%d is already exit", index)
		}
	}
}

func findVehicle() {
	c := swapi.DefaultClient
	invalidTime := 0
	for index := 1; ; index++ {
		jsonStr := dump(c.Vehicle(index))
		indexStr := strconv.Itoa(index)
		if len(database.GetValue([]byte("vehicles"), []byte(indexStr))) == 0 {
			if !putIntoDb([]byte("vehicles"), []byte(indexStr), jsonStr) {
				invalidTime ++
				if invalidTime == 10{
					break
				}
			}else{
				invalidTime = 0
			}
		}else{
			log.Printf("vehicles/%d is already exit", index)
		}
	}
}

func dump(data interface{}, err error) []byte {
	jsonStr, err := json.MarshalIndent(data, "", "  ")
	return jsonStr
}

func putIntoDb(bucketName []byte, index []byte, jsonStr []byte) bool {
	stb := &swapi.Film{}
	err := json.Unmarshal(jsonStr, &stb)

	if err != nil {
		log.Fatal(err)
		return false
	} else if len(stb.URL) == 0 {
		log.Printf("%s/%s is invalid", bucketName, index)
		return false
	}
	log.Printf("solve %s/%s", bucketName, index)
	database.Update(bucketName, index, jsonStr)
	return true

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值