springboot整合logback日志框架

一、添加依赖

但是呢,实际开发中我们不需要直接添加该依赖,你会发现spring-boot-starter其中包含了 spring-boot-starter-logging,该依赖内容就是 Spring Boot 默认的日志框架 Logback+SLF4J。而 spring-boot-starter-web 包含了spring-boot-stater,所以我们只需要引入web组件即可:

二、默认配置

默认情况下Spring Boot将日志输出到控制台,不会写到日志文件。如果要编写除控制台输出之外的日志文件,则需在application.properties中设置logging.file或logging.path属性

三、logback-spring.xml

可以看到上面那种方式配置简单,但是能实现的功能也非常有限,如果想要更复杂的需求,就需要下面的定制化配置了。

Spring Boot官方推荐优先使用带有-spring的文件名作为你的日志配置(如使用logback-spring.xml,而不是logback.xml),命名为logback-spring.xml的日志配置文件,将xml放至 src/main/resource下面。

 

 

0

如何配置Spring Cloud应用使用指定IP或忽略某张网卡?

问题说明

分布式应用部署到服务上,由于服务器可能存在多张网卡,造成IP地址不准的问题。

解决方法

1、直接添加忽略某张网卡的配置:

正则:

2、指定默认IP:

3、除了这些配置,还有以下的这些配置:

ignored-interfaces和preferred-networks这两个配置。这两个配置决定了spring cloud应用在启动的时候所使用的网卡和IP地址。ignored-interfaces接收一个正则表达式数组,配置名字虽然是ignored-interfaces,忽略的网卡,但是因为其接收的是正则表达式,所以我们可以任意的选择和反选本机的网卡。preferred-networks是指倾向于使用的IP地址,接收一个正则表达式数组,用于选择Spring Cloud应用使用的本机的IP地址。通过这两个配置,我们可以任意指定Spring Cloud应用使用的网卡和IP地址。

0

Spring5.X编译的踩坑记录

1.Spring5.0

Spring5.X编译的踩坑记录Spring 5.0是在2013年发布Spring 4后的第一个大版本.该次升级也更新了不少的新特性. 基本可以归为如下几类:

  • JDK版本升级
  • Core框架修订,核心容器更新
  • Kotlin函数式编程
  • 响应式编程模型
  • 测试改进
  • 额外库支持
  • 停止维护一些特性

2.编译源码的原因

如果想要深入理解Spring的特性并学习优秀开源项目的编程范式,必然要到源码中分析实现细节.但仅通过view代码的形式是无法明晰某些复杂逻辑的. Spring的源码中提供了很多单元测试,我们可以通过Debug单元测试的形式来加深对组件的理解.

3.踩坑细节

1.版本控制工具Gradle

Spring5.X编译的踩坑记录Gradle虽然特性优于Maven,且有完善的task体系.但终究是个战未来的工具,对于大多数人来说Gradle还是一个相对陌生的东西. 但我们的目的是编译Spring源码,对于技术栈中没有Gradle的人来说Gradle就成了一道绕不过的坎.

我使用的Gradle版本为5.5.1. 在运行build.gradle的过程中,由于连接的依赖仓库为外网仓库,所以网速惨不忍睹.我build了两个小时+都没有完成. 这时有两个解决方案.

  1. 修改中央仓库地址
    build.gradle中的maven中央仓库地址更换为阿里云的私服

    依赖中有些内容是私服中所没有的,解决方案是先使用私服把能下载的依赖都下载了,再切回中央仓库将剩余依赖下载,可以节省很多时间.

  2. 使用代理
    有梯子的小伙伴可以直接使用代理下载依赖,速度会快很多.

2.spring-core核心代码报错

spring-core编译时CoroutinesUtils找不到.

运行build.gradle后,会在spring-core-coroutines包中生成build文件夹,将buildlibs的内容导入到spring-core的依赖中即可解决该问题.

3.build顺序问题

由于spring-framework内的组件大部分都引用了spring-oxmspring-core.所以需要提前先编译这两个包 ,否则直接编译其他包会显示缺依赖而直接失败.

运行这两个包内的compileTestJavaTask.
Spring5.X编译的踩坑记录运行完成后,就可以继续编译我们常用的组件,例如:spring-beansspring-context了.

4.idea中kotlin插件冲突问题

Spring5.X编译的踩坑记录

直接关闭idea的kotlin插件即可.

5.关于spring-aspects的问题

由于引用了IntelliJ IDEA未知的方面类型,因此spring-aspects不会编译.可以从项目中排除’spring-aspects’以避免编译错误.

4.reference

0

Spring 里 @Lazy 和 @DependsOn 注解作用是什么?

