Skip to content
Snippets Groups Projects
Commit f9aff5ac authored by 李平's avatar 李平
Browse files

reslove conficts

parents d530be14 014d1bc6
No related branches found
No related tags found
No related merge requests found
Showing
with 1829 additions and 11 deletions
#!/usr/bin/env bash
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -e
export GOOS=linux
export GOARCH=amd64
export PROFILE="dev"
PROJECT_HOME=`pwd`
if [ -f "${PROJECT_HOME}/assembly/common/app.properties" ]; then
. ${PROJECT_HOME}/assembly/common/app.properties
fi
if [ -f "${PROJECT_HOME}/assembly/common/build.sh" ]; then
sh ${PROJECT_HOME}/assembly/common/build.sh
fi
#!/usr/bin/env bash
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -e
export GOOS=windows
export GOARCH=amd64
export PROFILE="release"
export PROJECT_HOME=`pwd`
if [ -f "${PROJECT_HOME}/assembly/common/app.properties" ]; then
. ${PROJECT_HOME}/assembly/common/app.properties
fi
if [ -f "${PROJECT_HOME}/assembly/common/build.sh" ]; then
sh ${PROJECT_HOME}/assembly/common/build.sh
fi
#!/usr/bin/env bash
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -e
export GOOS=windows
export GOARCH=amd64
export PROFILE="test"
export PROJECT_HOME=`pwd`
if [ -f "${PROJECT_HOME}/assembly/common/app.properties" ]; then
. ${PROJECT_HOME}/assembly/common/app.properties
fi
if [ -f "${PROJECT_HOME}/assembly/common/build.sh" ]; then
sh ${PROJECT_HOME}/assembly/common/build.sh
fi
# dubbo client yaml configure file
check: true
# client
request_timeout : "3s"
# connect timeout
connect_timeout : "3s"
# application config
application_config:
organization : "ikurento.com"
name : "BDTService"
module : "dubbogo user-info client"
version : "0.0.1"
owner : "ZX"
environment : "dev"
registries :
"hangzhouzk":
# 对应java配置中address属性的zookeeper <dubbo:registry address="zookeeper://127.0.0.1:2181"/>
protocol: "zookeeper"
timeout : "3s"
address: "127.0.0.1:2181"
username: ""
password: ""
"shanghaizk":
protocol: "zookeeper"
timeout : "3s"
address: "127.0.0.1:2182"
username: ""
password: ""
references:
"UserProvider":
# 可以指定多个registry,使用逗号隔开;不指定默认向所有注册中心注册
registry: "hangzhouzk"
filter: "example_fallback,hystrix_consumer"
protocol : "dubbo"
interface : "com.ikurento.user.UserProvider"
cluster: "failover"
methods :
- name: "GetUser"
retries: 3
protocol_conf:
dubbo:
reconnect_interval: 0
connection_number: 2
heartbeat_period: "5s"
session_timeout: "20s"
fail_fast_timeout: "5s"
pool_size: 64
pool_ttl: 600
getty_session_param:
compress_encoding: false
tcp_no_delay: true
tcp_keep_alive: true
keep_alive_period: "120s"
tcp_r_buf_size: 262144
tcp_w_buf_size: 65536
pkg_rq_size: 1024
pkg_wq_size: 512
tcp_read_timeout: "1s"
tcp_write_timeout: "5s"
wait_timeout: "1s"
max_msg_len: 10240
session_name: "client"
filter_conf:
hystrix:
configs:
"Default":
timeout : 1000
max_concurrent_requests : 25
sleep_window : 5000
error_percent_threshold : 50
request_volume_threshold: 20
"userp":
timeout: 2000
max_concurrent_requests: 512
sleep_window: 4000
error_percent_threshold: 35
request_volume_threshold: 6
"userp_m":
timeout : 1200
max_concurrent_requests : 512
sleep_window : 6000
error_percent_threshold : 60
request_volume_threshold: 16
error_whitelist: [".*exception.*"]
default: "Default"
services:
"com.ikurento.user.UserProvider":
service_config: "userp"
methods:
"GetUser": "userp_m"
"GetUser1": "userp_m"
\ No newline at end of file
level: "debug"
development: true
disableCaller: false
disableStacktrace: false
sampling:
encoding: "console"
# encoder
encoderConfig:
messageKey: "message"
levelKey: "level"
timeKey: "time"
nameKey: "logger"
callerKey: "caller"
stacktraceKey: "stacktrace"
lineEnding: ""
levelEncoder: "capitalColor"
timeEncoder: "iso8601"
durationEncoder: "seconds"
callerEncoder: "short"
nameEncoder: ""
outputPaths:
- "stderr"
errorOutputPaths:
- "stderr"
initialFields:
# dubbo client yaml configure file
check: true
# client
request_timeout : "3s"
# connect timeout
connect_timeout : "3s"
# application config
application_config:
organization : "ikurento.com"
name : "BDTService"
module : "dubbogo user-info client"
version : "0.0.1"
owner : "ZX"
environment : "release"
registries :
"hangzhouzk":
protocol: "zookeeper"
timeout : "3s"
address: "127.0.0.1:2181"
username: ""
password: ""
"shanghaizk":
protocol: "zookeeper"
timeout : "3s"
address: "127.0.0.1:2182"
username: ""
password: ""
references:
"UserProvider":
# 可以指定多个registry,使用逗号隔开;不指定默认向所有注册中心注册
registry: "hangzhouzk"
filter: "example_fallback,hystrix_consumer"
protocol : "dubbo"
# version: "2.0"
# group: "as"
interface : "com.ikurento.user.UserProvider"
cluster: "failover"
methods :
- name: "GetUser"
retries: 3
protocol_conf:
dubbo:
reconnect_interval: 0
connection_number: 2
heartbeat_period: "5s"
session_timeout: "20s"
fail_fast_timeout: "5s"
pool_size: 64
pool_ttl: 600
getty_session_param:
compress_encoding: false
tcp_no_delay: true
tcp_keep_alive: true
keep_alive_period: "120s"
tcp_r_buf_size: 262144
tcp_w_buf_size: 65536
pkg_rq_size: 1024
pkg_wq_size: 512
tcp_read_timeout: "1s"
tcp_write_timeout: "5s"
wait_timeout: "1s"
max_msg_len: 10240
session_name: "client"
filter_conf:
hystrix:
configs:
"Default":
timeout : 1000
max_concurrent_requests : 10
sleep_window : 5000
error_percent_threshold : 50
request_volume_threshold: 20
"userp":
timeout: 1200
max_concurrent_requests: 8
sleep_window: 4000
error_percent_threshold: 45
request_volume_threshold: 15
"userp_m":
timeout : 1200
max_concurrent_requests : 12
sleep_window : 6000
error_percent_threshold : 60
request_volume_threshold: 30
fallback: "exampleFallback"
default: "Default"
services:
"com.ikurento.user.UserProvider":
service_config: "userp"
methods:
"GetUser": "userp_m"
level: "warn"
development: true
disableCaller: true
disableStacktrace: true
sampling:
encoding: "console"
# encoder
encoderConfig:
messageKey: "message"
levelKey: "level"
timeKey: "time"
nameKey: "logger"
callerKey: "caller"
stacktraceKey: "stacktrace"
lineEnding: ""
levelEncoder: "capitalColor"
timeEncoder: "iso8601"
durationEncoder: "seconds"
callerEncoder: "short"
nameEncoder: ""
outputPaths:
- "stderr"
errorOutputPaths:
- "stderr"
initialFields:
# dubbo client yaml configure file
check: true
# client
request_timeout : "3s"
# connect timeout
connect_timeout : "3s"
# application config
application_config:
organization : "ikurento.com"
name : "BDTService"
module : "dubbogo user-info client"
version : "0.0.1"
owner : "ZX"
environment : "test"
registries :
"hangzhouzk":
protocol: "zookeeper"
timeout : "3s"
address: "127.0.0.1:2181"
username: ""
password: ""
"shanghaizk":
protocol: "zookeeper"
timeout : "3s"
address: "127.0.0.1:2182"
username: ""
password: ""
references:
"UserProvider":
# 可以指定多个registry,使用逗号隔开;不指定默认向所有注册中心注册
registry: "hangzhouzk"
filter: "example_fallback,hystrix_consumer"
protocol : "dubbo"
# version: "2.0"
# group: "as"
interface : "com.ikurento.user.UserProvider"
cluster: "failover"
methods :
- name: "GetUser"
retries: 3
protocol_conf:
dubbo:
reconnect_interval: 0
connection_number: 2
heartbeat_period: "5s"
session_timeout: "20s"
fail_fast_timeout: "5s"
pool_size: 64
pool_ttl: 600
getty_session_param:
compress_encoding: false
tcp_no_delay: true
tcp_keep_alive: true
keep_alive_period: "120s"
tcp_r_buf_size: 262144
tcp_w_buf_size: 65536
pkg_rq_size: 1024
pkg_wq_size: 512
tcp_read_timeout: "1s"
tcp_write_timeout: "5s"
wait_timeout: "1s"
max_msg_len: 10240
session_name: "client"
filter_conf:
hystrix:
configs:
"Default":
timeout : 1000
max_concurrent_requests : 25
sleep_window : 5000
error_percent_threshold : 50
request_volume_threshold: 20
"userp":
timeout: 2000
max_concurrent_requests: 512
sleep_window: 4000
error_percent_threshold: 35
request_volume_threshold: 6
"userp_m":
timeout : 1200
max_concurrent_requests : 512
sleep_window : 6000
error_percent_threshold : 60
request_volume_threshold: 16
error_whitelist: [".*exception.*"]
default: "Default"
services:
"com.ikurento.user.UserProvider":
service_config: "userp"
methods:
"GetUser": "userp_m"
"GetUser1": "userp_m"
\ No newline at end of file
level: "info"
development: false
disableCaller: false
disableStacktrace: true
sampling:
encoding: "console"
# encoder
encoderConfig:
messageKey: "message"
levelKey: "level"
timeKey: "time"
nameKey: "logger"
callerKey: "caller"
stacktraceKey: "stacktrace"
lineEnding: ""
levelEncoder: "capitalColor"
timeEncoder: "iso8601"
durationEncoder: "seconds"
callerEncoder: "short"
nameEncoder: ""
outputPaths:
- "stderr"
errorOutputPaths:
- "stderr"
initialFields:
......@@ -83,11 +83,7 @@ func struct2MapAll(obj interface{}) interface{} {
if t.Kind() == reflect.Struct {
result := make(map[string]interface{}, t.NumField())
for i := 0; i < t.NumField(); i++ {
if v.Field(i).Kind() == reflect.Struct {
if v.Field(i).CanInterface() {
setInMap(result, t.Field(i), struct2MapAll(v.Field(i).Interface()))
}
} else if v.Field(i).Kind() == reflect.Slice {
if v.Field(i).Kind() == reflect.Struct || v.Field(i).Kind() == reflect.Slice || v.Field(i).Kind() == reflect.Map {
if v.Field(i).CanInterface() {
setInMap(result, t.Field(i), struct2MapAll(v.Field(i).Interface()))
}
......@@ -106,6 +102,18 @@ func struct2MapAll(obj interface{}) interface{} {
newTemps = append(newTemps, newTemp)
}
return newTemps
} else if t.Kind() == reflect.Map {
var newTempMap = make(map[string]interface{}, v.Len())
iter := v.MapRange()
for iter.Next() {
mapK := iter.Key().String()
if !iter.Value().CanInterface() {
continue
}
mapV := iter.Value().Interface()
newTempMap[mapK] = struct2MapAll(mapV)
}
return newTempMap
} else {
return obj
}
......
......@@ -87,3 +87,35 @@ func Test_struct2MapAll_Slice(t *testing.T) {
assert.Equal(t, reflect.Slice, reflect.TypeOf(m["caCa"]).Kind())
assert.Equal(t, reflect.Map, reflect.TypeOf(m["caCa"].([]interface{})[0].(map[string]interface{})["xxYy"]).Kind())
}
func Test_struct2MapAll_Map(t *testing.T) {
var testData struct {
AaAa string
Baba map[string]interface{}
CaCa map[string]string
DdDd map[string]interface{}
}
testData.AaAa = "aaaa"
testData.Baba = make(map[string]interface{})
testData.CaCa = make(map[string]string)
testData.DdDd = nil
testData.Baba["kk"] = 1
var structData struct {
Str string
}
structData.Str = "str"
testData.Baba["struct"] = structData
testData.Baba["nil"] = nil
testData.CaCa["k1"] = "v1"
testData.CaCa["kv2"] = "v2"
m := struct2MapAll(testData)
assert.Equal(t, reflect.Map, reflect.TypeOf(m).Kind())
assert.Equal(t, reflect.String, reflect.TypeOf(m.(map[string]interface{})["aaAa"]).Kind())
assert.Equal(t, reflect.Map, reflect.TypeOf(m.(map[string]interface{})["baba"]).Kind())
assert.Equal(t, reflect.Map, reflect.TypeOf(m.(map[string]interface{})["baba"].(map[string]interface{})["struct"]).Kind())
assert.Equal(t, "str", m.(map[string]interface{})["baba"].(map[string]interface{})["struct"].(map[string]interface{})["str"])
assert.Equal(t, nil, m.(map[string]interface{})["baba"].(map[string]interface{})["nil"])
assert.Equal(t, reflect.Map, reflect.TypeOf(m.(map[string]interface{})["caCa"]).Kind())
assert.Equal(t, reflect.Map, reflect.TypeOf(m.(map[string]interface{})["ddDd"]).Kind())
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package impl
import (
"fmt"
"regexp"
"sync"
)
import (
"github.com/afex/hystrix-go/hystrix"
perrors "github.com/pkg/errors"
"gopkg.in/yaml.v2"
)
import (
"github.com/apache/dubbo-go/common/extension"
"github.com/apache/dubbo-go/common/logger"
"github.com/apache/dubbo-go/config"
"github.com/apache/dubbo-go/filter"
"github.com/apache/dubbo-go/protocol"
)
const (
HYSTRIX_CONSUMER = "hystrix_consumer"
HYSTRIX_PROVIDER = "hystrix_provider"
HYSTRIX = "hystrix"
)
var (
confConsumer = &HystrixFilterConfig{}
confProvider = &HystrixFilterConfig{}
configLoadMutex = sync.RWMutex{}
consumerConfigOnce sync.Once
providerConfigOnce sync.Once
)
//The filter in the server end of dubbo-go can't get the invoke result for now,
//this filter ONLY works in CLIENT end (consumer side) temporarily
//Only after the callService logic is integrated into the filter chain of server end can this filter be used,
//which will be done soon
func init() {
extension.SetFilter(HYSTRIX_CONSUMER, GetHystrixFilterConsumer)
extension.SetFilter(HYSTRIX_PROVIDER, GetHystrixFilterProvider)
}
type HystrixFilterError struct {
err error
failByHystrix bool
}
func (hfError *HystrixFilterError) Error() string {
return hfError.err.Error()
}
func (hfError *HystrixFilterError) FailByHystrix() bool {
return hfError.failByHystrix
}
func NewHystrixFilterError(err error, failByHystrix bool) error {
return &HystrixFilterError{
err: err,
failByHystrix: failByHystrix,
}
}
type HystrixFilter struct {
COrP bool //true for consumer
res map[string][]*regexp.Regexp
ifNewMap sync.Map
}
func (hf *HystrixFilter) Invoke(invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
cmdName := fmt.Sprintf("%s&method=%s", invoker.GetUrl().Key(), invocation.MethodName())
// Do the configuration if the circuit breaker is created for the first time
if _, load := hf.ifNewMap.LoadOrStore(cmdName, true); !load {
configLoadMutex.Lock()
filterConf := getConfig(invoker.GetUrl().Service(), invocation.MethodName(), hf.COrP)
for _, ptn := range filterConf.Error {
reg, err := regexp.Compile(ptn)
if err != nil {
logger.Warnf("[Hystrix Filter]Errors occurred parsing error omit regexp: %s, %v", ptn, err)
} else {
if hf.res == nil {
hf.res = make(map[string][]*regexp.Regexp)
}
hf.res[invocation.MethodName()] = append(hf.res[invocation.MethodName()], reg)
}
}
hystrix.ConfigureCommand(cmdName, hystrix.CommandConfig{
Timeout: filterConf.Timeout,
MaxConcurrentRequests: filterConf.MaxConcurrentRequests,
SleepWindow: filterConf.SleepWindow,
ErrorPercentThreshold: filterConf.ErrorPercentThreshold,
RequestVolumeThreshold: filterConf.RequestVolumeThreshold,
})
configLoadMutex.Unlock()
}
configLoadMutex.RLock()
_, _, err := hystrix.GetCircuit(cmdName)
configLoadMutex.RUnlock()
if err != nil {
logger.Errorf("[Hystrix Filter]Errors occurred getting circuit for %s , will invoke without hystrix, error is: ", cmdName, err)
return invoker.Invoke(invocation)
}
logger.Infof("[Hystrix Filter]Using hystrix filter: %s", cmdName)
var result protocol.Result
_ = hystrix.Do(cmdName, func() error {
result = invoker.Invoke(invocation)
err := result.Error()
if err != nil {
result.SetError(NewHystrixFilterError(err, false))
for _, reg := range hf.res[invocation.MethodName()] {
if reg.MatchString(err.Error()) {
logger.Debugf("[Hystrix Filter]Error in invocation but omitted in circuit breaker: %v; %s", err, cmdName)
return nil
}
}
}
return err
}, func(err error) error {
//Return error and if it is caused by hystrix logic, so that it can be handled by previous filters.
_, ok := err.(hystrix.CircuitError)
logger.Debugf("[Hystrix Filter]Hystrix health check counted, error is: %v, failed by hystrix: %v; %s", err, ok, cmdName)
result = &protocol.RPCResult{}
result.SetResult(nil)
result.SetError(NewHystrixFilterError(err, ok))
return err
})
return result
}
func (hf *HystrixFilter) OnResponse(result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
return result
}
func GetHystrixFilterConsumer() filter.Filter {
//When first called, load the config in
consumerConfigOnce.Do(func() {
if err := initHystrixConfigConsumer(); err != nil {
logger.Warnf("[Hystrix Filter]Config load failed for consumer, error is: %v , will use default", err)
}
})
return &HystrixFilter{COrP: true}
}
func GetHystrixFilterProvider() filter.Filter {
providerConfigOnce.Do(func() {
if err := initHystrixConfigProvider(); err != nil {
logger.Warnf("[Hystrix Filter]Config load failed for provider, error is: %v , will use default", err)
}
})
return &HystrixFilter{COrP: false}
}
func getConfig(service string, method string, cOrP bool) CommandConfigWithError {
//Find method level config
var conf *HystrixFilterConfig
if cOrP {
conf = confConsumer
} else {
conf = confProvider
}
getConf := conf.Configs[conf.Services[service].Methods[method]]
if getConf != nil {
logger.Infof("[Hystrix Filter]Found method-level config for %s - %s", service, method)
return *getConf
}
//Find service level config
getConf = conf.Configs[conf.Services[service].ServiceConfig]
if getConf != nil {
logger.Infof("[Hystrix Filter]Found service-level config for %s - %s", service, method)
return *getConf
}
//Find default config
getConf = conf.Configs[conf.Default]
if getConf != nil {
logger.Infof("[Hystrix Filter]Found global default config for %s - %s", service, method)
return *getConf
}
getConf = &CommandConfigWithError{}
logger.Infof("[Hystrix Filter]No config found for %s - %s, using default", service, method)
return *getConf
}
func initHystrixConfigConsumer() error {
if config.GetConsumerConfig().FilterConf == nil {
return perrors.Errorf("no config for hystrix")
}
filterConfig := config.GetConsumerConfig().FilterConf.(map[interface{}]interface{})[HYSTRIX]
if filterConfig == nil {
return perrors.Errorf("no config for hystrix")
}
hystrixConfByte, err := yaml.Marshal(filterConfig)
if err != nil {
return err
}
err = yaml.Unmarshal(hystrixConfByte, confConsumer)
if err != nil {
return err
}
return nil
}
func initHystrixConfigProvider() error {
if config.GetProviderConfig().FilterConf == nil {
return perrors.Errorf("no config for hystrix")
}
filterConfig := config.GetConsumerConfig().FilterConf.(map[interface{}]interface{})[HYSTRIX]
if filterConfig == nil {
return perrors.Errorf("no config for hystrix")
}
hystrixConfByte, err := yaml.Marshal(filterConfig)
if err != nil {
return err
}
err = yaml.Unmarshal(hystrixConfByte, confProvider)
if err != nil {
return err
}
return nil
}
//For sake of dynamic config
//func RefreshHystrix() error {
// conf = &HystrixFilterConfig{}
// hystrix.Flush()
// return initHystrixConfig()
//}
type CommandConfigWithError struct {
Timeout int `yaml:"timeout"`
MaxConcurrentRequests int `yaml:"max_concurrent_requests"`
RequestVolumeThreshold int `yaml:"request_volume_threshold"`
SleepWindow int `yaml:"sleep_window"`
ErrorPercentThreshold int `yaml:"error_percent_threshold"`
Error []string `yaml:"error_whitelist"`
}
//Config:
//- Timeout: how long to wait for command to complete, in milliseconds
//- MaxConcurrentRequests: how many commands of the same type can run at the same time
//- RequestVolumeThreshold: the minimum number of requests needed before a circuit can be tripped due to health
//- SleepWindow: how long, in milliseconds, to wait after a circuit opens before testing for recovery
//- ErrorPercentThreshold: it causes circuits to open once the rolling measure of errors exceeds this percent of requests
//See hystrix doc
type HystrixFilterConfig struct {
Configs map[string]*CommandConfigWithError
Default string
Services map[string]ServiceHystrixConfig
}
type ServiceHystrixConfig struct {
ServiceConfig string `yaml:"service_config"`
Methods map[string]string
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package impl
import (
"regexp"
"testing"
)
import (
"github.com/afex/hystrix-go/hystrix"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
)
import (
"github.com/apache/dubbo-go/protocol"
"github.com/apache/dubbo-go/protocol/invocation"
)
func init() {
mockInitHystrixConfig()
}
func TestNewHystrixFilterError(t *testing.T) {
get := NewHystrixFilterError(errors.New("test"), true)
assert.True(t, get.(*HystrixFilterError).FailByHystrix())
assert.Equal(t, "test", get.Error())
}
func mockInitHystrixConfig() {
//Mock config
confConsumer = &HystrixFilterConfig{
make(map[string]*CommandConfigWithError),
"Default",
make(map[string]ServiceHystrixConfig),
}
confConsumer.Configs["Default"] = &CommandConfigWithError{
Timeout: 1000,
MaxConcurrentRequests: 600,
RequestVolumeThreshold: 5,
SleepWindow: 5000,
ErrorPercentThreshold: 5,
Error: nil,
}
confConsumer.Configs["userp"] = &CommandConfigWithError{
Timeout: 2000,
MaxConcurrentRequests: 64,
RequestVolumeThreshold: 15,
SleepWindow: 4000,
ErrorPercentThreshold: 45,
Error: nil,
}
confConsumer.Configs["userp_m"] = &CommandConfigWithError{
Timeout: 1200,
MaxConcurrentRequests: 64,
RequestVolumeThreshold: 5,
SleepWindow: 6000,
ErrorPercentThreshold: 60,
Error: []string{
"exception",
},
}
confConsumer.Services["com.ikurento.user.UserProvider"] = ServiceHystrixConfig{
"userp",
map[string]string{
"GetUser": "userp_m",
},
}
}
func TestGetHystrixFilter(t *testing.T) {
filterGot := GetHystrixFilterConsumer()
assert.NotNil(t, filterGot)
}
func TestGetConfig_1(t *testing.T) {
mockInitHystrixConfig()
configGot := getConfig("com.ikurento.user.UserProvider", "GetUser", true)
assert.NotNil(t, configGot)
assert.Equal(t, 1200, configGot.Timeout)
assert.Equal(t, 64, configGot.MaxConcurrentRequests)
assert.Equal(t, 6000, configGot.SleepWindow)
assert.Equal(t, 60, configGot.ErrorPercentThreshold)
assert.Equal(t, 5, configGot.RequestVolumeThreshold)
}
func TestGetConfig_2(t *testing.T) {
mockInitHystrixConfig()
configGot := getConfig("com.ikurento.user.UserProvider", "GetUser0", true)
assert.NotNil(t, configGot)
assert.Equal(t, 2000, configGot.Timeout)
assert.Equal(t, 64, configGot.MaxConcurrentRequests)
assert.Equal(t, 4000, configGot.SleepWindow)
assert.Equal(t, 45, configGot.ErrorPercentThreshold)
assert.Equal(t, 15, configGot.RequestVolumeThreshold)
}
func TestGetConfig_3(t *testing.T) {
mockInitHystrixConfig()
//This should use default
configGot := getConfig("Mock.Service", "GetMock", true)
assert.NotNil(t, configGot)
assert.Equal(t, 1000, configGot.Timeout)
assert.Equal(t, 600, configGot.MaxConcurrentRequests)
assert.Equal(t, 5000, configGot.SleepWindow)
assert.Equal(t, 5, configGot.ErrorPercentThreshold)
assert.Equal(t, 5, configGot.RequestVolumeThreshold)
}
type testMockSuccessInvoker struct {
protocol.BaseInvoker
}
func (iv *testMockSuccessInvoker) Invoke(invocation protocol.Invocation) protocol.Result {
return &protocol.RPCResult{
Rest: "Sucess",
Err: nil,
}
}
type testMockFailInvoker struct {
protocol.BaseInvoker
}
func (iv *testMockFailInvoker) Invoke(invocation protocol.Invocation) protocol.Result {
return &protocol.RPCResult{
Err: errors.Errorf("exception"),
}
}
func TestHystrixFilter_Invoke_Success(t *testing.T) {
hf := &HystrixFilter{}
result := hf.Invoke(&testMockSuccessInvoker{}, &invocation.RPCInvocation{})
assert.NotNil(t, result)
assert.NoError(t, result.Error())
assert.NotNil(t, result.Result())
}
func TestHystrixFilter_Invoke_Fail(t *testing.T) {
hf := &HystrixFilter{}
result := hf.Invoke(&testMockFailInvoker{}, &invocation.RPCInvocation{})
assert.NotNil(t, result)
assert.Error(t, result.Error())
}
func TestHystricFilter_Invoke_CircuitBreak(t *testing.T) {
mockInitHystrixConfig()
hystrix.Flush()
hf := &HystrixFilter{COrP: true}
resChan := make(chan protocol.Result, 50)
for i := 0; i < 50; i++ {
go func() {
result := hf.Invoke(&testMockFailInvoker{}, &invocation.RPCInvocation{})
resChan <- result
}()
}
//This can not always pass the test when on travis due to concurrency, you can uncomment the code below and test it locally
//var lastRest bool
//for i := 0; i < 50; i++ {
// lastRest = (<-resChan).Error().(*HystrixFilterError).FailByHystrix()
//}
//Normally the last result should be true, which means the circuit has been opened
//
//assert.True(t, lastRest)
}
func TestHystricFilter_Invoke_CircuitBreak_Omit_Exception(t *testing.T) {
mockInitHystrixConfig()
hystrix.Flush()
reg, _ := regexp.Compile(".*exception.*")
regs := []*regexp.Regexp{reg}
hf := &HystrixFilter{res: map[string][]*regexp.Regexp{"": regs}, COrP: true}
resChan := make(chan protocol.Result, 50)
for i := 0; i < 50; i++ {
go func() {
result := hf.Invoke(&testMockFailInvoker{}, &invocation.RPCInvocation{})
resChan <- result
}()
}
//This can not always pass the test when on travis due to concurrency, you can uncomment the code below and test it locally
//time.Sleep(time.Second * 6)
//var lastRest bool
//for i := 0; i < 50; i++ {
// lastRest = (<-resChan).Error().(*HystrixFilterError).FailByHystrix()
//}
//
//assert.False(t, lastRest)
}
func TestGetHystrixFilterConsumer(t *testing.T) {
get := GetHystrixFilterConsumer()
assert.NotNil(t, get)
assert.True(t, get.(*HystrixFilter).COrP)
}
func TestGetHystrixFilterProvider(t *testing.T) {
get := GetHystrixFilterProvider()
assert.NotNil(t, get)
assert.False(t, get.(*HystrixFilter).COrP)
}
......@@ -2,6 +2,7 @@ module github.com/apache/dubbo-go
require (
github.com/Workiva/go-datastructures v1.0.50
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190802083043-4cd0c391755e // indirect
github.com/apache/dubbo-go-hessian2 v1.2.5-0.20190731020727-1697039810c8
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23 // indirect
......@@ -10,29 +11,29 @@ require (
github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f // indirect
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
github.com/dubbogo/getty v1.2.2
github.com/dubbogo/gost v1.1.1
github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 // indirect
github.com/go-errors/errors v1.0.1 // indirect
github.com/gogo/protobuf v1.2.1 // indirect
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect
github.com/golang/mock v1.3.1
github.com/google/btree v1.0.0 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 // indirect
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.9.5 // indirect
github.com/hashicorp/consul v1.5.3
github.com/hashicorp/consul/api v1.1.0
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 // indirect
github.com/jonboulle/clockwork v0.1.0 // indirect
github.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570 // indirect
github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f // indirect
github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 // indirect
github.com/magiconair/properties v1.8.1
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/nacos-group/nacos-sdk-go v0.0.0-20190723125407-0242d42e3dbb
github.com/pkg/errors v0.8.1
github.com/prometheus/client_golang v1.1.0 // indirect
github.com/samuel/go-zookeeper v0.0.0-20180130194729-c4fab1ac1bec
github.com/smartystreets/goconvey v0.0.0-20190710185942-9d28bd7c0945 // indirect
github.com/soheilhy/cmux v0.1.4 // indirect
github.com/stretchr/testify v1.3.0
github.com/tebeka/strftime v0.1.3 // indirect
......
This diff is collapsed.
......@@ -30,7 +30,9 @@ import (
"github.com/apache/dubbo-go/common"
"github.com/apache/dubbo-go/common/constant"
"github.com/apache/dubbo-go/common/extension"
"github.com/apache/dubbo-go/filter/impl"
"github.com/apache/dubbo-go/common/logger"
"github.com/apache/dubbo-go/filter"
//"github.com/apache/dubbo-go/filter/impl"
"github.com/apache/dubbo-go/protocol"
)
......@@ -40,7 +42,7 @@ func TestProtocolFilterWrapper_Export(t *testing.T) {
u := common.NewURLWithOptions(
common.WithParams(url.Values{}),
common.WithParamsValue(constant.SERVICE_FILTER_KEY, impl.ECHO))
common.WithParamsValue(constant.SERVICE_FILTER_KEY, "echo"))
exporter := filtProto.Export(protocol.NewBaseInvoker(*u))
_, ok := exporter.GetInvoker().(*FilterInvoker)
assert.True(t, ok)
......@@ -52,8 +54,35 @@ func TestProtocolFilterWrapper_Refer(t *testing.T) {
u := common.NewURLWithOptions(
common.WithParams(url.Values{}),
common.WithParamsValue(constant.REFERENCE_FILTER_KEY, impl.ECHO))
common.WithParamsValue(constant.REFERENCE_FILTER_KEY, "echo"))
invoker := filtProto.Refer(*u)
_, ok := invoker.(*FilterInvoker)
assert.True(t, ok)
}
//the same as echo filter, for test
func init() {
extension.SetFilter("echo", GetFilter)
}
type EchoFilterForTest struct{}
func (ef *EchoFilterForTest) Invoke(invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
logger.Infof("invoking echo filter.")
logger.Debugf("%v,%v", invocation.MethodName(), len(invocation.Arguments()))
if invocation.MethodName() == constant.ECHO && len(invocation.Arguments()) == 1 {
return &protocol.RPCResult{
Rest: invocation.Arguments()[0],
}
}
return invoker.Invoke(invocation)
}
func (ef *EchoFilterForTest) OnResponse(result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
return result
}
func GetFilter() filter.Filter {
return &EchoFilterForTest{}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package consul
import (
"sync"
)
import (
consul "github.com/hashicorp/consul/api"
"github.com/hashicorp/consul/api/watch"
perrors "github.com/pkg/errors"
)
import (
"github.com/apache/dubbo-go/common"
"github.com/apache/dubbo-go/common/logger"
"github.com/apache/dubbo-go/registry"
"github.com/apache/dubbo-go/remoting"
)
// Consul listener wraps the consul watcher, to
// listen the service information change in consul
// registry.
type consulListener struct {
// Registry url.
registryUrl common.URL
// Consumer url.
consumerUrl common.URL
// Consul watcher.
plan *watch.Plan
// Most recent service urls return by
// watcher.
urls []common.URL
// All service information changes will
// be wrapped into ServiceEvent, and be
// sent into eventCh. Then listener's
// Next method will get event from eventCh,
// and return to upstream.
eventCh chan *registry.ServiceEvent
// All errors, happening in the listening
// period, will be caught and send into
// errCh. Then listener's Next method will
// get error from errCh, and return to
// upstream.
errCh chan error
// Done field represents whether consul
// listener has been closed. When closing
// listener, this field will be closed,
// and will notify consul watcher to close.
done chan struct{}
// After listener notifies consul watcher
// to close, listener will call wg.wait to
// make sure that consul watcher is closed
// before the listener closes.
wg sync.WaitGroup
}
func newConsulListener(registryUrl common.URL, consumerUrl common.URL) (*consulListener, error) {
params := make(map[string]interface{}, 8)
params["type"] = "service"
params["service"] = consumerUrl.Service()
params["tag"] = "dubbo"
params["passingonly"] = true
plan, err := watch.Parse(params)
if err != nil {
return nil, err
}
listener := &consulListener{
registryUrl: registryUrl,
consumerUrl: consumerUrl,
plan: plan,
urls: make([]common.URL, 0, 8),
eventCh: make(chan *registry.ServiceEvent, 32),
errCh: make(chan error, 32),
done: make(chan struct{}),
}
// Set handler to consul watcher, and
// make watcher begin to watch service
// information change.
listener.plan.Handler = listener.handler
listener.wg.Add(1)
go listener.run()
return listener, nil
}
// Wrap the consul watcher run api. There are three
// conditions that will finish the run:
// - close done
// - call plan.Stop
// - close eventCh and errCh
// If run meets first two conditions, it will close
// gracefully. However, if run meets the last condition,
// run will close with panic, so use recover to cover
// this case.
func (l *consulListener) run() {
defer func() {
p := recover()
if p != nil {
logger.Warnf("consul listener finish with panic %v", p)
}
l.wg.Done()
}()
for {
select {
case <-l.done:
return
default:
err := l.plan.Run(l.registryUrl.Location)
if err != nil {
l.errCh <- err
}
}
}
}
func (l *consulListener) handler(idx uint64, raw interface{}) {
var (
service *consul.ServiceEntry
event *registry.ServiceEvent
url common.URL
ok bool
err error
)
services, ok := raw.([]*consul.ServiceEntry)
if !ok {
err = perrors.New("handler get non ServiceEntry type parameter")
l.errCh <- err
return
}
newUrls := make([]common.URL, 0, 8)
events := make([]*registry.ServiceEvent, 0, 8)
for _, service = range services {
url, err = retrieveURL(service)
if err != nil {
l.errCh <- err
return
}
newUrls = append(newUrls, url)
}
for _, url = range l.urls {
ok = in(url, newUrls)
if !ok {
event := &registry.ServiceEvent{Action: remoting.EventTypeDel, Service: url}
events = append(events, event)
}
}
for _, url = range newUrls {
ok = in(url, l.urls)
if !ok {
event := &registry.ServiceEvent{Action: remoting.EventTypeAdd, Service: url}
events = append(events, event)
}
}
l.urls = newUrls
for _, event = range events {
l.eventCh <- event
}
}
func (l *consulListener) Next() (*registry.ServiceEvent, error) {
select {
case event := <-l.eventCh:
return event, nil
case err := <-l.errCh:
return nil, err
}
}
func (l *consulListener) Close() {
close(l.done)
l.plan.Stop()
close(l.eventCh)
close(l.errCh)
l.wg.Wait()
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package consul
import (
"github.com/stretchr/testify/assert"
)
import (
"github.com/apache/dubbo-go/remoting"
)
func (suite *consulRegistryTestSuite) testListener(action remoting.EventType) {
event, err := suite.listener.Next()
assert.NoError(suite.t, err)
assert.Equal(suite.t, action, event.Action)
assert.True(suite.t, suite.providerUrl.URLEqual(event.Service))
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package consul
import (
"strconv"
)
import (
consul "github.com/hashicorp/consul/api"
)
import (
"github.com/apache/dubbo-go/common"
"github.com/apache/dubbo-go/common/constant"
"github.com/apache/dubbo-go/common/extension"
"github.com/apache/dubbo-go/registry"
)
func init() {
extension.SetRegistry("consul", newConsulRegistry)
}
// Consul registry wraps the consul client, to
// register and subscribe service.
type consulRegistry struct {
// Registry url.
*common.URL
// Consul client.
client *consul.Client
// Done field represents whether
// consul registry is closed.
done chan struct{}
}
func newConsulRegistry(url *common.URL) (registry.Registry, error) {
config := &consul.Config{Address: url.Location}
client, err := consul.NewClient(config)
if err != nil {
return nil, err
}
r := &consulRegistry{
URL: url,
client: client,
done: make(chan struct{}),
}
return r, nil
}
func (r *consulRegistry) Register(url common.URL) error {
var err error
role, _ := strconv.Atoi(r.URL.GetParam(constant.ROLE_KEY, ""))
if role == common.PROVIDER {
err = r.register(url)
if err != nil {
return err
}
}
return nil
}
func (r *consulRegistry) register(url common.URL) error {
service, err := buildService(url)
if err != nil {
return err
}
return r.client.Agent().ServiceRegister(service)
}
func (r *consulRegistry) Unregister(url common.URL) error {
var err error
role, _ := strconv.Atoi(r.URL.GetParam(constant.ROLE_KEY, ""))
if role == common.PROVIDER {
err = r.unregister(url)
if err != nil {
return err
}
}
return nil
}
func (r *consulRegistry) unregister(url common.URL) error {
return r.client.Agent().ServiceDeregister(buildId(url))
}
func (r *consulRegistry) Subscribe(url common.URL) (registry.Listener, error) {
var (
listener registry.Listener
err error
)
role, _ := strconv.Atoi(r.URL.GetParam(constant.ROLE_KEY, ""))
if role == common.CONSUMER {
listener, err = r.getListener(url)
if err != nil {
return nil, err
}
}
return listener, nil
}
func (r *consulRegistry) getListener(url common.URL) (registry.Listener, error) {
listener, err := newConsulListener(*r.URL, url)
return listener, err
}
func (r *consulRegistry) GetUrl() common.URL {
return *r.URL
}
func (r *consulRegistry) IsAvailable() bool {
select {
case <-r.done:
return false
default:
return true
}
}
func (r *consulRegistry) Destroy() {
close(r.done)
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package consul
import (
"github.com/stretchr/testify/assert"
)
func (suite *consulRegistryTestSuite) testNewProviderRegistry() {
providerRegistryUrl := newProviderRegistryUrl(registryHost, registryPort)
providerRegistry, err := newConsulRegistry(providerRegistryUrl)
assert.NoError(suite.t, err)
suite.providerRegistry = providerRegistry
}
func (suite *consulRegistryTestSuite) testNewConsumerRegistry() {
consumerRegistryUrl := newConsumerRegistryUrl(registryHost, registryPort)
consumerRegistry, err := newConsulRegistry(consumerRegistryUrl)
assert.NoError(suite.t, err)
suite.consumerRegistry = consumerRegistry
}
func (suite *consulRegistryTestSuite) testRegister() {
providerUrl := newProviderUrl(providerHost, providerPort, service, protocol)
suite.providerUrl = providerUrl
err := suite.providerRegistry.Register(providerUrl)
assert.NoError(suite.t, err)
}
func (suite *consulRegistryTestSuite) testUnregister() {
consulProviderRegistry, _ := suite.providerRegistry.(*consulRegistry)
err := consulProviderRegistry.Unregister(suite.providerUrl)
assert.NoError(suite.t, err)
}
func (suite *consulRegistryTestSuite) testSubscribe() {
consumerUrl := newConsumerUrl(consumerHost, consumerPort, service, protocol)
suite.consumerUrl = consumerUrl
listener, err := suite.consumerRegistry.Subscribe(consumerUrl)
assert.NoError(suite.t, err)
suite.listener = listener
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment