# web3j-ribbon-demo
**Repository Path**: wjr2012/web3j-ribbon-demo
## Basic Information
- **Project Name**: web3j-ribbon-demo
- **Description**: fork from https://github.com/zacscoding/web3j-ribbon-demo
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2022-06-17
- **Last Updated**: 2022-06-17
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# Ethereum load balancer with Web3j and Ribbon
; this is a demo project for load balanced ethereum client
i.e load balance from ribbon and ethererum client from web3j.
- Getting started
- Ribbon component
- Example with spring boot + jpa
---
## Ribbon component
todo : with docker-compose
---
## Ribbon component
### `com.netflix.loadbalancer.Server`
=> `eth.core.EthNode` (Source code)
Override `Server`'s methods and maintain `Web3jService` in member field
> `eth.core.EthNode`
```java
package eth.core;
...
public class EthNode extends Server {
private final String chainId;
private final String scheme;
private final String host;
private final int port;
private final String rpcUrl;
private final Map metadata;
private Web3jService web3jService;
...
}
```
### `com.netflix.loadbalancer.IPing`
=> `eth.core.loadbalancer.Web3jPing` (Source code)
Override `IPing`'s isAlive method by using `EthNode`'s `Web3jService`
> `eth.core.loadbalancer.Web3jPing`
```java
package eth.core.loadbalancer;
...
@Slf4j
public class Web3jPing implements IPing {
...
@Override
public boolean isAlive(Server server) {
boolean alive = isAliveInternal(server);
if (logger.isTraceEnabled()) {
logger.trace("Ping {} result >> {}", server, alive);
}
return alive;
}
...
/**
* Returns a {@link EthSyncing}'s result
*
* if use {@link WebSocketService} and not connected, then try to connect again
*/
private EthSyncing.Result getSyncingResult(EthNode ethNode) throws Exception {
final Web3j web3j = Web3j.build(ethNode.getWeb3jService());
try {
return web3j.ethSyncing().send().getResult();
} catch (WebsocketNotConnectedException e) {
final Web3jService web3jService = ethNode.getWeb3jService();
if (web3jService instanceof WebSocketService) {
// if use websocket service, then try to reconnect
((WebSocketService) web3jService).connect();
return web3j.ethSyncing().send().getResult();
}
// throw exception if not web socket service
throw e;
} catch (Exception e) {
throw e;
}
}
}
```
### `com.netflix.loadbalancer.IRule`
=> `eth.core.loadbalancer.MultiChainRoundRobinRule` (Source code)
Override `IRule`'s choose methods to maintain multiple nextServerCyclicCounter
> `eth.core.loadbalancer.MultiChainRoundRobinRule`
```java
package eth.core.loadbalancer;
...
public class MultiChainRoundRobinRule extends AbstractLoadBalancerRule {
private ConcurrentHashMap counterGroup;
...
public Server choose(ILoadBalancer lb, final Object key) {
...
final String chainId = key.toString();
final AtomicInteger nextServerCyclicCounter = getNextServerCyclicCounter(chainId);
Server server = null;
int count = 0;
final Predicate filter = s -> {
if (!(s instanceof EthNode)) {
return false;
}
return chainId.equals(((EthNode) s).getChainId());
};
while (count++ < 10) {
List reachableServers = lb.getReachableServers()
.stream()
.filter(filter)
.collect(Collectors.toList());
List allServers = lb.getAllServers()
.stream()
.filter(filter)
.collect(Collectors.toList());
int upCount = reachableServers.size();
int serverCount = allServers.size();
...
int nextServerIndex = incrementAndGetModulo(nextServerCyclicCounter, serverCount);
server = allServers.get(nextServerIndex);
if (server == null) {
/* Transient. */
Thread.yield();
continue;
}
if (server.isAlive() && (server.isReadyToServe())) {
return server;
}
}
if (count >= 10) {
logger.warn("No available alive servers after 10 tries from load balancer: {}", lb);
}
return null;
}
private int incrementAndGetModulo(AtomicInteger nextServerCyclicCounter, int modulo) {
while (true) {
int current = nextServerCyclicCounter.get();
int next = (current + 1) % modulo;
if (nextServerCyclicCounter.compareAndSet(current, next)) { return next; }
}
}
private AtomicInteger getNextServerCyclicCounter(String chainId) {
return counterGroup.computeIfAbsent(chainId, (key) -> new AtomicInteger(0));
}
}
```
---
## Example with spring boot + jpa