Agenda
- Introduction
- Demo
- Dependency Injection
- HTTP Server
- Testing
- HTTP Client
- Performance Comparison
- Functions
- Summary
The Team
Demo
New Microservice
curl -s https://get.sdkman.io | bash
source "$HOME/.sdkman/bin/sdkman-init.sh"
sdk install micronaut 1.0.0.RC2
sdk use micronaut 1.0.0.RC2
mn --version
mn create-app --features spock,graal-native-image cloud-winterboots-product
cd cloud-winterboots-product
mn create-bean ProductService
mn create-controller Product
IDE Support
Product Service
package cloud.winterboots.product;
import java.util.Arrays;
import java.util.List;
@javax.inject.Singleton
public class ProductService {
List<Product> getAll() {
return Arrays.asList(new Product("Hucule"), new Product("Superboots"));
}
}
package cloud.winterboots.product;
public class Product {
private final String name;
@java.beans.ConstructorProperties({ "name" })
public Product(String name) { this.name = name; }
public String getName() { return name; }
// equals, hashCode, toString
}
Product Controller
package cloud.winterboots.product;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import java.util.List;
@Controller("/product")
public class ProductController {
private final ProductService productService;
public ProductController(ProductService productService) {
this.productService = productService;
}
@Get("/")
public List<Product> index() {
return productService.getAll();
}
}
Product Controller Spec
package cloud.winterboots.product
// imports
class ProductControllerSpec extends Specification {
@Shared @AutoCleanup EmbeddedServer embeddedServer =
ApplicationContext.run(EmbeddedServer)
@Shared @AutoCleanup RxHttpClient client =
embeddedServer.applicationContext.createBean(RxHttpClient, embeddedServer.getURL())
void "test index"() {
given:
HttpResponse<List<Product>> response =
client.toBlocking().exchange(
HttpRequest.GET("/product"),
Argument.of(List, Product)
)
expect:
response.status == HttpStatus.OK
response.body()
response.body().size() == 2
response.body().get(0).name == 'Hucule'
}
}
AoT Compilation
Product Client
mn create-client Product
package cloud.winterboots.product;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.client.annotation.Client;
import io.reactivex.Flowable;
@Client("/product")
public interface ProductClient {
@Get("/")
Flowable<Product> index();
}
Product Controller Spec II
package cloud.winterboots.product
import io.micronaut.context.ApplicationContext
import io.micronaut.runtime.server.EmbeddedServer
import spock.lang.AutoCleanup
import spock.lang.Shared
import spock.lang.Specification
class ProductControllerSpec extends Specification {
@Shared @AutoCleanup EmbeddedServer embeddedServer =
ApplicationContext.run(EmbeddedServer)
void "test index"() {
given:
ProductClient client =
embeddedServer.applicationContext.createBean(ProductClient)
List<Product> products =
client.index().toList().blockingGet()
expect:
products
products.size() == 2
products.get(0).name == 'Hucule'
}
}
Running the Microservice
./gradlew run
# src/test/http/product.http
GET http://localhost:8080/product
Accept: application/json
Running the Microservice
GraalVM
sdk install java 1.0.0-rc7-graal
sdk use java 1.0.0-rc7-graal
./build-native-image.sh
./cloud-winterboots-product
GraalVM + Docker
FROM oracle/graalvm-ce:1.0.0-rc7
EXPOSE 8080
COPY build/libs/*-all.jar cloud-winterboots-product.jar
COPY build/reflect.json reflect.json
RUN java -cp cloud-winterboots-product.jar io.micronaut.graal.reflect.GraalClassLoadingAnalyzer
RUN native-image --no-server \
--class-path cloud-winterboots-product.jar \
-H:ReflectionConfigurationFiles=reflect.json \
-H:EnableURLProtocols=http \
-H:IncludeResources="logback.xml|application.yml|META-INF/services/*.*" \
-H:Name=cloud-winterboots-product \
-H:Class=cloud.winterboots.product.Application \
-H:+ReportUnsupportedElementsAtRuntime \
-H:+AllowVMInspection \
--rerun-class-initialization-at-runtime='sun.security.jca.JCAUtil$CachedSecureRandomHolder,javax.net.ssl.SSLContext' \
--delay-class-initialization-to-runtime=io.netty.handler.codec.http.HttpObjectEncoder,io.netty.handler.codec.http.websocketx.WebSocket00FrameEncoder,io.netty.handler.ssl.util.ThreadLocalInsecureRandom
CMD ./cloud-winterboots-product
./gradlew assemble
docker build --tag="cloud-winterboots-product" .
docker run -d -p 8080:8080 cloud-winterboots-product
Management
// build.gradle
dependencies {
compile "io.micronaut:management"
compile "io.micronaut.configuration:micrometer-core"
}
# src/main/resources/application.yml
---
endpoints:
metrics:
sensitive: false
Management
GET http://localhost:8080/health
Accept: application/json
###
GET http://localhost:8080/metrics
Accept: application/json
###
GET http://localhost:8080/metrics/jvm.memory.committed
Accept: application/json
###
GET http://localhost:8080/metrics/jvm.memory.used
Accept: application/json
###
GET http://localhost:8080/routes
Accept: application/json
###
GET http://localhost:8080/loggers
Accept: application/json
###
GET http://localhost:8080/beans
Accept: application/json
###
GET http://localhost:8080/info
Accept: application/json
Micronaut vs Spring Boot
cd ..
curl -L https://github.com/musketyr/lusk/releases/download/0.2.3/lusk.tar | tar -xvf -
lusk/bin/lusk cloud-winterboots-product -c 100
cd cloud-winterboots-product
./gradlew run
cd ..
curl https://start.spring.io/starter.tgz -d dependencies=web \
-d type=gradle-project -d baseDir=boot-sample-app | tar -xzvf -
lusk/bin/lusk boot-sample-app -c 100
cd boot-sample-app
./gradlew bootRun
./gradlew cleanTest test --tests HttpSpec
Functions
cd ..
mn create-function cloud-winterboots-scraper
cd cloud-winterboots-scraper
# build.gradle
dependencies {
compile "io.micronaut:micronaut-http-client"
}
Http Client
package cloud.winterboots.scraper;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.client.annotation.Client;
import java.util.List;
@Client("https://private-046fd-winterboots.apiary-mock.com")
public interface ProductClient {
@Get("/product")
public List<Product> index();
}
Functions
cd ..
mn create-function --features=spock cloud-winterboots-scraper
cd cloud-winterboots-scraper
// build.gradle
dependencies {
compile "io.micronaut:micronaut-http-client"
}
Mocking in the Tests
@FunctionBean("cloud-winterboots-scraper")
public class CloudWinterbootsScraperFunction implements Supplier<String> {
private final ProductApi client;
public CloudWinterbootsScraperFunction(ProductApi client) {
this.client = client;
}
@Override public String get() {
return client.index().stream().map(Product::getName).collect(Collectors.joining(", "));
}
}
public interface ProductApi {
@Get("/product") List<Product> index();
}
@Client("https://private-046fd-winterboots.apiary-mock.com")
public interface ProductClient extends ProductApi { }
Mocking in the Tests
@Primary
@Requires(env = Environment.TEST)
public class MockProductApi implements ProductApi {
@Override
public List<Product> index() {
return Arrays.asList(new Product("Superhucule"), new Product("Spring Boots"));
}
}
Deployment
./gradlew deploy
Invocation
// build.gradle
task invoke(type: jp.classmethod.aws.gradle.lambda.AWSLambdaInvokeTask) {
functionName = "cloud-winterboots-scraper"
invocationType = com.amazonaws.services.lambda.model.InvocationType.RequestResponse
doLast {
println "Lambda function result: " + new String(invokeResult.payload.array(), "UTF-8")
}
}
Micronaut Guides
Summary
- Dependency Injection and Inversion of Control (IoC)
- Sensible Defaults and Auto-Configuration
- Configuration and Configuration Sharing
- Service Discovery
- HTTP Routing
- HTTP Client with client-side load-balancing
- Fast startup time
- Reduced memory footprint
- Minimal use of reflection
- Minimal use of proxies
- Easy unit testing
- Ready to use (GA next week at Oracle Code One)
- 23 guides already and growing
- APL 2.0 + commercial support from OCI
Thank You
Micronaut - as small as it gets
By musketyr
Micronaut - as small as it gets
- 1,996