| 
									
										
										
										
											2013-01-04 21:12:49 +08:00
										 |  |  | // Copyright 2011 Xing Xing <mikespook@gmail.com>.
 | 
					
						
							|  |  |  | // All rights reserved.
 | 
					
						
							|  |  |  | // Use of this source code is governed by a MIT
 | 
					
						
							|  |  |  | // license that can be found in the LICENSE file.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package client | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2013-01-08 17:23:10 +08:00
										 |  |  |     "fmt" | 
					
						
							| 
									
										
										
										
											2013-01-05 15:24:06 +08:00
										 |  |  |     "time" | 
					
						
							|  |  |  |     "errors" | 
					
						
							|  |  |  |     "math/rand" | 
					
						
							|  |  |  |     "github.com/mikespook/gearman-go/common" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  |     PoolSize = 10 | 
					
						
							|  |  |  |     DefaultRetry = 5 | 
					
						
							| 
									
										
										
										
											2013-01-08 17:23:10 +08:00
										 |  |  |     DefaultTimeout = 30 * time.Second | 
					
						
							| 
									
										
										
										
											2013-01-05 15:24:06 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var ( | 
					
						
							|  |  |  |     ErrTooMany = errors.New("Too many errors occurred.") | 
					
						
							| 
									
										
										
										
											2013-01-04 21:12:49 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type poolItem struct { | 
					
						
							| 
									
										
										
										
											2013-01-05 15:24:06 +08:00
										 |  |  |     *Client | 
					
						
							|  |  |  |     Rate int | 
					
						
							|  |  |  |     Addr string | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (item *poolItem) connect(pool *Pool) (err error) { | 
					
						
							| 
									
										
										
										
											2013-01-08 17:23:10 +08:00
										 |  |  |     if item.Client, err = New(item.Addr); err != nil { | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if pool.ErrHandler != nil { | 
					
						
							|  |  |  |         item.ErrHandler = pool.ErrHandler | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2013-01-05 15:24:06 +08:00
										 |  |  |     return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type SelectionHandler func(map[string]*poolItem, string) string | 
					
						
							| 
									
										
										
										
											2013-01-04 21:12:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-05 15:24:06 +08:00
										 |  |  | func SelectWithRate(pool map[string]*poolItem, | 
					
						
							|  |  |  | last string) (addr string) { | 
					
						
							|  |  |  |     total := 0 | 
					
						
							|  |  |  |     for _, item := range pool { | 
					
						
							|  |  |  |         total += item.Rate | 
					
						
							|  |  |  |         if rand.Intn(total) < item.Rate { | 
					
						
							|  |  |  |             return item.Addr | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return last | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func SelectRandom(pool map[string]*poolItem, | 
					
						
							|  |  |  | last string) (addr string) { | 
					
						
							|  |  |  |     r := rand.Intn(len(pool)) | 
					
						
							|  |  |  |     i := 0 | 
					
						
							|  |  |  |     for k, _ := range pool { | 
					
						
							|  |  |  |         if r == i { | 
					
						
							|  |  |  |             return k | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         i ++ | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return last | 
					
						
							| 
									
										
										
										
											2013-01-04 21:12:49 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type Pool struct { | 
					
						
							| 
									
										
										
										
											2013-01-05 15:24:06 +08:00
										 |  |  |     SelectionHandler SelectionHandler | 
					
						
							|  |  |  |     ErrHandler common.ErrorHandler | 
					
						
							|  |  |  |     JobHandler JobHandler | 
					
						
							|  |  |  |     StatusHandler StatusHandler | 
					
						
							|  |  |  |     TimeOut time.Duration | 
					
						
							|  |  |  |     Retry int | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     items map[string]*poolItem | 
					
						
							|  |  |  |     last string | 
					
						
							|  |  |  |     handles map[string]string | 
					
						
							| 
									
										
										
										
											2013-01-04 21:12:49 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-05 15:24:06 +08:00
										 |  |  | // Create a new pool.
 | 
					
						
							| 
									
										
										
										
											2013-01-04 21:12:49 +08:00
										 |  |  | func NewPool() (pool *Pool) { | 
					
						
							| 
									
										
										
										
											2013-01-05 15:24:06 +08:00
										 |  |  |     return &Pool{ | 
					
						
							|  |  |  |         items: make(map[string]*poolItem, PoolSize), | 
					
						
							|  |  |  |         Retry: DefaultRetry, | 
					
						
							|  |  |  |         SelectionHandler: SelectWithRate, | 
					
						
							| 
									
										
										
										
											2013-01-08 17:23:10 +08:00
										 |  |  |         TimeOut: DefaultTimeout, | 
					
						
							| 
									
										
										
										
											2013-01-05 15:24:06 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2013-01-04 21:12:49 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-05 15:24:06 +08:00
										 |  |  | // Add a server with rate.
 | 
					
						
							| 
									
										
										
										
											2013-01-08 17:23:10 +08:00
										 |  |  | func (pool *Pool) Add(addr string, rate int) (err error) { | 
					
						
							| 
									
										
										
										
											2013-01-05 15:24:06 +08:00
										 |  |  |     var item *poolItem | 
					
						
							|  |  |  |     var ok bool | 
					
						
							|  |  |  |     if item, ok = pool.items[addr]; ok { | 
					
						
							|  |  |  |         item.Rate = rate | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         item = &poolItem{Rate: rate, Addr: addr} | 
					
						
							| 
									
										
										
										
											2013-01-08 17:23:10 +08:00
										 |  |  |         if err = item.connect(pool); err != nil { | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2013-01-05 15:24:06 +08:00
										 |  |  |         pool.items[addr] = item | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2013-01-08 17:23:10 +08:00
										 |  |  |     return | 
					
						
							| 
									
										
										
										
											2013-01-04 21:12:49 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-05 15:24:06 +08:00
										 |  |  | func (pool *Pool) Do(funcname string, data []byte, | 
					
						
							| 
									
										
										
										
											2013-01-18 17:03:45 +08:00
										 |  |  | flag byte, h JobHandler) (addr, handle string, err error) { | 
					
						
							|  |  |  |     for i := 0; i < pool.Retry; i ++ { | 
					
						
							|  |  |  |         addr = pool.SelectionHandler(pool.items, pool.last) | 
					
						
							|  |  |  |         item, ok := pool.items[addr] | 
					
						
							|  |  |  |         if ok { | 
					
						
							|  |  |  |             pool.last = addr | 
					
						
							|  |  |  |             handle = item.Do(funcname, data, flag, h) | 
					
						
							|  |  |  |             // error handling
 | 
					
						
							|  |  |  |             // mapping the handle to the server
 | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     err = ErrTooMany | 
					
						
							|  |  |  |     return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (pool *Pool) DoBg(funcname string, data []byte, | 
					
						
							| 
									
										
										
										
											2013-01-05 15:24:06 +08:00
										 |  |  | flag byte) (addr, handle string, err error) { | 
					
						
							|  |  |  |     for i := 0; i < pool.Retry; i ++ { | 
					
						
							|  |  |  |         addr = pool.SelectionHandler(pool.items, pool.last) | 
					
						
							|  |  |  |         item, ok := pool.items[addr] | 
					
						
							|  |  |  |         if ok { | 
					
						
							|  |  |  |             pool.last = addr | 
					
						
							| 
									
										
										
										
											2013-01-18 17:03:45 +08:00
										 |  |  |             handle = item.DoBg(funcname, data, flag) | 
					
						
							| 
									
										
										
										
											2013-01-05 15:24:06 +08:00
										 |  |  |             // error handling
 | 
					
						
							|  |  |  |             // mapping the handle to the server
 | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     err = ErrTooMany | 
					
						
							|  |  |  |     return | 
					
						
							| 
									
										
										
										
											2013-01-04 21:12:49 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-18 17:03:45 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-04 21:12:49 +08:00
										 |  |  | // Get job status from job server.
 | 
					
						
							|  |  |  | // !!!Not fully tested.!!!
 | 
					
						
							| 
									
										
										
										
											2013-01-05 15:24:06 +08:00
										 |  |  | func (pool *Pool) Status(addr, handle string) { | 
					
						
							|  |  |  |     if item, ok := pool.items[addr]; ok { | 
					
						
							|  |  |  |         item.Status(handle) | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2013-01-04 21:12:49 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Send a something out, get the samething back.
 | 
					
						
							|  |  |  | func (pool *Pool) Echo(data []byte) { | 
					
						
							| 
									
										
										
										
											2013-01-05 15:24:06 +08:00
										 |  |  |     for i := 0; i < pool.Retry; i ++ { | 
					
						
							| 
									
										
										
										
											2013-01-08 17:23:10 +08:00
										 |  |  |         addr := pool.SelectionHandler(pool.items, pool.last) | 
					
						
							| 
									
										
										
										
											2013-01-05 15:24:06 +08:00
										 |  |  |         item, ok := pool.items[addr] | 
					
						
							|  |  |  |         if ok { | 
					
						
							|  |  |  |             pool.last = addr | 
					
						
							|  |  |  |             item.Echo(data) | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2013-01-04 21:12:49 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Close
 | 
					
						
							| 
									
										
										
										
											2013-01-08 17:23:10 +08:00
										 |  |  | func (pool *Pool) Close() (err map[string]error) { | 
					
						
							|  |  |  |     err = make(map[string]error) | 
					
						
							| 
									
										
										
										
											2013-01-05 15:24:06 +08:00
										 |  |  |     for _, c := range pool.items { | 
					
						
							| 
									
										
										
										
											2013-01-08 17:23:10 +08:00
										 |  |  |         fmt.Printf("begin") | 
					
						
							|  |  |  |         err[c.Addr] = c.Close() | 
					
						
							|  |  |  |         fmt.Printf("end") | 
					
						
							| 
									
										
										
										
											2013-01-05 15:24:06 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2013-01-08 17:23:10 +08:00
										 |  |  |     fmt.Print("end-for") | 
					
						
							| 
									
										
										
										
											2013-01-05 15:24:06 +08:00
										 |  |  |     return | 
					
						
							| 
									
										
										
										
											2013-01-04 21:12:49 +08:00
										 |  |  | } |