首页 文章 精选 留言 我的

精选列表

搜索[SpringBoot],共4254篇文章
优秀的个人博客,低调大师

SpringBoot|第四章:日志管理

Spring Boot在所有内部日志中使用Commons Logging,但是默认配置也提供了对常用日志的支持,如:Java Util Logging,Log4J,Log4J2和Logback。每种Logger都可以通过配置使用控制台或者文件输出日志内容。 格式化日志 默认的日志输出如下: 12016-04-1308:23:50.120INFO 37397--- [ main] org.hibernate.Version : HHH000412: Hibernate Core {4.3.11.Final} 输出内容元素具体如下: 时间日期 — 精确到毫秒 日志级别 — ERROR, WARN, INFO, DEBUG or TRACE 进程ID 分隔符 —---标识实际日志的开始 线程名 — 方括号括起来(可能会截断控制台输出) Logger名 — 通常使用源代码的类名 日志内容 控制台输出 在Spring Boot中默认配置了ERROR、WARN和INFO级别的日志输出到控制台。 我们可以通过两种方式切换至DEBUG级别: 在运行命令后加入--debug标志,如:$ java -jar myapp.jar --debug 在application.properties中配置debug=true,该属性置为true的时候,核心Logger(包含嵌入式容器、hibernate、spring)会输出更多内容,但是你自己应用的日志并不会输出为DEBUG级别。 多彩输出 如果你的终端支持ANSI,设置彩色输出会让日志更具可读性。通过在application.properties中设置spring.output.ansi.enabled参数来支持。 NEVER:禁用ANSI-colored输出(默认项) DETECT:会检查终端是否支持ANSI,是的话就采用彩色输出(推荐项) ALWAYS:总是使用ANSI-colored格式输出,若终端不支持的时候,会有很多干扰信息,不推荐使用 文件输出 Spring Boot默认配置只会输出到控制台,并不会记录到文件中,但是我们通常生产环境使用时都需要以文件方式记录。 若要增加文件输出,需要在application.properties中配置logging.file或logging.path属性。 logging.file,设置文件,可以是绝对路径,也可以是相对路径。如:logging.file=my.log logging.path,设置目录,会在该目录下创建spring.log文件,并写入日志内容,如:logging.path=/var/log 日志文件会在10Mb大小的时候被截断,产生新的日志文件,默认级别为:ERROR、WARN、INFO 级别控制 在Spring Boot中只需要在application.properties中进行配置完成日志记录的级别控制。 配置格式:logging.level.*=LEVEL logging.level:日志级别控制前缀,*为包名或Logger名 LEVEL:选项TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF 举例: logging.level.com.didispace=DEBUG:com.didispace包下所有class以DEBUG级别输出 logging.level.root=WARN:root日志以WARN级别输出 自定义日志配置 由于日志服务一般都在ApplicationContext创建前就初始化了,它并不是必须通过Spring的配置文件控制。因此通过系统属性和传统的Spring Boot外部配置文件依然可以很好的支持日志控制和管理。 根据不同的日志系统,你可以按如下规则组织配置文件名,就能被正确加载: Logback:logback-spring.xml,logback-spring.groovy,logback.xml,logback.groovy Log4j:log4j-spring.properties,log4j-spring.xml,log4j.properties,log4j.xml Log4j2:log4j2-spring.xml,log4j2.xml JDK (Java Util Logging):logging.properties Spring Boot官方推荐优先使用带有-spring的文件名作为你的日志配置(如使用logback-spring.xml,而不是logback.xml) 自定义输出格式 在Spring Boot中可以通过在application.properties配置如下参数控制输出格式: logging.pattern.console:定义输出到控制台的样式(不支持JDK Logger) logging.pattern.file:定义输出到文件的样式(不支持JDK Logger) 如果你现在在JAVA这条路上挣扎,也想在IT行业拿高薪,可以参加我们的训练营课程,选择最适合自己的课程学习,技术大牛亲授,7个月后,进入名企拿高薪。我们的课程内容有:Java工程化、高性能及分布式、高性能、高架构、性能调优、Spring,MyBatis,Netty源码分析和大数据等多个知识点。如果你想拿高薪的,想学习的,想就业前景好的,想跟别人竞争能取得优势的,想进阿里面试但担心面试不过的,你都可以来,q群号为:835638062 注:加群要求 1、具有1-5工作经验的,面对目前流行的技术不知从何下手,需要突破技术瓶颈的可以加。 2、在公司待久了,过得很安逸,但跳槽时面试碰壁。需要在短时间内进修、跳槽拿高薪的可以加。 3、如果没有工作经验,但基础非常扎实,对java工作机制,常用设计思想,常用java开发框架掌握熟练的,可以加。 4、觉得自己很牛B,一般需求都能搞定。但是所学的知识点没有系统化,很难在技术领域继续突破的可以加。 5.阿里Java高级大牛直播讲解知识点,分享知识,多年工作经验的梳理和总结,带着大家全面、科学地建立自己的技术体系和技术认知!

优秀的个人博客,低调大师

SpringBoot实战(三)之使用RestFul Web服务

