文章目录
- 一、微服务的理论
- 1、微服务和分布式的区别
- 2、微服务的拆分规范和原则
- 二、微服务的注册与发现(Eureka)
- 1、Spring Cloud Eureka的概念
- 2、构建聚合父工程
- 3、搭建Eureka服务
- 4、搭建Eureka服务的提供者
- 5、创建Eureka服务的消费者
- 三、Eureka的其他功能
- 1、服务的剔除和自保
- 2、自定义服务在Eureka上的注册名
- 3、服务发现Discovery
- 4、Eureka的集群搭建
- 总结:
一、微服务的理论
1、微服务和分布式的区别
分布式:分布式解决的是系统性能问题: 即解决系统部署上单点的问题,尽量让组成系统的子系统分散在不同的机器上进而提高系统的吞吐能力。
即多个相同的子系统在不同的服务器上。
分布式系统架构存在问题通过服务治理解决。
微服务:将系统的业务功能划分为极小的独立微服务,每个微服务只关注于完成某个小的任务。系统中的单个微服务可以被独立部署和扩展,且各个微服务之间是高内聚、低耦合的。微服务之间采用轻量化通信机制暴露接口来实现通信。
即将一套系统拆分成不同子系统部署在不同服务器上。
微服务一般来说是针对应用层面的。
分布式属于微服务,但是微服务不一定是分布式,因为微服务的应用不一定是分散在多个服务器上,它也可以是同一个服务器。这也是分布式和微服务的一个细微差别。
2、微服务的拆分规范和原则
-
压力模型拆分,简单来说就是用户访问量,需要识别出某些超高并发量的业务,尽可能把这部分业务独立拆分出来。
将压力模型拆分为3个维度:
1)高频高并发场景,例如商品详情
2)低频突发流量场景,例如秒杀
3)低频流量场景,例如上下架商品 -
业务模型拆分,从主链路、领域模型和用户群体三个维度。
1)主链路拆分,指用户使用时必须经过的场景。
核心主链路拆分,有以下几个目的- 异常容错:为主链路建立层次化的降级策略(多级降级),以及合理的熔断策略。
- 调配资源:主链路通常来讲都是高频场景,自然需要更多的计算资源,最主要的体现就是集群里分配的虚机数量多。
- 服务隔离:主链路是主打输出的C位,把主链路与其他辅助的业务服务隔离开来,避免边缘服务的异常情况影响到主链路。
2)领域模型拆分,领域驱动设计DDD(Domain-Driven Design 领域驱动设计)。即在某个领域上进行拆分。
3)用户群体拆分,例如打车软件有司机端和乘客端。
二、微服务的注册与发现(Eureka)
1、Spring Cloud Eureka的概念
Spring Cloud Eureka本身是一个基于 REST 的服务。提供注册与发现,同时还提供了负载均衡、故障转移等能力。
- Eureka的三个角色:
- Eureka Server:服务器端。它提供服务的注册和发现功能,即实现服务的治理。
- Service Provider:服务提供者。它将自身服务注册到Eureka Server中,以便“服务消费者”能够通过服务器端提供的服务清单(注册服务列表)来调用它。
- Service Consumer:服务消费者。它从 Eureka 获取“已注册的服务列表”,从而消费服务。
- Zookeeper会出现这样一种情况,当Master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader选举。问题在于,选举leader的时间太长,30~120s,且选举期间整个zk集群都是不可用的,这就导致在选举期间注册服务瘫痪。而Eureka优先保证可用性。
2、构建聚合父工程
-
创建父项目
-
配置
1)配置编码,即都改为UTF-8
2)注解生效激活 -
修改POM配置文件
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.zzx</groupId> <artifactId>cloud</artifactId> <version>1.0-SNAPSHOT</version> <!-- 统一管理jar包版本 --> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <spring-cloud.version>2022.0.2</spring-cloud.version> <spring-boot.version>3.0.5</spring-boot.version> </properties> <!-- 子模块继承之后,提供作用:锁定版本+子modlue不用写groupId和version --> <dependencyManagement> <dependencies> <!--spring boot 3.0.5--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!--spring cloud 2022.0.2--> <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> </project>
即使用最新版的spring cloud 和spring boot
可以查看最新的springcloud和springboot的版本:https://docs.spring.io/spring-cloud/docs/current/reference/html/
-
开启Run Dashboard面板
1)打开存放项目的文件夹,在.idea文件夹下找到workspace.xml文件
添加如下代码:<component name="RunDashboard"> <option name="ruleStates"> <list> <RuleState> <option name="name" value="ConfigurationTypeDashboardGroupingRule" /> </RuleState> <RuleState> <option name="name" value="StatusDashboardGroupingRule" /> </RuleState> </list> </option> <option name="configurationTypes"> <set> <option value="SpringBootApplicationConfigurationType" /> </set> </option> </component>
然后重启IDEA即可。
3、搭建Eureka服务
-
在父工程cloud上右键,新建一个Module,创建Eureka的服务端
-
修改子模块的POM文件
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>cloud</artifactId> <groupId>com.zzx</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-eureka-server7001</artifactId> <properties> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> </properties> <dependencies> <!-- 服务注册发现Eureka--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> </project>
-
在子模块中的src下,创建包com.zzx,创建主启动类EurekaServerMain7001
package com.zzx; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; /** * 主启动类 */ @EnableEurekaServer @SpringBootApplication @Slf4j public class EurekaServerMain7001 { public static void main(String[] args) { SpringApplication.run(EurekaServerMain7001.class,args); log.info("*****Eureka服务7001启动成功****"); } }
-
在resources目录下创建application.yml配置文件
server: port: 7001 eureka: instance: # eureka服务端实例名字 hostname: localhost client: # 表示是否将自己注册到eureka服务中 register-with-eureka: false # 表示是否从eureka中获取注册的服务信息 fetch-registry: false # 设置与 Eureka server交互的地址 查询服务和注册服务都需要依赖这个地址 service-url: DefaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
-
此时版本太高,需要使用JDK17或以上版本
1)JDK17官网下载地址:https://download.oracle.com/java/17/latest/jdk-17_windows-x64_bin.zip
2)在SDK中指定你安装的路径,不需要到bin目录,然后将版本全部选到jdk17
3)将两个POM文件都修改为JDK17版本<properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> </properties>
-
测试Eureka服务
1)在浏览器中打开:localhost:7001
4、搭建Eureka服务的提供者
-
在cloud父工程下,创建子模块cloud-provider-payment8001
-
在子模块cloud-provider-payment8001的POM文件中引入Eureka客户端等依赖
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>cloud</artifactId> <groupId>com.zzx</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-provider-payment8001</artifactId> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> </properties> <dependencies> <!-- 引入Eureka client依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> </project>
-
在子模块cloud-provider-payment8001的src下,创建包com.zzx包,在包下创建主启动类PaymentMain8001
package com.zzx; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * 主启动类 */ @SpringBootApplication @Slf4j public class PaymentMain8001 { public static void main(String[] args) { SpringApplication.run(PaymentMain8001.class,args); log.info("****** PaymentMain8001服务启动成功 *****"); } }
新版本不需要使用注解将自己注册到Eureka Server中。
-
在resources目录下,创建application.yml配置文件
server: port: 8001 eureka: client: service-url: # Eureka server的地址 defaultZone: http://localhost:7001/eureka/ spring: application: #设置应用名 name: cloud-payment-provider
-
测试是否成功注册到Eureka Server中
1)启动服务后,浏览器访问:localhost:7001
2)UI界面
此时有显示该实例,证明注册成功。
参数:
- Environment: 环境,默认为test,该参数在实际使用过程中,可以不用更改
- Data center: 数据中心,使用的是默认的是 “MyOwn”
- Current time:当前的系统时间
- Uptime:已经运行了多少时间
- Lease expiration enabled:是否启用租约过期 ,自我保护机制关闭时,该值默认是true, 自我保护机制开启之后为false。
- Renews threshold: 每分钟最少续约数,Eureka Server 期望每分钟收到客户端实例续约的总数。
- Renews (last min): 最后一分钟的续约数量(不含当前,1分钟更新一次),Eureka Server 最后 1 分钟收到客户端实例续约的总数。
5、创建Eureka服务的消费者
-
在父工程cloud中右键创建子模块服务的消费者cloud-consumer-order80
-
在POM文件中引入依赖
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>cloud</artifactId> <groupId>com.zzx</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-consumer-order80</artifactId> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> </properties> <dependencies> <!-- 引入Eureka client依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> </project>
-
在子模块cloud-consumer-order80的src下,创建包com.zzx包,在包下创建主启动类OrderMain80
package com.zzx; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * 主启动类 */ @SpringBootApplication @Slf4j public class OrderMain80 { public static void main(String[] args) { SpringApplication.run(OrderMain80.class,args); log.info("***** OrderMain80服务启动成功 *****"); } }
-
在resources目录下,创建application.yml配置文件
server: port: 80 eureka: client: service-url: # Eureka server的地址 defaultZone: http://localhost:7001/eureka/ spring: application: #设置应用名 name: cloud-order-consumer
-
测试是否成功注册到Eureka Server中
1)启动服务后,浏览器访问:localhost:7001
三、Eureka的其他功能
1、服务的剔除和自保
-
服务的剔除
当服务提供者出现问题时,将服务提供者的信息从Eureka服务中剔除掉。
通过application.yml配置文件设置,添加如下eureka: server: enable-self-preservation: false
-
服务的自保(默认)
当服务提供者出现问题时,在Eureka服务中保留服务提供者的信息。
通过application.yml配置文件设置,添加如下eureka: server: enable-self-preservation: true
-
并且这两个模式只能二选一
2、自定义服务在Eureka上的注册名
-
在服务提供者和消费者的POM文件中引入Actuator依赖
<!-- actuator监控信息完善 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
-
在服务提供者的application.yml文件中添加如下
eureka: instance: # 注册的实例名 instance-id: cloud-provider-payment8001
-
在服务消费者的application.yml文件中添加如下
eureka: instance: # 注册的实例名 instance-id: cloud-consumer-order80
-
重启服务提供者和消费者后,效果如图
3、服务发现Discovery
-
在子模块cloud-consumer-order80的com.zzx包下创建一个controller包,创建类OrderController
package com.zzx.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; @RestController @RequestMapping("order") public class OrderController { //服务发现 @Autowired private DiscoveryClient discoveryClient; /** * 获取服务的列表清单 */ @GetMapping("discovery") public Object testDiscoveryClient(){ List<String> services = discoveryClient.getServices(); for (String service : services) { System.out.println(service); } return this.discoveryClient; } }
即获取Eureka服务中已经注册过的服务提供者列表清单。
-
启动全部子模块,然后在浏览器访问:
http://localhost/order/discovery
-
在子模块cloud-consumer-order80的com.zzx包下创建一个config包,创建类RestTemplateConfig
package com.zzx.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; /** * 将RestTemplate放入到Spring的IOC容器中 */ @Configuration public class RestTemplateConfig { @Bean public RestTemplate restTemplate(){ return new RestTemplate(); } }
即将创建RestTemplate对象的任务交给SpringIOC容器进行管理。
-
在子模块cloud-provider-payment8001的com.zzx包下创建一个controller包,创建类PaymentController
package com.zzx.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("payment") public class PaymentController { @RequestMapping("index") public String index(){ return "payment success"; } }
用于让服务消费者调用时做出响应。
-
在子模块cloud-consumer-order80的OrderController类中添加如下代码
package com.zzx.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import java.util.List; @RestController @RequestMapping("order") public class OrderController { //服务发现 @Autowired private DiscoveryClient discoveryClient; @Autowired private RestTemplate restTemplate; /** * 获取服务的列表清单 */ @GetMapping("discovery") public Object testDiscoveryClient() { List<String> services = discoveryClient.getServices(); for (String service : services) { System.out.println(service); } return this.discoveryClient; } @GetMapping("index") public String index() { //服务生产者名字 String hostname = "CLOUD-PAYMENT-PROVIDER"; //远程调用方法具体URL String url = "/payment/index"; //服务发现中获取服务生产者实例 List<ServiceInstance> instances = discoveryClient.getInstances(hostname); //获取具体实例 服务生产者实例 ServiceInstance serviceInstance = instances.get(0); //发起远程调用 //getForObject:返回响应体中数据转化成的对象,可以理解为json //getForEntity:返回的是ResponseEntity的对象包含了一些重要的信息 String forObject = restTemplate.getForObject(serviceInstance.getUri() + url, String.class); return forObject; } }
即将RestTemplate自动装配;通过DiscoveryClient获取指定的服务提供者实例,取出第一个实例对象;通过RestTemplate的getForObject方法去请求服务提供者的指定url,并指定返回值的类型。
4、Eureka的集群搭建
-
在父工程cloud上右键,新建一个Module,创建第二个Eureka的服务端
-
修改POM文件
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>cloud</artifactId> <groupId>com.zzx</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-eureka-server7002</artifactId> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> </properties> <dependencies> <!-- 服务注册发现Eureka--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> </project>
-
配置application.yml文件
1)配置cloud-eureka-server7002的application.yml文件server: port: 7002 eureka: server: enable-self-preservation: false instance: # eureka服务端实例名字 hostname: eureka7002.com client: # 表示是否将自己注册到eureka服务中 register-with-eureka: false # 表示是否从eureka中获取注册的服务信息 fetch-registry: false # 设置与 Eureka server交互的地址 查询服务和注册服务都需要依赖这个地址 service-url: defaultZone: http://localhost:7001/eureka/
此时7002的Eureka是指向7001端口的Eureka服务的,待会再将7001指向7002,即可相互获取配置。
2)修改cloud-eureka-server7001的application.yml文件server: port: 7001 eureka: server: enable-self-preservation: false instance: # eureka服务端实例名字 hostname: eureka7001.com client: # 表示是否将自己注册到eureka服务中 register-with-eureka: false # 表示是否从eureka中获取注册的服务信息 fetch-registry: false # 设置与 Eureka server交互的地址 查询服务和注册服务都需要依赖这个地址 service-url: defaultZone: http://localhost:7002/eureka/
3)修改cloud-provider-payment8001的application.yml文件
server: port: 8001 eureka: instance: # 注册名 instance-id: cloud-provider-payment8001 client: service-url: # Eureka server的地址 #单机 #defaultZone: http://localhost:7001/eureka/ #集群 defaultZone: http://localhost:7001/eureka,http://localhost:7002/eureka spring: application: #设置应用名 name: cloud-payment-provider
4)修改cloud-consumer-order80的application.yml文件
server: port: 80 eureka: instance: # 注册名 instance-id: cloud-consumer-order80 client: service-url: # Eureka server的地址 #单机 #defaultZone: http://localhost:7001/eureka/ #集群 defaultZone: http://localhost:7001/eureka,http://localhost:7002/eureka spring: application: #设置应用名 name: cloud-order-consumer
即后面这两个的配置就是将eureka的ip地址改成集群的配置。
-
在子模块cloud-eureka-server7002中的src下,创建包com.zzx,创建主启动类EurekaServerMain7002
package com.zzx; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; /** * 主启动类 */ @EnableEurekaServer @SpringBootApplication @Slf4j public class EurekaServerMain7002 { public static void main(String[] args) { SpringApplication.run(EurekaServerMain7002.class,args); log.info("*****Eureka服务7002启动成功****"); } }
总结:
- 1)SpringCloud版本,GA是正式发布的版本,选择版本时尽量选择使用最新的GA板。
服务治理解决分布式服务调用的问题。
2)Eureka是一个注册发现组件。
zookeeper重新选举leader的时间太长,Eureka则优先保证可用性。
Eureka有三个角色Eureka Server,Service Provider和Service Consumer。
Eureka Server用来提供注册和发现服务;Service Provider通过在Eureka Server进行服务的注册;Service Consumer通过在Eureka Server进行订阅,获取服务列表。 - 1)构建聚合父工程的流程,创建一个maven项目,然后设置编码为UTF-8,设置注解生效激活;以及修改POM文件,在属性中统一管理jar包版本以及JDK的版本。最后,通过修改该项目文件夹下的.idea文件夹的workspace.xml文件,开启Run Dashboard面板。
2)Eureka服务端子模块的流程,需要导入SpringCloud整合的Eureka Server包以及其他包;创建application.yml文件并配置;最后设置主启动类即可(并且在该类中需要添加开启Eureka服务的注解@EnableEurekaServer)。
3)Spring6和SpringBoot3.0版本以后需要使用JDK17及以上的版本。
4)Eureka提供者子模块的流程,需要导入SpringCloud整合的Eureka Client包以及其他包;创建application.yml文件并配置;最后设置主启动类即可。
新版本不需要使用注解将自身注册到服务端,并且也没有这个注解。
5)Eureka服务的消费者,引入的依赖等操作与提供者流程一样,只是性质不同。 - 1)服务的自保和剔除,可以通过配置文件设置;
服务的剔除,当服务提供者出现问题时,将服务提供者的信息从Eureka服务中剔除掉;
服务的自保,当服务提供者出现问题时,在Eureka服务中保留服务提供者的信息。该模式主要用于应对网络环境问题,例如,网络抖动。
这两个模式是当服务提供者不可用的时候才生效;就算剔除,当服务提供者恢复后,可以自动连接到该Eureka服务。
2)服务提供者或消费者想服务注册时,如果需要更改注册的实例名,则需要在服务提供者或消费者的POM文件中引入Actuator依赖,在服务提供者或消费者的application.yml文件中添加配置自定义的注册实例名。
3)Eureka集群的搭建,首先需要配置多个Eureka服务,此时是模拟,所以使用本地的多个不同端口号来搭建Eureka服务,并且yml文件中的每个Eureka服务都需要指向其他的Eureka服务的ip地址。然后服务提供者以及消费者的yml文件需要改为Eureka集群的形式,即将所有Eureka服务的ip地址都写上。