Commit 737b35b0 by 孙龙

up

parents
module github.com/ichunt2019/ichunt-micro-service
go 1.14
require (
github.com/coreos/etcd v3.3.25+incompatible
github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect
github.com/gogo/protobuf v1.3.1 // indirect
github.com/golang/protobuf v1.4.2 // indirect
github.com/google/uuid v1.1.2 // indirect
github.com/imroc/req v0.3.0
github.com/syyongx/php2go v0.9.4
go.uber.org/zap v1.16.0 // indirect
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d // indirect
google.golang.org/grpc v1.31.1 // indirect
)
replace google.golang.org/grpc => google.golang.org/grpc v1.26.0
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
git.apache.org/thrift.git v0.13.0/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/coreos/etcd v0.5.0-alpha.5 h1:0Qi6Jzjk2CDuuGlIeecpu+em2nrjhOgz2wsIwCmQHmc=
github.com/coreos/etcd v3.3.25+incompatible h1:0GQEw6h3YnuOVdtwygkIfJ+Omx0tZ8/QkVyXI4LkbeY=
github.com/coreos/etcd v3.3.25+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/e421083458/gateway_demo v0.0.0-20200517135651-109c687ff0cc h1:SLzbSaIHIV/H+9zR4umJCVNcXlAoBDz2mo1n2oB0cFU=
github.com/e421083458/gateway_demo v0.0.0-20200517135651-109c687ff0cc/go.mod h1:R6mK9Qj78dii2soW5B7QORlKVZbJajTLOfVqx4QOEew=
github.com/e421083458/grpc-proxy v0.2.0/go.mod h1:9/MdR/QZY8COiGUgRUEv7O4QBeRpXEjSPQEd8yuFPzk=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/grpc-gateway v1.14.4/go.mod h1:6CwZWGDSPRJidgKAtJVvND6soZe6fT7iteq8wDPdhb0=
github.com/imroc/req v0.3.0 h1:3EioagmlSG+z+KySToa+Ylo3pTFZs+jh3Brl7ngU12U=
github.com/imroc/req v0.3.0/go.mod h1:F+NZ+2EFSo6EFXdeIbpfE9hcC233id70kf0byW97Caw=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mwitkow/grpc-proxy v0.0.0-20181017164139-0f1106ef9c76/go.mod h1:x5OoJHDHqxHS801UIuhqGl6QdSAEJvtausosHSdazIo=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da h1:p3Vo3i64TCLY7gIfzeQaUJ+kppEO5WQG3cL8iE8tGHU=
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/syyongx/php2go v0.9.4 h1:qUtETTHzqHzxZK8plkbkb0YawD8bpLpxNsbzHQmb22Y=
github.com/syyongx/php2go v0.9.4/go.mod h1:meN2eIhhUoxOd2nMxbpe8g6cFPXI5O9/UAAuz7oDdzw=
go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM=
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20200420144010-e5e8543f8aeb/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d h1:92D1fum1bJLKSdr11OJ+54YeCMCGYIygTA7R/YZxH5M=
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.30.0-dev.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.1 h1:SfXqXS5hkufcdZ/mHtYCh53P2b+92WQq/DZcKLgsFRs=
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
package load_balance
import (
"context"
"fmt"
"github.com/ichunt2019/ichunt-micro-service/registry"
_ "github.com/ichunt2019/ichunt-micro-service/registry"
"sync"
"sync/atomic"
)
type Observer interface {
Update()
}
type LoadBalanceEtcdConf struct {
observers []Observer
value atomic.Value //缓存已经注册的服务节点信息
registry registry.Registry
}
var(
once sync.Once
//实例化对象
LoadBalanceConfig *LoadBalanceEtcdConf
)
func Init(reg registry.Registry) *LoadBalanceEtcdConf{
once.Do(func() {
LoadBalanceConfig = &LoadBalanceEtcdConf{}
rb := LoadBanlanceFactory(LbRoundRobin)
//添加负载均衡器到配置中
LoadBalanceConfig.Attach(rb)
LoadBalanceConfig.registry = reg
allServiceInfo := &registry.AllServiceInfo{
ServiceMap: make(map[string]*registry.Service),
}
//atomic.Value 原子操作 为了防止并发
LoadBalanceConfig.value.Store(allServiceInfo)
})
return LoadBalanceConfig
}
func (s *LoadBalanceEtcdConf) Attach(o Observer) {
s.observers = append(s.observers, o)
}
//更新配置时,通知监听者也更新
func (s *LoadBalanceEtcdConf) UpdateConf(serviceInfo *registry.AllServiceInfo) {
s.value.Store(serviceInfo)
for _, obs := range s.observers {
obs.Update()
}
}
func (s *LoadBalanceEtcdConf) GetLoadBalanceList() *registry.AllServiceInfo{
return s.value.Load().(*registry.AllServiceInfo)
}
func (s *LoadBalanceEtcdConf) GetLoadBalance() LoadBalance{
return s.observers[0].(LoadBalance)
}
func (s *LoadBalanceEtcdConf) GetRegistry() registry.Registry{
return s.registry
}
func (s *LoadBalanceEtcdConf) GetService(ctx context.Context,name string){
regService := s.GetRegistry()
_, err := regService.GetService(context.TODO(),name)
if err != nil {
fmt.Printf("get service failed, err:%v", err)
return
}
node ,err :=s.GetLoadBalance().Get(name)
fmt.Println(node)
//for _, node := range service.Nodes {
// fmt.Printf("服务名:%s, 节点::%#v\n", service.Name, node)
//}
}
package load_balance
import (
"errors"
"github.com/syyongx/php2go"
"hash/crc32"
"sort"
"strconv"
"sync"
)
type Hash func(data []byte) uint32
type UInt32Slice []uint32
func (s UInt32Slice) Len() int {
return len(s)
}
func (s UInt32Slice) Less(i, j int) bool {
return s[i] < s[j]
}
func (s UInt32Slice) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
type ConsistentHashBanlance struct {
mux sync.RWMutex
hash Hash
replicas int //复制因子
keys map[string]UInt32Slice //已排序的节点hash切片
hashMap map[string]map[uint32]string //节点哈希和Key的map,键是hash值,值是节点key
}
func NewConsistentHashBanlance(replicas int, fn Hash) *ConsistentHashBanlance {
m := &ConsistentHashBanlance{
replicas: replicas,
hash: fn,
hashMap: make(map[string]map[uint32]string),
}
if m.hash == nil {
//最多32位,保证是一个2^32-1环
m.hash = crc32.ChecksumIEEE
}
return m
}
// 验证是否为空
func (c *ConsistentHashBanlance) IsEmpty() bool {
return len(c.keys) == 0
}
// Add 方法用来添加缓存节点,参数为节点key,比如使用IP
func (c *ConsistentHashBanlance) Add(serviceName string,params ...string) error {
if len(params) == 0 {
return errors.New("param len 1 at least")
}
addr := params[0]
c.mux.Lock()
defer c.mux.Unlock()
if c.keys == nil{
c.keys = make(map[string]UInt32Slice)
}
// 结合复制因子计算所有虚拟节点的hash值,并存入m.keys中,同时在m.hashMap中保存哈希值和key的映射
for i := 0; i < c.replicas; i++ {
hash := c.hash([]byte(addr+strconv.Itoa(i)))
//c.keys = append(c.keys, hash)
if php2go.InArray(hash,c.keys[serviceName]) == false{
c.keys[serviceName] = append(c.keys[serviceName], hash)
}
if c.hashMap[serviceName] == nil{
c.hashMap[serviceName] = make(map[uint32]string)
c.hashMap[serviceName] = map[uint32]string{hash:addr}
}
c.hashMap[serviceName][hash] = addr
}
// 对所有虚拟节点的哈希值进行排序,方便之后进行二分查找
//sort.Sort(c.keys[serviceName])
//fmt.Println(c.keys)
//fmt.Println(c.hashMap)
return nil
}
// Get 方法根据给定的对象获取最靠近它的那个节点
func (c *ConsistentHashBanlance) Get(key ...string) (string, error) {
if c.IsEmpty() {
return "", errors.New("node is empty")
}
hash := c.hash([]byte(key[1]))
if _,ok := c.keys[key[0]];!ok{
return "", errors.New("node is empty")
}
// 通过二分查找获取最优节点,第一个"服务器hash"值大于"数据hash"值的就是最优"服务器节点"
idx := sort.Search(len(c.keys[key[0]]), func(i int) bool { return c.keys[key[0]][i] >= hash })
// 如果查找结果 大于 服务器节点哈希数组的最大索引,表示此时该对象哈希值位于最后一个节点之后,那么放入第一个节点中
if idx == len(c.keys[key[0]]) {
idx = 0
}
c.mux.RLock()
defer c.mux.RUnlock()
return c.hashMap[key[0]][c.keys[key[0]][idx]], nil
}
func (c *ConsistentHashBanlance) Update() {
}
package load_balance
import (
"fmt"
"testing"
)
func TestNewConsistentHashBanlance(t *testing.T) {
rb := NewConsistentHashBanlance(50, nil)
rb.Add("ichunt/abc","127.0.0.1:2001") //0
rb.Add("ichunt/abc","127.0.0.1:2001") //0
rb.Add("ichunt/abc","127.0.0.1:2001") //0
rb.Add("ichunt/abc","172.156.26.3:2002") //1
rb.Add("ichunt/abc","172.156.26.3:2002") //1
rb.Add("ichunt/abc","172.156.26.3:2002") //1
rb.Add("ichunt/abc","127.0.0.2:2003") //2
rb.Add("ichunt/abc","127.0.0.2:2003") //2
//rb.Add("ichunt/abc","127.0.0.1:2006") //2
//rb.Add("ichunt/abc","127.0.0.1:2007") //2
//url hash
//fmt.Println(rb.Get("http://127.0.0.1:2002/base/getinfo"))
//fmt.Println(rb.Get("http://127.0.0.1:2002/base/getinfo"))
//fmt.Println(rb.Get("http://127.0.0.1:2002/base/getinfo"))
//fmt.Println(rb.Get("http://127.0.0.1:2002/base/changepwd"))
//
////ip hash
fmt.Println(rb.Get("ichunt/abc","http://127.0.0.1:2002/base/getinfo"))
fmt.Println(rb.Get("ichunt/abc","http://127.0.0.1:2002/base/changepwd"))
fmt.Println(rb.Get("ichunt/abc","http://127.0.0.1:2002/base/getinfo"))
fmt.Println(rb.Get("ichunt/abc","http://127.0.0.1:2002/base/changepwd"))
fmt.Println(rb.Get("ichunt/abc","172.156.26.3:2002ggg/abase/agetinfo555"))
fmt.Println(rb.Get("ichunt/abc","172.156.26.3:2002fsdggg/abase/agetinfo555"))
}
\ No newline at end of file
package load_balance
type LbType int
const (
LbRandom LbType = iota
LbRoundRobin
LbWeightRoundRobin
LbConsistentHash
)
func LoadBanlanceFactory(lbType LbType) LoadBalance {
switch lbType {
case LbRandom:
return &RandomBalance{}
case LbConsistentHash:
return NewConsistentHashBanlance(50, nil)
case LbRoundRobin:
return &RoundRobinBalance{}
case LbWeightRoundRobin:
return &WeightRoundRobinBalance{}
default:
return &RandomBalance{}
}
}
package load_balance
type LoadBalance interface {
Add(string,...string) error
Get(...string) (string, error)
//后期服务发现补充
Update()
}
package load_balance
import (
"errors"
"github.com/syyongx/php2go"
"math/rand"
)
type RandomBalance struct {
curIndex map[string]int
rss map[string][]string
}
func (r *RandomBalance) Add(serviceName string,params ...string) error {
if len(params) == 0 {
return errors.New("param len 1 at least")
}
addr := params[0]
if (r.rss == nil){
r.rss = make(map[string][]string,0)
}
if (r.curIndex == nil){
r.curIndex = make(map[string]int,0)
}
if php2go.InArray(addr,r.rss[serviceName]) == false{
r.rss[serviceName] = append(r.rss[serviceName],addr)
}
r.curIndex[serviceName] = 0
return nil
}
func (r *RandomBalance) Next(key string) string {
if r.rss == nil || len(r.rss) == 0 {
return ""
}
if (r.curIndex == nil){
r.curIndex = make(map[string]int,0)
r.curIndex[key] = 0
}
serviceList,ok := r.rss[key]
if !ok{
return ""
}
r.curIndex[key] = rand.Intn(len(serviceList))
return serviceList[r.curIndex[key]]
}
func (r *RandomBalance) Get(key ...string) (string, error) {
return r.Next(key[0]), nil
}
func (r *RandomBalance) Update() {
}
package load_balance
import (
"fmt"
"testing"
"time"
)
func TestRandomBalance(t *testing.T) {
rb := &RandomBalance{}
rb.Add("ichunt/abc","127.0.0.1:2003") //0
rb.Add("ichunt/abc","127.0.0.1:2003") //0
rb.Add("ichunt/abc","127.0.0.1:2003") //0
rb.Add("ichunt/abc","127.0.0.1:2003") //0
rb.Add("ichunt/abc","127.0.0.1:2004") //1
rb.Add("ichunt/abc","127.0.0.1:2004") //1
rb.Add("ichunt/abc","127.0.0.1:2005") //2
rb.Add("ichunt/abc","127.0.0.1:2005") //2
rb.Add("ichunt/abc","127.0.0.1:2005") //2
//
//
//rb.Add("ichunt/123","127.0.0.1:3003") //0
//rb.Add("ichunt/123","127.0.0.1:3004") //1
//rb.Add("ichunt/123","127.0.0.1:3005") //2
//
//rb.Add("ichunt/999","127.0.0.1:4003") //0
//rb.Add("ichunt/999","127.0.0.1:4004") //1
//rb.Add("ichunt/999","127.0.0.1:4005") //2
for{
fmt.Println(rb.Next("ichunt/abc"))
//fmt.Println(rb.Next("ichunt/123"))
//fmt.Println(rb.Next("ichunt/999"))
time.Sleep(time.Second*1)
}
}
package load_balance
import (
"errors"
"fmt"
"sync"
//_ "github.com/ichunt2019/ichunt-micro-service/registry"
"github.com/syyongx/php2go"
"strconv"
)
type RoundRobinBalance struct {
curIndex map[string]int
rss map[string][]string
lock sync.Mutex
}
func (r *RoundRobinBalance) Add(serviceName string,params ...string) error {
if len(params) == 0 {
return errors.New("param len 1 at least")
}
addr := params[0]
if (r.rss == nil){
r.rss = make(map[string][]string,0)
}
if (r.curIndex == nil){
r.curIndex = make(map[string]int,0)
}
if php2go.InArray(addr,r.rss[serviceName]) == false{
r.rss[serviceName] = append(r.rss[serviceName],addr)
r.curIndex[serviceName] = 0
}
return nil
}
func (r *RoundRobinBalance) Next(key string) string {
defer func() {
if r := recover(); r != nil {
fmt.Println("````````````````````")
return
}
}()
if r.rss == nil || len(r.rss) == 0 {
return ""
}
if _,ok := r.rss[key]; !ok{
return ""
}
lens := len(r.rss[key]) //节点的个数
if (r.curIndex == nil){
r.curIndex = make(map[string]int,0)
r.curIndex[key] = 0
}
if _,ok :=r.curIndex[key];!ok{
return ""
}
if r.curIndex[key] >= lens {
r.curIndex[key] = 0
}
php2go.array_
curAddr := r.rss[key][r.curIndex[key]]
r.curIndex[key] = (r.curIndex[key] + 1) % lens
return curAddr
}
func (r *RoundRobinBalance) Get(key ...string) (string, error) {
return r.Next(key[0]), nil
}
/*
更新负载均衡器重缓存服务的节点信息
*/
func (r *RoundRobinBalance) Update() {
r.lock.Lock()
defer r.lock.Unlock()
fmt.Println("更新负载均衡配置.....")
allServiceInfo := LoadBalanceConfig.GetLoadBalanceList()
if allServiceInfo == nil || allServiceInfo.ServiceMap == nil{
return
}
//删除不存在的服务节点信息
for serviceName,_ := range r.rss{
if _,ok := allServiceInfo.ServiceMap[serviceName];!ok{
delete(r.rss,serviceName)
continue
}
//循环etcd中的节点信息
var tmpNodes []string
for _,etcdServiceNode := range allServiceInfo.ServiceMap[serviceName].Nodes{
if etcdServiceNode.IP == ""{
continue
}
if etcdServiceNode.Port == 0 {
tmpNodes = append(tmpNodes,etcdServiceNode.IP)
}else{
tmpNodes = append(tmpNodes,fmt.Sprintf("%s:%s",etcdServiceNode.IP,strconv.Itoa(etcdServiceNode.Port)))
}
}
r.rss[serviceName] = tmpNodes
}
for _,service := range allServiceInfo.ServiceMap{
serviceName := service.Name
nodes := service.Nodes
for _,node :=range nodes{
if node.IP == ""{
continue
}
if node.Port == 0{
r.Add(serviceName,node.IP)
}else{
r.Add(serviceName,fmt.Sprintf("%s:%s",node.IP,strconv.Itoa(node.Port)))
}
}
}
}
package load_balance
import (
"fmt"
"testing"
"time"
)
func Test_main(t *testing.T) {
rb := &RoundRobinBalance{}
rb.Add("ichunt/abc","127.0.0.1:2003") //0
rb.Add("ichunt/abc","127.0.0.1:2003") //0
rb.Add("ichunt/abc","127.0.0.1:2003") //0
rb.Add("ichunt/abc","127.0.0.1:2004") //1
rb.Add("ichunt/abc","127.0.0.1:2004") //1
rb.Add("ichunt/abc","127.0.0.1:2005") //2
rb.Add("ichunt/abc","127.0.0.1:2005") //2
//rb.Add("ichunt/123","127.0.0.1:3003") //0
//rb.Add("ichunt/123","127.0.0.1:3004") //1
//rb.Add("ichunt/123","127.0.0.1:3005") //2
//
//rb.Add("ichunt/999","127.0.0.1:4003") //0
//rb.Add("ichunt/999","127.0.0.1:4004") //1
//rb.Add("ichunt/999","127.0.0.1:4005") //2
for{
fmt.Println(rb.Next("ichunt/abc"))
//fmt.Println(rb.Next("ichunt/123"))
//fmt.Println(rb.Next("ichunt/999"))
time.Sleep(time.Second*1)
}
}
package load_balance
import (
"errors"
"strconv"
)
type WeightRoundRobinBalance struct {
rss map[string][]*WeightNode
}
type WeightNode struct {
addr string
weight int //权重值
currentWeight int //节点当前权重
effectiveWeight int //有效权重
}
func (r *WeightRoundRobinBalance) Add(serviceName string,params ...string) error {
var (
isInsert bool
)
if (r.rss == nil){
r.rss = make(map[string][]*WeightNode,0)
}
if len(params) != 2 {
return errors.New("param len need 2")
}
weight, err := strconv.ParseInt(params[1], 10, 64)
if err != nil {
return err
}
node := &WeightNode{addr: params[0], weight: int(weight)}
node.effectiveWeight = node.weight
isInsert = true
if _,ok := r.rss[serviceName];ok{
for k,v:=range r.rss[serviceName]{
if v.addr == params[0]{
isInsert = false
r.rss[serviceName][k].weight = int(weight)
r.rss[serviceName][k].effectiveWeight = int(weight)
break
}
}
}
if isInsert{
r.rss[serviceName] = append(r.rss[serviceName],node)
}
return nil
}
func (r *WeightRoundRobinBalance) Next(key string) string {
total := 0
var best *WeightNode
if r.rss == nil {
return ""
}
if _,ok:=r.rss[key];!ok{
return ""
}
for i := 0; i < len(r.rss[key]); i++ {
w := r.rss[key][i]
//step 1 统计所有有效权重之和
total += w.effectiveWeight
//step 2 变更节点临时权重为的节点临时权重+节点有效权重
w.currentWeight += w.effectiveWeight
//step 3 有效权重默认与权重相同,通讯异常时-1, 通讯成功+1,直到恢复到weight大小
if w.effectiveWeight < w.weight {
w.effectiveWeight++
}
//step 4 选择最大临时权重点节点
if best == nil || w.currentWeight > best.currentWeight {
best = w
}
}
if best == nil {
return ""
}
//step 5 变更临时权重为 临时权重-有效权重之和
best.currentWeight -= total
return best.addr
}
func (r *WeightRoundRobinBalance) Get(key ...string) (string, error) {
return r.Next(key[0]), nil
}
func (r *WeightRoundRobinBalance) Update() {
//list := Init().GetLoadBalanceList()
//fmt.Println("更新负载均衡配置..start...")
//for _,serviceList := range list.ServiceMap{
// for _,node:=range serviceList.Nodes{
// fmt.Printf("服务名%s,服务列表%s",serviceList.Name,node.IP+":"+strconv.Itoa(node.Port))
// r.Add(serviceList.Name,node.IP+":"+strconv.Itoa(node.Port),strconv.Itoa(node.Weight))
// }
//}
//fmt.Println("更新负载均衡配置..end...")
}
package load_balance
import (
"fmt"
"testing"
"time"
)
func TestLB(t *testing.T) {
rb := &WeightRoundRobinBalance{}
rb.Add("ichunt/abc","127.0.0.1:2003","3") //0
rb.Add("ichunt/abc","127.0.0.1:2004","1") //1
rb.Add("ichunt/abc","127.0.0.1:2005","2") //2
rb.Add("ichunt/123","127.0.0.1:3003","4") //0
rb.Add("ichunt/123","127.0.0.1:3004","3") //1
rb.Add("ichunt/123","127.0.0.1:3005","2") //2
//
//rb.Add("ichunt/999","127.0.0.1:4003","4") //0
//rb.Add("ichunt/999","127.0.0.1:4004","3") //1
//rb.Add("ichunt/999","127.0.0.1:4005","2") //2
for{
//fmt.Println(rb.Get("ichunt/abc"))
rb.Get("ichunt/abc")
fmt.Println(rb.Next("ichunt/123"))
//fmt.Println(rb.Next("ichunt/999"))
time.Sleep(time.Second*1)
}
}
package etcd
import (
"context"
"encoding/json"
"fmt"
"github.com/coreos/etcd/clientv3"
"github.com/ichunt2019/ichunt-micro-service/proxy/load_balance"
"github.com/ichunt2019/ichunt-micro-service/registry"
"path"
"sync"
"sync/atomic"
"time"
)
const (
MaxServiceNum = 8
MaxSyncServiceInterval = time.Second * 5
)
//etcd 注册插件
type EtcdRegistry struct {
options *registry.Options//etcd配置信息
client *clientv3.Client
serviceCh chan *registry.Service//服务注册 -》 服务信息 节点信息 servicename node
value atomic.Value //缓存已经注册的服务节点信息
lock sync.Mutex
registryServiceMap map[string]*RegisterService//需要注册到etcd中的服务节点信息
}
//存放所有服务信息 存入atomic.Value 为了防止并发
//type AllServiceInfo struct {
// serviceMap map[string]*registry.Service //节点信息 servicename node
//}
type RegisterService struct {
id clientv3.LeaseID
service *registry.Service //节点信息 servicename node
registered bool
keepAliveCh <-chan *clientv3.LeaseKeepAliveResponse //etcd返回的续租的信息 false表示续租失败 续租应答
}
var (
//实例化etcd服务
etcdRegistry *EtcdRegistry = &EtcdRegistry{
serviceCh: make(chan *registry.Service, MaxServiceNum),
registryServiceMap: make(map[string]*RegisterService, MaxServiceNum),
}
)
//导入包立即执行函数
func init() {
allServiceInfo := &registry.AllServiceInfo{
ServiceMap: make(map[string]*registry.Service, MaxServiceNum),
}
//atomic.Value 原子操作 为了防止并发
etcdRegistry.value.Store(allServiceInfo)
//注册etcd插件
//map["etcd"] = etcdRegistry
registry.RegisterPlugin(etcdRegistry)
go etcdRegistry.run()
}
//插件的名字
func (e *EtcdRegistry) Name() string {
return "etcd"
}
//初始化
func (e *EtcdRegistry) Init(ctx context.Context, opts ...registry.Option) (err error) {
e.options = &registry.Options{}
for _, opt := range opts {
opt(e.options)
}
e.client, err = clientv3.New(clientv3.Config{
Endpoints: e.options.Addrs,
DialTimeout: e.options.Timeout,
})
if err != nil {
err = fmt.Errorf("init etcd failed, err:%v", err)
return
}
return
}
//服务注册 把服务名称 节点信息放入通道serviceCh
func (e *EtcdRegistry) Register(ctx context.Context, service *registry.Service) (err error) {
select {
case e.serviceCh <- service:
default:
err = fmt.Errorf("register chan is full")
return
}
return
}
//服务反注册
func (e *EtcdRegistry) Unregister(ctx context.Context, service *registry.Service) (err error) {
return
}
func (e *EtcdRegistry) run() {
ticker := time.NewTicker(MaxSyncServiceInterval)
for {
select {
//读取注册进来的节点信息(服务名 ip+端口)
//手动加载过来的服务
case service := <-e.serviceCh:
//读取已经注册的服务
registryService, ok := e.registryServiceMap[service.Name]
if ok {
//更新节点信息
for _, node := range service.Nodes {
registryService.service.Nodes = append(registryService.service.Nodes, node)
}
registryService.registered = false
break
}
//插入节点信息
registryService = &RegisterService{
service: service,
}
//需要注册到etcd中的服务信息
e.registryServiceMap[service.Name] = registryService
case <-ticker.C:
//定时(10秒钟) 更新缓存中的服务信息
//e.value = AllServiceInfo
e.syncServiceFromEtcd()
default:
//注册服务 并 续约(把手动传过的需要注册的服务 put到etcd中 续租 )
//续租应答
e.registerOrKeepAlive()
time.Sleep(time.Millisecond * 500)
}
}
}
/*
服务注册
服务续租
*/
func (e *EtcdRegistry) registerOrKeepAlive() {
//循环注册的节点信息
for _, registryService := range e.registryServiceMap {
//如果是存活的节点 节点续期
if registryService.registered {
//处理续租应答 如果续租失败 registered = false
e.keepAlive(registryService)
continue
}
//服务注册 并永久续约
//设置registered = true
e.registerService(registryService)
}
}
/*
处理续租应答的协程
如果续租失败 registered = false
*/
func (e *EtcdRegistry) keepAlive(registryService *RegisterService) {
select {
case resp := <-registryService.keepAliveCh:
if resp == nil {
//租约失效 去除节点
registryService.registered = false
return
}
}
return
}
func (e *EtcdRegistry) registerService(registryService *RegisterService) (err error) {
resp, err := e.client.Grant(context.TODO(), e.options.HeartBeat)
if err != nil {
return
}
registryService.id = resp.ID
for _, node := range registryService.service.Nodes {
tmp := &registry.Service{
Name: registryService.service.Name,
Nodes: []*registry.Node{
node,
},
}
data, err := json.Marshal(tmp)
if err != nil {
continue
}
key := e.serviceNodePath(tmp)
fmt.Printf("register key:%s\n", key)
_, err = e.client.Put(context.TODO(), key, string(data), clientv3.WithLease(resp.ID))
if err != nil {
continue
}
// 自动续租
//<-ch <-ch==nil 租约失效
ch, err := e.client.KeepAlive(context.TODO(), resp.ID)
if err != nil {
continue
}
registryService.keepAliveCh = ch //续租
registryService.registered = true
}
return
}
func (e *EtcdRegistry) serviceNodePath(service *registry.Service) string {
nodeIP := fmt.Sprintf("%s:%d", service.Nodes[0].IP, service.Nodes[0].Port)
return path.Join(e.options.RegistryPath, service.Name, nodeIP)
}
func (e *EtcdRegistry) servicePath(name string) string {
return path.Join(e.options.RegistryPath, name)
}
func (e *EtcdRegistry) getServiceFromCache(ctx context.Context,
name string) (service *registry.Service, ok bool) {
allServiceInfo := e.value.Load().(*registry.AllServiceInfo)
//一般情况下,都会从缓存中读取
service, ok = allServiceInfo.ServiceMap[name]
return
}
func (e *EtcdRegistry) GetService(ctx context.Context, name string) (service *registry.Service, err error) {
//如果缓存中没有这个service,则从etcd中读取
e.lock.Lock()
defer e.lock.Unlock()
service, ok := e.getServiceFromCache(ctx, name)
if ok {
//fmt.Println("从缓存中获取...")
return
}
//从etcd中读取指定服务名字的服务信息
key := e.servicePath(name)
resp, err := e.client.Get(ctx, key, clientv3.WithPrefix())
if err != nil {
return
}
service = &registry.Service{
Name: name,
}
for _, kv := range resp.Kvs {
value := kv.Value
var tmpService registry.Service
err = json.Unmarshal(value, &tmpService)
if err != nil {
return
}
for _, node := range tmpService.Nodes {
if tmpService.Name != name{
continue
}
service.Nodes = append(service.Nodes, node)
}
}
//从内存中读取所有的服务列表
allServiceInfoOld := e.value.Load().(*registry.AllServiceInfo)
//声明新的缓存服务
var allServiceInfoNew = &registry.AllServiceInfo{
ServiceMap: make(map[string]*registry.Service, MaxServiceNum),
}
//copy旧的服务列表信息到新的变量中
for key, val := range allServiceInfoOld.ServiceMap {
allServiceInfoNew.ServiceMap[key] = val
}
//更新当前需要获取的服务列表
allServiceInfoNew.ServiceMap[name] = service
e.value.Store(allServiceInfoNew)
return
}
/*
读取缓存从etcd中拉取服务节点信息 并更新
*/
func (e *EtcdRegistry) syncServiceFromEtcd() {
//fmt.Println("更新缓存的服务列表...")
var allServiceInfoNew = &registry.AllServiceInfo{
ServiceMap: make(map[string]*registry.Service, MaxServiceNum),
}
ctx := context.TODO()
allServiceInfo := e.value.Load().(*registry.AllServiceInfo)
//对于缓存的每一个服务,都需要从etcd中进行更新
for _, service := range allServiceInfo.ServiceMap {
key := e.servicePath(service.Name)
resp, err := e.client.Get(ctx, key, clientv3.WithPrefix())
if err != nil {
allServiceInfoNew.ServiceMap[service.Name] = service
continue
}
serviceNew := &registry.Service{
Name: service.Name,
}
for _, kv := range resp.Kvs {
value := kv.Value
var tmpService registry.Service
err = json.Unmarshal(value, &tmpService)
if err != nil {
fmt.Printf("unmarshal failed, err:%v value:%s", err, string(value))
return
}
if tmpService.Name != service.Name{
continue
}
for _, node := range tmpService.Nodes {
serviceNew.Nodes = append(serviceNew.Nodes, node)
}
}
allServiceInfoNew.ServiceMap[serviceNew.Name] = serviceNew
}
e.value.Store(allServiceInfoNew)
load_balance.LoadBalanceConfig.UpdateConf(allServiceInfoNew)
}
package etcd
import (
"context"
"fmt"
"testing"
"time"
"github.com/ichunt2019/ichunt-micro-service/registry"
"github.com/ichunt2019/ichunt-micro-service/proxy/load_balance"
)
func TestRegister(t *testing.T) {
//初始化注册中心 注册etcd 服务中心
registryInst, err := registry.InitRegistry(context.TODO(), "etcd",
registry.WithAddrs([]string{"192.168.2.232:2379"}),
registry.WithTimeout(time.Second),
registry.WithRegistryPath("/ichuntMicroService/"),
registry.WithHeartBeat(5),
)
if err != nil {
t.Errorf("init registry failed, err:%v", err)
return
}
load_balance.Init(registryInst)
service := &registry.Service{
Name: "comment_service",
}
service.Nodes = append(service.Nodes, &registry.Node{
IP: "127.0.0.1",
Port: 8801,
},
&registry.Node{
IP: "127.0.0.2",
Port: 8801,
},
)
registryInst.Register(context.TODO(), service)
//for{
// go func() {
// loadBalance.GetService(context.TODO(), "comment_service")
// //loadBalance.GetService(context.TODO(), "comment_service22222")
// }()
// time.Sleep(time.Millisecond*5)
//}
forerver := make(chan struct{})
<-forerver
}
func TestRegister2(t *testing.T) {
//初始化注册中心 注册etcd 服务中心
registryInst, err := registry.InitRegistry(context.TODO(), "etcd",
registry.WithAddrs([]string{"192.168.2.232:2379"}),
registry.WithTimeout(time.Second),
registry.WithRegistryPath("/ichuntMicroService/"),
registry.WithHeartBeat(5),
)
if err != nil {
t.Errorf("init registry failed, err:%v", err)
return
}
load_balance.Init(registryInst)
service := &registry.Service{
Name: "comment_service",
}
service.Nodes = append(service.Nodes, &registry.Node{
IP: "127.0.0.3",
Port: 8801,
},
&registry.Node{
IP: "127.0.0.4",
Port: 8801,
},
)
registryInst.Register(context.TODO(), service)
forerver := make(chan struct{})
<-forerver
}
func TestGetService(t *testing.T){
//初始化注册中心 注册etcd 服务中心
registryInst, err := registry.InitRegistry(context.TODO(), "etcd",
registry.WithAddrs([]string{"192.168.2.232:2379"}),
registry.WithTimeout(time.Second),
registry.WithRegistryPath("/ichuntMicroService/"),
registry.WithHeartBeat(5),
)
if err != nil {
fmt.Printf("init registry failed, err:%v", err)
return
}
loadBalance := load_balance.Init(registryInst)
for{
go func() {
loadBalance.GetService(context.TODO(), "comment_service")
loadBalance.GetService(context.TODO(), "comment_service22222")
}()
//time.Sleep(time.Second*1)
time.Sleep(time.Millisecond*10)
}
}
package registry
import (
"time"
)
type Options struct {
Addrs []string
Timeout time.Duration
// example: /xxx_company/app/kuaishou/service_A/10.192.1.1:8801
// example: /xxx_company/app/kuaishou/service_A/10.192.1.2:8801
RegistryPath string
HeartBeat int64
}
type Option func(opts *Options)
func WithTimeout(timeout time.Duration) Option {
return func(opts *Options) {
opts.Timeout = timeout
}
}
func WithAddrs(addrs []string) Option {
return func(opts *Options) {
opts.Addrs = addrs
}
}
func WithRegistryPath(path string) Option {
return func(opts *Options) {
opts.RegistryPath = path
}
}
func WithHeartBeat(heartHeat int64) Option {
return func(opts *Options) {
opts.HeartBeat = heartHeat
}
}
package registry
import (
"context"
"fmt"
"sync"
)
var (
pluginMgr = &PluginMgr{
plugins: make(map[string]Registry),
}
)
type PluginMgr struct {
plugins map[string]Registry
lock sync.Mutex
}
func (p *PluginMgr) registerPlugin(plugin Registry) (err error) {
p.lock.Lock()
defer p.lock.Unlock()
_, ok := p.plugins[plugin.Name()]
if ok {
err = fmt.Errorf("duplicate registry plugin")
return
}
p.plugins[plugin.Name()] = plugin
return
}
func (p *PluginMgr) initRegistry(ctx context.Context, name string,
opts ...Option) (registry Registry, err error) {
//查找对应的插件是否存在
p.lock.Lock()
defer p.lock.Unlock()
plugin, ok := p.plugins[name]
if !ok {
err = fmt.Errorf("plugin %s not exists", name)
return
}
registry = plugin
err = plugin.Init(ctx, opts...)
return
}
// 注册插件
func RegisterPlugin(registry Registry) (err error) {
return pluginMgr.registerPlugin(registry)
}
// 初始化注册中心
func InitRegistry(ctx context.Context, name string, opts ...Option) (registry Registry, err error) {
return pluginMgr.initRegistry(ctx, name, opts...)
}
package registry
import (
"context"
)
// 服务注册插件的接口
type Registry interface {
//插件的名字
Name() string
//初始化
Init(ctx context.Context, opts ...Option) (err error)
//服务注册
Register(ctx context.Context, service *Service) (err error)
//服务反注册
Unregister(ctx context.Context, service *Service) (err error)
//服务发现:通过服务的名字获取服务的位置信息(ip和port列表)
GetService(ctx context.Context, name string) (service *Service, err error)
}
package registry
// 服务抽象
type Service struct {
Name string `json:"name"`
Nodes []*Node `json:"nodes"`
}
// 服务节点的抽象
type Node struct {
Id string `json:"id"`
IP string `json:"ip"`
Port int `json:"port"`
Weight int `json:"weight"`
}
type AllServiceInfo struct {
ServiceMap map[string]*Service //节点信息 servicename node
}
package main
import (
"bytes"
"github.com/ichunt2019/ichunt-micro-service/proxy/load_balance"
"io/ioutil"
"log"
"net"
"net/http"
"net/http/httputil"
"net/url"
"strconv"
"strings"
"time"
)
var (
addr = "127.0.0.1:2002"
transport = &http.Transport{
DialContext: (&net.Dialer{
Timeout: 30 * time.Second, //连接超时
KeepAlive: 30 * time.Second, //长连接超时时间
}).DialContext,
MaxIdleConns: 100, //最大空闲连接
IdleConnTimeout: 90 * time.Second, //空闲超时时间
TLSHandshakeTimeout: 10 * time.Second, //tls握手超时时间
ExpectContinueTimeout: 1 * time.Second, //100-continue状态码超时时间
}
)
func NewMultipleHostsReverseProxy(lb load_balance.LoadBalance) *httputil.ReverseProxy {
//请求协调者
director := func(req *http.Request) {
nextAddr, err := lb.Get(req.RemoteAddr)
if err != nil {
log.Fatal("get next addr fail")
}
target, err := url.Parse(nextAddr)
if err != nil {
log.Fatal(err)
}
targetQuery := target.RawQuery
req.URL.Scheme = target.Scheme
req.URL.Host = target.Host
req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)
if targetQuery == "" || req.URL.RawQuery == "" {
req.URL.RawQuery = targetQuery + req.URL.RawQuery
} else {
req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
}
if _, ok := req.Header["User-Agent"]; !ok {
req.Header.Set("User-Agent", "user-agent")
}
//只在第一代理中设置此header头
req.Header.Set("X-Real-Ip", req.RemoteAddr)
}
//更改内容
modifyFunc := func(resp *http.Response) error {
//请求以下命令:curl 'http://127.0.0.1:2002/error'
if resp.StatusCode != 200 {
//获取内容
oldPayload, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
//追加内容
newPayload := []byte("StatusCode error:" + string(oldPayload))
resp.Body = ioutil.NopCloser(bytes.NewBuffer(newPayload))
resp.ContentLength = int64(len(newPayload))
resp.Header.Set("Content-Length", strconv.FormatInt(int64(len(newPayload)), 10))
}
return nil
}
//错误回调 :关闭real_server时测试,错误回调
//范围:transport.RoundTrip发生的错误、以及ModifyResponse发生的错误
errFunc := func(w http.ResponseWriter, r *http.Request, err error) {
//todo 如果是权重的负载则调整临时权重
http.Error(w, "ErrorHandler error:"+err.Error(), 500)
}
return &httputil.ReverseProxy{Director: director, Transport: transport, ModifyResponse: modifyFunc, ErrorHandler: errFunc}
}
func singleJoiningSlash(a, b string) string {
aslash := strings.HasSuffix(a, "/")
bslash := strings.HasPrefix(b, "/")
switch {
case aslash && bslash:
return a + b[1:]
case !aslash && !bslash:
return a + "/" + b
}
return a + b
}
func main() {
rb := load_balance.LoadBanlanceFactory(load_balance.LbWeightRoundRobin)
if err := rb.Add("http://127.0.0.1:2003/", "10"); err != nil {
log.Println(err)
}
if err := rb.Add("http://127.0.0.1:2004/", "20"); err != nil {
log.Println(err)
}
proxy := NewMultipleHostsReverseProxy(rb)
log.Println("Starting httpserver at " + addr)
log.Fatal(http.ListenAndServe(addr, proxy))
}
package main
import (
"bytes"
"io/ioutil"
"log"
"math/rand"
"net"
"net/http"
"net/http/httputil"
"net/url"
"regexp"
"strconv"
"strings"
"time"
)
var addr = "127.0.0.1:2001"
func main() {
rs1 := "http://127.0.0.1:2002"
url1, err1 := url.Parse(rs1)
if err1 != nil {
log.Println(err1)
}
urls := []*url.URL{url1}
proxy := NewMultipleHostsReverseProxy(urls)
log.Println("Starting httpserver at " + addr)
log.Fatal(http.ListenAndServe(addr, proxy))
}
var transport = &http.Transport{
DialContext: (&net.Dialer{
Timeout: 30 * time.Second, //连接超时
KeepAlive: 30 * time.Second, //长连接超时时间
}).DialContext,
MaxIdleConns: 100, //最大空闲连接
IdleConnTimeout: 90 * time.Second, //空闲超时时间
TLSHandshakeTimeout: 10 * time.Second, //tls握手超时时间
ExpectContinueTimeout: 1 * time.Second, //100-continue 超时时间
}
func NewMultipleHostsReverseProxy(targets []*url.URL) *httputil.ReverseProxy {
//请求协调者
director := func(req *http.Request) {
//url_rewrite
//127.0.0.1:2002/dir/abc ==> 127.0.0.1:2003/base/abc ??
//127.0.0.1:2002/dir/abc ==> 127.0.0.1:2002/abc
//127.0.0.1:2002/abc ==> 127.0.0.1:2003/base/abc
re, _ := regexp.Compile("^/dir(.*)");
req.URL.Path = re.ReplaceAllString(req.URL.Path, "$1")
//随机负载均衡
targetIndex := rand.Intn(len(targets))
target := targets[targetIndex]
targetQuery := target.RawQuery
req.URL.Scheme = target.Scheme
req.URL.Host = target.Host
// url地址重写:重写前:/aa 重写后:/base/aa
req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)
if targetQuery == "" || req.URL.RawQuery == "" {
req.URL.RawQuery = targetQuery + req.URL.RawQuery
} else {
req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
}
if _, ok := req.Header["User-Agent"]; !ok {
req.Header.Set("User-Agent", "user-agent")
}
//只在第一代理中设置此header头
req.Header.Set("X-Real-Ip", req.RemoteAddr)
}
//更改内容
modifyFunc := func(resp *http.Response) error {
//请求以下命令:curl 'http://127.0.0.1:2002/error'
if resp.StatusCode != 200 {
//获取内容
oldPayload, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
//追加内容
newPayload := []byte("StatusCode error:" + string(oldPayload))
resp.Body = ioutil.NopCloser(bytes.NewBuffer(newPayload))
resp.ContentLength = int64(len(newPayload))
resp.Header.Set("Content-Length", strconv.FormatInt(int64(len(newPayload)), 10))
}
return nil
}
//错误回调 :关闭real_server时测试,错误回调
errFunc := func(w http.ResponseWriter, r *http.Request, err error) {
http.Error(w, "ErrorHandler error:"+err.Error(), 500)
}
return &httputil.ReverseProxy{
Director: director,
Transport: transport,
ModifyResponse: modifyFunc,
ErrorHandler: errFunc}
}
func singleJoiningSlash(a, b string) string {
aslash := strings.HasSuffix(a, "/")
bslash := strings.HasPrefix(b, "/")
switch {
case aslash && bslash:
return a + b[1:]
case !aslash && !bslash:
return a + "/" + b
}
return a + b
}
package main
import (
"context"
"fmt"
"github.com/ichunt2019/ichunt-micro-service/registry"
_ "github.com/ichunt2019/ichunt-micro-service/registry/etcd"
"io"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
rs1 := &RealServer{Addr: "127.0.0.1:2003"}
rs1.Run()
rs2 := &RealServer{Addr: "127.0.0.1:2004"}
rs2.Run()
//服务注册
register()
//监听关闭信号
quit := make(chan os.Signal)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
}
type RealServer struct {
Addr string
}
func register(){
registryInst, err := registry.InitRegistry(context.TODO(), "etcd",
registry.WithAddrs([]string{"192.168.2.232:2379"}),
registry.WithTimeout(time.Second),
registry.WithRegistryPath("/ichuntMicroService/"),
registry.WithHeartBeat(5),
)
if err != nil {
fmt.Printf("init registry failed, err:%v", err)
return
}
service := &registry.Service{
Name: "comment_service",
}
service.Nodes = append(service.Nodes, &registry.Node{
IP: "127.0.0.1",
Port: 2003,
},
&registry.Node{
IP: "127.0.0.1",
Port: 2004,
},
)
registryInst.Register(context.TODO(), service)
}
func (r *RealServer) Run() {
log.Println("Starting httpserver at " + r.Addr)
mux := http.NewServeMux()
mux.HandleFunc("/", r.HelloHandler)
mux.HandleFunc("/base/error", r.ErrorHandler)
server := &http.Server{
Addr: r.Addr,
WriteTimeout: time.Second * 3,
Handler: mux,
}
go func() {
log.Fatal(server.ListenAndServe())
}()
}
func (r *RealServer) HelloHandler(w http.ResponseWriter, req *http.Request) {
//127.0.0.1:8008/abc?sdsdsa=11
//r.Addr=127.0.0.1:8008
//req.URL.Path=/abc
fmt.Println(req.Host)
upath := fmt.Sprintf("http://%s%s\n", r.Addr, req.URL.Path)
realIP := fmt.Sprintf("RemoteAddr=%s,X-Forwarded-For=%v,X-Real-Ip=%v\n", req.RemoteAddr, req.Header.Get("X-Forwarded-For"),
req.Header.Get("X-Real-Ip"))
io.WriteString(w, upath)
io.WriteString(w, realIP)
}
func (r *RealServer) ErrorHandler(w http.ResponseWriter, req *http.Request) {
upath := "error handler"
w.WriteHeader(500)
io.WriteString(w, upath)
}
\ No newline at end of file
package main
import (
"bytes"
"compress/gzip"
"io/ioutil"
"log"
"math/rand"
"net"
"net/http"
"net/http/httputil"
"net/url"
"regexp"
"strconv"
"strings"
"time"
)
var addr = "127.0.0.1:2002"
func main() {
//rs1 := "http://www.baidu.com"
rs1 := "http://127.0.0.1:2003"
url1, err1 := url.Parse(rs1)
if err1 != nil {
log.Println(err1)
}
//rs2 := "http://www.baidu.com"
rs2 := "http://127.0.0.1:2004"
url2, err2 := url.Parse(rs2)
if err2 != nil {
log.Println(err2)
}
urls := []*url.URL{url1, url2}
proxy := NewMultipleHostsReverseProxy(urls)
log.Println("Starting httpserver at " + addr)
log.Fatal(http.ListenAndServe(addr, proxy))
}
var transport = &http.Transport{
DialContext: (&net.Dialer{
Timeout: 30 * time.Second, //连接超时
KeepAlive: 30 * time.Second, //长连接超时时间
}).DialContext,
MaxIdleConns: 100, //最大空闲连接
IdleConnTimeout: 90 * time.Second, //空闲超时时间
TLSHandshakeTimeout: 10 * time.Second, //tls握手超时时间
ExpectContinueTimeout: 1 * time.Second, //100-continue 超时时间
}
func NewMultipleHostsReverseProxy(targets []*url.URL) *httputil.ReverseProxy {
//请求协调者
director := func(req *http.Request) {
//url_rewrite
//127.0.0.1:2002/dir/abc ==> 127.0.0.1:2003/base/abc ??
//127.0.0.1:2002/dir/abc ==> 127.0.0.1:2002/abc
//127.0.0.1:2002/abc ==> 127.0.0.1:2003/base/abc
re, _ := regexp.Compile("^/dir(.*)");
req.URL.Path = re.ReplaceAllString(req.URL.Path, "$1")
//随机负载均衡
targetIndex := rand.Intn(len(targets))
target := targets[targetIndex]
targetQuery := target.RawQuery
req.URL.Scheme = target.Scheme
req.URL.Host = target.Host
//todo 部分章节补充1
//todo 当对域名(非内网)反向代理时需要设置此项。当作后端反向代理时不需要
req.Host = target.Host
// url地址重写:重写前:/aa 重写后:/base/aa
req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)
if targetQuery == "" || req.URL.RawQuery == "" {
req.URL.RawQuery = targetQuery + req.URL.RawQuery
} else {
req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
}
if _, ok := req.Header["User-Agent"]; !ok {
req.Header.Set("User-Agent", "user-agent")
}
//只在第一代理中设置此header头
//req.Header.Set("X-Real-Ip", req.RemoteAddr)
}
//更改内容
modifyFunc := func(resp *http.Response) error {
//请求以下命令:curl 'http://127.0.0.1:2002/error'
//todo 部分章节功能补充2
//todo 兼容websocket
if strings.Contains(resp.Header.Get("Connection"), "Upgrade") {
return nil
}
var payload []byte
var readErr error
//todo 部分章节功能补充3
//todo 兼容gzip压缩
if strings.Contains(resp.Header.Get("Content-Encoding"), "gzip") {
gr, err := gzip.NewReader(resp.Body)
if err != nil {
return err
}
payload, readErr = ioutil.ReadAll(gr)
resp.Header.Del("Content-Encoding")
} else {
payload, readErr = ioutil.ReadAll(resp.Body)
}
if readErr != nil {
return readErr
}
//异常请求时设置StatusCode
if resp.StatusCode != 200 {
payload = []byte("StatusCode error:" + string(payload))
}
//todo 部分章节功能补充4
//todo 因为预读了数据所以内容重新回写
resp.Body = ioutil.NopCloser(bytes.NewBuffer(payload))
resp.ContentLength = int64(len(payload))
resp.Header.Set("Content-Length", strconv.FormatInt(int64(len(payload)), 10))
return nil
}
//错误回调 :关闭real_server时测试,错误回调
errFunc := func(w http.ResponseWriter, r *http.Request, err error) {
http.Error(w, "ErrorHandler error:"+err.Error(), 500)
}
return &httputil.ReverseProxy{
Director: director,
Transport: transport,
ModifyResponse: modifyFunc,
ErrorHandler: errFunc}
}
func singleJoiningSlash(a, b string) string {
aslash := strings.HasSuffix(a, "/")
bslash := strings.HasPrefix(b, "/")
switch {
case aslash && bslash:
return a + b[1:]
case !aslash && !bslash:
return a + "/" + b
}
return a + b
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment