Merge branch '0.3.0' of https://github.com/alibaba/nacos into 0.3.0

# Conflicts:
#	console/src/main/resources/static/src/menu.js
This commit is contained in:
hanxie 2018-10-16 14:23:01 +08:00
commit d3f59f3b87
46 changed files with 1603 additions and 636 deletions

View File

@ -36,6 +36,10 @@
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -125,4 +125,5 @@ public class Constants {
public static final String NAMING_INSTANCE_ID_SPLITTER = "#";
public static final int NAMING_INSTANCE_ID_SEG_COUNT = 4;
public static final String NAMING_HTTP_HEADER_SPILIER = "\\|";
}

View File

@ -15,6 +15,13 @@
*/
package com.alibaba.nacos.api.naming.pojo;
import com.alibaba.fastjson.annotation.JSONField;
import com.alibaba.nacos.api.common.Constants;
import org.apache.commons.lang3.StringUtils;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
@ -32,6 +39,14 @@ public abstract class AbstractHealthChecker implements Cloneable {
this.type = type;
}
/**
* Clone all fields of this instance to another one
*
* @return Another instance with exactly the same fields.
* @throws CloneNotSupportedException
*/
public abstract AbstractHealthChecker clone() throws CloneNotSupportedException;
public static class Http extends AbstractHealthChecker {
public static final String TYPE = "HTTP";
@ -68,6 +83,25 @@ public abstract class AbstractHealthChecker implements Cloneable {
this.headers = headers;
}
@JSONField(serialize = false)
public Map<String, String> getCustomHeaders() {
if (StringUtils.isBlank(headers)) {
return Collections.emptyMap();
}
Map<String, String> headerMap = new HashMap<String, String>(16);
for (String s : headers.split(Constants.NAMING_HTTP_HEADER_SPILIER)) {
String[] splits = s.split(":");
if (splits.length != 2) {
continue;
}
headerMap.put(StringUtils.trim(splits[0]), StringUtils.trim(splits[1]));
}
return headerMap;
}
@Override
public int hashCode() {
return Objects.hash(path, headers, expectedResponseCode);
@ -93,6 +127,18 @@ public abstract class AbstractHealthChecker implements Cloneable {
}
return expectedResponseCode == other.getExpectedResponseCode();
}
@Override
public Http clone() throws CloneNotSupportedException {
Http config = new Http();
config.setPath(this.getPath());
config.setHeaders(this.getHeaders());
config.setType(this.getType());
config.setExpectedResponseCode(this.getExpectedResponseCode());
return config;
}
}
public static class Tcp extends AbstractHealthChecker {
@ -112,6 +158,12 @@ public abstract class AbstractHealthChecker implements Cloneable {
return obj instanceof Tcp;
}
public Tcp clone() throws CloneNotSupportedException {
Tcp config = new Tcp();
config.setType(this.type);
return config;
}
}
public static class Mysql extends AbstractHealthChecker {
@ -173,9 +225,20 @@ public abstract class AbstractHealthChecker implements Cloneable {
return strEquals(cmd, other.getCmd());
}
@Override
public Mysql clone() throws CloneNotSupportedException {
Mysql config = new Mysql();
config.setUser(this.getUser());
config.setPwd(this.getPwd());
config.setCmd(this.getCmd());
config.setType(this.getType());
return config;
}
}
private static boolean strEquals(String str1, String str2) {
return str1 == null ? str2 == null : str1.equals(str2);
}
private static boolean strEquals(String str1, String str2) {
return str1 == null ? str2 == null : str1.equals(str2);
}
}

View File