Spring 里 @Lazy 和 @DependsOn 注解作用是什么?

lazy是是否懒加载,如果一个类不是启动就需要的就可以设置为懒加载,用的时候再初始化。DependsOn是说该类的初始化依赖于另外一个类的初始化,也就是只有另外一个类初始化了,这个类才会初始化。

0

Spring Boot项目使用Docker和Kubernetes(k8s)进行容器化部署

Spring Boot项目的结构

项目名称是Test

  1. 项目根据环境不同需要读取不同的application.yml
  2. 项目使用logback作为日志工具,需要读取一个logback-spring.xml的配置文件。
  3. 项目还需要读取一个properties文件。
  4. 为方便,所有Kubernetes资源均在default namespace下。
namespace

Kubernetes的许多资源对象都隶属于某个namespace,namespace是对资源的逻辑划分,不同namespace下的资源可以做到简单隔离。

准备工作

镜像

针对项目jar包,我们需要将其打成一个Docker镜像,而Dockerfile就是打包过程的指导文件,类似于make过程中的Makefile

jar->Docker镜像

以下是Dockerfile的内容。


  1. FROM表示要生产的镜像的基础镜像,我们使用jdk8。
  2. VOLUME表示定义一个匿名卷,也可以理解为创建一个目录,这里的/tmp是由于tomcat的需要。
  3. ADD表示将项目编译后的jar包拷贝到镜像里,默认为根目录/
  4. ENTRYPOINT表示入口点,即容器启动时执行的命令,通过上述组合,实际执行的命令为:java -Djava.security.egd=file:/dev/./urandom -jar -Dspring.config.location=/etc/config/application.yml -Dlogging.config=/etc/config/logback-spring.xml -DconfPath=/etc/config /test.jar
    注意这里的配置路径/etc/config还不存在,下文中会创建和用到。

配置

Kubernetes ConfigMap

ConfigMap可以用来保存单个的键值对,也可以保存配置文件。

配置文件->ConfigMap

针对项目配置,需要做的改造就是把配置文件(应用、日志、业务)转化成ConfigMap对象,从而让Kubernetes化后的应用能够读取。

以下是test-config.yml的内容。


  1. 注意配置文件按照yaml规范缩进。
  2. data属性里记录实际的配置,本质上仍是键值对,如第一个数据的key为application-yml,内容为其配置内容。

应用部署定义

Kubernetes Deployment

Deployment是一种控制容器组的对象,在Deployment中定义一个期望的容器组的数量,Deployment在创建后会维持这个数量,当数量少于期望时会新建容器组,反之会停止超额的容器组。

部署->Deployment

针对本项目,对应的Deployment配置如下。

test-deployment.yml内容如下。


  1. replicas即为此Deployment期望的容器组的数量。
  2. metadata.labels用于跟下文中的Service对接。
  3. spec.selector.matchLabels与容器组的spec.template.metadata.labels对接。
  4. spec.template.spec.volumes定义了一些卷,name为卷名。
    • 如果定义了configMap属性,则items属性的key对应上文中ConfigMap对象的名称,path则为要输出的文件,如第一个item的行为是读取ConfigMap对象test-config的第一个数据的内容(key=application-yml),并将其保存为application.yml配置文件。
    • 如果定义了hostPath属性,则是宿主机上的同名目录,如此处将容器日志输出到宿主机的/var/log/test路路径。
  5. spec.template.spec.containers定义了容器组中的容器,包括容器名、镜像、容器暴露的端口,以及要挂载的卷,在volumeMounts中,name为上述卷名,mountPath为挂载路径,从而容器内部存在/etc/config路径,且可以读取到上述配置文件。

服务定义

Kubernetes Service

默认情况下,Kubernetes集群内的容器是不能被集群外访问的。此时我们需要将应用连接到Service,Service可以对集群内部暴露,也可以对外暴露宿主机端口,还能通过LB VIP暴露服务。

外部访问->Service

针对本项目,对应的Service配置如下。

test-service.yml内容如下。


  1. spec.selector用于连接容器组,与上文中Deployment定义的容器组的labels对应。
  2. spec.type定义Service的暴露方式,NodePort为对集群外暴露,用户通过宿主机端口访问应用,即:
    • 用户->宿主机IP:30777->Service:7777->容器:7777

部署工作

编译jar包

不论使用Maven还是Gradle,生成jar包,此处不细说。

生成Docker镜像

使用以下命令生成Docker镜像,当然,前提是生成镜像的机器上需要有基础镜像openjdk:8-jdk-alpine


同时,需要让所有Kubernetes的Node能获取到该镜像,有两种途径:

  1. 将该镜像打包并在所有Node上加载。

  2. 将该镜像推送到所有Node均能访问的镜像仓库里。
创建Kubernetes资源

分别创建ConfigMap、Deployment、Service。


验证

查看资源对象是否正常。


访问服务。


0

Spring Cloud Feign的超时和重试

Feign

Feign在英语中是伪装的意思,这里Feign可以把Rest的请求进行隐藏,伪装成类似SpringMVC的Controller一样。

这样我们就不用再自己拼接url,拼接参数等等操作,一切都交给Feign去做。

Feign也是Netflix开发的,是一种声明式、模板化的HTTP客户端,Feign可以让我们更加便捷地调用HTTP API。

Feign支持自带注解或JAX-RS注解。SpringCloud对Feign进行了增强,使Feign支持SpringMVC注解,并整合了Ribbon和Hystrix。

超时

在微服务架构中,一个服务对服务的访问至少得配置一个超时时间,不可能请求一个接口等了好几分钟都还没有返回,在设置超时时间后,超时后就认为这次接口请求失败了。

重试

服务B调用服务A,服务A部署了3台机器,现在服务B通过负载均衡的算法,调用到了服务A的机器1,因为服务A的机器1宕机了,请求超时了,可以让服务B再次请求一次服务A的机器1,如果还是不行,再请求服务A的机器2,如果还是不行,就再请求服务A的服务3,这就是重试机制。

Spring Cloud Feign 的重试机制的演进

在早期的Feign也有重试的模块,但是后来发现和Ribbon冲突了,于是SpringCloud团队在后面的版本将Feign的重试设置为NERVER_RETRY了。因此,对于Camden以及以后的版本,Feign的重试可使用如下属性进行配置:

注意,feign的超时时间优先级更高。

超时和重试源码

超时

如果feign没有配置超时时间,则读取ribbon的配置,否则读取feign的超时配置

FeignLoadBalancer.execute(),发送实际的http请求的时候,就会传入设置的超时参数

重试

Feign的重试

Feign本身也具备重试能力,在早期的Spring Cloud中,Feign使用的是 feign.Retryer.Default#Default() ,重试5次。但Feign整合了Ribbon,Ribbon也有重试的能力,此时,就可能会导致行为的混乱。

Spring Cloud意识到了此问题,因此做了改进,将Feign的重试改为 feign.Retryer#NEVER_RETRY ,如需使用Feign的重试,只需使用Ribbon的重试配置即可。

SynchronousMethodHandler.invoke()方法里面,如果抛了异常的话,也会默认根据Retryer进行重试。

Ribbon的重试

因为SpringCloud的Feign重试默认是NEVER_RETRY,所以主要是靠Ribbon的重试机制。

FeignLoadBalancer.getRequestSpecificRetryHandler()方法中,会读取配置的几个参数:OkToRetryOnAllOperations、MaxAutoRetries、MaxAutoRetriesNextServer

LoadBalancerCommand.submit()方法中,读取RetryHandler中配置的参数,会根据请求的情况,是否报错,是否报异常,进行重试的控制

LoadBalancerCommand包含了大量的重试逻辑,这里是判断是否对同一台机器进行重试

重试都会进入retryPolicy方法,判断是否需要进行重试,然后利用rxjava的retry方法进行重试。

对其他机器进行重试

上面的逻辑是服务宕机的时候的重试逻辑,在超时的时候重试逻辑却是在RetryableFeignLoadBalancer里

0

Spring Cloud Feign 如何配置负载均衡策略?

Spring Cloud Feign 如何配置负载均衡策略?

代码示例

1.使用@FeignClient注解发现服务

服务提供者的controller

client消费者端

在使用@FeignClient注解的时候,默认使用了ribbon进行客户端的负载均衡,默认策略随机,若要更改策略,需修改消费者yml中的配置,如下:

可以看到ribbon的策略主要有以下几种:

  1. com.netflix.loadbalancer.RandomRule #配置规则 随机,几个提供者间随机访问
  2. com.netflix.loadbalancer.RoundRobinRule #配置规则 轮询,轮流访问
  3. com.netflix.loadbalancer.RetryRule #配置规则 重试,在一段时间内通过RoundRobinRule选择服务实例,一段时间内没有选择出服务则线程终止
  4. com.netflix.loadbalancer.WeightedResponseTimeRule #配置规则 响应时间权重,根据平均响应时间来计算权重
  5. com.netflix.loadbalancer.BestAvailableRule #配置规则 最空闲连接策略

总结

