mirror of
https://github.com/corda/corda.git
synced 2025-06-17 14:48:16 +00:00
Support HA without load balancer (#3889)
Allow configuration in node for additional advertised addresses. fix logic error Use empty list as default config not null Allow multiple addresses in NodeInfo Describe new additionalP2PAddresses property in docs. Add integration test of additionalP2PAddress feature Fixup after rebase Address PR comment Address PR comments by removing unused element of NodeAddress
This commit is contained in:
@ -193,7 +193,7 @@ class AMQPBridgeTest {
|
||||
if (sourceQueueName != null) {
|
||||
// Local queue for outgoing messages
|
||||
artemis.session.createQueue(sourceQueueName, RoutingType.ANYCAST, sourceQueueName, true)
|
||||
bridgeManager.deployBridge(sourceQueueName, amqpAddress, setOf(BOB.name))
|
||||
bridgeManager.deployBridge(sourceQueueName, listOf(amqpAddress), setOf(BOB.name))
|
||||
}
|
||||
return Triple(artemisServer, artemisClient, bridgeManager)
|
||||
}
|
||||
|
@ -0,0 +1,70 @@
|
||||
package net.corda.services.messaging
|
||||
|
||||
import com.typesafe.config.ConfigValueFactory
|
||||
import junit.framework.TestCase.assertEquals
|
||||
import net.corda.client.rpc.CordaRPCClient
|
||||
import net.corda.core.contracts.Amount
|
||||
import net.corda.core.contracts.Issued
|
||||
import net.corda.core.contracts.withoutIssuer
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.finance.DOLLARS
|
||||
import net.corda.finance.contracts.asset.Cash
|
||||
import net.corda.finance.flows.CashIssueAndPaymentFlow
|
||||
import net.corda.node.services.Permissions.Companion.all
|
||||
import net.corda.testing.core.DUMMY_BANK_A_NAME
|
||||
import net.corda.testing.core.DUMMY_BANK_B_NAME
|
||||
import net.corda.testing.core.expect
|
||||
import net.corda.testing.core.expectEvents
|
||||
import net.corda.testing.driver.DriverParameters
|
||||
import net.corda.testing.driver.PortAllocation
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.node.User
|
||||
import org.junit.Test
|
||||
import java.util.*
|
||||
|
||||
class AdditionP2PAddressModeTest {
|
||||
private val portAllocation = PortAllocation.Incremental(27182)
|
||||
@Test
|
||||
fun `runs nodes with one configured to use additionalP2PAddresses`() {
|
||||
val testUser = User("test", "test", setOf(all()))
|
||||
driver(DriverParameters(startNodesInProcess = true, inMemoryDB = true, extraCordappPackagesToScan = listOf("net.corda.finance"))) {
|
||||
val mainAddress = portAllocation.nextHostAndPort().toString()
|
||||
val altAddress = portAllocation.nextHostAndPort().toString()
|
||||
val haConfig = mutableMapOf<String, Any?>()
|
||||
haConfig["detectPublicIp"] = false
|
||||
haConfig["p2pAddress"] = mainAddress //advertise this as primary
|
||||
haConfig["messagingServerAddress"] = altAddress // but actually host on the alternate address
|
||||
haConfig["messagingServerExternal"] = false
|
||||
haConfig["additionalP2PAddresses"] = ConfigValueFactory.fromIterable(listOf(altAddress)) // advertise this secondary address
|
||||
|
||||
val (nodeA, nodeB) = listOf(
|
||||
startNode(providedName = DUMMY_BANK_A_NAME, rpcUsers = listOf(testUser), customOverrides = haConfig),
|
||||
startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = listOf(testUser), customOverrides = mapOf("p2pAddress" to portAllocation.nextHostAndPort().toString()))
|
||||
).map { it.getOrThrow() }
|
||||
val (nodeARpc, nodeBRpc) = listOf(nodeA, nodeB).map {
|
||||
val client = CordaRPCClient(it.rpcAddress)
|
||||
client.start(testUser.username, testUser.password).proxy
|
||||
}
|
||||
|
||||
val nodeBVaultUpdates = nodeBRpc.vaultTrack(Cash.State::class.java).updates
|
||||
|
||||
val issueRef = OpaqueBytes.of(0.toByte())
|
||||
nodeARpc.startFlowDynamic(
|
||||
CashIssueAndPaymentFlow::class.java,
|
||||
DOLLARS(1234),
|
||||
issueRef,
|
||||
nodeB.nodeInfo.legalIdentities.get(0),
|
||||
true,
|
||||
defaultNotaryIdentity
|
||||
).returnValue.getOrThrow()
|
||||
nodeBVaultUpdates.expectEvents {
|
||||
expect { update ->
|
||||
println("Bob got vault update of $update")
|
||||
val amount: Amount<Issued<Currency>> = update.produced.first().state.data.amount
|
||||
assertEquals(1234.DOLLARS, amount.withoutIssuer())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -42,11 +42,7 @@ import net.corda.node.services.Permissions
|
||||
import net.corda.node.services.api.FlowStarter
|
||||
import net.corda.node.services.api.ServiceHubInternal
|
||||
import net.corda.node.services.api.StartedNodeServices
|
||||
import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.node.services.config.SecurityConfiguration
|
||||
import net.corda.node.services.config.shouldInitCrashShell
|
||||
import net.corda.node.services.config.shouldStartLocalShell
|
||||
import net.corda.node.services.config.JmxReporterType
|
||||
import net.corda.node.services.config.*
|
||||
import net.corda.node.services.messaging.*
|
||||
import net.corda.node.services.rpc.ArtemisRpcBroker
|
||||
import net.corda.node.utilities.AddressUtils
|
||||
@ -58,8 +54,8 @@ import net.corda.nodeapi.internal.addShutdownHook
|
||||
import net.corda.nodeapi.internal.bridging.BridgeControlListener
|
||||
import net.corda.nodeapi.internal.config.User
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
import net.corda.serialization.internal.*
|
||||
import net.corda.nodeapi.internal.persistence.CouldNotCreateDataSourceException
|
||||
import net.corda.serialization.internal.*
|
||||
import org.h2.jdbc.JdbcSQLException
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
@ -71,10 +67,10 @@ import java.net.InetAddress
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.time.Clock
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import javax.management.ObjectName
|
||||
import kotlin.system.exitProcess
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class NodeWithInfo(val node: Node, val info: NodeInfo) {
|
||||
val services: StartedNodeServices = object : StartedNodeServices, ServiceHubInternal by node.services, FlowStarter by node.flowStarter {}
|
||||
@ -282,7 +278,7 @@ open class Node(configuration: NodeConfiguration,
|
||||
}
|
||||
}
|
||||
|
||||
override fun myAddresses(): List<NetworkHostAndPort> = listOf(getAdvertisedAddress())
|
||||
override fun myAddresses(): List<NetworkHostAndPort> = listOf(getAdvertisedAddress()) + configuration.additionalP2PAddresses
|
||||
|
||||
private fun getAdvertisedAddress(): NetworkHostAndPort {
|
||||
return with(configuration) {
|
||||
|
@ -50,6 +50,7 @@ interface NodeConfiguration {
|
||||
val notary: NotaryConfig?
|
||||
val additionalNodeInfoPollingFrequencyMsec: Long
|
||||
val p2pAddress: NetworkHostAndPort
|
||||
val additionalP2PAddresses: List<NetworkHostAndPort>
|
||||
val rpcOptions: NodeRpcOptions
|
||||
val messagingServerAddress: NetworkHostAndPort?
|
||||
val messagingServerExternal: Boolean
|
||||
@ -198,6 +199,7 @@ data class NodeConfigurationImpl(
|
||||
override val verifierType: VerifierType,
|
||||
override val flowTimeout: FlowTimeoutConfiguration,
|
||||
override val p2pAddress: NetworkHostAndPort,
|
||||
override val additionalP2PAddresses: List<NetworkHostAndPort> = emptyList(),
|
||||
private val rpcAddress: NetworkHostAndPort? = null,
|
||||
private val rpcSettings: NodeRpcSettings,
|
||||
override val messagingServerAddress: NetworkHostAndPort?,
|
||||
|
@ -120,7 +120,7 @@ class P2PMessagingClient(val config: NodeConfiguration,
|
||||
private lateinit var advertisedAddress: NetworkHostAndPort
|
||||
private var maxMessageSize: Int = -1
|
||||
|
||||
override val myAddress: SingleMessageRecipient get() = NodeAddress(myIdentity, advertisedAddress)
|
||||
override val myAddress: SingleMessageRecipient get() = NodeAddress(myIdentity)
|
||||
override val ourSenderUUID = UUID.randomUUID().toString()
|
||||
|
||||
private val state = ThreadBox(InnerState())
|
||||
@ -233,7 +233,7 @@ class P2PMessagingClient(val config: NodeConfiguration,
|
||||
fun gatherAddresses(node: NodeInfo): Sequence<BridgeEntry> {
|
||||
return state.locked {
|
||||
node.legalIdentitiesAndCerts.map {
|
||||
val messagingAddress = NodeAddress(it.party.owningKey, node.addresses.first())
|
||||
val messagingAddress = NodeAddress(it.party.owningKey)
|
||||
BridgeEntry(messagingAddress.queueName, node.addresses, node.legalIdentities.map { it.name })
|
||||
}.filter { producerSession!!.queueQuery(SimpleString(it.queueName)).isExists }.asSequence()
|
||||
}
|
||||
@ -242,14 +242,14 @@ class P2PMessagingClient(val config: NodeConfiguration,
|
||||
fun deployBridges(node: NodeInfo) {
|
||||
gatherAddresses(node)
|
||||
.forEach {
|
||||
sendBridgeControl(BridgeControl.Create(myIdentity.toStringShort(), it))
|
||||
sendBridgeControl(BridgeControl.Create(config.myLegalName.toString(), it))
|
||||
}
|
||||
}
|
||||
|
||||
fun destroyBridges(node: NodeInfo) {
|
||||
gatherAddresses(node)
|
||||
.forEach {
|
||||
sendBridgeControl(BridgeControl.Delete(myIdentity.toStringShort(), it))
|
||||
sendBridgeControl(BridgeControl.Delete(config.myLegalName.toString(), it))
|
||||
}
|
||||
}
|
||||
|
||||
@ -289,7 +289,7 @@ class P2PMessagingClient(val config: NodeConfiguration,
|
||||
delayStartQueues += queue.toString()
|
||||
}
|
||||
}
|
||||
val startupMessage = BridgeControl.NodeToBridgeSnapshot(myIdentity.toStringShort(), inboxes, requiredBridges)
|
||||
val startupMessage = BridgeControl.NodeToBridgeSnapshot(config.myLegalName.toString(), inboxes, requiredBridges)
|
||||
sendBridgeControl(startupMessage)
|
||||
}
|
||||
|
||||
@ -495,7 +495,7 @@ class P2PMessagingClient(val config: NodeConfiguration,
|
||||
val peers = networkMap.getNodesByOwningKeyIndex(keyHash)
|
||||
for (node in peers) {
|
||||
val bridge = BridgeEntry(queueName, node.addresses, node.legalIdentities.map { it.name })
|
||||
val createBridgeMessage = BridgeControl.Create(myIdentity.toStringShort(), bridge)
|
||||
val createBridgeMessage = BridgeControl.Create(config.myLegalName.toString(), bridge)
|
||||
sendBridgeControl(createBridgeMessage)
|
||||
}
|
||||
}
|
||||
@ -540,7 +540,7 @@ class P2PMessagingClient(val config: NodeConfiguration,
|
||||
|
||||
override fun getAddressOfParty(partyInfo: PartyInfo): MessageRecipients {
|
||||
return when (partyInfo) {
|
||||
is PartyInfo.SingleNode -> NodeAddress(partyInfo.party.owningKey, partyInfo.addresses.single())
|
||||
is PartyInfo.SingleNode -> NodeAddress(partyInfo.party.owningKey)
|
||||
is PartyInfo.DistributedNode -> ServiceAddress(partyInfo.party.owningKey)
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ keyStorePassword = "cordacadevpass"
|
||||
trustStorePassword = "trustpass"
|
||||
crlCheckSoftFail = true
|
||||
lazyBridgeStart = true
|
||||
additionalP2PAddresses = []
|
||||
dataSourceProperties = {
|
||||
dataSourceClassName = org.h2.jdbcx.JdbcDataSource
|
||||
dataSource.url = "jdbc:h2:file:"${baseDirectory}"/persistence;DB_CLOSE_ON_EXIT=FALSE;WRITE_DELAY=0;LOCK_TIMEOUT=10000"
|
||||
|
Reference in New Issue
Block a user