mirror of https://github.com/apache/cassandra
Unregistering a node should also remove it from tokenMap if it is there and recalculate the placements
Patch by marcuse; reviewed by Sam Tunnicliffe for CASSANDRA-20346
This commit is contained in:
parent
369daf56bc
commit
ca227d82ba
|
|
@ -1,4 +1,5 @@
|
||||||
5.1
|
5.1
|
||||||
|
* Unregistering a node should also remove it from tokenMap if it is there and recalculate the placements (CASSANDRA-20346)
|
||||||
* Fix PartitionUpdate.isEmpty deserialization issue to avoid potential EOFException (CASSANDRA-20345)
|
* Fix PartitionUpdate.isEmpty deserialization issue to avoid potential EOFException (CASSANDRA-20345)
|
||||||
* Avoid adding LEFT nodes to tokenMap on upgrade from gossip (CASSANDRA-20344)
|
* Avoid adding LEFT nodes to tokenMap on upgrade from gossip (CASSANDRA-20344)
|
||||||
* Allow empty placements when deserializing cluster metadata (CASSANDRA-20343)
|
* Allow empty placements when deserializing cluster metadata (CASSANDRA-20343)
|
||||||
|
|
|
||||||
|
|
@ -1630,7 +1630,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE
|
||||||
throw new RuntimeException("Can't abort bootstrap for " + nodeId + " since it is not bootstrapping");
|
throw new RuntimeException("Can't abort bootstrap for " + nodeId + " since it is not bootstrapping");
|
||||||
ClusterMetadataService.instance().commit(new CancelInProgressSequence(nodeId));
|
ClusterMetadataService.instance().commit(new CancelInProgressSequence(nodeId));
|
||||||
}
|
}
|
||||||
ClusterMetadataService.instance().commit(new Unregister(nodeId, EnumSet.of(REGISTERED, BOOTSTRAPPING, BOOT_REPLACING)));
|
ClusterMetadataService.instance().commit(new Unregister(nodeId, EnumSet.of(REGISTERED, BOOTSTRAPPING, BOOT_REPLACING), ClusterMetadataService.instance().placementProvider()));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new RuntimeException("Can't abort bootstrap for node " + nodeId + " since the state is " + nodeState);
|
throw new RuntimeException("Can't abort bootstrap for node " + nodeId + " since the state is " + nodeState);
|
||||||
|
|
|
||||||
|
|
@ -252,7 +252,7 @@ public class CMSOperations implements CMSOperationsMBean
|
||||||
for (NodeId nodeId : nodeIds)
|
for (NodeId nodeId : nodeIds)
|
||||||
{
|
{
|
||||||
logger.info("Unregistering " + nodeId);
|
logger.info("Unregistering " + nodeId);
|
||||||
cms.commit(new Unregister(nodeId, EnumSet.of(NodeState.LEFT)));
|
cms.commit(new Unregister(nodeId, EnumSet.of(NodeState.LEFT), ClusterMetadataService.instance().placementProvider()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -411,7 +411,9 @@ public class ClusterMetadata
|
||||||
|
|
||||||
public Transformer unregister(NodeId nodeId)
|
public Transformer unregister(NodeId nodeId)
|
||||||
{
|
{
|
||||||
directory = directory.without(nodeId);
|
directory = directory.withoutRackAndDC(nodeId).without(nodeId);
|
||||||
|
if (!tokenMap.tokens(nodeId).isEmpty())
|
||||||
|
tokenMap = tokenMap.unassignTokens(nodeId);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -252,7 +252,11 @@ public class Directory implements MetadataValue<Directory>
|
||||||
{
|
{
|
||||||
InetAddressAndPort endpoint = peers.get(id);
|
InetAddressAndPort endpoint = peers.get(id);
|
||||||
Location location = locations.get(id);
|
Location location = locations.get(id);
|
||||||
|
if (location == null)
|
||||||
|
return this;
|
||||||
BTreeMultimap<String, InetAddressAndPort> rackEP = (BTreeMultimap<String, InetAddressAndPort>) racksByDC.get(location.datacenter);
|
BTreeMultimap<String, InetAddressAndPort> rackEP = (BTreeMultimap<String, InetAddressAndPort>) racksByDC.get(location.datacenter);
|
||||||
|
if (rackEP == null)
|
||||||
|
return this;
|
||||||
rackEP = rackEP.without(location.rack, endpoint);
|
rackEP = rackEP.without(location.rack, endpoint);
|
||||||
BTreeMap<String, Multimap<String, InetAddressAndPort>> newRacksByDC;
|
BTreeMap<String, Multimap<String, InetAddressAndPort>> newRacksByDC;
|
||||||
if (rackEP.isEmpty())
|
if (rackEP.isEmpty())
|
||||||
|
|
|
||||||
|
|
@ -115,8 +115,7 @@ public class Register implements Transformation
|
||||||
if (nodeId == null || metadata.directory.peerState(nodeId) == NodeState.LEFT)
|
if (nodeId == null || metadata.directory.peerState(nodeId) == NodeState.LEFT)
|
||||||
{
|
{
|
||||||
if (nodeId != null)
|
if (nodeId != null)
|
||||||
ClusterMetadataService.instance().commit(new Unregister(nodeId, EnumSet.of(NodeState.LEFT)));
|
ClusterMetadataService.instance().commit(new Unregister(nodeId, EnumSet.of(NodeState.LEFT), ClusterMetadataService.instance().placementProvider()));
|
||||||
|
|
||||||
Register registration = new Register(nodeAddresses, location, nodeVersion);
|
Register registration = new Register(nodeAddresses, location, nodeVersion);
|
||||||
nodeId = ClusterMetadataService.instance().commit(registration).directory.peerId(nodeAddresses.broadcastAddress);
|
nodeId = ClusterMetadataService.instance().commit(registration).directory.peerId(nodeAddresses.broadcastAddress);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,8 @@ import org.apache.cassandra.tcm.ClusterMetadataService;
|
||||||
import org.apache.cassandra.tcm.Transformation;
|
import org.apache.cassandra.tcm.Transformation;
|
||||||
import org.apache.cassandra.tcm.membership.NodeId;
|
import org.apache.cassandra.tcm.membership.NodeId;
|
||||||
import org.apache.cassandra.tcm.membership.NodeState;
|
import org.apache.cassandra.tcm.membership.NodeState;
|
||||||
|
import org.apache.cassandra.tcm.ownership.DataPlacements;
|
||||||
|
import org.apache.cassandra.tcm.ownership.PlacementProvider;
|
||||||
import org.apache.cassandra.tcm.sequences.LockedRanges;
|
import org.apache.cassandra.tcm.sequences.LockedRanges;
|
||||||
import org.apache.cassandra.tcm.serialization.AsymmetricMetadataSerializer;
|
import org.apache.cassandra.tcm.serialization.AsymmetricMetadataSerializer;
|
||||||
import org.apache.cassandra.tcm.serialization.Version;
|
import org.apache.cassandra.tcm.serialization.Version;
|
||||||
|
|
@ -43,11 +45,13 @@ public class Unregister implements Transformation
|
||||||
|
|
||||||
private final NodeId nodeId;
|
private final NodeId nodeId;
|
||||||
private final EnumSet<NodeState> allowedNodeStartStates;
|
private final EnumSet<NodeState> allowedNodeStartStates;
|
||||||
|
private final PlacementProvider placementProvider;
|
||||||
|
|
||||||
public Unregister(NodeId nodeId, EnumSet<NodeState> allowedNodeStartStates)
|
public Unregister(NodeId nodeId, EnumSet<NodeState> allowedNodeStartStates, PlacementProvider placementProvider)
|
||||||
{
|
{
|
||||||
this.nodeId = nodeId;
|
this.nodeId = nodeId;
|
||||||
this.allowedNodeStartStates = allowedNodeStartStates;
|
this.allowedNodeStartStates = allowedNodeStartStates;
|
||||||
|
this.placementProvider = placementProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -67,6 +71,15 @@ public class Unregister implements Transformation
|
||||||
return new Transformation.Rejected(INVALID, "Can't unregister " + nodeId + " - node state is " + startState + " not " + allowedNodeStartStates);
|
return new Transformation.Rejected(INVALID, "Can't unregister " + nodeId + " - node state is " + startState + " not " + allowedNodeStartStates);
|
||||||
|
|
||||||
ClusterMetadata.Transformer next = prev.transformer().unregister(nodeId);
|
ClusterMetadata.Transformer next = prev.transformer().unregister(nodeId);
|
||||||
|
if (!prev.tokenMap.tokens(nodeId).isEmpty())
|
||||||
|
{
|
||||||
|
ClusterMetadata nextMetadata = next.build().metadata;
|
||||||
|
DataPlacements placements = placementProvider.calculatePlacements(nextMetadata.epoch,
|
||||||
|
nextMetadata.tokenMap.toRanges(),
|
||||||
|
nextMetadata,
|
||||||
|
nextMetadata.schema.getKeyspaces());
|
||||||
|
next = next.with(placements);
|
||||||
|
}
|
||||||
|
|
||||||
return Transformation.success(next, LockedRanges.AffectedRanges.EMPTY);
|
return Transformation.success(next, LockedRanges.AffectedRanges.EMPTY);
|
||||||
}
|
}
|
||||||
|
|
@ -78,7 +91,7 @@ public class Unregister implements Transformation
|
||||||
public static void unregister(NodeId nodeId)
|
public static void unregister(NodeId nodeId)
|
||||||
{
|
{
|
||||||
ClusterMetadataService.instance()
|
ClusterMetadataService.instance()
|
||||||
.commit(new Unregister(nodeId, EnumSet.allOf(NodeState.class)));
|
.commit(new Unregister(nodeId, EnumSet.allOf(NodeState.class), ClusterMetadataService.instance().placementProvider()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString()
|
public String toString()
|
||||||
|
|
@ -113,7 +126,7 @@ public class Unregister implements Transformation
|
||||||
states.add(NodeState.valueOf(in.readUTF()));
|
states.add(NodeState.valueOf(in.readUTF()));
|
||||||
}
|
}
|
||||||
NodeId nodeId = NodeId.serializer.deserialize(in, version);
|
NodeId nodeId = NodeId.serializer.deserialize(in, version);
|
||||||
return new Unregister(nodeId, version.isAtLeast(Version.V2) ? states : EnumSet.allOf(NodeState.class));
|
return new Unregister(nodeId, version.isAtLeast(Version.V2) ? states : EnumSet.allOf(NodeState.class), ClusterMetadataService.instance().placementProvider());
|
||||||
}
|
}
|
||||||
|
|
||||||
public long serializedSize(Transformation t, Version version)
|
public long serializedSize(Transformation t, Version version)
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ import org.apache.cassandra.tcm.membership.NodeAddresses;
|
||||||
import org.apache.cassandra.tcm.membership.NodeId;
|
import org.apache.cassandra.tcm.membership.NodeId;
|
||||||
import org.apache.cassandra.tcm.membership.NodeState;
|
import org.apache.cassandra.tcm.membership.NodeState;
|
||||||
import org.apache.cassandra.tcm.membership.NodeVersion;
|
import org.apache.cassandra.tcm.membership.NodeVersion;
|
||||||
|
import org.apache.cassandra.tcm.ownership.PlacementProvider;
|
||||||
import org.apache.cassandra.tcm.sequences.LeaveStreams;
|
import org.apache.cassandra.tcm.sequences.LeaveStreams;
|
||||||
import org.apache.cassandra.tcm.sequences.UnbootstrapAndLeave;
|
import org.apache.cassandra.tcm.sequences.UnbootstrapAndLeave;
|
||||||
import org.apache.cassandra.tcm.serialization.Version;
|
import org.apache.cassandra.tcm.serialization.Version;
|
||||||
|
|
@ -75,15 +76,16 @@ public class RegisterTest extends TestBaseImpl
|
||||||
for (int i : new int[]{ 3, 2 })
|
for (int i : new int[]{ 3, 2 })
|
||||||
{
|
{
|
||||||
cluster.get(i).runOnInstance(() -> {
|
cluster.get(i).runOnInstance(() -> {
|
||||||
|
PlacementProvider pp = ClusterMetadataService.instance().placementProvider();
|
||||||
ClusterMetadataService.instance().commit(new PrepareLeave(ClusterMetadata.current().myNodeId(),
|
ClusterMetadataService.instance().commit(new PrepareLeave(ClusterMetadata.current().myNodeId(),
|
||||||
true,
|
true,
|
||||||
ClusterMetadataService.instance().placementProvider(),
|
pp,
|
||||||
LeaveStreams.Kind.UNBOOTSTRAP));
|
LeaveStreams.Kind.UNBOOTSTRAP));
|
||||||
UnbootstrapAndLeave unbootstrapAndLeave = (UnbootstrapAndLeave) ClusterMetadata.current().inProgressSequences.get(ClusterMetadata.current().myNodeId());
|
UnbootstrapAndLeave unbootstrapAndLeave = (UnbootstrapAndLeave) ClusterMetadata.current().inProgressSequences.get(ClusterMetadata.current().myNodeId());
|
||||||
ClusterMetadataService.instance().commit(unbootstrapAndLeave.startLeave);
|
ClusterMetadataService.instance().commit(unbootstrapAndLeave.startLeave);
|
||||||
ClusterMetadataService.instance().commit(unbootstrapAndLeave.midLeave);
|
ClusterMetadataService.instance().commit(unbootstrapAndLeave.midLeave);
|
||||||
ClusterMetadataService.instance().commit(unbootstrapAndLeave.finishLeave);
|
ClusterMetadataService.instance().commit(unbootstrapAndLeave.finishLeave);
|
||||||
ClusterMetadataService.instance().commit(new Unregister(ClusterMetadata.current().myNodeId(), EnumSet.of(NodeState.LEFT)));
|
ClusterMetadataService.instance().commit(new Unregister(ClusterMetadata.current().myNodeId(), EnumSet.of(NodeState.LEFT), pp));
|
||||||
});
|
});
|
||||||
|
|
||||||
cluster.get(1).runOnInstance(() -> {
|
cluster.get(1).runOnInstance(() -> {
|
||||||
|
|
@ -142,7 +144,7 @@ public class RegisterTest extends TestBaseImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we unregister oldNode, then the ceiling for serialization version will rise
|
// If we unregister oldNode, then the ceiling for serialization version will rise
|
||||||
ClusterMetadataService.instance().commit(new Unregister(oldNode, EnumSet.allOf(NodeState.class)));
|
ClusterMetadataService.instance().commit(new Unregister(oldNode, EnumSet.allOf(NodeState.class), ClusterMetadataService.instance().placementProvider()));
|
||||||
assertEquals(ClusterMetadata.current().directory.clusterMinVersion.serializationVersion,
|
assertEquals(ClusterMetadata.current().directory.clusterMinVersion.serializationVersion,
|
||||||
NodeVersion.CURRENT_METADATA_VERSION.asInt());
|
NodeVersion.CURRENT_METADATA_VERSION.asInt());
|
||||||
bytes = t.kind().toVersionedBytes(t);
|
bytes = t.kind().toVersionedBytes(t);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,148 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cassandra.tcm;
|
||||||
|
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
import com.google.common.collect.Streams;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.apache.cassandra.dht.Murmur3Partitioner;
|
||||||
|
import org.apache.cassandra.dht.Token;
|
||||||
|
import org.apache.cassandra.locator.InetAddressAndPort;
|
||||||
|
import org.apache.cassandra.schema.DistributedMetadataLogKeyspace;
|
||||||
|
import org.apache.cassandra.schema.DistributedSchema;
|
||||||
|
import org.apache.cassandra.schema.KeyspaceMetadata;
|
||||||
|
import org.apache.cassandra.schema.KeyspaceParams;
|
||||||
|
import org.apache.cassandra.schema.Keyspaces;
|
||||||
|
import org.apache.cassandra.tcm.membership.Directory;
|
||||||
|
import org.apache.cassandra.tcm.membership.Location;
|
||||||
|
import org.apache.cassandra.tcm.membership.NodeAddresses;
|
||||||
|
import org.apache.cassandra.tcm.membership.NodeId;
|
||||||
|
import org.apache.cassandra.tcm.membership.NodeState;
|
||||||
|
import org.apache.cassandra.tcm.membership.NodeVersion;
|
||||||
|
import org.apache.cassandra.tcm.ownership.UniformRangePlacement;
|
||||||
|
import org.apache.cassandra.tcm.transformations.Assassinate;
|
||||||
|
import org.apache.cassandra.tcm.transformations.Register;
|
||||||
|
import org.apache.cassandra.tcm.transformations.Unregister;
|
||||||
|
import org.apache.cassandra.tcm.transformations.UnsafeJoin;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
|
||||||
|
public class UnregisterTest
|
||||||
|
{
|
||||||
|
private final KeyspaceMetadata ksm = KeyspaceMetadata.create("ks_nts", KeyspaceParams.nts("dc1", 3, "dc2", 3));
|
||||||
|
private final Keyspaces kss = Keyspaces.of(DistributedMetadataLogKeyspace.initialMetadata(Sets.newHashSet("dc1", "dc2")), ksm);
|
||||||
|
private final DistributedSchema initialSchema = new DistributedSchema(kss);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBasicUnregister()
|
||||||
|
{
|
||||||
|
unregisterHelper((toUnregister, metadata) -> {
|
||||||
|
metadata = assassinate(toUnregister, metadata);
|
||||||
|
metadata = unregister(toUnregister, metadata);
|
||||||
|
return metadata;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void badStateUnregister()
|
||||||
|
{
|
||||||
|
unregisterHelper((toUnregister, metadata) -> {
|
||||||
|
metadata = left(toUnregister, metadata);
|
||||||
|
metadata = unregister(toUnregister, metadata);
|
||||||
|
return metadata;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void unregisterHelper(BiFunction<Integer, ClusterMetadata, ClusterMetadata> f)
|
||||||
|
{
|
||||||
|
ClusterMetadata metadata = new ClusterMetadata(Murmur3Partitioner.instance, Directory.EMPTY, initialSchema);
|
||||||
|
|
||||||
|
int toUnregister = 5;
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
// node we're unregistering is only node in dc2 - make sure the dc is gone after unregistration
|
||||||
|
String dc = "dc"+(i == toUnregister ? "2" :"1");
|
||||||
|
metadata = unsafejoin(i, register(i, dc, metadata));
|
||||||
|
}
|
||||||
|
Token t = token(toUnregister, metadata);
|
||||||
|
InetAddressAndPort ep = ep(toUnregister);
|
||||||
|
NodeId nodeId = metadata.directory.peerId(ep(toUnregister));
|
||||||
|
metadata = f.apply(toUnregister, metadata);
|
||||||
|
assertNoTrace(ep, nodeId, t, metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClusterMetadata left(int i, ClusterMetadata metadata)
|
||||||
|
{
|
||||||
|
NodeId nodeId = metadata.directory.peerId(ep(i));
|
||||||
|
ClusterMetadata.Transformer t = metadata.transformer().withNodeState(nodeId, NodeState.LEFT);
|
||||||
|
return t.build().metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertNoTrace(InetAddressAndPort ep, NodeId nodeId, Token t, ClusterMetadata metadata)
|
||||||
|
{
|
||||||
|
assertNull(metadata.tokenMap.owner(t));
|
||||||
|
assertFalse(metadata.directory.states.containsKey(nodeId));
|
||||||
|
assertFalse(metadata.directory.peerIds().contains(nodeId));
|
||||||
|
assertFalse(metadata.directory.allAddresses().contains(ep));
|
||||||
|
assertFalse(metadata.directory.allJoinedEndpoints().contains(ep));
|
||||||
|
assertFalse(metadata.directory.allDatacenterRacks().containsKey("dc2"));
|
||||||
|
assertFalse(metadata.directory.knownDatacenters().contains("dc2"));
|
||||||
|
metadata.placements.asMap().forEach((params, placement) -> {
|
||||||
|
assertFalse(Streams.concat(placement.writes.endpoints.stream(), placement.reads.endpoints.stream()).anyMatch((fr) -> fr.endpoints().contains(ep)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Token token(int i, ClusterMetadata metadata)
|
||||||
|
{
|
||||||
|
NodeId nodeId = metadata.directory.peerId(ep(i));
|
||||||
|
return metadata.tokenMap.tokens(nodeId).iterator().next();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClusterMetadata assassinate(int i, ClusterMetadata metadata)
|
||||||
|
{
|
||||||
|
return new Assassinate(metadata.directory.peerId(ep(i)), new UniformRangePlacement()).execute(metadata).success().metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClusterMetadata unregister(int i, ClusterMetadata metadata )
|
||||||
|
{
|
||||||
|
return new Unregister(metadata.directory.peerId(ep(i)), EnumSet.of(NodeState.LEFT), new UniformRangePlacement()).execute(metadata).success().metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClusterMetadata register(int i, String dc, ClusterMetadata metadata)
|
||||||
|
{
|
||||||
|
return new Register(new NodeAddresses(ep(i)), new Location(dc, "rack1"), NodeVersion.CURRENT).execute(metadata).success().metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClusterMetadata unsafejoin(int i, ClusterMetadata metadata)
|
||||||
|
{
|
||||||
|
NodeId nodeId = metadata.directory.peerId(ep(i));
|
||||||
|
return new UnsafeJoin(nodeId, ImmutableSet.of(Murmur3Partitioner.instance.getRandomToken()), new UniformRangePlacement()).execute(metadata).success().metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
private InetAddressAndPort ep(int i)
|
||||||
|
{
|
||||||
|
return InetAddressAndPort.getByNameUnchecked("127.0.0."+i);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue