本达人课结合我当前自身的微服务的设计、开发工作,为有志于从传统开发模式向当前比较流行的微服务架构模式转变的开发者,提供比较完整的知识梳理及全技术栈障碍的打通。
其中的技术栈主要包括 Spring、Spring MVC 常用注解以及请求流程的梳理,Spring Boot 工作中常用基础知识以及主要会涉及到的各种技术框架的整合,Spring Cloud 的各个组件设计原理梳理以及如何使用,Docker 架构介绍以及容器化管理平台等。
达人课分为五大部分:
第一部分(第01-05课),主要分享微服务开发过程中,Spring、Spring MVC 常用的主要知识点,并讲解 Spring Boot 在实际工作如何整合常用技术,诸如 Spring Data JPA、Redis、MongoDB、RabbitMQ、Elasticsearch 等。
第二部分(第06-09课),除了分享 Consul 相关知识,带领大家利用 Spring Cloud 和 Consul 实现服务的注册与发现,还将介绍 Spring Cloud 常用组件(如 Zuul、Ribbon、Hystrix、Feign 等)及在实际工作中的使用方法。此外,在网关层整合 OAuth 2 实现微服务鉴权、配置中心及请求链路跟踪等内容也会在本部分进行详细解读与演示。
第三部分(第10-14课),主要包括几大核心内容,(1)对 Redis 分片原理、集群搭建、分片数据迁移,Redis 与 Lua 脚本整合进行详解;(2)对 ZooKeeper 相关算法(如 Paxos、ZAB)进行介绍,并对其 watcher 的实现原理及 ZooKeeper 集群搭建过程进行分析与说明;(3)分析如何利用 Redis、DB、ZooKeeper 等实现分布式锁,介绍分布式事务相关原理,并演示在工作中如何利用消息中间件实现数据最终一致性和利用第三方组件实现数据实时一致性等内容。
第四部分(第15-17课):主要介绍 Docker 架构及常用命令,并对常用容器管理平台如 Rancher、K8S 进行详细说明,同时讲解工作中如何使用其管理容器。
第五部分(第18-19课),实战部分,对前面所讲内容进行综合实战演练,让读者了解在工作中如何应用以上技术。其中主要包括微服务开发、微服务网关、负载均衡、熔断、微服务鉴权、微服务镜像生成、容器管理平台搭建及如何管理容器等内容。
本课程全面介绍了基于 Spring Cloud 体系开发微服务的全过程,不仅包含基础的服务开发,而且深入地介绍了完整的微服务架构需要的服务治理、分布式事务等核心要素,课程还介绍了容器化部署微服务,微服务能够通过 DevOps 支持快速上线运营。
——润和软件研发总监,吴昊
随着技术的发展,Spring Boot 以其高效的开发越来越受广大开发者喜爱,本课程从基础到实践全方位解读了 Spring Boot,很值得学习。
——中国石油规划总院高级开发工程师,李熠
技术理论知识往往是枯燥、理想化的,如果没有实践和运用,终将停留在纸面上。黄朝兵通过实践和运用实例生动阐述了技术理论知识,使技术人员易懂会用。
——江苏润和软件股份有限公司资深 PMO,陈伟荣
黄朝兵,本科毕业于南京工业大学,目前在南京大学软件学院读软件工程硕士;工作10年左右,现就职于某家跨国公司担任资深开发工程师,主要从事微服务相关设计、开发工作;曾在日企从事过医疗软件核心系统开发工作;在国企完成电力调度、操作票核心系统开发与维护;在互联网公司职位为高级工程师,从事金融系统研发,主要职责是完成交易系统的核心功能的架构设计、开发。
十年前左右,我去公司面试时,面试官问我的第一个问题是让我简要介绍下软件设计中的三层设计模型(表示层、业务逻辑处理层、数据访问层):
尽管在软件设计过程中,架构师或者程序设计者遵守了流行一时的经典的三层模型,但由于并未按照业务场景进行划分,使得最终的系统应用将所有的业务场景的表示层、业务逻辑处理层、数据访问层放在一个 Project 中,然后经过编译、打包并部署到一台服务器上。
这种架构适用于用户业务不复杂、访问量较小的时候,甚至可以将应用服务、数据库、文件服务器部署在一台服务器上。但随着用户业务场景变得越来越复杂,单体架构的局限性就很快暴露出来了,主要体现在如下几方面:
SOA 是一种粗粒度、松耦合服务架构,服务之间通过简单、精确定义接口进行通讯,不涉及底层编程接口和通讯模型。SOA 可以看作是 B/S 模型、XML(标准通用标记语言的子集)/Web Service 技术之后的自然延伸。
其主要优点有:
主要缺点:
微服务的概念是 Martin Flower 在2014年写的一篇论文《MicroServices》中提出来的,其主要特点是:
主要挑战:
通过以上对微服务的分析,相信各位读者已了解了微服务开发过程中我们将会面临的各种挑战。为了让大家在微服务开发过程中更加顺利,少踩坑,我在本达人课中分享了自己在微服务开发过程中的所有总结,希望对各位读者有所帮助。
众所周知,对于做技术的工程师而言,除了理论之外,我们更关注于实战,因此在课程的每篇文章中,主要以实战为主,比较关键的理论将同步为各位做详细说明。
为了方便大家更清晰地学习本课程,我们来看下课程目录安排。
Spring Boot 未出现之前,我们利用 Spring、Spring MVC 进行项目开发时,整个项目从引入依赖 Jar 包,书写配置文件,打包部署到开发环境、测试环境、生产环境都需要大量人力,但是最终的效果仍不尽如人意,甚至还会给一个项目组带来项目延期的风险等。
随着敏捷开发思想被越来越多的人接受以及开发者对开发效率的不断追求,最终推出了具有颠覆和划时代意义的框架 Spring Boot。
Spring Boot 首先遵循“习惯优于配置”原则,言外之意即是尽量使用自动配置让开发者从繁琐的书写配置文件的工作中解放出来;Spring Boot 另外一个比较明显的特点就是尽量不使用原来 Spring 框架中的 XML 配置,而主要使用注解代替。
Spring Boot 由 Spring、Spring MVC 演化而来,注解也继承自两者。下面我们看下 Spring MVC 中常用的注解:
@Controller
:该注解用于类上,其表明该类是 Spring MVC 的 Controller;@RequestMapping
:该注解主要用来映射 Web 请求,其可以用于类或者方法上;@RequestParam
:该注解主要用于将请求参数数据映射到功能处理方法的参数上;@ResponseBody
:该注解的作用是将方法的返回值放在 Response 中,而不是返回一个页面,其可以用于方法上或者方法返回值前;@RequestBody
:用于读取 HTTP 请求的内容(字符串),通过 Spring MVC 提供的 HttpMessageConverter 接口将读到的内容转换为 JSON、XML 等格式的数据并绑定到 Controller 方法的参数上;@PathVariable
:用于接收请求路径参数,将其绑定到方法参数上;@RestController
:该注解是一个组合注解,只能用于类上,其作用与 @Controller
、@ResponseBody
一起用于类上等价。注:在 Spring 4.3 中引进了
@GetMapping
、@PostMapping
、@PutMapping
、@DeleteMapping
、@PatchMapping
。
简单使用,如下代码所示:
@RestController@RequestMapping("/springmvc")public class TestAnnotationController { @RequestMapping(value = "/setUserInfo",method = RequestMethod.GET) public String setUserInfo(@RequestParam(name = "userName") String userName){ System.out.println(userName); return "success"; }}
利用 Postman 测试结果如下图所示:
以上是运用 Spring MVC 注解的一个简单实例,下面对其发送请求到结果返回的整个流程进行简要分析:
1. 调用类 DispatcherServlet 中 doService 方法对请求进行处理,该方法主要就进一步调用 doDispatch 方法对请求进行处理,源码如下:
2. 根据请求 processedRequest 获取 HandlerExecutionChain,源码如下:
3. 获取 HandlerAdapter,源码如下:
4. 调用类 RequestMappingHandlerAdapter 中方法 handleInternal 初始化 invocableMethod 的方法参数解释器和返回值解释器,源码如下:
5. 调用 invocableMethod 的方法 invokeAndHandle 对请求进行处理,源码如下:
在该方法中主要调用 invokeForRequest 方法获取参数之后,再利用反射调用 @RequestMapping
注解对应的方法获取返回值,代码如下:
6. 最后再利用返回值处理器对步骤4中的结果进行处理后得到最终结果并填充到 Response 对象中,源码如下:
以上就是从前端发送一个请求到后端,后端收到请求处理后返回结果的主要流程。因为篇幅问题很多环节无法一一写出,假如需要详细了解请自行根据以上主线 Debug 查看源代码。
第01课中,主要为各位读者介绍了实际开发中常用注解和 Spring Boot 中的请求主要流程。本课中,主要带大家掌握 Spring Boot 开发时所需要的以下技能:
首先,我们访问:http://start.spring.io/,选择构建工具 Maven,采用编程语言 Java,输入如图中的信息,下载项目压缩包:
将下载的项目压缩包解压后,导入 idea 中,然后在此基础项目结构中进行开发即可,在此实例中,项目结构如下图所示:
为了能够开发 RESTful 风格的接口,需要在 pom.xml 文件中添加如下依赖:
org.springframework.boot spring-boot-starter-web
继下来编写 Controller:
@RestController@RequestMapping(value = "/restful")public class TestRestfulInterfaceController{ @RequestMapping("/test") public String testRestfulInterface(@RequestParam("name") String name){ return "hello" + name; }}
将程序启动起来,并利用 Postman 进行测试,输出如下结果:
利用 Spring Boot 进行微服务开发时,配置文件既可以利用 *.properties
文件格式,又可以利用 *.yml
格式文件。不过在开发中,通常选择利用 yml 格式配置文件,该格式文件可读性更好。
下面分别新建 application.yml、application-dev.yml、application-test.yml、application-prod.yml 四个配置文件:
将配置文件切换到测试环境并启动程序后,程序监听的端口变为8100,利用 Postman 测试结果如下:
读取配置文件中的配置项,分别在 dev、test、prod 对应配置文件中添加 student 相关配置信息,在程序中读取配置文件并将其内容打印出来。
配置文件内容,如下图所示:
读取配置文件内容代码:
@Componentpublic class StudentConfig { @Value("${student.name}") private String name; @Value("${student.age}") private Integer age; ...此处省略get set方法}
打印学生信息代码:
@RestController@RequestMapping(value = "/restful")public class TestRestfulInterfaceController{ @Autowired private StudentConfig studentConfig; @RequestMapping("/test") public String testRestfulInterface(@RequestParam("name") String name){ return "hello:" + name; } @RequestMapping("/printStudentInfo") public String getStudentInfo(){ return "studentName:" + studentConfig.getName() + " studentAge:" + studentConfig.getAge(); }}
Postman 测试结果,见下图:
Listener 是 Spring 为开发人员提供的一种监听、订阅实现机制,其实现原理是设计模式之观察者模式,设计的初衷是为了系统业务之间进行解耦,以便提高系统可扩展性、可维护性。Listener 主要包括定义事件、事件监听、事件发布。下面分别对它们做介绍。
1. 定义用户注册事件
/** *事件定义类 */public class UserRegisterEvent extends ApplicationEvent { private User user; /** * source 表示事件源对象 * user表示注册用户对象 * @param source * @param user */ public UserRegisterEvent(Object source,User user) { super(source); this.user = user; } public User getUser() { return user; } public void setUser(User user) { this.user = user; }}
2. 用户注册服务实现类
@Service("userService")public class UserServiceImpl implements IUserService { @Autowired private ApplicationContext applicationContext; /** * 用户注册 * @param user */ @Override public void registerUser(User user) { if (user != null) { //调用持久层注册用户 //发布用户注册事件 applicationContext.publishEvent(new UserRegisterEvent(this, user)); } }}
3. 用户注册控制类
@RestController@RequestMapping("/user")public class UserController { @Autowired private IUserService userService; @RequestMapping("/register") public String registerUser(@NotNull String userName, @NotNull Integer age) { String msg = "success"; try { userService.registerUser(new User(userName,age)); } catch (Exception e) { msg = "error"; } return msg; }}
4. 监听器代码
利用 @EventListener
实现监听用户注册事件:
/** * 利用@EventListener注解监听用户注册事件 */@Componentpublic class AnnotationUserRegisterListener { @EventListener public void sendMailToUser(UserRegisterEvent userRegisterEvent){ System.out.println("利用@EventListener注解监听用户注册事件并向用户发送邮件"); System.out.println("注册用户名:" + userRegisterEvent.getUser().getName()); }}
利用接口 ApplicationListener 实现监听用户注册事件:
/** * 实现ApplicationListener实现用户注册监听 */@Componentpublic class RegisterUserApplicationListener implements ApplicationListener { @Override public void onApplicationEvent(UserRegisterEvent userRegisterEvent) { System.out.println("实现接口ApplicationListener监听用户注册事件并向用户发送邮件"); System.out.println("注册用户名:" + userRegisterEvent.getUser().getName()); }}
利用接口 SmartApplicationListener 实现监听用户注册事件:
/** *实现接口SmartApplicationListener实现监听用户注册 */@Componentpublic class RegisterUserSmartApplicationListener implements SmartApplicationListener { @Override public boolean supportsEventType(Class extends ApplicationEvent> aClass) { return aClass == UserRegisterEvent.class; } /** * 注意此处aClass不能与IUserService.class比较 * @param aClass * @return */ @Override public boolean supportsSourceType(Class> aClass) { return aClass == UserServiceImpl.class; } @Override public void onApplicationEvent(ApplicationEvent applicationEvent) { UserRegisterEvent userRegisterEvent = (UserRegisterEvent) applicationEvent; System.out.println("实现接口SmartApplicationListener监听用户注册事件并向用户发送邮件"); System.out.println("注册用户名:" + userRegisterEvent.getUser().getName()); } /** * 返回值越小监听越靠前 * @return */ @Override public int getOrder() { return 1; }}
利用 Postman 调用接口:
http://localhost:8100/user/register?userName=huangchaobing&age=32
实现用户注册,控制台输出监听相关信息:
以上三种监听都属于同步监听,必须等监听逻辑处理完成之后,用户注册业务逻辑才算完成。然而这样,当有用户操作时,会让用户进行等待,给用户的体验不太好,因此下面我们用 @Async
注解实现异步监控。
1. 为异步监听器设置异步线程池对象:
@Configuration@EnableAsyncpublic class AsyncListenerConfiguration implements AsyncConfigurer { /** * 获取异步线程池执行对象 * @return */ @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor(); //设置线程数量 threadPoolTaskExecutor.setCorePoolSize(10); threadPoolTaskExecutor.setMaxPoolSize(10); threadPoolTaskExecutor.setQueueCapacity(50); threadPoolTaskExecutor.initialize(); return threadPoolTaskExecutor; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return null; }}
2. 在需要执行异步监听方法上面添加 @Async
注解:
@Componentpublic class AsyncAnnotationUserRegisterListener { @Async @EventListener public void sendMailToUser(UserRegisterEvent userRegisterEvent) { try { Thread.sleep(5000); } catch (Exception e) { } System.out.println("利用@EventListener注解监听用户注册事件并异步向用户发送邮件"); }}
3. 发送注册用户请求,测试异步监听:
以上是自定义事件、发布事件、监听事件的常用开发方式。为了方便开发者开发,在 Spring Boot 2.0 以后,为开发者定义了如下事件:
在工作中我们经常在程序上下文和 Bean 创建成功后做一些比如初始化缓存、缓存预热等操作,这时,便会通过监听 ApplicationReadyEvent 事件完成,该过程分两步完成。
1. 定义监听器:
/** 1. 初始化redis缓存listener */public class InitializeRedisCacheListener implements ApplicationListener { @Override public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) { ConfigurableApplicationContext applicationContext = applicationReadyEvent.getApplicationContext(); RedisUtil redisUtil = applicationContext.getBean(RedisUtil.class); if(redisUtil != null){ redisUtil.initializeRedisData(); } }}
2. 注册监听器 Listener:
@SpringBootApplicationpublic class Lesson2Application { public static void main(String[] args) { SpringApplication springApplication = new SpringApplication(Lesson2Application.class); springApplication.addListeners(new InitializeRedisCacheListener()); springApplication.run(args); }}
启动程序后,开始初始化 Redis 缓存数据,如下图所示:
Filter 通常用于在请求过程中对用户身份进行认证、过滤等操作,开发过程如下。
1. 继承 Filter 接口定义过滤器:
@WebFilter(filterName = "loginFilter",urlPatterns = "/*")public class LoginFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("对登录进行过滤操作"); //请求放行 filterChain.doFilter(servletRequest,servletResponse); } @Override public void destroy() { }}
2. 在程序启动类加上注解 @ServletComponentScan
,将过滤器加入过滤器链:
@SpringBootApplication@ServletComponentScanpublic class Lesson2Application { public static void main(String[] args) { SpringApplication springApplication = new SpringApplication(Lesson2Application.class); springApplication.addListeners(new InitializeRedisCacheListener()); springApplication.run(args); }}
启动程序利用 Postman 发送请求,测试结果如下:
除了利用 @ServletComponentScan
将 Filter 加入过滤器链,还可以在程序启动类中加入 @Bean
注解将 Filter 加入过滤器链:
@SpringBootApplication//@ServletComponentScanpublic class Lesson2Application { public static void main(String[] args) { SpringApplication springApplication = new SpringApplication(Lesson2Application.class); springApplication.addListeners(new InitializeRedisCacheListener()); springApplication.run(args); } @Bean public FilterRegistrationBean securityFilter() { FilterRegistrationBean registration = new FilterRegistrationBean(); Filter loginFilter = new LoginFilter(); registration.setFilter(loginFilter); registration.addUrlPatterns("/*"); registration.setName("loginFilter"); registration.setOrder(1); return registration; }}
测试结果如下:
本节将大家利用拦截器 Interceptor 完成请求过滤、记录请求日志、判断用户是否登录等操作。
实现自定义拦截器需要如下三步:
1. 实现接口 HandlerInterceptor 并根据需要重载其方法。其方法主要有:
示例代码如下:
public class WebInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception { System.out.println("============== request before =============="); return true; } @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { System.out.println("============== request =============="); } @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { System.out.println("============== request completion =============="); }}
2. 创建类 DefInterceptorConfig,并继承于 WebMvcConfigurerAdapter,重写其 addInterceptors 方法,将自定义拦截器加入拦截器链中:
@Configurationpublic class DefInterceptorConfig extends WebMvcConfigurerAdapter { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new WebInterceptor()).addPathPatterns("/**"); }}
3. 启动应用程序,访问接口,查看自定义拦截器输出如下:
本文就分享到此了,内容不少,各位好好消化下。下一课我将大家学习 Spring Boot 整合常用技术框架的方法。
阅读全文: http://gitbook.cn/gitchat/column/5b4fd439bf8ece6c81e44cfb