一、导入maven依赖 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.springframework</groupId> <artifactId>gs-consuming-rest</artifactId> <version>0.1.0</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.8.RELEASE</version> </parent> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> 二、构建实体 Quote.java package hello; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) public class Quote { private String type; private Value value; public Quote() { } public String getType() { return type; } public void setType(String type) { this.type = type; } public Value getValue() { return value; } public void setValue(Value value) { this.value = value; } @Override public String toString() { return "Quote{" + "type='" + type + '\'' + ", value=" + value + '}'; } } Value.java package hello; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) public class Value { private Long id; private String quote; public Value() { } public Long getId() { return this.id; } public String getQuote() { return this.quote; } public void setId(Long id) { this.id = id; } public void setQuote(String quote) { this.quote = quote; } @Override public String toString() { return "Value{" + "id=" + id + ", quote='" + quote + '\'' + '}'; } } @JsonIgnore注解用来忽略某些字段,可以用在Field或者Getter方法上,用在Setter方法时,和Filed效果一样。这个注解只能用在POJO存在的字段要忽略的情况,不能满足现在需要的情况。 @JsonIgnoreProperties(ignoreUnknown = true),将这个注解写在类上之后,就会忽略类中不存在的字段,可以满足当前的需要。这个注解还可以指定要忽略的字段。使用方法如下: @JsonIgnoreProperties({ "internalId", "secretKey" })指定的字段不会被序列化和反序列化。 三、编写启动类 package hello; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.client.RestTemplate; public class Application { private static final Logger log = LoggerFactory.getLogger(Application.class); public static void main(String args[]) { RestTemplate restTemplate = new RestTemplate(); Quote quote = restTemplate.getForObject("http://gturnquist-quoters.cfapps.io/api/random", Quote.class); log.info(quote.toString()); } } 运行后控制台显示结果为: package hello; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @SpringBootApplication public class Application { private static final Logger log = LoggerFactory.getLogger(Application.class); public static void main(String args[]) { SpringApplication.run(Application.class); } @Bean public RestTemplate restTemplate(RestTemplateBuilder builder) { return builder.build(); } @Bean public CommandLineRunner run(RestTemplate restTemplate) throws Exception { return args -> { Quote quote = restTemplate.getForObject( "http://gturnquist-quoters.cfapps.io/api/random", Quote.class); log.info(quote.toString()); }; } } 它RestTemplateBuilder是由Spring注入的,如果你使用它来创建一个,RestTemplate那么你将受益于Spring Boot中带有消息转换器和请求工厂的所有自动配置。我们还将其提取RestTemplate为a@Bean以使其更容易测试(可以通过这种方式更容易地进行模拟)。 最后执行mvn clean install或者mvn install,生成jar包,通过cmd命令执行java -jar gs-consume-rest-0.1.0.jar 出现如红色标记处,表示成功开发一个RestFul客户端。

优秀的个人博客,低调大师

SpringBoot-11-之从本地到线上

一、本地:[注]现在对于已经有了服务器和域名(已备案)的你,可以继续阅读 1.打包项目为jar 打包项目 2.备份数据库 备份数据库:mysqldump -uroot -p密码 toly>toly.sql 二、服务器: [1]将数据库拷贝到服务器 [2]进入mysql后创建同名数据库:mysql> create database toly [3]使用数据库:mysql> use toly [4]导入数据库:mysql> source 路径名\toly.sql 数据库恢复 运行:需要java环境 java -jar 文件路径 三、访问:把一直以来的localhost换成域名就行了

优秀的个人博客,低调大师

初窥RabbitMQ消息中间及SpringBoot整合

