Go 语言中同时有函数和方法?span style="color: #ff0000;">Ҏ是一个包含了接受者(receiverQ的函数Qreceiver可以是内|类型或者结构体cd的一个值或者是一个指针。所有给定类型的Ҏ属于该类型的Ҏ集?br />
如下面的q个例子Q定义了一个新cdIntegerQ它和int一P只是为它内置的intcd增加了个新方法Less()
type Integer int func (a Integer) Less(b Integer) bool { return a < b } func main() { var a Integer = 1 if a.Less(2) { fmt.Println("less then 2") } }
可以看出QGo语言在自定义cd的对象中没有C++/Java那种隐藏的this指针Q而是在定义成员方法时昑ּ声明了其所属的对象?/p>
method的语法如下:
func (r ReceiverType) funcName(parameters) (results)
当调用methodӞ会将receiver作ؓ函数的第一个参敎ͼ
funcName(r, parameters);
所以,receiver是值类型还是指针类型要看method的作用。如果要修改对象的|需要传递对象的指针?/p>
指针作ؓReceiver会对实例对象的内容发生操?而普通类型作为Receiver仅仅是以副本作ؓ操作对象,q不对原实例对象发生操作?/p>
func (a *Ingeger) Add(b Integer) { *a += b } func main() { var a Integer = 1 a.Add(3) fmt.Println("a =", a) // a = 4 }
如果AddҎ不用指针,则aq回的结果不变,q是因ؓGo语言函数的参C是基于g递?/p>
注意Q?span style="color: #ff0000;">当方法的接受者是指针Ӟ即用值类型调用那么方法内部也是对指针的操作?/span>
之前说过QGo语言没有构造函数的概念Q通常使用一个全局函数来完成。例如:
func NewRect(x, y, width, height float64) *Rect { return &Rect{x, y, width, height} } func main() { rect1 := NewRect(1,2,10,20) fmt.Println(rect1.width) }
Go语言提供了承,但是采用了组合的语法Q我们将其称为匿名组合,例如Q?/span>
type Base struct { name string } func (base *Base) Set(myname string) { base.name = myname } func (base *Base) Get() string { return base.name } type Derived struct { Base age int } func (derived *Derived) Get() (nm string, ag int) { return derived.name, derived.age } func main() { b := &Derived{} b.Set("sina") fmt.Println(b.Get()) }
例子中,在Basecd定义了get()和set()两个ҎQ而Derivedcdl承了Basec,q改写了Get()ҎQ在Derived对象调用Set()ҎQ会加蝲基类对应的方法;而调用Get()ҎӞ加蝲zcL写的Ҏ?/p>
l合的类型和被组合的cd包含同名成员Ӟ 会不会有问题呢?可以参考下面的例子Q?/p>
type Base struct { name string age int } func (base *Base) Set(myname string, myage int) { base.name = myname base.age = myage } type Derived struct { Base name string } func main() { b := &Derived{} b.Set("sina", 30) fmt.Println("b.name =",b.name, "\tb.Base.name =", b.Base.name) fmt.Println("b.age =",b.age, "\tb.Base.age =", b.Base.age) }
D义和引用语义的差别在于赋|比如
b = a b.Modify()
如果b的修改不会媄响a的|那么此类型属于值类型;如果会媄响a的|那么此类型是引用cd?/span>
Go语言中的大多数类型都ZD义,包括Q?/p>
C语言中的数组比较特别Q通过函数传递一个数l的时候基于引用语义,但是在结构体定义数组变量的时候基于D义。而在Go语言中,数组和基本类型没有区别,是很Ua的值类型,例如Q?/p>
var a = [3] int{1,2,3} var b = a b[1]++ fmt.Println(a, b) // [1 2 3] [1 3 3]
从结果看Qb=a赋D句是数组内容的完整复Ӟ要想表达引用Q需要用指针Q?/p>
var a = [3] int{1,2,3} var b = &a // 引用语义 b[1]++ fmt.Println(a, b) // [1 3 3] [1 3 3]
Interface 是一l抽象方法(未具体实现的Ҏ/仅包含方法名参数q回值的ҎQ的集合Q如果实C interface 中的所有方法,卌c?对象实C该接口?/p>
Interface 的声明格式:
type interfaceName interface { //Ҏ列表 }
Interface 可以被Q意对象实玎ͼ一个类?对象也可以实现多?interfaceQ?br />interface的变量可以持有Q意实现该interfacecd的对象?/span>
如下面的例子Q?/p>
package main import "fmt" type Human struct { name string age int phone string } type Student struct { Human //匿名字段 school string loan float32 } type Employee struct { Human //匿名字段 company string money float32 } //Human实现SayHiҎ func (h Human) SayHi() { fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone) } //Human实现SingҎ func (h Human) Sing(lyrics string) { fmt.Println("La la la la...", lyrics) } //Employee重蝲Human的SayHiҎ func (e Employee) SayHi() { fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name, e.company, e.phone) } // Interface Men被Human,Student和Employee实现 // 因ؓq三个类型都实现了这两个Ҏ type Men interface { SayHi() Sing(lyrics string) } func main() { mike := Student{Human{"Mike", 25, "222-222-XXX"}, "MIT", 0.00} paul := Student{Human{"Paul", 26, "111-222-XXX"}, "Harvard", 100} sam := Employee{Human{"Sam", 36, "444-222-XXX"}, "Golang Inc.", 1000} tom := Employee{Human{"Tom", 37, "222-444-XXX"}, "Things Ltd.", 5000} //定义Mencd的变量i var i Men //i能存储Student i = mike fmt.Println("This is Mike, a Student:") i.SayHi() i.Sing("November rain") //i也能存储Employee i = tom fmt.Println("This is tom, an Employee:") i.SayHi() i.Sing("Born to be wild") //定义了slice Men fmt.Println("Let's use a slice of Men and see what happens") x := make([]Men, 3) //q三个都是不同类型的元素Q但是他们实Cinterface同一个接?/span> x[0], x[1], x[2] = paul, sam, mike for _, value := range x{ value.SayHi() } }
Iinterface(interface{})不包含Q何的methodQ正因ؓ如此Q?span style="background-color: #ffff00;">所有的cd都实CIinterface。空interface对于描述起不CQ何的作用(因ؓ它不包含M的methodQ,但是Iinterface在我们需要存储Q意类型的数值的时候相当有用,因ؓ它可以存储Q意类型的数倹{它有点cM于C语言的void*cd?/span>
// 定义a为空接口 var a interface{} var i int = 5 s := "Hello world" // a可以存储Lcd的数?/span> a = i a = s
interface的变量里面可以存储Q意类型的数|该类型实CinterfaceQ,那么我们怎么反向知道q个interface变量里面实际保存了的是哪个类型的对象呢?目前常用的有两种ҎQswitch试、Comma-ok断言?/p>
switch试如下Q?/p>
type Element interface{} type List [] Element type Person struct { name string age int } //打印 func (p Person) String() string { return "(name: " + p.name + " - age: "+strconv.Itoa(p.age)+ " years)" } func main() { list := make(List, 3) list[0] = 1 //an int list[1] = "Hello" //a string list[2] = Person{"Dennis", 70} for index, element := range list{ switch value := element.(type) { case int: fmt.Printf("list[%d] is an int and its value is %d\n", index, value) case string: fmt.Printf("list[%d] is a string and its value is %s\n", index, value) case Person: fmt.Printf("list[%d] is a Person and its value is %s\n", index, value) default: fmt.Println("list[%d] is of a different type", index) } } }
如果使用Comma-ok断言的话Q?/p>
func main() { list := make(List, 3) list[0] = 1 // an int list[1] = "Hello" // a string list[2] = Person{"Dennis", 70} for index, element := range list { if value, ok := element.(int); ok { fmt.Printf("list[%d] is an int and its value is %d\n", index, value) } else if value, ok := element.(string); ok { fmt.Printf("list[%d] is a string and its value is %s\n", index, value) } else if value, ok := element.(Person); ok { fmt.Printf("list[%d] is a Person and its value is %s\n", index, value) } else { fmt.Printf("list[%d] is of a different type\n", index) } } }
正如structcd可以包含一个匿名字D,interface也可以嵌套另外一个接口?/p>
如果一个interface1作ؓinterface2的一个嵌入字D,那么interface2隐式的包含了interface1里面的method?/p>
所谓反(reflectQ就是能查程序在q行时的状态?/p>
使用reflect一般分成三步,下面要的讲解一下:要去反射是一个类型的?q些值都实现了空interface)Q首先需要把它{化成reflect对象(reflect.Type或者reflect.ValueQ根据不同的情况调用不同的函?。这两种获取方式如下Q?/p>
t := reflect.TypeOf(i) //得到cd的元数据,通过t我们能获取类型定义里面的所有元?/span> v := reflect.ValueOf(i) //得到实际的|通过v我们获取存储在里面的|q可以去改变?/span>
转化为reflect对象之后我们可以进行一些操作了Q也是reflect对象转化成相应的|例如
tag := t.Elem().Field(0).Tag //获取定义在struct里面的标{?/span> name := v.Elem().Field(0).String() //获取存储在第一个字D里面的?/span>
获取反射Dq回相应的类型和数?/p>
var x float64 = 3.4 v := reflect.ValueOf(x) fmt.Println("type:", v.Type()) fmt.Println("kind is float64:", v.Kind() == reflect.Float64) fmt.Println("value:", v.Float())
最后,反射的话Q那么反的字段必须是可修改的,我们前面学习q传值和传引用,q个里面也是一L道理。反的字段必须是可d的意思是Q如果下面这样写Q那么会发生错误
var x float64 = 3.4 v := reflect.ValueOf(x) v.SetFloat(7.1)
如果要修改相应的|必须q样?/p>
var x float64 = 3.4 p := reflect.ValueOf(&x) v := p.Elem() v.SetFloat(7.1)
上面只是对反的单介l,更深入的理解q需要自己在~程中不断的实践?/p>
参考文档:
http://se77en.cc/2014/05/05/methods-interfaces-and-embedded-types-in-golang/
http://se77en.cc/2014/05/04/choose-whether-to-use-a-value-or-pointer-receiver-on-methods/
http://www.cnblogs.com/chenny7/p/4497969.html
func (p myType ) funcName ( a, b int , c string ) ( r , s int ) { return }
通过函数定义Q我们可以看到Go中函数和其他语言中的共性和Ҏ?/p>
Go中函数的Ҏ是非常LQ给我们带来不一L~程体验?/p>
在Go中通过l函数标明所属类型,来给该类型定义方法,上面?nbsp;p myType
卌C给myType声明了一个方法, p myType
不是必须的。如果没有,则纯_Ҏ一个函敎ͼ通过包名U访问。packageName.funcationName
如:
//定义新的cddoubleQ主要目的是lfloat64cd扩充Ҏ type double float64 //判断a是否{于b func (a double) IsEqual(b double) bool { var r = a - b if r == 0.0 { return true } else if r < 0.0 { return r > -0.0001 } return r < 0.0001 } //判断a是否{于b func IsEqual(a, b float64) bool { var r = a - b if r == 0.0 { return true } else if r < 0.0 { return r > -0.0001 } return r < 0.0001 } func main() { var a double = 1.999999 var b double = 1.9999998 fmt.Println(a.IsEqual(b)) fmt.Println(a.IsEqual(3)) fmt.Println( IsEqual( (float64)(a), (float64)(b) ) ) }
上述CZ?float64 基本cd扩充了方法IsEqualQ该Ҏ主要是解决精度问题?其方法调用方式ؓQ?nbsp;a.IsEqual(double)
Q如果不扩充ҎQ我们只能用函?code style="margin: 0px 2px; padding: 0px 5px; border: 1px solid #eaeaea; background-color: #f8f8f8; border-radius: 3px; white-space: nowrap; font-family: monospace, Monaco;">IsEqual(a, b float64)
?nbsp;func IsEqual(a, b float64) bool
q个Ҏ只保留了一个类型声?此时入参a和b均是float64数据cd?q样也是可以的: func IsEqual(a, b float64, accuracy int) bool
?nbsp;func Sum(args ...int)
参数args是的sliceQ其元素cd为int 。经怋用的fmt.Printf是一个接受Q意个数参数的函数 fmt.Printf(format string, args ...interface{})
前面我们定义函数时返回值有两个r,s 。这是非常有用的Q我在写C#代码Ӟ常常Z从已有函C获得更多的信息,需要修改函数签名,使用out ,ref {方式去获得更多q回l果。而现在用Go时则很简单,直接在返回值后面添加返回参数即可?/p>
?在C#中一个字W串转换为intcd旉辑代码
int v=0; if ( int.TryPase("123456",out v) ) { //code }
而在Go中,则可以这样实?逻辑_而明?/p>
if v,isOk :=int.TryPase("123456") ; isOk { //code }
同时在Go中很多函数充分利用了多返回?/p>
那么如果我只需要某一个返回|而不兛_其他q回值的话,我该如何办呢Q?q时可以单的使用W号下划U?#8221;_“ 来忽略不兛_的返回倹{如Q?/p>
_, cos = math.Sincos(3.1415) //只需要cos计算的?
前面我们说了函数可以有多个返回|q里我还要说的是Q在函数定义时可以给所有的q回值分别命名,q样p在函CL位置l不同返回值复Ӟ而不需要在return语句中才指定q回倹{同时也能增强可L,也提高godoc所生成文档的可L?/p>
如果不支持命名返回|我可能会是这样做?/p>
func ReadFull(r Reader, buf []byte) (int, error) { var n int var err error for len(buf) > 0 { var nr int nr, err = r.Read(buf) n += nr if err !=nil { return n,err } buf = buf[nr:] } return n,err }
但支持给q回值命名后Q实际上是省略了变量的声明Qreturn时无需写成return n,err
而是直接将D?/p>
func ReadFull(r Reader, buf []byte) (n int, err error) { for len(buf) > 0 && err == nil { var nr int nr, err = r.Read(buf) n += nr buf = buf[nr:] } return }
和Go中其他东西一P函数也是|q样可以声明一个函数类型的变量Q将函数作ؓ参数传递?/p>
声明函数为值的变量(匿名函数:可赋g变量Q也可直接执?
//赋?fc := func(msg string) { fmt.Println("you say :", msg) } fmt.Printf("%T \n", fc) fc("hello,my love") //直接执行 func(msg string) { fmt.Println("say :", msg) }("I love to code")
输出l果如下Q这里表明fc 的类型ؓQfunc(string)
func(string) you say : hello,my love say : I love to code
函C为入参(回调函数Q,能带来便利。如日志处理Qؓ了统一处理Q将信息均通过指定函数去记录日志,且是否记录日志还有开?/p>
func Log(title string, getMsg func() string) { //如果开启日志记?则记录日? if true { fmt.Println(title, ":", getMsg()) } } //---------调用-------------- count := 0 msg := func() string { count++ return "您没有即使提醒我,已触犯法? } Log("error", msg) Log("warring", msg) Log("info", msg) fmt.Println(count)
q里输出l果如下Qcount 也发生了变化
error : 您没有即使提醒我,已触犯法?warring : 您没有即使提醒我,已触犯法?info : 您没有即使提醒我,已触犯法?3
你有没有注意C面示例中?nbsp;fc := func(msg string)...
Q既然匿名函数可以赋值给一个变量,同时我们l常q样lint赋?nbsp;value := 2
,是否我们可以声明func(string) cd 呢,当然是可以的?/p>
//一个记录日志的cdQfunc(string) type saveLog func(msg string) //字W串转换为int64,如果转换p|调用saveLog func stringToInt(s string, log saveLog) int64 { if value, err := strconv.ParseInt(s, 0, 0); err != nil { log(err.Error()) return 0 } else { return value } } //记录日志消息的具体实?func myLog(msg string) { fmt.Println("Find Error:", msg) } func main() { stringToInt("123", myLog) //转换时将调用mylog记录日志 stringToInt("s", myLog) }
q里我们定义了一个类型,专门用作记录日志的标准接口。在stringToInt函数中如果{换失败则调用我自己定义的接口函数q行日志处理Q至于最l执行的哪个函数Q则无需兛_?/p>
defer 又是一个创斎ͼ它的作用是:延迟执行Q在声明时不会立x行,而是在函数return后时按照后进先出的原则依ơ执行每一个defer。这样带来的好处是,能确保我们定义的函数能百分之百能够被执行刎ͼq样p做很多我们想做的事,如释放资源,清理数据Q记录日志等
q里我们重点来说明下defer的执行顺?/p>
func deferFunc() int { index := 0 fc := func() { fmt.Println(index, "匿名函数1") index++ defer func() { fmt.Println(index, "匿名函数1-1") index++ }() } defer func() { fmt.Println(index, "匿名函数2") index++ }() defer fc() return func() int { fmt.Println(index, "匿名函数3") index++ return index }() } func main() { deferFunc() }
q里输出l果如下Q?/p>
0 匿名函数3 1 匿名函数1 2 匿名函数1-1 3 匿名函数2
有如下结论:
另外Q我们常使用deferd闭IO,在正常打开文g后,qd明一个deferQ这样就不会忘记关闭文gQ也能保证在出现异常{不可预料的情况下也能关闭文件。而不像其他语aQ?code style="margin: 0px 2px; padding: 0px 5px; border: 1px solid #eaeaea; background-color: #f8f8f8; border-radius: 3px; white-space: nowrap; font-family: monospace, Monaco;">try-catch 或?nbsp;using()
方式q行处理?/p>
file , err :=os.Open(file) if err != nil { return err } defer file.Close() //dosomething with file
后箋Q我讨论: 作用域、传值和传指?以及 保留函数init(),main()
本笔C所写代码存储位|:
以前x的数据存储过E不太懂其中奥妙Q最q遇到跨数据?/a>Q同时对多个表进行CURDQCreate增、Update攏VRead诅RDelete删)Q怎么才能让繁琐的数据CURD同步变得更容易呢Q相信很多h会首先想CMySQL存储q程、触发器Q这U想法确实不错。于是饶有兴地亲自写了CUDQ增、改、删Q触发器的实例,用触发器实现多表数据同步更新?/p>
定义Q?何ؓMySQL触发器?
在MySQL Server里面也就是对某一个表的一定的操作Q触发某U条ӞInsert,Update,Delete {)Q从而自动执行的一D늨序。从q种意义上讲触发器是一个特D的存储q程。下面通过MySQL触发器实例,来了解一下触发器的工作过E吧Q?/p>
一、创建MySQL实例数据表:
在mysql的默认的试test数据库下Q创Z个表t_a与t_bQ?/p>
在t_a表上分创Z个CUDQ增、改、删Q?个触发器Q将t_a的表数据与t_b同步实现CUDQ注意创发器每个表同cM件有且仅有一个对应触发器Qؓ什么只能对一个触发器Q不解释啦,看MYSQL的说明帮助文档吧?/p>
二、创建MySQL实例触发器:
在实例数据表t_a上依ơ按照下面步骤创建tr_a_insert、tr_a_update、tr_a_delete三个触发?/p>
1、创建INSERT触发器trigger_a_insertQ?/strong>
三、测试MySQL实例触发器:
分别试实现t_a与t_b实现数据同步CUD(增、改、删)3个Triggers
1、测试MySQL的实例tr_a_insert触发器:
在t_a表中新增一条数据,然后分别查询t_a/t_b表的数据是否数据同步Q测试触发器成功标志Qt_a表无论在何种情况下,新增了一条或多条记录集时Q没有t_b表做M数据insert操作Q它同时新增了一L多条记录集?/p>
下面来进行MySQL触发器实例测试:
2、测试MySQL的实例tr_a_update、tr_a_delete触发器:
q两个MySQL触发器测试原理、步骤与tr_a_insert触发器一LQ先修改/删除一条数据,然后分别查看t_a、t_b表的数据变化情况Q数据变化同步说明Trigger实例成功Q否则需要逐步排查错误原因?/p>
世界上Q何一U事物都其其优点和缺点,优点与缺Ҏ自n一个相对立的面。当然这里不是强?#8220;世界非黑即白”式的“二元?#8221;Q?#8220;存在卛_?#8221;嘛。当?MySQL触发器的优点不说了,说一下不之处,MySQL Trigger没有很好的调试、管理环境,难于在各U系l环境下试Q测试比MySQL存储q程要难Q所以徏议在生成环境下,量用存储过E来代替 MySQL触发器?/p>
本篇l束前再一下,支持触发器的MySQL版本需?.0以上Q?.0以前版本的MySQL升?.0以后版本方可使用触发器哦Q?/p>
If you prefer command line client, then you can install it on your Linux with the following command.
sudo apt-get install python-pip sudo pip install shadowsocks
Yes, you can use the above commands to install shadowsocks client on ubuntu. But it will install it under ~/.local/bin/ directory and it causes loads of trouble. So I suggest using su to become root first and then issue the following two commands.
apt-get install python-pip pip install shadowsocks
sudo yum install python-setuptools or sudo dnf install python-setuptools sudo easy_install pip sudo pip install shadowsocks
sudo zypper install python-pip sudo pip install shadowsocks
sudo pacman -S python-pip sudo pip install shadowsocks
As you can see the command of installing shadowsocks client is the same to the command of installing shadowsocks server, because the above command will install both the client and the server. You can verify this by looking at the installation script output
Downloading/unpacking shadowsocks Downloading shadowsocks-2.8.2.tar.gz Running setup.py (path:/tmp/pip-build-PQIgUg/shadowsocks/setup.py) egg_info for package shadowsocks Installing collected packages: shadowsocks Running setup.py install for shadowsocks Installing sslocal script to /usr/local/bin Installing ssserver script to /usr/local/bin Successfully installed shadowsocks Cleaning up...
sslocal is the client software and ssserver is the server software. On some Linux distros such as ubuntu, the shadowsocks client sslocal is installed under /usr/local/bin. On Others such as Archsslocal is installed under /usr/bin/. Your can use whereis command to find the exact location.
user@debian:~$ whereis sslocal sslocal: /usr/local/bin/sslocal
we will create a configuration file under /etc/
sudo vi /etc/shadowsocks.json
Put the following text in the file. Replace server-ip with your actual IP and set a password.
{
"server":"server-ip",
"server_port":8000,
"local_address": "127.0.0.1",
"local_port":1080,
"password":"your-password",
"timeout":600,
"method":"aes-256-cfb"
}
Save and close the file. Next start the client using command line
sslocal -c /etc/shadowsocks.json
sudo sslocal -c /etc/shadowsocks.json -d start
Edit /etc/rc.local file
sudo vi /etc/rc.local
Put the following line above the exit 0 line:
sudo sslocal -c /etc/shadowsocks.json -d start
Save and close the file. Next time you start your computer, shadowsocks client will automatically start and connect to your shadowsocks server.
After you rebooted your computer, enter the following command in terminal:
sudo systemctl status rc-local.service
If your sslocal command works then you will get this ouput:
● rc-local.service - /etc/rc.local
Compatibility Loaded: loaded (/etc/systemd/system/rc-local.service; enabled; vendor preset: enabled)
Active: active (running) since Fri 2015-11-27 03:19:25 CST; 2min 39s ago
Process: 881 ExecStart=/etc/rc.local start (code=exited, status=0/SUCCESS)
CGroup: /system.slice/rc-local.service
├─ 887 watch -n 60 su matrix -c ibam
└─1112 /usr/bin/python /usr/local/bin/sslocal -c /etc/shadowsocks....
As you can see from the last line, the sslocal command created a process whose pid is 1112 on my machine. It means shadowsocks client is running smoothly. And of course you can tell your browser to connect through your shadowsocks client to see if everything goes well.
If for some reason your /etc/rc.local script won’t run, then check the following post to find the solution.
If you prefer command line client, then you can install it on your Linux with the following command.
sudo apt-get install python-pip sudo pip install shadowsocks
Yes, you can use the above commands to install shadowsocks client on ubuntu. But it will install it under ~/.local/bin/ directory and it causes loads of trouble. So I suggest using su to become root first and then issue the following two commands.
apt-get install python-pip pip install shadowsocks
sudo yum install python-setuptools or sudo dnf install python-setuptools sudo easy_install pip sudo pip install shadowsocks
sudo zypper install python-pip sudo pip install shadowsocks
sudo pacman -S python-pip sudo pip install shadowsocks
As you can see the command of installing shadowsocks client is the same to the command of installing shadowsocks server, because the above command will install both the client and the server. You can verify this by looking at the installation script output
Downloading/unpacking shadowsocks Downloading shadowsocks-2.8.2.tar.gz Running setup.py (path:/tmp/pip-build-PQIgUg/shadowsocks/setup.py) egg_info for package shadowsocks Installing collected packages: shadowsocks Running setup.py install for shadowsocks Installing sslocal script to /usr/local/bin Installing ssserver script to /usr/local/bin Successfully installed shadowsocks Cleaning up...
sslocal is the client software and ssserver is the server software. On some Linux distros such as ubuntu, the shadowsocks client sslocal is installed under /usr/local/bin. On Others such as Archsslocal is installed under /usr/bin/. Your can use whereis command to find the exact location.
user@debian:~$ whereis sslocal sslocal: /usr/local/bin/sslocal
we will create a configuration file under /etc/
sudo vi /etc/shadowsocks.json
Put the following text in the file. Replace server-ip with your actual IP and set a password.
{ "server":"server-ip", "server_port":8000, "local_address": "127.0.0.1", "local_port":1080, "password":"your-password", "timeout":600, "method":"aes-256-cfb" }
Save and close the file. Next start the client using command line
sslocal -c /etc/shadowsocks.json
sudo sslocal -c /etc/shadowsocks.json -d start
Edit /etc/rc.local file
sudo vi /etc/rc.local
Put the following line above the exit 0 line:
sudo sslocal -c /etc/shadowsocks.json -d start
Save and close the file. Next time you start your computer, shadowsocks client will automatically start and connect to your shadowsocks server.
After you rebooted your computer, enter the following command in terminal:
sudo systemctl status rc-local.service
If your sslocal command works then you will get this ouput:
● rc-local.service - /etc/rc.local Compatibility
Loaded: loaded (/etc/systemd/system/rc-local.service; enabled; vendor preset: enabled)
Active: active (running) since Fri 2015-11-27 03:19:25 CST; 2min 39s ago
Process: 881 ExecStart=/etc/rc.local start (code=exited, status=0/SUCCESS)
CGroup: /system.slice/rc-local.service
├─ 887 watch -n 60 su matrix -c ibam
└─1112 /usr/bin/python /usr/local/bin/sslocal -c /etc/shadowsocks....
As you can see from the last line, the sslocal command created a process whose pid is 1112 on my machine. It means shadowsocks client is running smoothly. And of course you can tell your browser to connect through your shadowsocks client to see if everything goes well.
If for some reason your /etc/rc.local script won’t run, then check the following post to find the solution.
How to enable /etc/rc.local with Systemd
package com.abin.lee.util;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.*;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import javax.net.ssl.*;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.*;
/**
* Created with IntelliJ IDEA.
* User: abin
* Date: 16-4-18
* Time: 上午10:24
* To change this template use File | Settings | File Templates.
*/
public class HttpClientUtil {
private static CloseableHttpClient httpsClient = null;
private static CloseableHttpClient httpClient = null;
static {
httpClient = getHttpClient();
httpsClient = getHttpsClient();
}
public static CloseableHttpClient getHttpClient() {
try {
httpClient = HttpClients.custom()
.setConnectionManager(PoolManager.getHttpPoolInstance())
.setConnectionManagerShared(true)
.setDefaultRequestConfig(requestConfig())
.setRetryHandler(retryHandler())
.build();
} catch (Exception e) {
e.printStackTrace();
}
return httpClient;
}
public static CloseableHttpClient getHttpsClient() {
try {
//Secure Protocol implementation.
SSLContext ctx = SSLContext.getInstance("SSL");
//Implementation of a trust manager for X509 certificates
TrustManager x509TrustManager = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] xcs,
String string) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] xcs,
String string) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
ctx.init(null, new TrustManager[]{x509TrustManager}, null);
//首先讄全局的标准cookie{略
// RequestConfig requestConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD_STRICT).build();
ConnectionSocketFactory connectionSocketFactory = new SSLConnectionSocketFactory(ctx, hostnameVerifier);
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.INSTANCE)
.register("https", connectionSocketFactory).build();
// 讄q接?br /> httpsClient = HttpClients.custom()
.setConnectionManager(PoolsManager.getHttpsPoolInstance(socketFactoryRegistry))
.setConnectionManagerShared(true)
.setDefaultRequestConfig(requestConfig())
.setRetryHandler(retryHandler())
.build();
} catch (Exception e) {
e.printStackTrace();
}
return httpsClient;
}
// 配置h的超时设|?br /> //首先讄全局的标准cookie{略
public static RequestConfig requestConfig(){
RequestConfig requestConfig = RequestConfig.custom()
.setCookieSpec(CookieSpecs.STANDARD_STRICT)
.setConnectionRequestTimeout(20000)
.setConnectTimeout(20000)
.setSocketTimeout(20000)
.build();
return requestConfig;
}
public static HttpRequestRetryHandler retryHandler(){
//h重试处理
HttpRequestRetryHandler httpRequestRetryHandler = new HttpRequestRetryHandler() {
public boolean retryRequest(IOException exception,int executionCount, HttpContext context) {
if (executionCount >= 5) {// 如果已经重试?ơ,放?br /> return false;
}
if (exception instanceof NoHttpResponseException) {// 如果服务器丢掉了q接Q那么就重试
return true;
}
if (exception instanceof SSLHandshakeException) {// 不要重试SSL握手异常
return false;
}
if (exception instanceof InterruptedIOException) {// 时
return false;
}
if (exception instanceof UnknownHostException) {// 目标服务器不可达
return false;
}
if (exception instanceof ConnectTimeoutException) {// q接被拒l?br /> return false;
}
if (exception instanceof SSLException) {// ssl握手异常
return false;
}
HttpClientContext clientContext = HttpClientContext.adapt(context);
HttpRequest request = clientContext.getRequest();
// 如果h是幂{的Q就再次试
if (!(request instanceof HttpEntityEnclosingRequest)) {
return true;
}
return false;
}
};
return httpRequestRetryHandler;
}
//创徏HostnameVerifier
//用于解决javax.net.ssl.SSLException: hostname in certificate didn't match: <123.125.97.66> != <123.125.97.241>
static HostnameVerifier hostnameVerifier = new NoopHostnameVerifier(){
@Override
public boolean verify(String s, SSLSession sslSession) {
return super.verify(s, sslSession);
}
};
public static class PoolManager {
public static PoolingHttpClientConnectionManager clientConnectionManager = null;
private static int maxTotal = 200;
private static int defaultMaxPerRoute = 100;
private PoolManager(){
clientConnectionManager.setMaxTotal(maxTotal);
clientConnectionManager.setDefaultMaxPerRoute(defaultMaxPerRoute);
}
private static class PoolManagerHolder{
public static PoolManager instance = new PoolManager();
}
public static PoolManager getInstance() {
if(null == clientConnectionManager)
clientConnectionManager = new PoolingHttpClientConnectionManager();
return PoolManagerHolder.instance;
}
public static PoolingHttpClientConnectionManager getHttpPoolInstance() {
PoolManager.getInstance();
// System.out.println("getAvailable=" + clientConnectionManager.getTotalStats().getAvailable());
// System.out.println("getLeased=" + clientConnectionManager.getTotalStats().getLeased());
// System.out.println("getMax=" + clientConnectionManager.getTotalStats().getMax());
// System.out.println("getPending="+clientConnectionManager.getTotalStats().getPending());
return PoolManager.clientConnectionManager;
}
}
public static class PoolsManager {
public static PoolingHttpClientConnectionManager clientConnectionManager = null;
private static int maxTotal = 200;
private static int defaultMaxPerRoute = 100;
private PoolsManager(){
clientConnectionManager.setMaxTotal(maxTotal);
clientConnectionManager.setDefaultMaxPerRoute(defaultMaxPerRoute);
}
private static class PoolsManagerHolder{
public static PoolsManager instance = new PoolsManager();
}
public static PoolsManager getInstance(Registry<ConnectionSocketFactory> socketFactoryRegistry) {
if(null == clientConnectionManager)
clientConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
return PoolsManagerHolder.instance;
}
public static PoolingHttpClientConnectionManager getHttpsPoolInstance(Registry<ConnectionSocketFactory> socketFactoryRegistry) {
PoolsManager.getInstance(socketFactoryRegistry);
// System.out.println("getAvailable=" + clientConnectionManager.getTotalStats().getAvailable());
// System.out.println("getLeased=" + clientConnectionManager.getTotalStats().getLeased());
// System.out.println("getMax=" + clientConnectionManager.getTotalStats().getMax());
// System.out.println("getPending="+clientConnectionManager.getTotalStats().getPending());
return PoolsManager.clientConnectionManager;
}
}
public static String httpPost(Map<String, String> request, String httpUrl){
String result = "";
CloseableHttpClient httpClient = getHttpClient();
try {
if(MapUtils.isEmpty(request))
throw new Exception("h参数不能为空");
HttpPost httpPost = new HttpPost(httpUrl);
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
for(Iterator<Map.Entry<String, String>> iterator=request.entrySet().iterator(); iterator.hasNext();){
Map.Entry<String, String> entry = iterator.next();
nvps.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
httpPost.setEntity(new UrlEncodedFormEntity(nvps, Consts.UTF_8));
System.out.println("Executing request: " + httpPost.getRequestLine());
CloseableHttpResponse response = httpClient.execute(httpPost);
result = EntityUtils.toString(response.getEntity());
System.out.println("Executing response: "+ result);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
try {
httpClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
public static String httpPost(String json, String httpUrl, Map<String, String> headers){
String result = "";
CloseableHttpClient httpClient = getHttpClient();
try {
if(StringUtils.isBlank(json))
throw new Exception("h参数不能为空");
HttpPost httpPost = new HttpPost(httpUrl);
for(Iterator<Map.Entry<String, String>> iterator=headers.entrySet().iterator();iterator.hasNext();){
Map.Entry<String, String> entry = iterator.next();
Header header = new BasicHeader(entry.getKey(), entry.getValue());
httpPost.setHeader(header);
}
httpPost.setEntity(new StringEntity(json, Charset.forName("UTF-8")));
System.out.println("Executing request: " + httpPost.getRequestLine());
CloseableHttpResponse response = httpClient.execute(httpPost);
result = EntityUtils.toString(response.getEntity());
System.out.println("Executing response: "+ result);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
try {
httpClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
public static String httpGet(String httpUrl, Map<String, String> headers) {
String result = "";
CloseableHttpClient httpClient = getHttpClient();
try {
HttpGet httpGet = new HttpGet(httpUrl);
System.out.println("Executing request: " + httpGet.getRequestLine());
for(Iterator<Map.Entry<String, String>> iterator=headers.entrySet().iterator();iterator.hasNext();){
Map.Entry<String, String> entry = iterator.next();
Header header = new BasicHeader(entry.getKey(), entry.getValue());
httpGet.setHeader(header);
}
CloseableHttpResponse response = httpClient.execute(httpGet);
result = EntityUtils.toString(response.getEntity());
System.out.println("Executing response: "+ result);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
try {
httpClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
public static String httpGet(String httpUrl) {
String result = "";
CloseableHttpClient httpClient = getHttpClient();
try {
HttpGet httpGet = new HttpGet(httpUrl);
System.out.println("Executing request: " + httpGet.getRequestLine());
CloseableHttpResponse response = httpClient.execute(httpGet);
result = EntityUtils.toString(response.getEntity());
System.out.println("Executing response: "+ result);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
try {
httpClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.1</version>
</dependency>
当我们有大量 Redis ?Memcached 的时候,通常只能通过客户端的一些数据分配算法(比如一致性哈希)Q?/span>来实现集存储的Ҏ?/span>虽然Redis 2.6版本已经发布Redis ClusterQ但q不是很成熟适用正式生环境?span style="color:#333333; font-family:Helvetica,Tahoma,Arial,sans-serif; font-size:14px; line-height:25.200000762939453px"> Redis ?Cluster Ҏq没有正式推Z前,我们通过 Proxy 的方式来实现集群存储?/span>
TwitterQ世界最大的Redis集群之一部v在Twitter用于为用h供时间u数据。Twitter Open Source部门提供了Twemproxy?/span>
Twemproxy,也叫nutcraker。是一个twtter开源的一个redis和memcache代理服务器?redis作ؓ一个高效的~存服务器,非常h应用价倹{但是当使用比较多的时候,希望可以通过某种方式 l一q?/span>行管理。避免每个应用每个客L理q接的松散性。同时在一定程度上变得可以控制?/p> Twemproxy是一个快速的单线E代理程序,支持Memcached ASCII协议和更新的Redis协议Q?/span> 它全部用C写成Q用Apache 2.0 License授权。项目在Linux上可以工作,而在OSX上无法编译,因ؓ它依赖了epoll API. Twemproxy 通过引入一个代理层Q可以将其后端的多台 Redis ?Memcached 实例q行l一理与分配,使应用程序只需要在 Twemproxy 上进行操作,而不用关心后面具体有多少个真实的 Redis ?Memcached 存储?/span> 2?/span>twemproxyҎ: 支持p|节点自动删除 支持讄HashTag 减少与redis的直接连接数 自动分片到后端多个redis实例?/p> 避免单点问题 支持redis pipelining request 支持h的流式与批处理,降低来回的消?/span> 支持状态监?/p> 高吞吐量 另外可以修改redis的源代码Q抽取出redis中的前半部分Q作Z个中间代理层。最l都是通过linux下的epoll 事g机制提高q发效率Q其中nutcraker本n也是使用epoll的事件机制。ƈ且在性能试上的表现非常?/p>
3、twemproxy问题与不?/span>
4、安装与配置
apt-get install libtool
git clone git://github.com/twitter/twemproxy.git
cd twemproxy
autoreconf -fvi
./configure --enable-debug=log
make
src/nutcracker -h
listen: 127.0.0.1:6379 #使用哪个端口启动Twemproxy
redis: true #是否是Redis的proxy
hash: fnv1a_64 #指定具体的hash函数
distribution: ketama #具体的hash法
auto_eject_hosts: true #是否在结Ҏ法响应的时候时摘除结?
timeout: 400 #时旉Q毫U)
server_retry_timeout: 2000 #重试的时_毫秒Q?
server_failure_limit: 1 #l点故障多少ơ就摘除掉
servers: #下面表示所有的Redis节点QIP:端口?权重Q?
- 127.0.0.1:6380:1
- 127.0.0.1:6381:1
- 127.0.0.1:6382:1
redis2:
listen: 0.0.0.0:10000
redis: true
hash: fnv1a_64
distribution: ketama
auto_eject_hosts: false
timeout: 400
servers:
- 127.0.0.1:6379:1
- 127.0.0.1:6380:1
- 127.0.0.1:6381:1
- 127.0.0.1:6382:1
你可以同时开启多?Twemproxy 实例Q它们都可以q行dQ这样你的应用程序就可以完全避免所谓的单点故障?
]]>