Spring Boot 微服务架构设计与实现

Spring Boot 微服务架构设计与实现

Spring Boot 微服务架构设计与实现

在这里插入图片描述
25.1 学习目标与重点提示

学习目标:掌握Spring Boot微服务架构设计与实现的核心概念与使用方法,包括微服务架构的定义与特点、Spring Boot与微服务的集成、Spring Boot与微服务的配置、Spring Boot与微服务的基本方法、Spring Boot的实际应用场景,学会在实际开发中处理微服务架构设计与实现问题。
重点:微服务架构的定义与特点Spring Boot与微服务的集成Spring Boot与微服务的配置Spring Boot与微服务的基本方法Spring Boot的实际应用场景

25.2 微服务架构概述

微服务架构是Java开发中的重要组件。

25.2.1 微服务架构的定义

定义:微服务架构是一种软件架构风格,将应用程序拆分为一组独立的服务,每个服务运行在自己的进程中,通过网络进行通信。
作用

  • 提高应用程序的可扩展性。
  • 提高应用程序的可维护性。
  • 提高应用程序的可靠性。

常见的微服务架构

  • Spring Cloud:Spring Cloud是Spring Boot提供的微服务框架。
  • Netflix OSS:Netflix OSS是Netflix提供的微服务框架。
  • Docker:Docker是一种容器化技术,用于打包和部署应用程序。
  • Kubernetes:Kubernetes是一种容器编排工具,用于管理和调度应用程序。

✅ 结论:微服务架构是一种软件架构风格,作用是提高应用程序的可扩展性、可维护性、可靠性。

25.2.2 微服务架构的特点

定义:微服务架构的特点是指微服务架构的特性。
特点

  • 独立部署:每个服务可以独立部署。
  • 独立开发:每个服务可以独立开发。
  • 独立运行:每个服务运行在自己的进程中。
  • 网络通信:每个服务通过网络进行通信。

✅ 结论:微服务架构的特点包括独立部署、独立开发、独立运行、网络通信。

25.3 Spring Boot与微服务的集成

Spring Boot与微服务的集成是Java开发中的重要内容。

25.3.1 集成Spring Cloud Eureka的步骤

定义:集成Spring Cloud Eureka的步骤是指使用Spring Boot与Spring Cloud Eureka集成的方法。
步骤

  1. 创建Spring Boot项目。
  2. 添加所需的依赖。
  3. 配置Spring Cloud Eureka。
  4. 创建服务提供者。
  5. 创建服务消费者。
  6. 测试应用。

示例
服务注册中心(Eureka Server)的pom.xml文件中的依赖:

<dependencies><!-- Eureka Server依赖 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId></dependency><!-- 测试依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>Hoxton.SR12</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>

服务注册中心(Eureka Server)的application.properties文件中的配置:

# 服务器端口 server.port=8761 # Eureka Server配置 eureka.client.register-with-eureka=false eureka.client.fetch-registry=false eureka.instance.hostname=localhost 

服务注册中心(Eureka Server)的启动类:

importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.cloud.netflix.eureka.server.EnableEurekaServer;@SpringBootApplication@EnableEurekaServerpublicclassEurekaServerApplication{publicstaticvoidmain(String[] args){SpringApplication.run(EurekaServerApplication.class, args);}}

服务提供者(Product Service)的pom.xml文件中的依赖:

<dependencies><!-- Web依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Eureka Client依赖 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency><!-- 测试依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>Hoxton.SR12</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>

服务提供者(Product Service)的application.properties文件中的配置:

# 服务器端口 server.port=8081 # 应用名称 spring.application.name=product-service # Eureka Client配置 eureka.client.service-url.defaultZone=http://localhost:8761/eureka/ eureka.instance.prefer-ip-address=true 

服务提供者(Product Service)的实体类:

