基本介绍
微服务最早由Martin Fowler与James Lewis于2014年共同提出,微服务架构风格是一种使用一套小服务来开发单个应用的方式途径,每个服务运行在自己的进程中,并使用轻量级机制通信,通常是HTTP API,这些服务基于业务能力构建,并能够通过自动化部署机制来独立部署,这些服务使用不同的编程语言实现,以及不同数据存储技术,并保持最低限度的集中式管理。
而所谓服务,一定要区别于系统,服务一个或者一组相对较小且独立的功能单元,是用户可以感知最小功能集。
单体架构在规模比较小的情况下工作情况良好,但是随着系统规模的扩大,它暴露出来的问题也越来越多,主要有以下几点:
1.复杂性逐渐变高
比如有的项目有几十万行代码,各个模块之间区别比较模糊,逻辑比较混乱,代码越多复杂性越高,越难解决遇到的问题。
2.技术债务逐渐上升
公司的人员流动是再正常不过的事情,有的员工在离职之前,疏于代码质量的自我管束,导致留下来很多坑,由于单体项目代码量庞大的惊人,留下的坑很难被发觉,这就给新来的员工带来很大的烦恼,人员流动越大所留下的坑越多,也就是所谓的技术债务越来越多。
3.部署速度逐渐变慢
这个就很好理解了,单体架构模块非常多,代码量非常庞大,导致部署项目所花费的时间越来越多,曾经有的项目启动就要一二十分钟,这是多么恐怖的事情啊,启动几次项目一天的时间就过去了,留给开发者开发的时间就非常少了。
4.阻碍技术创新
比如以前的某个项目使用struts2写的,由于各个模块之间有着千丝万缕的联系,代码量大,逻辑不够清楚,如果现在想用spring mvc来重构这个项目将是非常困难的,付出的成本将非常大,所以更多的时候公司不得不硬着头皮继续使用老的struts架构,这就阻碍了技术的创新。
5.无法按需伸缩
比如说电影模块是CPU密集型的模块,而订单模块是IO密集型的模块,假如我们要提升订单模块的性能,比如加大内存、增加硬盘,但是由于所有的模块都在一个架构下,因此我们在扩展订单模块的性能时不得不考虑其它模块的因素,因为我们不能因为扩展某个模块的性能而损害其它模块的性能,从而无法按需进行伸缩。
2:优缺点
特点
易于开发和维护
由于微服务单个模块就相当于一个项目,开发这个模块我们就只需关心这个模块的逻辑即可,代码量和逻辑复杂度都会降低,从而易于开发和维护。
启动较快
这是相对单个微服务来讲的,相比于启动单体架构的整个项目,启动某个模块的服务速度明显是要快很多的。
局部修改容易部署
在开发中发现了一个问题,如果是单体架构的话,我们就需要重新发布并启动整个项目,非常耗时间,但是微服务则不同,哪个模块出现了bug我们只需要解决那个模块的bug就可以了,解决完bug之后,我们只需要重启这个模块的服务即可,部署相对简单,不必重启整个项目从而大大节约时间。
技术栈不受限
比如订单微服务和电影微服务原来都是用java写的,现在我们想把电影微服务改成nodeJs技术,这是完全可以的,而且由于所关注的只是电影的逻辑而已,因此技术更换的成本也就会少很多。
按需伸缩
我们上面说了单体架构在想扩展某个模块的性能时不得不考虑到其它模块的性能会不会受影响,对于我们微服务来讲,完全不是问题,电影模块通过什么方式来提升性能不必考虑其它模块的情况。
运维要求较高
对于单体架构来讲,我们只需要维护好这一个项目就可以了,但是对于微服务架构来讲,由于项目是由多个微服务构成的,每个模块出现问题都会造成整个项目运行出现异常,想要知道是哪个模块造成的问题往往是不容易的,因为我们无法一步一步通过debug的方式来跟踪,这就对运维人员提出了很高的要求。
分布式的复杂性
对于单体架构来讲,我们可以不使用分布式,但是对于微服务架构来说,分布式几乎是必会用的技术,由于分布式本身的复杂性,导致微服务架构也变得复杂起来。
接口调整成本高
比如,用户微服务是要被订单微服务和电影微服务所调用的,一旦用户微服务的接口发生大的变动,那么所有依赖它的微服务都要做相应的调整,由于微服务可能非常多,那么调整接口所造成的成本将会明显提高。
重复劳动
对于单体架构来讲,如果某段业务被多个模块所共同使用,我们便可以抽象成一个工具类,被所有模块直接调用,但是微服务却无法这样做,因为这个微服务的工具类是不能被其它微服务所直接调用的,从而我们便不得不在每个微服务上都建这么一个工具类,从而导致代码的重复。
2:技术列表
服务开发 Springboot、Spring、SpringMVC 、mybatis
服务注册与发现 Eureka、Consul、Zookeeper等
服务调用 REST、RPC、gRPC
服务熔断器 Hystrix
负载均衡 Ribbon、Nginx等
服务调用(客户端调用服务发简单工具) Feign等
消息队列 RabbitMQ、ActiveMQ,kafka等
服务配置中心管理 SpringCloudConfig、Chef等
服务路由(API网关) Zuul等
服务部署 Docker、OpenStack等
事件消息总线 SpringCloud Bus
数据缓存redis
3:环境要求
–jdk1.8:Spring Boot 推荐jdk1.7及以上;java version "1.8.0_112"
–maven3.x:maven 3.3以上版本;
–IntelliJIDEA2017:IntelliJ IDEA 2017.2.2 x64、STS
–SpringBoot 2.2.2.RELEASE
给maven 的settings.xml配置文件的profifiles标签添加:
springboot程序
1:第一个springboot程序helloworld
需求:浏览器发送一个hello请求,返回服务器返回一个helloworld
1:向导创建项目
使用Spring Initializer快速创建Spring Boot项目
注意:选择需要自己的模块,向导会联网创建Spring Boot项目;
2:编写后台程序
现在的后台程序其实就是springmvc的程序,添加对应的注解
启动主程序测试
Springboot项目创建好之后,会自动生成一个主程序,主程序的名字为projectNameApplication,主程序上有一个固定的注解@SpringBootApplication
启动主程序之后就相当于将项目发布了,通过浏览器访问对应的后台程序。
注意:所有的后台程序必须位于主程序的子包中
2:目录介绍
resources文件夹中目录结构
static:保存所有的静态资源; js css images;
templates:保存所有的模板页面;(Spring Boot默认jar包使用嵌入式的Tomcat,默认不支持JSP页 面);可以使用模板引擎(freemarker、thymeleaf);
application.properties:Spring Boot应用的配置文件;可以修改一些默认设置;
3:helloWorld探究
1:POM文件
所有的springboot项目都自动的继承于一个父项目,该项目对依赖的版本做了统一的管理;
Spring Boot的版本仲裁中心; 以后我们导入依赖默认是不需要写版本;(没有在dependencies里面管理的依赖自然需要声明版本号)
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.x.x.RELEASE</version>
<relativePath/>
</parent>
继承 spring-boot-starter-parent 来作为父项目, spring-boot-starter-parent的父项目又是Spring Boot Dependencies,
已经定义好了很多包的依赖,可以统一项目的依赖,避免后续的版本冲突。在dependencies里的部分配置可以不用填写version信息,这些version信息会从spring-boot-dependencies里得到继承。
2:启动类
spring-boot-starter:spring-boot场景启动器;帮我们导入了web模块正常运行所依赖的组件
Spring Boot将所有的功能场景都抽取出来,做成一个个的starters(启动器),只需要在项目里面引入这些starter 相关场景的所有依赖都会导入进来。
要用什么功能就导入什么场景的启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
主程序
@SpringBootApplication: Spring Boot应用标注在某个类上说明这个类是SpringBoot的主配置类,
SpringBoot 就应该运行这个类的main方法来启动SpringBoot应用;
@SpringBootConfiguration:Spring Boot的配置类; 标注在某个类上,表示这是一个Spring Boot的配置类;
@Configuration: 配置类上来标注这个注解; 配置类 ----- 配置文件;配置类也是容器中的一个组件;@Component
@EnableAutoConfiguration:开启自动配置功能;以前我们需要配置的东西,Spring Boot帮我们自动配置;
会给容器中导入非常多的自动配置类(xxxAutoConfiguration);就是给容器中导入这个场景需要的所有组件, 并配置好这些组件;
Spring Boot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值,将这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作;以前我们需要自己配置的东 西,自动配置类都帮我们;
如何自定义一个配置类? - 就是一个简单的类
在一个类上加入@Configuration 注解,表明这就是一个配置类。在类中的方法上使用@Bean 进行注释,并返回某个类的一个实例,
表明这个方法是需要被Spring进行管理的bean。@Bean 如果不指定名称的话,默认使用方法的名称,也就是小写的名称。
任何一个标注了@Bean的方法,其返回值将作为一个bean定义注册到Spring的IoC容器,方法名将默认成该bean定义的id。
如何测试IOC中的bean呢?
Main方法中的主启动类其实有一个返回值,通过getBean的方法可以获取容器中的bean。
在springboot中@Component注解仍然可以使用。
5:配置文件
SpringBoot使用一个全局的配置文件,配置文件名是固定的; 配置文件的作用:修改SpringBoot自动配置的默认值;SpringBoot在底层都给我们自动配置好
•application.properties
•application.yml
YAML(YAML Ain't Markup Language)
YAML A Markup Language:是一个标记语言
YAML isn't Markup Language:不是一个标记语言;
标记语言: 以前的配置文件;大多都使用的是 xxxx.xml文件;
YAML:以数据为中心,比json、xml等更适合做配置文件;
YAML:配置例子
server:
port: 8081
Xml:
<server>
<port>8081</port>
</server>
1:yml基本语法
k:(空格)v:表示一对键值对(空格必须有);
以空格的缩进来控制层级关系;只要是左对齐的一列数据,都是同一个层级的
server:
port: 8081
servlet:
context-path: /first
2:值的类型
1:简单字面量类型
k: v:字面直接来写; 字符串默认不用加上单引号或者双引号;
"":双引号;不会转义字符串里面的特殊字符;特殊字符会作为本身想表示的意思
name: "zhangsan \n lisi":输出;zhangsan 换行 lisi
'':单引号;会转义特殊字符,特殊字符最终只是一个普通的字符串数据
name: ‘zhangsan \n lisi’:输出;zhangsan \n lisi
注意:
1:将配置文件中的内容和bean对象进行映射,需要在实体类上添加 @ConfigurationProperties(prefix = "配置文件中的前缀"),它默认是从全局的配置文件中取值
2:必须要将javabean添加至容器中需要使用 @Component 注解
2:对象、MAP的值
k: v:在下一行来写对象的属性和值的关系;注意缩进 ,注意 : 不要自己引用自己,在一个类中引用了另外一个类可以这样编写
对象还是k: v的方式
friends:
name: zhangsan
age: 20
行内写法:
friends: {name: zhangsan,age: 18}
3、数组、集合
用- 值表示数组中的一个元素
Friends
- xiaoming
- xiaoli
行内写法
Friends: [xiaoming,xiaoli]
注意:
1:如果想将配置文件中的数据注入到javabean(类)中,需要在实体类上添加 @ConfigurationProperties(prefix = "配置文件中的前缀"),它默认是从全局的配置文件中取值
2:必须要将javabean添加至容器中需要使用 @Component 注解, javabean中的属性一定要和配置文件中的key一模一样。
3:添加@ConfigurationProperties注解后,idea上面爆红的话不是配置问题,spring boot Configuration Annotation Proessor not found in classpath,
是缺少了依赖,需要在中添加配置文件处理器的配置,配置了处理器之后就不会在爆红了,而且配置的时候会有提示
上面的做法是将配置文件中的内容和bean对象进行映射,如果只有少量的属性需要赋值的话,可以直接在属性上加@Value("${key.value}")的注解进行取值或者直接赋值,
但是他不支持复杂的数据进行赋值,比如对象,集合等。一般只有简单的取值会使用。采用完全映射的时候不需要使用@value这个注解。
5:上面的做法都是从全局的配置文件中读取一些值,如果全部写在全局的配置文件中的话,那么会显得很是臃肿,springboot还可以从指定的配置文件中读取相关的值。
可以使用@PropertySource(value = {"classpath:xxx.properties"})来指定具体的文件,注意这个注解要个@ConfigurationProperties(prefix = "配置文件中的前缀")一起使用
注意引号的位置。
3:配置类
Springboot给容器中添加组件的方式可以使用传统的添加spring配置文件的方式或者简单的注解方式,也可以写一个配置类。Springboot意在给配置文件做减肥,
所以官方建议我们需要添加组件的话可以使用配置类的方式。配置类中方法上添加@Bean即可,他会将方法的返回值添加到容器中。通过main方法中调用方法的返回值可以在容器中获取这个bean.
注意:配置类上一定要加一个@Configuration注解,标注这个类是一个配置类
了解下:
4:多环境配置profile
1:properties多profile
我们在主配置文件编写的时候,文件名可以是 application-{profile}.properties/yml
如:application-dev.properties, application-test.properties,application-prod.properties
如果不去指导使用哪一个配置文件的话,则默认使用application.properties的配置;
如果需要制定的话,则可以使用 spring.profiles.active=dev 来进行指定
2: yml多文档快
如果配置文件使用yml方式的话,yml支持多文档快的配置方式
使用 --- 进行分割,每一个快可以使用spring.profiles: dev 来给每一块起个名字
然后使用spring.profiles.active: test 来进行激活某一块的配置
3:激活指定的profile
如果使用properties进行多文件配置方式的话,则默认使用application.properties的配置,
如果需要指定的话,则可以使用 spring.profiles.active=dev 来进行激活。
5:配置文件位置
springboot 启动会扫描以下位置的application.properties或者application.yml文件作为Spring boot的默认配置文件
–file:./config/ 项目下的config目录下的配置文件,优先级最高
–file:./ 项目下的配置文件
–classpath:/config/ resources下的config目录下的配置文件
–classpath:/ resources下的配置文件
优先级由高到底,高优先级的配置会覆盖低优先级的配置; SpringBoot会从这四个位置全部加载主配置文件;相冲突的会覆盖,互补配置;
6:自动配置原理 - 记住
精髓:
1)、SpringBoot启动会加载大量的自动配置类,META-INF/spring.factories
2)、查找我们需要的功能有没有SpringBoot默认写好的自动配置类;
3)、我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件有,我们就不需要再来配置了)
4)、给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们就可以在配置文件中指定这些属性的值;
配置的时候一般是@ConditionalOnProperty(prefix = "XXX")的前缀加具体的属性值,
如果没有属性的话则说明只有一个可以配置的,直接使用前缀即可。
注意:
xxxxAutoConfifigurartion:自动配置类;给容器中添加组件配置信息。
细节:注解的简单了解:
@Conditional是一个衍生的判断注解,必须是@Conditional指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效;
自动配置类必须在一定的条件下才能生效; 我们怎么知道哪些自动配置类生效?
我们可以在配置文件中启用 debug=true属性;来让控制台打印自动配置报告,这样我们就可以很方便的知道哪些自动配置类生效;