go 语言中同时有函数和方法。方法就是一个包含了接受者(receiver)的函数,receiver可以是内置类型或者结构体类型的一个值或者是一个指针。所有给定类型的方法属于该类型的方法集。
如下面的这个例子,定义了一个新类型integer,它和int一样,只是为它内置的int类型增加了个新方法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") } }
可以看出,go语言在自定义类型的对象中没有c /java那种隐藏的this指针,而是在定义成员方法时显式声明了其所属的对象。
method的语法如下:
func (r receivertype) funcname(parameters) (results)
当调用method时,会将receiver作为函数的第一个参数:
funcname(r, parameters);
所以,receiver是值类型还是指针类型要看method的作用。如果要修改对象的值,就需要传递对象的指针。
指针作为receiver会对实例对象的内容发生操作,而普通类型作为receiver仅仅是以副本作为操作对象,并不对原实例对象发生操作。
func (a *ingeger) add(b integer) { *a = b } func main() { var a integer = 1 a.add(3) fmt.println("a =", a) // a = 4 }
如果add方法不使用指针,则a返回的结果不变,这是因为go语言函数的参数也是基于值传递。
注意:当方法的接受者是指针时,即使用值类型调用那么方法内部也是对指针的操作。
之前说过,go语言没有构造函数的概念,通常使用一个全局函数来完成。例如:
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语言提供了继承,但是采用了组合的语法,我们将其称为匿名组合,例如:
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()) }
例子中,在base类型定义了get()和set()两个方法,而derived类型继承了base类,并改写了get()方法,在derived对象调用set()方法,会加载基类对应的方法;而调用get()方法时,加载派生类改写的方法。
组合的类型和被组合的类型包含同名成员时, 会不会有问题呢?可以参考下面的例子:
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) }
值语义和引用语义的差别在于赋值,比如
b = a b.modify()
如果b的修改不会影响a的值,那么此类型属于值类型;如果会影响a的值,那么此类型是引用类型。
go语言中的大多数类型都基于值语义,包括:
c语言中的数组比较特别,通过函数传递一个数组的时候基于引用语义,但是在结构体定义数组变量的时候基于值语义。而在go语言中,数组和基本类型没有区别,是很纯粹的值类型,例如:
var a = [3] int{1,2,3} var b = a b[1] fmt.println(a, b) // [1 2 3] [1 3 3]
从结果看,b=a赋值语句是数组内容的完整复制,要想表达引用,需要用指针:
var a = [3] int{1,2,3} var b = &a // 引用语义 b[1] fmt.println(a, b) // [1 3 3] [1 3 3]
interface 是一组抽象方法(未具体实现的方法/仅包含方法名参数返回值的方法)的集合,如果实现了 interface 中的所有方法,即该类/对象就实现了该接口。
interface 的声明格式:
type interfacename interface { //方法列表 }
interface 可以被任意对象实现,一个类型/对象也可以实现多个 interface;
interface的变量可以持有任意实现该interface类型的对象。
如下面的例子:
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实现 // 因为这三个类型都实现了这两个方法 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} //定义men类型的变量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) //这三个都是不同类型的元素,但是他们实现了interface同一个接口 x[0], x[1], x[2] = paul, sam, mike for _, value := range x{ value.sayhi() } }
空interface(interface{})不包含任何的method,正因为如此,所有的类型都实现了空interface。空interface对于描述起不到任何的作用(因为它不包含任何的method),但是空interface在我们需要存储任意类型的数值的时候相当有用,因为它可以存储任意类型的数值。它有点类似于c语言的void*类型。
// 定义a为空接口 var a interface{} var i int = 5 s := "hello world" // a可以存储任意类型的数值 a = i a = s
interface的变量里面可以存储任意类型的数值(该类型实现了interface),那么我们怎么反向知道这个interface变量里面实际保存了的是哪个类型的对象呢?目前常用的有两种方法:switch测试、comma-ok断言。
switch测试如下:
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断言的话:
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) } } }
正如struct类型可以包含一个匿名字段,interface也可以嵌套另外一个接口。
如果一个interface1作为interface2的一个嵌入字段,那么interface2隐式的包含了interface1里面的method。
所谓反射(reflect)就是能检查程序在运行时的状态。
使用reflect一般分成三步,下面简要的讲解一下:要去反射是一个类型的值(这些值都实现了空interface),首先需要把它转化成reflect对象(reflect.type或者reflect.value,根据不同的情况调用不同的函数)。这两种获取方式如下:
t := reflect.typeof(i) //得到类型的元数据,通过t我们能获取类型定义里面的所有元素 v := reflect.valueof(i) //得到实际的值,通过v我们获取存储在里面的值,还可以去改变值
转化为reflect对象之后我们就可以进行一些操作了,也就是将reflect对象转化成相应的值,例如
tag := t.elem().field(0).tag //获取定义在struct里面的标签 name := v.elem().field(0).string() //获取存储在第一个字段里面的值
获取反射值能返回相应的类型和数值
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())
最后,反射的话,那么反射的字段必须是可修改的,我们前面学习过传值和传引用,这个里面也是一样的道理。反射的字段必须是可读写的意思是,如果下面这样写,那么会发生错误
var x float64 = 3.4 v := reflect.valueof(x) v.setfloat(7.1)
如果要修改相应的值,必须这样写
var x float64 = 3.4 p := reflect.valueof(&x) v := p.elem() v.setfloat(7.1)
上面只是对反射的简单介绍,更深入的理解还需要自己在编程中不断的实践。
参考文档:
http://www.cnblogs.com/chenny7/p/4497969.html
func (p mytype ) funcname ( a, b int , c string ) ( r , s int ) { return }
通过函数定义,我们可以看到go中函数和其他语言中的共性和特性
go中函数的特性是非常酷的,给我们带来不一样的编程体验。
在go中通过给函数标明所属类型,来给该类型定义方法,上面的 p mytype
即表示给mytype声明了一个方法, p mytype
不是必须的。如果没有,则纯粹是一个函数,通过包名称访问。packagename.funcationname
如:
//定义新的类型double,主要目的是给float64类型扩充方法 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) ) ) }
上述示例为 float64 基本类型扩充了方法isequal,该方法主要是解决精度问题。 其方法调用方式为: a.isequal(double)
,如果不扩充方法,我们只能使用函数isequal(a, b float64)
如 func isequal(a, b float64) bool
这个方法就只保留了一个类型声明,此时入参a和b均是float64数据类型。 这样也是可以的: func isequal(a, b float64, accuracy int) bool
如 func sum(args ...int)
参数args是的slice,其元素类型为int 。经常使用的fmt.printf就是一个接受任意个数参数的函数 fmt.printf(format string, args ...interface{})
前面我们定义函数时返回值有两个r,s 。这是非常有用的,我在写c#代码时,常常为了从已有函数中获得更多的信息,需要修改函数签名,使用out ,ref 等方式去获得更多返回结果。而现在使用go时则很简单,直接在返回值后面添加返回参数即可。
如,在c#中一个字符串转换为int类型时逻辑代码
int v=0; if ( int.trypase("123456",out v) ) { //code }
而在go中,则可以这样实现,逻辑精简而明确
if v,isok :=int.trypase("123456") ; isok { //code }
同时在go中很多函数充分利用了多返回值
那么如果我只需要某一个返回值,而不关心其他返回值的话,我该如何办呢? 这时可以简单的使用符号下划线”_“ 来忽略不关心的返回值。如:
_, cos = math.sincos(3.1415) //只需要cos计算的值
前面我们说了函数可以有多个返回值,这里我还要说的是,在函数定义时可以给所有的返回值分别命名,这样就能在函数中任意位置给不同返回值复制,而不需要在return语句中才指定返回值。同时也能增强可读性,也提高godoc所生成文档的可读性
如果不支持命名返回值,我可能会是这样做的
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 }
但支持给返回值命名后,实际上就是省略了变量的声明,return时无需写成return n,err
而是将直接将值返回
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中其他东西一样,函数也是值,这样就可以声明一个函数类型的变量,将函数作为参数传递。
声明函数为值的变量(匿名函数:可赋值个变量,也可直接执行)
//赋值 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")
输出结果如下,这里表明fc 的类型为:func(string)
func(string) you say : hello,my love say : i love to code
将函数作为入参(回调函数),能带来便利。如日志处理,为了统一处理,将信息均通过指定函数去记录日志,且是否记录日志还有开关
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)
这里输出结果如下,count 也发生了变化
error : 您没有即使提醒我,已触犯法律 warring : 您没有即使提醒我,已触犯法律 info : 您没有即使提醒我,已触犯法律 3
你有没有注意到上面示例中的 fc := func(msg string)...
,既然匿名函数可以赋值给一个变量,同时我们经常这样给int赋值 value := 2
,是否我们可以声明func(string) 类型 呢,当然是可以的。
//一个记录日志的类型:func(string) type savelog func(msg string) //将字符串转换为int64,如果转换失败调用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) }
这里我们定义了一个类型,专门用作记录日志的标准接口。在stringtoint函数中如果转换失败则调用我自己定义的接口函数进行日志处理,至于最终执行的哪个函数,则无需关心。
defer 又是一个创新,它的作用是:延迟执行,在声明时不会立即执行,而是在函数return后时按照后进先出的原则依次执行每一个defer。这样带来的好处是,能确保我们定义的函数能百分之百能够被执行到,这样就能做很多我们想做的事,如释放资源,清理数据,记录日志等
这里我们重点来说明下defer的执行顺序
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() }
这里输出结果如下,
0 匿名函数3 1 匿名函数1 2 匿名函数1-1 3 匿名函数2
有如下结论:
另外,我们常使用defer去关闭io,在正常打开文件后,就立刻声明一个defer,这样就不会忘记关闭文件,也能保证在出现异常等不可预料的情况下也能关闭文件。而不像其他语言:try-catch
或者 using()
方式进行处理。
file , err :=os.open(file) if err != nil { return err } defer file.close() //dosomething with file
后续,我将讨论: 作用域、传值和传指针 以及 保留函数init(),main()
本笔记中所写代码存储位置:
以前关注的数据存储过程不太懂其中奥妙,最近遇到跨,同时对多个表进行curd(create增、update改、read读、delete删),怎么才能让繁琐的数据curd同步变得更容易呢?相信很多人会首先想到了存储过程、触发器,这种想法确实不错。于是饶有兴趣地亲自写了cud(增、改、删)触发器的实例,用触发器实现多表数据同步更新。
定义: 何为mysql触发器?
在mysql server里面也就是对某一个表的一定的操作,触发某种条件(insert,update,delete 等),从而自动执行的一段程序。从这种意义上讲触发器是一个特殊的存储过程。下面通过mysql触发器实例,来了解一下触发器的工作过程吧!
一、创建mysql实例数据表:
在mysql的默认的测试test数据库下,创建两个表t_a与t_b:
在t_a表上分创建一个cud(增、改、删)3个触发器,将t_a的表数据与t_b同步实现cud,注意创建触发器每个表同类事件有且仅有一个对应触发器,为什么只能对一个触发器,不解释啦,看mysql的说明帮助文档吧。
二、创建mysql实例触发器:
在实例数据表t_a上依次按照下面步骤创建tr_a_insert、tr_a_update、tr_a_delete三个触发器
1、创建insert触发器trigger_a_insert:
三、测试mysql实例触发器:
分别测试实现t_a与t_b实现数据同步cud(增、改、删)3个triggers
1、测试mysql的实例tr_a_insert触发器:
在t_a表中新增一条数据,然后分别查询t_a/t_b表的数据是否数据同步,测试触发器成功标志,t_a表无论在何种情况下,新增了一条或多条记录集时,没有t_b表做任何数据insert操作,它同时新增了一样的多条记录集。
下面来进行mysql触发器实例测试:
2、测试mysql的实例tr_a_update、tr_a_delete触发器:
这两个mysql触发器测试原理、步骤与tr_a_insert触发器一样的,先修改/删除一条数据,然后分别查看t_a、t_b表的数据变化情况,数据变化同步说明trigger实例成功,否则需要逐步排查错误原因。
世界上任何一种事物都其其优点和缺点,优点与缺点是自身一个相对立的面。当然这里不是强调“世界非黑即白”式的“二元论”,“存在即合理”嘛。当然 mysql触发器的优点不说了,说一下不足之处,mysql trigger没有很好的调试、管理环境,难于在各种系统环境下测试,测试比mysql存储过程要难,所以建议在生成环境下,尽量用存储过程来代替 mysql触发器。
本篇结束前再强调一下,支持触发器的mysql版本需要5.0以上,5.0以前版本的mysql升级到5.0以后版本方可使用触发器哦!
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.
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);
registrysocketfactoryregistry = registrybuilder. create()
.register("http", plainconnectionsocketfactory.instance)
.register("https", connectionsocketfactory).build();
// 设置连接池
httpsclient = httpclients.custom()
.setconnectionmanager(poolsmanager.gethttpspoolinstance(socketfactoryregistry))
.setconnectionmanagershared(true)
.setdefaultrequestconfig(requestconfig())
.setretryhandler(retryhandler())
.build();
} catch (exception e) {
e.printstacktrace();
}
return httpsclient;
}
// 配置请求的超时设置
//首先设置全局的标准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(){
//请求重试处理
httprequestretryhandler httprequestretryhandler = new httprequestretryhandler() {
public boolean retryrequest(ioexception exception,int executioncount, httpcontext context) {
if (executioncount >= 5) {// 如果已经重试了5次,就放弃
return false;
}
if (exception instanceof nohttpresponseexception) {// 如果服务器丢掉了连接,那么就重试
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) {// 连接被拒绝
return false;
}
if (exception instanceof sslexception) {// ssl握手异常
return false;
}
httpclientcontext clientcontext = httpclientcontext.adapt(context);
httprequest request = clientcontext.getrequest();
// 如果请求是幂等的,就再次尝试
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(registrysocketfactoryregistry) {
if(null == clientconnectionmanager)
clientconnectionmanager = new poolinghttpclientconnectionmanager(socketfactoryregistry);
return poolsmanagerholder.instance;
}
public static poolinghttpclientconnectionmanager gethttpspoolinstance(registrysocketfactoryregistry) {
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(maprequest, string httpurl){
string result = "";
closeablehttpclient httpclient = gethttpclient();
try {
if(maputils.isempty(request))
throw new exception("请求参数不能为空");
httppost httppost = new httppost(httpurl);
listnvps = new arraylist ();
for(iterator> iterator=request.entryset().iterator(); iterator.hasnext();){
map.entryentry = 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, mapheaders){
string result = "";
closeablehttpclient httpclient = gethttpclient();
try {
if(stringutils.isblank(json))
throw new exception("请求参数不能为空");
httppost httppost = new httppost(httpurl);
for(iterator> iterator=headers.entryset().iterator();iterator.hasnext();){
map.entryentry = 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, mapheaders) {
string result = "";
closeablehttpclient httpclient = gethttpclient();
try {
httpget httpget = new httpget(httpurl);
system.out.println("executing request: " httpget.getrequestline());
for(iterator> iterator=headers.entryset().iterator();iterator.hasnext();){
map.entryentry = 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.commonsgroupid>
<artifactid>commons-collections4artifactid>
<version>4.1version>
dependency>
当我们有大量 redis 或 memcached 的时候,通常只能通过客户端的一些数据分配算法(比如一致性哈希),来实现集群存储的特性。虽然redis 2.6版本已经发布redis cluster,但还不是很成熟适用正式生产环境。 redis 的 cluster 方案还没有正式推出之前,我们通过 proxy 的方式来实现集群存储。
twitter,世界最大的redis集群之一部署在twitter用于为用户提供时间轴数据。twitter open source部门提供了twemproxy。
twemproxy,也叫nutcraker。是一个twtter开源的一个redis和memcache代理服务器。 redis作为一个高效的缓存服务器,非常具有应用价值。但是当使用比较多的时候,就希望可以通过某种方式 统一进行管理。避免每个应用每个客户端管理连接的松散性。同时在一定程度上变得可以控制。
twemproxy是一个快速的单线程代理程序,支持memcached ascii协议和更新的redis协议:
它全部用c写成,使用apache 2.0 license授权。项目在linux上可以工作,而在osx上无法编译,因为它依赖了epoll api.
twemproxy 通过引入一个代理层,可以将其后端的多台 redis 或 memcached 实例进行统一管理与分配,使应用程序只需要在 twemproxy 上进行操作,而不用关心后面具体有多少个真实的 redis 或 memcached 存储。
2、twemproxy特性:
支持失败节点自动删除
支持设置hashtag
减少与redis的直接连接数
自动分片到后端多个redis实例上
避免单点问题
支持redis pipelining request
支持请求的流式与批处理,降低来回的消耗
支持状态监控
高吞吐量
另外可以修改redis的源代码,抽取出redis中的前半部分,作为一个中间代理层。最终都是通过linux下的epoll 事件机制提高并发效率,其中nutcraker本身也是使用epoll的事件机制。并且在性能测试上的表现非常出色。