#502 Fix serialize problem and do some refactoring for health check

This commit is contained in:
nkorange 2019-01-27 22:50:41 +08:00
parent e3440fe7f0
commit caf7213c2d
25 changed files with 93 additions and 127 deletions

View File

@ -50,6 +50,7 @@ public class Service {
/** /**
* Health check mode. * Health check mode.
*/ */
@Deprecated
private String healthCheckMode; private String healthCheckMode;
/** /**

View File

@ -64,3 +64,7 @@ server.tomcat.accesslog.enabled=true
server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D
# default current work dir # default current work dir
server.tomcat.basedir= server.tomcat.basedir=
nacos.naming.partition.taskDispatchThreadCount=10
nacos.naming.partition.taskDispatchPeriod=200
nacos.naming.partition.batchSyncKeyCount=1000

View File

@ -38,3 +38,7 @@ server.tomcat.basedir=
#nacos.security.ignore.urls=/** #nacos.security.ignore.urls=/**
nacos.security.ignore.urls=/,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/v1/auth/login,/v1/console/health,/v1/cs/**,/v1/ns/**,/v1/cmdb/**,/actuator/** nacos.security.ignore.urls=/,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/v1/auth/login,/v1/console/health,/v1/cs/**,/v1/ns/**,/v1/cmdb/**,/actuator/**
nacos.naming.partition.taskDispatchThreadCount=10
nacos.naming.partition.taskDispatchPeriod=200
nacos.naming.partition.batchSyncKeyCount=1000

View File

@ -28,8 +28,10 @@ import com.alibaba.nacos.naming.misc.UtilsAndCommons;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@ -43,6 +45,7 @@ import java.util.concurrent.ConcurrentHashMap;
* @since 1.0.0 * @since 1.0.0
*/ */
@Component @Component
@DependsOn("serverListManager")
public class DataSyncer implements MemberChangeListener { public class DataSyncer implements MemberChangeListener {
@Autowired @Autowired
@ -61,7 +64,8 @@ public class DataSyncer implements MemberChangeListener {
private List<Member> servers; private List<Member> servers;
public DataSyncer() { @PostConstruct
public void init() {
serverListManager.listen(this); serverListManager.listen(this);
startTimedSync(); startTimedSync();
} }

View File

@ -16,6 +16,7 @@
package com.alibaba.nacos.naming.consistency.ephemeral.partition; package com.alibaba.nacos.naming.consistency.ephemeral.partition;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/** /**
* Stores some configurations for Partition protocol * Stores some configurations for Partition protocol
@ -23,15 +24,16 @@ import org.springframework.beans.factory.annotation.Value;
* @author nkorange * @author nkorange
* @since 1.0.0 * @since 1.0.0
*/ */
@Component
public class PartitionConfig { public class PartitionConfig {
@Value("taskDispatchThreadCount") @Value("${nacos.naming.partition.taskDispatchThreadCount}")
private int taskDispatchThreadCount = 10; private int taskDispatchThreadCount = 10;
@Value("taskDispatchPeriod") @Value("${nacos.naming.partition.taskDispatchPeriod}")
private int taskDispatchPeriod = 2000; private int taskDispatchPeriod = 2000;
@Value("batchSyncKeyCount") @Value("${nacos.naming.partition.batchSyncKeyCount}")
private int batchSyncKeyCount = 1000; private int batchSyncKeyCount = 1000;
public int getTaskDispatchThreadCount() { public int getTaskDispatchThreadCount() {

View File

@ -135,25 +135,37 @@ public class RaftStore {
} }
if (KeyBuilder.matchServiceMetaKey(file.getName())) { if (KeyBuilder.matchServiceMetaKey(file.getName())) {
try {
return JSON.parseObject(json.replace("\\", ""), new TypeReference<Datum<Service>>() { return JSON.parseObject(json.replace("\\", ""), new TypeReference<Datum<Service>>() {
}); });
} catch (Exception e) {
Datum<String> datum = JSON.parseObject(json, new TypeReference<Datum<String>>(){});
Datum<Service> serviceDatum = new Datum<>();
serviceDatum.timestamp.set(datum.timestamp.get());
serviceDatum.key = datum.key;
serviceDatum.value = JSON.parseObject(datum.value, Service.class);
return serviceDatum;
}
} }
if (KeyBuilder.matchInstanceListKey(file.getName())) { if (KeyBuilder.matchInstanceListKey(file.getName())) {
Datum<List<Instance>> datum = JSON.parseObject(json, new TypeReference<Datum<List<Instance>>>() { try {
return JSON.parseObject(json, new TypeReference<Datum<Instances>>() {
}); });
Map<String, Instance> instanceMap = new HashMap<>(64); } catch (Exception e) {
if (datum.value == null || datum.value.isEmpty()) { Datum<String> datum = JSON.parseObject(json, new TypeReference<Datum<String>>(){});
return datum; List<Instance> instanceList = JSON.parseObject(datum.value, new TypeReference<List<Instance>>(){});
Datum<Instances> instancesDatum = new Datum<>();
instancesDatum.key = datum.key;
instancesDatum.timestamp.set(datum.timestamp.get());
Instances instances = new Instances();
instances.setInstanceMap(new HashMap<>(16));
for (Instance instance : instanceList) {
instances.getInstanceMap().put(instance.getDatumKey(), instance);
} }
for (Instance instance : datum.value) { return instancesDatum;
instanceMap.put(instance.getDatumKey(), instance);
} }
Datum<Map<String, Instance>> mapDatum = new Datum<>();
mapDatum.value = instanceMap;
mapDatum.key = datum.key;
mapDatum.timestamp.set(datum.timestamp.get());
return mapDatum;
} }
return JSON.parseObject(json, Datum.class); return JSON.parseObject(json, Datum.class);

View File

@ -46,7 +46,7 @@ import java.util.*;
* @author nkorange * @author nkorange
*/ */
@RestController @RestController
@RequestMapping(UtilsAndCommons.NACOS_NAMING_CONTEXT + UtilsAndCommons.NACOS_NAMING_CATALOG_CONTEXT) @RequestMapping(UtilsAndCommons.NACOS_NAMING_CONTEXT + "/catalog")
public class CatalogController { public class CatalogController {
@Autowired @Autowired

View File

@ -23,7 +23,7 @@ import com.alibaba.nacos.naming.core.DistroMapper;
import com.alibaba.nacos.naming.core.Instance; import com.alibaba.nacos.naming.core.Instance;
import com.alibaba.nacos.naming.core.Service; import com.alibaba.nacos.naming.core.Service;
import com.alibaba.nacos.naming.core.ServiceManager; import com.alibaba.nacos.naming.core.ServiceManager;
import com.alibaba.nacos.naming.healthcheck.HealthCheckMode; import com.alibaba.nacos.naming.healthcheck.HealthCheckType;
import com.alibaba.nacos.naming.misc.HttpClient; import com.alibaba.nacos.naming.misc.HttpClient;
import com.alibaba.nacos.naming.misc.Loggers; import com.alibaba.nacos.naming.misc.Loggers;
import com.alibaba.nacos.naming.misc.UtilsAndCommons; import com.alibaba.nacos.naming.misc.UtilsAndCommons;
@ -102,7 +102,7 @@ public class HealthController {
} else { } else {
Service service = serviceManager.getService(namespaceId, dom); Service service = serviceManager.getService(namespaceId, dom);
// Only health check "none" need update health status with api // Only health check "none" need update health status with api
if (service.getHealthCheckMode().equals(HealthCheckMode.none.name())) { if (HealthCheckType.NONE.name().equals(service.getClusterMap().get(clusterName).getHealthChecker().getType())) {
for (Instance instance : service.allIPs(Lists.newArrayList(clusterName))) { for (Instance instance : service.allIPs(Lists.newArrayList(clusterName))) {
if (instance.getIp().equals(ip) && instance.getPort() == port) { if (instance.getIp().equals(ip) && instance.getPort() == port) {
instance.setValid(valid); instance.setValid(valid);

View File

@ -33,6 +33,7 @@ import com.alibaba.nacos.naming.web.NeedAuth;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -45,6 +46,7 @@ import java.util.List;
* @author nkorange * @author nkorange
*/ */
@RestController @RestController
@RequestMapping(UtilsAndCommons.NACOS_NAMING_CONTEXT + "/operator")
public class OperatorController { public class OperatorController {
@Autowired @Autowired
@ -120,7 +122,7 @@ public class OperatorController {
} }
@RequestMapping("/metrics") @RequestMapping(value = "/metrics", method = RequestMethod.GET)
public JSONObject metrics(HttpServletRequest request) { public JSONObject metrics(HttpServletRequest request) {
JSONObject result = new JSONObject(); JSONObject result = new JSONObject();

View File

@ -78,7 +78,6 @@ public class ServiceController {
} }
float protectThreshold = NumberUtils.toFloat(WebUtils.optional(request, "protectThreshold", "0")); float protectThreshold = NumberUtils.toFloat(WebUtils.optional(request, "protectThreshold", "0"));
String healthCheckMode = WebUtils.optional(request, "healthCheckMode", switchDomain.defaultHealthCheckMode);
String metadata = WebUtils.optional(request, "metadata", StringUtils.EMPTY); String metadata = WebUtils.optional(request, "metadata", StringUtils.EMPTY);
String selector = WebUtils.optional(request, "selector", StringUtils.EMPTY); String selector = WebUtils.optional(request, "selector", StringUtils.EMPTY);
Map<String, String> metadataMap = new HashMap<>(16); Map<String, String> metadataMap = new HashMap<>(16);
@ -89,7 +88,6 @@ public class ServiceController {
Service domObj = new Service(); Service domObj = new Service();
domObj.setName(serviceName); domObj.setName(serviceName);
domObj.setProtectThreshold(protectThreshold); domObj.setProtectThreshold(protectThreshold);
domObj.setHealthCheckMode(healthCheckMode.toLowerCase());
domObj.setEnabled(true); domObj.setEnabled(true);
domObj.setMetadata(metadataMap); domObj.setMetadata(metadataMap);
domObj.setSelector(parseSelector(selector)); domObj.setSelector(parseSelector(selector));
@ -142,7 +140,6 @@ public class ServiceController {
res.put("name", serviceName); res.put("name", serviceName);
res.put("namespaceId", domain.getNamespaceId()); res.put("namespaceId", domain.getNamespaceId());
res.put("protectThreshold", domain.getProtectThreshold()); res.put("protectThreshold", domain.getProtectThreshold());
res.put("healthCheckMode", domain.getHealthCheckMode());
res.put("metadata", domain.getMetadata()); res.put("metadata", domain.getMetadata());
res.put("selector", domain.getSelector()); res.put("selector", domain.getSelector());

View File

@ -409,10 +409,6 @@ public class Service extends com.alibaba.nacos.api.naming.pojo.Service implement
resetWeight = vDom.getResetWeight(); resetWeight = vDom.getResetWeight();
} }
if (getHealthCheckMode().equals(vDom.getHealthCheckMode())) {
Loggers.SRV_LOG.info("[DOM-UPDATE] dom: {}, healthCheckMode: {} -> {}", getName(), getHealthCheckMode(), vDom.getHealthCheckMode());
}
if (enabled != vDom.getEnabled().booleanValue()) { if (enabled != vDom.getEnabled().booleanValue()) {
Loggers.SRV_LOG.info("[DOM-UPDATE] dom: {}, enabled: {} -> {}", getName(), enabled, vDom.getEnabled()); Loggers.SRV_LOG.info("[DOM-UPDATE] dom: {}, enabled: {} -> {}", getName(), enabled, vDom.getEnabled());
enabled = vDom.getEnabled(); enabled = vDom.getEnabled();

View File

@ -60,12 +60,11 @@ public class ClientBeatCheckTask implements Runnable {
@Override @Override
public void run() { public void run() {
try { try {
if (!domain.getHealthCheckMode().equals(HealthCheckMode.client.name()) || if (!getDistroMapper().responsible(domain.getName())) {
!getDistroMapper().responsible(domain.getName())) {
return; return;
} }
List<Instance> instances = domain.allIPs(); List<Instance> instances = domain.allIPs(true);
for (Instance instance : instances) { for (Instance instance : instances) {
if (System.currentTimeMillis() - instance.getLastBeat() > ClientBeatProcessor.CLIENT_BEAT_TIMEOUT) { if (System.currentTimeMillis() - instance.getLastBeat() > ClientBeatProcessor.CLIENT_BEAT_TIMEOUT) {

View File

@ -61,10 +61,6 @@ public class ClientBeatProcessor implements Runnable {
public void process() { public void process() {
Service service = this.service; Service service = this.service;
if (!service.getHealthCheckMode().equals(HealthCheckMode.client.name())) {
return;
}
Loggers.EVT_LOG.debug("[CLIENT-BEAT] processing beat: {}", rsInfo.toString()); Loggers.EVT_LOG.debug("[CLIENT-BEAT] processing beat: {}", rsInfo.toString());
String ip = rsInfo.getIp(); String ip = rsInfo.getIp();

View File

@ -108,10 +108,6 @@ public class HealthCheckCommon {
}, 500, TimeUnit.MILLISECONDS); }, 500, TimeUnit.MILLISECONDS);
} }
public boolean isHealthCheckEnabled(Service service) {
return service.getHealthCheckMode().equals(HealthCheckMode.server.name());
}
public void reEvaluateCheckRT(long checkRT, HealthCheckTask task, SwitchDomain.HealthParams params) { public void reEvaluateCheckRT(long checkRT, HealthCheckTask task, SwitchDomain.HealthParams params) {
task.setCheckRTLast(checkRT); task.setCheckRTLast(checkRT);

View File

@ -1,36 +0,0 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.nacos.naming.healthcheck;
/**
* Health check mode
*
* @author nkorange
*/
public enum HealthCheckMode {
/**
* Health check sent from server.
*/
server,
/**
* Health check sent from client.
*/
client,
/**
* Health check disabled.
*/
none
}

View File

@ -33,6 +33,9 @@ public class HealthCheckProcessorDelegate implements HealthCheckProcessor {
@Autowired @Autowired
private MysqlHealthCheckProcessor mysqlProcessor; private MysqlHealthCheckProcessor mysqlProcessor;
@Autowired
private NoneHealthCheckProcessor noneProcessor;
@Override @Override
public void process(HealthCheckTask task) { public void process(HealthCheckTask task) {
@ -53,7 +56,7 @@ public class HealthCheckProcessorDelegate implements HealthCheckProcessor {
return; return;
} }
throw new IllegalArgumentException("Unknown check type: " + type); noneProcessor.process(task);
} }
@Override @Override

View File

@ -19,7 +19,6 @@ import com.alibaba.fastjson.annotation.JSONField;
import com.alibaba.nacos.naming.boot.SpringContext; import com.alibaba.nacos.naming.boot.SpringContext;
import com.alibaba.nacos.naming.core.Cluster; import com.alibaba.nacos.naming.core.Cluster;
import com.alibaba.nacos.naming.core.DistroMapper; import com.alibaba.nacos.naming.core.DistroMapper;
import com.alibaba.nacos.naming.core.Service;
import com.alibaba.nacos.naming.misc.Loggers; import com.alibaba.nacos.naming.misc.Loggers;
import com.alibaba.nacos.naming.misc.SwitchDomain; import com.alibaba.nacos.naming.misc.SwitchDomain;
import org.apache.commons.lang3.RandomUtils; import org.apache.commons.lang3.RandomUtils;
@ -70,7 +69,8 @@ public class HealthCheckTask implements Runnable {
public void run() { public void run() {
try { try {
if (distroMapper.responsible(cluster.getDom().getName())) { if (distroMapper.responsible(cluster.getDom().getName()) &&
switchDomain.isHealthCheckEnabled(cluster.getDom().getName())) {
healthCheckProcessor.process(this); healthCheckProcessor.process(this);
Loggers.EVT_LOG.debug("[HEALTH-CHECK] schedule health check task: {}", cluster.getDom().getName()); Loggers.EVT_LOG.debug("[HEALTH-CHECK] schedule health check task: {}", cluster.getDom().getName());
} }
@ -92,7 +92,6 @@ public class HealthCheckTask implements Runnable {
this.setCheckRTLastLast(this.getCheckRTLast()); this.setCheckRTLastLast(this.getCheckRTLast());
Cluster cluster = this.getCluster(); Cluster cluster = this.getCluster();
if ((cluster.getDom()).getHealthCheckMode().equals(HealthCheckMode.server.name())) {
Loggers.CHECK_RT.info("{}:{}@{}->normalized: {}, worst: {}, best: {}, last: {}, diff: {}", Loggers.CHECK_RT.info("{}:{}@{}->normalized: {}, worst: {}, best: {}, last: {}, diff: {}",
cluster.getDom().getName(), cluster.getName(), cluster.getHealthChecker().getType(), cluster.getDom().getName(), cluster.getName(), cluster.getHealthChecker().getType(),
this.getCheckRTNormalized(), this.getCheckRTWorst(), this.getCheckRTBest(), this.getCheckRTNormalized(), this.getCheckRTWorst(), this.getCheckRTBest(),
@ -101,7 +100,6 @@ public class HealthCheckTask implements Runnable {
} }
} }
} }
}
public Cluster getCluster() { public Cluster getCluster() {
return cluster; return cluster;

View File

@ -30,5 +30,9 @@ public enum HealthCheckType {
/** /**
* MySQL type * MySQL type
*/ */
MYSQL MYSQL,
/**
* No check
*/
NONE
} }

View File

@ -90,9 +90,7 @@ public class HttpHealthCheckProcessor implements HealthCheckProcessor {
return; return;
} }
Service service = task.getCluster().getDom(); if (!switchDomain.isHealthCheckEnabled()) {
if (!switchDomain.isHealthCheckEnabled() || !service.getHealthCheckMode().equals(HealthCheckMode.server.name())) {
return; return;
} }

View File

@ -96,12 +96,6 @@ public class MysqlHealthCheckProcessor implements HealthCheckProcessor {
return; return;
} }
Service service = task.getCluster().getDom();
if (!healthCheckCommon.isHealthCheckEnabled(service)) {
return;
}
for (Instance ip : ips) { for (Instance ip : ips) {
try { try {

View File

@ -0,0 +1,23 @@
package com.alibaba.nacos.naming.healthcheck;
import org.springframework.stereotype.Component;
/**
* Health checker that does nothing
*
* @author nkorange
* @since 1.0.0
*/
@Component
public class NoneHealthCheckProcessor implements HealthCheckProcessor {
@Override
public void process(HealthCheckTask task) {
return;
}
@Override
public String getType() {
return "NONE";
}
}

View File

@ -113,10 +113,6 @@ public class TcpSuperSenseProcessor implements HealthCheckProcessor, Runnable {
} }
Service service = task.getCluster().getDom(); Service service = task.getCluster().getDom();
if (!healthCheckCommon.isHealthCheckEnabled(service)) {
return;
}
for (Instance ip : ips) { for (Instance ip : ips) {
if (ip.isMarked()) { if (ip.isMarked()) {

View File

@ -17,8 +17,6 @@ package com.alibaba.nacos.naming.misc;
import com.alibaba.fastjson.annotation.JSONField; import com.alibaba.fastjson.annotation.JSONField;
import com.alibaba.nacos.naming.consistency.DataListener; import com.alibaba.nacos.naming.consistency.DataListener;
import com.alibaba.nacos.naming.healthcheck.HealthCheckMode;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.*; import java.util.*;
@ -52,8 +50,6 @@ public class SwitchDomain implements DataListener<SwitchDomain> {
public boolean healthCheckEnabled = true; public boolean healthCheckEnabled = true;
public String defaultHealthCheckMode = HealthCheckMode.client.name();
public boolean distroEnabled = true; public boolean distroEnabled = true;
public boolean enableStandalone = true; public boolean enableStandalone = true;
@ -259,14 +255,6 @@ public class SwitchDomain implements DataListener<SwitchDomain> {
return healthCheckEnabled || getHealthCheckWhiteList().contains(dom); return healthCheckEnabled || getHealthCheckWhiteList().contains(dom);
} }
public String getDefaultHealthCheckMode() {
return defaultHealthCheckMode;
}
public void setDefaultHealthCheckMode(String defaultHealthCheckMode) {
this.defaultHealthCheckMode = defaultHealthCheckMode;
}
public boolean isDistroEnabled() { public boolean isDistroEnabled() {
return distroEnabled; return distroEnabled;
} }

View File

@ -235,17 +235,6 @@ public class SwitchManager {
return; return;
} }
if (entry.equals(SwitchEntry.DEFAULT_HEALTH_CHECK_MODE)) {
String defaultHealthCheckMode = value;
switchDomain.setDefaultHealthCheckMode(defaultHealthCheckMode);
if (!debug) {
consistencyService.put(UtilsAndCommons.getSwitchDomainKey(), JSON.toJSONString(switchDomain));
;
}
return;
}
if (entry.equals(SwitchEntry.DOM_STATUS_SYNC_PERIOD)) { if (entry.equals(SwitchEntry.DOM_STATUS_SYNC_PERIOD)) {
Long millis = Long.parseLong(value); Long millis = Long.parseLong(value);

View File

@ -15,8 +15,6 @@
*/ */
package com.alibaba.nacos.naming.core; package com.alibaba.nacos.naming.core;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.naming.healthcheck.HealthCheckMode;
import com.alibaba.nacos.naming.misc.UtilsAndCommons; import com.alibaba.nacos.naming.misc.UtilsAndCommons;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
@ -49,7 +47,6 @@ public class DomainTest {
Service newDomain = new Service(); Service newDomain = new Service();
newDomain.setName("nacos.domain.1"); newDomain.setName("nacos.domain.1");
newDomain.setHealthCheckMode(HealthCheckMode.client.name());
newDomain.setProtectThreshold(0.7f); newDomain.setProtectThreshold(0.7f);
Cluster cluster = new Cluster(); Cluster cluster = new Cluster();
cluster.setName(UtilsAndCommons.DEFAULT_CLUSTER_NAME); cluster.setName(UtilsAndCommons.DEFAULT_CLUSTER_NAME);
@ -58,7 +55,6 @@ public class DomainTest {
domain.update(newDomain); domain.update(newDomain);
Assert.assertEquals(HealthCheckMode.client.name(), domain.getHealthCheckMode());
Assert.assertEquals(0.7f, domain.getProtectThreshold(), 0.0001f); Assert.assertEquals(0.7f, domain.getProtectThreshold(), 0.0001f);
} }