Spring Cloud

Spring官方在spring boot的基础上,对Netflix开源组件进一步封装,提供了一个名为Spring Cloud的开源的一套云应用开发工具,一个非常优秀的服务治理框架。另一个非常优秀的服务治理框架 Dubbo。
Spring Cloud专注于提供良好的开箱即用经验的典型用例和可扩展性机制覆盖。

Netflix公司和Pivotal公司

Netflix是一家视频网站,该网站上的美剧应该是最火的。Netflix的微服务大规模的应用,在技术上毫无保留的把一整套微服务架构核心技术栈开源了出来,叫做Netflix OSS。

Pivotal把Netflix开源的一整套核心技术产品线做了一系列的封装,就变成了Spring Cloud,Spring Cloud到现在为止不只有Netflix提供的方案可以集成,但其中Netflix是最成熟的。

Spring Cloud微服务架构

在Spring Cloud微服务系统中,一种常见的负载均衡方式是:客户端的请求首先经过负载均衡(zuul、Ngnix),再到达服务网关(zuul集群),然后再到具体的服务。服务统一注册到高可用的服务注册中心集群,服务的所有的配置文件由配置服务管理,配置服务的配置文件放在git仓库,方便开发人员随时改配置。

Spring Cloud微服务架构中的几个基础服务治理组件:

  • 服务注册中心:Eureka Server
  • 注册服务:Eureka Client
  • 发现服务:Discovery Client
  • 负载均衡:Load Balance、RestTemplate、Ribbon,Feign
  • 断路器:Hystrix,另外Feign自带断路器功能。断路打开后,可以避免连锁故障,fallback方法可以直接返回一个固定值。Turbine可以汇聚监控信息,并提供给Hystrix Dashboard集中展示和监控。
  • 分布式配置中心:Spring Cloud Config,它支持配置服务放在配置服务的内存中(即本地),也支持放在远程Git仓库中。两个角色,config server config client。
  • 消息总线:Spring Cloud Bus,将分布式节点加入消息总线,利用轻量的消息代理(message broker 经纪人)使得应用程序可以高效地解耦通信过程。在众多的开源消息中间件产品中,当前版本的spring cloud bus仅支持两款中间件产品:rabbitmq和kafka两个binder,也可以自己写binder扩展。
  • Api gateway:微服务场景下,将细粒度的服务组合起来提供一个粗粒度的服务,所有请求都导入一个统一的入口,那么整个服务只需要暴露一个api,对外屏蔽了服务端的实现细节,也减少了客户端与服务器的网络调用次数。与业务关系并不大的通用处理逻辑可以从Api gateway中剥离出来,Api gateway仅仅负责服务的编排与结果的组装。这样对一些后端微服务进行了代理,避免了各个后端服务独立管理CORS和验证问题。
  • 路由网关转发和过滤器:zuul,zuul还能过滤做一些安全验证,zuul默认和Ribbon结合实现了负载均衡的功能。Zuul是Netflix 提供的一个开源组件,是致力于在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。zuul静态路由配置规则:
    分布式系统中未使用注册服务时,service1的名称可更换,下面的配置中.path中的**会被添加到.url的后面。若不配置.url,可直接使用service1作为请求路径。

    zuul.routes.service1.url=http://localhost:8002/storage/
    zuul.routes.service1.path=/proxy/**
    
  • 动态路由
    动态路由需要达到配置持久化和动态刷新的效果。即不仅要能满足从spring的配置文件properties加载路由信息,还需要从数据库加载配置。另外一点是,在容器启动后,能动态刷新内存中的路由信息,达到不停机维护路由信息的效果。
  • 数据监控,Spring Boot Actuator 提供了强大的应用自省功能,提供了丰富的 Endpoints 的信息,覆盖 Spring Boot 应用程序运行的方方面面。这些指标都是以 JSON 接口数据的方式呈现。而使用 Spring Boot Admin 可以实现这些 JSON 接口数据的界面展现,结合可视化的 Spring Boot Admin 管理界面,一切显得如此“高大上”。
  • 链路追踪:Sleuth。

Ribbon、Feign与Nginx

Feign与Ribbon区别之一是调用方式不同。Ribbon需要自己构建http请求使用RestTemplate发送给其它服务,步骤繁琐。Feign则是在Ribbon的基础上进行了改进,采用接口的方式将需要调用的其他服务的方法定义成抽象方法。Feign 是一个声明web服务客户端,Feign 是一个使用起来更加方便的HTTP 客戶端,使用起来就像是调用自身工程的方法。Feign包含了ribben,feign是远程调用的,ribbon是做负载均衡。

Nginx是服务端负载均衡,即请求由 nginx 服务器端进行转发。负载均衡、反向代理,代理后端服务器。隐藏真实地址,防火墙,不能外网直接访问,安全性较高。

Ribbon是客户端负载均衡, 是从 eureka 注册中心服务器端上获取服务注册信息列表,缓存到本地,然后在本地实现轮询负载均衡策略,即在客户端实现负载均衡。

应用场景的区别:Nginx 适合于服务器端实现负载均衡 比如 Tomcat ,Ribbon 适合与在微服务中 RPC 远程调用实现本地服务负载均衡,比如 Dubbo、SpringCloud 中都是采用本地负载均衡。

Cloud与Boot与微服务的关系

微服务是一种架构的理念,提出了微服务的设计原则,从理论为具体的技术落地提供了指导思想。
Spring Boot是一套快速配置脚手架,可以基于Spring Boot快速开发单个微服务。Spring Cloud是一个基于Spring Boot实现的服务治理工具包。Spring Boot专注于快速、方便集成的单个微服务个体,Spring Cloud关注全局的服务治理框架。Spring Boot/Cloud是微服务实践的最佳落地方案。

Cloud版本变化

  • Spring Cloud Finchley 在2018年 06 月 19 日正式发布,这次的重大发布主要带来了以下 4 项重大更新。
    (1)新增 Spring Cloud Gateway 组件
    Spring Cloud Gateway 是一个基于 Spring Webflux 和响应式 Netty 的下一代 API 网关,用来替换 Spring Cloud Netflix Zuul。它提供了更加简单的动态路由,以及针对每个路由的过滤器(如地址重写、断路器、添加/删除请求头、限流和安全等)。
    (2)新增 Spring Cloud Function 组件
    Spring Cloud Function 的主要功能如下:
    通过一系列函数推进业务逻辑的实现;
    将业务逻辑的开发生命周期从任何特定运行目标中分离,以便相同的代码可以作为一个 Web 端点、一个流处理器或一个任务来运行;
    支持一个跨 serverless providers 的统一编程模型,并拥有独立运行的能力(本地或 PaaS 平台);
    支持在 serverless providers 上面启用 Spring Boot 特性,如自动配置、依赖注入、指标等;
    (3)兼容 Spring Boot 2.0.x
    Finchley 版本是基于 Spring Boot 2.0.x 构建的,官方建议不要与 Spring Boot 1.5.x 及之前的版本一起工作。
    (4)最低支持 JDK 1.8
    JDK 门槛提高了,1.8 毕竟是现在的主流。
  • Camden。
  • Dalston将于 2018 年 12 月结束生命周期。
  • Edgware伴随着 Spring Boot 1.5.x 的结束而结束生命周期。

参考:
《Spring Cloud微服务实战》
http://blog.didispace.com/
http://blog.csdn.net/forezp/article/details/70148833
http://blog.csdn.net/u010066934/article/details/54232622

Spring Cloud 文档
http://cloud.spring.io/spring-cloud-static/spring-cloud.html#_features

PiggyMetrics微服务编译运行解析
http://blog.csdn.net/nihaomanihao11/article/details/73822681
http://blog.csdn.net/rickiyeat/article/details/60792925

中小型互联网公司微服务实践-经验和教训
http://www.ityouknow.com/springcloud/2017/10/19/micro-service-practice.html

Spring Cloud各个微服务之间用HTTP通信的效率的讨论

Spring cloud的http从几个方面可以优化:1、换成okhttp3。2、应用中不要做异步传输,防止异步等待,如果遇到异步场景,一定要利用好消息队列和缓存。3、如果http1.1,有些场景利用keep-alive,减少连接损耗。4、可以考虑尝试HTTP/2。替换原有的Jackson序列化为更快的或者更适合的序列化方式(例如fastJson,kryo);还有http连接池替换实现等都会有可观的性能提升。
一般场景下spring cloud的性能足以满足大多数需求,最终要看服务拆分是否合理和业务场景的实际需求。
rpc固然速度比http快,但是稳定性、数据传输的可靠性、熔断保护、更方便的链路监控和追踪,这时候HTTP的优势就体现了。
http传输内容上比rpc多了几十字节的头,对于一般都是几k到几十上百k的数据,几十个字节的头影响不大。
如果使用feign,会默认加keepalive也是长链接,就多了http的20字节报文头,但是开发效率却提升了几个档次。
目前很多大型项目多语言共存,http是最通用的协议,可以很好地解决跨语言跨平台兼容性。比如Dubbo协议,无法简单地实现跨语言。http交互只是协议比较重,但不会慢太多,追求单机极致性能的确可以考虑换协议。
微服务的一个重要理念就是水平扩展,慢这个问题通过业务设计还有多实例部署可以很好地提速。http协议很成熟,并且特点熟知:1.支持客户/服务器模式。2.简单快速。3.灵活。4.无连接。5.无状态。也是一个速度与开发成本还有兼容性相互妥协兼顾的结果。

https://www.zhihu.com/question/270355472

spring cloud与dubbo性能对比
http://www.cnblogs.com/chen110xi/p/6349580.html

Eureka配置

eureka.client.register-with-eureka: 表示是否将自己注册到Eureka Server, 默认为true。 如果当前应用就是Eureka Server,则设为 false。
eureka.client.fetch-registry: 表示是否从Eureka Server获取注册信息,默认为true。如果这是一个单点的 Eureka Server,不需要同步其他节点的数据,可以设为false。
eureka.client.serviceUrl.defaultZone:在client端可以指定多个eureka服务,用逗号分隔。
https://www.jianshu.com/p/67fa6bbdbadf

在eureka中注册的服务能互通的前提是各服务间的网络本来就是通的。
主机服务程序在eureka中默认使用哈希值代替ip来作为识别码,可以配置eureka.instance.preferIpAddress=true来指定使用IP来作为识别码。

是纯正的 servlet 应用,需部署到web容器中。

Hystrix

使用feign时断路器不起作用,检查在引入了openfeign依赖后,不要再引入ribbon、hystrix。

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>

Spring Boot 调优

java启动时参数
内存:java -Xms64m -Xmx512m -jar xxx.jar
GC线程数:-XX:ParallelGCThreads=2
关掉CompilerThread线程 -Djava.compiler=NONE
JIT是JVM为了优化执行频率比较高的字节码设计的技术。JIT把字节码编译为机器码,之后执行则不需要解释字节码,直接运行机器码即可。如果服务没有什么负载,即使不优化也不受影响,可以优化把JIT关掉。

application.properties中配置:
并发线程数:server.tomcat.max-threads = 10
Spring boot自带的tomcat线程数默认值为200个。

Web容器

Spring Boot 默认使用 Tomcat 来作为内嵌的 Servlet 容器,可以将 Web 服务器切换到 Undertow 来提高应用性能。Undertow 是一个采用 Java 开发的灵活的高性能 Web 服务器,是红帽公司的开源产品,是 Wildfly 默认的 Web 服务器,提供包括阻塞和基于 NIO 的非堵塞机制。下面从依赖信息里移除 Tomcat 配置,并添加 Undertow:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>

        <exclusions>
            <exclusion>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-tomcat</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-undertow</artifactId>
    </dependency>
</dependencies>

一些查看命令
jmap
jstack:jstack查看内存发现一些线程在运行,主要有GC task thread,http-nio thread,C2 CompilerThread。
GC task thread:垃圾回收线程
http-nio thread:tomcat网络处理网络请求线程
C2CompilerThread:JIT编译线程,动态编译Java运行代码,C2表示编译的是server端代码
JVM默认线程栈大小是1M。

https://blog.csdn.net/github_32521685/article/details/50463895
https://blog.csdn.net/lijingyao8206/article/details/80432402

服务器

undertow、jetty和tomcat是javaweb项目当下最火的三款服务器,spring boot 完美集成了tomcat、jetty和undertow。
jetty和undertow都是基于NIO实现的高并发轻量级的服务器,支持servlet3.1和websocket。

远程调试和JVM工具远程连接
https://www.cnblogs.com/superfj/p/8667977.html

Spring Cloud Bus

Spring Cloud是按照spring的配置,对一系列微服务框架的集成。
Spring生态消息中间件相关的几个项目有:spring messaging、spring integration、spring cloud stream、spring cloud bus。

Spring Cloud Bus

spring cloud bus是Spring Cloud中的微服务框架之一,用于实现微服务之间的通信。
spring cloud bus整合java的事件处理机制和消息中间件消息的发送和接受,主要由发送端、接收端和事件组成。针对不同的业务需求,可以设置不同的事件,发送端发送事件,接收端接受相应的事件,并进行相应的处理。
Spring Cloud Bus将Spring的事件处理机制和消息中间件消息的发送和接收整合起来,可以轻松的将分布式应用中连接有消息中间件的多个服务节点连接起来,实现多个服务节点消息互通。
Spring Cloud Bus 将分布式的节点用轻量的消息代理连接起来。它可以用于广播配置文件的更改,或者服务之间的通讯,也可以用于监控。
Spring Cloud Bus 是用Spring Cloud Stream来实现消息的发布订阅的,所以配置中有许多stream相关的配置。
spring cloud bus 内置了两个endpoint,一个是/bus/refresh,一个是/bus/env,它们被认为是actuator的env和refresh的分布式版本。
Spring Cloud Bus 默认订阅发布的topic是springCloudBus。input channel是springCloudBusInput,用来订阅处理消息。output channel是springCloudBusOutput,用来发布消息到总线。比如/bus/refresh刷新配置事件,就是通过springCloudBusOutput channel将事件写入到springCloudBus这个topic中去。

Spring Cloud Bus 扩展消息总线
https://www.jianshu.com/p/093ed9816993
使用kafka作为springcloud bus的消息总线,以及如何自定义总线事件
http://www.voidcn.com/article/p-phpbizrd-bnz.html

Spring Cloud Stream

Spring Cloud Stream是一个构建消息驱动应用的微服务框架。它构建在Spring Boot之上,通过Spring Integration提供了和消息代理的连接。Spring Cloud Stream为一些供应商的消息中间件产品提供了个性化的自动化配置实现(目前仅支持RabbitMQ和Kafka),同时引入了持久发布订阅、消费者组和分区的概念。
一个业务会牵扯到多个任务,任务之间是通过事件触发的,这就是Spring Cloud stream要干的事。
可以将@EnableBinding注释添加到应用程序,以便立即连接到消息代理,并且可以将@StreamListener添加到方法中,以使其接收流处理的事件。

Spring Cloud Stream初窥
http://blog.csdn.net/u012702547/article/details/78652554
使用 Spring Cloud Stream 构建消息驱动微服务
https://www.jianshu.com/p/fb7d11c7f798

Spring Integration

集成Spring应用之间的系统,Spring提供了分布式的调用,方式有RMI,HttpInvok,Hessian和Burlap。
Spring Ingegration 提供了基于Spring的企业集成模式(EIP,Enterprise Integration Patterns)的实现。Spring Integration 主要解决的问题是不同系统之间交互的问题,通过异步消息驱动来达到交互系统之间的松耦合。
Spring Integration 通过更高层次的抽象把任何的数据通过管道一样的把数据导向下一个需要的地方,把各种系统杂合到一起,组成一个无缝的系统。

Spring Integration 组成

Spring Integration 主要有Message、Channel、EndPoint组成:
Message
Message是用来在不同部分之间传递的数据。由两部分组成:消息头(header)和消息体(payload 负载)。消息头表示的元数据就是解释消息体的内容,包含消息Id、时间戳、优先级等信息。消息体可以是任何数据类型,是真正要交互的数据。
Channel
消息生产者发送消息到通道(Channel),消息消费者从通道(Channel)接收消息。顶级接口MessageChannel中提供了send方法发送消息,它有两大子接口:PollableChannel (可轮询通道)和SubscribableChannel(可订阅通道)。Channel可以对Message组件解耦,可以在Channel上设置拦截器,拦截器作用在Channel上,可以实现编解码、加解密、鉴权、日志、监控等操作。
EndPoint
消息端点(Message EndPoint)是处理消息(Message)的组件,它还可以控制通道的路由。在Channel中不能处理消息,对消息的处理只能在这里完成。

通道

PollableChannel具备轮询获取消息的能力,其中提供了receive方法。
SubscribableChannel发送消息给订阅者,其中提供了subscribe和unsubscribe方法。
常用的消息通道:
DirectChannel是Spring Integration默认的消息通道,它允许将消息发送给为一个订阅者,然后阻碍发送直到消息被接收。
PublishSubscribeChannel允许广播消息给所有订阅者。
QueueChannel允许消息接收者轮询获取消息到一个队列。
PriorityChannel可按照消息头priority优先级将数据存储到队列。
RendezvousChannel确保每一个接收者都接收到消息后再发送消息。
ExecutorChannel可绑定一个多线程的task executor。

通道拦截器

Spring Integration给消息通道提供了通道拦截器(ChannelInterceptor),用来拦截发送和接收消息的操作。只需要实现ChannelInterceptor接口。
通过如下代码给所有的channel增加拦截器 channel.addInterceptor(
channelInterceptor1);

消息端点

可用的消息端点有以下几类:
(1) Channel Adapter
通道适配器(Channel Adapter)是一种连接外部系统或传输协议的端点(EndPoint),可以分为入站(inbound)和出站(outbound)。 通道适配器是单向的,入站通道适配器只支持接收消息,出站通道适配器只支持输出消息。

Spring Integration内置了如下的适配器:RabbitMQ、Feed、File、FTP/SFTP、Gemfire、HTTP、TCP/UDP、JDBC、JPA、JMS、Mail、MongoDB、Redis、RMI、Twitter、XMPP、WebServices(SOAP、REST)、WebSocket。
(2) Gateway
消息网关(Gateway)类似于Adapter,但是提供了双向的请求/返回集成方式,也分为入站(inbound)和出站(outbound)。 Spring Integration 对Adapter都提供了相应的Gateway。
(3) Service Activator
Service Activator 可调用Spring的Bean来处理消息,并将处理后的结果输出到指定的消息通道。
(4) Router
路由(Router) 可根据消息体内容(Payload Type Router)、消息头的值(Header Value Router) 以及定义好的接收表(Recipient List Router) 作为条件,来决定消息传递到的通道。
(5) Filter
过滤器(Filter) 类似于路由(Router),不同的是过滤器不决定消息路由到哪里,而是决定消息是否可以传递给消息通道。
(6) Splitter
拆分器(Splitter)将消息拆分为几个部分单独处理,拆分器处理的返回值是一个集合或者数组。
(7) Aggregator
聚合器(Aggregator)与拆分器相反,它接收一个java.util.List作为参数,将多个消息合并为一个消息。
(8) Enricher
当我们从外部获得消息后,需要增加额外的消息到已有的消息中,这时就需要使用消息增强器(Enricher)。消息增强器主要有消息体
增强器(Payload Enricher)和消息头增强器(Header Enricher)两种。
(9) Transformer
转换器(Transformer)是对获得的消息进行一定的转换处理(如数据格式转换).
(10) Bridge
使用连接桥(Bridge)可以简单的将两个消息通道连接起来。

IntegrationFlow

Spring Integration 还提供了一个 IntegrationFlow 来定义系统继承流程。

参考:
Spring Integration集成框架之Message-Channel
https://my.oschina.net/zhzhenqin/blog/86586
Spring Cloud Stream Messaging Annotations
http://blog.sisopipo.com/2017/02/21/spring-cloud-stream-messaging-annotations.html
在SpringBoot中集成MQTT
http://webfuse.cn/2017/08/17/%E5%9C%A8SpringBoot%E4%B8%AD%E9%9B%86%E6%88%90MQTT/
Spring整合MQTT
http://blog.csdn.net/u010784959/article/details/76416246
在spring web中启动mqtt
https://my.oschina.net/youway/blog/521197?p=1
MQTT Java客户端Eclipse paho实现数据的发送和接收
http://blog.csdn.net/ty497122758/article/details/77281247/
MQTT和spring的整合(生产者和消费者)
http://blog.csdn.net/zhang89xiao/article/details/51871973
http://blog.csdn.net/zhang89xiao/article/details/51871624

spring-integration-mqtt

Spring Integration Reference Manual
https://docs.spring.io/spring-integration/reference/html/index.html
MQTT Support Part V. Integration Endpoints
https://docs.spring.io/spring-integration/reference/html/mqtt.html
Eclipse Paho Java Client
https://www.eclipse.org/paho/clients/java/
http://www.eclipse.org/paho/files/javadoc/index.html

多次报下述错误,并且观察到MqttPahoMessageHandler实例地址是同一个:
error occurred in message handler [org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler@1a301c52]; nested exception is Too many publishes in progress (32202)

# 客户端心跳消息的最大并发数
#max_inflight_messages 10

https://stackoverflow.com/questions/38751546/send-many-publish-message-too-many-publishes-in-progress-error
Paho MQTT client, max in-flight messages for QoS > 0
https://stackoverflow.com/questions/29692065/mqtt-exception-too-many-publishes-in-progress-32202
https://www.jianshu.com/p/6e314d677f2c

在Springboot中使用MQTT收发不同主题的消息
http://www.docin.com/p-2102131938.html
Spring集成MQTT推送消息
https://www.jianshu.com/p/6b60858b7d44

Spring Boot 异步

当某个请求执行非常耗时,当有大量访问该请求的时候,再访问请求其他服务时,会出现没有连接使用的情况。主要原因是,容器中线程的数量是一定的,这些线程都正在处理请求服务的时候,再有请求进来则没有多余的连接可用了,只能拒绝连接。

SpringBoot异步机制可以使请求到controller中时,容器线程直接返回,同时使用非web线程来执行耗时的服务,处理结束后果再将请求返回给客户端。

Servlet 3.0 新增了请求的异步处理,Spring 3.2 也在此基础上做了封装处理。

Spring通过任务执行器(TaskExecutor)来实现多线程和并发编程。使用ThreadPoolTaskExecutor可实现一个基于线程池的TaskExecutor。Executor框架是在 Java 5 中引入的,其内部使用了线程池机制。

在 Spring Boot 框架中,可以在配置类中添加@EnableAsync开始对异步任务的支持,并将相应的方法使用@Async注解来声明为一个异步任务。

对于@Transactional、@Async等注解, Spring扫描时具有这些注解方法的类时,生成一个代理类由代理类去执行,所以异步方法必须在类外部调用,并且@Async所修饰的不能是static方法。获取得到执行结果则可设置异步函数返回类型为Future

@Component  
public class MyAsyncTask {  
    @Async  
    public Future task1() throws InterruptedException{  
        return new AsyncResult("task1执行完毕");  
    }  

Controller的方法可以很快返回一个Callable或Deferredresult,同时Servlet容器的主线程退出和释放,但这并不意味着客户端收到了一个响应。

//Callable
@Controller
public class DefaultController {
    @RequestMapping("/async/test")
    @ResponseBody
    public Callable callable() {
        //调用后生成一个非web线程来执行部分代码。
        return new Callable() {
            @Override
            public String call() throws Exception {
                Thread.sleep(3 * 1000L);
                return "finish" + System.currentTimeMillis();
            }
        };
    }
}

//DeferredResult
@RestController  
public class AsyncDeferredController {  
    private final TaskService taskService;  
      
    @Autowired  
    public AsyncDeferredController(TaskService taskService) {  
        this.taskService = taskService;  
    }  
      
    @RequestMapping(value = "/deferred", method = RequestMethod.GET, produces = "text/html")  
    public DeferredResult executeSlowTask() {  
        DeferredResult deferredResult = new DeferredResult<>();  
        CompletableFuture.supplyAsync(taskService::execute).whenCompleteAsync((result, throwable) -> deferredResult.setResult(result));  

        return deferredResult;  
    }  
}  

Callable的其它示例:

 public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable callable = new CallableImpl("my callable test!");
        FutureTask task = new FutureTask<>(callable);
        new Thread(task).start();
        //调用get()会阻塞主线程。
        String result = task.get();
    }

Callable和Deferredresult的区别:
java.util.concurrent.Callable和Deferredresult效果都是释放容器线程,在另一个线程中运行长时间的任务。不同的是谁管理执行任务的线程:Callable在执行线程完成时返回;Deferredresult是通过设置返回对象值返回,可以在任何地方控制返回。

https://blog.csdn.net/he90227/article/details/52262163
Spring MVC 异步处理请求,提高程序性能

定时任务

1.在程序入口类上添加类注解 @EnableScheduling 来启用计划任务
2.在定时任务的类上加上类注解 @Component,在具体的定时任务方法上加上注解 @Scheduled 启动该定时任务。
@Scheduled(initialDelay=10000, fixedDelay=10000) //10 seconds, or use cron express

默认所有的@Scheduled都是串行执行的,并行需要继承SchedulingConfigurer类并重写其方法,实现并行任务。

@Configuration
@EnableScheduling
public class ScheduleConfig implements SchedulingConfigurer {
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(taskExecutor());
    }

    @Bean(destroyMethod="shutdown")
    public Executor taskExecutor() {
        return Executors.newScheduledThreadPool(100);
    }
}

测试发现,RestController中接收处理请求每次所处的线程不一样,Mqtt处理请求也是单独的线程。

详解SpringBoot Schedule配置
http://www.cfei.net/archives/88832
Spring Boot 异步请求(Servlet 3.0)
https://blog.csdn.net/catoop/article/details/51034866

Spring Boot 应用属性配置

Spring Boot程序默认从application.properties或者application.yaml读取配置。

变量名引用

在application.properties文件中,使用${变量名}引用变量。例如:
db.name=db1
spring.data.mongodb.uri=mongodb://${db.username}:${db.password}@${db.url}:${db.port}/${db.name}
还可以自定义 application.properties 进行外部配置。
https://blog.csdn.net/fanhenghui/article/details/77447918

配置日志

一般程序入口要有日志,记录传入的数据是什么;部分重要的处理逻辑要有日志输出;程序出口也要有日志,记录其最终的处理结果。这样在解决生产上的问题时,可以很快的定位问题的位置,是传入数据的问题还是代码逻辑写错了。
在生产上严禁使用System.out输出,性能太低,原因是System.out输出会导致线程等待(同步),而使用Logger输出线程不等待日志的输出(异步),而继续执行。

#debug=true
logging.file=yirui_clock_server.log
logging.level.com.yirui.clock.server=INFO
logging.level.root=INFO
logging.level.org.springframework.data.mongodb=INFO

配置ssl、配置https

#配置.p12自签名证书
server.ssl.key-store: /etc/letsencrypt/live/xxxx/keystore.p12
server.ssl.key-store-password: jsqc
server.ssl.keyStoreType: PKCS12
server.ssl.keyAlias: tomcat
#配置.pfx证书
server:
  ssl:
    key-store: /opt/ssl/tomcat/xxx.pfx
    key-store-password: 25tUhz9Q
    keyStoreType: PKCS12

容器配置

Spring Boot 内置的容器有三个分别是Undertow、Jetty、Tomcat。Spring Boot 对这三个容器分别进行了实现,它们上层接口都是EmbeddedServletContainerFactory。

对于内置容器的定制与优化主要有两种方式:第一种方式是通过配置文件来配置,另外一种是通过码代码的方式。
配置的核心内容参考org.springframework.boot.autoconfigure.web.ServerProperties这个服务属性类。

spring boot 限制初始值大小及参数中文详解 https://www.jianshu.com/p/552e49571893

静态资源路径配置

spring.mvc.static-path-pattern 用于阐述静态资源的HTTP映射路径
spring.resources.static-locations 用于描述静态资源文件的存放位置

以jar包发布项目时,可以把静态资源文件存储为与jar包同级的static目录中,因此需要在jar包中配置静态资源路径。
#设置静态资源路径,多个以逗号分隔
spring.resources.static-locations=classpath:static/,file:static/

如果在不同的目录中存在多个配置文件,读取顺序是:
1、config/application.properties(项目根目录中config目录下)
2、config/application.yml
3、application.properties(项目根目录下)
4、application.yml
5、resources/config/application.properties(项目resources目录中config目录下)
6、resources/config/application.yml
7、resources/application.properties(项目的resources目录下)
8、resources/application.yml

或者Spring程序会按优先级从下面这些路径来加载application.properties配置文件:
当前目录下的/config目录
当前目录
classpath里的/config目录
classpath 跟目录

SpringBoot项目默认配置文件放置在“项目的resources目录下”。
同一个目录下,先读取application.properties,再读取gapplication.yml。
同一个配置属性,在多个配置文件都配置了,使用第一个读取到的值,后面读取的值不覆盖前面读取到的值。

指定配置文件

java -jar xxx.jar –spring.config.location=classpath:/default.properties,classpath:/override.properties
或者
java -jar -Dspring.config.location=D:\config\config.properties xxx.jar
还能在代码里指定
@SpringBootApplication
@PropertySource(value={“file:config.properties”})

运行时命令行中指定配置文件,jar包中的配置文件则全部失效了。

java -jar -Dspring.config.location=application-outside.properties 
xxx.jar

从命令行指定配置项的优先级最高,不过可以通过setAddCommandLineProperties来禁用命令行配置:
SpringApplication.setAddCommandLineProperties(false);

按Profile不同环境读取不同配置,可以在application.properties中指定使用哪一个文件
spring.profiles.active = dev
也可以在运行的时候手动指定:
java -jar
xxx.jar –spring.profiles.active = prod