Update throttling pattern (#1937)

* Create component.urm.puml

* Create App.java

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Create AppTest.java

* Add files via upload

* Update README.md

* Update README.md

* Update pom.xml

* Update App.java

* Update BjornGraphicsComponent.java

* Update BjornInputComponent.java

* Update BjornPhysicsComponent.java

* Update Component.java

* Update App.java

* Delete App.java

* Delete BjornGraphicsComponent.java

* Delete BjornInputComponent.java

* Delete BjornPhysicsComponent.java

* Delete Component.java

* Delete GameObject.java

* Delete GraphicsComponent.java

* Delete InputComponent.java

* Delete PhysicsComponent.java

* Create App.java

* Update App.java

* Update App.java

* Create BjornGraphicsComponent.java

* Create BjornInputComponent.java

* Create BjornPhysicsComponent.java

* Create Component.java

* Create GameObject.java

* Create GraphicsComponent.java

* Create InputComponent.java

* Create PhysicsComponent.java

* Delete AppTest.java

* Delete UpdateTest.java

* Create AppTest.java

* Create UpdateTest.java

* Update throttling pattern example

* delete unwanted files

Co-authored-by: YanchaoMiao <11710204@mail.sustech.edu.cn>
This commit is contained in:
Ilkka Seppälä
2022-01-07 09:46:59 +02:00
committed by GitHub
parent c66ca67201
commit 11f20593b2
8 changed files with 156 additions and 130 deletions

View File

@ -34,11 +34,11 @@ import lombok.extern.slf4j.Slf4j;
* complete service by users or a particular tenant. This can allow systems to continue to function
* and meet service level agreements, even when an increase in demand places load on resources.
* <p>
* In this example we have ({@link App}) as the initiating point of the service. This is a time
* In this example there is a {@link Bartender} serving beer to {@link BarCustomer}s. This is a time
* based throttling, i.e. only a certain number of calls are allowed per second.
* </p>
* ({@link Tenant}) is the Tenant POJO class with which many tenants can be created ({@link
* B2BService}) is the service which is consumed by the tenants and is throttled.
* ({@link BarCustomer}) is the service tenant class having a name and the number of calls allowed.
* ({@link Bartender}) is the service which is consumed by the tenants and is throttled.
*/
@Slf4j
public class App {
@ -50,33 +50,35 @@ public class App {
*/
public static void main(String[] args) {
var callsCount = new CallsCount();
var adidas = new Tenant("Adidas", 5, callsCount);
var nike = new Tenant("Nike", 6, callsCount);
var human = new BarCustomer("young human", 2, callsCount);
var dwarf = new BarCustomer("dwarf soldier", 4, callsCount);
var executorService = Executors.newFixedThreadPool(2);
executorService.execute(() -> makeServiceCalls(adidas, callsCount));
executorService.execute(() -> makeServiceCalls(nike, callsCount));
executorService.execute(() -> makeServiceCalls(human, callsCount));
executorService.execute(() -> makeServiceCalls(dwarf, callsCount));
executorService.shutdown();
try {
executorService.awaitTermination(10, TimeUnit.SECONDS);
if (!executorService.awaitTermination(10, TimeUnit.SECONDS)) {
executorService.shutdownNow();
}
} catch (InterruptedException e) {
LOGGER.error("Executor Service terminated: {}", e.getMessage());
executorService.shutdownNow();
}
}
/**
* Make calls to the B2BService dummy API.
* Make calls to the bartender.
*/
private static void makeServiceCalls(Tenant tenant, CallsCount callsCount) {
var timer = new ThrottleTimerImpl(10, callsCount);
var service = new B2BService(timer, callsCount);
private static void makeServiceCalls(BarCustomer barCustomer, CallsCount callsCount) {
var timer = new ThrottleTimerImpl(1000, callsCount);
var service = new Bartender(timer, callsCount);
// Sleep is introduced to keep the output in check and easy to view and analyze the results.
IntStream.range(0, 20).forEach(i -> {
service.dummyCustomerApi(tenant);
IntStream.range(0, 50).forEach(i -> {
service.orderDrink(barCustomer);
try {
Thread.sleep(1);
Thread.sleep(100);
} catch (InterruptedException e) {
LOGGER.error("Thread interrupted: {}", e.getMessage());
}

View File

@ -25,22 +25,26 @@ package com.iluwatar.throttling;
import java.security.InvalidParameterException;
/**
* A Pojo class to create a basic Tenant with the allowed calls per second.
*/
public class Tenant {
import lombok.Getter;
/**
* BarCustomer is a tenant with a name and a number of allowed calls per second.
*/
public class BarCustomer {
@Getter
private final String name;
@Getter
private final int allowedCallsPerSecond;
/**
* Constructor.
*
* @param name Name of the tenant
* @param allowedCallsPerSecond The number of calls allowed for a particular tenant.
* @param name Name of the BarCustomer
* @param allowedCallsPerSecond The number of calls allowed for this particular tenant.
* @throws InvalidParameterException If number of calls is less than 0, throws exception.
*/
public Tenant(String name, int allowedCallsPerSecond, CallsCount callsCount) {
public BarCustomer(String name, int allowedCallsPerSecond, CallsCount callsCount) {
if (allowedCallsPerSecond < 0) {
throw new InvalidParameterException("Number of calls less than 0 not allowed");
}
@ -48,12 +52,4 @@ public class Tenant {
this.allowedCallsPerSecond = allowedCallsPerSecond;
callsCount.addTenant(name);
}
public String getName() {
return name;
}
public int getAllowedCallsPerSecond() {
return allowedCallsPerSecond;
}
}

View File

@ -29,33 +29,32 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A service which accepts a tenant and throttles the resource based on the time given to the
* tenant.
* Bartender is a service which accepts a BarCustomer (tenant) and throttles
* the resource based on the time given to the tenant.
*/
class B2BService {
class Bartender {
private static final Logger LOGGER = LoggerFactory.getLogger(B2BService.class);
private static final Logger LOGGER = LoggerFactory.getLogger(Bartender.class);
private final CallsCount callsCount;
public B2BService(Throttler timer, CallsCount callsCount) {
public Bartender(Throttler timer, CallsCount callsCount) {
this.callsCount = callsCount;
timer.start();
}
/**
* Calls dummy customer api.
*
* Orders a drink from the bartender.
* @return customer id which is randomly generated
*/
public int dummyCustomerApi(Tenant tenant) {
var tenantName = tenant.getName();
public int orderDrink(BarCustomer barCustomer) {
var tenantName = barCustomer.getName();
var count = callsCount.getCount(tenantName);
LOGGER.debug("Counter for {} : {} ", tenant.getName(), count);
if (count >= tenant.getAllowedCallsPerSecond()) {
LOGGER.error("API access per second limit reached for: {}", tenantName);
if (count >= barCustomer.getAllowedCallsPerSecond()) {
LOGGER.error("I'm sorry {}, you've had enough for today!", tenantName);
return -1;
}
callsCount.incrementCount(tenantName);
LOGGER.debug("Serving beer to {} : [{} consumed] ", barCustomer.getName(), count + 1);
return getRandomCustomerId();
}

View File

@ -69,7 +69,7 @@ public final class CallsCount {
* Resets the count of all the tenants in the map.
*/
public void reset() {
LOGGER.debug("Resetting the map.");
tenantCallsCount.replaceAll((k, v) -> new AtomicLong(0));
LOGGER.info("reset counters");
}
}

View File

@ -23,20 +23,21 @@
package com.iluwatar.throttling;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;
import java.security.InvalidParameterException;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertThrows;
/**
* TenantTest to test the creation of Tenant with valid parameters.
*/
public class TenantTest {
public class BarCustomerTest {
@Test
void constructorTest() {
assertThrows(InvalidParameterException.class, () -> {
new Tenant("FailTenant", -1, new CallsCount());
new BarCustomer("sirBrave", -1, new CallsCount());
});
}
}

View File

@ -32,19 +32,18 @@ import org.junit.jupiter.api.Test;
/**
* B2BServiceTest class to test the B2BService
*/
public class B2BServiceTest {
public class BartenderTest {
private final CallsCount callsCount = new CallsCount();
@Test
void dummyCustomerApiTest() {
var tenant = new Tenant("testTenant", 2, callsCount);
var tenant = new BarCustomer("pirate", 2, callsCount);
// In order to assure that throttling limits will not be reset, we use an empty throttling implementation
var timer = (Throttler) () -> {
};
var service = new B2BService(timer, callsCount);
var timer = (Throttler) () -> {};
var service = new Bartender(timer, callsCount);
IntStream.range(0, 5).mapToObj(i -> tenant).forEach(service::dummyCustomerApi);
IntStream.range(0, 5).mapToObj(i -> tenant).forEach(service::orderDrink);
var counter = callsCount.getCount(tenant.getName());
assertEquals(2, counter, "Counter limit must be reached");
}