Article
Fission is a Kubernetes-native serverless framework that enables you to run functions without managing complex infrastructure. For Java developers, Fission provides a powerful way to deploy microservices, APIs, and event-driven functions with minimal overhead and maximum efficiency.
What is Fission?
Fission is an open-source serverless framework built on Kubernetes that allows you to write functions in various languages (including Java) and execute them in response to events. It handles auto-scaling, networking, and resource management while you focus on business logic.
Key Benefits for Java Teams:
- No Infrastructure Management: Focus on code, not containers or pods
- Fast Cold Starts: Pre-warmed pools and optimized environments
- Event-Driven Architecture: Support for HTTP, message queues, timers, and more
- Kubernetes Native: Leverages existing Kubernetes clusters and tools
- Cost Effective: Pay only for execution time with automatic scaling to zero
Fission Architecture Overview
- Router: Handles HTTP requests and routes to functions
- Executor: Manages function instances (NewDeploy vs PoolManager)
- Environment: Pre-built containers with language runtimes
- Function: Your Java code with dependencies
- Trigger: Event sources that invoke functions (HTTP, Message Queue, Timer)
Installation and Setup
Install Fission
Using Helm (Recommended):
# Add Fission chart repository helm repo add fission https://fission.github.io/fission-charts/ helm repo update # Install Fission in fission namespace helm install fission fission/fission-all --version v1.18.0 --namespace fission --create-namespace
Using kubectl:
kubectl create namespace fission kubectl -n fission apply -f https://github.com/fission/fission/releases/download/v1.18.0/fission-core-v1.18.0.yaml
Install Fission CLI
Mac:
curl -Lo fission https://github.com/fission/fission/releases/download/v1.18.0/fission-v1.18.0-darwin-amd64 chmod +x fission && sudo mv fission /usr/local/bin/
Linux:
curl -Lo fission https://github.com/fission/fission/releases/download/v1.18.0/fission-v1.18.0-linux-amd64 chmod +x fission && sudo mv fission /usr/local/bin/
Verify Installation:
fission version
Creating Java Functions
1. Simple HTTP Function
Basic Java Function:
package com.example.fission; import io.fission.Function; import org.springframework.http.HttpStatus; import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; import java.util.Map; import java.util.HashMap; public class HelloWorldFunction implements Function { @Override public ResponseEntity<?> call(RequestEntity<?> request, Context context) { // Get query parameters String name = request.getParams().getFirst("name"); if (name == null) { name = "World"; } // Create response Map<String, String> response = new HashMap<>(); response.put("message", "Hello, " + name + "!"); response.put("timestamp", java.time.Instant.now().toString()); response.put("language", "Java"); return ResponseEntity.ok(response); } } 2. JSON Processing Function
package com.example.fission; import io.fission.Function; import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; import com.fasterxml.jackson.databind.ObjectMapper; import java.util.Map; public class JsonProcessorFunction implements Function { private final ObjectMapper mapper = new ObjectMapper(); @Override public ResponseEntity<?> call(RequestEntity<?> request, Context context) { try { // Parse request body if (request.getBody() instanceof String) { String body = (String) request.getBody(); Map<String, Object> data = mapper.readValue(body, Map.class); // Process data data.put("processed", true); data.put("processedAt", java.time.Instant.now().toString()); data.put("processor", "java-function"); return ResponseEntity.ok(data); } else { return ResponseEntity.badRequest() .body("{\"error\": \"Expected JSON body\"}"); } } catch (Exception e) { return ResponseEntity.status(500) .body("{\"error\": \"Processing failed: " + e.getMessage() + "\"}"); } } } 3. Database-Aware Function
package com.example.fission; import io.fission.Function; import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DriverManagerDataSource; import java.util.List; import java.util.Map; public class UserServiceFunction implements Function { private JdbcTemplate jdbcTemplate; public UserServiceFunction() { // Initialize datasource from environment variables String dbUrl = System.getenv("DB_URL"); String dbUser = System.getenv("DB_USERNAME"); String dbPassword = System.getenv("DB_PASSWORD"); DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("org.postgresql.Driver"); dataSource.setUrl(dbUrl); dataSource.setUsername(dbUser); dataSource.setPassword(dbPassword); this.jdbcTemplate = new JdbcTemplate(dataSource); } @Override public ResponseEntity<?> call(RequestEntity<?> request, Context context) { String method = request.getMethod().name(); String path = request.getUrl().getPath(); try { switch (method) { case "GET": return handleGet(request); case "POST": return handlePost(request); case "PUT": return handlePut(request); default: return ResponseEntity.status(405).body("Method not allowed"); } } catch (Exception e) { return ResponseEntity.status(500) .body("{\"error\": \"Database operation failed: " + e.getMessage() + "\"}"); } } private ResponseEntity<?> handleGet(RequestEntity<?> request) { String path = request.getUrl().getPath(); if (path.endsWith("/users")) { List<Map<String, Object>> users = jdbcTemplate.queryForList( "SELECT id, name, email, created_at FROM users ORDER BY created_at DESC"); return ResponseEntity.ok(users); } else { return ResponseEntity.status(404).body("Not found"); } } private ResponseEntity<?> handlePost(RequestEntity<?> request) { if (request.getBody() instanceof Map) { @SuppressWarnings("unchecked") Map<String, Object> userData = (Map<String, Object>) request.getBody(); jdbcTemplate.update( "INSERT INTO users (name, email) VALUES (?, ?)", userData.get("name"), userData.get("email")); return ResponseEntity.status(201).body("{\"status\": \"User created\"}"); } return ResponseEntity.badRequest().body("{\"error\": \"Invalid user data\"}"); } private ResponseEntity<?> handlePut(RequestEntity<?> request) { // Implementation for update return ResponseEntity.ok("{\"status\": \"Update endpoint\"}"); } } Build Configuration
Maven Configuration (pom.xml)
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>fission-java-functions</artifactId> <version>1.0.0</version> <packaging>jar</packaging> <properties> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <fission.version>1.18.0</fission.version> <spring.version>5.3.23</spring.version> </properties> <dependencies> <!-- Fission Java Support --> <dependency> <groupId>io.fission</groupId> <artifactId>java-function</artifactId> <version>${fission.version}</version> </dependency> <!-- Spring Web for HTTP handling --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <!-- Jackson for JSON --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.14.2</version> </dependency> <!-- Database Support --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <version>42.5.4</version> </dependency> <!-- Logging --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>2.0.6</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.11.0</version> <configuration> <source>11</source> <target>11</target> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.4.1</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>io.fission.java.JavaMain</mainClass> </transformer> </transformers> </configuration> </execution> </executions> </plugin> </plugins> </build> </project> Gradle Configuration (build.gradle)
plugins { id 'java' id 'com.github.johnrengelman.shadow' version '7.1.2' } group = 'com.example' version = '1.0.0' sourceCompatibility = '11' repositories { mavenCentral() } dependencies { implementation 'io.fission:java-function:1.18.0' implementation 'org.springframework:spring-web:5.3.23' implementation 'com.fasterxml.jackson.core:jackson-databind:2.14.2' implementation 'org.springframework:spring-jdbc:5.3.23' implementation 'org.postgresql:postgresql:42.5.4' implementation 'org.slf4j:slf4j-simple:2.0.6' } shadowJar { mergeServiceFiles() manifest { attributes 'Main-Class': 'io.fission.java.JavaMain' } } assemble.dependsOn shadowJar Deploying Java Functions
1. Create Java Environment
# Create a Java environment with JRE 11 fission environment create --name java11 \ --image fission/jvm-env:1.18.0 \ --version 3 \ --poolsize 3 \ --builder fission/jvm-builder:1.18.0 \ --mincpu 100 --maxcpu 500 \ --minmemory 128 --maxmemory 512
2. Deploy Simple Function
# Build the project first mvn clean package # Create the function fission function create --name helloworld \ --env java11 \ --code target/fission-java-functions-1.0.0.jar \ --entrypoint "com.example.fission.HelloWorldFunction" \ --executortype newdeploy \ --minscale 1 --maxscale 5
3. Create HTTP Trigger
# Create route for the function fission route create --name helloworld-route \ --function helloworld \ --url /hello \ --method GET
4. Test the Function
# Invoke the function via HTTP curl http://$FISSION_ROUTER/hello?name=JavaDeveloper # Expected response: # {"message":"Hello, JavaDeveloper!","timestamp":"2023-11-15T10:30:00Z","language":"Java"} Advanced Function Patterns
1. Function with Secrets
# Create secret for database credentials kubectl create secret generic db-secret \ --namespace fission-function \ --from-literal=db-url=jdbc:postgresql://postgresql:5432/mydb \ --from-literal=db-username=admin \ --from-literal=db-password=secret123 # Create function with secret fission function create --name user-service \ --env java11 \ --code target/functions.jar \ --entrypoint "com.example.fission.UserServiceFunction" \ --secret db-secret
2. Timer-Based Function (Cron Job)
package com.example.fission; import io.fission.Function; import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; public class ScheduledCleanupFunction implements Function { @Override public ResponseEntity<?> call(RequestEntity<?> request, Context context) { // This function runs on a schedule performCleanup(); logCleanupActivity(); return ResponseEntity.ok("{\"status\": \"Cleanup completed\"}"); } private void performCleanup() { // Clean up temporary files, expired sessions, etc. System.out.println("Performing scheduled cleanup at " + java.time.Instant.now()); } private void logCleanupActivity() { // Log to centralized logging system System.out.println("Cleanup activity logged"); } } Create Timer Trigger:
# Create the function first fission function create --name scheduled-cleanup \ --env java11 \ --code target/functions.jar \ --entrypoint "com.example.fission.ScheduledCleanupFunction" # Create timer trigger that runs every hour fission timer create --name hourly-cleanup \ --function scheduled-cleanup \ --cron "0 * * * *"
3. Message Queue Function
package com.example.fission; import io.fission.Function; import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; import com.fasterxml.jackson.databind.ObjectMapper; public class MessageProcessorFunction implements Function { private final ObjectMapper mapper = new ObjectMapper(); @Override public ResponseEntity<?> call(RequestEntity<?> request, Context context) { try { String messageBody = (String) request.getBody(); Message message = mapper.readValue(messageBody, Message.class); // Process the message processMessage(message); return ResponseEntity.ok("{\"status\": \"Message processed\"}"); } catch (Exception e) { // In message processing, you might want to handle errors differently System.err.println("Failed to process message: " + e.getMessage()); return ResponseEntity.status(500).body("{\"error\": \"Processing failed\"}"); } } private void processMessage(Message message) { // Business logic for message processing System.out.println("Processing message: " + message.getId()); // Example: Send email, update database, call external API switch (message.getType()) { case "USER_REGISTERED": sendWelcomeEmail(message.getUserId()); break; case "ORDER_PLACED": updateInventory(message.getOrderId()); break; default: System.out.println("Unknown message type: " + message.getType()); } } private void sendWelcomeEmail(String userId) { // Implementation for sending email System.out.println("Sending welcome email to user: " + userId); } private void updateInventory(String orderId) { // Implementation for inventory update System.out.println("Updating inventory for order: " + orderId); } // Inner class for message structure public static class Message { private String id; private String type; private String userId; private String orderId; // Getters and setters public String getId() { return id; } public void setId(String id) { this.id = id; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getOrderId() { return orderId; } public void setOrderId(String orderId) { this.orderId = orderId; } } } Create Message Queue Trigger:
# Create function fission function create --name message-processor \ --env java11 \ --code target/functions.jar \ --entrypoint "com.example.fission.MessageProcessorFunction" # Create Kafka trigger (if Kafka is configured) fission mqt create --name kafka-trigger \ --function message-processor \ --mqtype kafka \ --topic user-events \ --resptopic responses
Monitoring and Debugging
1. Function Logs
# View function logs fission function logs --name helloworld # Follow logs in real-time fission function logs --name helloworld --follow # View logs with specific time range fission function logs --name helloworld --since 1h
2. Function Metrics
# Get function information fission function get --name helloworld # List all functions fission function list # Get function metrics fission function metrics --name helloworld
3. Testing and Debugging
# Test function directly fission function test --name helloworld # Test with specific input echo '{"name": "TestUser"}' | fission function test --name helloworld # Update function code fission function update --name helloworld --code target/new-version.jar Best Practices for Java Functions
1. Keep Functions Stateless
public class StatelessFunction implements Function { @Override public ResponseEntity<?> call(RequestEntity<?> request, Context context) { // Avoid instance variables that maintain state between invocations // Use external storage (database, Redis) for state persistence return ResponseEntity.ok("Stateless response"); } } 2. Optimize Cold Starts
# Use pre-warmed pool with optimal size fission environment create --name java11-optimized \ --image fission/jvm-env:1.18.0 \ --poolsize 5 \ --minscale 2
3. Proper Resource Allocation
# Set appropriate resource limits fission function create --name resource-aware \ --env java11 \ --code target/app.jar \ --entrypoint "com.example.Function" \ --minmemory 256 --maxmemory 512 \ --mincpu 200 --maxcpu 1000
4. Error Handling and Retries
@Override public ResponseEntity<?> call(RequestEntity<?> request, Context context) { try { return processRequest(request); } catch (TemporaryFailureException e) { // Return 5xx for retryable errors return ResponseEntity.status(503) .body("{\"error\": \"Service temporarily unavailable\"}"); } catch (PermanentFailureException e) { // Return 4xx for permanent failures return ResponseEntity.status(400) .body("{\"error\": \"Invalid request: " + e.getMessage() + "\"}"); } } CI/CD Pipeline Example
GitHub Actions Workflow
name: Deploy Fission Function on: push: branches: [ main ] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Java uses: actions/setup-java@v3 with: java-version: '11' distribution: 'temurin' - name: Build with Maven run: mvn -B package -DskipTests - name: Install Fission CLI run: | curl -Lo fission https://github.com/fission/fission/releases/download/v1.18.0/fission-v1.18.0-linux-amd64 chmod +x fission sudo mv fission /usr/local/bin/ - name: Configure Kubernetes run: | echo "${{ secrets.KUBECONFIG }}" > kubeconfig export KUBECONFIG=kubeconfig - name: Deploy Function run: | fission function update --name helloworld --code target/fission-java-functions-1.0.0.jar # Or create if it doesn't exist # fission function create --name helloworld --env java11 --code target/fission-java-functions-1.0.0.jar --entrypoint "com.example.fission.HelloWorldFunction" Conclusion
Fission provides Java developers with a powerful platform for building serverless functions that can scale automatically and integrate seamlessly with Kubernetes ecosystems. Key advantages include:
- Rapid Development: Focus on business logic without infrastructure concerns
- Java Ecosystem: Leverage existing Java libraries and frameworks
- Event-Driven Architecture: Support for multiple trigger types
- Cost Efficiency: Scale to zero when not in use
- Kubernetes Native: Integrates with existing Kubernetes tooling
For Java teams looking to adopt serverless patterns without abandoning their language expertise, Fission offers an ideal balance of productivity, performance, and platform integration.