Spring Cloud Feign 如何配置负载均衡策略到这边就介绍完了,大家需要结合自己实际业务场景,来选择合适的负载均衡策略。

0

Spring Cloud Feign 如何自定义编码器、解码器和客户端?

Spring Cloud Feign 为何需要自定义编码器、解码器?

已有的编解码器无法满足需求。

说明

由Feign的实现原理可知,编码器(Encoder)是用来将RequestBean转换成 Http报文正文,解码器(Decoder)是用来将http server的返回报文转换成Response Bean。

spring cloud feign encoder decoder 图解
spring cloud feign encoder decoder 图解

Spring Cloud Feign 的编码器、解码器和客户端都是支持自定义扩展,可以对请求以及结果和发起请求的过程进行自定义实现,Feign 默认encoder实现是SpringEncoder,默认decocer实现是ResponseEntityDecoder,另外还有一些其它的编解码器。

Encoder/ Decoder 实现 说明
JacksonEncoder,JacksonDecoder 基于 Jackson 格式的持久化转换协议
GsonEncoder,GsonDecoder 基于Google GSON 格式的持久化转换协议
SaxEncoder,SaxDecoder 基于XML 格式的Sax 库持久化转换协议
JAXBEncoder,JAXBDecoder 基于XML 格式的JAXB 库持久化转换协议
ResponseEntityEncoder,ResponseEntityDecoder Spring MVC 基于 ResponseEntity< T > 返回格式的转换协议
SpringEncoder,SpringDecoder 基于Spring MVC HttpMessageConverters 一套机制实现的转换协议 ,应用于Spring Cloud 体系中

所以如果希望支持其他的或者自定义格式就需要编写自己的编码器和解码器。若要实现自己的编码器,需要实现 feign.codec.Encoder 接口,解码器需要实现 feign.codec.Decoder 接口,示例如下:

Spring Cloud Feign 为何需要自定义客户端实现?

默认实现性能差,需要优化。

说明

Feign 默认底层通过JDK 的 java.net.HttpURLConnection 实现了feign.Client接口类,在每次发送请求的时候,都会创建新的HttpURLConnection 链接,这也就是为什么默认情况下Feign的性能很差的原因。可以通过拓展该接口,使用Apache HttpClient 或者OkHttp3等基于连接池的高性能Http客户端。

具体实现请参考这篇文章《Spring Cloud Feign 使用OkHttp或HttpClient覆盖默认客户端》。

总结

实际开发中,每个公司都有一些自己的实现,这些东西搭框架时都已经写好了,自己若只是写写业务代码,是涉及不到这块的。

 

+1

Spring Cloud Feign 使用OkHttp或HttpClient覆盖默认客户端

前言

我们知道,流行的开源Http库的性能均远高于JDK源生的HttpURLConnection,因此实际生产中肯定是用的三方库来发送Http请求。

Feign它提供了feign.Client抽象来发送Http请求,因此使得它拥有良好的扩展性,而恰好Feign的子模块里亦提供了对OkHttp以及Apache HttpClient的整合,本文将教你如何把Feign切换为第三方HC以提高性能。

Feign的模块中有三个关于HC的子模块:feign-okhttp、feign-httpclient、feign-googlehttpclient。

这边以OkHttp为例。

GAV如下

“携带”的okhttp版本号是:3.6.0。(若把Feign调整到最新版本10.7.4,那么它携带的okhttp版本号也就是最新的3.14.6的了)

说明:okhttp虽然目前最新版本是4.x版本的,关于区别你可以简单粗暴的理解:前者是用kotlin改写了,后者还是用Java写的,其它的并无什么变化。
所以,在Server端使用okhttp,请务必使用3.x版本~移动端可酌情使用4.x版本

我们知道Feign最终是通过它的feign.Client这个API去发送远程请求的,而feign.Client是可以在构建的时候由使用者自定义指定的。有了以上理论的支撑,若想切换最终发送Http请求的HC,仅需在构建时使用自己的feign.Client即可。

使用示例

构建Feign时,指定使用OkHttpClient:

当然,如果你已经有现成的定制好的okhttpClient里,直接使用即可,形如这样:

源码解析

feign-okhttp这个jar包内,有且仅有一个类:feign.okhttp.OkHttpClient,它是对feign.Client接口的实现。

这个逻辑不难,其实就一普通的Http请求的发送,不同之处在于进行了两次数据转换:

  • Request之前的转换
  • Response之间的转换

其中,需要特别特别注意的是:请务必确保每次请求都是线程安全的。feign.Client接口的Javadoc也特别强调了这一点~

总结

Apache HttpClient 和 GoogleHttpClient 的实现也是一样,这边就不详述了。

0