publicclassProduct{privateLong id;privateString productId;privateString productName;privatedouble price;privateint sales;publicProduct(){}publicProduct(Long id,String productId,String productName,double price,int sales){this.id = id;this.productId = productId;this.productName = productName;this.price = price;this.sales = sales;}// Getter和Setter方法publicLonggetId(){return id;}publicvoidsetId(Long id){this.id = id;}publicStringgetProductId(){return productId;}publicvoidsetProductId(String productId){this.productId = productId;}publicStringgetProductName(){return productName;}publicvoidsetProductName(String productName){this.productName = productName;}publicdoublegetPrice(){return price;}publicvoidsetPrice(double price){this.price = price;}publicintgetSales(){return sales;}publicvoidsetSales(int sales){this.sales = sales;}@OverridepublicStringtoString(){return"Product{"+"id="+ id +",+ productId +'\''+",+ productName +'\''+", price="+ price +", sales="+ sales +'}';}}

服务提供者(Product Service)的控制器类:

importorg.springframework.web.bind.annotation.*;importjava.util.ArrayList;importjava.util.List;importjava.util.stream.Collectors;@RestController@RequestMapping("/api/products")publicclassProductController{privateList<Product> products =newArrayList<>();publicProductController(){ products.add(newProduct(1L,"P001","手机",1000.0,100)); products.add(newProduct(2L,"P002","电脑",5000.0,50)); products.add(newProduct(3L,"P003","电视",3000.0,80)); products.add(newProduct(4L,"P004","手表",500.0,200)); products.add(newProduct(5L,"P005","耳机",300.0,150));}@GetMapping("/")publicList<Product>getAllProducts(){return products;}@GetMapping("/{id}")publicProductgetProductById(@PathVariableLong id){return products.stream().filter(product -> product.getId().equals(id)).findFirst().orElse(null);}@PostMapping("/")publicProductaddProduct(@RequestBodyProduct product){ product.setId((long)(products.size()+1)); products.add(product);return product;}@PutMapping("/{id}")publicProductupdateProduct(@PathVariableLong id,@RequestBodyProduct product){Product existingProduct =getProductById(id);if(existingProduct !=null){ existingProduct.setProductId(product.getProductId()); existingProduct.setProductName(product.getProductName()); existingProduct.setPrice(product.getPrice()); existingProduct.setSales(product.getSales());}return existingProduct;}@DeleteMapping("/{id}")publicvoiddeleteProduct(@PathVariableLong id){ products.removeIf(product -> product.getId().equals(id));}}

服务提供者(Product Service)的启动类:

importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.cloud.netflix.eureka.EnableEurekaClient;@SpringBootApplication@EnableEurekaClientpublicclassProductServiceApplication{publicstaticvoidmain(String[] args){SpringApplication.run(ProductServiceApplication.class, args);}}

服务消费者(Order Service)的pom.xml文件中的依赖:

<dependencies><!-- Web依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Eureka Client依赖 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency><!-- Ribbon依赖 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-ribbon</artifactId></dependency><!-- 测试依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>Hoxton.SR12</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>

服务消费者(Order Service)的application.properties文件中的配置:

# 服务器端口 server.port=8082 # 应用名称 spring.application.name=order-service # Eureka Client配置 eureka.client.service-url.defaultZone=http://localhost:8761/eureka/ eureka.instance.prefer-ip-address=true 

服务消费者(Order Service)的实体类:

publicclassProduct{privateLong id;privateString productId;privateString productName;privatedouble price;privateint sales;publicProduct(){}publicProduct(Long id,String productId,String productName,double price,int sales){this.id = id;this.productId = productId;this.productName = productName;this.price = price;this.sales = sales;}// Getter和Setter方法publicLonggetId(){return id;}publicvoidsetId(Long id){this.id = id;}publicStringgetProductId(){return productId;}publicvoidsetProductId(String productId){this.productId = productId;}publicStringgetProductName(){return productName;}publicvoidsetProductName(String productName){this.productName = productName;}publicdoublegetPrice(){return price;}publicvoidsetPrice(double price){this.price = price;}publicintgetSales(){return sales;}publicvoidsetSales(int sales){this.sales = sales;}@OverridepublicStringtoString(){return"Product{"+"id="+ id +",+ productId +'\''+",+ productName +'\''+", price="+ price +", sales="+ sales +'}';}}publicclassOrder{privateLong id;privateString orderId;privateList<Product> products;publicOrder(){}publicOrder(Long id,String orderId,List<Product> products){this.id = id;this.orderId = orderId;this.products = products;}// Getter和Setter方法publicLonggetId(){return id;}publicvoidsetId(Long id){this.id = id;}publicStringgetOrderId(){return orderId;}publicvoidsetOrderId(String orderId){this.orderId = orderId;}publicList<Product>getProducts(){return products;}publicvoidsetProducts(List<Product> products){this.products = products;}@OverridepublicStringtoString(){return"Order{"+"id="+ id +",+ orderId +'\''+", products="+ products +'}';}}

服务消费者(Order Service)的控制器类:

importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.cloud.client.loadbalancer.LoadBalanced;importorg.springframework.context.annotation.Bean;importorg.springframework.http.ResponseEntity;importorg.springframework.web.bind.annotation.*;importorg.springframework.web.client.RestTemplate;importjava.util.ArrayList;importjava.util.List;importjava.util.stream.Collectors;@RestController@RequestMapping("/api/orders")publicclassOrderController{@AutowiredprivateRestTemplate restTemplate;privateList<Order> orders =newArrayList<>();publicOrderController(){ orders.add(newOrder(1L,"O001",newArrayList<>())); orders.add(newOrder(2L,"O002",newArrayList<>())); orders.add(newOrder(3L,"O003",newArrayList<>()));}@Bean@LoadBalancedpublicRestTemplaterestTemplate(){returnnewRestTemplate();}@GetMapping("/")publicList<Order>getAllOrders(){return orders;}@GetMapping("/{id}")publicOrdergetOrderById(@PathVariableLong id){return orders.stream().filter(order -> order.getId().equals(id)).findFirst().orElse(null);}@PostMapping("/")publicOrderaddOrder(@RequestBodyOrder order){ order.setId((long)(orders.size()+1)); orders.add(order);return order;}@PutMapping("/{id}")publicOrderupdateOrder(@PathVariableLong id,@RequestBodyOrder order){Order existingOrder =getOrderById(id);if(existingOrder !=null){ existingOrder.setOrderId(order.getOrderId()); existingOrder.setProducts(order.getProducts());}return existingOrder;}@DeleteMapping("/{id}")publicvoiddeleteOrder(@PathVariableLong id){ orders.removeIf(order -> order.getId().equals(id));}@GetMapping("/{id}/products")publicList<Product>getOrderProducts(@PathVariableLong id){Order order =getOrderById(id);if(order !=null){List<Product> products = order.getProducts();return products;}returnnewArrayList<>();}@PostMapping("/{id}/products")publicOrderaddOrderProduct(@PathVariableLong id,@RequestBodyProduct product){Order order =getOrderById(id);if(order !=null){ order.getProducts().add(product);}return order;}@DeleteMapping("/{id}/products/{productId}")publicOrderdeleteOrderProduct(@PathVariableLong id,@PathVariableLong productId){Order order =getOrderById(id);if(order !=null){ order.getProducts().removeIf(product -> product.getId().equals(productId));}return order;}@GetMapping("/{id}/product/{productId}")publicProductgetProductById(@PathVariableLong id,@PathVariableLong productId){Order order =getOrderById(id);if(order !=null){List<Product> products = order.getProducts();return products.stream().filter(product -> product.getId().equals(productId)).findFirst().orElse(null);}returnnull;}}

服务消费者(Order Service)的启动类:

importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.cloud.netflix.eureka.EnableEurekaClient;@SpringBootApplication@EnableEurekaClientpublicclassOrderServiceApplication{publicstaticvoidmain(String[] args){SpringApplication.run(OrderServiceApplication.class, args);}}

测试类:

importorg.junit.jupiter.api.Test;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.boot.test.context.SpringBootTest;importorg.springframework.boot.test.web.client.TestRestTemplate;importorg.springframework.boot.web.server.LocalServerPort;importorg.springframework.http.HttpEntity;importorg.springframework.http.HttpHeaders;importorg.springframework.http.HttpMethod;importorg.springframework.http.ResponseEntity;importjava.util.ArrayList;importjava.util.List;importstaticorg.assertj.core.api.Assertions.assertThat;@SpringBootTest(webEnvironment =SpringBootTest.WebEnvironment.RANDOM_PORT)classOrderServiceApplicationTests{@LocalServerPortprivateint port;@AutowiredprivateTestRestTemplate restTemplate;@TestvoidcontextLoads(){}@TestvoidtestGetAllOrders(){List<Order> orders = restTemplate.getForObject("http://localhost:"+ port +"/api/orders/",List.class);assertThat(orders).isNotNull();assertThat(orders.size()).isGreaterThanOrEqualTo(3);}@TestvoidtestAddOrder(){Order order =newOrder(null,"O004",newArrayList<>());Order savedOrder = restTemplate.postForObject("http://localhost:"+ port +"/api/orders/", order,Order.class);assertThat(savedOrder).isNotNull();assertThat(savedOrder.getOrderId()).isEqualTo("O004");}@TestvoidtestAddOrderProduct(){Product product =newProduct(1L,"P001","手机",1000.0,100);HttpEntity<Product> requestEntity =newHttpEntity<>(product);ResponseEntity<Order> response = restTemplate.exchange("http://localhost:"+ port +"/api/orders/1/products",HttpMethod.POST, requestEntity,Order.class);assertThat(response.getStatusCodeValue()).isEqualTo(200);assertThat(response.getBody()).isNotNull();assertThat(response.getBody().getProducts().size()).isGreaterThanOrEqualTo(1);}}

✅ 结论:集成Spring Cloud Eureka的步骤包括创建Spring Boot项目、添加所需的依赖、配置Spring Cloud Eureka、创建服务提供者、创建服务消费者、测试应用。

25.4 Spring Boot与微服务的配置

Spring Boot与微服务的配置是Java开发中的重要内容。

25.4.1 配置Spring Cloud Config

定义:配置Spring Cloud Config是指使用Spring Boot与Spring Cloud Config集成的方法。
步骤

  1. 创建Spring Boot项目。
  2. 添加所需的依赖。
  3. 配置Spring Cloud Config。
  4. 创建配置文件。
  5. 测试应用。

示例
配置服务器(Config Server)的pom.xml文件中的依赖:

<dependencies><!-- Config Server依赖 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-config-server</artifactId></dependency><!-- 测试依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>Hoxton.SR12</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>

配置服务器(Config Server)的application.properties文件中的配置:

# 服务器端口 server.port=8888 # 配置服务器配置 spring.cloud.config.server.git.uri=https://github.com/username/config-repo spring.cloud.config.server.git.search-paths=config-repo spring.cloud.config.server.git.username=username spring.cloud.config.server.git.password=password 

配置服务器(Config Server)的启动类:

importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.cloud.config.server.EnableConfigServer;@SpringBootApplication@EnableConfigServerpublicclassConfigServerApplication{publicstaticvoidmain(String[] args){SpringApplication.run(ConfigServerApplication.class, args);}}

配置客户端(Product Service)的pom.xml文件中的依赖:

<dependencies><!-- Web依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Config Client依赖 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-config</artifactId></dependency><!-- 测试依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>Hoxton.SR12</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>

配置客户端(Product Service)的bootstrap.properties文件中的配置:

# 应用名称 spring.application.name=product-service # 配置服务器地址 spring.cloud.config.uri=http://localhost:8888 

配置客户端(Product Service)的application.properties文件中的配置:

# 服务器端口 server.port=8081 

配置文件(product-service-dev.properties):

# 应用名称 spring.application.name=product-service # 服务器端口 server.port=8081 # 数据库连接信息 spring.datasource.url=jdbc:h2:mem:testdb spring.datasource.driver-class-name=org.h2.Driver spring.datasource.username=sa spring.datasource.password=password # JPA配置 spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=true # H2数据库控制台 spring.h2.console.enabled=true spring.h2.console.path=/h2-console 

✅ 结论:配置Spring Cloud Config是指使用Spring Boot与Spring Cloud Config集成的方法,步骤包括创建Spring Boot项目、添加所需的依赖、配置Spring Cloud Config、创建配置文件、测试应用。

25.5 Spring Boot与微服务的基本方法

Spring Boot与微服务的基本方法包括使用Ribbon、使用Feign、使用Hystrix。

25.5.1 使用Ribbon

定义:使用Ribbon是指Spring Boot与微服务集成的基本方法之一。
作用

  • 实现服务间的通信。
  • 提高应用程序的性能。

示例
服务消费者(Order Service)的控制器类:

importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.cloud.client.loadbalancer.LoadBalanced;importorg.springframework.context.annotation.Bean;importorg.springframework.http.ResponseEntity;importorg.springframework.web.bind.annotation.*;importorg.springframework.web.client.RestTemplate;importjava.util.ArrayList;importjava.util.List;importjava.util.stream.Collectors;@RestController@RequestMapping("/api/orders")publicclassOrderController{@AutowiredprivateRestTemplate restTemplate;privateList<Order> orders =newArrayList<>();publicOrderController(){ orders.add(newOrder(1L,"O001",newArrayList<>())); orders.add(newOrder(2L,"O002",newArrayList<>())); orders.add(newOrder(3L,"O003",newArrayList<>()));}@Bean@LoadBalancedpublicRestTemplaterestTemplate(){returnnewRestTemplate();}@GetMapping("/")publicList<Order>getAllOrders(){return orders;}@GetMapping("/{id}")publicOrdergetOrderById(@PathVariableLong id){return orders.stream().filter(order -> order.getId().equals(id)).findFirst().orElse(null);}@PostMapping("/")publicOrderaddOrder(@RequestBodyOrder order){ order.setId((long)(orders.size()+1)); orders.add(order);return order;}@PutMapping("/{id}")publicOrderupdateOrder(@PathVariableLong id,@RequestBodyOrder order){Order existingOrder =getOrderById(id);if(existingOrder !=null){ existingOrder.setOrderId(order.getOrderId()); existingOrder.setProducts(order.getProducts());}return existingOrder;}@DeleteMapping("/{id}")publicvoiddeleteOrder(@PathVariableLong id){ orders.removeIf(order -> order.getId().equals(id));}@GetMapping("/{id}/products")publicList<Product>getOrderProducts(@PathVariableLong id){Order order =getOrderById(id);if(order !=null){List<Product> products = order.getProducts();return products;}returnnewArrayList<>();}@PostMapping("/{id}/products")publicOrderaddOrderProduct(@PathVariableLong id,@RequestBodyProduct product){Order order =getOrderById(id);if(order !=null){ order.getProducts().add(product);}return order;}@DeleteMapping("/{id}/products/{productId}")publicOrderdeleteOrderProduct(@PathVariableLong id,@PathVariableLong productId){Order order =getOrderById(id);if(order !=null){ order.getProducts().removeIf(product -> product.getId().equals(productId));}return order;}@GetMapping("/{id}/product/{productId}")publicProductgetProductById(@PathVariableLong id,@PathVariableLong productId){Order order =getOrderById(id);if(order !=null){List<Product> products = order.getProducts();return products.stream().filter(product -> product.getId().equals(productId)).findFirst().orElse(null);}returnnull;}}

✅ 结论:使用Ribbon是指Spring Boot与微服务集成的基本方法之一,作用是实现服务间的通信、提高应用程序的性能。

25.6 Spring Boot的实际应用场景

在实际开发中,Spring Boot微服务架构设计与实现的应用场景非常广泛,如:

  • 实现产品服务的微服务化。
  • 实现用户服务的微服务化。
  • 实现订单服务的微服务化。
  • 实现支付服务的微服务化。

示例

importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.cloud.netflix.eureka.EnableEurekaClient;importorg.springframework.web.bind.annotation.*;importorg.springframework.web.client.RestTemplate;importjava.util.ArrayList;importjava.util.List;importjava.util.stream.Collectors;@SpringBootApplication@EnableEurekaClientpublicclassProductServiceApplication{publicstaticvoidmain(String[] args){SpringApplication.run(ProductServiceApplication.class, args);}}@RestController@RequestMapping("/api/products")classProductController{privateList<Product> products =newArrayList<>();publicProductController(){ products.add(newProduct(1L,"P001","手机",1000.0,100)); products.add(newProduct(2L,"P002","电脑",5000.0,50)); products.add(newProduct(3L,"P003","电视",3000.0,80)); products.add(newProduct(4L,"P004","手表",500.0,200)); products.add(newProduct(5L,"P005","耳机",300.0,150));}@GetMapping("/")publicList<Product>getAllProducts(){return products;}@GetMapping("/{id}")publicProductgetProductById(@PathVariableLong id){return products.stream().filter(product -> product.getId().equals(id)).findFirst().orElse(null);}@PostMapping("/")publicProductaddProduct(@RequestBodyProduct product){ product.setId((long)(products.size()+1)); products.add(product);return product;}@PutMapping("/{id}")publicProductupdateProduct(@PathVariableLong id,@RequestBodyProduct product){Product existingProduct =getProductById(id);if(existingProduct !=null){ existingProduct.setProductId(product.getProductId()); existingProduct.setProductName(product.getProductName()); existingProduct.setPrice(product.getPrice()); existingProduct.setSales(product.getSales());}return existingProduct;}@DeleteMapping("/{id}")publicvoiddeleteProduct(@PathVariableLong id){ products.removeIf(product -> product.getId().equals(id));}}@SpringBootApplication@EnableEurekaClientpublicclassOrderServiceApplication{publicstaticvoidmain(String[] args){SpringApplication.run(OrderServiceApplication.class, args);}}@RestController@RequestMapping("/api/orders")classOrderController{@AutowiredprivateRestTemplate restTemplate;privateList<Order> orders =newArrayList<>();publicOrderController(){ orders.add(newOrder(1L,"O001",newArrayList<>())); orders.add(newOrder(2L,"O002",newArrayList<>())); orders.add(newOrder(3L,"O003",newArrayList<>()));}@Bean@LoadBalancedpublicRestTemplaterestTemplate(){returnnewRestTemplate();}@GetMapping("/")publicList<Order>getAllOrders(){return orders;}@GetMapping("/{id}")publicOrdergetOrderById(@PathVariableLong id){return orders.stream().filter(order -> order.getId().equals(id)).findFirst().orElse(null);}@PostMapping("/")publicOrderaddOrder(@RequestBodyOrder order){ order.setId((long)(orders.size()+1)); orders.add(order);return order;}@PutMapping("/{id}")publicOrderupdateOrder(@PathVariableLong id,@RequestBodyOrder order){Order existingOrder =getOrderById(id);if(existingOrder !=null){ existingOrder.setOrderId(order.getOrderId()); existingOrder.setProducts(order.getProducts());}return existingOrder;}@DeleteMapping("/{id}")publicvoiddeleteOrder(@PathVariableLong id){ orders.removeIf(order -> order.getId().equals(id));}@GetMapping("/{id}/products")publicList<Product>getOrderProducts(@PathVariableLong id){Order order =getOrderById(id);if(order !=null){List<Product> products = order.getProducts();return products;}returnnewArrayList<>();}@PostMapping("/{id}/products")publicOrderaddOrderProduct(@PathVariableLong id,@RequestBodyProduct product){Order order =getOrderById(id);if(order !=null){ order.getProducts().add(product);}return order;}@DeleteMapping("/{id}/products/{productId}")publicOrderdeleteOrderProduct(@PathVariableLong id,@PathVariableLong productId){Order order =getOrderById(id);if(order !=null){ order.getProducts().removeIf(product -> product.getId().equals(productId));}return order;}@GetMapping("/{id}/product/{productId}")publicProductgetProductById(@PathVariableLong id,@PathVariableLong productId){Order order =getOrderById(id);if(order !=null){List<Product> products = order.getProducts();return products.stream().filter(product -> product.getId().equals(productId)).findFirst().orElse(null);}returnnull;}}// 测试类@SpringBootTest(webEnvironment =SpringBootTest.WebEnvironment.RANDOM_PORT)classOrderServiceApplicationTests{@LocalServerPortprivateint port;@AutowiredprivateTestRestTemplate restTemplate;@TestvoidcontextLoads(){}@TestvoidtestGetAllOrders(){List<Order> orders = restTemplate.getForObject("http://localhost:"+ port +"/api/orders/",List.class);assertThat(orders).isNotNull();assertThat(orders.size()).isGreaterThanOrEqualTo(3);}@TestvoidtestAddOrder(){Order order =newOrder(null,"O004",newArrayList<>());Order savedOrder = restTemplate.postForObject("http://localhost:"+ port +"/api/orders/", order,Order.class);assertThat(savedOrder).isNotNull();assertThat(savedOrder.getOrderId()).isEqualTo("O004");}@TestvoidtestAddOrderProduct(){Product product =newProduct(1L,"P001","手机",1000.0,100);HttpEntity<Product> requestEntity =newHttpEntity<>(product);ResponseEntity<Order> response = restTemplate.exchange("http://localhost:"+ port +"/api/orders/1/products",HttpMethod.POST, requestEntity,Order.class);assertThat(response.getStatusCodeValue()).isEqualTo(200);assertThat(response.getBody()).isNotNull();assertThat(response.getBody().getProducts().size()).isGreaterThanOrEqualTo(1);}}

输出结果

  • 访问http://localhost:8082/api/orders/:返回所有订单信息。
  • 访问http://localhost:8082/api/orders/1/products:返回订单1的产品信息。

✅ 结论:在实际开发中,Spring Boot微服务架构设计与实现的应用场景非常广泛,需要根据实际问题选择合适的微服务架构和工具。

总结

本章我们学习了Spring Boot微服务架构设计与实现,包括微服务架构的定义与特点、Spring Boot与微服务的集成、Spring Boot与微服务的配置、Spring Boot与微服务的基本方法、Spring Boot的实际应用场景,学会了在实际开发中处理微服务架构设计与实现问题。其中,微服务架构的定义与特点、Spring Boot与微服务的集成、Spring Boot与微服务的配置、Spring Boot与微服务的基本方法、Spring Boot的实际应用场景是本章的重点内容。从下一章开始,我们将学习Spring Boot的其他组件、微服务等内容。

Read more

首席情绪架构师(Chief Emotion Architect, CEA):工程化写作的总设计师

首席情绪架构师(Chief Emotion Architect, CEA):工程化写作的总设计师

笔言: 本文旨在对首席情绪架构师(Chief Emotion Architect, CEA)的职责边界进行系统性界定与划分。 题外话: 歌曲地址 【生成曲子不一定完全按照设计带有感情唱出来】 歌曲《天堑:写给所有在相亲路上沉默的人 》情绪曲线设计图(带情绪标记) 情绪曲线图 情绪强度 10 │ 9 │ 🔥【绝望的爆发·被定价的屈辱】 8 │ 😔【悲凉的自嘲·被淘汰的叹息】 7 │ 😮‍💨【无力的呐喊·被计算的自己】 💧【柔软的渴望·一句“回来啦”】 6 │ ╱ ╲ 5 │ 😐【现实的冰冷·被筛选的开始】 🤍【释然的退场·“吹吹风也挺好”】 4 │ 3 │ 2 │ 1 │__________________________________________________________ 主歌1 副歌1 主歌2 副歌2 桥段 尾声 【平静压抑】 【无奈爆发】 【今昔对比】 【绝望高潮】

By Ne0inhk
轮腿机器人代码调试补充

轮腿机器人代码调试补充

* @Author: 星夜雨夜 * @brief: 轮腿基础代码编写调试补充,移植自达妙开源代码 * @attention:笔者默认读者已经熟练掌握机甲大师RoboMaster c型开发板例程代码的底盘代码和INS_task.c陀螺仪代码、熟练掌握各电机can协议和遥控器dbus协议。默认读者已能看懂轮腿圣经和玺佬的五连杆运动学解算与VMC。建议读者仔细研读轮腿圣经3~5遍,边看MATLAB文件和达妙开源代码,掌握轮腿调试和编写大致思路。一定要注意各状态变量的单位和正负号是否正确,轮腿调试过程中,最难之处在于极性是否正确。本车所有电机均为逆时针旋转为正方向。 !!!强烈建议读者在开发轮腿之前,先运用LQR算法完成一阶倒立摆的平衡小车(即板凳模型)的实现 !!!如果时间紧,其实完全可以不搞仿真,直接实机开调。仿真不疯,实物不一定不疯;但实物疯,仿真必疯。 调试成果展示视频链接(抖音):轮腿机器人 一阶倒立摆平衡小车参考资料: 1.本科毕设 轮腿式双足机器人 开源文件演示_哔哩哔哩_bilibili(资料在视频评论区) 2.达妙平衡小车开源:[达妙科技开源系列-平衡小车] 第一弹_哔哩

By Ne0inhk
AIOps实践:基于 Dify+LangBot 实现飞书智能体对话机器人

AIOps实践:基于 Dify+LangBot 实现飞书智能体对话机器人

文章目录 * AIOps实践:Dify接入飞书实现与智能体对话 * 前言 * 环境搭建 * 1、Docker环境搭建 * 2、LangBot搭建 * 3、编辑流水线 * 4、配置飞书机器人 * 5、创建机器人 * 6、进行测试 * 附:遇到的问题 AIOps实践:Dify接入飞书实现与智能体对话 前言 前端时间把dify的智能体接入到了Prometheus和夜莺上,实现了与智能体的基本对话,并可以调取Prometheus数据进行分析,在那之后就开始深度研究AIOps实现原理于深度赋能运维的可能性,所以正在研究AIOps的核心:MCP Server;现在还并未成型,在研究的过程中,就想到了可否基于dify的agent,连接自建的mcp服务器,对接到飞书的机器人上,这样就可以和智能体进行对话,配合成型的mcp,就可以基本实现AIOps。 这里需要借助一个三方的开源工具LangBot,LangBot是一个生产级多平台 LLM 机器人开发平台。那么就开始实践吧: MCP Server开发的当前阶

By Ne0inhk
【AI】——SpringAI通过Ollama本地部署的Deepseek模型实现一个对话机器人(二)

【AI】——SpringAI通过Ollama本地部署的Deepseek模型实现一个对话机器人(二)

🎼个人主页:【Y小夜】 😎作者简介:一位双非学校的大三学生,编程爱好者, 专注于基础和实战分享,欢迎私信咨询! 🎆入门专栏:🎇【MySQL,Javaweb,Rust,python】 🎈热门专栏:🎊【Springboot,Redis,Springsecurity,Docker,AI】  感谢您的点赞、关注、评论、收藏、是对我最大的认可和支持!❤️ 目录 🎈Java调用Deepseek  🍕下载Deepseek模型  🍕本地测试  🍕Java调用模型 🎈构建数据库  🍕增强检索RAG  🍕向量数据库  🍕Springboot集成pgvector 🎈chatpdf 🎈function call调用自定义函数 🎈多模态能力 🎈Java调用Deepseek 本地没有安装Ollama、Docker,openwebUI,可以先学习一下这篇文章:【AI】——结合Ollama、Open WebUI和Docker本地部署可视化AI大语言模型_ollma+本地大模型+open web ui-ZEEKLOG博客

By Ne0inhk