@ -53,6 +53,8 @@ public class Instance {
@JSONField(name = "valid")
private boolean healthy = true;
private boolean enabled = true;
/**
* Cluster information of instance
*/
@ -146,6 +148,14 @@ public class Instance {
this.metadata.put(key, value);
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
@Override
public String toString() {
return JSON.toJSONString(this);

View File

@ -43,6 +43,8 @@ public class HostReactor {
private Map<String, ServiceInfo> serviceInfoMap;
private Map<String, Object> updatingMap;
private PushRecver pushRecver;
private EventDispatcher eventDispatcher;
@ -58,6 +60,7 @@ public class HostReactor {
this.serverProxy = serverProxy;
this.cacheDir = cacheDir;
this.serviceInfoMap = new ConcurrentHashMap<String, ServiceInfo>(DiskCache.read(this.cacheDir));
this.updatingMap = new ConcurrentHashMap<String, Object>();
this.failoverReactor = new FailoverReactor(this, cacheDir);
this.pushRecver = new PushRecver(this);
}
@ -218,12 +221,16 @@ public class HostReactor {
serviceInfoMap.put(serviceObj.getKey(), serviceObj);
updatingMap.put(serviceName, new Object());
if (allIPs) {
updateService4AllIPNow(serviceName, clusters, env);
} else {
updateServiceNow(serviceName, clusters, env);
}
} else if (serviceObj.getHosts().isEmpty()) {
updatingMap.remove(serviceName);
} else if (updatingMap.containsKey(serviceName)) {
if (updateHoldInterval > 0) {
// hold a moment waiting for update finish

View File

@ -158,6 +158,7 @@ public class NamingProxy {
params.put("ip", instance.getIp());
params.put("port", String.valueOf(instance.getPort()));
params.put("weight", String.valueOf(instance.getWeight()));
params.put("enable", String.valueOf(instance.isEnabled()));
params.put("healthy", String.valueOf(instance.isHealthy()));
params.put("metadata", JSON.toJSONString(instance.getMetadata()));
if (instance.getService() == null) {

View File

@ -110,7 +110,7 @@ window.i18ndoc = {
"nacos.component.CloneDialog.target_space": "目标空间:",
"com.alibaba.nacos.component.DiffEditorDialog.original_value": "原始值",
"com.alibaba.nacos.page.configurationManagement.environment": "地域:",
"nacos.page.configdetail.Description": "描述:",
"nacos.page.configdetail.Description": "描述",
"com.alibaba.nacos.page.pushTrajectory.Data_Id_can_not_be_empty": "Data ID不能为空",
"com.alibaba.nacos.page.listAllEnvironmental.Into_the": "进入",
"com.alibaba.nacos.page.listeningToQuery._Push_state": "推送状态",
@ -473,8 +473,42 @@ window.i18ndoc = {
"com.alibaba.nacos.page.serviceManagement.service_list": "服务列表",
"com.alibaba.nacos.page.serviceManagement.service_name": "服务名称",
"com.alibaba.nacos.page.serviceManagement.please_enter_the_service_name": "请输入服务名称",
"com.alibaba.nacos.page.serviceManagement.table.column.service_name": "服务名",
"com.alibaba.nacos.page.serviceManagement.table.column.cluster_count": "集群数目",
"com.alibaba.nacos.page.serviceManagement.table.column.ip_count": "IP数目",
"com.alibaba.nacos.page.serviceManagement.table.column.health_status": "健康实例/所有实例",
"com.alibaba.nacos.page.serviceManagement.table.column.operation": "操作",
"com.alibaba.nacos.page.serviceManagement.query": "查询",
"serviceManagement": "服务列表"
"serviceManagement": "服务列表",
"com.alibaba.nacos.page.serviceDetail.service_details": "服务详情",
"com.alibaba.nacos.page.serviceDetail.edit_service": "编辑服务",
"com.alibaba.nacos.page.serviceDetail.back": "返回",
"com.alibaba.nacos.page.serviceDetail.service_name": "服务名",
"com.alibaba.nacos.page.serviceDetail.protect_threshold": "保护阀值",
"com.alibaba.nacos.page.serviceDetail.health_check_pattern": "健康检查模式",
"com.alibaba.nacos.page.serviceDetail.health_check_pattern.service": "服务端",
"com.alibaba.nacos.page.serviceDetail.health_check_pattern.client": "客户端",
"com.alibaba.nacos.page.serviceDetail.health_check_pattern.none": "禁止",
"com.alibaba.nacos.page.serviceDetail.metadata": "元数据",
"com.alibaba.nacos.page.serviceDetail.update_service": "更新服务",
"com.alibaba.nacos.page.serviceDetail.cluster": "集群",
"com.alibaba.nacos.page.serviceDetail.edit_cluster": "集群配置",
"com.alibaba.nacos.page.serviceDetail.port": "端口",
"com.alibaba.nacos.page.serviceDetail.weight": "权重",
"com.alibaba.nacos.page.serviceDetail.healthy": "健康状态",
"com.alibaba.nacos.page.serviceDetail.operation": "操作",
"com.alibaba.nacos.page.serviceDetail.editor": "编辑",
"com.alibaba.nacos.page.serviceDetail.offline": "下线",
"com.alibaba.nacos.page.serviceDetail.online": "上线",
"com.alibaba.nacos.page.serviceDetail.check_type": "检查类型",
"com.alibaba.nacos.page.serviceDetail.check_port": "检查端口",
"com.alibaba.nacos.page.serviceDetail.use_ip_port_check": "使用IP端口检查",
"com.alibaba.nacos.page.serviceDetail.check_path": "检查路径",
"com.alibaba.nacos.page.serviceDetail.check_headers": "检查头",
"com.alibaba.nacos.page.serviceDetail.update_cluster": "更新集群",
"com.alibaba.nacos.page.serviceDetail.update_instance": "编辑实例",
"com.alibaba.nacos.page.serviceDetail.whether_online": "是否上线",
"serviceDetail": "服务详情"
},
"en-us": {
"com.alibaba.nacos.layout.noenv.nacosversion":"1.0",
@ -949,7 +983,41 @@ window.i18ndoc = {
"com.alibaba.nacos.page.serviceManagement.service_list": "Service List",
"com.alibaba.nacos.page.serviceManagement.service_name": "Service Name",
"com.alibaba.nacos.page.serviceManagement.please_enter_the_service_name": "Enter Service Name",
"com.alibaba.nacos.page.serviceManagement.table.column.service_name": "Service Name",
"com.alibaba.nacos.page.serviceManagement.table.column.cluster_count": "Cluster Count",
"com.alibaba.nacos.page.serviceManagement.table.column.ip_count": "IP Count",
"com.alibaba.nacos.page.serviceManagement.table.column.health_status": "Healthy Instances/All Instances",
"com.alibaba.nacos.page.serviceManagement.table.column.operation": "Operation",
"com.alibaba.nacos.page.serviceManagement.query": "Search",
"serviceManagement": "Service Management"
"serviceManagement": "Service Management",
"com.alibaba.nacos.page.serviceDetail.service_details": "Service Details",
"com.alibaba.nacos.page.serviceDetail.edit_service": "Edit Service",
"com.alibaba.nacos.page.serviceDetail.back": "Back",
"com.alibaba.nacos.page.serviceDetail.service_name": "Service Name",
"com.alibaba.nacos.page.serviceDetail.protect_threshold": "Protect Threshold",
"com.alibaba.nacos.page.serviceDetail.health_check_pattern": "Health check pattern",
"com.alibaba.nacos.page.serviceDetail.health_check_pattern.service": "Service",
"com.alibaba.nacos.page.serviceDetail.health_check_pattern.client": "Client",
"com.alibaba.nacos.page.serviceDetail.health_check_pattern.none": "None",
"com.alibaba.nacos.page.serviceDetail.metadata": "Metadata",
"com.alibaba.nacos.page.serviceDetail.update_service": "Update Service",
"com.alibaba.nacos.page.serviceDetail.cluster": "Cluster",
"com.alibaba.nacos.page.serviceDetail.edit_cluster": "Edit Cluster",
"com.alibaba.nacos.page.serviceDetail.port": "Port",
"com.alibaba.nacos.page.serviceDetail.weight": "Weight",
"com.alibaba.nacos.page.serviceDetail.healthy": "Healthy",
"com.alibaba.nacos.page.serviceDetail.operation": "Operation",
"com.alibaba.nacos.page.serviceDetail.editor": "Edit",
"com.alibaba.nacos.page.serviceDetail.offline": "Offline",
"com.alibaba.nacos.page.serviceDetail.online": "Online",
"com.alibaba.nacos.page.serviceDetail.check_type": "Check Type",
"com.alibaba.nacos.page.serviceDetail.check_port": "Check Port",
"com.alibaba.nacos.page.serviceDetail.use_ip_port_check": "Use port of IP",
"com.alibaba.nacos.page.serviceDetail.check_path": "Check Path",
"com.alibaba.nacos.page.serviceDetail.check_headers": "Check Headers",
"com.alibaba.nacos.page.serviceDetail.update_cluster": "Update Cluster",
"com.alibaba.nacos.page.serviceDetail.update_instance": "Update Instance",
"com.alibaba.nacos.page.serviceDetail.whether_online": "Whether Online",
"serviceDetail": "Service Details"
}
}

View File

@ -24,7 +24,7 @@
height: 30px;
line-height: 30px;
text-align: center;
}
.righttitle {
height: 40px;
@ -423,7 +423,7 @@ form.vertical-margin-lg .form-group {
}*/
.xrange-container {
position: relative;
position: relative;
border: 1px solid #ccc;
margin: 0;
padding: 0;
@ -464,7 +464,7 @@ form.vertical-margin-lg .form-group {
min-width: 100px;
display: inline-flex;
margin-bottom: 8px;
}
.xrange-layer {
@ -493,7 +493,7 @@ form.vertical-margin-lg .form-group {
display: flex;
flex-direction: column;
justify-content: space-around;
box-sizing: content-box !important;
box-sizing: content-box !important;
align-items: center; }
.xrange-panel .quick-list > span {
flex: 0 0 auto;
@ -1037,6 +1037,9 @@ form.vertical-margin-lg .form-group {
margin: 10px 0 10px 0;
}
.alert-success {
border-color: #E0E0E0!important;
border-color: #E0E0E0!important;
}
.main-container{
padding: 10px;
}

View File

@ -232,7 +232,7 @@ class HistoryRollback extends React.Component {
return (
<div style={{ padding: 10 }}>
<Loading shape="flower" style={{ position: 'relative', width: "100%" }} visible={this.state.loading} tip="Loading..." color="#333">
<RegionGroup left={<h5 style={{ borderLeft: '2px solid rgb(136, 183, 224)', textIndent: 8, lineHeight: '32px', marginTop: 8, fontSize: '16px' }}>{window.aliwareIntl.get('com.alibaba.nacos.page.historyRollback.to_configure')}</h5>} namespaceCallBack={this.cleanAndGetData.bind(this)} />
<RegionGroup left={window.aliwareIntl.get('com.alibaba.nacos.page.historyRollback.to_configure')} namespaceCallBack={this.cleanAndGetData.bind(this)} />
{/**<div className={'namespacewrapper'}>
<NameSpaceList namespaceCallBack={this.cleanAndGetData.bind(this)} />
</div>**/}

View File

@ -31,7 +31,9 @@ class Namespace extends React.Component {
url: `/nacos/v1/console/namespaces`,
success: res => {
if (res.code === 200) {
let data = res.data;
let data = res.data || [];
window.namespaceList = data;
for (var i = 0; i < data.length; i++) {
if (data[i].type === 1) {
this.setState({
@ -86,6 +88,7 @@ class Namespace extends React.Component {
success: res => {
if (res !== null) {
Dialog.alert({
style: { width: "500px" },
needWrapper: false,
language: window.pageLanguage || 'zh-cn',
title: window.aliwareIntl.get('nacos.page.namespace.Namespace_details'),
@ -245,7 +248,7 @@ class Namespace extends React.Component {
<div>
<div style={{ textAlign: 'right', marginBottom: 10 }}>
<Button type="primary" style={{ marginRight: 20, marginTop: 10 }} onClick={this.addNameSpace.bind(this)}>{window.aliwareIntl.get('com.alibaba.nacos.page.namespace.add')}</Button>
<Button type="primary" style={{ marginRight: 0, marginTop: 10 }} onClick={this.addNameSpace.bind(this)}>{window.aliwareIntl.get('com.alibaba.nacos.page.namespace.add')}</Button>
</div>
<div>
<Table dataSource={this.state.dataSource} locale={locale} language={window.aliwareIntl.currentLanguageCode}>

View File

@ -0,0 +1,155 @@
import React from 'react';
import {Dialog, Form, Input, Switch, Select, Message} from '@alifd/next';
import {I18N, DIALOG_FORM_LAYOUT} from './constant'
const FormItem = Form.Item;
const Option = Select.Option
/*****************************此行为标记行, 请勿删和修改此行, 文件和组件依赖请写在此行上面, 主体代码请写在此行下面的class中*****************************/
class EditClusterDialog extends React.Component {
constructor(props) {
super(props);
this.state = {
editCluster: {},
editClusterDialogVisible: false
}
this.show = this.show.bind(this)
}
show(editCluster) {
const {metadata = {}} = editCluster
editCluster.metadataText = Object.keys(metadata).map(k => `${k}=${metadata[k]}`).join(',')
this.setState({
editCluster,
editClusterDialogVisible: true
})
}
hide() {
this.setState({editClusterDialogVisible: false})
}
onConfirm() {
const {openLoading, closeLoading, getServiceDetail} = this.props
const {name, serviceName, metadataText, defaultCheckPort, useIPPort4Check, healthChecker} = this.state.editCluster
window.request({
method: 'POST',
url: '/nacos/v1/ns/cluster/update',
data: {
serviceName,
clusterName: name,
metadata: metadataText,
checkPort: defaultCheckPort,
useInstancePort4Check: useIPPort4Check,
healthChecker: JSON.stringify(healthChecker)
},
dataType: 'text',
beforeSend: () => openLoading(),
success: res => {
if (res !== 'ok') {
Message.error(res)
return
}
this.hide()
getServiceDetail()
},
complete: () => closeLoading()
})
}
onChangeCluster(changeVal) {
const {editCluster = {}} = this.state
this.setState({
editCluster: Object.assign({}, editCluster, changeVal)
})
}
render() {
const {editCluster = {}, editClusterDialogVisible} = this.state
const {
healthChecker = {},
useIPPort4Check,
defaultCheckPort = '80',
metadataText = ''
} = editCluster
const {type, path, headers} = healthChecker
const healthCheckerChange = changeVal => this.onChangeCluster({
healthChecker: Object.assign({}, healthChecker, changeVal)
})
return (
<Dialog
className="cluster-edit-dialog"
title={I18N.UPDATE_CLUSTER}
visible={editClusterDialogVisible}
onOk={() => this.onConfirm()}
onCancel={() => this.hide()}
onClose={() => this.hide()}
>
<Form {...DIALOG_FORM_LAYOUT}>
<FormItem label={`${I18N.CHECK_TYPE}:`}>
<Select
className="in-select"
defaultValue={type}
onChange={type => healthCheckerChange({type})}
>
<Option value="TCP">TCP</Option>
<Option value="HTTP">HTTP</Option>
</Select>
</FormItem>
<FormItem label={`${I18N.CHECK_PORT}:`}>
<Input className="in-text"
value={defaultCheckPort}
onChange={defaultCheckPort => this.onChangeCluster({defaultCheckPort})}
/>
</FormItem>
<FormItem label={`${I18N.USE_IP_PORT_CHECK}:`}>
<Switch
checked={useIPPort4Check}
onChange={useIPPort4Check => this.onChangeCluster({useIPPort4Check})}
/>
</FormItem>
{
type === 'HTTP'
? (<div>
<div className="next-row next-form-item next-left next-medium">
<div className="next-col next-col-fixed-12 next-form-item-label">
<label>{`${I18N.CHECK_PATH}:`}</label>
</div>
<div className="next-col next-col-12 next-form-item-control">
<Input
className="in-text"
value={path}
onChange={path => healthCheckerChange({path})}
/>
</div>
</div>
<div className="next-row next-form-item next-left next-medium">
<div className="next-col next-col-fixed-12 next-form-item-label">
<label>{`${I18N.CHECK_HEADERS}:`}</label>
</div>
<div className="next-col next-col-12 next-form-item-control">
<Input
className="in-text"
value={headers}
onChange={headers => healthCheckerChange({headers})}
/>
</div>
</div>
</div>)
: null
}
<FormItem label={`${I18N.METADATA}:`}>
<Input
className="in-text"
value={metadataText}
onChange={metadataText => this.onChangeCluster({metadataText})}
/>
</FormItem>
</Form>
</Dialog>
)
}
}
/*****************************此行为标记行, 请勿删和修改此行, 主体代码请写在此行上面的class中, 组件导出语句及其他信息请写在此行下面*****************************/
export default EditClusterDialog;

View File

@ -0,0 +1,102 @@
import React from 'react';
import {Dialog, Form, Input, Switch, Message} from '@alifd/next';
import {I18N, DIALOG_FORM_LAYOUT} from './constant'
const FormItem = Form.Item;
/*****************************此行为标记行, 请勿删和修改此行, 文件和组件依赖请写在此行上面, 主体代码请写在此行下面的class中*****************************/
class EditInstanceDialog extends React.Component {
constructor(props) {
super(props);
this.state = {
editInstance: {},
editInstanceDialogVisible: false
}
this.show = this.show.bind(this)
}
show(editInstance) {
const {metadata = {}} = editInstance
if (Object.keys(metadata).length) {
editInstance.metadataText = Object.keys(metadata).map(k => `${k}=${metadata[k]}`).join(',')
}
this.setState({editInstance, editInstanceDialogVisible: true})
}
hide() {
this.setState({editInstanceDialogVisible: false})
}
onConfirm() {
const {serviceName, clusterName, getInstanceList, openLoading, closeLoading} = this.props
const {ip, port, weight, enabled, metadataText} = this.state.editInstance
window.request({
method: 'POST',
url: '/nacos/v1/ns/instance/update',
data: {serviceName, clusterName, ip, port, weight, enable: enabled, metadata: metadataText},
dataType: 'text',
beforeSend: () => openLoading(),
success: res => {
if (res !== 'ok') {
Message.error(res)
return
}
this.hide()
getInstanceList()
},
complete: () => closeLoading()
})
}
onChangeCluster(changeVal) {
const {editInstance = {}} = this.state
this.setState({
editInstance: Object.assign({}, editInstance, changeVal)
})
}
render() {
const {editInstanceDialogVisible, editInstance} = this.state
return (
<Dialog
className="instance-edit-dialog"
title={I18N.UPDATE_INSTANCE}
visible={editInstanceDialogVisible}
onOk={() => this.onConfirm()}
onCancel={() => this.hide()}
onClose={() => this.hide()}
>
<Form {...DIALOG_FORM_LAYOUT}>
<FormItem label="IP:">
<p>{editInstance.ip}</p>
</FormItem>
<FormItem label={`${I18N.PORT}:`}>
<p>{editInstance.port}</p>
</FormItem>
<FormItem label={`${I18N.WEIGHT}:`}>
<Input
className="in-text"
value={editInstance.weight}
onChange={weight => this.onChangeCluster({weight})}
/>
</FormItem>
<FormItem label={`${I18N.WHETHER_ONLINE}:`}>
<Switch
checked={editInstance.enabled}
onChange={enabled => this.onChangeCluster({enabled})}/>
</FormItem>
<FormItem label={`${I18N.METADATA}:`}>
<Input
className="in-text"
value={editInstance.metadataText}
onChange={metadataText => this.onChangeCluster({metadataText})}
/>
</FormItem>
</Form>
</Dialog>
)
}
}
/*****************************此行为标记行, 请勿删和修改此行, 主体代码请写在此行上面的class中, 组件导出语句及其他信息请写在此行下面*****************************/
export default EditInstanceDialog;

View File

@ -0,0 +1,112 @@
import React from 'react';
import {Dialog, Form, Input, Select, Message} from '@alifd/next';
import {I18N, DIALOG_FORM_LAYOUT} from './constant'
const FormItem = Form.Item;
const Option = Select.Option
/*****************************此行为标记行, 请勿删和修改此行, 文件和组件依赖请写在此行上面, 主体代码请写在此行下面的class中*****************************/
class EditServiceDialog extends React.Component {
constructor(props) {
super(props);
this.state = {
editService: {},
editServiceDialogVisible: false
}
this.show = this.show.bind(this)
}
show(editService) {
const {metadata = {}} = editService
if (Object.keys(metadata).length) {
editService.metadataText = Object.keys(metadata).map(k => `${k}=${metadata[k]}`).join(',')
}
this.setState({editService, editServiceDialogVisible: true})
}
hide() {
this.setState({editServiceDialogVisible: false})
}
onConfirm() {
const editService = Object.assign({}, this.state.editService)
const {name, protectThreshold, healthCheckMode, metadataText} = editService
window.request({
method: 'POST',
url: '/nacos/v1/ns/service/update',
data: {serviceName: name, protectThreshold, healthCheckMode, metadata: metadataText},
dataType: 'text',
beforeSend: () => this.setState({loading: true}),
success: res => {
if (res !== 'ok') {
Message.error(res)
return
}
this.props.getServiceDetail()
},
complete: () => this.setState({loading: false})
})
this.hide()
}
onChangeCluster(changeVal) {
const {editService = {}} = this.state
this.setState({
editService: Object.assign({}, editService, changeVal)
})
}
render() {
const {editService, editServiceDialogVisible} = this.state
const {
name,
protectThreshold,
healthCheckMode,
metadataText
} = editService
return (
<Dialog
className="service-detail-edit-dialog"
title={I18N.UPDATE_SERVICE}
visible={editServiceDialogVisible}
onOk={() => this.onConfirm()}
onCancel={() => this.hide()}
onClose={() => this.hide()}
>
<Form {...DIALOG_FORM_LAYOUT}>
<FormItem label={`${I18N.SERVICE_NAME}:`}>
<p>{name}</p>
</FormItem>
<FormItem label={`${I18N.PROTECT_THRESHOLD}:`}>
<Input
className="in-text"
value={protectThreshold}
onChange={protectThreshold => this.onChangeCluster({protectThreshold})}
/>
</FormItem>
<FormItem label={`${I18N.HEALTH_CHECK_PATTERN}:`}>
<Select
className="in-select"
defaultValue={healthCheckMode}
onChange={healthCheckMode => this.onChangeCluster({healthCheckMode})}
>
<Option value="server">{I18N.HEALTH_CHECK_PATTERN_SERVICE}</Option>
<Option value="client">{I18N.HEALTH_CHECK_PATTERN_CLIENT}</Option>
<Option value="none">{I18N.HEALTH_CHECK_PATTERN_NONE}</Option>
</Select>
</FormItem>
<FormItem label={`${I18N.METADATA}:`}>
<Input
className="in-text"
value={metadataText}
onChange={metadataText => this.onChangeCluster({metadataText})}
/>
</FormItem>
</Form>
</Dialog>
)
}
}
/*****************************此行为标记行, 请勿删和修改此行, 主体代码请写在此行上面的class中, 组件导出语句及其他信息请写在此行下面*****************************/
export default EditServiceDialog;

View File

@ -0,0 +1,132 @@
import React from 'react';
import {Button, Pagination, Table} from '@alifd/next';
import {I18N} from './constant'
import EditInstanceDialog from "./EditInstanceDialog";
/*****************************此行为标记行, 请勿删和修改此行, 文件和组件依赖请写在此行上面, 主体代码请写在此行下面的class中*****************************/
class InstanceTable extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: false,
instance: {count: 0, list: []},
pageNum: 1,
pageSize: 10
}
}
componentDidMount() {
this.getInstanceList()
}
openLoading() {
this.setState({loading: true})
}
closeLoading() {
this.setState({loading: false})
}
getInstanceList() {
const {clusterName, serviceName} = this.props
if (!clusterName) return
const {pageSize, pageNum} = this.state
window.request({
url: '/nacos/v1/ns/catalog/instanceList',
data: {
serviceName,
clusterName,
pgSize: pageSize,
startPg: pageNum
},
beforeSend: () => this.openLoading(),
success: instance => this.setState({instance}),
complete: () => this.closeLoading()
})
}
openInstanceDialog(instance) {
this.refs.editInstanceDialog.show(instance)
}
switchState(index, record) {
const {instance} = this.state
const {ip, port, weight, enabled} = record
const {clusterName, serviceName} = this.props
const newVal = Object.assign({}, instance)
newVal.list[index]['enabled'] = !enabled
window.request({
method: 'POST',
url: '/nacos/v1/ns/instance/update',
data: {serviceName, clusterName, ip, port, weight, enable: !enabled},
dataType: 'text',
beforeSend: () => this.openLoading(),
success: () => this.setState({instance: newVal}),
complete: () => this.closeLoading()
})
}
onChangePage(pageNum) {
this.setState({pageNum}, () => this.getInstanceList())
}
render() {
const {clusterName, serviceName} = this.props
const {instance, pageSize, loading} = this.state
return instance.count ? (
<div>
<Table dataSource={instance.list} loading={loading}>
<Table.Column title="IP" dataIndex="ip"/>
<Table.Column title={I18N.PORT} dataIndex="port"/>
<Table.Column title={I18N.WEIGHT} dataIndex="weight"/>
<Table.Column title={I18N.HEALTHY} dataIndex="healthy" cell={val => `${val}`}/>
<Table.Column
title={I18N.METADATA}
dataIndex="metadata"
cell={metadata => Object.keys(metadata).map(k => `${k}=${metadata[k]}`).join(',')}
/>
<Table.Column
title={I18N.OPERATION}
width={150}
cell={(value, index, record) => (
<div>
<Button
type="normal"
className="edit-btn"
onClick={() => this.openInstanceDialog(record)}
>{I18N.EDITOR}</Button>
<Button
type={record.enabled ? 'normal' : 'secondary'}
onClick={() => this.switchState(index, record)}
>{I18N[record.enabled ? 'OFFLINE' : 'ONLINE']}</Button>
</div>
)}/>
</Table>
{
instance.count > pageSize
? (
<Pagination
className="pagination"
total={instance.count}
pageSize={pageSize}
onChange={currentPage => this.onChangePage(currentPage)}
/>
)
: null
}
<EditInstanceDialog
ref="editInstanceDialog"
serviceName={serviceName}
clusterName={clusterName}
openLoading={()=>this.openLoading()}
closeLoading={()=>this.closeLoading()}
getInstanceList={() => this.getInstanceList()}
/>
</div>
) : null
}
}
/*****************************此行为标记行, 请勿删和修改此行, 主体代码请写在此行上面的class中, 组件导出语句及其他信息请写在此行下面*****************************/
export default InstanceTable;

View File

@ -0,0 +1,151 @@
import React from 'react';
import {Button, Card, Form, Loading} from '@alifd/next';
import EditServiceDialog from './EditServiceDialog'
import EditClusterDialog from './EditClusterDialog'
import InstanceTable from './InstanceTable'
import queryString from 'query-string'
import {I18N} from './constant'
import './ServiceDetail.less'
const FormItem = Form.Item;
const pageFormLayout = {
labelCol: {fixedSpan: 10},
wrapperCol: {span: 14}
};
/*****************************此行为标记行, 请勿删和修改此行, 文件和组件依赖请写在此行上面, 主体代码请写在此行下面的class中*****************************/
class ServiceDetail extends React.Component {
constructor(props) {
super(props);
this.state = {
serviceName: queryString.parse(props.location.search).name,
loading: false,
currentPage: 1,
clusters: [],
instances: {},
service: {},
pageSize: 10,
pageNum: {}
}
}
componentDidMount() {
if (!this.state.serviceName) {
this.props.history.goBack()
return
}
this.getServiceDetail()
}
getServiceDetail() {
const {serviceName} = this.state
window.request({
url: `/nacos/v1/ns/catalog/serviceDetail?serviceName=${serviceName}`,
beforeSend: () => this.openLoading(),
success: ({clusters = [], service = {}}) => this.setState({service, clusters}),
complete: () => this.closeLoading()
})
}
openLoading() {
this.setState({loading: true})
}
closeLoading() {
this.setState({loading: false})
}
openEditServiceDialog() {
this.refs.editServiceDialog.show(this.state.service)
}
openClusterDialog(cluster) {
this.refs.editClusterDialog.show(cluster)
}
render() {
const {serviceName, loading, service = {}, clusters} = this.state
const {metadata = {}} = service
const metadataText = Object.keys(metadata).map(key => `${key}=${metadata[key]}`).join(',')
return (
<div className="main-container service-detail">
<Loading
shape={"flower"}
tip={"Loading..."}
className="loading"
visible={loading}
color={"#333"}
>
<h1 style={{
position: 'relative',
width: '100%'
}}>
{I18N.SERVICE_DETAILS}
<Button
type="primary"
className="header-btn"
onClick={() => this.props.history.goBack()}
>{I18N.BACK}</Button>
<Button
type="normal"
className="header-btn"
onClick={() => this.openEditServiceDialog()}
>{I18N.EDIT_SERVICE}</Button>
</h1>
<Form style={{width: '60%'}} {...pageFormLayout}>
<FormItem label={`${I18N.SERVICE_NAME}:`}>
<p>{service.name}</p>
</FormItem>
<FormItem label={`${I18N.PROTECT_THRESHOLD}:`}>
<p>{service.protectThreshold}</p>
</FormItem>
<FormItem label={`${I18N.HEALTH_CHECK_PATTERN}:`}>
<p>{service.healthCheckMode}</p>
</FormItem>
<FormItem label={`${I18N.METADATA}:`}>
<p>{metadataText}</p>
</FormItem>
</Form>
{
clusters.map(cluster => (
<Card
key={cluster.name}
className="cluster-card"
title={`${I18N.CLUSTER}:`}
subTitle={cluster.name}
contentHeight="auto"
extra={(
<Button
type="normal"
onClick={() => this.openClusterDialog(cluster)}
>{I18N.EDIT_CLUSTER}</Button>
)}
>
<InstanceTable
clusterName={cluster.name}
serviceName={serviceName}
/>
</Card>
))
}
</Loading>
<EditServiceDialog
ref="editServiceDialog"
openLoading={() => this.openLoading()}
closeLoading={() => this.closeLoading()}
getServiceDetail={() => this.getServiceDetail()}
/>
<EditClusterDialog
ref="editClusterDialog"
openLoading={() => this.openLoading()}
closeLoading={() => this.closeLoading()}
getServiceDetail={() => this.getServiceDetail()}
/>
</div>
);
}
}
/*****************************此行为标记行, 请勿删和修改此行, 主体代码请写在此行上面的class中, 组件导出语句及其他信息请写在此行下面*****************************/
export default ServiceDetail;

View File

@ -0,0 +1,39 @@
.service-detail {
.header-btn {
float: right;
margin-left: 20px;
}
.edit-btn {
margin-right: 10px;
}
.next-form-item {
margin-bottom: 10px;
}
.loading {
position: relative;
width: 100%
}
.pagination {
float: right;
margin-top: 15px;
}
.cluster-card {
margin-bottom: 30px;
}
}
.service-detail-edit-dialog, .instance-edit-dialog, .cluster-edit-dialog {
.next-form-item {
margin-bottom: 10px;
}
.next-col-fixed-12 {
flex: 1;
}
.next-switch-off {
background-color: #F2F3F7;
border-color: #C4C6CF;
}
.in-text, .in-select {
width: 120px;
}
}

View File

@ -0,0 +1,119 @@
const getI18N = (key, prefix = 'com.alibaba.nacos.page.serviceDetail.') => window.aliwareIntl.get(prefix + key)
export const I18N = {}
/**
* 服务列表
*/
I18N.SERVICE_DETAILS = getI18N('service_details')
/**
* 编辑服务
*/
I18N.EDIT_SERVICE = getI18N('edit_service')
/**
* 返回
*/
I18N.BACK = getI18N('back')
/**
* 服务名
*/
I18N.SERVICE_NAME = getI18N('service_name')
/**
* 保护阀值
*/
I18N.PROTECT_THRESHOLD = getI18N('protect_threshold')
/**
* 健康检查模式
*/
I18N.HEALTH_CHECK_PATTERN = getI18N('health_check_pattern')
/**
* 健康检查模式 - 服务端
*/
I18N.HEALTH_CHECK_PATTERN_SERVICE = getI18N('health_check_pattern.service')
/**
* 健康检查模式 - 客户端
*/
I18N.HEALTH_CHECK_PATTERN_CLIENT = getI18N('health_check_pattern.client')
/**
* 健康检查模式 - 禁止
*/
I18N.HEALTH_CHECK_PATTERN_NONE = getI18N('health_check_pattern.none')
/**
* 元数据
*/
I18N.METADATA = getI18N('metadata')
/**
* 更新服务
*/
I18N.UPDATE_SERVICE = getI18N('update_service')
/**
* 集群
*/
I18N.CLUSTER = getI18N('cluster')
/**
* 端口
*/
I18N.PORT = getI18N('port')
/**
* 权重
*/
I18N.WEIGHT = getI18N('weight')
/**
* 健康状态
*/
I18N.HEALTHY = getI18N('healthy')
/**
* 操作
*/
I18N.OPERATION = getI18N('operation')
/**
* 编辑
*/
I18N.EDITOR = getI18N('editor')
/**
* 上线
*/
I18N.ONLINE = getI18N('online')
/**
* 下线
*/
I18N.OFFLINE = getI18N('offline')
/**
* 集群配置
*/
I18N.EDIT_CLUSTER = getI18N('edit_cluster')
/**
* 检查类型
*/
I18N.CHECK_TYPE = getI18N('check_type')
/**
* 检查端口
*/
I18N.CHECK_PORT = getI18N('check_port')
/**
* 使用IP端口检查
*/
I18N.USE_IP_PORT_CHECK = getI18N('use_ip_port_check')
/**
* 检查路径
*/
I18N.CHECK_PATH = getI18N('check_path')
/**
* 检查头
*/
I18N.CHECK_HEADERS = getI18N('check_headers')
/**
* 更新集群
*/
I18N.UPDATE_CLUSTER = getI18N('update_cluster')
/**
* 编辑实例
*/
I18N.UPDATE_INSTANCE = getI18N('update_instance')
/**
* 是否上线
*/
I18N.WHETHER_ONLINE = getI18N('whether_online')
export const DIALOG_FORM_LAYOUT = {
labelCol: {fixedSpan: 12},
wrapperCol: {span: 12}
}

View File

@ -0,0 +1,3 @@
import ServiceDetail from './ServiceDetail'
export default ServiceDetail

View File

@ -1,33 +1,13 @@
import React from 'react';
import RegionGroup from '../components/RegionGroup' ;
import RegionGroup from '../../components/RegionGroup' ;
import {Button, Field, Form, Grid, Input, Loading, Pagination, Table} from '@alifd/next';
import {I18N, STATUS_COLOR_MAPPING} from './constant'
import './ServiceManagement.less'
const FormItem = Form.Item;
const {Row, Col} = Grid;
const {Column} = Table
const getI18N = (key, prefix = 'com.alibaba.nacos.page.serviceManagement.') => window.aliwareIntl.get(prefix + key)
/**
* 服务列表
*/
const I18N_SERVICE_LIST = getI18N('service_list')
/**
* 服务名称
*/
const I18N_SERVICE_NAME = getI18N('service_name')
/**
* 请输入服务名称
*/
const I18N_ENTER_SERVICE_NAME = getI18N('please_enter_the_service_name')
/**
* 查询
*/
const I18N_QUERY = window.aliwareIntl.get('com.alibaba.nacos.page.serviceManagement.query')
/**
* 查询
*/
const I18N_PUBNODEDATA = window.aliwareIntl.get('pubnodedata', '')
/*****************************此行为标记行, 请勿删和修改此行, 文件和组件依赖请写在此行上面, 主体代码请写在此行下面的class中*****************************/
class ServiceManagement extends React.Component {
constructor(props) {
@ -43,9 +23,6 @@ class ServiceManagement extends React.Component {
this.field = new Field(this);
}
componentDidMount() {
}
openLoading() {
this.setState({loading: true})
}
@ -77,15 +54,17 @@ class ServiceManagement extends React.Component {
setTimeout(() => this.queryServiceList());
}
rowColor = ({status}) => ({className: `row-bg-${STATUS_COLOR_MAPPING[status]}`})
render() {
const {keyword} = this.state
const {init, getValue} = this.field;
this.init = init;
this.getValue = getValue;
const locale = {empty: I18N_PUBNODEDATA}
const locale = {empty: I18N.PUBNODEDATA}
return (
<div style={{padding: 10}}>
<div className="main-container service-management">
<Loading
shape="flower"
style={{position: 'relative'}}
@ -94,15 +73,15 @@ class ServiceManagement extends React.Component {
color="#333"
>
<RegionGroup
left={I18N_SERVICE_LIST}
left={I18N.SERVICE_LIST}
namespaceCallBack={this.getQueryLater.bind(this)}
/>
<Row className="demo-row" style={{marginBottom: 10, padding: 0}}>
<Col span="24">
<Form inline field={this.field}>
<FormItem label={I18N_SERVICE_NAME}>
<FormItem label={I18N.SERVICE_NAME}>
<Input
placeholder={I18N_ENTER_SERVICE_NAME}
placeholder={I18N.ENTER_SERVICE_NAME}
style={{width: 200}}
value={keyword}
onChange={keyword => this.setState({keyword})}
@ -113,7 +92,7 @@ class ServiceManagement extends React.Component {
type="primary"
onClick={() => this.setState({currentPage: 1}, () => this.queryServiceList())}
style={{marginRight: 10}}
>{I18N_QUERY}</Button>
>{I18N.QUERY}</Button>
</FormItem>
</Form>
</Col>
@ -127,13 +106,17 @@ class ServiceManagement extends React.Component {
locale={locale}
language={window.aliwareIntl.currentLanguageCode}
className={r => this.rowColor[r.status]}
getRowProps={this.rowColor}
>
<Column title="服务名" dataIndex="name"/>
<Column title="集群数目" dataIndex="clusterCount"/>
<Column title="实例数目" dataIndex="ipCount"/>
<Column title="健康程度" dataIndex="status"/>
<Column title="操作" align="center" cell={(value, index, record) => (
<Button type="normal" disabled>详情</Button>
<Column title={I18N.COLUMN_SERVICE_NAME} dataIndex="name"/>
<Column title={I18N.COLUMN_CLUSTER_COUNT} dataIndex="clusterCount"/>
<Column title={I18N.COLUMN_IP_COUNT} dataIndex="ipCount"/>
<Column title={I18N.COLUMN_HEALTH_STATUS} dataIndex="status"/>
<Column title={I18N.COLUMN_OPERATION} align="center" cell={(value, index, record) => (
<Button
type="normal"
onClick={() => this.props.history.push(`/serviceDetail?name=${record.name}`)}
>详情</Button>
)}/>
</Table>
</Col>

View File

@ -0,0 +1,14 @@
.service-management {
.row-bg-green {
background-color: #e4fdda;
}
.row-bg-light-green {
background-color: #e3fff8;
}
.row-bg-orange {
background-color: #fff3e0;
}
.row-bg-red {
background-color: #ffece4;
}
}

View File

@ -0,0 +1,49 @@
const getI18N = (key, prefix = 'com.alibaba.nacos.page.serviceManagement.') => window.aliwareIntl.get(prefix + key)
export const I18N = {}
/**
* 服务列表
*/
I18N.SERVICE_LIST = getI18N('service_list')
/**
* 服务名称
*/
I18N.SERVICE_NAME = getI18N('service_name')
/**
* 请输入服务名称
*/
I18N.ENTER_SERVICE_NAME = getI18N('please_enter_the_service_name')
/**
* 查询
*/
I18N.QUERY = getI18N('query')
/**
* 查询
*/
I18N.PUBNODEDATA = getI18N('pubnodata', '')
/**
* 服务名
*/
I18N.COLUMN_SERVICE_NAME = getI18N('table.column.service_name')
/**
* 集群数目
*/
I18N.COLUMN_CLUSTER_COUNT = getI18N('table.column.cluster_count')
/**
* IP数目
*/
I18N.COLUMN_IP_COUNT = getI18N('table.column.ip_count')
/**
* 健康程度
*/
I18N.COLUMN_HEALTH_STATUS = getI18N('table.column.health_status')
/**
* 操作
*/
I18N.COLUMN_OPERATION = getI18N('table.column.operation')
export const STATUS_COLOR_MAPPING = {
: 'green',
: 'light-green',
: 'orange',
: 'red'
}

View File

@ -0,0 +1,3 @@
import ServiceManagement from './ServiceManagement'
export default ServiceManagement

View File

@ -13,7 +13,8 @@ import ConfigRollback from './pages/ConfigRollback';
import HistoryRollback from './pages/HistoryRollback';
import ListeningToQuery from './pages/ListeningToQuery';
import ConfigurationManagement from './pages/ConfigurationManagement';
import ServiceManagement from './pages/ServiceManagement';
import ServiceManagement from './pages/ServiceManagement/index';
import ServiceDetail from './pages/ServiceDetail/ServiceDetail';
function RouterConfig({ history }) {
window.hashHistory = history;
@ -32,6 +33,7 @@ function RouterConfig({ history }) {
<Route path="/ListeningToQuery" component={ListeningToQuery} />
<Route path="/ConfigurationManagement" component={ConfigurationManagement} />
<Route path="/ServiceManagement" component={ServiceManagement} />
<Route path="/ServiceDetail" component={ServiceDetail} />
</App>
</Switch>
</Router>

View File

@ -46,6 +46,11 @@
<artifactId>nacos-core</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>nacos-api</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>

View File

@ -17,16 +17,20 @@ package com.alibaba.nacos.naming.controllers;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.api.naming.pojo.Cluster;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.naming.pojo.Service;
import com.alibaba.nacos.naming.core.Domain;
import com.alibaba.nacos.naming.core.DomainsManager;
import com.alibaba.nacos.naming.core.IpAddress;
import com.alibaba.nacos.naming.core.VirtualClusterDomain;
import com.alibaba.nacos.naming.exception.NacosException;
import com.alibaba.nacos.naming.healthcheck.HealthCheckMode;
import com.alibaba.nacos.naming.misc.UtilsAndCommons;
import com.alibaba.nacos.naming.view.ServiceDetailView;
import com.alibaba.nacos.naming.view.ServiceView;
import com.alibaba.nacos.naming.web.BaseServlet;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
@ -57,7 +61,7 @@ public class CatalogController {
String keyword = BaseServlet.optional(request, "keyword", StringUtils.EMPTY);
List<Domain> doms = new ArrayList<>();
int total = domainsManager.getPagedDom(page, pageSize, keyword, doms);
int total = domainsManager.getPagedDom(page - 1, pageSize, keyword, doms);
if (CollectionUtils.isEmpty(doms)) {
result.put("serviceList", Collections.emptyList());
@ -81,20 +85,7 @@ public class CatalogController {
}
}
double validRatio = validCount * 1.0 / vDomain.allIPs().size();
// FIXME:
validRatio = RandomUtils.nextDouble(0, 1.2);
if (validRatio > 0.9) {
serviceView.setStatus("");
} else if (validRatio > 0.6) {
serviceView.setStatus("");
} else if (validRatio > 0.3) {
serviceView.setStatus("");
} else {
serviceView.setStatus("");
}
serviceView.setStatus(validCount + "/" + vDomain.allIPs().size());
domArray.add(serviceView);
}
@ -106,9 +97,7 @@ public class CatalogController {
}
@RequestMapping(value = "/serviceDetail")
public VirtualClusterDomain serviceDetail(HttpServletRequest request) throws Exception {
JSONObject result = new JSONObject();
public ServiceDetailView serviceDetail(HttpServletRequest request) throws Exception {
String serviceName = BaseServlet.required(request, "serviceName");
VirtualClusterDomain domain = (VirtualClusterDomain) domainsManager.getDomain(serviceName);
@ -116,6 +105,91 @@ public class CatalogController {
throw new NacosException(NacosException.NOT_FOUND, "serivce " + serviceName + " is not found!");
}
return null;
ServiceDetailView detailView = new ServiceDetailView();
Service service = new Service(serviceName);
service.setName(serviceName);
service.setProtectThreshold(domain.getProtectThreshold());
service.setHealthCheckMode(HealthCheckMode.none.name());
if (domain.getEnableHealthCheck()) {
service.setHealthCheckMode(HealthCheckMode.server.name());
}
if (domain.getEnableClientBeat()) {
service.setHealthCheckMode(HealthCheckMode.client.name());
}
service.setMetadata(domain.getMetadata());
detailView.setService(service);
List<Cluster> clusters = new ArrayList<>();
for (com.alibaba.nacos.naming.core.Cluster cluster : domain.getClusterMap().values()) {
Cluster clusterView = new Cluster();
clusterView.setName(cluster.getName());
clusterView.setHealthChecker(cluster.getHealthChecker());
clusterView.setMetadata(cluster.getMetadata());
clusterView.setUseIPPort4Check(cluster.isUseIPPort4Check());
clusterView.setDefaultPort(cluster.getDefIPPort());
clusterView.setDefaultCheckPort(cluster.getDefCkport());
clusterView.setServiceName(serviceName);
clusters.add(clusterView);
}
detailView.setClusters(clusters);
return detailView;
}
@RequestMapping(value = "/instanceList")
public JSONObject instanceList(HttpServletRequest request) throws Exception {
String serviceName = BaseServlet.required(request, "serviceName");
String clusterName = BaseServlet.required(request, "clusterName");
int page = Integer.parseInt(BaseServlet.required(request, "startPg"));
int pageSize = Integer.parseInt(BaseServlet.required(request, "pgSize"));
VirtualClusterDomain domain = (VirtualClusterDomain) domainsManager.getDomain(serviceName);
if (domain == null) {
throw new NacosException(NacosException.NOT_FOUND, "serivce " + serviceName + " is not found!");
}
if (!domain.getClusterMap().containsKey(clusterName)) {
throw new NacosException(NacosException.NOT_FOUND, "cluster " + clusterName + " is not found!");
}
List<Instance> instances = new ArrayList<>();
for (IpAddress ipAddress : domain.getClusterMap().get(clusterName).allIPs()) {
Instance instance = new Instance();
instance.setIp(ipAddress.getIp());
instance.setMetadata(ipAddress.getMetadata());
instance.setHealthy(ipAddress.isValid());
instance.setPort(ipAddress.getPort());
instance.setInstanceId(ipAddress.getInstanceId());
instance.setWeight(ipAddress.getWeight());
instance.setEnabled(ipAddress.isEnabled());
instances.add(instance);
}
int start = (page - 1) * pageSize;
int end = page * pageSize;
if (start < 0) {
start = 0;
}
if (start > instances.size()) {
start = instances.size();
}
if (end > instances.size()) {
end = instances.size();
}
JSONObject result = new JSONObject();
result.put("list", instances.subList(start, end));
result.put("count", instances.size());
return result;
}
}

View File

@ -15,8 +15,86 @@
*/
package com.alibaba.nacos.naming.controllers;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.api.naming.pojo.AbstractHealthChecker;
import com.alibaba.nacos.naming.core.Cluster;
import com.alibaba.nacos.naming.core.DomainsManager;
import com.alibaba.nacos.naming.core.VirtualClusterDomain;
import com.alibaba.nacos.naming.exception.NacosException;
import com.alibaba.nacos.naming.misc.UtilsAndCommons;
import com.alibaba.nacos.naming.web.BaseServlet;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
/**
* @author dungu.zpf
*/
@RestController
@RequestMapping(UtilsAndCommons.NACOS_NAMING_CONTEXT + "/cluster")
public class ClusterController {
@Autowired
protected DomainsManager domainsManager;
@RequestMapping(value = "/update", method = RequestMethod.POST)
public String update(HttpServletRequest request) throws Exception {
String clusterName = BaseServlet.required(request, "clusterName");
String serviceName = BaseServlet.required(request, "serviceName");
String healthChecker = BaseServlet.required(request, "healthChecker");
String metadata = BaseServlet.optional(request, "metadata", StringUtils.EMPTY);
String checkPort = BaseServlet.required(request, "checkPort");
String useInstancePort4Check = BaseServlet.required(request, "useInstancePort4Check");
VirtualClusterDomain domain = (VirtualClusterDomain) domainsManager.getDomain(serviceName);
if (domain == null) {
throw new NacosException(NacosException.INVALID_PARAM, "service not found:" + serviceName);
}
Cluster cluster = domain.getClusterMap().get(clusterName);
if (cluster == null) {
throw new NacosException(NacosException.INVALID_PARAM, "cluster not found:"+ clusterName + ", " + serviceName);
}
cluster.setDefCkport(NumberUtils.toInt(checkPort));
cluster.setUseIPPort4Check(BooleanUtils.toBoolean(useInstancePort4Check));
JSONObject healthCheckObj = JSON.parseObject(healthChecker);
AbstractHealthChecker abstractHealthChecker;
switch (healthCheckObj.getString("type")) {
case AbstractHealthChecker.Tcp.TYPE:
abstractHealthChecker = JSON.parseObject(healthChecker, AbstractHealthChecker.Tcp.class);
break;
case AbstractHealthChecker.Http.TYPE:
abstractHealthChecker = JSON.parseObject(healthChecker, AbstractHealthChecker.Http.class);
break;
case AbstractHealthChecker.Mysql.TYPE:
abstractHealthChecker = JSON.parseObject(healthChecker, AbstractHealthChecker.Mysql.class);
break;
default:
throw new NacosException(NacosException.INVALID_PARAM, "unknown health check type:" + healthChecker);
}
cluster.setHealthChecker(abstractHealthChecker);
cluster.setMetadata(UtilsAndCommons.parseMetadata(metadata));
domain.getClusterMap().put(clusterName, cluster);
domain.setLastModifiedMillis(System.currentTimeMillis());
domain.recalculateChecksum();
domain.valid();
domainsManager.easyAddOrReplaceDom(domain);
return "ok";
}
}

View File

@ -17,9 +17,9 @@ package com.alibaba.nacos.naming.controllers;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.naming.exception.NacosException;
import com.alibaba.nacos.naming.core.IpAddress;
import com.alibaba.nacos.naming.core.VirtualClusterDomain;
import com.alibaba.nacos.naming.exception.NacosException;
import com.alibaba.nacos.naming.healthcheck.HealthCheckMode;
import com.alibaba.nacos.naming.misc.UtilsAndCommons;
import com.alibaba.nacos.naming.web.ApiCommands;
@ -115,9 +115,13 @@ public class InstanceController extends ApiCommands {
return deRegService(request);
}
@RequestMapping(value = "/instance", method = RequestMethod.POST)
@RequestMapping(value = "/instance/update", method = RequestMethod.POST)
public String update(HttpServletRequest request) throws Exception {
return addIP4Dom(request);
String serviceName = BaseServlet.required(request, "serviceName");
Map<String, String[]> params = new HashMap<>(request.getParameterMap());
MockHttpRequest mockHttpRequest = MockHttpRequest.buildRequest(params);
mockHttpRequest.addParameter("dom", serviceName);
return regService(mockHttpRequest);
}
@RequestMapping(value = {"/instances", "/instance/list"}, method = RequestMethod.GET)

View File

@ -17,8 +17,12 @@ package com.alibaba.nacos.naming.controllers;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.naming.core.DomainsManager;
import com.alibaba.nacos.naming.core.VirtualClusterDomain;
import com.alibaba.nacos.naming.exception.NacosException;
import com.alibaba.nacos.naming.healthcheck.HealthCheckMode;
import com.alibaba.nacos.naming.misc.UtilsAndCommons;
import com.alibaba.nacos.naming.web.BaseServlet;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
@ -26,7 +30,9 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author dungu.zpf
@ -65,4 +71,46 @@ public class ServiceController {
return result;
}
@RequestMapping(value = "/update", method = RequestMethod.POST)
public String update(HttpServletRequest request) throws Exception {
String serviceName = BaseServlet.required(request, "serviceName");
float protectThreshold = NumberUtils.toFloat(BaseServlet.required(request, "protectThreshold"));
String healthCheckMode = BaseServlet.required(request, "healthCheckMode");
String metadata = BaseServlet.optional(request, "metadata", StringUtils.EMPTY);
VirtualClusterDomain domain = (VirtualClusterDomain) domainsManager.getDomain(serviceName);
if (domain == null) {
throw new NacosException(NacosException.INVALID_PARAM, "service " + serviceName + " not found!");
}
domain.setProtectThreshold(protectThreshold);
if (HealthCheckMode.server.name().equals(healthCheckMode)) {
domain.setEnableHealthCheck(true);
domain.setEnableClientBeat(false);
}
if (HealthCheckMode.client.name().equals(healthCheckMode)) {
domain.setEnableClientBeat(true);
domain.setEnableHealthCheck(false);
}
if (HealthCheckMode.none.name().equals(healthCheckMode)) {
domain.setEnableClientBeat(false);
domain.setEnableHealthCheck(false);
}
Map<String, String> metadataMap = UtilsAndCommons.parseMetadata(metadata);
domain.setMetadata(metadataMap);
domain.setLastModifiedMillis(System.currentTimeMillis());
domain.recalculateChecksum();
domain.valid();
domainsManager.easyAddOrReplaceDom(domain);
return "ok";
}
}

View File

@ -16,7 +16,7 @@
package com.alibaba.nacos.naming.core;
import com.alibaba.fastjson.annotation.JSONField;
import com.alibaba.nacos.naming.healthcheck.AbstractHealthCheckConfig;
import com.alibaba.nacos.api.naming.pojo.AbstractHealthChecker;
import com.alibaba.nacos.naming.healthcheck.HealthCheckReactor;
import com.alibaba.nacos.naming.healthcheck.HealthCheckStatus;
import com.alibaba.nacos.naming.healthcheck.HealthCheckTask;
@ -32,11 +32,10 @@ import java.util.concurrent.ConcurrentHashMap;
/**
* @author dungu.zpf
*/
public class Cluster implements Cloneable {
public class Cluster extends com.alibaba.nacos.api.naming.pojo.Cluster implements Cloneable {
private static final String CLUSTER_NAME_SYNTAX = "[0-9a-zA-Z-]+";
private String name;
/**
* in fact this is CIDR(Classless Inter-Domain Routing). for naming it 'submask' it has historical reasons
*/
@ -56,14 +55,11 @@ public class Cluster implements Cloneable {
private String legacySyncConfig;
@JSONField(name = "healthChecker")
private AbstractHealthCheckConfig healthChecker = new AbstractHealthCheckConfig.Tcp();
private AbstractHealthChecker healthChecker = new AbstractHealthChecker.Tcp();
@JSONField(serialize = false)
private HealthCheckTask checkTask;
@JSONField(serialize = false)
private Set<IpAddress> ips = new HashSet<IpAddress>();
@JSONField(serialize = false)
private Set<IpAddress> raftIPs = new HashSet<IpAddress>();
@ -136,14 +132,6 @@ public class Cluster implements Cloneable {
return checkTask;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Domain getDom() {
return dom;
}
@ -160,14 +148,6 @@ public class Cluster implements Cloneable {
this.legacySyncConfig = nodegroup;
}
public AbstractHealthCheckConfig getHealthChecker() {
return healthChecker;
}
public void setHealthChecker(AbstractHealthCheckConfig healthChecker) {
this.healthChecker = healthChecker;
}
@Override
public Cluster clone() throws CloneNotSupportedException {
super.clone();
@ -175,23 +155,17 @@ public class Cluster implements Cloneable {
cluster.setHealthChecker(healthChecker.clone());
cluster.setDom(getDom());
cluster.ips = new HashSet<IpAddress>();
cluster.raftIPs = new HashSet<IpAddress>();
cluster.checkTask = null;
cluster.metadata = new HashMap<>(metadata);
return cluster;
}
public void updateIPs(List<IpAddress> ips, boolean diamond) {
public void updateIPs(List<IpAddress> ips) {
HashMap<String, IpAddress> oldIPMap = new HashMap<>(raftIPs.size());
if (diamond) {
for (IpAddress ip : this.ips) {
oldIPMap.put(ip.getDatumKey(), ip);
}
} else {
for (IpAddress ip : this.raftIPs) {
oldIPMap.put(ip.getDatumKey(), ip);
}
for (IpAddress ip : this.raftIPs) {
oldIPMap.put(ip.getDatumKey(), ip);
}
List<IpAddress> updatedIPs = updatedIPs(ips, oldIPMap.values());
@ -213,7 +187,7 @@ public class Cluster implements Cloneable {
// ip validation status updated
Loggers.EVT_LOG.info("{" + getDom().getName() + "} {SYNC} " +
"{IP-" + (ip.isValid() ? "ENABLED" : "DISABLED") + "} " + ip.getIp()
+ ":" + ip.getPort() + "@" + name);
+ ":" + ip.getPort() + "@" + getName());
}
}
@ -227,7 +201,7 @@ public class Cluster implements Cloneable {
List<IpAddress> newIPs = subtract(ips, oldIPMap.values());
if (newIPs.size() > 0) {
Loggers.EVT_LOG.info("{" + getDom().getName() + "} {SYNC} {IP-NEW} cluster: " + name
Loggers.EVT_LOG.info("{" + getDom().getName() + "} {SYNC} {IP-NEW} cluster: " + getName()
+ ", new ips(" + newIPs.size() + "): " + newIPs.toString());
for (IpAddress ip : newIPs) {
@ -238,7 +212,7 @@ public class Cluster implements Cloneable {
List<IpAddress> deadIPs = subtract(oldIPMap.values(), ips);
if (deadIPs.size() > 0) {
Loggers.EVT_LOG.info("{" + getDom().getName() + "} {SYNC} {IP-DEAD} cluster: " + name
Loggers.EVT_LOG.info("{" + getDom().getName() + "} {SYNC} {IP-DEAD} cluster: " + getName()
+ ", dead ips(" + deadIPs.size() + "): " + deadIPs.toString());
for (IpAddress ip : deadIPs) {
@ -246,11 +220,8 @@ public class Cluster implements Cloneable {
}
}
if (diamond) {
this.ips = new HashSet<IpAddress>(ips);
} else {
this.raftIPs = new HashSet<IpAddress>(ips);
}
this.raftIPs = new HashSet<IpAddress>(ips);
StringBuilder stringBuilder = new StringBuilder();
for (IpAddress ipAddress : raftIPs) {
stringBuilder.append(ipAddress.toIPAddr()).append(ipAddress.isValid());
@ -337,7 +308,7 @@ public class Cluster implements Cloneable {
@Override
public int hashCode() {
return Objects.hash(name);
return Objects.hash(getName());
}
@Override
@ -346,7 +317,7 @@ public class Cluster implements Cloneable {
return false;
}
return name.equals(((Cluster) obj).getName());
return getName().equals(((Cluster) obj).getName());
}
public int getDefCkport() {
@ -420,14 +391,6 @@ public class Cluster implements Cloneable {
this.sitegroup = sitegroup;
}
public Map<String, String> getMetadata() {
return metadata;
}
public void setMetadata(Map<String, String> metadata) {
this.metadata = metadata;
}
public boolean responsible(IpAddress ip) {
return Switch.isHealthCheckEnabled(dom.getName())
&& !getHealthCheckTask().isCancelled()
@ -436,8 +399,8 @@ public class Cluster implements Cloneable {
}
public void valid() {
if (!name.matches(CLUSTER_NAME_SYNTAX)) {
throw new IllegalArgumentException("cluster name can only have these characters: 0-9a-zA-Z-, current: " + name);
if (!getName().matches(CLUSTER_NAME_SYNTAX)) {
throw new IllegalArgumentException("cluster name can only have these characters: 0-9a-zA-Z-, current: " + getName());
}
String[] cidrGroups = submask.split("\\|");
@ -446,7 +409,7 @@ public class Cluster implements Cloneable {
for (String cidr : cidrs) {
if (!cidr.matches(UtilsAndCommons.CIDR_REGEX)) {
throw new IllegalArgumentException("malformed submask: " + submask + " for cluster: " + name);
throw new IllegalArgumentException("malformed submask: " + submask + " for cluster: " + getName());
}
}
}

View File

@ -176,12 +176,12 @@ public class DomainsManager {
JSONObject dom = JSON.parseObject(msg.getData());
JSONArray ipList = dom.getJSONArray("ips");
Map<String, Pair> ipsMap = new HashMap<>(ipList.size());
Map<String, String> ipsMap = new HashMap<>(ipList.size());
for (int i=0; i<ipList.size(); i++) {
String ip = ipList.getString(i);
String[] strings = ip.split("_");
ipsMap.put(strings[0], new Pair(strings[1], strings[2]));
ipsMap.put(strings[0], strings[1]);
}
VirtualClusterDomain raftVirtualClusterDomain = (VirtualClusterDomain) raftDomMap.get(domName);
@ -192,14 +192,10 @@ public class DomainsManager {
List<IpAddress> ipAddresses = raftVirtualClusterDomain.allIPs();
for (IpAddress ipAddress : ipAddresses) {
Pair pair = ipsMap.get(ipAddress.toIPAddr());
if (pair == null) {
continue;
}
Boolean valid = Boolean.parseBoolean(pair.getKey());
Boolean valid = Boolean.parseBoolean(ipsMap.get(ipAddress.toIPAddr()));
if (valid != ipAddress.isValid()) {
ipAddress.setValid(Boolean.parseBoolean(pair.getKey()));
ipAddress.setInvalidType(pair.getValue());
ipAddress.setValid(valid);
Loggers.EVT_LOG.info("{" + domName + "} {SYNC} " +
"{IP-" + (ipAddress.isValid() ? "ENABLED" : "DISABLED") + "} " + ipAddress.getIp()
+ ":" + ipAddress.getPort() + "@" + ipAddress.getClusterName());

View File

@ -17,6 +17,7 @@ package com.alibaba.nacos.naming.core;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.annotation.JSONField;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.naming.healthcheck.HealthCheckStatus;
import com.alibaba.nacos.naming.misc.Loggers;
import com.alibaba.nacos.naming.misc.UtilsAndCommons;
@ -33,55 +34,25 @@ import java.util.regex.Pattern;
*
* @author dungu.zpf
*/
public class IpAddress implements Comparable {
public class IpAddress extends Instance implements Comparable {
private static final double MAX_WEIGHT_VALUE = 10000.0D;
private static final double MIN_POSTIVE_WEIGHT_VALUE = 0.01D;
private static final double MIN_WEIGHT_VALUE = 0.00D;
private String ip;
private int port = 0;
private double weight = 1.0;
private String clusterName = UtilsAndCommons.DEFAULT_CLUSTER_NAME;
private volatile long lastBeat = System.currentTimeMillis();
@JSONField(serialize = false)
private String invalidType = InvalidType.VALID;
public static class InvalidType {
public final static String HTTP_404 = "404";
public final static String WEIGHT_0 = "weight_0";
public final static String NORMAL_INVALID = "invalid";
public final static String VALID = "valid";
}
public String getInvalidType() {
return invalidType;
}
public void setInvalidType(String invalidType) {
this.invalidType = invalidType;
}
@JSONField(serialize = false)
private Cluster cluster;
private volatile boolean valid = true;
@JSONField(serialize = false)
private volatile boolean mockValid = false;
@JSONField(serialize = false)
private volatile boolean preValid = true;
private volatile boolean marked = false;
private String tenant;
private String app;
private Map<String, String> metadata = new ConcurrentHashMap<>();
public static final Pattern IP_PATTERN
= Pattern.compile("(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}):?(\\d{1,5})?");
@ -107,20 +78,20 @@ public class IpAddress implements Comparable {
}
public IpAddress(String ip, int port) {
this.ip = ip.trim();
this.port = port;
this.setIp(ip);
this.setPort(port);
this.clusterName = UtilsAndCommons.DEFAULT_CLUSTER_NAME;
}
public IpAddress(String ip, int port, String clusterName) {
this.ip = ip.trim();
this.port = port;
this.setIp(ip.trim());
this.setPort(port);
this.clusterName = clusterName;
}
public IpAddress(String ip, int port, String clusterName, String tenant, String app) {
this.ip = ip.trim();
this.port = port;
this.setIp(ip.trim());
this.setPort(port);
this.clusterName = clusterName;
this.tenant = tenant;
this.app = app;
@ -192,12 +163,12 @@ public class IpAddress implements Comparable {
}
public String toIPAddr() {
return ip + ":" + port;
return getIp() + ":" + getPort();
}
@Override
public String toString() {
return getDatumKey() + SPLITER + weight + SPLITER + valid + SPLITER + marked + SPLITER + clusterName;
return getDatumKey() + SPLITER + getWeight() + SPLITER + isHealthy() + SPLITER + marked + SPLITER + clusterName;
}
public String toJSON() {
@ -241,30 +212,30 @@ public class IpAddress implements Comparable {
IpAddress other = (IpAddress) obj;
// 0 means wild
return ip.equals(other.getIp()) && (port == other.port || port == 0);
return getIp().equals(other.getIp()) && (getPort() == other.getPort() || getPort() == 0);
}
@JSONField(serialize = false)
public String getDatumKey() {
if (port > 0) {
return ip + ":" + port + ":" + DistroMapper.LOCALHOST_SITE;
if (getPort() > 0) {
return getIp() + ":" + getPort() + ":" + DistroMapper.LOCALHOST_SITE;
} else {
return ip + ":" + DistroMapper.LOCALHOST_SITE;
return getIp() + ":" + DistroMapper.LOCALHOST_SITE;
}
}
@JSONField(serialize = false)
public String getDefaultKey() {
if (port > 0) {
return ip + ":" + port + ":" + UtilsAndCommons.UNKNOWN_SITE;
if (getPort() > 0) {
return getIp() + ":" + getPort() + ":" + UtilsAndCommons.UNKNOWN_SITE;
} else {
return ip + ":" + UtilsAndCommons.UNKNOWN_SITE;
return getIp() + ":" + UtilsAndCommons.UNKNOWN_SITE;
}
}
@Override
public int hashCode() {
return ip.hashCode();
return getIp().hashCode();
}
public void setBeingChecked(boolean isBeingChecked) {
@ -295,14 +266,6 @@ public class IpAddress implements Comparable {
HealthCheckStatus.get(this).checkRT = checkRT;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getClusterName() {
return clusterName;
}
@ -311,41 +274,12 @@ public class IpAddress implements Comparable {
this.clusterName = clusterName;
}
public void setCluster(Cluster cluster) {
this.cluster = cluster;
}
public Cluster getCluster() {
return cluster;
}
public void setIp(String ip) {
this.ip = ip;
}
public String getIp() {
return ip;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
public synchronized void setValid(boolean valid) {
this.preValid = this.valid;
this.valid = valid;
setHealthy(valid);
}
public boolean isValid() {
return valid;
}
public boolean isPreValid() {
return preValid;
return isHealthy();
}
public boolean isMarked() {
@ -372,16 +306,8 @@ public class IpAddress implements Comparable {
this.tenant = tenant;
}
public Map<String, String> getMetadata() {
return metadata;
}
public void setMetadata(Map<String, String> metadata) {
this.metadata = metadata;
}
public String generateInstanceId() {
return this.ip + "#" + this.port + "#" + this.cluster.getName() + "#" + this.cluster.getDom().getName();
return getIp() + "#" + getPort() + "#" + getCluster().getName() + "#" + getService().getName();
}
@Override

View File

@ -175,7 +175,7 @@ public class VirtualClusterDomain implements Domain, RaftListener {
}
}
updateIPs(ips, false);
updateIPs(ips);
recalculateChecksum();
}
@ -185,7 +185,7 @@ public class VirtualClusterDomain implements Domain, RaftListener {
// ignore
}
public void updateIPs(List<IpAddress> ips, boolean diamond) {
public void updateIPs(List<IpAddress> ips) {
if (CollectionUtils.isEmpty(ips) && allIPs().size() > 1) {
return;
}
@ -236,7 +236,7 @@ public class VirtualClusterDomain implements Domain, RaftListener {
ip.setCluster(clusterMap.get(ip.getClusterName()));
}
clusterMap.get(entry.getKey()).updateIPs(entryIPs, diamond);
clusterMap.get(entry.getKey()).updateIPs(entryIPs);
}
setLastModifiedMillis(System.currentTimeMillis());
PushService.domChanged(name);

View File

@ -1,327 +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;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONField;
import com.alibaba.fastjson.parser.DefaultJSONParser;
import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer;
import com.alibaba.fastjson.serializer.JSONSerializer;
import com.alibaba.fastjson.serializer.ObjectSerializer;
import com.alibaba.fastjson.serializer.SerializeWriter;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* @author nacos
*/
public abstract class AbstractHealthCheckConfig implements Cloneable {
protected String type = "unknown";
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
/**
* Get copy of health check config
*
* @return Copy of health check config
* @throws CloneNotSupportedException
*/
@Override
public abstract AbstractHealthCheckConfig clone() throws CloneNotSupportedException;
public static class Http extends AbstractHealthCheckConfig {
public static final String TYPE = "HTTP";
public static final String HTTP_HEADER_SPLIT_STRING = "\\|";
private String path = StringUtils.EMPTY;
private String headers = StringUtils.EMPTY;
private int expectedResponseCode = 200;
public Http() {
this.type = TYPE;
}
public int getExpectedResponseCode() {
return expectedResponseCode;
}
public void setExpectedResponseCode(int expectedResponseCode) {
this.expectedResponseCode = expectedResponseCode;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getHeaders() {
return headers;
}
public void setHeaders(String headers) {
this.headers = headers;
}
@JSONField(serialize = false)
public Map<String, String> getCustomHeaders() {
if (StringUtils.isBlank(headers)) {
return Collections.emptyMap();
}
Map<String, String> headers = new HashMap<String, String>(this.headers.split(HTTP_HEADER_SPLIT_STRING).length);
for (String s : this.headers.split(HTTP_HEADER_SPLIT_STRING)) {
String[] splits = s.split(":");
if (splits.length != 2) {
continue;
}
headers.put(StringUtils.trim(splits[0]), StringUtils.trim(splits[1]));
}
return headers;
}
@Override
public int hashCode() {
return Objects.hash(headers, path, expectedResponseCode);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Http)) {
return false;
}
Http other = (Http) obj;
if (!StringUtils.equals(type, other.getType())) {
return false;
}
if (!StringUtils.equals(path, other.getPath())) {
return false;
}
if (!StringUtils.equals(headers, other.getHeaders())) {
return false;
}
return expectedResponseCode == other.getExpectedResponseCode();
}
@SuppressFBWarnings("CN_IDIOM_NO_SUPER_CALL")
@Override
public AbstractHealthCheckConfig.Http clone() throws CloneNotSupportedException {
AbstractHealthCheckConfig.Http config = new AbstractHealthCheckConfig.Http();
config.setPath(this.path);
config.setHeaders(this.headers);
config.setType(this.type);
config.setExpectedResponseCode(this.expectedResponseCode);
return config;
}
}
public static class Tcp extends AbstractHealthCheckConfig {
public static final String TYPE = "TCP";
public Tcp() {
this.type = TYPE;
}
@Override
public int hashCode() {
return Objects.hash(Tcp.TYPE);
}
@Override
public boolean equals(Object obj) {
return obj instanceof Tcp;
}
@SuppressFBWarnings("CN_IDIOM_NO_SUPER_CALL")
@Override
public AbstractHealthCheckConfig.Tcp clone() throws CloneNotSupportedException {
AbstractHealthCheckConfig.Tcp config = new AbstractHealthCheckConfig.Tcp();
config.setType(this.type);
return config;
}
}
public static class Mysql extends AbstractHealthCheckConfig {
public static final String TYPE = "MYSQL";
private String user;
private String pwd;
private String cmd;
public Mysql() {
this.type = TYPE;
}
public String getCmd() {
return cmd;
}
public String getPwd() {
return pwd;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public void setCmd(String cmd) {
this.cmd = cmd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public int hashCode() {
return Objects.hash(user, pwd, cmd);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Mysql)) {
return false;
}
Mysql other = (Mysql) obj;
if (!StringUtils.equals(user, other.getUser())) {
return false;
}
if (!StringUtils.equals(pwd, other.getPwd())) {
return false;
}
return StringUtils.equals(cmd, other.getCmd());
}
@SuppressFBWarnings("CN_IDIOM_NO_SUPER_CALL")
@Override
public AbstractHealthCheckConfig.Mysql clone() throws CloneNotSupportedException {
AbstractHealthCheckConfig.Mysql config = new AbstractHealthCheckConfig.Mysql();
config.setUser(this.user);
config.setPwd(this.pwd);
config.setCmd(this.cmd);
config.setType(this.type);
return config;
}
}
public static class JsonAdapter implements ObjectDeserializer, ObjectSerializer {
private static JsonAdapter INSTANCE = new JsonAdapter();
private JsonAdapter() {
}
;
public static JsonAdapter getInstance() {
return INSTANCE;
}
@Override
public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
JSONObject jsonObj = (JSONObject) parser.parse();
String checkType = jsonObj.getString("type");
if (StringUtils.equals(checkType, AbstractHealthCheckConfig.Http.TYPE)) {
return (T) JSON.parseObject(jsonObj.toJSONString(), AbstractHealthCheckConfig.Http.class);
}
if (StringUtils.equals(checkType, AbstractHealthCheckConfig.Tcp.TYPE)) {
return (T) JSON.parseObject(jsonObj.toJSONString(), AbstractHealthCheckConfig.Tcp.class);
}
if (StringUtils.equals(checkType, AbstractHealthCheckConfig.Mysql.TYPE)) {
return (T) JSON.parseObject(jsonObj.toJSONString(), AbstractHealthCheckConfig.Mysql.class);
}
return null;
}
@Override
public int getFastMatchToken() {
return 0;
}
@Override
public void write(JSONSerializer jsonSerializer, Object o, Object o1, Type type, int i) throws IOException {
SerializeWriter writer = jsonSerializer.getWriter();
if (o == null) {
writer.writeNull();
return;
}
AbstractHealthCheckConfig config = (AbstractHealthCheckConfig) o;
writer.writeFieldValue(',', "type", config.getType());
if (StringUtils.equals(config.getType(), HealthCheckType.HTTP.name())) {
AbstractHealthCheckConfig.Http httpCheckConfig = (Http) config;
writer.writeFieldValue(',', "path", httpCheckConfig.getPath());
writer.writeFieldValue(',', "headers", httpCheckConfig.getHeaders());
}
if (StringUtils.equals(config.getType(), HealthCheckType.TCP.name())) {
// nothing sepcial to handle
}
if (StringUtils.equals(config.getType(), HealthCheckType.MYSQL.name())) {
AbstractHealthCheckConfig.Mysql mysqlCheckConfig = (Mysql) config;
writer.writeFieldValue(',', "user", mysqlCheckConfig.getUser());
writer.writeFieldValue(',', "pwd", mysqlCheckConfig.getPwd());
writer.writeFieldValue(',', "cmd", mysqlCheckConfig.getCmd());
}
}
}
}

View File

@ -16,6 +16,7 @@
package com.alibaba.nacos.naming.healthcheck;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.naming.pojo.AbstractHealthChecker;
import com.alibaba.nacos.naming.boot.RunningConfig;
import com.alibaba.nacos.naming.core.Cluster;
import com.alibaba.nacos.naming.core.DistroMapper;
@ -146,7 +147,7 @@ public abstract class AbstractHealthCheckProcessor {
public static final TcpSuperSenseProcessor TCP_PROCESSOR = new TcpSuperSenseProcessor();
public static final MysqlHealthCheckProcessor MYSQL_PROCESSOR = new MysqlHealthCheckProcessor();
public static AbstractHealthCheckProcessor getProcessor(AbstractHealthCheckConfig config) {
public static AbstractHealthCheckProcessor getProcessor(AbstractHealthChecker config) {
if (config == null || StringUtils.isEmpty(config.getType())) {
throw new IllegalArgumentException("empty check type");
}
@ -207,7 +208,6 @@ public abstract class AbstractHealthCheckProcessor {
if (cluster.responsible(ip)) {
ip.setValid(true);
ip.setMockValid(true);
ip.setInvalidType(IpAddress.InvalidType.VALID);
VirtualClusterDomain vDom = (VirtualClusterDomain) cluster.getDom();
vDom.setLastModifiedMillis(System.currentTimeMillis());
@ -249,7 +249,6 @@ public abstract class AbstractHealthCheckProcessor {
if (cluster.responsible(ip)) {
ip.setValid(false);
ip.setMockValid(false);
setInvalidType(ip, msg);
VirtualClusterDomain vDom = (VirtualClusterDomain) cluster.getDom();
vDom.setLastModifiedMillis(System.currentTimeMillis());
@ -281,14 +280,6 @@ public abstract class AbstractHealthCheckProcessor {
ip.setBeingChecked(false);
}
private void setInvalidType(IpAddress ipAddress, String msg) {
if (msg.equals(HTTP_CHECK_MSG_PREFIX + IpAddress.InvalidType.HTTP_404)) {
ipAddress.setInvalidType(IpAddress.InvalidType.HTTP_404);
} else {
ipAddress.setInvalidType(IpAddress.InvalidType.NORMAL_INVALID);
}
}
protected void checkFailNow(IpAddress ip, HealthCheckTask task, String msg) {
Cluster cluster = task.getCluster();
try {
@ -297,8 +288,6 @@ public abstract class AbstractHealthCheckProcessor {
ip.setValid(false);
ip.setMockValid(false);
setInvalidType(ip, msg);
VirtualClusterDomain vDom = (VirtualClusterDomain) cluster.getDom();
vDom.setLastModifiedMillis(System.currentTimeMillis());

View File

@ -16,8 +16,6 @@
package com.alibaba.nacos.naming.healthcheck;
import com.alibaba.nacos.naming.core.Cluster;
import com.alibaba.nacos.naming.core.Domain;
import com.alibaba.nacos.naming.core.IpAddress;
import com.alibaba.nacos.naming.misc.Loggers;
@ -58,16 +56,9 @@ public class HealthCheckStatus {
private static String buildKey(IpAddress ip) {
try {
Cluster cluster = ip.getCluster();
Domain domain = cluster.getDom();
if (domain == null) {
Loggers.SRV_LOG.warn("BUILD-KEY", "domain is null, ip: " + ip.toIPAddr());
return ip.getDefaultKey();
}
String clusterName = cluster.getName();
String dom = domain.getName();
String clusterName = ip.getCluster().getName();
String dom = ip.getCluster().getServiceName();
String datumKey = ip.getDatumKey();
return dom + ":"
+ clusterName + ":"

View File

@ -15,6 +15,7 @@
*/
package com.alibaba.nacos.naming.healthcheck;
import com.alibaba.nacos.api.naming.pojo.AbstractHealthChecker;
import com.alibaba.nacos.naming.core.Cluster;
import com.alibaba.nacos.naming.core.IpAddress;
import com.alibaba.nacos.naming.core.VirtualClusterDomain;
@ -107,7 +108,7 @@ public class HttpHealthCheckProcessor extends AbstractHealthCheckProcessor {
continue;
}
AbstractHealthCheckConfig.Http healthChecker = (AbstractHealthCheckConfig.Http) cluster.getHealthChecker();
AbstractHealthChecker.Http healthChecker = (AbstractHealthChecker.Http) cluster.getHealthChecker();
int ckPort = cluster.isUseIPPort4Check() ? ip.getPort() : cluster.getDefCkport();
URL host = new URL("http://" + ip.getIp() + ":" + ckPort);

View File

@ -0,0 +1,84 @@
package com.alibaba.nacos.naming.healthcheck;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.DefaultJSONParser;
import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer;
import com.alibaba.fastjson.serializer.JSONSerializer;
import com.alibaba.fastjson.serializer.ObjectSerializer;
import com.alibaba.fastjson.serializer.SerializeWriter;
import com.alibaba.nacos.api.naming.pojo.AbstractHealthChecker;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.lang.reflect.Type;
/**
* @author dungu.zpf
*/
public class JsonAdapter implements ObjectDeserializer, ObjectSerializer {
private static JsonAdapter INSTANCE = new JsonAdapter();
private JsonAdapter() {
}
public static JsonAdapter getInstance() {
return INSTANCE;
}
@Override
public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
JSONObject jsonObj = (JSONObject) parser.parse();
String checkType = jsonObj.getString("type");
if (StringUtils.equals(checkType, AbstractHealthChecker.Http.TYPE)) {
return (T) JSON.parseObject(jsonObj.toJSONString(), AbstractHealthChecker.Http.class);
}
if (StringUtils.equals(checkType, AbstractHealthChecker.Tcp.TYPE)) {
return (T) JSON.parseObject(jsonObj.toJSONString(), AbstractHealthChecker.Tcp.class);
}
if (StringUtils.equals(checkType, AbstractHealthChecker.Mysql.TYPE)) {
return (T) JSON.parseObject(jsonObj.toJSONString(), AbstractHealthChecker.Mysql.class);
}
return null;
}
@Override
public int getFastMatchToken() {
return 0;
}
@Override
public void write(JSONSerializer jsonSerializer, Object o, Object o1, Type type, int i) throws IOException {
SerializeWriter writer = jsonSerializer.getWriter();
if (o == null) {
writer.writeNull();
return;
}
AbstractHealthChecker config = (AbstractHealthChecker) o;
writer.writeFieldValue(',', "type", config.getType());
if (StringUtils.equals(config.getType(), HealthCheckType.HTTP.name())) {
AbstractHealthChecker.Http httpCheckConfig = (AbstractHealthChecker.Http) config;
writer.writeFieldValue(',', "path", httpCheckConfig.getPath());
writer.writeFieldValue(',', "headers", httpCheckConfig.getHeaders());
}
if (StringUtils.equals(config.getType(), HealthCheckType.TCP.name())) {
// nothing sepcial to handle
}
if (StringUtils.equals(config.getType(), HealthCheckType.MYSQL.name())) {
AbstractHealthChecker.Mysql mysqlCheckConfig = (AbstractHealthChecker.Mysql) config;
writer.writeFieldValue(',', "user", mysqlCheckConfig.getUser());
writer.writeFieldValue(',', "pwd", mysqlCheckConfig.getPwd());
writer.writeFieldValue(',', "cmd", mysqlCheckConfig.getCmd());
}
}
}

View File

@ -15,6 +15,7 @@
*/
package com.alibaba.nacos.naming.healthcheck;
import com.alibaba.nacos.api.naming.pojo.AbstractHealthChecker;
import com.alibaba.nacos.naming.core.Cluster;
import com.alibaba.nacos.naming.core.IpAddress;
import com.alibaba.nacos.naming.core.VirtualClusterDomain;
@ -132,7 +133,7 @@ public class MysqlHealthCheckProcessor extends AbstractHealthCheckProcessor {
Cluster cluster = task.getCluster();
String key = cluster.getDom().getName() + ":" + cluster.getName() + ":" + ip.getIp() + ":" + ip.getPort();
Connection connection = CONNECTION_POOL.get(key);
AbstractHealthCheckConfig.Mysql config = (AbstractHealthCheckConfig.Mysql) cluster.getHealthChecker();
AbstractHealthChecker.Mysql config = (AbstractHealthChecker.Mysql) cluster.getHealthChecker();
if (connection == null || connection.isClosed()) {
MysqlDataSource dataSource = new MysqlDataSource();

View File

@ -16,14 +16,19 @@
package com.alibaba.nacos.naming.misc;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.nacos.api.naming.pojo.AbstractHealthChecker;
import com.alibaba.nacos.naming.core.Domain;
import com.alibaba.nacos.naming.healthcheck.AbstractHealthCheckConfig;
import com.alibaba.nacos.naming.exception.NacosException;
import com.alibaba.nacos.naming.healthcheck.JsonAdapter;
import org.apache.commons.lang3.StringUtils;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
@ -115,9 +120,9 @@ public class UtilsAndCommons {
static {
// custom serializer and deserializer for fast-json
SerializeConfig.getGlobalInstance()
.put(AbstractHealthCheckConfig.class, AbstractHealthCheckConfig.JsonAdapter.getInstance());
.put(AbstractHealthChecker.class, JsonAdapter.getInstance());
ParserConfig.getGlobalInstance()
.putDeserializer(AbstractHealthCheckConfig.class, AbstractHealthCheckConfig.JsonAdapter.getInstance());
.putDeserializer(AbstractHealthChecker.class, JsonAdapter.getInstance());
// write null values, otherwise will cause compatibility issues
JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.WriteNullStringAsEmpty.getMask();
@ -203,4 +208,29 @@ public class UtilsAndCommons {
return UtilsAndCommons.DOMAINS_DATA_ID + "." + dom.getName();
}
public static Map<String, String> parseMetadata(String metadata) throws NacosException {
Map<String, String> metadataMap = new HashMap<>(16);
if (StringUtils.isBlank(metadata)) {
return metadataMap;
}
try {
metadataMap = JSON.parseObject(metadata, new TypeReference<Map<String, String>>(){});
} catch (Exception e) {
String[] datas = metadata.split(",");
if (datas.length > 0) {
for (String data : datas) {
String[] kv = data.split("=");
if (kv.length != 2) {
throw new NacosException(NacosException.INVALID_PARAM, "metadata format incorrect:" + metadata);
}
metadataMap.put(kv[0], kv[1]);
}
}
}
return metadataMap;
}
}

View File

@ -129,7 +129,7 @@ public class PeerSet {
RaftPeer peer = peers.get(first);
peer.state = RaftPeer.State.LEADER;
if (!peer.equals(leader)) {
if (!Objects.equals(leader, peer)) {
leader = peer;
Loggers.RAFT.info(leader.ip + " has become the LEADER");
}
@ -139,14 +139,14 @@ public class PeerSet {
}
public RaftPeer makeLeader(RaftPeer candidate) {
if (!leader.equals(candidate)) {
if (!Objects.equals(leader, candidate)) {
leader = candidate;
Loggers.RAFT.info(leader.ip + " has become the LEADER" + ",local :" + JSON.toJSONString(local()) + ", leader: " + JSON.toJSONString(leader));
}
for (final RaftPeer peer : peers.values()) {
Map<String, String> params = new HashMap<String, String>(1);
if (!peer.equals(candidate) && peer.state == RaftPeer.State.LEADER) {
if (!Objects.equals(peer, candidate) && peer.state == RaftPeer.State.LEADER) {
try {
String url = RaftCore.buildURL(peer.ip, RaftCore.API_GET_PEER);
HttpClient.asyncHttpPost(url, null, params, new AsyncCompletionHandler<Integer>() {

View File

@ -1,10 +1,40 @@
package com.alibaba.nacos.naming.view;
import com.alibaba.nacos.naming.core.VirtualClusterDomain;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.naming.pojo.Cluster;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.naming.pojo.Service;
import java.util.List;
import java.util.Map;
/**
* @author dungu.zpf
*/
public class ServiceDetailView {
private Service service;
private List<Cluster> clusters;
public Service getService() {
return service;
}
public void setService(Service service) {
this.service = service;
}
public List<Cluster> getClusters() {
return clusters;
}
public void setClusters(List<Cluster> clusters) {
this.clusters = clusters;
}
@Override
public String toString() {
return JSON.toJSONString(this);
}
}

View File

@ -19,6 +19,8 @@ import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.nacos.api.naming.pojo.AbstractHealthChecker;
import com.alibaba.nacos.api.naming.pojo.Service;
import com.alibaba.nacos.common.util.IoUtils;
import com.alibaba.nacos.common.util.Md5Utils;
import com.alibaba.nacos.common.util.SystemUtil;
@ -41,6 +43,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.apache.catalina.util.ParameterMap;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.codehaus.jackson.util.VersionUtil;
@ -179,7 +182,7 @@ public class ApiCommands {
JSONArray ipArray = new JSONArray();
for (IpAddress ip : ips) {
ipArray.add(ip.toIPAddr() + "_" + ip.isValid() + "_" + ip.getInvalidType());
ipArray.add(ip.toIPAddr() + "_" + ip.isValid());
}
result.put("ips", ipArray);
@ -237,7 +240,6 @@ public class ApiCommands {
}
ipPac.put("checkRT", ip.getCheckRT());
ipPac.put("cluster", ip.getClusterName());
ipPac.put("invalidType", ip.getInvalidType());
ipArray.add(ipPac);
}
@ -410,25 +412,25 @@ public class ApiCommands {
}));
}
if (AbstractHealthCheckConfig.Tcp.TYPE.equals(cktype)) {
AbstractHealthCheckConfig.Tcp config = new AbstractHealthCheckConfig.Tcp();
if (AbstractHealthChecker.Tcp.TYPE.equals(cktype)) {
AbstractHealthChecker.Tcp config = new AbstractHealthChecker.Tcp();
cluster.setHealthChecker(config);
} else if (AbstractHealthCheckConfig.Http.TYPE.equals(cktype)) {
} else if (AbstractHealthChecker.Http.TYPE.equals(cktype)) {
String path = BaseServlet.optional(request, "path", StringUtils.EMPTY);
String headers = BaseServlet.optional(request, "headers", StringUtils.EMPTY);
String expectedResponseCode = BaseServlet.optional(request, "expectedResponseCode", "200");
AbstractHealthCheckConfig.Http config = new AbstractHealthCheckConfig.Http();
AbstractHealthChecker.Http config = new AbstractHealthChecker.Http();
config.setType(cktype);
config.setPath(path);
config.setHeaders(headers);
config.setExpectedResponseCode(Integer.parseInt(expectedResponseCode));
cluster.setHealthChecker(config);
} else if (AbstractHealthCheckConfig.Mysql.TYPE.equals(cktype)) {
} else if (AbstractHealthChecker.Mysql.TYPE.equals(cktype)) {
AbstractHealthCheckConfig.Mysql config = new AbstractHealthCheckConfig.Mysql();
AbstractHealthChecker.Mysql config = new AbstractHealthChecker.Mysql();
String user = BaseServlet.optional(request, "user", StringUtils.EMPTY);
String pwd = BaseServlet.optional(request, "pwd", StringUtils.EMPTY);
String cmd = BaseServlet.optional(request, "cmd", StringUtils.EMPTY);
@ -472,16 +474,18 @@ public class ApiCommands {
String ip = BaseServlet.required(request, "ip");
String port = BaseServlet.required(request, "port");
String weight = BaseServlet.optional(request, "weight", "1");
String cluster = BaseServlet.optional(request, "cluster", UtilsAndCommons.DEFAULT_CLUSTER_NAME);
String cluster = BaseServlet.optional(request, "cluster", StringUtils.EMPTY);
if (StringUtils.isEmpty(cluster)) {
cluster = BaseServlet.required(request, "clusterName");
cluster = BaseServlet.optional(request, "clusterName", UtilsAndCommons.DEFAULT_CLUSTER_NAME);
}
boolean enabled = BooleanUtils.toBoolean(BaseServlet.optional(request, "enable", "true"));
IpAddress ipAddress = new IpAddress();
ipAddress.setPort(Integer.parseInt(port));
ipAddress.setIp(ip);
ipAddress.setWeight(Double.parseDouble(weight));
ipAddress.setClusterName(cluster);
ipAddress.setEnabled(enabled);
return ipAddress;
}
@ -518,16 +522,18 @@ public class ApiCommands {
String tenant = BaseServlet.optional(request, "tid", StringUtils.EMPTY);
String app = BaseServlet.optional(request, "app", "DEFAULT");
String env = BaseServlet.optional(request, "env", StringUtils.EMPTY);
String instanceMetadataJson = BaseServlet.optional(request, "metadata", StringUtils.EMPTY);
String metadata = BaseServlet.optional(request, "metadata", StringUtils.EMPTY);
VirtualClusterDomain virtualClusterDomain = (VirtualClusterDomain) domainsManager.getDomain(dom);
IpAddress ipAddress = getIPAddress(request);
Service service = new Service(dom);
ipAddress.setApp(app);
ipAddress.setService(service);
ipAddress.setInstanceId(ipAddress.generateInstanceId());
ipAddress.setLastBeat(System.currentTimeMillis());
if (StringUtils.isNotEmpty(instanceMetadataJson)) {
ipAddress.setMetadata(JSON.parseObject(instanceMetadataJson, new TypeReference<Map<String, String>>() {
}));
if (StringUtils.isNotEmpty(metadata)) {
ipAddress.setMetadata(UtilsAndCommons.parseMetadata(metadata));
}
Loggers.TENANT.debug("reg-service: " + dom + "|" + ipAddress.toJSON() + "|" + env + "|" + tenant + "|" + app);
@ -622,16 +628,16 @@ public class ApiCommands {
}
if (cktype.equals(AbstractHealthCheckProcessor.HTTP_PROCESSOR.getType())) {
AbstractHealthCheckConfig.Http config = new AbstractHealthCheckConfig.Http();
AbstractHealthChecker.Http config = new AbstractHealthChecker.Http();
config.setType(cktype);
config.setPath(BaseServlet.required(request, "path"));
cluster.setHealthChecker(config);
} else if (cktype.equals(AbstractHealthCheckProcessor.TCP_PROCESSOR.getType())) {
AbstractHealthCheckConfig.Tcp config = new AbstractHealthCheckConfig.Tcp();
AbstractHealthChecker.Tcp config = new AbstractHealthChecker.Tcp();
config.setType(cktype);
cluster.setHealthChecker(config);
} else if (cktype.equals(AbstractHealthCheckProcessor.MYSQL_PROCESSOR.getType())) {
AbstractHealthCheckConfig.Mysql config = new AbstractHealthCheckConfig.Mysql();
AbstractHealthChecker.Mysql config = new AbstractHealthChecker.Mysql();
config.setCmd(BaseServlet.required(request, "cmd"));
config.setPwd(BaseServlet.required(request, "pwd"));
config.setUser(BaseServlet.required(request, "user"));
@ -1044,7 +1050,7 @@ public class ApiCommands {
ipObj.put("valid", ip.isValid());
ipObj.put("weight", ip.getWeight());
ipObj.put("doubleWeight", ip.getWeight());
ipObj.put("instanceId", ip.generateInstanceId());
ipObj.put("instanceId", ip.getInstanceId());
ipObj.put("metadata", ip.getMetadata());
ipArray.add(ipObj);
}
@ -1176,10 +1182,9 @@ public class ApiCommands {
ipObj.put("port", ip.getPort());
ipObj.put("valid", entry.getKey());
ipObj.put("marked", ip.isMarked());
ipObj.put("instanceId", ip.generateInstanceId());
ipObj.put("instanceId", ip.getInstanceId());
ipObj.put("metadata", ip.getMetadata());
double weight = ip.getWeight();
ipObj.put("enabled", ip.isEnabled());
ipObj.put("weight", ip.getWeight());
hosts.add(ipObj);
@ -1824,14 +1829,14 @@ public class ApiCommands {
private Cluster getClusterFromJson(String json) {
JSONObject object = JSON.parseObject(json);
String type = object.getJSONObject("healthChecker").getString("type");
AbstractHealthCheckConfig abstractHealthCheckConfig;
AbstractHealthChecker abstractHealthCheckConfig;
if (type.equals(HealthCheckType.HTTP.name())) {
abstractHealthCheckConfig = JSON.parseObject(object.getString("healthChecker"), AbstractHealthCheckConfig.Http.class);
abstractHealthCheckConfig = JSON.parseObject(object.getString("healthChecker"), AbstractHealthChecker.Http.class);
} else if (type.equals(HealthCheckType.TCP.name())) {
abstractHealthCheckConfig = JSON.parseObject(object.getString("healthChecker"), AbstractHealthCheckConfig.Tcp.class);
abstractHealthCheckConfig = JSON.parseObject(object.getString("healthChecker"), AbstractHealthChecker.Tcp.class);
} else if (type.equals(HealthCheckType.MYSQL.name())) {
abstractHealthCheckConfig = JSON.parseObject(object.getString("healthChecker"), AbstractHealthCheckConfig.Mysql.class);
abstractHealthCheckConfig = JSON.parseObject(object.getString("healthChecker"), AbstractHealthChecker.Mysql.class);
} else {
throw new IllegalArgumentException("can not prase cluster from json: " + json);
}
@ -1889,18 +1894,18 @@ public class ApiCommands {
}
if (StringUtils.equals(cktype, HealthCheckType.HTTP.name())) {
AbstractHealthCheckConfig.Http config = new AbstractHealthCheckConfig.Http();
AbstractHealthChecker.Http config = new AbstractHealthChecker.Http();
config.setType(cktype);
config.setPath(path);
config.setHeaders(headers);
config.setExpectedResponseCode(Integer.parseInt(expectedResponseCode));
cluster.setHealthChecker(config);
} else if (StringUtils.equals(cktype, HealthCheckType.TCP.name())) {
AbstractHealthCheckConfig.Tcp config = new AbstractHealthCheckConfig.Tcp();
AbstractHealthChecker.Tcp config = new AbstractHealthChecker.Tcp();
config.setType(cktype);
cluster.setHealthChecker(config);
} else if (StringUtils.equals(cktype, HealthCheckType.MYSQL.name())) {
AbstractHealthCheckConfig.Mysql config = new AbstractHealthCheckConfig.Mysql();
AbstractHealthChecker.Mysql config = new AbstractHealthChecker.Mysql();
String cmd = BaseServlet.required(request, "cmd");
String pwd = BaseServlet.required(request, "pwd");
String user = BaseServlet.required(request, "user");

View File

@ -1,4 +1,4 @@
server.port=8080
server.port=8848
server.servlet.context-path=/nacos

View File

@ -82,7 +82,7 @@ public class InstanceControllerTest extends BaseTest {
ipAddress.setPort(9999);
List<IpAddress> ipList = new ArrayList<IpAddress>();
ipList.add(ipAddress);
domain.updateIPs(ipList, false);
domain.updateIPs(ipList);
Mockito.when(domainsManager.getDomain("nacos.test.1")).thenReturn(domain);
@ -129,7 +129,7 @@ public class InstanceControllerTest extends BaseTest {
ipAddress.setWeight(2.0);
List<IpAddress> ipList = new ArrayList<IpAddress>();
ipList.add(ipAddress);
domain.updateIPs(ipList, false);
domain.updateIPs(ipList);
Mockito.when(domainsManager.getDomain("nacos.test.1")).thenReturn(domain);

View File

@ -15,7 +15,7 @@
*/
package com.alibaba.nacos.naming.core;
import com.alibaba.nacos.naming.healthcheck.AbstractHealthCheckConfig;
import com.alibaba.nacos.api.naming.pojo.AbstractHealthChecker;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@ -50,7 +50,7 @@ public class ClusterTest {
Cluster newCluster = new Cluster();
newCluster.setDefCkport(8888);
newCluster.setDefIPPort(9999);
AbstractHealthCheckConfig.Http healthCheckConfig = new AbstractHealthCheckConfig.Http();
AbstractHealthChecker.Http healthCheckConfig = new AbstractHealthChecker.Http();
healthCheckConfig.setPath("/nacos-path-1");
healthCheckConfig.setExpectedResponseCode(500);
healthCheckConfig.setHeaders("Client-Version:nacos-test-1");
@ -65,8 +65,8 @@ public class ClusterTest {
Assert.assertEquals(8888, cluster.getDefCkport());
Assert.assertEquals(9999, cluster.getDefIPPort());
Assert.assertTrue(cluster.getHealthChecker() instanceof AbstractHealthCheckConfig.Http);
AbstractHealthCheckConfig.Http httpHealthCheck = (AbstractHealthCheckConfig.Http)(cluster.getHealthChecker());
Assert.assertTrue(cluster.getHealthChecker() instanceof AbstractHealthChecker.Http);
AbstractHealthChecker.Http httpHealthCheck = (AbstractHealthChecker.Http)(cluster.getHealthChecker());
Assert.assertEquals("/nacos-path-1", httpHealthCheck.getPath());
Assert.assertEquals(500, httpHealthCheck.getExpectedResponseCode());
Assert.assertEquals("Client-Version:nacos-test-1", httpHealthCheck.getHeaders());
@ -87,7 +87,7 @@ public class ClusterTest {
list.add(ipAddress1);
list.add(ipAddress2);
cluster.updateIPs(list, false);
cluster.updateIPs(list);
List<IpAddress> ips = cluster.allIPs();
Assert.assertNotNull(ips);

View File

@ -15,6 +15,7 @@
*/
package com.alibaba.nacos.test.naming;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingFactory;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
@ -29,7 +30,9 @@ import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import static com.alibaba.nacos.test.naming.NamingBase.*;
@ -56,6 +59,48 @@ public class SelectInstances_ITCase {
}
}
@Test
public void getAllInstances() throws Exception {
// final String serviceName = "dungu.test.100";
// naming.registerInstance(serviceName, "127.0.0.1", TEST_PORT);
//
//
//
// Thread thread = new Thread(new Runnable() {
// @Override
// public void run() {
// try {
// TimeUnit.SECONDS.sleep(10);
// naming.deregisterInstance(serviceName, "127.0.0.1", TEST_PORT);
// System.out.println("deregister ok!");
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
// });
//
// thread.start();
//
//
// while (true) {
//
// System.out.println(new Date());
// System.out.println(naming.getAllInstances("dungu.test.100"));
//
// TimeUnit.SECONDS.sleep(1);
// }
}
@Test
public void deregister() throws NacosException {
// String serviceName = "dungu.test.100";
// naming.deregisterInstance(serviceName, "127.0.0.1", TEST_PORT);
}
/**
* 获取所有健康的Instance
* @throws Exception