导语
  在之前的一篇中简单的介绍一些在SpringBoot中常见的使用方式,已经如何实现自定义的一些配置,设置等等的内容,这一次的分享中来介绍一些在Spring Boot 中的高级用法,这样会对Spring Boot的整个的使用场景,有更加深刻的认识。

文章目录

  • 统一异常处理
  • 异步执行
  • 端口随机
  • 总结

统一异常处理

  现在REST Ful 风格的兴起对于接口的定义,通常会有一些统一的格式,这样的话在调用方与实现方之间有一个统一的规则如下所示

{"status":true,"code":200,"msg":"numal","data":{"name":"nihui","password":"adjfakdfja;lkdjfla;kds"}
}

  但是如果在调用接口的时候,出现异常或者是系统内部出现其他异常的时候,会得到一个错误的提示信息

{"timestamp":"456456456456","status":500,"error":"System Error","message":"No message available","path":"/hello/test"
}

  表示后端服务器一定是出现什么问题了,这样其实也还是很好的,但是这样会导致与上面自定义的返回数据不一致,在接口的使用上会带来一定程度上的不便,这个时候就需要在服务端编写接口的时候将这个因素考虑进去,如果出现了异常怎么实现。
  这样Spring Boot就提供了一个全局的异常处理机制。这样无论在什么情况下都可以准确的返回统一格式的数据信息。如下操作

@ControllerAdvice
public class GlobelExecptionHandler{private Logger logger = LoggerFactory.getLogger(GlobelExceptionHandler.class)@ExceptiongHandler(value=Exception.class)@ResponseBodypublic AjaxResult defalueErrorHandler(HttpServletRequest req,Exception e) throw Exception{logger.error("",e)AjaxResult result = new AjaxResult();result.setMessage(e.getMessage())if(e instanceof Exception){result.setCode(500)}result.setData(null);result.setStatus(false);return result
}
}

  其中AjaxResult 是返回格式的实体类,在发生异常的时候被捕捉到之后,会将封装好的数据返回到调用方,但是只到这里是不够的。还要在配置文件中加入如下的配置才可以

# 出现错误的时候直接抛出异常
spring.mvc.throw-exceptioin-if-no-handler-found=true
# 不要为工程中的资源文件建立映射
spring.resource.add-mappings=false

  当然上面介绍的只是一种简单的实现方式,但是这个是全局的异常处理,包括自定义异常处理提供了一个解决思路。其他的高级的扩展思路都是从最简单的扩展思路上进行操作的,这个也就是所谓的万变不离其宗。实现的原理掌握了,怎么实现就由大家来决定呢?