一:RabbitMQ简介 RabbitMQ介绍 RabbitMQ 即一个消息队列,主要是用来实现应用程序的异步和解耦,同时也能起到消息缓冲,消息分发的作用。 消息中间件最主要的作用是解耦,中间件最标准的用法是生产者生产消息传送到队列,消费者从队列中拿取消息并处理,生产者不用关心是谁来消费,消费者不用关心谁在生产消息,从而达到解耦的目的。在分布式的系统中,消息队列也会被用在很多其它的方面,比如:分布式事务的支持,RPC的调用等等。 RabbitMQ是实现AMQP(高级消息队列协议)的消息中间件的一种,最初起源于金融系统,用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。RabbitMQ主要是为了实现系统之间的双向解耦而实现的。当生产者大量产生数据时,消费者无法快速消费,那么需要一个中间层。保存这个数据。 AMQP,即Advanced Message Queuing Protocol,高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。消息中间件主要用于组件之间的解耦,消息的发送者无需知道消息使用者的存在,反之亦然。AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。 RabbitMQ是一个开源的AMQP实现,服务器端用Erlang语言编写,支持多种客户端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等,支持AJAX。 运行机制 通常我们谈到队列服务, 会有三个概念: 发消息者、队列、收消息者,RabbitMQ 在这个基本概念之上, 多做了一层抽象, 在发消息者和 队列之间, 加入了交换器 (Exchange). 这样发消息者和队列就没有直接联系, 转而变成发消息者把消息给交换器, 交换器根据调度策略再把消息再给队列。 左侧 P 代表 生产者,也就是往 RabbitMQ 发消息的程序。 中间即是 RabbitMQ,其中包括了 交换机 和 队列。 右侧 C 代表 消费者,也就是往 RabbitMQ 拿消息的程序。 那么,其中比较重要的概念有 4 个,分别为:虚拟主机,交换机,队列,和绑定。 虚拟主机:一个虚拟主机持有一组交换机、队列和绑定。为什么需要多个虚拟主机呢?很简单,RabbitMQ当中,用户只能在虚拟主机的粒度进行权限控制。 因此,如果需要禁止A组访问B组的交换机/队列/绑定,必须为A和B分别创建一个虚拟主机。每一个RabbitMQ服务器都有一个默认的虚拟主机“/”。 交换机:Exchange 用于转发消息,但是它不会做存储 ,如果没有 Queue bind 到 Exchange 的话,它会直接丢弃掉 Producer 发送过来的消息。 这里有一个比较重要的概念:路由键(routing_key) 。消息到交换机的时候,交互机会转发到对应的队列中,那么究竟转发到哪个队列,就要根据该路由键。 绑定:也就是交换机需要和队列相绑定,这其中如上图所示,是多对多的关系。 交换机 交换机的功能主要是接收消息并且转发到绑定的队列,交换机不存储消息,在启用ack模式后,交换机找不到队列会返回错误。交换机有四种类型:Direct, topic, Headers and Fanout Direct:direct 类型的行为是”先匹配, 再投送”. 即在绑定时设定一个 routing_key, 消息的routing_key 匹配时, 才会被交换器投送到绑定的队列中去. Topic:按规则转发消息(最灵活) Headers:设置header attribute参数类型的交换机 Fanout:转发消息到所有绑定队列(广播:忽略routing_key ) 一句话总结 RabbitMQ简单来说,就是生产者发送消息到虚拟主机,虚拟主机把消息交给指定的交换机,交换机按照规则扔给消息队列进行存储,消息队列等待消费者来消费。 由此我想到了商品买卖:厂家生产商品卖给批发部,批发部交给指定的超市出售,超市按照售价摆放在门店,并等待顾客上门购买。 完美~~ 二:RabbitMQ安装 因为RabbitMQ是由erlang语言写的,就像Java程序需要jdk环境一样,运行RabbitMQ也需要erlang环境。 1.安装erlang 环境:Centos7.4 下载地址:http://erlang.org/download/ 在Linux终端运行命令行 下载: wget http://erlang.org/download/otp_src_18.3.tar.gz 下载一定要认准otp_src_字样。 解压: tar -zxvf otp_src_18.3.tar.gz 进入解压后的根目录: ./configure --prefix=/usr/local/erlang --enable-hipe --enable-threads --enable-smp-support --enable-kernel-poll --without-javac make && make install 上面有点慢。 把erlang加入环境变量: vi /etc/profile export ERLANG=/usr/local/erlang/erlang export PATH=$ERLANG/bin:$PATH 使环境变量生效 source /etc/profile 然后,我们测试下是否安装成功: [root@yueshutong ~]# erl Erlang/OTP 18 [erts-7.3] [source] [64-bit] [async-threads:10] [hipe] [kernel-poll:false] Eshell V7.3 (abort with ^G) 1> 2.安装RabbitMQ 下载地址:http://www.rabbitmq.com/releases/rabbitmq-server/v3.6.1/ 下载: wget http://www.rabbitmq.com/releases/rabbitmq-server/v3.6.1/rabbitmq-server-generic-unix-3.6.1.tar.xz 对于下载xz包进行解压,首先先下载xz压缩工具: yum install xz 对rabbitmq包进行解压: xz -d xz -d rabbitmq-server-generic-unix-3.6.1.tar.xz tar -xvf rabbitmq-server-generic-unix-3.6.1.tar 随后在sbin目录启用MQ管理方式: ./rabbitmq-plugins enable rabbitmq_management #启动后台管理 ./rabbitmq-server -detached #后台运行rabbitmq 添加用户和权限 默认网页guest用户是不允许访问的,需要增加一个用户修改一下权限,代码如下: 添加用户: ./rabbitmqctl add_user admin admin 添加权限: ./rabbitmqctl set_permissions -p "/" admin ".*" ".*" ".*" 修改用户角色: ./rabbitmqctl set_user_tags admin administrator 然后就可以远程访问了,然后可直接配置用户权限等信息。 验证 访问http://localhost:15672/,输入admin用户密码,登录成功! 三:整合Spring Boot 新建一个1.5版本的Spring boot项目,选择rabbitmq+web模块。 1.导入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> 2.yml配置rabbitmq virtual-host: / 默认就是斜杠,具体查看rabbitmq后台admin栏。如果默认这行不用写。 spring: rabbitmq: host: 127.0.0.1 username: admin password: admin virtual-host: / #可以不用写 3.发送direct消息 direct 类型的行为是”先匹配, 再投送”. 即在绑定时设定一个 routing_key, 消息的routing_key 匹配时, 才会被交换器投送到绑定的队列中去. 1.我们在RabbitMQ后台新建一个交换机,demo-direct交换机名,direct交换机类型,Durable持久化: 2.再新建一个消息队列,取名为demo: 3.点击demo-direct交换机进入绑定消息队列demo: 如果没有指定routingkey,消息队列的名称就是routingkey 4.绑定成功后查看: 你可以直接在交换机页面下方的Publish message发送消息,在消息队列的Get message查看消息,不过我们实际生产比较多使用编程: 在Spring Boot创建单元测试 demo-direct交换机名 demo是该交换机绑定的消息队列名 发送消息 @Autowired RabbitTemplate rabbitTemplate; @Test public void contextLoads() { //message需要自己构造一个,定义消息体内容和消息体 //rabbitTemplate.send(exchange,routingkey,message); Map map = new HashMap(); map.put("key","值"); map.put("msg",true); //对象被默认序列化后发送 rabbitTemplate.convertAndSend("demo-direct","demo",map); } 此时发送消息我们在rabbitmq网页发现消息是经序列化后的,我们如果想改变序列化机制为JSON,也很简单,只需要注入一个人Bean: @Configuration public class MyAMQPConfig { @Bean public MessageConverter messageConverter(){ return new Jackson2JsonMessageConverter(); } } 然后我们再启动测试,发送。 接收消息 demo是消息队列名,也就是消费者只需要得到消息队列的名字就可以接收队列中的消息。 @Test public void receive(){ Object o = rabbitTemplate.receiveAndConvert("demo"); System.out.println(o.getClass()); System.out.println(o); } 查看打印 class java.util.HashMap {msg=true, key=值} 4.广播fanout消息 转发消息到所有绑定队列(广播:忽略routing_key ) 1.首先我们需要在RabbitMQ后台创建一个广播交换机: 2.再创建一些(demo1,demo2)消息队列,以一个作为演示: 3.进入创建的交换机页面: 4.交换机与消息队列(demo1,demo2)进行绑定: 如果没有指定routingkey,消息队列的名称就是routingkey 在Spring Boot创建单元测试 注意在广播模式下会忽略忽略routing_key 发送消息 @Autowired RabbitTemplate rabbitTemplate; @Test public void send() { Book book = new Book(); book.setName("<西游记>"); book.setAnthony("吴承恩"); //对象被默认序列化后发送 rabbitTemplate.convertAndSend("demo-fanout","",book); } 接收消息 @Test public void receive(){ Object o = rabbitTemplate.receiveAndConvert("demo"); System.out.println(o.getClass()); System.out.println(o); } 打印输出: class cn.zyzpp.rabbitmq.entity.Book Book{name='<西游记>', anthony='吴承恩'} 5.发送topic消息 按规则转发消息(使用通配符) 1.在rabbitmq后台新建一个topic交换机 2.新建一个消息队列demo 3.交换机绑定消息队列,注意此处的路由键Routing key使用了通配符 *表示一个词. #表示零个或多个词. 那我们如何区分几个字母为一个单词呢? 答案是通过”点分”的 routing_key 形式,比如两个单词是 *.demo hello.demo, 如果路由键为demo.#,那可以匹配demo.开头的所有路由键。 4.查看此时的交换机 在Spring Boot创建单元测试 发送消息 @Test public void contextLoads() { //message需要自己构造一个,定义消息体内容和消息体 //rabbitTemplate.send(exchange,routingkey,message); Map map = new HashMap(); map.put("key","topic交换机"); map.put("msg",true); //对象被默认序列化后发送 rabbitTemplate.convertAndSend("demo-topic","demo.hello",map); } 接收消息 @Test public void receive(){ Object o = rabbitTemplate.receiveAndConvert("demo"); System.out.println(o.getClass()); System.out.println(o); } 打印输出 class java.util.HashMap {msg=true, key=topic交换机} 6.代码创建交换机与消息队列 1.上面演示的是通过RabbitMQ网页后台创建,通过编程的方式也非常简单: @Autowired AmqpAdmin amqpAdmin; /** * 代码创建交换机与消息队列并绑定 */ @Test public void createExChange(){ // new TopicExchange("topic.exChange"); // new FanoutExchange("fanout.exChange"); amqpAdmin.declareExchange(new DirectExchange("amqp.exChange"));//创建交换机(remove为删除交换机) System.out.println("单播交换机创建完成"); amqpAdmin.declareQueue(new Queue("amqp.queue",true)); //创建消息队列 amqpAdmin.declareBinding(new Binding("amqp.queue", Binding.DestinationType.QUEUE,"amqp.exChange","amqp.exChange",null));//绑定 } 2.登录后台查看,创建成功! 7.监听消息队列 监听消息队列,当有消息发到消息队列,立马获取并操作。方法也很简单。 1.开启基于注解的Rabbit模式 @EnableRabbit //开启基于注解的Rabbit模式 @SpringBootApplication public class RabbitmqApplication { public static void main(String[] args) { SpringApplication.run(RabbitmqApplication.class, args); } } 2.@RabbitListener注解实现监听 注意此时你的消息队列已经有了demo和demo.news @Service public class BookService { @RabbitListener(queues = "demo") public void receive(Book book){ System.out.println("收到消息:"+ book); } @RabbitListener(queues = "demo.news") public void receiveMess(Message message){ System.out.println("收到消息:"+ message); System.out.println(message.getBody()); //getBody返回的byte[]字节数组 System.out.println(message.getMessageProperties()); } } 然后我们在开启主程序时再运行测试用例: 测试用例 @Test public void send() { Book book = new Book(); book.setName("<西游记>"); book.setAnthony("吴承恩"); //对象被默认序列化后发送 rabbitTemplate.convertAndSend("demo-fanout","",book); } 查看主控制台打印 收到消息:Book{name='<西游记>', anthony='吴承恩'} 测试用例 @Test public void contextLoads() { //message需要自己构造一个,定义消息体内容和消息体 //rabbitTemplate.send(exchange,routingkey,message); Map map = new HashMap(); map.put("key","topic交换机"); map.put("msg",true); //对象被默认序列化后发送 rabbitTemplate.convertAndSend("demo-topic","demo.hello",map); } 查看主控制台打印 收到消息:(Body:'{"msg":true,"key":"topic交换机"}' MessageProperties [headers={__ContentTypeId__=java.lang.Object, __KeyTypeId__=java.lang.Object, __TypeId__=java.util.HashMap}, timestamp=null, messageId=null, userId=null, receivedUserId=null, appId=null, clusterId=null, type=null, correlationId=null, correlationIdString=null, replyTo=null, contentType=application/json, contentEncoding=UTF-8, contentLength=0, deliveryMode=null, receivedDeliveryMode=PERSISTENT, expiration=null, priority=0, redelivered=false, receivedExchange=demo-topic, receivedRoutingKey=demo.hello, receivedDelay=null, deliveryTag=1, messageCount=0, consumerTag=amq.ctag-KRZh2DyNETjEaGSH0JZ2dA, consumerQueue=demo.news]) [B@5332f99e MessageProperties [headers={__ContentTypeId__=java.lang.Object, __KeyTypeId__=java.lang.Object, __TypeId__=java.util.HashMap}, timestamp=null, messageId=null, userId=null, receivedUserId=null, appId=null, clusterId=null, type=null, correlationId=null, correlationIdString=null, replyTo=null, contentType=application/json, contentEncoding=UTF-8, contentLength=0, deliveryMode=null, receivedDeliveryMode=PERSISTENT, expiration=null, priority=0, redelivered=false, receivedExchange=demo-topic, receivedRoutingKey=demo.hello, receivedDelay=null, deliveryTag=1, messageCount=0, consumerTag=amq.ctag-KRZh2DyNETjEaGSH0JZ2dA, consumerQueue=demo.news]

