Background:
I have a TCP online multiplayer game server, which the player requests to log-in. However, the game server does not handle the authentication part of the login process, instead, it uses an outer micro-service which is an HTTP server to see if the user can authenticate with the given credentials.
Now why is that a problem? - Well I use netty.io networking framework, which is a non-blocking framework, it handles all connections in a sequence on the same thread. Since I don't want to cause any delays, I will not send an HTTP request on the same thread because I don't want to stop it.
Instead I followed a pattern of callbacks like java script promise resolve-reject, and I Would like to hear what you think, and if it could be done better.
Don't mind the generic types, just take the basic idea of it:
First of all this is my ServiceProvider
, the point of this class is to provide a singleton of each service that interacts with microservices such as UserService which is an HTTP server to handle everything related to user (authentication, data, etc):
public class ServiceProvider {
private static ExecutorService executorService = Executors.newFixedThreadPool(ServicesConfig.SERVICES_THREAD_POOL_CAPACITY);
private static UserService userService;
/**
* Provdes an user service singleton instance
* @return The user service
*/
public static UserService getUserService() {
if (userService == null) {
userService = new UserService();
userService.setBaseUrl(ServicesConfig.USER_SERVICE_BASE_URL);
}
return userService;
}
public static void submitServiceTask(ServicePromise task) {
executorService.submit(task);
}
}
Secondly is my UserService
for example:
public class UserService extends HeroesHttpService {
/**
* Authenticates an user
* @param username The username
* @param password The password
* @return ?
*/
public ServicePromise<?> authenticateUser(String username, String password) {
ServicePromise<HttpResponse> promise = new ServicePromise<HttpResponse>() {
@Override
public void run() {
HttpClient client = HttpClients.createDefault();
HttpGet request = new HttpGet(getBaseUrl() + "/user/authenticate");
try {
HttpResponse response = client.execute(request);
resolve(response);
} catch (IOException e) {
e.printStackTrace();
}
}
};
ServiceProvider.submitServiceTask(promise);
return promise;
}
}
Now this is my promise interface:
public abstract class ServicePromise<T> implements Runnable {
private ServicePromiseCallback<T> callback;
public ServicePromise<T> then(ServicePromiseCallback<T> cb) {
this.callback = cb;
return this;
}
@Override
public void run() {
}
public void resolve(T data) {
if (this.callback == null) {
return;
}
this.callback.onResolve(data);
}
public void reject(T data) {
if (this.callback == null) {
return;
}
this.callback.onReject(data);
}
}
And that's the callback interface:
public interface ServicePromiseCallback<T> {
void onResolve(T response);
void onReject(T response);
}
And finally, thats how I use it:
ServiceProvider.getUserService().authenticateUser(player.playerName, player.playerPass).then(new ServicePromiseCallback<?> () {
@Override
public void onResolve(Object response) {
}
@Override
public void onReject(Object response) {
}
});
Again, don't mind the types in resolve/reject, it's going to definitely be AuthenticateUserResponseDto
and so on, but it doesn't matter right now