一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT)实现算法,设计目标是为了解决因特网中的热点(Hot spot)问题,初衷和CARP十分类似。一致性哈希修正了CARP使用的简单哈希算法带来的问题,使得分布式哈希(DHT)可以在P2P环境中真正得到应用。
一致性hash算法提出了在动态变化的Cache环境中,判定哈希算法好坏的四个定义:
1、平衡性(Balance): 平衡性是指哈希的结果能够尽可能分布到所有的缓冲中去,这样可以使得所有的缓冲空间都得到利用。很多哈希算法都能够满足这一条件。
2、单调性(Monotonicity): 单调性是指如果已经有一些内容通过哈希分派到了相应的缓冲中,又有新的缓冲加入到系统中。哈希的结果应能够保证原有已分配的内容可以被映射到原有的或者新的缓冲中去,而不会被映射到旧的缓冲集合中的其他缓冲区。
3、分散性(Spread): 在分布式环境中,终端有可能看不到所有的缓冲,而是只能看到其中的一部分。当终端希望通过哈希过程将内容映射到缓冲上时,由于不同终端所见的缓冲范围有可能不同,从而导致哈希的结果不一致,最终的结果是相同的内容被不同的终端映射到不同的缓冲区中。这种情况显然是应该避免的,因为它导致相同内容被存储到不同缓冲中去,降低了系统存储的效率。分散性的定义就是上述情况发生的严重程度。好的哈希算法应能够尽量避免不一致的情况发生,也就是尽量降低分散性。
4、负载(Load): 负载问题实际上是从另一个角度看待分散性问题。既然不同的终端可能将相同的内容映射到不同的缓冲区中,那么对于一个特定的缓冲区而言,也可能被不同的用户映射为不同 的内容。与分散性一样,这种情况也是应当避免的,因此好的哈希算法应能够尽量降低缓冲的负荷。
在分布式集群中,对机器的添加删除,或者机器故障后自动脱离集群这些操作是分布式集群管理最基本的功能。如果采用常用的hash(object)%N算法,那么在有机器添加或者删除后,很多原有的数据就无法找到了,这样严重的违反了单调性原则。接下来主要讲解一下一致性哈希算法是如何设计的:
环形Hash空间
按照常用的hash算法来将对应的key哈希到一个具有2^32次方个桶的空间中,即0~(2^32)-1的数字空间中。现在我们可以将这些数字头尾相连,想象成一个闭合的环形。如下图
把数据通过一定的hash算法处理后映射到环上
现在我们将obj
Linux下安装ffmpeg
官网下载:http://ffmpeg.org/download.html
下载之后上传至Linux准备安装,首先解压安装包(注意文件后缀名称通过不同的解压命令解压)
tar -zxvf ffmpeg-4.1.6.tar.gz
cd ffmpeg-4.1.6/
如果现在执行configure配置的话,可能会报如下的错误:
看来是需要安装yasm
Linux下安装yasm
官网下载:http://yasm.tortall.net/Download.html
下载之后上传至Linux准备安装,解压、安装
tar -xvzf yasm-1.3.0.tar.gz
cd yasm-1.3.0/
./configure
make
make install
安装成功之后继续回到ffmpeg解压后的目录,执行下面命令编译并安装
./configure --enable-shared --prefix=/opt/ffmpeg
make:编译过程有点长
make install
make install会把ffmpeg相关执行程序、头文件、lib库安装在/opt/ffmpeg/下
耐心等待完成之后执行
cd /opt/ffmpeg/
进入安装目录,查看一下发现有bin,include,lib,share这4个目录
bin是ffmpeg主程序二进制目录
include是C/C++头文件目录
lib是编译好的库文件目录
share是文档目录
然后进入bin目录,执行
./ffmpeg -version
查看当前版本的详细信息,默认情况下一般会出现错误
我这里出现了ffmpeg编译错误,提示找不到相应的shared libraries :libavdevice.so.58
ffmpeg: error while loading shared libraries: libavdevice.so.58: cannot open shared object file: No such file or directory
查看需要哪些依赖
ldd ffmpeg
先find一下
find /usr -name 'libavdevice.so.58'
没有直接
vi /etc/ld.so.conf
在内容页中添加一行
/opt/ffmpeg/lib
退出执行ldconfig使配置生效
sudo ldconfig
编辑环境变量
vim /e
一、安装
sudo apt-get install mysql-server
1
二、启动服务
注意:先停止windows的mysql服务。
因为子系统与windows共用端口,有可能出现因为端口占用导致服务启动失败。
sudo service mysql start
报错:
* Starting MySQL database server mysqld
No directory, logging in with HOME=/
这个是由于mysql日志输出的目录没有权限导致的。
解决方法:
# 停止mysql服务
sudo service mysql stop
# 修改权限
sudo usermod -d /var/lib/mysql/ mysql
# 重启mysql服务
sudo service mysql start
三、登录
mysql -u root -p
报错:
ERROR 1698 (28000): Access denied for user 'root'@'localhost'
因为安装的过程中没让设置密码,可能密码为空,但无论如何都进不去mysql。
解决方法:
step1:
输入
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
进入到这个配置文件,然后在这个配置文件中的[mysqld]这一块的最后加入
skip-grant-tables
然后保存退出: ctrl+O,回车,再ctrl+X
作用:就是让你可以不用密码登录进去mysql。
再重新启动mysql:
service mysql restart
报错:
su: Authentication failure
解决方法:
先进入到root用户:
su root
然后在重新启动即可。
step2:
在终端上输入
mysql -u root -p
遇见输入密码的提示直接回车即可,进入mysql后,分别执行下面三句话:
use mysql; # 回车
update user set authentication_string=password("你的密码") where user="root"; # 回车
flush privileges; # 回车
然后退出mysql:
quit
step3:
重新进入到mysqld.cnf文件中去把刚开始加的 skip-grant-tables 这条语句给注释掉:
sudo nano /etc/mysql/mysq
对于Go语言(golang)的错误设计,相信很多人已经体验过了,它是通过返回值的方式,来强迫调用者对错误进行处理,要么你忽略,要么你处理(处理也可以是继续返回给调用者),对于golang这种设计方式,我们会在代码中写大量的if判断,以便做出决定。
func main() {
conent,err:=ioutil.ReadFile("filepath")
if err !=nil{
//错误处理
}else {
fmt.Println(string(conent))
}
}
这类代码,在我们编码中是非常的,大部分情况下error都是nil,也就是没有任何错误,但是非nil的时候,意味着错误就出现了,我们需要对他进行处理。
error 接口
error其实一个接口,内置的,我们看下它的定义
// The error built-in interface type is the conventional interface for
// representing an error condition, with the nil value representing no error.
type error interface {
Error() string
}
它只有一个方法 Error,只要实现了这个方法,就是实现了error。现在我们自己定义一个错误试试。
type fileError struct {
}
func (fe *fileError) Error() string {
return "文件错误"
}
自定义 error
自定义了一个fileError类型,实现了error接口。现在测试下看看效果。
func main() {
conent, err := openFile()
if err != nil {
fmt.Println(err)
} else {
fmt.Println(string(conent))
}
}
//只是模拟一个错误
func openFile() ([]byte, error) {
return nil, &fileError{}
}
我们运行模拟的代码,可以看到文件错误的通知。
在实际的使用过程中,我们可能遇到很多错误,他们的区别是错误信息不一样,一种做法是每
error类型是go语言的一种内置类型,使用的时候不用特定去import,他本质上是一个接口
type error interface{
Error() string //Error()是每一个订制的error对象需要填充的错误消息,可以理解成是一个字段Error
}
怎样去理解这个订制呢?
我们知道接口这个东西,必须拥有它的实现块才能调用,放在这里就是说,Error()必须得到填充,才能使用.
比方说下面三种方式:
第一种:通过errors包去订制error
error := errors.New("hello,error")//使用errors必须import "errors"包
if error != nil {
fmt.Print(error)
}
来解释一下errors包,只是一个为Error()填充的简易封装,整个包的内容,只有一个New方法,可以直接看
func New(text string) error
第二种,通过fmt.Errorf()去订制
err := fmt.Errorf("hello error")
if err != nil {
fmt.Print(err)
}
可以说和第一种雷同了.
第三种,就是通过自定义的MyError块去订制了
//一个包裹了错误类型对象的自定义错误类型
type MyError struct {
err error
}
//订制Error()
func (e MyError) Error() string {
return e.err.Error()
}
func main() {
err:=MyError{
errors.New("hello error"),
}
fmt.Println(err.Error())
}
三种方式差异都不大,输出结果都是 hello error
实际上error只是一段错误信息,真正抛出异常并不是单纯靠error,panic和recover的用法以后总结
对于web开发而言,缓存必不可少,也是提高性能最常用的方式。无论是浏览器缓存(如果是chrome浏览器,可以通过chrome:😕/cache查看),还是服务端的缓存(通过memcached或者redis等内存数据库)。缓存不仅可以加速用户的访问,同时也可以降低服务器的负载和压力。那么,了解常见的缓存淘汰算法的策略和原理就显得特别重要。
像浏览器的缓存策略、memcached的缓存策略都是使用LRU这个算法,LRU算法会将近期最不会访问的数据淘汰掉。LRU如此流行的原因是实现比较简单,而且对于实际问题也很实用,良好的运行时性能,命中率较高。下面谈谈如何实现LRU缓存:
LRU Cache具备的操作:
LRU实现采用双向链表 + Map 来进行实现。这里采用双向链表的原因是:如果采用普通的单链表,则删除节点的时候需要从表头开始遍历查找,效率为O(n),采用双向链表可以直接改变节点的前驱的指针指向进行删除达到O(1)的效率。使用Map来保存节点的key、value值便于能在O(logN)的时间查找元素,对应get操作。
双链表节点的定义:
struct CacheNode { int key; // 键 int value;
作者:李骁
Gin是Go语言写的一个web框架,API性能超强,运行速度号称较httprouter要快40x。开源网址:https://github.com/gin-gonic/gin
下载安装gin包:
go get -u github.com/gin-gonic/gin
一个简单的例子:
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.Json(200, gin.H{
"message": "pong",
})
})
r.Run() // listen and serve on 0.0.0.0:8080
}
编译运行程序,打开浏览器,访问 http://localhost:8080/ping
页面显示:
{"message":"pong"}
以Json格式输出了数据。
gin的功能不只是简单输出Json数据。它是一个轻量级的WEB框架,支持RestFull风格API,支持GET,POST,PUT,PATCH,DELETE,OPTIONS 等http方法,支持文件上传,分组路由,Multipart/Urlencoded FORM,以及支持JsonP,参数处理等等功能,这些都和WEB紧密相关,通过提供这些功能,使开发人员更方便地处理WEB业务。
接下来使用Gin作为框架来搭建一个拥有静态资源站点,动态WEB站点,以及RESTFull API接口站点(可专门作为手机APP应用提供服务使用)组成的,亦可根据情况分拆这套系统,每种功能独立出来单独提供服务。
下面按照一套系统但采用分站点来说明,首先是整个系统的目录结构,website目录下面static是资源类文件,为静态资源站点专用;photo目录是UGC上传图片目录,tpl是动态站点的模板。
作者:李骁
go-colly是用Go实现的网络爬虫框架。go-colly快速优雅,在单核上每秒可以发起1K以上请求;以回调函数的形式提供了一组接口,可以实现任意类型的爬虫。
Colly 特性:
清晰的API
快速(单个内核上的请求数大于1k)
管理每个域的请求延迟和最大并发数
自动cookie 和会话处理
同步/异步/并行抓取
高速缓存
自动处理非Unicode的编码
Robots.txt 支持
下面是官方提供的抓取例子:
package main
import (
"fmt"
"github.com/gocolly/colly"
)
func main() {
c := colly.NewCollector()
// Find and visit all links
c.OnHTML("a[href]", func(e *colly.HTMLElement) {
e.Request.Visit(e.Attr("href"))
})
c.OnRequest(func(r *colly.Request) {
fmt.Println("Visiting", r.URL)
})
c.Visit("http://go-colly.org/")
}
程序输出:
Visiting http://go-colly.org/
Visiting http://go-colly.org/docs/
Visiting http://go-colly.org/articles/
Visiting http://go-colly.org/services/
Visiting http://go-colly.org/datasets/
......
Colly大致的使用说明:
在代码中导入包:
import "github.com/gocolly/colly"
colly的主体是Collector对象,管理网络通信和负责在作业运行时执行附加的回掉函数。使用colly需要先初始化Colle
作者:李骁
LevelDB 和 BoltDB 都是k/v数据库。
但LevelDB没有事务,LevelDB实现了一个日志结构化的merge tree。它将有序的key/value存储在不同文件的之中,通过db, _ := leveldb.OpenFile("db", nil),在db目录下有很多数据文件,并通过“层级”把它们分开,并且周期性地将小的文件merge为更大的文件。这让其在随机写的时候会很快,但是读的时候却很慢。
这也让LevelDB的性能不可预知:但数据量很小的时候,它可能性能很好,但是当随着数据量的增加,性能只会越来越糟糕。而且做merge的线程也会在服务器上出现问题。
LSM树而且通过批量存储技术规避磁盘随机写入问题。 LSM树的设计思想非常朴素,它的原理是把一颗大树拆分成N棵小树, 它首先写入到内存中(内存没有寻道速度的问题,随机写的性能得到大幅提升),在内存中构建一颗有序小树,随着小树越来越大,内存的小树会flush到磁盘上。磁盘中的树定期可以做merge操作,合并成一棵大树,以优化读性能。
BoltDB会在数据文件上获得一个文件锁,所以多个进程不能同时打开同一个数据库。BoltDB使用一个单独的内存映射的文件(.db),实现一个写入时拷贝的B+树,这能让读取更快。而且,BoltDB的载入时间很快,特别是在从crash恢复的时候,因为它不需要去通过读log去找到上次成功的事务,它仅仅从两个B+树的根节点读取ID。
BoltDB支持完全可序列化的ACID事务,让应用程序可以更简单的处理复杂操作。
BoltDB设计源于LMDB,具有以下特点:
LMDB的全称是Lightning
作者:李骁
Go 提供了database/sql包用于对SQL数据库的访问,作为操作数据库的入口对象sql.DB,主要为我们提供了两个重要的功能:
需要注意的是,sql.DB表示操作数据库的抽象访问接口, 而非一个数据库连接对象;它可以根据driver打开关闭数据库连接,管理连接池。正在使用的连接被标记为繁忙,用完后回到连接池等待下次使用。所以,如果你没有把连接释放回连接池,会导致过多连接使系统资源耗尽。
导入mysql数据库驱动
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
)
通常来说,不应该直接使用驱动所提供的方法,而是应该使用 sql.DB,因此在导入 mysql 驱动时,这里使用了匿名导入的方式(在包路径前添加 _),当导入了一个数据库驱动后,此驱动会自行初始化并注册自己到Go的database/sql上下文中,因此我们就可以通过 database/sql 包提供的方法访问数据库了。
我们先建立表结构:
CREATE TABLE t_article_cate (
`cid` int(10) NOT NULL AUTO_INCREMENT,
`cname` varchar(60) NOT NULL,
`ename` varchar(100),
`cateimg` varchar(255),
`addtime` int(10) unsigned NOT NULL DEFAULT '0',
`publishtime` int(10) unsigned NOT NULL DEFAULT '0',
`scope` int(10) unsigned NOT NULL DEFAULT '10000',
`status` tinyint(1) unsigned NOT NULL