优秀的个人博客,低调大师

SpringBoot 并发编程学习历程(绝对的干货)

如果一个项目总用单线程来跑,难免会遇到一些性能问题,所以再开发中,我们应该尽量适量的使用多线程(在保证线程安全的情况下)。 本教程大概目录: 模拟单线程情节 用Callable实现 并发编程 用DeferedResult实现异步处理 模拟单线程情节 /** * Created by Fant.J. */ @RestController @Slf4j public class AsyncController { /** * 单线程测试 * @return * @throws InterruptedException */ @RequestMapping("/order") public String order() throws InterruptedException { log.info("主线程开始"); Thread.sleep(1000); log.info("主线程返回"); return "success"; } } 我们把线程休息一秒当作模拟处理业务所花费的时间。很明显能看出来,这是个单线程。 nio-8080-exec-1表示主线程的线程1。 用Callable实现 并发编程 /** * 用Callable实现异步 * @return * @throws InterruptedException */ @RequestMapping("/orderAsync") public Callable orderAsync() throws InterruptedException { log.info("主线程开始"); Callable result = new Callable() { @Override public Object call() throws Exception { log.info("副线程开始"); Thread.sleep(1000); log.info("副线程返回"); return "success"; } }; log.info("主线程返回"); return result; } 我们可以看到,主线程的开始和返回(结束处理)是首先执行的,然后副线程才执行真正的业务处理。说明主线程在这里的作用是调用(唤醒)子线程,子线程处理完会返回一个Object对象,然后返回给用户。 这样虽然实现了并发处理,但是有一个问题,就是主线程和副线程没有做到完全分离,毕竟是一个嵌套进去的副线程。 所以为了优化我们的实现,我在这里模拟 消息中间件 来实现主线程副线程的完全分离。 用DeferedResult实现异步处理 因为本章主要讲的是并发编程原理,所以这里我们不用现成的消息队列来搞,我们模拟一个消息队列来处理。 MockQueue .java /** * 模拟消息队列 类 * Created by Fant.J. */ @Component @Slf4j public class MockQueue { //下单消息 private String placeOrder; //订单完成消息 private String completeOrder; public String getPlaceOrder() { return placeOrder; } public void setPlaceOrder(String placeOrder) throws InterruptedException { new Thread(()->{ log.info("接到下单请求"+placeOrder); //模拟处理 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //给completeOrder赋值 this.completeOrder = placeOrder; log.info("下单请求处理完毕"+placeOrder); }).start(); } public String getCompleteOrder() { return completeOrder; } public void setCompleteOrder(String completeOrder) { this.completeOrder = completeOrder; } } 注意再setPlaceOrder(String placeOrder)方法里,我创建了一个新的线程来处理接单的操作(为什么要建立新线程,怕主线程在这挂起,此段逻辑也没有线程安全问题,况且异步处理更快)。传进来的参数是个 订单号 ,经过1s的处理成功后,把订单号传给completeOrder 字段,说明用户下单成功,我在下面付controller调用该方法的代码 //注入模拟消息队列类 @Autowired private MockQueue mockQueue; @Autowired private DeferredResultHolder deferredResultHolder; .... @RequestMapping("/orderMockQueue") public DeferredResult orderQueue() throws InterruptedException { log.info("主线程开始"); //随机生成8位数 String orderNumber = RandomStringUtils.randomNumeric(8); mockQueue.setPlaceOrder(orderNumber); DeferredResult result = new DeferredResult(); deferredResultHolder.getMap().put(orderNumber,result); Thread.sleep(1000); log.info("主线程返回"); return result; } 好了,然后我们还需要一个中介类来存放订单号和处理结果。为什么需要这么一个类呢,因为我们之前说过要实现主线程和副线程分离,所以需要一个中介来存放处理信息(比如:这个订单号信息,和处理结果信息),我们判断处理结果是否为空就知道该副线程执行了没有。所以我们写一个中介类DeferredResultHolder 。 DeferredResultHolder .java /** * 订单处理情况 中介/持有者 * Created by Fant.J. */ @Component public class DeferredResultHolder { /** * String: 订单号 * DeferredResult:处理结果 */ private Map<String,DeferredResult> map = new HashMap<>(); public Map<String, DeferredResult> getMap() { return map; } public void setMap(Map<String, DeferredResult> map) { this.map = map; } } 在重复一次-.-,为什么需要这么一个类呢,因为我们之前说过要实现主线程和副线程分离,所以需要一个中介来存放处理信息(比如:这个订单号信息,和处理结果信息),一个订单肯定要对应一个结果。不然岂不是乱了套。 DeferredResult是用来放处理结果的对象。 好了,那新问题又来了,我们怎么去判断订单处理成功了没有,我们此时就需要写一个监听器,过100毫秒监听一次MockQueue类中的completeOrder中是否有值,如果有值,那么这个订单就需要被处理。我们写一个监听器。 QueueListener .java /** * Queue监听器 * Created by Fant.J. */ @Component @Slf4j public class QueueListener implements ApplicationListener<ContextRefreshedEvent>{ @Autowired private MockQueue mockQueue; @Autowired private DeferredResultHolder deferredResultHolder; @Override public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) { new Thread(()->{ while(true){ //判断CompleteOrder字段是否是空 if (StringUtils.isNotBlank(mockQueue.getCompleteOrder())){ String orderNumber = mockQueue.getCompleteOrder(); deferredResultHolder.getMap().get(orderNumber).setResult("place order success"); log.info("返回订单处理结果"); //将CompleteOrder设为空,表示处理成功 mockQueue.setCompleteOrder(null); }else { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } } 我们可以看到一共有三个不同的线程来处理。 分割线后,我再给大家带来一批干货,自定义线程池 https://www.jianshu.com/p/832f2b162450 学完这个后,再看下面的。。 我们前面的代码中,有两部分有用new Thread()来创建线程,我们有自己的线程池后,就可以用线程池来分配线程任务了,我在自定义线程里有讲,我用的是第二种配置方法(用@Async注解来给线程 )。 修改如下: @Async public void setPlaceOrder(String placeOrder) throws InterruptedException { log.info("接到下单请求"+placeOrder); //模拟处理 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //给completeOrder赋值 this.completeOrder = placeOrder; log.info("下单请求处理完毕"+placeOrder); } 我们看看效果: 圈红圈的就是我们自己定义的线程池里分配的线程。 谢谢大家! 介绍下我的所有文集: 流行框架 SpringCloudspringbootnginxredis 底层实现原理: Java NIO教程Java reflection 反射详解Java并发学习笔录Java Servlet教程jdbc组件详解Java NIO教程Java语言/版本 研究

优秀的个人博客,低调大师

springboot(十九):使用Spring Boot Actuator监控应用

微服务的特点决定了功能模块的部署是分布式的,大部分功能模块都是运行在不同的机器上,彼此通过服务调用进行交互,前后台的业务流会经过很多个微服务的处理和传递,出现了异常如何快速定位是哪个环节出现了问题? 在这种框架下,微服务的监控显得尤为重要。本文主要结合Spring Boot Actuator,跟大家一起分享微服务Spring Boot Actuator的常见用法,方便我们在日常中对我们的微服务进行监控治理。 Actuator监控 Spring Boot使用“习惯优于配置的理念”,采用包扫描和自动化配置的机制来加载依赖jar中的Spring bean,不需要任何Xml配置,就可以实现Spring的所有配置。虽然这样做能让我们的代码变得非常简洁,但是整个应用的实例创建和依赖关系等信息都被离散到了各个配置类的注解上,这使得我们分析整个应用中资源和实例的各种关系变得非常的困难。 Actuator是Spring Boot提供的对应用系统的自省和监控的集成功能,可以查看应用配置的详细信息,例如自动化配置信息、创建的Spring beans以及一些环境属性等。 Actuator监控只需要添加以下依赖就可以完成 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> </dependencies> 为了保证actuator暴露的监控接口的安全性,需要添加安全控制的依赖spring-boot-start-security依赖,访问应用监控端点时,都需要输入验证信息。Security依赖,可以选择不加,不进行安全管理,但不建议这么做。 Actuator 的 REST 接口 Actuator监控分成两类:原生端点和用户自定义端点;自定义端点主要是指扩展性,用户可以根据自己的实际应用,定义一些比较关心的指标,在运行期进行监控。 原生端点是在应用程序里提供众多 Web 接口,通过它们了解应用程序运行时的内部状况。原生端点又可以分成三类: 应用配置类:可以查看应用在运行期的静态信息:例如自动配置信息、加载的springbean信息、yml文件配置信息、环境信息、请求映射信息; 度量指标类:主要是运行期的动态信息,例如堆栈、请求连、一些健康指标、metrics信息等; 操作控制类:主要是指shutdown,用户可以发送一个请求将应用的监控功能关闭。 Actuator 提供了 13 个接口,具体如下表所示。 HTTP 方法 路径 描述 GET /autoconfig 提供了一份自动配置报告,记录哪些自动配置条件通过了,哪些没通过 GET /configprops 描述配置属性(包含默认值)如何注入Bean GET /beans 描述应用程序上下文里全部的Bean,以及它们的关系 GET /dump 获取线程活动的快照 GET /env 获取全部环境属性 GET /env/{name} 根据名称获取特定的环境属性值 GET /health 报告应用程序的健康指标,这些值由HealthIndicator的实现类提供 GET /info 获取应用程序的定制信息,这些信息由info打头的属性提供 GET /mappings 描述全部的URI路径,以及它们和控制器(包含Actuator端点)的映射关系 GET /metrics 报告各种应用程序度量信息,比如内存用量和HTTP请求计数 GET /metrics/{name} 报告指定名称的应用程序度量值 POST /shutdown 关闭应用程序,要求endpoints.shutdown.enabled设置为true GET /trace 提供基本的HTTP请求跟踪信息(时间戳、HTTP头等) 快速上手 相关配置 项目依赖 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> </dependencies> 配置文件 server: port: 8080 management: security: enabled: false #关掉安全认证 port: 8088 #管理端口调整成8088 context-path: /monitor #actuator的访问路径 endpoints: shutdown: enabled: true info: app: name: spring-boot-actuator version: 1.0.0 management.security.enabled=false默认有一部分信息需要安全验证之后才可以查看,如果去掉这些安全认证,直接设置management.security.enabled=false management.context-path=/monitor 代表启用单独的url地址来监控Spring Boot应用,为了安全一般都启用独立的端口来访问后端的监控信息 endpoints.shutdown.enabled=true 启用接口关闭Spring Boot 配置完成之后,启动项目就可以继续验证各个监控功能了。 命令详解 autoconfig Spring Boot的自动配置功能非常便利,但有时候也意味着出问题比较难找出具体的原因。使用 autoconfig 可以在应用运行时查看代码了某个配置在什么条件下生效,或者某个自动配置为什么没有生效。 启动示例项目,访问:http://localhost:8088/monitor/autoconfig返回部分信息如下: { "positiveMatches": { "DevToolsDataSourceAutoConfiguration": { "notMatched": [ { "condition": "DevToolsDataSourceAutoConfiguration.DevToolsDataSourceCondition", "message": "DevTools DataSource Condition did not find a single DataSource bean" } ], "matched": [ ] }, "RemoteDevToolsAutoConfiguration": { "notMatched": [ { "condition": "OnPropertyCondition", "message": "@ConditionalOnProperty (spring.devtools.remote.secret) did not find property 'secret'" } ], "matched": [ { "condition": "OnClassCondition", "message": "@ConditionalOnClass found required classes 'javax.servlet.Filter', 'org.springframework.http.server.ServerHttpRequest'; @ConditionalOnMissingClass did not find unwanted class" } ] } } } configprops 查看配置文件中设置的属性内容,以及一些配置属性的默认值。 启动示例项目,访问:http://localhost:8088/monitor/configprops返回部分信息如下: { ... "environmentEndpoint": { "prefix": "endpoints.env", "properties": { "id": "env", "sensitive": true, "enabled": true } }, "spring.http.multipart-org.springframework.boot.autoconfigure.web.MultipartProperties": { "prefix": "spring.http.multipart", "properties": { "maxRequestSize": "10MB", "fileSizeThreshold": "0", "location": null, "maxFileSize": "1MB", "enabled": true, "resolveLazily": false } }, "infoEndpoint": { "prefix": "endpoints.info", "properties": { "id": "info", "sensitive": false, "enabled": true } } ... } beans 根据示例就可以看出,展示了bean的别名、类型、是否单例、类的地址、依赖等信息。 启动示例项目,访问:http://localhost:8088/monitor/beans返回部分信息如下: [ { "context": "application:8080:management", "parent": "application:8080", "beans": [ { "bean": "embeddedServletContainerFactory", "aliases": [ ], "scope": "singleton", "type": "org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory", "resource": "null", "dependencies": [ ] }, { "bean": "endpointWebMvcChildContextConfiguration", "aliases": [ ], "scope": "singleton", "type": "org.springframework.boot.actuate.autoconfigure.EndpointWebMvcChildContextConfiguration$$EnhancerBySpringCGLIB$$a4a10f9d", "resource": "null", "dependencies": [ ] } } ] dump /dump 接口会生成当前线程活动的快照。这个功能非常好,方便我们在日常定位问题的时候查看线程的情况。 主要展示了线程名、线程ID、线程的状态、是否等待锁资源等信息。 启动示例项目,访问:http://localhost:8088/monitor/dump返回部分信息如下: [ { "threadName": "http-nio-8088-exec-6", "threadId": 49, "blockedTime": -1, "blockedCount": 0, "waitedTime": -1, "waitedCount": 2, "lockName": "java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@1630a501", "lockOwnerId": -1, "lockOwnerName": null, "inNative": false, "suspended": false, "threadState": "WAITING", "stackTrace": [ { "methodName": "park", "fileName": "Unsafe.java", "lineNumber": -2, "className": "sun.misc.Unsafe", "nativeMethod": true }, { "methodName": "park", "fileName": "LockSupport.java", "lineNumber": 175, "className": "java.util.concurrent.locks.LockSupport", "nativeMethod": false }, { "methodName": "await", "fileName": "AbstractQueuedSynchronizer.java", "lineNumber": 2039, "className": "java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject", "nativeMethod": false }, ... { "methodName": "getTask", "fileName": "ThreadPoolExecutor.java", "lineNumber": 1067, "className": "java.util.concurrent.ThreadPoolExecutor", "nativeMethod": false }, { "methodName": "runWorker", "fileName": "ThreadPoolExecutor.java", "lineNumber": 1127, "className": "java.util.concurrent.ThreadPoolExecutor", "nativeMethod": false }, { "methodName": "run", "fileName": "ThreadPoolExecutor.java", "lineNumber": 617, "className": "java.util.concurrent.ThreadPoolExecutor$Worker", "nativeMethod": false }, { "methodName": "run", "fileName": "TaskThread.java", "lineNumber": 61, "className": "org.apache.tomcat.util.threads.TaskThread$WrappingRunnable", "nativeMethod": false }, { "methodName": "run", "fileName": "Thread.java", "lineNumber": 745, "className": "java.lang.Thread", "nativeMethod": false } ], "lockedMonitors": [ ], "lockedSynchronizers": [ ], "lockInfo": { "className": "java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject", "identityHashCode": 372286721 } } ... ] env 展示了系统环境变量的配置信息,包括使用的环境变量、JVM 属性、命令行参数、项目使用的jar包等信息。和configprops不同的是,configprops关注于配置信息,env关注运行环境信息。 启动示例项目,访问:http://localhost:8088/monitor/env返回部分信息如下: { "profiles": [ ], "server.ports": { "local.management.port": 8088, "local.server.port": 8080 }, "servletContextInitParams": { }, "systemProperties": { "com.sun.management.jmxremote.authenticate": "false", "java.runtime.name": "Java(TM) SE Runtime Environment", "spring.output.ansi.enabled": "always", "sun.boot.library.path": "C:\\Program Files\\Java\\jdk1.8.0_101\\jre\\bin", "java.vm.version": "25.101-b13", "java.vm.vendor": "Oracle Corporation", "java.vendor.url": "http://java.oracle.com/", "java.rmi.server.randomIDs": "true", "path.separator": ";", "java.vm.name": "Java HotSpot(TM) 64-Bit Server VM", "file.encoding.pkg": "sun.io", "user.country": "CN", "user.script": "", "sun.java.launcher": "SUN_STANDARD", "sun.os.patch.level": "", "PID": "5268", "com.sun.management.jmxremote.port": "60093", "java.vm.specification.name": "Java Virtual Machine Spe 为了避免敏感信息暴露到 /env 里,所有名为password、secret、key(或者名字中最后一段是这些)的属性在 /env 里都会加上“*”。举个例子,如果有一个属性名字是database.password,那么它在/env中的显示效果是这样的: "database.password":"******" /env/{name}用法 就是env的扩展 可以获取指定配置信息,比如:http://localhost:8088/monitor/env/java.vm.version,返回:{"java.vm.version":"25.101-b13"} health 可以看到 HealthEndPoint 给我们提供默认的监控结果,包含 磁盘检测和数据库检测 启动示例项目,访问:http://localhost:8088/monitor/health返回部分信息,下面的JSON响应是由状态、磁盘空间和db。描述了应用程序的整体健康状态,UP 表明应用程序是健康的。磁盘空间描述总磁盘空间,剩余的磁盘空间和最小阈值。application.properties阈值是可配置的 { "status": "UP", "diskSpace": { "status": "UP", "total": 209715195904, "free": 183253909504, "threshold": 10485760 } "db": { "status": "UP", "database": "MySQL", "hello": 1 } } 其实看 Spring Boot-actuator 源码,你会发现 HealthEndPoint 提供的信息不仅限于此,org.springframework.boot.actuate.health 包下 你会发现 ElasticsearchHealthIndicator、RedisHealthIndicator、RabbitHealthIndicator 等 info info就是我们自己配置在配置文件中以Info开头的配置信息,比如我们在示例项目中的配置是: info: app: name: spring-boot-actuator version: 1.0.0 启动示例项目,访问:http://localhost:8088/monitor/info返回部分信息如下: { "app": { "name": "spring-boot-actuator", "version": "1.0.0" } } mappings 描述全部的URI路径,以及它们和控制器的映射关系 启动示例项目,访问:http://localhost:8088/monitor/mappings返回部分信息如下: { "/**/favicon.ico": { "bean": "faviconHandlerMapping" }, "{[/hello]}": { "bean": "requestMappingHandlerMapping", "method": "public java.lang.String com.neo.controller.HelloController.index()" }, "{[/error]}": { "bean": "requestMappingHandlerMapping", "method": "public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)" } } metrics 最重要的监控内容之一,主要监控了JVM内容使用、GC情况、类加载信息等。 启动示例项目,访问:http://localhost:8088/monitor/metrics返回部分信息如下: { "mem": 337132, "mem.free": 183380, "processors": 4, "instance.uptime": 254552, "uptime": 259702, "systemload.average": -1.0, "heap.committed": 292864, "heap.init": 129024, "heap.used": 109483, "heap": 1827840, "nonheap.committed": 45248, "nonheap.init": 2496, "nonheap.used": 44269, "nonheap": 0, "threads.peak": 63, "threads.daemon": 43, "threads.totalStarted": 83, "threads": 46, "classes": 6357, "classes.loaded": 6357, "classes.unloaded": 0, "gc.ps_scavenge.count": 8, "gc.ps_scavenge.time": 99, "gc.ps_marksweep.count": 1, "gc.ps_marksweep.time": 43, "httpsessions.max": -1, "httpsessions.active": 0 } 对 /metrics接口提供的信息进行简单分类如下表: 分类 前缀 报告内容 垃圾收集器 gc.* 已经发生过的垃圾收集次数,以及垃圾收集所耗费的时间,适用于标记-清理垃圾收集器和并行垃圾收集器(数据源自java.lang.management. GarbageCollectorMXBean) 内存 mem.* 分配给应用程序的内存数量和空闲的内存数量(数据源自java.lang. Runtime) 堆 heap.* 当前内存用量(数据源自java.lang.management.MemoryUsage) 类加载器 classes.* JVM类加载器加载与卸载的类的数量(数据源自java.lang. management.ClassLoadingMXBean) 系统 processors、instance.uptime、uptime、systemload.average 系统信息,例如处理器数量(数据源自java.lang.Runtime)、运行时间(数据源自java.lang.management.RuntimeMXBean)、平均负载(数据源自java.lang.management.OperatingSystemMXBean) 线程池 thread.* 线程、守护线程的数量,以及JVM启动后的线程数量峰值(数据源自 java.lang .management.ThreadMXBean) 数据源 datasource.* 数据源连接的数量(源自数据源的元数据,仅当Spring应用程序上下文里存在 DataSource Bean 的时候才会有这个信息) Tomcat 会话 httpsessions.* Tomcat的活跃会话数和最大会话数(数据源自嵌入式Tomcat的Bean,仅在使用嵌入式Tomcat服务器运行应用程序时才有这个信息) HTTP counter.status.、gauge.response. 多种应用程序服务HTTP请求的度量值与计数器 解释说明: 请注意,这里的一些度量值,比如数据源和Tomcat会话,仅在应用程序中运行特定组件时才有数据。你还可以注册自己的度量信息。 HTTP的计数器和度量值需要做一点说明。counter.status 后的值是HTTP状态码,随后是所请求的路径。举个例子,counter.status.200.metrics 表明/metrics端点返回 200(OK) 状态码的次数。 HTTP的度量信息在结构上也差不多,却在报告另一类信息。它们全部以gauge.response 开头,,表明这是HTTP响应的度量信息。前缀后是对应的路径。度量值是以毫秒为单位的时间,反映了最近处理该路径请求的耗时。 这里还有几个特殊的值需要注意。root路径指向的是根路径或/。star-star代表了那些Spring 认为是静态资源的路径,包括图片、JavaScript和样式表,其中还包含了那些找不到的资源。这就是为什么你经常会看到 counter.status.404.star-star,这是返回了HTTP 404 (NOT FOUND) 状态的请求数。 /metrics接口会返回所有的可用度量值,但你也可能只对某个值感兴趣。要获取单个值,请求时可以在URL后加上对应的键名。例如,要查看空闲内存大小,可以向/metrics/mem.free发一 个GET请求。例如访问:http://localhost:8088/monitor/metrics/mem.free,返回:{"mem.free":178123}。 shutdown 开启接口优雅关闭Spring Boot应用,要使用这个功能首先需要在配置文件中开启: endpoints: shutdown: enabled: true 配置完成之后,启动示例项目,访问:http://localhost:8088/monitor/shutdown返回部分信息如下: { "message": "Shutting down, bye..." } 此时你会发现应用已经被关闭。 trace /trace 接口能报告所有Web请求的详细信息,包括请求方法、路径、时间戳以及请求和响应的头信息,记录每一次请求的详细信息。 启动示例项目,先访问一次:http://localhost:8080/hello,再到浏览器执行:http://localhost:8088/monitor/trace查看返回信息: [ { "timestamp": 1516780334777, "info": { "method": "GET", "path": "/hello", "headers": { "request": { "host": "localhost:8080", "connection": "keep-alive", "cache-control": "max-age=0", "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36", "upgrade-insecure-requests": "1", "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", "accept-encoding": "gzip, deflate, br", "accept-language": "zh-CN,zh;q=0.9", "cookie": "UM_distinctid=16053ba344f1cd-0dc220c44cc94-b7a103e-13c680-16053ba3450751; Hm_lvt_0fb30c642c5f6453f17d881f529a1141=1513076406,1514961720,1515649377; CNZZDATA1260945749=232252692-1513233181-%7C1516085149; Hm_lvt_6d8e8bb59814010152d98507a18ad229=1515247964,1515296008,1515672972,1516086283" }, "response": { "X-Application-Context": "application:8080", "Content-Type": "text/html;charset=UTF-8", "Content-Length": "11", "Date": "Wed, 24 Jan 2018 07:52:14 GMT", "status": "200" } }, "timeTaken": "4" } } ] 上述信息展示了,/hello请求的详细信息。 其它配置 敏感信息访问限制 根据上面表格,鉴权为false的,表示不敏感,可以随意访问,否则就是做了一些保护,不能随意访问。 endpoints.mappings.sensitive=false 这样需要对每一个都设置,比较麻烦。敏感方法默认是需要用户拥有ACTUATOR角色,因此,也可以设置关闭安全限制: management.security.enabled=false 或者配合Spring Security做细粒度控制。 启用和禁用接口 虽然Actuator的接口都很有用,但你不一定需要全部这些接口。默认情况下,所有接口(除 了/shutdown)都启用。比如要禁用 /metrics 接口,则可以设置如下: endpoints.metrics.enabled = false 如果你只想打开一两个接口,那就先禁用全部接口,然后启用那几个你要的,这样更方便。 endpoints.enabled = false endpoints.metrics.enabled = true 参考 Spring Boot Actuator: Production-ready features对没有监控的微服务Say No!Spring Boot Actuator 使用 作者:纯洁的微笑 出处:www.ityouknow.com 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。 本文如对您有帮助,还请多帮 【推荐】 下此文。 如果喜欢我的文章,请关注我的公众号

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册