什么是微服务?
微服务架构是一种架构模式,它提倡将单一应用程序划分成一组小的服务,服务之间相互协调、相互配合,为用户提供最终价值。每个服务运行在其独立的进程中,服务与服务间采用轻量级的通信机制互相协作(通常基于HTTP协议的RESTful API)。每个服务都围绕着具本业务进行构建,并且能够被独立的部署到生产环境、类生产环境等。另外,应当尽量避免统一的、集中式的服务管理机制,对一个服务而言,应根据业务上下文,选择合适的语言、工具对其进行构建。
主题词01:95后数字化生活-落地维度
主题词02:分布式微服务架构-落地维度
是什么
SpringCloud = 分布式为服务架构一站式解决方案,是多种微服务架构落地技术的集合体,俗称微服务全家桶
京东
阿里
符合微服务技术维度
上篇:springboot2.x和springcloud H
版本对应
导入依赖
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<maven.compiler.source>1.8maven.compiler.source>
<maven.compiler.target>1.8maven.compiler.target>
<junit.version>4.12junit.version>
<log4j.version>1.2.17log4j.version>
<lombok.version>1.18.12lombok.version>
<mysql.version>5.1.47mysql.version>
<druid.version>1.1.16druid.version>
<mybatis.spring.boot.version>1.3.0mybatis.spring.boot.version>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-dependenciesartifactId>
<version>2.2.2.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>Hoxton.SR1version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>2.1.0.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>${mysql.version}version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>${druid.version}version>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>${mybatis.spring.boot.version}version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>${junit.version}version>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>${log4j.version}version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>${lombok.version}version>
<optional>trueoptional>
dependency>
dependencies>
dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<configuration>
<fork>truefork>
<addResources>trueaddResources>
configuration>
plugin>
plugins>
build>
Maven使用dependencyManagement元素来提供了一种管理依赖版本号的方式。
通常会在一个组织或者项目的最顶层的父POM中看到dependencyManagement元素。
使用pom.xml中的dependencyManagement元素能让所有在子项目中引用一个依赖而不用显示的列出版本号。
Maven会沿着父子层次向上走,直到找到一个拥有dependencyManagement元素项目,然后它就会使用这个dependencyManagement元素中指定的版本号。
所以啊,你在使用dependencyManagement包住dependencies之后,就会发现,我们的依赖里面根本就没有导入这些依赖,我觉得吧这些就是起到限定作用,导入还是需要你在子项目中导入才行。
这样做的好处:
- 如果有多个子项目都引用同一个依赖,则可以避免在每个使用的子项目都声明一个版本号,这样当想要升级或切换到另一个版本时,只需要在顶层父容器里更新,而不需要一个一个子项目修改;另外如果某个子项目需要另外一个版本,只需要声明version就可。
父工程创建完成执行mvn:install将父工程发布到仓库方便子工程继承
建module
- 额。。。。。。
改POM
<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>cloud2020artifactId> <groupId>com.lingroupId> <version>1.0-SNAPSHOTversion> parent> <modelVersion>4.0.0modelVersion> <artifactId>cloud-provider-payment8001artifactId> <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.mybatis.spring.bootgroupId> <artifactId>mybatis-spring-boot-starterartifactId> dependency> <dependency> <groupId>com.alibabagroupId> <artifactId>druid-spring-boot-starterartifactId> <version>1.1.10version> dependency> <dependency> <groupId>mysqlgroupId> <artifactId>mysql-connector-javaartifactId> dependency> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-jdbcartifactId> 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>
写yml
server: port: 8001 spring: application: name: cloud-payment-service datasource: type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型 driver-class-name: org.gjt.mm.mysql.Driver # mysql驱动包 com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/db2019?useUnicode=true&characterEncoding=utf-8&useSSL=false username: root password: 429208 mybatis: mapperLocations: classpath:mapper/*.xml type-aliases-package: com.atguigu.springcloud.entities # 所有Entity别名类所在包
主启动类
package com.atguigu.springcloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * @auther zzyy * @create 2020-01-27 19:50 */ @SpringBootApplication public class PaymentMain8001 { public static void main(String[] args) { SpringApplication.run(PaymentMain8001.class,args); } }
业务类
这两个依赖要一起出现
vue-controller-service-dao-mysql
我们从后面向前推进
举个例子,一个Student类,其成绩属性,我们设置为
private Integer socre;
而不设置为
private int socre;
这是因为有的学生会出现考0分,而有的学生缺考,这样,就没有办法去区分了。
包装类型 Integer 和 基本类型 int 的最大区别就是包装类可以设置为 null。
缺考设置为null,就ok了。再举例 在我们使用mybatis的时候,mybatis会判断参数属性是否为空null,而我们实体类中的某个属性如果使用基本数据类型,如int,默认为0,那mybatis就无法对这个参数进行判断。
上面用法
应用背景
在开发过程中,可能一次对多个表进行操作。比如现在有两个表:用户表和订单表,主键id都是自增的。应用场景:一个新用户添加了一个新的订单。
操作过程:
在用户表中插入一条新纪录,
获取这个用户的id,
再在订单表中插入订单的相关信息(比如商品名称、价格……,以及用户的id)。
分成三步,就太浪费与数据库链接的资源了。为什么不直接在插入用户记录后,将新用户的主键与插入的结果一起返回呢?这个时候,在mapper.xml中加入参数 useGeneratedKeys=“true” keyProperty=“id”,就比较方便了!
原理
Mybatis 配置文件 useGeneratedKeys 参数只针对 insert 语句生效,默认为 false。当设置为 true 时,表示如果插入的表以自增列为主键,则允许 JDBC 支持自动生成主键,并可将自动生成的主键返回。
————————————————
读完上面的就懂了
<resultMap id="BaseResultMap" type="com.lin.entities.Payment">
<id column="id" property="id" jdbcType="BIGINT"/>
<result column="serial" property="serial" jdbcType="VARCHAR"/>
resultMap>
<insert id="create" parameterType="Payment" useGeneratedKeys="true" keyProperty="id">
INSERT INTO payment(`serial`) VALUES(#{serial});
insert>
<select id="getPaymentById" parameterType="Long" resultMap="BaseResultMap" >
SELECT * FROM payment WHERE id=#{id};
select>
resultMap能够帮助我们将mysql查询结果返回元素映射到我们一个对象上面,然后完成属性一 一映射
useGeneratedKeys=“true” keyProperty="id"这个上面介绍了,就不讲了
2022-01-17 22:10:25.335 INFO 34924 — [nio-8001-exec-7] com.lin.controller.PaymentController : *****插入操作返回结果:1
返回这个结果有点打脸,但是瞬间我有悟了。因为我这个不是项目里面自增后的结果,所以不会返回?不知道,后面再说吧
if (result > 0) {
return new CommonResult(200, "插入数据库成功", result);
} else {
return new CommonResult(444, "插入数据库失败", null);
}
post请求,我发现直接问好就能拼上去了,观察现象,以后透过本质。
别悟了,两年前的东西,现在变了
还是乖乖这样吧,xdm
要项目多的时候才能出现,现在还是一个项目看不到效果
文章出处
上面这个方法已经对我的idea没有用了
这篇文章可以
RestTemplate提供了多种便捷访问远程Http服务的方法,
是一种简单便捷的访问restful服务模板类,是Spring提供的用于访问Rest服务的客户端模板工具集
官网地址
https://docs.spring.io/spring-framework/docs/5.2.2.RELEASE/javadoc-api/org/springframework/web/client/RestTemplate.html
使用
使用restTemplate访问restful接口非常的简单粗暴无脑。
(url, requestMap, ResponseBean.class)这三个参数分别代表
REST请求地址、请求参数、HTTP响应转换被转换成的对象类型。
参数解释:
- url:就是发出请求的url
- Object request:请求体,接收这部分的是@RequestBody
- Class> responseType:响应体,返回的参数对象
- uriVariables:这个应该就是我们使用restful风格拼接字符串是还要把拼接参数一并传过去
将两个类的entities的相同类抽取出来
gitee开源代码
简介:小而全的Java工具类库,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。
使用方法
这两个东西的作用:
這就是一个pom对应的maven操作命令,
那这些命令中,最常用的打包项目的命令是什么?
两种最常用打包方法:
1.先 clean,然后 package
2.先 clean,然后install
下面详细介绍它们:
clean:翻译:打扫清理,最直接的就是作用于橙色的target目录。在进行真正的构建之前进行一些清理工作,移除所有上一次构建生成的文件。执行该命令会删除项目路径下的target文件,但是不会删除本地的maven仓库已经生成的jar文件。
validate:翻译:验证。验证工程正确性,所需信息完整否。
compile:翻译:编译。大伙都知道java的识别文件是.class,编译生成class文件,编译命令,只编译选定的目标,不管之前是否已经编译过,会在你的项目路径下生成一个target目录,在该目录中包含一个classes文件夹,里面全是生成的class文件及字节码文件。与build区别:只编译选定的目标,不管之前是否已经编译过。
test:翻译:测试。单元测试。
package:翻译:打包。将工程文件打包为指定的格式,例如JAR,WAR等(看你项目的pom文件,里面的packaging标签就是来指定打包类型的)。这个命令会在你的项目路径下一个target目录,并且拥有compile命令的功能进行编译,同时会在target目录下生成项目的jar/war文件。如果a项目依赖于b项目,打包b项目时,只会打包到b项目下target下,编译a项目时就会报错,因为找不到所依赖的b项目,说明a项目在本地仓库是没有找到它所依赖的b项目,这时就用到install命令。
verify:翻译:核实。主要是对package检查是否有效、符合标准。
install:翻译:安装。将包安装至本地仓库,以让其它项目依赖。该命令包含了package命令功能,不但会在项目路径下生成class文件和jar包,同时会在你的本地maven仓库生成jar文件,供其他项目使用(如果没有设置过maven本地仓库,一般在用户/.m2目录下。如果a项目依赖于b项目,那么install b项目时,会在本地仓库同时生成pom文件和jar文件,解决了上面打包package出错的问题)。
build:翻译:建造。功能类似compile,区别是对整个项目进行编译。与compile区别及特点:是对整个工程进行彻底的重新编译,而不管是否已经编译过。Build过程往往会生成发布包,这个具体要看对IDE的配置了,Build在实际中应用很少,因为开发时候基本上不用,发布生产时候一般都用ANT等工具来发布。Build因为要全部编译,还要执行打包等额外工 作,因此时间较长。
site:翻译:站点。生成项目的站点文档。
deploy:翻译:配置部署。复制到远程仓库。
Spring Cloud 封装了 Netflix 公司开发的 Eureka 模块来实现服务治理
在传统的rpc远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务治理,管理服务于服务之间依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册。
- 服务与服务之间
- 依赖关系,管理
- 管理服务与服务之间依赖关系
- 服务调用
- 负载均衡
- 容错
- 服务发现与注册
Eureka采用了CS的设计架构,Eureka Server 作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用 Eureka的客户端连接到 Eureka Server并维持心跳连接。这样系统的维护人员就可以通过 Eureka Server 来监控系统中各个微服务是否正常运行。
在服务注册与发现中,有一个注册中心。当服务器启动的时候,会把当前自己服务器的信息 比如 服务地址通讯地址等以别名方式注册到注册中心上。另一方(消费者|服务提供者),以该别名的方式去注册中心上获取到实际的服务通讯地址,然后再实现本地RPC调用RPC远程调用框架核心设计思想:在于注册中心,因为使用注册中心管理每个服务与服务之间的一个依赖关系(服务治理概念)。在任何rpc远程框架中,都会有一个注册中心(存放服务地址相关信息(接口地址))
1、Eureka Server提供服务注册服务
各个微服务节点通过配置启动后,会在EurekaServer中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。
2、EurekaClient通过注册中心进行访问
是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除(默认90秒)
创建一个基础项目
改pom
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>
<dependency>
<groupId>com.lingroupId>
<artifactId>cloud-api-commonartifactId>
<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>
写yml
server:
port: 7001
eureka:
instance:
hostname: localhost #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;
@EnableEurekaServer
@SpringBootApplication
public class CloudEurekaServer7001Application {
public static void main(String[] args) {
SpringApplication.run(CloudEurekaServer7001Application.class,args);
}
}
一定要加@EnableEurekaServer,不然就完蛋了不要加@EnableEurekaClient
localhost:7001就可以看到下面这张图
服务端
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>
客户端
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
为什么Eureka不需要加版本号了呢?是不是因为由于springcloud里面已经限定好了呢?
创建项目
改pom
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
写yml
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
defaultZone: http://localhost:7001/eureka
[fetch](javascript:: vt. 取来;接来;到达;吸引 | vi. 拿;
registry n. 注册;登记处;挂号处;船舶的国籍
主启动
自我保护机制
问题:微服务RPC远程服务调用最核心的是什么
高可用,试想你的注册中心只有一个only one, 它出故障了那就呵呵( ̄▽ ̄)"了,会导致整个为服务环境不可用,所以解决办法:搭建Eureka注册中心集群 ,实现负载均衡+故障容错
首先,就是先配置我们本地的hosts
C:\Windows\System32\drivers\etc
然后加上
####################SpringCloud2020.1.2##################
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
然后就是在创建一个Eureka服务端,端口7002
写yml
# 集群配置
eureka:
instance:
hostname: eureka7001.com #eureka服务端的实例名称
client:
register-with-eureka: false #false表示不向注册中心注册自己。
fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
defaultZone: http://eureka7002.com:7002/eureka/
7001那个也要改成这个
最后就是consumer还有payment的yml对应的Eureka要改一下
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka # 集群版
解决方法
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
默认负载均衡规则:
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Tue Jan 18 17:29:26 CST 2022
There was an unexpected error (type=Internal Server Error, status=500).
I/O error on GET request for "http://CLOUD-PAYMENT-SERVICE/payment/get/1": CLOUD-PAYMENT-SERVICE; nested exception is java.net.UnknownHostException: CLOUD-PAYMENT-SERVICE
org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://CLOUD-PAYMENT-SERVICE/payment/get/1": CLOUD-PAYMENT-SERVICE; nested exception is java.net.UnknownHostException: CLOUD-PAYMENT-SERVICE
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:751)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:677)
at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:318)
at com.lin.controller.OrderController.getPayment(OrderController.java:27)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:888)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:108)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:526)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:367)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:860)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1591)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.net.UnknownHostException: CLOUD-PAYMENT-SERVICE
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:196)
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:162)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:394)
at java.net.Socket.connect(Socket.java:606)
at java.net.Socket.connect(Socket.java:555)
at sun.net.NetworkClient.doConnect(NetworkClient.java:180)
at sun.net.www.http.HttpClient.openServer(HttpClient.java:463)
at sun.net.www.http.HttpClient.openServer(HttpClient.java:558)
at sun.net.www.http.HttpClient.(HttpClient.java:242)
at sun.net.www.http.HttpClient.New(HttpClient.java:339)
at sun.net.www.http.HttpClient.New(HttpClient.java:357)
at sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:1226)
at sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1162)
at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:1056)
at sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:990)
at org.springframework.http.client.SimpleBufferingClientHttpRequest.executeInternal(SimpleBufferingClientHttpRequest.java:76)
at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:742)
... 57 more
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka # 集群版
instance:
instance-id: payment8001
prefer-ip-address: true
效果图:
@Resource
private DiscoveryClient discoveryClient;
@GetMapping(value = "/discovery")
public Object discovery() {
List<String> services = discoveryClient.getServices();
for (String element : services) {
System.out.println(element);
}
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
for (ServiceInstance element : instances) {
System.out.println(element.getServiceId() + "\t" + element.getHost() + "\t" + element.getPort() + "\t"
+ element.getUri());
}
return this.discoveryClient;
}
结果:
cloud-consumer-order
cloud-payment-serviceCLOUD-PAYMENT-SERVICE 192.168.1.6 8001 http://192.168.1.6:8001
CLOUD-PAYMENT-SERVICE 192.168.1.6 8002 http://192.168.1.6:8002
概述
保护模式主要用于一组客户端和Eureka Server之间存在网络分区场景下的保护。一旦进入保护模式,
Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据,也就是不会注销任何微服务。如果在Eureka Server的首页看到以下这段提示,则说明Eureka进入了保护模式:
EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT.
RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE
为什么会产生Eureka自我保护机制?
为了防止EurekaClient可以正常运行,但是 与 EurekaServer网络不通情况下,EurekaServer不会立刻将EurekaClient服务剔除什么是自我保护模式?
默认情况下,如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳,EurekaServer将会注销该实例(默认90秒)。但是当网络分区故障发生(延时、卡顿、拥挤)时,微服务与EurekaServer之间无法正常通信,以上行为可能变得非常危险了——因为微服务本身其实是健康的,此时本不应该注销这个微服务。Eureka通过“自我保护模式”来解决这个问题——当EurekaServer节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。
在自我保护模式中,Eureka Server会保护服务注册表中的信息,不再注销任何服务实例。
它的**设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。**一句话讲解:好死不如赖活着综上,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留)也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka集群更加的健壮、稳定。
使用eureka.server.enable-self-preservation = false 可以禁用自我保护模式
默认是开启的
关闭是在eureka server关闭
关闭自我保护模式后的效果
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka # 集群版
instance:
instance-id: payment8001
prefer-ip-address: true
lease-renewal-interval-in-seconds: 10 # Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认为30秒)
lease-expiration-duration-in-seconds: 20 # Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是90秒),超时将剔除服务
想要测试效果,可以将8001的服务关闭,一刷新页面你会发现直接gg了,Eureka直接将8001服务移除了。
docker安装,简单快捷,哈哈哈哈
docker pull zookeeper:3.5.9
docker run -d -e TZ=“Asia/Shanghai” -p 2181:2181 --name zookeeper --restart always zookeeper
创建项目
改pom
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>com.lingroupId>
<artifactId>cloud-api-commonartifactId>
<version>${project.version}version>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-zookeeper-discoveryartifactId>
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>
写yml
#8004表示注册到zookeeper服务器的支付服务提供者端口号
server:
port: 8004
#服务别名----注册zookeeper到注册中心名称
spring:
application:
name: cloud-provider-payment
cloud:
zookeeper:
connect-string: 47.98.251.199:2181
主启动
@EnableDiscoveryClient //该注解用于向使用consul或者zookeeper作为注册中心时注册服务
@SpringBootApplication
public class CloudProviderPayment8004Application {
public static void main(String[] args) {
SpringApplication.run(CloudProviderPayment8004Application.class,args);
}
}
@EnableDiscoveryClient :该注解用于向使用consul或者zookeeper作为注册中心时注册服务
我们引入的spring-cloud-starter-zookeeper-discovery依赖自带一个zookeeper3.5.3的,如果我们的依赖大于这个版本,我们在不改变依赖的前提下要怎么办呢?下面是解决方案。
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-zookeeper-discoveryartifactId>
<exclusions>
<exclusion>
<groupId>org.apache.zookeepergroupId>
<artifactId>zookeeperartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.apache.zookeepergroupId>
<artifactId>zookeeperartifactId>
<version>3.4.9version>
dependency>
- docker exec -it d46a204f6e1c(this is id) bash
- cd bin
- ./zkCli.sh
- ls /services
我的操作
[root@iZbp1cor8i4hqjofynoigtZ /]# docker exec -it 3fa9d17cb9d4 bash root@3fa9d17cb9d4:/apache-zookeeper-3.5.9-bin# cd bin root@3fa9d17cb9d4:/apache-zookeeper-3.5.9-bin/bin# ./zkCli.sh Connecting to localhost:2181 2022-01-18 23:33:38,179 [myid:] - INFO [main:Environment@109] - Client environment:zookeeper.version=3.5.9-83df9301aa5c2a5d284a9940177808c01bc35cef, built on 01/06/2021 19:49 GMT 2022-01-18 23:33:38,182 [myid:] - INFO [main:Environment@109] - Client environment:host.name=3fa9d17cb9d4 2022-01-18 23:33:38,182 [myid:] - INFO [main:Environment@109] - Client environment:java.version=11.0.13 2022-01-18 23:33:38,195 [myid:] - INFO [main:Environment@109] - Client environment:java.vendor=Oracle Corporation 2022-01-18 23:33:38,195 [myid:] - INFO [main:Environment@109] - Client environment:java.home=/usr/local/openjdk-11 2022-01-18 23:33:38,195 [myid:] - INFO [main:Environment@109] - Client environment:java.class.path=/apache-zookeeper-3.5.9-bin/bin/../zookeeper-server/target/classes:/apache-zookeeper-3.5.9-bin/bin/../build/classes:/apache-zookeeper-3.5.9-bin/bin/../zookeeper-server/target/lib/*.jar:/apache-zookeeper-3.5.9-bin/bin/../build/lib/*.jar:/apache-zookeeper-3.5.9-bin/bin/../lib/zookeeper-jute-3.5.9.jar:/apache-zookeeper-3.5.9-bin/bin/../lib/zookeeper-3.5.9.jar:/apache-zookeeper-3.5.9-bin/bin/../lib/slf4j-log4j12-1.7.25.jar:/apache-zookeeper-3.5.9-bin/bin/../lib/slf4j-api-1.7.25.jar:/apache-zookeeper-3.5.9-bin/bin/../lib/netty-transport-native-unix-common-4.1.50.Final.jar:/apache-zookeeper-3.5.9-bin/bin/../lib/netty-transport-native-epoll-4.1.50.Final.jar:/apache-zookeeper-3.5.9-bin/bin/../lib/netty-transport-4.1.50.Final.jar:/apache-zookeeper-3.5.9-bin/bin/../lib/netty-resolver-4.1.50.Final.jar:/apache-zookeeper-3.5.9-bin/bin/../lib/netty-handler-4.1.50.Final.jar:/apache-zookeeper-3.5.9-bin/bin/../lib/netty-common-4.1.50.Final.jar:/apache-zookeeper-3.5.9-bin/bin/../lib/netty-codec-4.1.50.Final.jar:/apache-zookeeper-3.5.9-bin/bin/../lib/netty-buffer-4.1.50.Final.jar:/apache-zookeeper-3.5.9-bin/bin/../lib/log4j-1.2.17.jar:/apache-zookeeper-3.5.9-bin/bin/../lib/json-simple-1.1.1.jar:/apache-zookeeper-3.5.9-bin/bin/../lib/jline-2.14.6.jar:/apache-zookeeper-3.5.9-bin/bin/../lib/jetty-util-ajax-9.4.35.v20201120.jar:/apache-zookeeper-3.5.9-bin/bin/../lib/jetty-util-9.4.35.v20201120.jar:/apache-zookeeper-3.5.9-bin/bin/../lib/jetty-servlet-9.4.35.v20201120.jar:/apache-zookeeper-3.5.9-bin/bin/../lib/jetty-server-9.4.35.v20201120.jar:/apache-zookeeper-3.5.9-bin/bin/../lib/jetty-security-9.4.35.v20201120.jar:/apache-zookeeper-3.5.9-bin/bin/../lib/jetty-io-9.4.35.v20201120.jar:/apache-zookeeper-3.5.9-bin/bin/../lib/jetty-http-9.4.35.v20201120.jar:/apache-zookeeper-3.5.9-bin/bin/../lib/javax.servlet-api-3.1.0.jar:/apache-zookeeper-3.5.9-bin/bin/../lib/jackson-databind-2.10.5.1.jar:/apache-zookeeper-3.5.9-bin/bin/../lib/jackson-core-2.10.5.jar:/apache-zookeeper-3.5.9-bin/bin/../lib/jackson-annotations-2.10.5.jar:/apache-zookeeper-3.5.9-bin/bin/../lib/commons-cli-1.2.jar:/apache-zookeeper-3.5.9-bin/bin/../lib/audience-annotations-0.5.0.jar:/apache-zookeeper-3.5.9-bin/bin/../zookeeper-*.jar:/apache-zookeeper-3.5.9-bin/bin/../zookeeper-server/src/main/resources/lib/*.jar:/conf: 2022-01-18 23:33:38,195 [myid:] - INFO [main:Environment@109] - Client environment:java.library.path=/usr/java/packages/lib:/usr/lib64:/lib64:/lib:/usr/lib 2022-01-18 23:33:38,195 [myid:] - INFO [main:Environment@109] - Client environment:java.io.tmpdir=/tmp 2022-01-18 23:33:38,195 [myid:] - INFO [main:Environment@109] - Client environment:java.compiler=<NA> 2022-01-18 23:33:38,195 [myid:] - INFO [main:Environment@109] - Client environment:os.name=Linux 2022-01-18 23:33:38,196 [myid:] - INFO [main:Environment@109] - Client environment:os.arch=amd64 2022-01-18 23:33:38,196 [myid:] - INFO [main:Environment@109] - Client environment:os.version=3.10.0-1160.31.1.el7.x86_64 2022-01-18 23:33:38,196 [myid:] - INFO [main:Environment@109] - Client environment:user.name=root 2022-01-18 23:33:38,196 [myid:] - INFO [main:Environment@109] - Client environment:user.home=/root 2022-01-18 23:33:38,202 [myid:] - INFO [main:Environment@109] - Client environment:user.dir=/apache-zookeeper-3.5.9-bin/bin 2022-01-18 23:33:38,202 [myid:] - INFO [main:Environment@109] - Client environment:os.memory.free=22MB 2022-01-18 23:33:38,204 [myid:] - INFO [main:Environment@109] - Client environment:os.memory.max=247MB 2022-01-18 23:33:38,204 [myid:] - INFO [main:Environment@109] - Client environment:os.memory.total=29MB 2022-01-18 23:33:38,225 [myid:] - INFO [main:ZooKeeper@868] - Initiating client connection, connectString=localhost:2181 sessionTimeout=30000 watcher=org.apache.zookeeper.ZooKeeperMain$MyWatcher@2344fc66 2022-01-18 23:33:38,244 [myid:] - INFO [main:X509Util@79] - Setting -D jdk.tls.rejectClientInitiatedRenegotiation=true to disable client-initiated TLS renegotiation 2022-01-18 23:33:38,290 [myid:] - INFO [main:ClientCnxnSocket@237] - jute.maxbuffer value is 4194304 Bytes 2022-01-18 23:33:38,308 [myid:] - INFO [main:ClientCnxn@1653] - zookeeper.request.timeout value is 0. feature enabled= Welcome to ZooKeeper! 2022-01-18 23:33:38,359 [myid:localhost:2181] - INFO [main-SendThread(localhost:2181):ClientCnxn$SendThread@1112] - Opening socket connection to server localhost/127.0.0.1:2181. Will not attempt to authenticate using SASL (unknown error) JLine support is enabled 2022-01-18 23:33:38,404 [myid:localhost:2181] - INFO [main-SendThread(localhost:2181):ClientCnxn$SendThread@959] - Socket connection established, initiating session, client: /127.0.0.1:55092, server: localhost/127.0.0.1:2181 2022-01-18 23:33:38,421 [myid:localhost:2181] - INFO [main-SendThread(localhost:2181):ClientCnxn$SendThread@1394] - Session establishment complete on server localhost/127.0.0.1:2181, sessionid = 0x101ef1ffe2d0001, negotiated timeout = 30000 WATCHER:: WatchedEvent state:SyncConnected type:None path:null [zk: localhost:2181(CONNECTED) 0] ls /services [cloud-provider-payment] [zk: localhost:2181(CONNECTED) 1] ls /services/cloud-provider-payment [13bce8e5-9b9d-4c3e-9820-d23077e45bfb] [zk: localhost:2181(CONNECTED) 2] ls /services/cloud-provider-payment/13bce8e5-9b9d-4c3e-9820-d23077e45bfb [] [zk: localhost:2181(CONNECTED) 3] get /services/cloud-provider-payment/13bce8e5-9b9d-4c3e-9820-d23077e45bfb {"name":"cloud-provider-payment","id":"13bce8e5-9b9d-4c3e-9820-d23077e45bfb","address":"192.168.1.6","port":8004,"sslPort":null,"payload":{"@class":"org.springframework.cloud.zookeeper.discovery.ZookeeperInstance","id":"application-1","name":"cloud-provider-payment","metadata":{}},"registrationTimeUTC":1642519693310,"serviceType":"DYNAMIC","uriSpec":{"parts":[{"value":"scheme","variable":true},{"value":"://","variable":false},{"value":"address","variable":true},{"value":":","variable":false},{"value":"port","variable":true}]}} [zk: localhost:2181(CONNECTED) 4]
粗分:临时节点、持久节点
细分:带序号的临时节点、临时节点;带序号的持节节点、持节节点
我们正常开启的zookeeper客户端,默认情况下是临时节点,当我们停了我们的服务,我们在zookeeper就查询不到这个服务节点了,当我们再次开启这个节点的时候,我们会发现我们服务节点在zookeeper里面的序号就发生了改变了。
通过这个现象我们也可以看出这个情况。
Eureka不会这么恨心,还是会在再次开启的时候保留序号。
[zk: localhost:2181(CONNECTED) 4] ls /services/cloud-provider-payment [13bce8e5-9b9d-4c3e-9820-d23077e45bfb] [zk: localhost:2181(CONNECTED) 5] ls /services/cloud-provider-payment Node does not exist: /services/cloud-provider-payment [zk: localhost:2181(CONNECTED) 6] ls /services/cloud-provider-payment [4fb25cc0-8f37-41a1-8ead-16bb7507e081]
然后就是我存在一个疑问
Eureka默认是开启保护模式的吗?好像是吧,但是为什么我在测试的时候,当我关闭一个服务的时候,开启保护模式的服务和未开启保护模式的服务端一样,都显示不出来那个服务了。
下面是你要的答案
一、开启Eureka自我保护模式
访问Eureka主页时,如果看到这样一段大红色的句子:*EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.*
那么表明Eureka的自我保护模式是启动的。
解决这种情况的方法主要有几种方式:
\1. 等待 Eureka Server 自动恢复
正常的情况下,等待网络恢复(或者没有频繁的启动与关闭实例)后,等待一段时间 Eureka Server 会自动关闭自我保护模式,但是如果它迟迟没有关闭该模式,那么便可以尝试手动关闭,如下。\2. 重启 Eureka Server
通常而言,PRD 环境建议对 Eureka Server 做负载均衡,这样在依次关闭并开启 Eureka Server 后,无效的实例会被清除,并且不会对正常的使用照成影响。\3. 关闭 Eureka 的自我保护模式
后面介绍二、Eureka自我保护模式原理
默认情况下,如果在15分钟内超过85%的客户端节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障(比如网络故障或频繁的启动关闭客户端),Eureka Server自动进入自我保护模式。不再剔除任何服务,当网络故障恢复后,该节点自动退出自我保护模式。eureka: server: #自我保护模式,当出现网络分区故障、频繁的开启关闭客户端、eureka在短时间内丢失过多客户端时,会进入自我保护模式,即一个服务长时间没有发送心跳,eureka也不会将其删除,默认为true enable-self-preservation: true #eureka server清理无效节点的时间间隔,默认60000毫秒,即60秒 eviction-interval-timer-in-ms: 60000 #阈值更新的时间间隔,单位为毫秒,默认为15 * 60 * 1000 renewal-threshold-update-interval-ms: 15 * 60 * 1000 #阈值因子,默认是0.85,如果阈值比最小值大,则自我保护模式开启 renewal-percent-threshold: 0.85 #清理任务程序被唤醒的时间间隔,清理过期的增量信息,单位为毫秒,默认为30 * 1000 delta-retention-timer-interval-in-ms: 30000
自我保护模式是一种对网络异常的安全保护措施。使用自我保护模式让Eureka集群更加的健壮、稳定。
三、关闭 Eureka 的自我保护模式
可以使用eureka.server.enable-self-preservation=false来禁用自我保护模式,****适合在开发/测试环境中使用,生产环境建议打开自我保护模式。****对于开发/测试环境的 Eureka Server,个人更建议关闭它的自我保护模式,因为你可能需要不断的开启与关闭实例,如果并未关闭自我保护模式,那么很容易就会触发自我保护模式,此时对调试会相对比较麻烦。
但是关闭自我保护模式,会有另外一个可能的问题,即隔一段时间后,可能会发生实例并未关闭,却无法通过网关访问了,此时很可能是由于网络问题,导致实例(或网关)与 Eureka Server 断开了连接,Eureka Server 已经将其注销(网络恢复后,实例并不会再次注册),此时重启 Eureka Server 节点或实例,并等待一小段时间即可。
关闭自我保护模式,可以在服务端和客户端配置。
服务端配置: eureka: server: enable-self-preservation: false #eureka server清理无效节点的时间间隔,默认60000毫秒,即60秒 eviction-interval-timer-in-ms: 60000 # 单位毫秒 客户端配置: # 心跳检测检测与续约时间 # 测试时将值设置设置小些,保证服务关闭后注册中心能及时踢出服务 eureka: instance: lease-renewal-interval-in-seconds: 1 lease-expiration-duration-in-seconds: 2 配置说明lease-renewal-interval-in-seconds 每间隔1s,向服务端发送一次心跳,证明自己依然”存活“。 lease-expiration-duration-in-seconds 告诉服务端,如果我2s之内没有给你发心跳,就代表我“死”了,请将我踢掉。
四、Eureka的健康检查
说明:在Status栏显示着UP,表示应用程序状态正常。其它取值DOWN、OUT_OF_SERVICE、UNKNOWN等,只有UP的微服务会被请求。由于Eureka Server与Eureka Client之间使用心跳机制来确定Eureka Client的状态,默认情况下,服务器端与客户端的心跳保持正常,应用程序就会始终保持“UP”状态,所以微服务的UP并不能完全反应应用程序的状态。
Spring Boot Actuator提供了/health端点,该端点可展示应用程序的健康信息,只有将该端点中的健康状态传播到Eureka Server就可以了,实现这点很简单,只需为微服务配置如下内容:
#开启健康检查(需要spring-boot-starter-actuator依赖)eureka.client.healthcheck.enabled = true
我们如何实现远程调用的呢?
我们通过注入RestTemplate,然后在上面加上@LoadBalanced,然后我们的服务已经注册进了服务端,所以我们发起请求
我们的restTemplate对象就会通过url到注册中心里面找了
public static final String INVOKE_URL = "http://cloud-provider-payment"; //cloud-provider-payment:服务名称,该服务注册进了服务中心
Consul 是一套开源的分布式服务发现和配置管理系统,由 HashiCorp 公司用 Go 语言开发。
提供了微服务系统中的服务治理、配置中心、控制总线等功能。这些功能中的每一个都可以根据需要单独使用,也可以一起使用以构建全方位的服务网格,总之Consul提供了一种完整的服务网格解决方案。
它具有很多优点。包括: 基于 raft 协议,比较简洁; 支持健康检查, 同时支持 HTTP 和 DNS 协议 支持跨数据中心的 WAN 集群 提供图形界面 跨平台,支持 Linux、Mac、Windows
docker pull consul:1.6.1
docker run -d -p 8500:8500 --restart=always --name:consul consul:1.6.1
创建
改pom
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-consul-discoveryartifactId>
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>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
写yml
###consul服务端口号
server:
port: 80
spring:
application:
name: consul-consumer-order
####consul注册中心地址
cloud:
consul:
host: 47.98.251.199
port: 8500
discovery:
#hostname: 127.0.0.1
service-name: ${spring.application.name}
heartbeat:
enabled: true # 心跳发送
主启动
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
public class PaymentMain8006 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8006.class,args);
}
}
C:Consistency(强一致性)
A:Availability(可用性)
P:Partition tolerance(分区容错性)
CPA理论关注粒度是数据,而不是整体系统设计的策略。
CAP理论的核心
最多只能同时较好的满足两个。
CAP理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求,
因此,根据 CAP 原理将 NoSQL 数据库分成了满足 CA 原则、满足 CP 原则和满足 AP 原则三 大类:
CA - 单点集群,满足一致性,可用性的系统,通常在可扩展性上不太强大。
CP - 满足一致性,分区容忍性的系统,通常性能不是特别高。
AP - 满足可用性,分区容忍性的系统,通常可能对一致性要求低一些。
AP架构
当网络分区出现后,为了保证可用性,系统B可以返回旧值,保证系统的可用性。
结论:违背了一致性C的要求,只满足可用性和分区容错,即AP
CP架构
当网络分区出现后,为了保证一致性,就必须拒接请求,否则无法保证一致性
结论:违背了可用性A的要求,只满足一致性和分区容错,即CP
Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端 负载均衡的工具。
简单的说,Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。我们很容易使用Ribbon实现自定义的负载均衡算法。
LB负载均衡(Load Balance)是什么
简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA(高可用)。
常见的负载均衡有软件Nginx,LVS,硬件 F5等。Ribbon本地负载均衡客户端 VS Nginx服务端负载均衡区别
Nginx是服务器负载均衡,客户端所有请求都会交给nginx,然后由nginx实现转发请求。即负载均衡是由服务端实现的。Ribbon本地负载均衡,在调用微服务接口时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用技术。
即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5, 也可以是软件,如nginx), 由该设施负责把访问请求通过某种策略转发至服务的提供方;
将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。
Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。
Ribbon在工作时分成两步
第一步先选择 EurekaServer ,它优先选择在同一个区域内负载较少的server.
第二步再根据用户指定的策略,在从server取到的服务注册列表中选择一个地址。
其中Ribbon提供了多种策略:比如轮询、随机和根据响应时间加权。
总结:Ribbon其实就是一个软负载均衡的客户端组件,
他可以和其他所需请求的客户端结合使用,和eureka结合只是其中的一个实例。
Ribbon和Eureka配置使用,我们不难发现就是,Eureka内置了Ribbon
T getForObject(String url, Class responseType, Object… uriVariables);
T getForObject(String url, Class responseType, Map
uriVariables); T getForObject(URI url, Class responseType);
ResponseEntity getForEntity(String url, Class responseType, Object… uriVariables);
ResponseEntity getForEntity(String url, Class responseType, Map
uriVariables); ResponseEntity getForEntity(URI var1, Class responseType);
T postForObject(String url, @Nullable Object request, Class responseType, Object… uriVariables);
T postForObject(String url, @Nullable Object request, Class responseType, Map
uriVariables); T postForObject(URI url, @Nullable Object request, Class responseType);
ResponseEntity postForEntity(String url, @Nullable Object request, Class responseType, Object… uriVariables);
ResponseEntity postForEntity(String url, @Nullable Object request, Class responseType, Map
uriVariables); ResponseEntity postForEntity(URI url, @Nullable Object request, Class responseType);
- com.netflix.loadbalancer.RoundRobinRule:轮询
- com.netflix.loadbalancer.RandomRule:随机
- com.netflix.loadbalancer.RetryRule:先按照RoundRobinRule(轮询)的策略获取服务,如果获取服务失败则在指定时间内会进行重试,获取可用的服务
- WeightedResponseTimeRule:对RoundRobinRule(轮询)的扩展,响应速度越快的实例选择权重越大,越容易被选择
- BestAvailableRule:会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
- AvailabilityFilteringRule:先过滤掉故障实例,再选择并发较小的实例
- ZoneAvoidanceRule:默认规则,复合判断server所在区域的性能和server的可用性选择服务器
官方文档明确给出了警告:
这个自定义配置类不能放在@ComponentScan所扫描的当前包下以及子包下,
否则我们自定义的这个配置类就会被所有的Ribbon客户端所共享,达不到特殊化定制的目的了。
1、配置
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MySelfRule {
@Bean
public IRule myRule() {
return new RandomRule();//定义为随机
}
}
2、主启动
@RibbonClient(name = "cloud-payment-service",configuration = MySelfRule.class)//故意名字用小写
@EnableEurekaClient
@SpringBootApplication
public class CloudConsumerOrder80Application {
public static void main(String[] args) {
SpringApplication.run(CloudConsumerOrder80Application.class,args);
}
}
3、restTemplate
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
负载均衡算法:rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标 ,每次服务重启动后rest接口计数从1开始。
List
instances = discoveryClient.getInstances(“CLOUD-PAYMENT-SERVICE”); 如: List [0] instances = 127.0.0.1:8002
List [1] instances = 127.0.0.1:80018001+ 8002 组合成为集群,它们共计2台机器,集群总数为2, 按照轮询算法原理:
当总请求数为1时: 1 % 2 =1 对应下标位置为1 ,则获得服务地址为127.0.0.1:8001
当总请求数位2时: 2 % 2 =0 对应下标位置为0 ,则获得服务地址为127.0.0.1:8002
当总请求数位3时: 3 % 2 =1 对应下标位置为1 ,则获得服务地址为127.0.0.1:8001
当总请求数位4时: 4 % 2 =0 对应下标位置为0 ,则获得服务地址为127.0.0.1:8002
如此类推…
里面涉及了JUC的知识,我目前实力看不懂,以后再说。
周阳说JUC,非常好,找时间全刷了
目前我的能力还没有办法掌握
import org.springframework.cloud.client.ServiceInstance;
import java.util.List;
public interface LoadBalancer {
ServiceInstance instances(List<ServiceInstance> serviceInstances);
}
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@Component
public class MyLoadBalancer implements LoadBalancer{
private AtomicInteger atomicInteger = new AtomicInteger(0);
public final int getAndIncrement() {
int current;
int next;
do {
current = this.atomicInteger.get();
next = current >= Integer.MAX_VALUE ? 0 : current + 1;
} while (!this.atomicInteger.compareAndSet(current,next));
System.out.println("*****next: "+next);
return next;
}
@Override
public ServiceInstance instances(List<ServiceInstance> serviceInstances) {
int index = getAndIncrement() % serviceInstances.size();
return serviceInstances.get(index);
}
}
官网解释:
https://cloud.spring.io/spring-cloud-static/Hoxton.SR1/reference/htmlsingle/#spring-cloud-openfeignFeign是一个声明式WebService客户端。使用Feign能让编写Web Service客户端更加简单。
它的使用方法是定义一个服务接口然后在上面添加注解。Feign也支持可拔插式的编码器和解码器。Spring Cloud对Feign进行了封装,使其支持了Spring MVC标准注解和HttpMessageConverters。Feign可以与Eureka和Ribbon组合使用以支持负载均衡
Feign能干什么
Feign旨在使编写Java Http客户端变得更容易。
前面在使用Ribbon+RestTemplate时,利用RestTemplate对http请求的封装处理,形成了一套模版化的调用方法。但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。所以,Feign在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义。在Feign的实现下,我们只需创建一个接口并使用注解的方式来配置它(以前是Dao接口上面标注Mapper注解,现在是一个微服务接口上面标注一个Feign注解即可),即可完成对服务提供方的接口绑定,简化了使用Spring cloud Ribbon时,自动封装服务调用客户端的开发量。Feign集成了Ribbon
利用Ribbon维护了Payment的服务列表信息,并且通过轮询实现了客户端的负载均衡。而与Ribbon不同的是,通过feign只需要定义服务绑定接口且以声明式的方法,优雅而简单的实现了服务调用
创建项目
改pom
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>com.lingroupId>
<artifactId>cloud-api-commonartifactId>
<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>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
写yml
server:
port: 80
eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
主启动
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableFeignClients
@SpringBootApplication
public class OrderFeignMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderFeignMain80.class,args);
}
}
@EnableFeignClients:这个可一定不要忘记了
业务类
需要远程调用的接口
import com.lin.entities.CommonResult;
import com.lin.entities.Payment;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@Component
@FeignClient("CLOUD-PAYMENT-SERVICE")
public interface PaymentFeignService {
@GetMapping(value = "/payment/get/{id}")
CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);
}
本项目自己的控制层
@RestController
public class OrderFeignController {
@Resource
private PaymentFeignService paymentFeignService;
@GetMapping(value = "/consumer/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) {
return paymentFeignService.getPaymentById(id);
}
}
这个调用确实高,比我强上万倍
测试
实现类中做负载均衡并调用其他服务,这句话就很nice
package com.atguigu.springcloud.controller;
import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import com.atguigu.springcloud.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* @auther zzyy
* @create 2020-01-27 21:17
*/
@RestController
@Slf4j
public class PaymentController
{
@Value("${server.port}")
private String serverPort;
@Resource
private PaymentService paymentService;
@Resource
private DiscoveryClient discoveryClient;
@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,"插入成功,返回结果"+result+"\t 服务端口:"+serverPort,payment);
}else{
return new CommonResult(444,"插入失败",null);
}
}
@GetMapping(value = "/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id)
{
Payment payment = paymentService.getPaymentById(id);
log.info("*****查询结果:{}",payment);
if (payment != null) {
return new CommonResult(200,"查询成功"+"\t 服务端口:"+serverPort,payment);
}else{
return new CommonResult(444,"没有对应记录,查询ID: "+id,null);
}
}
@GetMapping(value = "/payment/discovery")
public Object discovery()
{
List<String> services = discoveryClient.getServices();
for (String element : services) {
System.out.println(element);
}
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
for (ServiceInstance element : instances) {
System.out.println(element.getServiceId() + "\t" + element.getHost() + "\t" + element.getPort() + "\t"
+ element.getUri());
}
return this.discoveryClient;
}
@GetMapping(value = "/payment/lb")
public String getPaymentLB()
{
System.out.println("*****lb from port: "+serverPort);
return serverPort;
}
@GetMapping(value = "/payment/feign/timeout")
public String paymentFeignTimeOut()
{
System.out.println("*****paymentFeignTimeOut from port: "+serverPort);
//暂停几秒钟线程
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
return serverPort;
}
}
package com.atguigu.springcloud.service;
import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* @auther zzyy
* @create 2020-02-03 12:00
*/
@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE")
public interface PaymentFeignService
{
@GetMapping(value = "/payment/get/{id}")
CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);
@GetMapping(value = "/payment/feign/timeout")
String paymentFeignTimeOut();
}
package com.atguigu.springcloud.controller;
import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import com.atguigu.springcloud.service.PaymentFeignService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @auther zzyy
* @create 2020-02-03 14:10
*/
@RestController
public class OrderFeignController
{
@Resource
private PaymentFeignService paymentFeignService;
@GetMapping(value = "/consumer/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id)
{
return paymentFeignService.getPaymentById(id);
}
@GetMapping(value = "/consumer/payment/feign/timeout")
public String paymentFeignTimeOut()
{
return paymentFeignService.paymentFeignTimeOut();
}
}
默认Feign客户端只等待一秒钟,但是服务端处理需要超过1秒钟,导致Feign客户端不想等待了,直接返回报错。
为了避免这样的情况,有时候我们需要设置Feign客户端的超时控制。yml文件中开启配置
重头戏,配置
配置是在客户端,就是在引入openFeign的项目里面配置
#设置feign客户端超时时间(OpenFeign默认支持ribbon) ribbon: #指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间 ReadTimeout: 5000 #指的是建立连接后从服务器读取到可用资源所用的时间 ConnectTimeout: 5000
Feign 提供了日志打印功能,我们可以通过配置来调整日志级别,从而了解 Feign 中 Http 请求的细节。
说白了就是对Feign接口的调用情况进行监控和输出
NONE:默认的,不显示任何日志;none
BASIC:仅记录请求方法、URL、响应状态码及执行时间;basic
HEADERS:除了 BASIC 中定义的信息之外,还有请求和响应的头信息;headers
FULL:除了 HEADERS 中定义的信息之外,还有请求和响应的正文及元数据。full
@Configuration public class FeignConfig { @Bean Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } }
logging: level: com.lin.service.PaymentFeignService: debug
2022-01-19 21:53:57.623 DEBUG 39224 --- [p-nio-80-exec-8] com.lin.service.PaymentFeignService : [PaymentFeignService#paymentFeignTimeOut] <--- HTTP/1.1 200 (3041ms) 2022-01-19 21:53:57.623 DEBUG 39224 --- [p-nio-80-exec-8] com.lin.service.PaymentFeignService : [PaymentFeignService#paymentFeignTimeOut] connection: keep-alive 2022-01-19 21:53:57.623 DEBUG 39224 --- [p-nio-80-exec-8] com.lin.service.PaymentFeignService : [PaymentFeignService#paymentFeignTimeOut] content-length: 4 2022-01-19 21:53:57.623 DEBUG 39224 --- [p-nio-80-exec-8] com.lin.service.PaymentFeignService : [PaymentFeignService#paymentFeignTimeOut] content-type: text/plain;charset=UTF-8 2022-01-19 21:53:57.624 DEBUG 39224 --- [p-nio-80-exec-8] com.lin.service.PaymentFeignService : [PaymentFeignService#paymentFeignTimeOut] date: Wed, 19 Jan 2022 13:53:57 GMT 2022-01-19 21:53:57.624 DEBUG 39224 --- [p-nio-80-exec-8] com.lin.service.PaymentFeignService : [PaymentFeignService#paymentFeignTimeOut] keep-alive: timeout=60 2022-01-19 21:53:57.624 DEBUG 39224 --- [p-nio-80-exec-8] com.lin.service.PaymentFeignService : [PaymentFeignService#paymentFeignTimeOut] 2022-01-19 21:53:57.624 DEBUG 39224 --- [p-nio-80-exec-8] com.lin.service.PaymentFeignService : [PaymentFeignService#paymentFeignTimeOut] 8001 2022-01-19 21:53:57.624 DEBUG 39224 --- [p-nio-80-exec-8] com.lin.service.PaymentFeignService : [PaymentFeignService#paymentFeignTimeOut] <--- END HTTP (4-byte body)
- 日志信息。
#指的是建立连接后从服务器读取到可用资源所用的时间
ConnectTimeout: 5000
Feign 提供了日志打印功能,我们可以通过配置来调整日志级别,从而了解 Feign 中 Http 请求的细节。
说白了就是对Feign接口的调用情况进行监控和输出
NONE:默认的,不显示任何日志;none
BASIC:仅记录请求方法、URL、响应状态码及执行时间;basic
HEADERS:除了 BASIC 中定义的信息之外,还有请求和响应的头信息;headers
FULL:除了 HEADERS 中定义的信息之外,还有请求和响应的正文及元数据。full
@Configuration public class FeignConfig { @Bean Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } }
logging: level: com.lin.service.PaymentFeignService: debug
2022-01-19 21:53:57.623 DEBUG 39224 --- [p-nio-80-exec-8] com.lin.service.PaymentFeignService : [PaymentFeignService#paymentFeignTimeOut] <--- HTTP/1.1 200 (3041ms) 2022-01-19 21:53:57.623 DEBUG 39224 --- [p-nio-80-exec-8] com.lin.service.PaymentFeignService : [PaymentFeignService#paymentFeignTimeOut] connection: keep-alive 2022-01-19 21:53:57.623 DEBUG 39224 --- [p-nio-80-exec-8] com.lin.service.PaymentFeignService : [PaymentFeignService#paymentFeignTimeOut] content-length: 4 2022-01-19 21:53:57.623 DEBUG 39224 --- [p-nio-80-exec-8] com.lin.service.PaymentFeignService : [PaymentFeignService#paymentFeignTimeOut] content-type: text/plain;charset=UTF-8 2022-01-19 21:53:57.624 DEBUG 39224 --- [p-nio-80-exec-8] com.lin.service.PaymentFeignService : [PaymentFeignService#paymentFeignTimeOut] date: Wed, 19 Jan 2022 13:53:57 GMT 2022-01-19 21:53:57.624 DEBUG 39224 --- [p-nio-80-exec-8] com.lin.service.PaymentFeignService : [PaymentFeignService#paymentFeignTimeOut] keep-alive: timeout=60 2022-01-19 21:53:57.624 DEBUG 39224 --- [p-nio-80-exec-8] com.lin.service.PaymentFeignService : [PaymentFeignService#paymentFeignTimeOut] 2022-01-19 21:53:57.624 DEBUG 39224 --- [p-nio-80-exec-8] com.lin.service.PaymentFeignService : [PaymentFeignService#paymentFeignTimeOut] 8001 2022-01-19 21:53:57.624 DEBUG 39224 --- [p-nio-80-exec-8] com.lin.service.PaymentFeignService : [PaymentFeignService#paymentFeignTimeOut] <--- END HTTP (4-byte body)
- 日志信息。