学习目标
- 微服务架构的演进——推导单体架构到微服务架构一路走来的根本原因。
- 深层次理解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
@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;
@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要怎么做:
导包
2.创建配置文件
mybatis配置举例:
配置spring-mybatis.xml
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;
@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);
}
}