异步执行

  异步的意思就是不需要等待处理结果的返回就可以执行后面的逻辑;而同步的意思则是需要等待结果的然后再执行后续的逻辑,通常情况下使用异步操作的时候都会创建一个线程执行一段逻辑,然后把线程放入到线程池中去执行,例如

ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.execute(()->{try{//业务逻辑}cache(Exception e){e.printStackTrace();}finally{}
})

  上面这种方式使用了Lambda表达式,但是整个的执行逻辑还是比较繁琐的,而在Spring中提供了一个@Async 的注解,通过这个注解可以实现方法的异步调用。

@Async
public void saveLog(){System.out.println(Thread.currentThread().getName());
}

  通过这种方式可以直接在Controller中调用该方法,这个方法就是异步进行执行的,这样它会在Spring的默认线程池中执行。但是需要注意的一点就是,一定要在外部类中调用,在本类中调用它是不起作用的。
  既然说到了执行异步操作,那就不得不提线程池,在Spring Boot通过如下的方式进行线程池的配置

@Configuration
@ConfigurationProperties(prefix="spring.task.pool")
public class TaskThreadPoolConfig{// 核心线程数private int corePoolSize=5;// 最大线程数private int maxPoolSize=50;// 线程中维护线程所需要的空闲时间private int keepAliveSeconds = 60;// 队列长度private int queueCapacity = 1000;// 线程前缀名private String threadNamePrefix = "SPRING-"
}

  定义好配置参数之后,接下来就是重新定义线程池配置类

@Configuration
public class AsyncTaskExecutePool implements AsyncConfigurer{private Logger logger = LoggerFactory.getLogger(AsyncTaskExecutePool.class);@Autowiredprivate TaskThreadPoolConfig config;@Overridepublic Executor getAsyncExecutor(){ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(config.getCorePoolSize());………………executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.initialize();return executor;}@Overridepublic AsyncUncaughExceptionHandler getAsyncUncaughtExceptionHandler(){return new AsyncUncaughtExceptionHandler(){@Overridepublic void handlerUncaughtException(Throwable arg0,Method,arg1,Object arg2){……}}}
}

  配置完成之后,整个的异步线程池就是自定义的了,可以在其中配置上面提到的内容。但是其中有一个比较关键的点,就是线程的拒绝策略。要知道在线程池外部等待队列空间是有限的,当这个等待队列满了之后,如果还有线程加入的话就需要支持一种线程拒绝策略,否则就会导致内存溢出的后果。目前支持的两种拒绝策略

  • AbortPolicy:直接抛出异常
  • CallerRunsPolicy:主线程直接执行该任务,之心完成之后尝试添加下一个任务线程到线程池中,这样可以对线程池有一个保护的作用。

  推荐使用CallerRunsPolicy策略,当队列中任务满了之后,如果执行抛出异常那么这个任务将会被丢失。使用这个策略可以保证该任务不会被丢失。

端口随机

  在实际开发的过程中,每个项目的端口其实都是在开发过程中被规定好的,通过server.port 进行配置,如果想启动多实例的时候就只需要修改端口号即可。但是在有些场景下,要根据线上的流量对实例进行动态的扩容,那么这个时候就需要能动态的去改变端口号,这个因为是全自动的,所以不可能人为的去修改端口然后启动实例。这个时候就可以通过如下的方式来实现端口号的随机

server.port=${random.int[2000,8000]}

  通过上面一种方式来实现随机生成一个2000-8000的端口号,这样每次启动之后的端口号就不一样了,但是为了保证正常业务是可以访问的在整个的架构实现的过程中需要保证服务高可用。但是这样一来还有一个新问题出现,那就是如果该端口已经被使用了如果再次启动就会报错了。那么这个时候就需要进行如下的一个操作

public class StartCommand{public StartCommand(String[] args){Boolean isServerPort = false;String serverPort = "";if(args != null){for(String arg;args){if(StringUtils.hasText(arg)&&arg.startWith("--server.port")){isServerPort = true;serverPort = arg;break;}}}if(!isServerPort){int port = ServerPortUtils.getAvailablePort();System.setProperty("server.port",String.valueOf(port))}else{System.setProperty("server.port",serverPort.split("=")[1])}}
}

  通过对启动参数的遍历,如果指定启动端口,后续不需要自动生成,如果没有进行指定,就通过ServerPortUitls 进行指定,然后设置到环境变量中,在application.properties中通过如下的方式进行获取

server.port = ${server.port}

  可以在启动类中加入如下的操作

new SrartCommand(args);

总结

  在这次分享中主要是说了一些自定义的配置异常处理,异步执行操作以及端口随机等操作。对于日常的开发中还是比较重要的,学会了这些配置,并且理解了其原理,对提升开发效率会有很大的帮助。

Spring Cloud 微服务实战系列-Spring Boot再次入门(二)相关推荐

  1. Spring Cloud 微服务实战系列-Spring Boot再次入门(一)

    导语   看到标题大家都疑惑,为什么叫做再入门呢?在之前的博客中也分享过相关的内容,但为了让Spring Cloud 微服务实战系列更加完整就再次编写一个入门的内容,也是为了这个系列的内容更加的完整, ...

  2. Spring Cloud 微服务实战系列-Eureka注册中心(一)

    导语   在这一个系列的分享中,笔者主要是来分享在实战实际操作中的一些使用场景,了解了实战的操作中的使用场景才能更好的从实战中的问题出发找到自己需要的原理性的东西,找到原理性的东西之后才可以更好的根据 ...

  3. Spring Cloud 微服务实战系列-Ribbon整合RestTemplate实现负载均衡

    导语   在Spring Cloud项目中想要整合Ribbon只需要在pom文件中加入对应的依赖就可以了,那么在这次的分享中就来看看在Ribbon怎么通过RestTemplate来进行负载均衡,以及扩 ...

  4. Spring Cloud 微服务实战系列-Eureka注册中心(二)

    导语   之前的分享中,简单的介绍了SpringBoot的入门知识以及如何使用Eureka搭建服务注册中心,这一次的分享主要是来讲解一些在Eureka中的常用的配置,方便大家在使用Eureka的时候可 ...

  5. 手把手,嘴对嘴教你Spring Cloud 微服务实战 -- 初识Spring Cloud

    Spring Cloud 简介 摘自百度百科: Spring Cloud是一系列框架的有序集合.它利用SpringBoot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册.配置中心.消 ...

  6. Spring Cloud 微服务实战系列-Ribbon入门RestTemplate 介绍

    导语   目前在开发中主要的负载均衡方案分为两种:一种是集中式的负载均衡,在生产者和消费者之间通过F5或者是Nginx来进行负载均衡,而另一种则是在客户端自己进行负载均衡,也就是说请求客户端可以根据自 ...

  7. 手把手,嘴对嘴教你Spring Cloud 微服务实战 -- 前言

    Spring Cloud 总结 博主接触到Spring Cloud 大概已经一年多了,当时Spring Cloud微服务框架已经是潮流了,不会一点都不好意思出去面试.并且主流技术基本上都在谈论微服务, ...

  8. Spring Cloud 微服务实战笔记

    Spring Cloud 微服务实战笔记 微服务知识 传统开发所有业务逻辑都在一个应用中, 开发,测试,部署随着需求增加会不断为单个项目增加不同业务模块:前端展现也不局限于html视图模板的形式,后端 ...

  9. Spring Cloud 微服务实战精品文章大汇总,错过了血亏!

    随着互联网的高速发展,庞大的用户群体和快速的需求变化已经成为了传统架构的痛点.在这种情况下,如何从系统架构的角度出发,构建出灵活.易扩展的系统来快速响应需求的变化,同时,随着用户量的增加,如何保证系统 ...

最新文章

  1. opencv:在二维定标中的应用
  2. 【MFC】定义XP风格的工具栏
  3. 献礼724运维日 | 首届腾讯运维技术开放日讲了啥? (附演讲PPT领取方式)
  4. 解决Failed to load the JNI shared library xxx/xxx/jvm.dll 错误
  5. python列表删除行_Python DataFrame – 删除具有属于值列表的列值的行
  6. smartconfig配置模式
  7. cdecl.org 翻译C声明的网站
  8. vi/vim命令使用
  9. sketch 3.8.1(破解版涵盖3.0,3.7,3.8.0以上版本) 安装and使用指南(20160524)更新)
  10. Phaser3初体验
  11. 安卓修改Airpods的双击功能,改“播放暂停”————下一首
  12. Redis的基本使用
  13. Java中四舍五入和四舍六入五成双
  14. Egg Config
  15. 语法体系:快速区分同位语从句和定语从句day10
  16. SPSS实现快速聚类(K-Means/K-均值聚类)
  17. 维修电工仪表照明实训考核装置
  18. 算法第十期——DFS(深度优先搜索)的剪枝优化
  19. chrome收藏夹位置在哪里?
  20. Java学习之给定如图所示的Animal类及其子类的继承关系UML图。编写代码实现这些类。

热门文章

  1. vue点击input框出现弹窗_vue组件实现弹出框点击显示隐藏效果
  2. c语言填空题题库6,二级C语言题库-填空题.doc
  3. mysql 的数据备份_MySQL 数据备份与还原
  4. vim 环境写 markdown 的插件推荐
  5. oracle中sql语句的优化
  6. MySql关键字-保留字
  7. 57、Design Support Library 介绍及环境搭建
  8. Hadoop1.9安装配置
  9. 了解 XML 架构(XML与OO)
  10. 【解放日报】除了CEO首席执行官,你了解CIO吗?