🎧 New: AI-Generated Podcasts Turn your study notes into engaging audio conversations. Learn more

AttachmentUnit_2024-05-05T21-49-11-353__EIST__T03__Cloud_Design_Requirements_and_Software_Architectures.pdf

Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...

Full Transcript

T03: Cloud Design Requirements and Software Architectures Prof. Pramod Bhatotia Systems Research Group https://dse.in.tum.de/ 1 Roadmap Access Client...

T03: Cloud Design Requirements and Software Architectures Prof. Pramod Bhatotia Systems Research Group https://dse.in.tum.de/ 1 Roadmap Access Clients Our focus: Cloud software How to analyze, design, implement, test, and deploy cloud software systems? L03: L04 + L05 + L06: L07 + L08: L09: Cloud Design System Design and Software Testing and Software Management, Requirements Implementation Program Analysis Deployment, & Monitoring L10: Software Quality and Program management L03 + L04: Cloud Software Architectures L02: Cloud Systems Engineering 2 Tutorial outline - Part I: Lecture summary - Q&A for the lecture material - Part II: Homework programming exercises (Artemis) - Part III: Programming basics - Part IV: Discuss text exercises (Artemis) 3 Lecture overview - Part I: Requirements engineering - Stages in requirements engineering - Requirement types (functional/non-functional) - Non-functional requirements in the cloud - Part II: Software architectures in the cloud - Three-tier architecture - Monolithic architecture - Part III: Microservice architecture - Overview - From monoliths to microservices - Deploying with Kubernetes 4 Stages in requirement engineering Technical specs for Customer Requirement engineering system development Problem Elicitation Analysis Specification Verification Management Maintenance / Validation May involve several iterations 5 Requirement types - Functional requirements: - Describe the specific tasks and functions that a system or product must perform - Typically expressed in terms of use cases or user stories, and describe the features and functionalities of a system or product - Non-functional requirements: - Describe the characteristics or qualities that the system or product must possess to meet the desired level of performance, usability, and reliability - Typically expressed in terms of quality attributes, such as system's response time, availability, or its ability to handle a certain number of users or transactions per second IMPORTANT: Both functional and non-functional requirements are essential to the success of a software project, as they help to ensure that the system meets the needs and expectations of its intended users 6 Non-functional requirements #1: Performance #2: Scalability #3: Reliability #4: Availability #5: Security #6: Maintainability #7: Deployability Software architectures in the cloud Choice of architecture depends on: Main architectures in the cloud: - Requirements - Monolithic - Functional - Non-functional Application - Development process - Size of the software - Number of developers - Microservice-based Deployment models for microservices Baremetal Containers ✗ Hard to scale on-demand ✔ Scales on-demand ✗ Lack of isolation between services ✔ Isolation between services (+) ✔ Easy environment packaging Virtual machines Function-as-a-Service (FaaS) ✔ Scales on-demand ✔ Scales on-demand ✔ Isolation between services (++) ✔ Cost efficient ✔ Easy environment packaging ✗ Limited control of the environment ✗ Cost of virtualization 9 Container orchestration with Kubernetes Kubernetes is a container orchestrator from Google - Container deployment - Map containers on physical machines - Schedule containers - Network management - Service discovery - Load balancing - Scaling up/down - Replicate/destroy containers to scale - Load balancing Tutorial outline - Part I: Lecture summary - Q&A for the lecture material - Part II: Homework programming exercises (Artemis) - Part III: Programming basics - Part IV: Discuss text exercises (Artemis) 11 Homework programming exercises - Explain the homework programming exercises in Artemis https://artemis.cit.tum.de/ 12 Tutorial outline - Part I: Lecture summary - Q&A for the lecture material - Part II: Homework programming exercises (Artemis) - Part III: Programming basics - Part IV: Discuss text exercises (Artemis) 13 L03PB01 Strangler Pattern - Problem Statement - Tasks: - Migrating a legacy monolithic server (TUMMit) into a microservices architecture - Applying strangler pattern to shrink the monolith gradually - Implementation in Java - Goals: - Understand the concept of strangler pattern - Experience how the strangler pattern simplifies code refactoring of a complex system 14 L03PB01 Strangler Pattern - Problem Statement To-do: 1. TUMMit (A Reddit like social app) has monolithic structure with register/delete user, authentication, create discussion, add/get comments functionalities. Current implementation is slow and inefficient, refactor it to microservices. 2. All functionality is inside Tummit class. Divide its functions into three logical groups (User, Authentication, Discussion) which are implemented by microservices. 3. Create API endpoints for each microservice. 15 L03PB01 Strangler Pattern - Background - Strangler pattern puts the old system behind an intermediary facade - Over time, external replacement services for the old system are added behind the facade - The facade represents the functional entry points to the existing system - For more information visit the link! https://www.redhat.com/architect/pros-and-cons-strangler-architecture-pattern 16 L03PB01 Strangler Pattern - Background Why is the use of strangler pattern particularly appropriate in this context: - It allows gradual shrinkage of the complexity of monolith without compromising functionality, enables incremental testing - Defines a higher-level interface which hides the complexity of the backend migration from the client - Reduces the overall risk of breaking the application completely 17 L03PB01 Strangler Pattern - Solution Steps - UML Diagram of Microservice Application - Dividing Monolith into Microservices - Understand functionality groups that serve similar purpose: User, Authentication, Discussion - Create microservices and API endpoints to them 18 L03PB01 Strangler Pattern - Example Solution (1/3) - Migrate user operations from Tummit to UserService: public void registerUser(String username, String password) { if (!users.stream().filter(user -> user.getUsername().equals(username)).findFirst().isEmpty()) { return; } User user = new User(username, password); users.add(user); } public void deleteUser(String username, Long token) { if (!authenticationService.isAuthenticated(username, token)) { return; } discussionService.deleteUserComments(username); users.removeIf(user -> user.getUsername().equals(username)); authenticationService.deauthenticateUser(username); } 19 L03PB01 Strangler Pattern - Example Solution (2/3) - Migrate authentication operations from Tummit to AuthenticationService: public Long authenticateUser(String username, String password) { User user = userService.getUser(username); if (user == null) { return -1L; } if (!user.getPassword().equals(password)) { return -1L; } Long token = Math.abs(this.random.nextLong()); userTokens.put(username, token); return token; } 20 L03PB01 Strangler Pattern - Example Solution (3/3) - Migrate discussion operations from Tummit to AuthenticationService: public void createDiscussion(String username, Long token, String topic) { if (!authenticationService.isAuthenticated(username, token)) { return; } Discussion discussion = new Discussion(topic); discussions.add(discussion); } public void addComment(String username, Long token, String topic, String comment) { if (!authenticationService.isAuthenticated(username, token)) { return; } var optionalDiscussion = discussions.stream().filter(discussion -> discussion.getTopic().equals(topic)).findFirst(); if (optionalDiscussion.isEmpty()) { return; } Discussion discussion = optionalDiscussion.get(); discussion.addComment(username, comment); } 21 L03PB01 Strangler Pattern - Example Solution (3/3) - Migrate discussion operations from Tummit to AuthenticationService: public List getComments(String topic) { var optionalDiscussion = discussions.stream().filter(discussion -> discussion.getTopic().equals(topic)).findFirst(); if (optionalDiscussion.isEmpty()) { return null; } Discussion discussion = optionalDiscussion.get(); return discussion.getComments(); } public List getCommentsByUser(String topic, String username) { var optionalDiscussion = discussions.stream().filter(discussion -> discussion.getTopic().equals(topic)).findFirst(); if (optionalDiscussion.isEmpty()) { return null; } Discussion discussion = optionalDiscussion.get(); return discussion.getCommentsByUser(username); } 22 L03PB02 REST Architectural Style (Client-Server) - Problem Statement - Tasks: - Designing and implementing a REST API server - Implementing necessary endpoints - Deploying the server using Docker - Goals: - Understand the concept behind REST Architectural Style - Experience the flexibility of REST, as well as the advantages of stateless APIs 23 L03PB02 REST Architectural Style (Client-Server) - Problem Statement Already implemented from last week (L02PB01): - CustomerResource and CustomerController To-do: - Implementing online ordering and takeaways for a restaurant with a REST API - Implement the OrderResource class, which implements customer related operations - Implement the OrderController class, which is the REST controller for customers - Create a Dockerfile to deploy the REST server 24 L03PB02 REST Architectural Style (Client-Server) - Background REpresentational State Transfer is a set of constraints for communication: - Client-server: client and server are independent - Stateless: the server does not maintain a state between requests - Cacheable: data can be cached anywhere (client, server, on the network) - Uniform interface: the server interface should be usable by an arbitrary client, without knowledge of the internal server representation - Layered systems: clients can transparently communicate with the server through other layers (proxy, load balancer) 25 L03PB02 REST Architectural Style (Client-Server) - Background REST methods manipulate resources and are usually mapped to HTTP methods, usually formatted in HTML, XML or JSON: - GET: retrieve a resource, e.g., query the list of branches of a GitHub repository: curl -X GET https://api.github.com/repos/OWNER/REPO/branches - POST: create a resource, e.g., rename a branch in a GitHub repository: curl -X POST \ https://api.github.com/repos/OWNER/REPO/branches/BRANCH/rename \ -d '{"new_name":"my_renamed_branch"}' - PUT: update a resource, e.g., merge a pull request in a GitHub repository: curl -X PUT \ https://api.github.com/repos/OWNER/REPO/pulls/PULL_NUMBER/merge \ -d '{"commit_title":"title","commit_message":"msg"}' - DELETE: delete a resource, e.g., delete a GitHub repository: curl -X DELETE https://api.github.com/repos/OWNER/REPO 26 L03PB02 REST Architectural Style (Client-Server) - Example Solution (1/3) - Implement GET/PUT/POST/DELETE methods in the OrderResource class @PostMapping("orders") public ResponseEntity createOrder(@RequestBody Order.OrderRequest orderRequest) { List customers = customerService.getAllPersons(null); var optionalCustomer = customers.stream().filter(customer -> customer.getId().equals(orderRequest.getCustomerId())).findFirst(); if (optionalCustomer.isEmpty()) return ResponseEntity.badRequest().build(); Order.MenuItem item = switch (orderRequest.getItem()) { case "Pizza" -> MenuItem.Pizza; case "Spaghetti" -> MenuItem.Spaghetti; case "Hamburger" -> MenuItem.Hamburger; default -> null; }; if (item == null) return ResponseEntity.badRequest().build(); Order order = new Order(); order.setId(UUID.randomUUID()); order.setCustomerId(orderRequest.getCustomerId()); order.setItem(item); order.setOrderedOn(LocalDate.now()); return ResponseEntity.ok(orderService.saveOrder(order)); } 27 L03PB02 REST Architectural Style (Client-Server) - Example Solution (1/3) - Implement GET/PUT/POST/DELETE methods in the OrderResource class @PutMapping("orders/{orderId}") public ResponseEntity updateOrder(@RequestBody Order.OrderRequest orderRequest, @PathVariable("orderId") UUID orderId) { List orders = orderService.getAllOrders(null, null); var optionalOrder = orders.stream().filter(order -> order.getId().equals(orderId)).findFirst(); if (optionalOrder.isEmpty()) { return ResponseEntity.badRequest().build(); } List customers = customerService.getAllPersons(null); var optionalCustomer = customers.stream().filter(customer -> customer.getId().equals(orderRequest.getCustomerId())).findFirst(); if (optionalCustomer.isEmpty()) { return ResponseEntity.badRequest().build(); } Order.MenuItem item = switch (orderRequest.getItem()) { case "Pizza" -> MenuItem.Pizza; case "Spaghetti" -> MenuItem.Spaghetti; case "Hamburger" -> MenuItem.Hamburger; default -> null; }; if (item == null) { return ResponseEntity.badRequest().build(); } Order order = optionalOrder.get(); order.setCustomerId(orderRequest.getCustomerId()); order.setItem(item); return ResponseEntity.ok(order); 28 } L03PB02 REST Architectural Style (Client-Server) - Example Solution (1/3) - Implement GET/PUT/POST/DELETE methods in the OrderResource class @GetMapping("orders") public ResponseEntity getAllOrders(@RequestParam(value = "from", defaultValue = "") Long from, @RequestParam(value = "to", defaultValue = "") Long to) { return ResponseEntity.ok(orderService.getAllOrders(from, to)); } @DeleteMapping("orders/{orderId}") public ResponseEntity deleteOrder(@PathVariable("orderId") UUID orderId) { orderService.deleteOrder(orderId); return ResponseEntity.noContent().build(); } 29 L03PB02 REST Architectural Style (Client-Server) - Example Solution (2/3) - Implement GET/PUT/POST/DELETE methods in the OrderController class public void addOrder(Order.OrderRequest orderRequest, public void updateOrder(UUID orderId, Order.OrderRequest Consumer ordersConsumer) { orderRequest, Consumer ordersConsumer) { webClient.post() webClient.put().uri("orders").uri("orders/" + orderId).bodyValue(orderRequest).bodyValue(orderRequest).retrieve().retrieve().bodyToMono(Order.class).bodyToMono(Order.class).onErrorStop().onErrorStop().subscribe(newOrder -> {.subscribe(newOrder -> { orders.add(newOrder); orders.replaceAll(oldOrder -> ordersConsumer.accept(orders); oldOrder.getId().equals(newOrder.getId()) ? }); newOrder : oldOrder); } ordersConsumer.accept(orders); }); } 30 L03PB02 REST Architectural Style (Client-Server) - Example Solution (2/3) - Implement GET/PUT/POST/DELETE methods in the OrderController class public void deleteOrder(UUID orderId, public void getAllOrders(Long from, Long to, Consumer ordersConsumer) { Consumer ordersConsumer) { webClient.delete() webClient.get().uri("orders/" + orderId).uri(uriBuilder -> uriBuilder.retrieve().path("orders").toBodilessEntity().queryParam("from", from).onErrorStop().queryParam("to", to).subscribe(v -> {.build()) orders.removeIf(order ->.retrieve() order.getId().equals(orderId));.bodyToMono( ordersConsumer.accept(orders); new ParameterizedTypeReference() {}) });.onErrorStop() }.subscribe(newOrders -> { orders.clear(); orders.addAll(newOrders); ordersConsumer.accept(orders); }); } 31 L03PB02 REST Architectural Style (Client-Server) - Example Solution (3/3) - Complete the Dockerfile FROM openjdk:17-bullseye WORKDIR /app # Copy the compiled jar COPY build/libs/L03PB02-Solution-1.0.0-plain.jar app.jar # Copy the start.sh script COPY start.sh start.sh # Make start.sh executable RUN chmod 770 start.sh # Set the start command CMD./start.sh 32 L03PB03 Microservices Architechture - Problem Statement - Tasks: - Designing and adding a message broker into a project which utilizes microservices architecture - Handling the asynchronous communication between the microservices and message broker - Goals: - Understand how microservices architectures work - Experience the isolation and simplification that microservices architectures bring, as well as its implementation difficulties, e.g., managing asynchronous communication between individual services 33 L03PB03 Microservices Architechture - Problem Statement To-do: - Refactor the structure to add a message broker between TweetMicroservice and Client - Understand message broker in between them and its message queue system - Fill the empty methods in message broker and convert TweetMicroservice to async communication 34 L03PB03 Microservices Architechture - Background - A message broker is software that enables applications, systems, and services to communicate with each other and exchange information. The message broker does this by translating messages between formal messaging protocol. - IBM - We are revisiting TUM Social App, this time in Microservices Architecture - Same functionality: login, tweet, delete tweet, follow and unfollow 35 L03PB03 Microservices Architechture - Background - Each logical functionality group has its own Spring Boot microservice application and its respective API endpoint - We want to enhance communication between client and microservices to provide async communication 36 L03PB03 Microservices Architechture - Example Solution (1/3) UML Diagram of the Microservices Application with Message Broker - Communication for sending tweets happening through MessageBroker with controlling the load of the operation 37 L03PB03 Microservices Architechture - Example Solution (2/3) - Implement an asynchronous call in sendTweet in TweetController @PostMapping(value = "/send") public CompletableFuture sendTweet(@RequestBody Tweet tweet){ return CompletableFuture.supplyAsync(() -> { saveToDb(); return ("The tweet is sent"); }); } 38 L03PB03 Microservices Architechture - Example Solution (2/3) - Implement sendTweet and enqueue tweets in MBController @PostMapping(value = "/tweet/send") public CompletableFuture sendTweet(@RequestBody Tweet tweet){ CompletableFuture future = new CompletableFuture(); // Enqueue the tweet tweetQueue.offer(tweet); // Respond immediately with a placeholder message future.complete("Tweet request received. Processing..."); return future; } 39 L03PB03 Microservices Architechture - Example Solution (3/3) - Implement processTweets in MBController and redirect tweets to TweetController inside a Thread private void processTweets() { new Thread(() -> { while (true) { try { Tweet tweet = tweetQueue.take(); // Dequeue tweets and process them int userId = tweet.getUser().getUserID(); userTweetCount.putIfAbsent(userId,0); if((userTweetCount.containsKey(userId) && userTweetCount.get(userId) < 5)) { headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity requestEntity = new HttpEntity(tweet, headers); ResponseEntity responseEntity = restTemplate.postForEntity(sendTweetURL, requestEntity, String.class); if(responseEntity.getStatusCode() == HttpStatusCode.valueOf(200)) userTweetCount.replace(userId, 1 + userTweetCount.get(userId)); responses.putIfAbsent(tweet.getTweetID(), responseEntity.getBody()); } else { responses.putIfAbsent(tweet.getTweetID(),"You finished your daily Tweet limit"); } notifyUser(tweet); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } 40 Tutorial outline - Part I: Lecture summary - Q&A for the lecture material - Part II: Homework programming exercises (Artemis) - Part III: Programming basics - Part IV: Discuss text exercises (Artemis) 41 Discuss text exercises - Discuss text exercises in Artemis https://artemis.cit.tum.de/ 42

Use Quizgecko on...
Browser
Browser