1、微服务概述
本案例主要解决微服务之间的相互调用问题
如果已经理解什么是微服务,可以直接跳到实战。
本案例采用springBoot3.1.7+springCloud2022.0.4版本测试
本案例使用springboot2.7.x版本测试代码相同
1、微服务是分布式架构,那么为什么要需要使用分布式架构?
因为单体服务是将所有的模块都放在一个项目中,这种架构部署简单,维护方便,但是随着功能的增加各个模块之间的边界会越来越模糊(即使各个模块的融合会越来越密切),这就造成了一个问题如当修改订单服务的时候,可能会影响到商品系统、短信系统等,使程序的维护性降低等。且单体服务有性能瓶颈,越来越无法满足当今的互联网工程,这就出现了微服务。
2、微服务和springCloud之间的关系?
如果说springCloud就是微服务这是不正确的,springCloud其实是一个工具集,将解决微服务中出现的各种问题的插件集成到一起,形成了一个整套的解决方案,这些解决方案偏向于服务治理等。
除此之外还有很多的其他工具如springCloudAlibaba等,都是很好的解决微服务问题的工具。
3、什么是微服务?
微服务就是一种分布式架构,本质就是将单体服务拆分中一个一个的独立运行的小工程,这些小的工程如订单系统、商品系统等运行在不同的服务器中,这些独立运行的小工程就是微服务。
使用了微服务之后我们需要解决一些列的问题?
问题1:微服务之间怎么知道彼此的存在,我们可以通过注册中心将每个微服务的信息注册到注册中心,注册中心可以使用Eureka或Nacos等。
问题2:各个独立运行的微服务之间如何进行通信,我们可以使用RestTemplate或openfeign
问题3:当微服务特别多的时候,各个微服务都有配置文件,如何统一管理这些配置文件,我们可以使用配置中心,将配置都放置在同一个地方如git中,通过配置中心统一加载。
问题4:使用了微服务之后,就会出现在分布式系统下的一些问题,如分布式的id,分布式锁,分布式事务等问题,我们需要使用到分布式相关的技术。
问题5:使用了微服务之后,一个项目会出现很多的微服务,这个时候项目的部署是一个非常麻烦的事情,为了方便,我们可以使用Jekins等实现自动化部署等。
问题6:使用了微服务之后,我们的服务部署在不同的服务上,不同的IP和不同的端口会为我们的方位带来一定的困扰,这个时候我们可以使用网关服务如zuul,gateway等技术解决这些问题。
2、案例中的微服务结构
重点1:我们创建二个微服务,一个订单系统,一个商品系统,并将这两个系统注册到注册中心EurekaServer中。这个时候我们需要解决OrderServer订单系统与GoodsServer商品系统的通信问题,我们可以通过二中方式解决通信问题,第一:RestTemplate,第二:OpenFeign。
重点2:我们在GoodsServer商品系统中创建deductStock方法模拟减库存,在OrderServer订单系统中创建createOrder方法模拟创建订单。然后在createOrder中调用deductStock方法。
3、创建EurekaServer
3.1、创建工程并导入包信息
核心包:
spring-cloud-starter-netflix-eureka-server
spring-boot-starter-web
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.1.7</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.txc</groupId> <artifactId>eurekaserver0828</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>eurekaserver0828</name> <description>eurekaserver0828</description> <properties> <java.version>17</java.version> <spring-cloud.version>2022.0.4</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <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-tomcat</artifactId> <scope>provided</scope> </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>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>3.1.0</version> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <mainClass>com.txc.eurekaserver0828.Eurekaserver0828Application</mainClass> </configuration> </plugin> </plugins> </build> </project>
3.2、在application.properties中配置注册中心信息
#注册中心端口号 server.port=8761 #注册中心地址 eureka.client.service-url.defaultZone=http://localhost:8761/eureka/ #是否将自己注册到Eureka-Server中,默认的为true eureka.client.register-with-eureka=false eureka.client.fetch-registry=false #为服务起一个别名 spring.application.name=eurekaserver
3.3、在启动类中添加注册中心服务配置
核心注解:@EnableEurekaServer
@SpringBootApplication @EnableEurekaServer public class Eurekaserver0828Application { public static void main(String[] args) { SpringApplication.run(Eurekaserver0828Application.class, args); } }
3.4、访问Eureka注册中心监控页面
访问地址:http://localhost:8761/
此时注册中心中没有任何的微服务
主要参数含义介绍:
total-avail-memory:总可用内存
num-of-cpus:cpu数量
current-memory-usage:当前内存使用量
server-uptime:服务器正常运行时间
registerd-replicas:注册副本
available-replicas:可用副本
4、创建GoodsServer微服务并注册到注册中心中(服务注册)
服务注册:将微服务注册到Eureka Server中
4.1、创建GoodsServer工程并导包
核心包:
spring-cloud-starter-netflix-eureka-client
spring-boot-starter-web
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.1.7</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.txc</groupId> <artifactId>goodsserver</artifactId> <version>0.0.1-SNAPSHOT</version> <name>goodsserver</name> <description>goodsserver</description> <properties> <java.version>17</java.version> <spring-cloud.version>2022.0.4</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </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>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <image> <builder>paketobuildpacks/builder-jammy-base:latest</builder> </image> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>
4.2、创建GoodsServer配置信息
在application.properties中添加如下信息
server.port=8001 spring.application.name=goodsserver eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
4.3、在启动类中添加Eureka客户端注解
核心注解:@EnableDiscoveryClient
@SpringBootApplication @EnableDiscoveryClient public class GoodsserverApplication { public static void main(String[] args) { SpringApplication.run(GoodsserverApplication.class, args); } }
4.4、创建测试方法
创建deductStock模拟减库存行为,接收一个String类型参数,返回String类型参数。
@Controller public class GoodsController { @RequestMapping("/deductStock") @ResponseBody public String deductStock(String goodsCode){ System.out.println(goodsCode+"=====goods deduct success======"); //模拟减库存 return "库存减去成功"; } }
4.5、启动工程并查看Eureka Server注册中心信息
5、创建OrderServer微服务并注册到注册中心中
5.1、创建OrderServer工程并导包
核心包:
spring-cloud-starter-netflix-eureka-client
spring-boot-starter-web
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.1.7</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.txc</groupId> <artifactId>orderserver</artifactId> <version>0.0.1-SNAPSHOT</version> <name>orderserver</name> <description>orderserver</description> <properties> <java.version>17</java.version> <spring-cloud.version>2022.0.4</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </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>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <image> <builder>paketobuildpacks/builder-jammy-base:latest</builder> </image> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>
5.2、创建OrderServer工程配置信息
这个工程我特地使用了yml文件配置,本质与properties相同
GoodsServer工程使用8081端口,所以我们的OrderServer工程使用8082端口
server: port: 8082 spring: application: name: orderserver eureka: client: service-url: defaultZone: http://localhost:8761/eureka/
5.3、在启动类中添加Eureka客户端注解
核心注解:@EnableDiscoveryClient
@SpringBootApplication @EnableDiscoveryClient public class OrderserverApplication { public static void main(String[] args) { SpringApplication.run(OrderserverApplication.class, args); } }
5.4、创建createOrder测试方法
@Controller public class OrderController { @RequestMapping("/createOrder") @ResponseBody public String createOrder(){ System.out.println("=====order create success====="); return "订单创建成功返回值。"; } }
5.5、启动工程并查看EurekaServer注册中心信息
6、实现OrderServer工程与GoodsServer工程通信(服务发现)
服务发现:就是通过RestTemplate和OpenFeign方式通信。
两个微服务通信的听着高大上,本质就是实现A工程中的方法调用B工程中的方法。在单体服务中可以直接通过new类调用方法的方式,微服务中稍微麻烦一些。
6.1、方式1:在OrderServer工程中调用deductStore方法
在OrderServer的createOrder方法中调用deductStore方法
这种调用方式的不足之处:无法使用高可用服务,只能指定访问8081端口。
@Controller public class OrderController { @RequestMapping("/createOrder") @ResponseBody public String createOrder(){ System.out.println("=====order create success====="); //需要调用商品服务中的商品信息,扣除库存信息。 RestTemplate restTemplate=new RestTemplate(); String result=restTemplate.getForObject("http://localhost:8081/deductStock?goodsCode=1001",String.class); System.out.println("===result==="+result); return "订单创建成功返回值。"; } }
访问结果:
测试地址:http://localhost:8082/createOrder?goodsCode=1001
6.2、方式2:在OrderServer工程中调用deductStore方法
这种方式能够适应高可用服务,使用到了LoadBalanced
第一步:在启动类中添加如下信息
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
@SpringBootApplication @EnableDiscoveryClient public class OrderserverApplication { public static void main(String[] args) { SpringApplication.run(OrderserverApplication.class, args); } @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); } }
第二步:在控制类中使用
在调用的时候,不需要写具体的ip地址和port,而是直接写服务的名称,也就是GoodsServer工程中通过spring.application.name起的名字。
@Controller public class OrderController { @Autowired RestTemplate restTemplate; @RequestMapping("/createOrder") @ResponseBody public String createOrder(){ System.out.println("=====order create success====="); //需要调用商品服务中的商品信息,扣除库存信息。 //第一种 /*RestTemplate restTemplate=new RestTemplate(); String result=restTemplate.getForObject("http://localhost:8081/deductStock?goodsCode=1001",String.class); System.out.println("===result==="+result);*/ //第二种 String result=restTemplate.getForObject("http://GOODSSERVER/deductStock?goodsCode=1001",String.class); System.out.println("===result==="+result); return "订单创建成功返回值。"; } }
测试结果:
测试地址:
测试地址:http://localhost:8082/createOrder?goodsCode=1001
6.3、方式3:通过OpenFeign方式调用
OpenFeign方式调用能让开发者感觉在调用本地方法一样,体验较好。底层使用动态代理的方式实现,本质是基于HTTP的远程调用。
声明式REST客户端(伪RPC),采用基于接口的注解,内部使用ribbon做负载均衡—集群,能够达到仿佛调用本地方法的体验。
6.3.1、在OrderServer中加入OpenFeign的包信息
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> <version>3.1.1</version> </dependency>
6.3.2、在启动类中配置OpenFeign注解启动
核心注解:@EnableFeignClients
@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients public class OrderserverApplication { public static void main(String[] args) { SpringApplication.run(OrderserverApplication.class, args); } @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); } }
6.3.3、在OrderServer中创建调用注解类
说明1:@FeignClient(name = "GOODSSERVER"):name的值必须等于调用的微服务goodsServer中spring.application.name定义的值 说明2:@RequestMapping("/deductStock"):括号中的名称必须等于调用的微服务中真实的请求的地址
@FeignClient(name = "GOODSSERVER") public interface GoodsServerClients { @RequestMapping("/deductStock") public String deductStock(String goodsCode); }
6.3.4、测试程序
测试地址:http://localhost:8082/createOrder?goodsCode=1001
7、源码下载
下载地址:https://download.csdn.net/download/tangshiyilang/88771218