视频来源:Spring-cloud 1 1-27
微服务架构是一种架构模式,它提倡将单一应用程序划分成一组小的服务,服务之间相互协调、互相配合,为用户提供最终价值。每个服务运行在其独立的进程中,服务与服务之间采用轻量级的通行机制相互协作(通常是基于HTTP协议的RestFul API)。每个服务都围绕着具体本业务进行构建,并且能够被独立部署到生成环境、类生产环境等。另外,应当尽量避免统一的、集中式的服务管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言、工具对其进行构建。
拆分来理解就是:
主题词01:现代数字化生活-落地维度
主题词02:分布式微服务架构-落地维度
Spring Cloud是什么? 什么是微服务架构
springcloud官方文档(Hoxton SR5):https://cloud.spring.io/spring-cloud-static/Hoxton.SR5/reference/htmlsingle/
springcloud中文文档:https://www.springcloud.cc/
springcloud中国社区文档:http://docs.springcloud.cn/
https://www.bookstack.cn/read/spring-cloud-docs/docs-index.md
SpringCloud=分布式微服务架构的一站式解决方案,是多种微服务架构落地的技术框架的集合体,俗称微服务全家桶
SpringCloud 包含的技术组件?
Spring Boot 源码地址
Spring Cloud 源码地址
Spring Boot 与 Spring Cloud 兼容性查看
接下来开发用到的组件版本
停更引发的升级惨案
Cloud升级
服务注册中心
服务调用
服务降级
服务网关
服务配置
服务总线
约定 -> 配置 -> 编码
创建微服务cloud整体聚合父工程Project ,有8个关键步骤:
1.New Project-maven工程
4.工程名称
5.字符编码 - Setting -File encoding(选择UTF-8)
6.注解生效激活 -Setting-Annotation Processors
7.Java编译版本选8
8.File Type过滤 -Setting -File Type
UTF-8
1.8
1.8
4.12
1.18.10
1.2.17
8.0.18
1.1.20
1.3.2
org.springframework.boot
spring-boot-dependencies
2.2.2.RELEASE
pom
import
org.springframework.cloud
spring-cloud-dependencies
Hoxton.SR1
pom
import
com.alibaba.cloud
spring-cloud-alibaba-dependencies
2.1.0.RELEASE
pom
import
mysql
mysql-connector-java
${mysql.version}
runtime
com.alibaba
druid-spring-boot-starter
${druid.version}
org.mybatis.spring.boot
mybatis-spring-boot-starter
${mybatis.spring.boot.version}
junit
junit
${junit.version}
log4j
log4j
${log4j.version}
org.springframework.boot
spring-boot-maven-plugin
true
true
Maven使用dependencyManagement元素来提供了一种管理依赖版本号的方式。
通常会在一个项目的最顶层的父POM中看到DependencyManager元素
使用pom.xml中的dependencyManagement元素能让所有的子项目中引用个依赖而不用显示的列出版本量。
Maven会沿着父子层次向上走,直到找到一个拥有dependencyManagement元素的项目,然后它就会使用这个dependencyManagement元素中的指定的版本号。
然后子元素可以添加父引入的依赖可以不携带版本号方便统一管理。
IDEA右侧有maven插件有Toggle Skip Test’s Mode按钮 ,这样maven可以跳过单元测试
父工程创建完成执行 mvn:install 将会把父工程发布到仓库方面子工程继承。
模块构建:
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-actuator
org.mybatis.spring.boot
mybatis-spring-boot-starter
com.alibaba
druid-spring-boot-starter
1.1.20
mysql
mysql-connector-java
org.springframework.boot
spring-boot-starter-jdbc
org.springframework.boot
spring-boot-devtools
runtime
true
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
#微服务建议一定要写服务端口号和微服务名称
server:
#端口号
port: 8001
spring:
application:
#微服务名称
name: cloud-payment-service
#数据库配置
datasource:
type: com.alibaba.druid.pool.DruidDataSource
#mysql5.x的没有cj
driver-class-name: com.mysql.cj.jdbc.Driver
#记得先创建数据库
url: jdbc:mysql://localhost:3306/db2020?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: 200088lx
#mybatis配置
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.zero.entity #所有Entity别名类所在包
在java包下创建主启动类com.angenin.springcloud.PaymentMain8001
@SpringBootApplication
public class PaymentMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8001.class, args);
}
}
CREATE TABLE `payment`(
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
`serial` VARCHAR(200) DEFAULT '',
PRIMARY KEY(`id`)
)ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
INSERT INTO payment(`serial`)VALUES("张三");
2.entity
在对应自己创建的entity包下面创建出对应实体
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Payment implements Serializable {
private Long id;
private String serial;
}
在entity包下新建CommonResult(json封装体 ,传给前端的)
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T>{
private Integer code;
private String message;
private T data;
public CommonResult(Integer code, String message){
this(code, message, null);
}
}
3.dao
在对应包下新建Dao.PaymentDao接口
@Mapper
//@Repository不用Spring的
public interface PaymentDao
{
public int create(Payment payment);
public Payment getPaymentById(@Param("id") Long id);
}
4.mapper
在resource目录下新建mapper目录,然后新建PaymentMapper.xml
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.zero.mapper.PaymentDao">
<insert id="create" parameterType="Payment" useGeneratedKeys="true" keyProperty="id">
insert into payment(serial) values(#{serial});
insert>
<resultMap id="BaseResultMap" type="com.zero.entity.Payment">
<id column="id" property="id" jdbcType="BIGINT"/>
<id column="serial" property="serial" jdbcType="VARCHAR"/>
resultMap>
<select id="getPaymentById" parameterType="Long" resultMap="BaseResultMap">
select * from payment where id=#{id};
select>
mapper>
5.service
在springcloud包下新建service.PaymentService接口
public interface PaymentService
{
public int create(Payment payment);
public Payment getPaymentById(@Param("id") Long id);
}
在service包下新建impl.PaymentServiceImpl实现下
@Service
public class PaymentServiceImpl implements PaymentService
{
@Resource
private PaymentDao paymentDao;
@Override
public int create(Payment payment)
{
return paymentDao.create(payment);
}
@Override
public Payment getPaymentById(Long id)
{
return paymentDao.getPaymentById(id);
}
}
在springcloud包下新建controller.PaymentController
@RestController
@Slf4j
public class PaymentController{
@Resource
private PaymentService paymentService;
@PostMapping(value = "/payment/create")
public CommonResult create(Payment payment)
{
int result = paymentService.create(payment);
log.info("*****插入结果:"+result);
if(result > 0)
{
return new CommonResult(200,"插入数据库成功",result);
}else{
return new CommonResult(444,"插入数据库失败",null);
}
}
@GetMapping(value = "/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id)
{
Payment payment = paymentService.getPaymentById(id);
if(payment != null)
{
return new CommonResult(200,"查询成功",payment);
}else{
return new CommonResult(444,"没有对应记录,查询ID: "+id,id);
}
}
}
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
2.添加一个插件到父类工程的pom.xml里
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<configuration>
<fork>truefork>
<addResources>trueaddResources>
configuration>
plugin>
plugins>
build>
File -> Settings(New Project Settings->Settings for New Projects) ->Complier
组合键 Shift+Ctrl+Alt+/ ,选中Registry
cloud-consumer-order81
往pom中添加:
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
project>
在resouce目录下新建application.yml
#访问一个网站时,默认是80端口,给用户80端口,用户就可以不用加端口直接访问页面
server:
port: 81
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @Auther: lixiang
* @Date: 2022/03/30/22:57
* @Description:
*/
@SpringBootApplication
public class OrderMain81
{
public static void main( String[] args ){
SpringApplication.run(OrderMain81.class, args);
}
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Payment implements Serializable {
private Long id;
private String serial;
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T>{
private Integer code;
private String message;
private T data;
public CommonResult(Integer code, String message){
this(code, message, null);
}
}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class ApplicationContextConfig {
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
import com.lun.springcloud.entities.CommonResult;
import com.lun.springcloud.entities.Payment;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
@Slf4j
@RestController
public class OrderController {
public static final String PAYMENT_URL = "http://localhost:8001";
@Resource
private RestTemplate restTemplate;
@GetMapping("/consumer/payment/create")
public CommonResult<Payment> create(Payment payment){
return restTemplate.postForObject(PAYMENT_URL+"/payment/create", payment, CommonResult.class);
}
@GetMapping("/consumer/payment/get/{id}")
public CommonResult<Payment> getPayment(@PathVariable("id") Long id){
return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id, CommonResult.class);
}
}
运行cloud-consumer-order80与cloud-provider-payment8001两工程
浏览器 - http://localhost:81/consumer/payment/get/1
RestTemplate提供了多种便捷访问远程Http服务的方法,是一种简单便捷的访问restful服务模板类,是Spring提供的用于访问Rest服务的客户端模板工具集
官网地址
使用:
使用restTemplate访问restful接口非常的简单粗暴无脑。
(url, requestMap, ResponseBean.class)这三个参数分别代表。
REST请求地址、请求参数、HTTP响应转换被转换成的对象类型。
浏览器 - http://localhost:81/consumer/payment/create?serial=lun3
虽然,返回成功,但是观测数据库中,并没有创建serial为lun3的行。
解决方法:在cloud-provider-payment8001工程的PaymentController中添加@RequestBody注解。
public class PaymentController
{
@PostMapping(value = "/payment/create")
public CommonResult create(@RequestBody/*添加到这里*/ Payment payment){
...
}
}
打开工程路径下的.idea文件夹的workspace.xml
在中修改或添加以下代码:
<option name="configurationTypes">
<set>
<option value="SpringBootApplicationConfigurationType"/>
set>
option>
观察cloud-consumer-order81与cloud-provider-payment8001两工程有重复代码(entity)(公共代码),重构。
cloud-api-commons
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>cn.hutoolgroupId>
<artifactId>hutool-allartifactId>
<version>5.1.0version>
dependency>
dependencies>
将cloud-consumer-order81与cloud-provider-payment8001两工程的公有entities包移至cloud-api-commons工程下。
maven clean、install
cloud-api-commons工程,以供给cloud-consumer-order81与cloud-provider-payment8001两工程调用。
<dependency>
<groupId>com.zerogroupId>
<artifactId>cloud-api-commonsartifactId>
<version>${project.version}version>
dependency>
什么是服务治理
Spring Cloud封装了Netflix公司开发的Eureka模块来实现服务治理
在传统的rpc远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务治理,管理服务于服务之间依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册。
什么是服务注册与发现
Eureka采用了CS的设计架构,Eureka Server作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用Eureka的客户端连接到Eureka Server并维持心跳连接。这样系统的维护人员就可以通过Eureka Server来监控系统中各个微服务是否正常运行。
在服务注册与发现中,有一个注册中心。当服务器启动的时候,会把当前自己服务器的信息 比如服务地址通讯地址等以别名方式注册到注册中心上。另一方(消费者|服务提供者),以别名的方式去注册中心上获取到实际的通讯地址,然后再实现本地RPC调用RPC远程调用框架核心设计思想:在于注册中心管理每个服务与服务之间的一个依赖关系(服务治理概念)。在任何rpc远程框架中,都会有一个注册中心(存放服务地址相关信息(接口地址))
Eureka包含两个组件:Eureka Server和Eureka Client
Eureka Server提供服务注册服务
各个微服务节点通过配置启动后,会在EurekaServer中进行注册,这样EurekaServer中的服务注册表中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。
EurekaClient通过注册中心进行访问
是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除(默认90秒)
cloud-eureka-server7001的Maven工程
server端依赖对比:
dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>
<dependency>
<groupId>com.zerogroupId>
<artifactId>cloud-api-commonsartifactId>
<version>${project.version}version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
dependency>
dependencies>
在resource目录下新建application.yml文件
server:
port: 7001
eureka:
instance:
hostname: locathost #eureka服务端的实例名称
client:
#false表示不向注册中心注册自己。
register-with-eureka: false
#false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
fetch-registry: false
service-url:
#设置与Eureka server交互的地址查询服务和注册服务都需要依赖这个地址。
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class EurekaMain7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaMain7001.class, args);
}
}
测试运行EurekaMain7001,浏览器输入http://localhost:7001/回车,会查看到Spring Eureka服务主页。
添加spring-cloud-starter-netflix-eureka-client依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
eureka:
client:
#表示是否将自己注册进Eurekaserver默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
defaultZone: http://localhost:7001/eureka
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient//<-----添加该注解
public class PaymentMain001 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain001.class, args);
}
}
EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT. RENEWALS ARELESSER THAN THRESHOLD AND HENCFT ARE NOT BEING EXPIRED JUST TO BE SAFE.
紧急情况!EUREKA可能错误地声称实例在没有启动的情况下启动了。续订小于阈值,因此实例不会为了安全而过期。
EurekaClient端cloud-consumer-order81将注册进EurekaServer成为服务消费者consumer,类似来上课消费的同学
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
server:
port: 81
spring:
application:
name: cloud-order-service
eureka:
client:
#表示是否将自己注册进Eurekaserver默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
defaultZone: http://localhost:7001/eureka
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient//<--- 添加该标签
public class OrderMain81
{
public static void main( String[] args ){
SpringApplication.run(OrderMain80.class, args);
}
}
搭建Eureka注册集群,实现负载均衡+故障容错。
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
127.0.0.1 eureka7003.com
eureka:
instance:
hostname: eureka7001.com #eureka服务端的实例名称
client:
register-with-eureka: false
fetch-registry: false
service-url:
# 单机
# defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
#集群版 相互注册,相互守望
defaultZone: http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/
# defaultZone是固定写法,如果想自定义,需要按以下写法才行:
# region: eureka-server
# availability-zones:
# eureka-server: server1,server2
# service-url:
# server1: http://eureka7002.com:7002/eureka/
# server2: http://eureka7003.com:7003/eureka/
eureka:
instance:
hostname: eureka7002.com #eureka服务端的实例名称
client:
register-with-eureka: false
fetch-registry: false
service-url:
#集群版 相互注册,相互守望
defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7003.com:7003/eureka/ #相互注册,相互守望
eureka:
instance:
hostname: eureka7003.com #eureka服务端的实例名称
client:
register-with-eureka: false
fetch-registry: false
service-url:
#集群版 相互注册,相互守望
defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/ #相互注册,相互守望
把两个项目的yml文件中的defaultZone改为:
#集群版
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka
启动5个项目进行测试:(先启动集群,再启动8001,最后启动81)
@RestController
@Slf4j
public class PaymentController{
@Resource
private PaymentService paymentService;
@Value("${server.port}")
private String serverPort;
@PostMapping(value = "/payment/create")
public CommonResult create(@RequestBody Payment payment)
{
int result = paymentService.create(payment);
log.info("*****插入结果:"+result);
if(result > 0)
{
return new CommonResult(200,"插入数据库成功 serverPort:" + serverPort,result);
}else{
return new CommonResult(444,"插入数据库失败",null);
}
}
@GetMapping(value = "/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id)
{
Payment payment = paymentService.getPaymentById(id);
if(payment != null)
{
return new CommonResult(200,"查询成功 serverPort:" + serverPort,payment);
}else{
return new CommonResult(444,"没有对应记录,查询ID: "+id);
}
}
}
public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
先启动EurekaServer , 7001/70025服务
在启动服务提供者Provider , 8001/8002服务
浏览器输入 - http://localhost:81/consumer/payment/get/1
结果:负载均衡效果达到,8001/8002端口交替出现。
主机名称:服务名称修改(也就是将IP地址,换成可读性比较高的名字)
访问信息有IP信息提示(鼠标移至服务名称下面 , 会显示对应的IP地址显示)
修改cloud-provider-payment8001,cloud-provider-payment8002,consumer81 名称
#######8001#######
# client:
# ... instance要和client对齐
instance:
instance-id: payment8001
prefer-ip-address: true #访问路径可以显示ip地址
#######8002#######
instance:
instance-id: payment8002 #修改显示的主机名
prefer-ip-address: true #访问路径可以显示ip地址
#######80#######
instance:
instance-id: consumer80 #修改显示的主机名
prefer-ip-address: true #访问路径可以显示ip地址
对于注册进eureka里面的微服务,可以通过服务发现来获得该服务的信息。
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
public class PaymentMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8001.class, args);
}
}
@Resource
private DiscoveryClient discoveryClient;
@GetMapping("/payment/discovery")
public Object discovery(){
//获取服务列表的信息
List<String> services = discoveryClient.getServices();
for (String element : services) {
log.info("*******element:" + element);
}
//获取CLOUD-PAYMENT-SERVICE服务的所有具体实例
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
for (ServiceInstance instance : instances) {
//getServiceId服务器id getHost主机名称 getPort端口号 getUri地址
log.info(instance.getServiceId() + "\t" + instance.getHost() + "\t" + instance.getPort() + "\t" + instance.getUri());
}
return this.discoveryClient;
}
浏览器输出:
{"services":["cloud-payment-service","cloud-order-service"],"order":0}
后台输出:
2022-04-03 22:18:50.754 INFO 2820 --- [nio-8001-exec-1] com.zero.controller.PaymentController : *******element:cloud-payment-service
2022-04-03 22:18:50.754 INFO 2820 --- [nio-8001-exec-1] com.zero.controller.PaymentController : *******element:cloud-order-service
2022-04-03 22:18:50.758 INFO 2820 --- [nio-8001-exec-1] com.zero.controller.PaymentController : CLOUD-PAYMENT-SERVICE 192.168.56.1 8002 http://192.168.56.1:8002
保护模式主要用于一组客户端和Eureka Server之间存在网络分区场景下的保护。一旦进入保护模式,EurekaServer将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据,也就是不会注册任何微服务。
如果在Eureka Server的首页看到以下这段提示,则说明Eureka进入了保护模式:
某时刻某一个微服务不可用了,Eureka不会立即清除,依旧会对该服务的信息进行保存。
属于CAP里面的AP分支。
为了EurekaClient端可以正常运行,防止了网络通信不良的情况下,server端不会立即把Client端给剔除掉。
默认情况下如果在 Server在一定时间内没有介绍到某个Client端没有接受到某个微服务实例的心跳,server端将会注销该实例(默认90秒)。但是当网络分区故障发生 ,client与server之间正常无法通行,本身client端是很健康的本不应该立马去注销这个微服务,Eureka通过自我保护模式来解决这个问题。
自我保护机制:默认情况下client定时向server端发送心跳包(默认间隔90秒),便会直接从服务注册列表中剔除该服务,但是在短时间内丢失了大量的服务实例心跳,这时候server会开启自我保护机制,不会剔除该服务。
在EurekaServer端7001处设置关闭自我保护机制
默认配置是开启的
# client
# ... server与client对齐
server:
#关闭自我保护,默认为true
enable-self-preservation: false
#心跳的间隔时间,单位毫秒
eviction-interval-timer-in-ms: 2000
关闭效果:
THE SELF PRESERVATION MODE IS TURNED OFF. THIS MAY NOT PROTECT INSTANCE EXPIRY IN CASE OF NETWORK/OTHER PROBLEMS.
在EurekaClient端8001
cloud-provider-payment8001的yml文件:
instance:
instance-id: payment8001
prefer-ip-address: true #访问路径可以显示ip地址
#Eureka客户端向服务端发送心跳的时间间隔,单位秒(默认30秒)
lease-renewal-interval-in-seconds: 1
#Eureka服务端在收到最后一次心跳后等待的时间上限,单位秒(默认90秒),超时剔除服务
lease-expiration-duration-in-seconds: 2
测试:
* 7001和8001都配置完成
* 先启动7001再启动8001
结果:先关闭8001,马上被删除了
Eureka 2.0 (Discontinued)
The existing open source work on eureka 2.0 is discontinued. The code base and artifacts that were released as part of the existing repository of work on the 2.x branch is considered use at your own risk.
Eureka 1.x is a core part of Netflix’s service discovery system and is still an active project.