123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162 |
- package hello;
- import com.fasterxml.jackson.databind.ObjectMapper;
- import com.github.mustachejava.DefaultMustacheFactory;
- import com.github.mustachejava.MustacheFactory;
- import com.google.common.cache.CacheBuilder;
- import com.google.common.cache.CacheLoader;
- import com.google.common.cache.LoadingCache;
- import com.google.common.net.MediaType;
- import com.mongodb.DB;
- import com.mongodb.MongoClient;
- import io.undertow.Handlers;
- import io.undertow.Undertow;
- import io.undertow.UndertowOptions;
- import io.undertow.util.Headers;
- import org.xnio.Options;
- import javax.sql.DataSource;
- import java.io.IOException;
- import java.io.InputStream;
- import java.sql.Connection;
- import java.sql.PreparedStatement;
- import java.sql.ResultSet;
- import java.sql.SQLException;
- import java.util.Properties;
- /**
- * An implementation of the TechEmpower benchmark tests using the Undertow web
- * server. The only test that truly exercises Undertow in isolation is the
- * plaintext test. For the rest, it uses best-of-breed components that are
- * expected to perform well. The idea is that using these components enables
- * these tests to serve as performance baselines for hypothetical, Undertow-based
- * frameworks. For instance, it is unlikely that such frameworks would complete
- * the JSON test faster than this will, because this implementation uses
- * Undertow and Jackson in the most direct way possible to fulfill the test
- * requirements.
- */
- public final class HelloWebServer {
- //MediaType.toString() does non-trivial work and does not cache the result
- //so we cache it here
- public static final String JSON_UTF8 = MediaType.JSON_UTF_8.toString();
- public static final String TEXT_PLAIN = MediaType.PLAIN_TEXT_UTF_8.toString();
- public static final String HTML_UTF8 = MediaType.HTML_UTF_8.toString();
- public static void main(String[] args) throws Exception {
- new HelloWebServer();
- }
- /**
- * Creates and starts a new web server whose configuration is specified in the
- * {@code server.properties} file.
- *
- * @throws IOException if the application properties file cannot be read or
- * the Mongo database hostname cannot be resolved
- * @throws SQLException if reading from the SQL database (while priming the
- * cache) fails
- */
- public HelloWebServer() throws ClassNotFoundException, IOException, SQLException {
- Class.forName("org.postgresql.Driver");
- Properties properties = new Properties();
- try (InputStream in = HelloWebServer.class.getResourceAsStream(
- "server.properties")) {
- properties.load(in);
- }
- final ObjectMapper objectMapper = new ObjectMapper();
- final MustacheFactory mustacheFactory = new DefaultMustacheFactory();
- final DataSource mysql = Helper.newDataSource(
- properties.getProperty("mysql.uri"),
- properties.getProperty("mysql.user"),
- properties.getProperty("mysql.password"));
- final DataSource postgresql = Helper.newDataSource(
- properties.getProperty("postgresql.uri"),
- properties.getProperty("postgresql.user"),
- properties.getProperty("postgresql.password"));
- final DB mongodb = new MongoClient(properties.getProperty("mongodb.host"))
- .getDB(properties.getProperty("mongodb.name"));
- //
- // The world cache is primed at startup with all values. It doesn't
- // matter which database backs it; they all contain the same information
- // and the CacheLoader.load implementation below is never invoked.
- //
- final LoadingCache<Integer, World> worldCache = CacheBuilder.newBuilder()
- .build(new CacheLoader<Integer, World>() {
- @Override
- public World load(Integer id) throws Exception {
- try (Connection connection = mysql.getConnection();
- PreparedStatement statement = connection.prepareStatement(
- "SELECT * FROM World WHERE id = ?",
- ResultSet.TYPE_FORWARD_ONLY,
- ResultSet.CONCUR_READ_ONLY)) {
- statement.setInt(1, id);
- try (ResultSet resultSet = statement.executeQuery()) {
- resultSet.next();
- return new World(
- resultSet.getInt("id"),
- resultSet.getInt("randomNumber"));
- }
- }
- }
- });
- try (Connection connection = mysql.getConnection();
- PreparedStatement statement = connection.prepareStatement(
- "SELECT * FROM World",
- ResultSet.TYPE_FORWARD_ONLY,
- ResultSet.CONCUR_READ_ONLY);
- ResultSet resultSet = statement.executeQuery()) {
- while (resultSet.next()) {
- World world = new World(
- resultSet.getInt("id"),
- resultSet.getInt("randomNumber"));
- worldCache.put(world.id, world);
- }
- }
- Undertow.builder()
- .addHttpListener(
- Integer.parseInt(properties.getProperty("web.port")),
- properties.getProperty("web.host"))
- .setBufferSize(1024 * 16)
- .setIoThreads(Runtime.getRuntime().availableProcessors() * 2) //this seems slightly faster in some configurations
- .setSocketOption(Options.BACKLOG, 10000)
- .setServerOption(UndertowOptions.ALWAYS_SET_KEEP_ALIVE, false) //don't send a keep-alive header for HTTP/1.1 requests, as it is not required
- .setServerOption(UndertowOptions.ALWAYS_SET_DATE, true)
- .setHandler(Handlers.header(Handlers.path()
- .addPrefixPath("/json",
- new JsonHandler(objectMapper))
- .addPrefixPath("/db/mysql",
- new DbSqlHandler(objectMapper, mysql, false))
- .addPrefixPath("/queries/mysql",
- new DbSqlHandler(objectMapper, mysql, true))
- .addPrefixPath("/db/postgresql",
- new DbSqlHandler(objectMapper, postgresql, false))
- .addPrefixPath("/queries/postgresql",
- new DbSqlHandler(objectMapper, postgresql, true))
- .addPrefixPath("/db/mongodb",
- new DbMongoHandler(objectMapper, mongodb, false))
- .addPrefixPath("/queries/mongodb",
- new DbMongoHandler(objectMapper, mongodb, true))
- .addPrefixPath("/fortunes/mysql",
- new FortunesSqlHandler(mustacheFactory, mysql))
- .addPrefixPath("/fortunes/postgresql",
- new FortunesSqlHandler(mustacheFactory, postgresql))
- .addPrefixPath("/fortunes/mongodb",
- new FortunesMongoHandler(mustacheFactory, mongodb))
- .addPrefixPath("/updates/mysql",
- new UpdatesSqlHandler(objectMapper, mysql))
- .addPrefixPath("/updates/postgresql",
- new UpdatesSqlHandler(objectMapper, postgresql))
- .addPrefixPath("/updates/mongodb",
- new UpdatesMongoHandler(objectMapper, mongodb))
- .addPrefixPath("/plaintext",
- new PlaintextHandler())
- .addPrefixPath("/cache",
- new CacheHandler(objectMapper, worldCache)),
- Headers.SERVER_STRING, "U-tow"))
- .setWorkerThreads(200)
- .build()
- .start();
- }
- }
|