学习目标
- 微服务架构的演进——推导单体架构到微服务架构一路走来的根本原因。
- 深层次理解spring的问题。
- 理解spring一路走来所做的努力。
- springboot对spring的优化。
- springboot使用。
第1章:微服务架构的演进
1、单体架构
任何一个网站在发布初期几乎都不可能立马就拥有庞大的用户流量和海量数据,都是在不停地试错过程中一步一步演变其自身架构,满足其自身业务。比如现在能够抗住双十一这么大流量的淘宝,它的技术最早用的是 LAMP(Linux+Apache+Mysql+Php)。
实际上,架构越复杂,意味着业务的体量越庞大。
对于一个刚刚起步的项目,我们会选择最简单最快速的方式来实现。而单体架构是最好的选择,目前很多的传统软件行业仍然采用这类的架构。
一般的实施方案是,把所有的功能模块都打包在一个(jar、war),并且部署在一个 web 容器下,比如 tomcat、weblogic、jboss 中运行
2、集群架构
一旦用户量以及流量开始增加,服务器的性能就会遇到瓶颈,这个时候必须要对系统架构做调整以及优化。而在这个阶段主要需要解决的问题是提升业务系统的并行处理能力,降低单机系统负载,以便支撑更多的用户访问操作。
集群就是一种很好的方式,它可以把多台独立的服务器通过网络连接进行组合,对外形成一个整体提供服务。当一台服务器的处理能力接近或已超出其容量上限时,我们不会去尝试换一个更高性能的服务器,因为投入产出比不高,一般的做法就是采用集群技术,通过增加新的服务器来分散并发访问流量,只要业务系统能够随意支持服务器的横向扩容,那么从理论上来说就应该无惧任何挑战,从而实现可伸缩性和高可用性架构。
3、业务垂直化拆分
虽然通过集群可以提升并行处理能力以及对于高可用的实现,但是同时还需要考虑到业务的复杂度,如果仍然把所有的业务逻辑全部耦合在一起放在一个 war 包中来管理,那对于代码的维护和扩展来说是非常困难的。而且如果某个业务功能出现故障,会导致整个系统不可用。所以这个阶段要做的就是降低业务的耦合度,提升系统的容错性。
所以这个时候可以对业务进行垂直化拆分,简单来说,就是可以按照系统的业务功能拆分出多个业务模块,比如电商网站,会拆分出:首页、用户、搜索、订单、支付、商品等子系统。每个子系统由不同的业务团队负责。
4、服务化改造
随着对业务系统进行垂直化改造之后,以业务功能纬度拆分出来多个子系统,而在各个子系统中,会存在比较多的共享业务,比如用户信息查询,在支付业务中会涉及到、在首页中也会涉及到。那么势必会造成重复开发产生非常多的冗余代码。那么这个时候就引入了服务化改造的思想,也就是 SOA把一些通用的、会被多个上层服务调用的模块独立拆分出来,形成一些共享的基础服务。这些被拆分出来的共享服务相对来说是比较独立,并且可重用。 比如用户管理服务,包含用户注册、用户查询等功能。比如单点登录服务;
SOA 的核心目标就是通过服务的流程化来实现业务的灵活性,而这个流程化其实就是由一系列相关联的任务组成,这一系列相关联的任务可以通过一系列的服务组合来实现具体的业务功能SOA 面向服务架构,从语义上说,它与面向过程、面向对象、面向组件一样,是一种软件组建及开发的方式。所以在 SOA 中,服务是最核心的抽象手段,业务被划分为一些列粗粒度的业务服务和业务流程
SOA 中更强调 ESB 企业服务总线,企业服务总线可以使得服务之间的交互是动态的,以及服务位置是透明的。这样的好处是服务的调用者和服务的提供者之间是高度解耦的。从而使得服务有更高的灵活性以及隔离性。
ESB: 是从面相服务架构(SOA)发展过来的,主要是对多个系统中的服务调用者和服务提供者的解耦。ESB 本身提供了服务暴露、接入、协议转化、数据格式转化、路由等功能。
SOA 主要解决的问题是:
- 信息孤岛
- 互联互通
- 业务重用
5、微服务架构
业务系统实施服务化改造后,原本共享的业务被拆分,形成可复用的服务,可以在最大程度上避免共享业务的重复建设、资源连接瓶颈等问题出现。那么那些被拆分出来的服务,是否也需要以业务功能为维度来进行拆分,使之能够独立进行部署,以降低业务藕合和提升容错性呢?
微服务并不是一种新思想的方法。它更像是一种思想的精炼,是一种服务化思想的最佳实践方向而已,所以我认为微服务其实是在 SOA 思路下,随着各个企业对于服务化治理上不断地完善,以及对软件的交付链路以及基础设施逐步成熟之下的一种自然的产物。 微服务也是一种面向服务的架构模型,只是它更强调服务的粒度。也就是服务的职责更加单一更加精炼我们也可以把 SOA 看成是微服务的超集。 也就是说多个微服务可以组成一个 soa 服务。
6、微服务和 SOA 架构的区别
经常会有同学问,微服务和 SOA 架构有什么区别。这个区别一定要从架构的发展过程来了解。这两种架构模式,其实本质上应该是在分布式架构这条时间线上,基于服务化思想的不断完善,以及基础设施的逐步成熟之下的一种升级。既然存在于时间线的先后,那也就意味着,这两种架构模式所关注的点不一样
- SOA 关注的是服务的重用性、以及解决企业内部的信息孤岛问题。
- 微服务关注的是解耦,解耦和可重用性在特定的角度来看是一样,但本质上是不同的。解耦是降低业务之间的耦合度(也就是微服务关注的服务粒度),而可重用性关注的是服务的复用。
- 微服务会使用更轻量级的通信协议,使用 Restful 风格的 API。轻量级协议可以很好地支持跨语言,使得语言生态更加丰富。
- 微服务会更多地关注 Devops 持续交付,因为服务粒度更细使得开发运维变得更加重要。所以微服务与容器化技术的结合更加紧密。
- SOA 应该是微服务的超集。
随着架构的不断演进,底层框架为了满足需求也在不断地努力,spring为了简化开发,从最开始xml配置到后面的注解配置,其实都在简化开发这条路上不断努力,但是spring依然做的不够,而且也无法满足微服务架构的各种组件引入带来的各种配置,这个时候springboot产生了,那么接下来,我们先来看看spring做出了哪些努力,又有什么不足之处。
第2章:Spring的演进
spring的核心思想实际上就是IoC、DI、AOP三大块,它的核心目的就是为了简化开发,容器化管理我们的bean对象。基于这个目的,spring做了很多努力,在一定程度上,spring确实简化了开发,但是随着业务复杂度越来越深,功能需求越来越大,各种组件的出现,spring的使用也开始变的吃力,这个吃力实际上就体现在两个方便:bean对象注入到IoC容器的过程越来越麻烦;配置文件越来越多,无法统一管理。事实上,spring也做过很多的努力,我们现在看看它的发展史吧。
先创建spring的maven项目,然后通过集成springMVC和Mybatis举例
项目创建
添加依赖包
4.0.0
com.example
spring-vip-jt-demo
1.0-SNAPSHOT
war
spring-vip-jt-demo Maven Webapp
http://www.example.com
UTF-8
1.8
1.8
5.2.8.RELEASE
3.5.6
org.springframework
spring-core
${spring.version}
org.springframework
spring-web
${spring.version}
org.springframework
spring-oxm
${spring.version}
org.springframework
spring-tx
${spring.version}
org.springframework
spring-jdbc
${spring.version}
org.springframework
spring-webmvc
${spring.version}
org.springframework
spring-aop
${spring.version}
org.springframework
spring-context-support
${spring.version}
org.springframework
spring-test
${spring.version}
org.mybatis
mybatis
${mybatis.version}
junit
junit
4.11
test
org.slf4j
slf4j-log4j12
1.8.0-alpha0
test
javax.servlet
javax.servlet-api
4.0.0
provided
javax.servlet.jsp
jsp-api
2.2
javax.servlet
jstl
1.2
org.mybatis
mybatis-spring
2.0.6
mysql
mysql-connector-java
5.1.39
org.apache.maven
maven-model
3.0
配置web.xml
Archetype Created Web Application
index.jsp
CharacterEncodingFilter
org.springframework.web.filter.CharacterEncodingFilter
encoding
utf-8
forceEncoding
true
CharacterEncodingFilter
/*
springMVC
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:springMVC.xml
1
true
springMVC
/
contextConfigLocation
classpath:applicationContext.xml
org.springframework.web.context.ContextLoaderListener
配置springMCV.xml
配置applicationContext.xml
创建a.jsp
${version}
${name}:${msg}
(1)Spring1.x时代
在此时因为jdk1.5刚刚出来,注解开发并未盛行,因此一切Spring配置都是xml格式,想象一下所有的bean都用xml配置,细思极恐啊,心疼那个时候的程序员2秒
在applicationContext.xml中配置
controller
package com.example.demo01;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author Eclipse_2019
* @create 2021/7/31 17:52
*/
@Controller
public class HelloController01 {
@Autowired
private HelloService01 helloService01;
@RequestMapping("/hello01")
public ModelAndView hello01(@RequestParam String name){
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("version","1.X版本");
modelAndView.addObject("name",name);
modelAndView.addObject("msg",helloService01.sayHello(name));
modelAndView.setViewName("a");
return modelAndView;
}
}
service
public class HelloService01 {
public String sayHello(String name){
return "你真棒!"+name;
}
}
测试
public class DemoMain {
public static void main(String[] args) {
// ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
ApplicationContext context = new FileSystemXmlApplicationContext("classpath:applicationContext.xml");
System.out.println(context.getBean(HelloService01.class));
}
}
Tomcat启动。
(2)Spring2.x时代
Spring引入了注解开发,但是因为并不完善,因此并未完全替代xml,此时的程序员往往是把xml与注解进行结合,貌似我们之前都是这种方式。
- @Required/@Repository/@Aspect
spring 2.5
- @Component
- @Service
- @Controller
- @RequestMapping
在applicationContext.xml增加配置
controller
@Controller
public class HelloController02 {
@Autowired
private HelloService02 helloService02;
@RequestMapping("/hello02")
public ModelAndView hello(@RequestParam String name){
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("version","2.X版本");
modelAndView.addObject("name",name);
modelAndView.addObject("msg",helloService02.sayHello(name));
modelAndView.setViewName("a");
return modelAndView;
}
}
service
@Service
public class HelloService02 {
public String sayHello(String name){
return "你好帅啊!";
}
}
测试
public class Demo02Main {
public static void main(String[] args) {
ApplicationContext context = new FileSystemXmlApplicationContext("classpath:applicationContext.xml");
System.out.println(context.getBean(HelloController02.class));
}
}
Tomcat启动。
3、Spring3.x时代
(1)Configuration
3.0以后Spring的注解已经非常完善了,因此Spring推荐大家使用完全的java配置来代替以前的xml,不过似乎在国内并未推广盛行。然后当SpringBoot来临,人们才慢慢认识到java配置的优雅。
- @Configuration 去XML化
configuration
package com.example.demo03;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Eclipse_2019
* @create 2021/8/3 17:16
*/
@Configuration
public class SpringConfiguration {
@Bean
public HelloService03 helloService03(){
return new HelloService03();
}
@Bean
public HelloController03 helloController03(){
return new HelloController03();
}
}
controller同2.x版本
service
public class HelloService03 {
public String sayHello(String name){
return "你好帅啊!";
}
}
测试同上。
(2)Import
案例见spring-vip-jt-demo的demo04中//
(3)Enable
案例见spring-vip-jt-demo的demo05中。
- @Enable模块驱动
(3)Spring4.x时代
- @Conditional
案例见spring-vip-jt-demo的demo06中
springboot条件注解
@ConditionalOnBean(仅仅在当前上下文中存在某个对象时,才会实例化一个Bean)。@ConditionalOnClass(某个class位于类路径上,才会实例化一个Bean)。@ConditionalOnExpression(当表达式为true的时候,才会实例化一个Bean)。@ConditionalOnMissingBean(仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean)。@ConditionalOnMissingClass(某个class类路径上不存在的时候,才会实例化一个Bean)。@ConditionalOnNotWebApplication(不是web应用)。
5、集成Mybatis
集成Redis或者mybatis要怎么做:
1.导包2.创建配置文件
mybatis配置举例:
配置spring-mybatis.xml
配置mybatis.xml
通过上面这种方式才能把mybatis注入到我们springIOC容器中,但是这种方式太麻烦了。怎么做更简便呢?
第3章:SpringBoot应运而生
1、简介
(1)是什么
Springboot是Spring项目中的一个子工程,与我们所熟知的Spring-framework 同属于spring的产品:
官网地址:https://spring.io/projects
actuator:https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.endpoints
我们可以看到下面的一段介绍:
Takes an opinionated view of building production-ready Spring applications. Spring Boot favors convention over configuration and is designed to get you up and running as quickly as possible.
翻译一下:
用一些固定的方式来构建生产级别的spring应用。Spring Boot 推崇约定大于配置的方式以便于你能够尽可能快速的启动并运行程序。
其实人们把Spring Boot 称为搭建程序的脚手架。其最主要作用就是帮我们快速的构建庞大的spring项目,并且尽可能的减少一切xml配置,做到开箱即用,迅速上手,让我们关注与业务而非配置。
(2)为什么
java一直被人诟病的一点就是臃肿、麻烦。当我们还在辛苦的搭建项目时,可能Python程序员已经把功能写好了。为了解决这些问题,然后出现了一个框架叫做Spring,Spring翻译过来就是春天,意味着程序员的春天来了,但是真的是这样吗?其实大佬们创建出Spring这个框架本质上就是为了给程序员们减负,为了使我们构建项目更加便捷,为了让我们开发更加快速,但是呢,早期的Spring框架还是会有很多问题,最主要的两点就是:
- 复杂的配置
项目各种配置其实是开发时的损耗, 因为在思考 Spring 特性配置和解决业务问题之间需要进行思维切换,所以写配置挤占了写应用程序逻辑的时间。
- 一个是混乱的依赖管理
项目的依赖管理也是件吃力不讨好的事情。决定项目里要用哪些库就已经够让人头痛的了,你还要知道这些库的哪个版本和其他库不会有冲突,这难题实在太棘手。并且,依赖管理也是一种损耗,添加依赖不是写应用程序代码。一旦选错了依赖的版本,随之而来的不兼容问题毫无疑问会是生产力杀手。
而SpringBoot让这一切成为过去!
Spring Boot 简化了基于Spring的应用开发,只需要“run”就能创建一个独立的、生产级别的Spring应用。Spring Boot为Spring平台及第三方库提供开箱即用的设置(提供默认设置,存放默认配置的包就是启动器),这样我们就可以简单的开始。多数Spring Boot应用只需要很少的Spring配置。
我们可以使用SpringBoot创建java应用,并使用java –jar 启动它,就能得到一个生产级别的web工程。
(3)特性
Spring Boot 主要目标是:
- 为所有 Spring 的开发者提供一个非常快速的、广泛接受的入门体验
- 开箱即用(启动器starter-其实就是SpringBoot提供的一个jar包),但通过自己设置参数(.properties),即可快速摆脱这种方式。
- 提供了一些大型项目中常见的非功能性特性,如内嵌服务器、安全、指标,健康检测、外部化配置等
- 绝对没有代码生成,也无需 XML 配置。
自动配置、Starter组件、Actuator、命令行界面(CLI) 是 Spring Boot 最重要的 4 大核心特性,其中 CLI 是 Spring Boot 的可选特性,虽然它功能强大,但也引入了一套不太常规的开发模型,因而这里仅关注其它 3 种特性。
2、快速入门
接下来,我们就来利用Springboot快速搭建一个web工程
(1)创建工程
(2)编写controller
代码:
@RestController
public class HelloController {
@GetMapping("hello")
public String hello(){
return "hello, spring boot!";
}
}
(3)启动项目
接下来,我们运行main函数,查看控制台:
- 监听的端口是8080
打开页面访问:http://localhost:8080/hello
3、集成mybatis
引包
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.1.3
mysql
mysql-connector-java
配置文件
spring.datasource.username=root
spring.datasource.password=jingtian
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.8.74:3306/test
server.port=8080
mybatis.mapper-locations=classpath:*Mapper.xml
controller
package com.example.springbootvipjtdemo.demo01.controller;
import com.example.springbootvipjtdemo.demo01.dao.entity.User;
import com.example.springbootvipjtdemo.demo01.services.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
/**
* @author Eclipse_2019
* @create 2021/8/2 16:33
*/
@RestController
public class HelloController {
@Autowired
IUserService userService;
@PostMapping("addUser")
public String addUser(@RequestBody User user){
int a = userService.insert(user);
if(a!=0){
return "插入成功";
}else{
return "失败";
}
}
}
service
package com.example.springbootvipjtdemo.demo01.services;
import com.example.springbootvipjtdemo.demo01.dao.entity.User;
public interface IUserService {
int insert(User user);
}
package com.example.springbootvipjtdemo.demo01.services.impl;
import com.example.springbootvipjtdemo.demo01.dao.entity.User;
import com.example.springbootvipjtdemo.demo01.dao.mappers.UserMapper;
import com.example.springbootvipjtdemo.demo01.services.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author Eclipse_2019
* @create 2021/8/2 16:47
*/
@Service
public class UserServiceImpl implements IUserService {
@Autowired
UserMapper userMapper;
@Override
public int insert(User user) {
return userMapper.insert(user);
}
}
dao
package com.example.springbootvipjtdemo.demo01.dao.mappers;
import com.example.springbootvipjtdemo.demo01.dao.entity.User;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper {
int insert(User user);
}
mapper
insert into user(username,age) values(#{userName,jdbcType=VARCHAR},#{age,jdbcType=BIGINT})
启动类
package com.example.springbootvipjtdemo.demo01;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@MapperScan("com.example.springbootvipjtdemo.demo01.dao.mappers")
@SpringBootApplication(scanBasePackages = {"com.example.springbootvipjtdemo.demo01"})
public class SpringBootDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootDemoApplication.class, args);
}
}