mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2025-10-21 10:07:24 +08:00
migrated multi host connection pool from godropbox package
removing unneeded dependencies, which involved etcd versions.
This commit is contained in:
159
weed/wdclient/net2/base_connection_pool.go
Normal file
159
weed/wdclient/net2/base_connection_pool.go
Normal file
@@ -0,0 +1,159 @@
|
||||
package net2
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
rp "github.com/chrislusf/seaweedfs/weed/wdclient/resource_pool"
|
||||
)
|
||||
|
||||
const defaultDialTimeout = 1 * time.Second
|
||||
|
||||
func defaultDialFunc(network string, address string) (net.Conn, error) {
|
||||
return net.DialTimeout(network, address, defaultDialTimeout)
|
||||
}
|
||||
|
||||
func parseResourceLocation(resourceLocation string) (
|
||||
network string,
|
||||
address string) {
|
||||
|
||||
idx := strings.Index(resourceLocation, " ")
|
||||
if idx >= 0 {
|
||||
return resourceLocation[:idx], resourceLocation[idx+1:]
|
||||
}
|
||||
|
||||
return "", resourceLocation
|
||||
}
|
||||
|
||||
// A thin wrapper around the underlying resource pool.
|
||||
type connectionPoolImpl struct {
|
||||
options ConnectionOptions
|
||||
|
||||
pool rp.ResourcePool
|
||||
}
|
||||
|
||||
// This returns a connection pool where all connections are connected
|
||||
// to the same (network, address)
|
||||
func newBaseConnectionPool(
|
||||
options ConnectionOptions,
|
||||
createPool func(rp.Options) rp.ResourcePool) ConnectionPool {
|
||||
|
||||
dial := options.Dial
|
||||
if dial == nil {
|
||||
dial = defaultDialFunc
|
||||
}
|
||||
|
||||
openFunc := func(loc string) (interface{}, error) {
|
||||
network, address := parseResourceLocation(loc)
|
||||
return dial(network, address)
|
||||
}
|
||||
|
||||
closeFunc := func(handle interface{}) error {
|
||||
return handle.(net.Conn).Close()
|
||||
}
|
||||
|
||||
poolOptions := rp.Options{
|
||||
MaxActiveHandles: options.MaxActiveConnections,
|
||||
MaxIdleHandles: options.MaxIdleConnections,
|
||||
MaxIdleTime: options.MaxIdleTime,
|
||||
OpenMaxConcurrency: options.DialMaxConcurrency,
|
||||
Open: openFunc,
|
||||
Close: closeFunc,
|
||||
NowFunc: options.NowFunc,
|
||||
}
|
||||
|
||||
return &connectionPoolImpl{
|
||||
options: options,
|
||||
pool: createPool(poolOptions),
|
||||
}
|
||||
}
|
||||
|
||||
// This returns a connection pool where all connections are connected
|
||||
// to the same (network, address)
|
||||
func NewSimpleConnectionPool(options ConnectionOptions) ConnectionPool {
|
||||
return newBaseConnectionPool(options, rp.NewSimpleResourcePool)
|
||||
}
|
||||
|
||||
// This returns a connection pool that manages multiple (network, address)
|
||||
// entries. The connections to each (network, address) entry acts
|
||||
// independently. For example ("tcp", "localhost:11211") could act as memcache
|
||||
// shard 0 and ("tcp", "localhost:11212") could act as memcache shard 1.
|
||||
func NewMultiConnectionPool(options ConnectionOptions) ConnectionPool {
|
||||
return newBaseConnectionPool(
|
||||
options,
|
||||
func(poolOptions rp.Options) rp.ResourcePool {
|
||||
return rp.NewMultiResourcePool(poolOptions, nil)
|
||||
})
|
||||
}
|
||||
|
||||
// See ConnectionPool for documentation.
|
||||
func (p *connectionPoolImpl) NumActive() int32 {
|
||||
return p.pool.NumActive()
|
||||
}
|
||||
|
||||
// See ConnectionPool for documentation.
|
||||
func (p *connectionPoolImpl) ActiveHighWaterMark() int32 {
|
||||
return p.pool.ActiveHighWaterMark()
|
||||
}
|
||||
|
||||
// This returns the number of alive idle connections. This method is not part
|
||||
// of ConnectionPool's API. It is used only for testing.
|
||||
func (p *connectionPoolImpl) NumIdle() int {
|
||||
return p.pool.NumIdle()
|
||||
}
|
||||
|
||||
// BaseConnectionPool can only register a single (network, address) entry.
|
||||
// Register should be call before any Get calls.
|
||||
func (p *connectionPoolImpl) Register(network string, address string) error {
|
||||
return p.pool.Register(network + " " + address)
|
||||
}
|
||||
|
||||
// BaseConnectionPool has nothing to do on Unregister.
|
||||
func (p *connectionPoolImpl) Unregister(network string, address string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *connectionPoolImpl) ListRegistered() []NetworkAddress {
|
||||
result := make([]NetworkAddress, 0, 1)
|
||||
for _, location := range p.pool.ListRegistered() {
|
||||
network, address := parseResourceLocation(location)
|
||||
|
||||
result = append(
|
||||
result,
|
||||
NetworkAddress{
|
||||
Network: network,
|
||||
Address: address,
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// This gets an active connection from the connection pool. Note that network
|
||||
// and address arguments are ignored (The connections with point to the
|
||||
// network/address provided by the first Register call).
|
||||
func (p *connectionPoolImpl) Get(
|
||||
network string,
|
||||
address string) (ManagedConn, error) {
|
||||
|
||||
handle, err := p.pool.Get(network + " " + address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewManagedConn(network, address, handle, p, p.options), nil
|
||||
}
|
||||
|
||||
// See ConnectionPool for documentation.
|
||||
func (p *connectionPoolImpl) Release(conn ManagedConn) error {
|
||||
return conn.ReleaseConnection()
|
||||
}
|
||||
|
||||
// See ConnectionPool for documentation.
|
||||
func (p *connectionPoolImpl) Discard(conn ManagedConn) error {
|
||||
return conn.DiscardConnection()
|
||||
}
|
||||
|
||||
// See ConnectionPool for documentation.
|
||||
func (p *connectionPoolImpl) EnterLameDuckMode() {
|
||||
p.pool.EnterLameDuckMode()
|
||||
}
|
97
weed/wdclient/net2/connection_pool.go
Normal file
97
weed/wdclient/net2/connection_pool.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package net2
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ConnectionOptions struct {
|
||||
// The maximum number of connections that can be active per host at any
|
||||
// given time (A non-positive value indicates the number of connections
|
||||
// is unbounded).
|
||||
MaxActiveConnections int32
|
||||
|
||||
// The maximum number of idle connections per host that are kept alive by
|
||||
// the connection pool.
|
||||
MaxIdleConnections uint32
|
||||
|
||||
// The maximum amount of time an idle connection can alive (if specified).
|
||||
MaxIdleTime *time.Duration
|
||||
|
||||
// This limits the number of concurrent Dial calls (there's no limit when
|
||||
// DialMaxConcurrency is non-positive).
|
||||
DialMaxConcurrency int
|
||||
|
||||
// Dial specifies the dial function for creating network connections.
|
||||
// If Dial is nil, net.DialTimeout is used, with timeout set to 1 second.
|
||||
Dial func(network string, address string) (net.Conn, error)
|
||||
|
||||
// This specifies the now time function. When the function is non-nil, the
|
||||
// connection pool will use the specified function instead of time.Now to
|
||||
// generate the current time.
|
||||
NowFunc func() time.Time
|
||||
|
||||
// This specifies the timeout for any Read() operation.
|
||||
// Note that setting this to 0 (i.e. not setting it) will make
|
||||
// read operations block indefinitely.
|
||||
ReadTimeout time.Duration
|
||||
|
||||
// This specifies the timeout for any Write() operation.
|
||||
// Note that setting this to 0 (i.e. not setting it) will make
|
||||
// write operations block indefinitely.
|
||||
WriteTimeout time.Duration
|
||||
}
|
||||
|
||||
func (o ConnectionOptions) getCurrentTime() time.Time {
|
||||
if o.NowFunc == nil {
|
||||
return time.Now()
|
||||
} else {
|
||||
return o.NowFunc()
|
||||
}
|
||||
}
|
||||
|
||||
// A generic interface for managed connection pool. All connection pool
|
||||
// implementations must be threadsafe.
|
||||
type ConnectionPool interface {
|
||||
// This returns the number of active connections that are on loan.
|
||||
NumActive() int32
|
||||
|
||||
// This returns the highest number of active connections for the entire
|
||||
// lifetime of the pool.
|
||||
ActiveHighWaterMark() int32
|
||||
|
||||
// This returns the number of idle connections that are in the pool.
|
||||
NumIdle() int
|
||||
|
||||
// This associates (network, address) to the connection pool; afterwhich,
|
||||
// the user can get connections to (network, address).
|
||||
Register(network string, address string) error
|
||||
|
||||
// This dissociate (network, address) from the connection pool;
|
||||
// afterwhich, the user can no longer get connections to
|
||||
// (network, address).
|
||||
Unregister(network string, address string) error
|
||||
|
||||
// This returns the list of registered (network, address) entries.
|
||||
ListRegistered() []NetworkAddress
|
||||
|
||||
// This gets an active connection from the connection pool. The connection
|
||||
// will remain active until one of the following is called:
|
||||
// 1. conn.ReleaseConnection()
|
||||
// 2. conn.DiscardConnection()
|
||||
// 3. pool.Release(conn)
|
||||
// 4. pool.Discard(conn)
|
||||
Get(network string, address string) (ManagedConn, error)
|
||||
|
||||
// This releases an active connection back to the connection pool.
|
||||
Release(conn ManagedConn) error
|
||||
|
||||
// This discards an active connection from the connection pool.
|
||||
Discard(conn ManagedConn) error
|
||||
|
||||
// Enter the connection pool into lame duck mode. The connection pool
|
||||
// will no longer return connections, and all idle connections are closed
|
||||
// immediately (including active connections that are released back to the
|
||||
// pool afterward).
|
||||
EnterLameDuckMode()
|
||||
}
|
6
weed/wdclient/net2/doc.go
Normal file
6
weed/wdclient/net2/doc.go
Normal file
@@ -0,0 +1,6 @@
|
||||
// net2 is a collection of functions meant to supplement the capabilities
|
||||
// provided by the standard "net" package.
|
||||
package net2
|
||||
|
||||
// copied from https://github.com/dropbox/godropbox/tree/master/net2
|
||||
// removed other dependencies
|
177
weed/wdclient/net2/ip.go
Normal file
177
weed/wdclient/net2/ip.go
Normal file
@@ -0,0 +1,177 @@
|
||||
package net2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var myHostname string
|
||||
var myHostnameOnce sync.Once
|
||||
|
||||
// Like os.Hostname but caches first successful result, making it cheap to call it
|
||||
// over and over.
|
||||
// It will also crash whole process if fetching Hostname fails!
|
||||
func MyHostname() string {
|
||||
myHostnameOnce.Do(func() {
|
||||
var err error
|
||||
myHostname, err = os.Hostname()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
})
|
||||
return myHostname
|
||||
}
|
||||
|
||||
var myIp4 *net.IPAddr
|
||||
var myIp4Once sync.Once
|
||||
|
||||
// Resolves `MyHostname()` to an Ip4 address. Caches first successful result, making it
|
||||
// cheap to call it over and over.
|
||||
// It will also crash whole process if resolving the IP fails!
|
||||
func MyIp4() *net.IPAddr {
|
||||
myIp4Once.Do(func() {
|
||||
var err error
|
||||
myIp4, err = net.ResolveIPAddr("ip4", MyHostname())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
})
|
||||
return myIp4
|
||||
}
|
||||
|
||||
var myIp6 *net.IPAddr
|
||||
var myIp6Once sync.Once
|
||||
|
||||
// Resolves `MyHostname()` to an Ip6 address. Caches first successful result, making it
|
||||
// cheap to call it over and over.
|
||||
// It will also crash whole process if resolving the IP fails!
|
||||
func MyIp6() *net.IPAddr {
|
||||
myIp6Once.Do(func() {
|
||||
var err error
|
||||
myIp6, err = net.ResolveIPAddr("ip6", MyHostname())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
})
|
||||
return myIp6
|
||||
}
|
||||
|
||||
// This returns the list of local ip addresses which other hosts can connect
|
||||
// to (NOTE: Loopback ip is ignored).
|
||||
// Also resolves Hostname to an address and adds it to the list too, so
|
||||
// IPs from /etc/hosts can work too.
|
||||
func GetLocalIPs() ([]*net.IP, error) {
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to lookup hostname: %v", err)
|
||||
}
|
||||
// Resolves IP Address from Hostname, this way overrides in /etc/hosts
|
||||
// can work too for IP resolution.
|
||||
ipInfo, err := net.ResolveIPAddr("ip4", hostname)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to resolve ip: %v", err)
|
||||
}
|
||||
ips := []*net.IP{&ipInfo.IP}
|
||||
|
||||
// TODO(zviad): Is rest of the code really necessary?
|
||||
addrs, err := net.InterfaceAddrs()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf( "Failed to get interface addresses: %v", err)
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
ipnet, ok := addr.(*net.IPNet)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if ipnet.IP.IsLoopback() {
|
||||
continue
|
||||
}
|
||||
|
||||
ips = append(ips, &ipnet.IP)
|
||||
}
|
||||
return ips, nil
|
||||
}
|
||||
|
||||
var localhostIPNets []*net.IPNet
|
||||
|
||||
func init() {
|
||||
for _, mask := range []string{"127.0.0.1/8", "::1/128"} {
|
||||
_, ipnet, err := net.ParseCIDR(mask)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
localhostIPNets = append(localhostIPNets, ipnet)
|
||||
}
|
||||
}
|
||||
|
||||
func IsLocalhostIp(ipStr string) bool {
|
||||
ip := net.ParseIP(ipStr)
|
||||
if ip == nil {
|
||||
return false
|
||||
}
|
||||
for _, ipnet := range localhostIPNets {
|
||||
if ipnet.Contains(ip) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Given a host string, return true if the host is an ip (v4/v6) localhost.
|
||||
func IsLocalhost(host string) bool {
|
||||
return IsLocalhostIp(host) ||
|
||||
host == "localhost" ||
|
||||
host == "ip6-localhost" ||
|
||||
host == "ipv6-localhost"
|
||||
}
|
||||
|
||||
// Resolves hostnames in addresses to actual IP4 addresses. Skips all invalid addresses
|
||||
// and all addresses that can't be resolved.
|
||||
// `addrs` are assumed to be of form: ["<hostname>:<port>", ...]
|
||||
// Returns an error in addition to resolved addresses if not all resolutions succeed.
|
||||
func ResolveIP4s(addrs []string) ([]string, error) {
|
||||
resolvedAddrs := make([]string, 0, len(addrs))
|
||||
var lastErr error
|
||||
|
||||
for _, server := range addrs {
|
||||
hostPort := strings.Split(server, ":")
|
||||
if len(hostPort) != 2 {
|
||||
lastErr = fmt.Errorf("Skipping invalid address: %s", server)
|
||||
continue
|
||||
}
|
||||
|
||||
ip, err := net.ResolveIPAddr("ip4", hostPort[0])
|
||||
if err != nil {
|
||||
lastErr = err
|
||||
continue
|
||||
}
|
||||
resolvedAddrs = append(resolvedAddrs, ip.IP.String()+":"+hostPort[1])
|
||||
}
|
||||
return resolvedAddrs, lastErr
|
||||
}
|
||||
|
||||
func LookupValidAddrs() (map[string]bool, error) {
|
||||
hostName, err := os.Hostname()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addrs, err := net.LookupHost(hostName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
validAddrs := make(map[string]bool)
|
||||
validAddrs[hostName] = true
|
||||
for _, addr := range addrs {
|
||||
validAddrs[addr] = true
|
||||
}
|
||||
// Special case localhost/127.0.0.1 so that this works on devVMs. It should
|
||||
// have no affect in production.
|
||||
validAddrs["127.0.0.1"] = true
|
||||
validAddrs["localhost"] = true
|
||||
return validAddrs, nil
|
||||
}
|
185
weed/wdclient/net2/managed_connection.go
Normal file
185
weed/wdclient/net2/managed_connection.go
Normal file
@@ -0,0 +1,185 @@
|
||||
package net2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"errors"
|
||||
"github.com/chrislusf/seaweedfs/weed/wdclient/resource_pool"
|
||||
)
|
||||
|
||||
// Dial's arguments.
|
||||
type NetworkAddress struct {
|
||||
Network string
|
||||
Address string
|
||||
}
|
||||
|
||||
// A connection managed by a connection pool. NOTE: SetDeadline,
|
||||
// SetReadDeadline and SetWriteDeadline are disabled for managed connections.
|
||||
// (The deadlines are set by the connection pool).
|
||||
type ManagedConn interface {
|
||||
net.Conn
|
||||
|
||||
// This returns the original (network, address) entry used for creating
|
||||
// the connection.
|
||||
Key() NetworkAddress
|
||||
|
||||
// This returns the underlying net.Conn implementation.
|
||||
RawConn() net.Conn
|
||||
|
||||
// This returns the connection pool which owns this connection.
|
||||
Owner() ConnectionPool
|
||||
|
||||
// This indictes a user is done with the connection and releases the
|
||||
// connection back to the connection pool.
|
||||
ReleaseConnection() error
|
||||
|
||||
// This indicates the connection is an invalid state, and that the
|
||||
// connection should be discarded from the connection pool.
|
||||
DiscardConnection() error
|
||||
}
|
||||
|
||||
// A physical implementation of ManagedConn
|
||||
type managedConnImpl struct {
|
||||
addr NetworkAddress
|
||||
handle resource_pool.ManagedHandle
|
||||
pool ConnectionPool
|
||||
options ConnectionOptions
|
||||
}
|
||||
|
||||
// This creates a managed connection wrapper.
|
||||
func NewManagedConn(
|
||||
network string,
|
||||
address string,
|
||||
handle resource_pool.ManagedHandle,
|
||||
pool ConnectionPool,
|
||||
options ConnectionOptions) ManagedConn {
|
||||
|
||||
addr := NetworkAddress{
|
||||
Network: network,
|
||||
Address: address,
|
||||
}
|
||||
|
||||
return &managedConnImpl{
|
||||
addr: addr,
|
||||
handle: handle,
|
||||
pool: pool,
|
||||
options: options,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *managedConnImpl) rawConn() (net.Conn, error) {
|
||||
h, err := c.handle.Handle()
|
||||
return h.(net.Conn), err
|
||||
}
|
||||
|
||||
// See ManagedConn for documentation.
|
||||
func (c *managedConnImpl) RawConn() net.Conn {
|
||||
h, _ := c.handle.Handle()
|
||||
return h.(net.Conn)
|
||||
}
|
||||
|
||||
// See ManagedConn for documentation.
|
||||
func (c *managedConnImpl) Key() NetworkAddress {
|
||||
return c.addr
|
||||
}
|
||||
|
||||
// See ManagedConn for documentation.
|
||||
func (c *managedConnImpl) Owner() ConnectionPool {
|
||||
return c.pool
|
||||
}
|
||||
|
||||
// See ManagedConn for documentation.
|
||||
func (c *managedConnImpl) ReleaseConnection() error {
|
||||
return c.handle.Release()
|
||||
}
|
||||
|
||||
// See ManagedConn for documentation.
|
||||
func (c *managedConnImpl) DiscardConnection() error {
|
||||
return c.handle.Discard()
|
||||
}
|
||||
|
||||
// See net.Conn for documentation
|
||||
func (c *managedConnImpl) Read(b []byte) (n int, err error) {
|
||||
conn, err := c.rawConn()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if c.options.ReadTimeout > 0 {
|
||||
deadline := c.options.getCurrentTime().Add(c.options.ReadTimeout)
|
||||
_ = conn.SetReadDeadline(deadline)
|
||||
}
|
||||
n, err = conn.Read(b)
|
||||
if err != nil {
|
||||
var localAddr string
|
||||
if conn.LocalAddr() != nil {
|
||||
localAddr = conn.LocalAddr().String()
|
||||
} else {
|
||||
localAddr = "(nil)"
|
||||
}
|
||||
|
||||
var remoteAddr string
|
||||
if conn.RemoteAddr() != nil {
|
||||
remoteAddr = conn.RemoteAddr().String()
|
||||
} else {
|
||||
remoteAddr = "(nil)"
|
||||
}
|
||||
err = fmt.Errorf("Read error from host: %s <-> %s: %v", localAddr, remoteAddr, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// See net.Conn for documentation
|
||||
func (c *managedConnImpl) Write(b []byte) (n int, err error) {
|
||||
conn, err := c.rawConn()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if c.options.WriteTimeout > 0 {
|
||||
deadline := c.options.getCurrentTime().Add(c.options.WriteTimeout)
|
||||
_ = conn.SetWriteDeadline(deadline)
|
||||
}
|
||||
n, err = conn.Write(b)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Write error: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// See net.Conn for documentation
|
||||
func (c *managedConnImpl) Close() error {
|
||||
return c.handle.Discard()
|
||||
}
|
||||
|
||||
// See net.Conn for documentation
|
||||
func (c *managedConnImpl) LocalAddr() net.Addr {
|
||||
conn, _ := c.rawConn()
|
||||
return conn.LocalAddr()
|
||||
}
|
||||
|
||||
// See net.Conn for documentation
|
||||
func (c *managedConnImpl) RemoteAddr() net.Addr {
|
||||
conn, _ := c.rawConn()
|
||||
return conn.RemoteAddr()
|
||||
}
|
||||
|
||||
// SetDeadline is disabled for managed connection (The deadline is set by
|
||||
// us, with respect to the read/write timeouts specified in ConnectionOptions).
|
||||
func (c *managedConnImpl) SetDeadline(t time.Time) error {
|
||||
return errors.New("Cannot set deadline for managed connection")
|
||||
}
|
||||
|
||||
// SetReadDeadline is disabled for managed connection (The deadline is set by
|
||||
// us with respect to the read timeout specified in ConnectionOptions).
|
||||
func (c *managedConnImpl) SetReadDeadline(t time.Time) error {
|
||||
return errors.New("Cannot set read deadline for managed connection")
|
||||
}
|
||||
|
||||
// SetWriteDeadline is disabled for managed connection (The deadline is set by
|
||||
// us with respect to the write timeout specified in ConnectionOptions).
|
||||
func (c *managedConnImpl) SetWriteDeadline(t time.Time) error {
|
||||
return errors.New("Cannot set write deadline for managed connection")
|
||||
}
|
19
weed/wdclient/net2/port.go
Normal file
19
weed/wdclient/net2/port.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package net2
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Returns the port information.
|
||||
func GetPort(addr net.Addr) (int, error) {
|
||||
_, lport, err := net.SplitHostPort(addr.String())
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
lportInt, err := strconv.Atoi(lport)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return lportInt, nil
|
||||
}
|
Reference in New Issue
Block a user