All Downloads are FREE. Search and download functionalities are using the official Maven repository.

discord4j.connect.rsocket.shared.ExampleRSocketLeader Maven / Gradle / Ivy

There is a newer version: 0.6.8
Show newest version
/*
 * This file is part of Discord4J.
 *
 * Discord4J is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Discord4J is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Discord4J. If not, see .
 */

package discord4j.connect.rsocket.shared;

import discord4j.common.JacksonResources;
import discord4j.connect.Constants;
import discord4j.connect.common.ConnectGatewayOptions;
import discord4j.connect.common.UpstreamGatewayClient;
import discord4j.connect.rsocket.gateway.RSocketJacksonSinkMapper;
import discord4j.connect.rsocket.gateway.RSocketJacksonSourceMapper;
import discord4j.connect.rsocket.gateway.RSocketPayloadSink;
import discord4j.connect.rsocket.gateway.RSocketPayloadSource;
import discord4j.connect.rsocket.global.RSocketGlobalRateLimiter;
import discord4j.connect.rsocket.router.RSocketRouter;
import discord4j.connect.rsocket.router.RSocketRouterOptions;
import discord4j.connect.rsocket.shard.RSocketShardCoordinator;
import discord4j.core.DiscordClient;
import discord4j.core.GatewayDiscordClient;
import discord4j.core.event.dispatch.DispatchEventMapper;
import discord4j.core.shard.InvalidationStrategy;
import discord4j.core.shard.ShardingStrategy;
import discord4j.store.redis.RedisStoreService;
import io.lettuce.core.RedisClient;
import reactor.core.publisher.Mono;
import reactor.netty.http.server.HttpServer;
import reactor.util.Logger;
import reactor.util.Loggers;

import java.net.InetSocketAddress;

/**
 * An example distributed Discord4J leader, or a node that is capable of connecting to Discord Gateway and routing
 * its messages to other nodes, across JVM boundaries.
 * 

* In particular, this example covers: *

    *
  • Connecting to a distributed GlobalRateLimiter for API requests
  • *
  • Connecting to a distributed Router for API requests
  • *
  • Connecting to a distributed ShardCoordinator for connect/IDENTIFY request rate limiting
  • *
  • Connecting to a RSocket payload server to send messages across boundaries
  • *
  • Using redis as entity cache (write capable in leaders, read-only in workers)
  • *
  • Defining the sharding strategy for the leaders
  • *
*/ public class ExampleRSocketLeader { public static void main(String[] args) { // define the port where the global router is listening to // define the port where the shard coordinator is listening to // define the port where the payload server is listening to InetSocketAddress globalRouterServerAddress = new InetSocketAddress(Constants.GLOBAL_ROUTER_SERVER_PORT); InetSocketAddress coordinatorServerAddress = new InetSocketAddress(Constants.SHARD_COORDINATOR_SERVER_PORT); InetSocketAddress payloadServerAddress = new InetSocketAddress(Constants.PAYLOAD_SERVER_PORT); // use a common jackson factory to reuse it where possible JacksonResources jackson = JacksonResources.create(); // use redis to store entity caches RedisClient redisClient = RedisClient.create(Constants.REDIS_CLIENT_URI); // Select your ShardingStrategy // a) use the recommended amount of shards, and connect this leader to all of them into a group // b) use a set number of shards, and connect this leader to all of them into a group // c) build a custom sharding strategy: // - indexes to filter which shard IDs to connect this leader // - count to set a fixed shard count (or do not set one to use the recommended amount) ShardingStrategy recommendedStrategy = ShardingStrategy.recommended(); ShardingStrategy fixedStrategy = ShardingStrategy.fixed(2); ShardingStrategy customStrategy = ShardingStrategy.builder() .indices(0, 2) // only connect this leader to shard IDs 0 and 2 .count(4) // but still split our bot guilds into 4 shards .build(); // define the GlobalRouterServer as GRL for all nodes in this architecture // define the GlobalRouterServer as Router for all request buckets in this architecture // create the RSocket capable Router of queueing API requests across boundaries // coordinate ws connect and IDENTIFY rate limit across leader nodes using this server // define the ConnectGatewayOptions to send payloads across boundaries // RSocketPayloadSink: payloads leaders send to workers through the payload server // RSocketPayloadSource: payloads workers send to leaders through the payload server // we use UpstreamGatewayClient that is capable of using above components to work in a distributed way GatewayDiscordClient client = DiscordClient.builder(System.getenv("token")) .setJacksonResources(jackson) .setGlobalRateLimiter(new RSocketGlobalRateLimiter(globalRouterServerAddress)) .setExtraOptions(o -> new RSocketRouterOptions(o, request -> globalRouterServerAddress)) .build(RSocketRouter::new) .gateway() .setSharding(recommendedStrategy) .setShardCoordinator(new RSocketShardCoordinator(coordinatorServerAddress)) // .setDisabledIntents(IntentSet.of( // Intent.GUILD_PRESENCES, // Intent.GUILD_MESSAGE_TYPING, // Intent.DIRECT_MESSAGE_TYPING)) // .setInitialStatus(s -> Presence.invisible()) .setInvalidationStrategy(InvalidationStrategy.disable()) .setStoreService(RedisStoreService.builder() .redisClient(redisClient) .useSharedConnection(false) .build()) .setDispatchEventMapper(DispatchEventMapper.discardEvents()) .setExtraOptions(o -> new ConnectGatewayOptions(o, new RSocketPayloadSink(payloadServerAddress, new RSocketJacksonSinkMapper(jackson.getObjectMapper(), "inbound")), new RSocketPayloadSource(payloadServerAddress, "outbound", new RSocketJacksonSourceMapper(jackson.getObjectMapper())))) .login(UpstreamGatewayClient::new) .blockOptional() .orElseThrow(RuntimeException::new); // Proof of concept allowing leader management via API HttpServer.create() .port(0) // use an ephemeral port .route(routes -> routes .get("/logout", (req, res) -> { return client.logout() .then(Mono.from(res.addHeader("content-type", "application/json") .status(200) .chunkedTransfer(false) .sendString(Mono.just("OK")))); }) ) .bind() .doOnNext(facade -> { log.info("*************************************************************"); log.info("Server started at {}:{}", facade.host(), facade.port()); log.info("*************************************************************"); // kill the server on JVM exit Runtime.getRuntime().addShutdownHook(new Thread(() -> facade.disposeNow())); }) .subscribe(); client.onDisconnect().block(); } private static final Logger log = Loggers.getLogger(ExampleRSocketLeader.class); }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy