1. 前言
1.1 Spring Cloud五大组件
-
注册和发现服务——
-
负载均衡:
-
客户端负载均衡——
-
平衡服务端负载:——(也依赖Ribbon,只是将调用方式RestTemplete 更改成Service 接口)
-
-
断路器——
-
服务网关——
-
分布式配置——
三层架构 MVC 框架: Spring IOC AOP SpringBoot:新一代的javaEE开发标准,自动装配 模块化 all in one 模块化开发=====>all in one 代码没有变化 微服务的四个核心问题? 1.服务多,如何访问客户端? 1.服务多,如何访问客户端? 2.这么多服务?如何在服务之间通信? 3.这么多服务?如何治理? 4.服务挂了怎么办? 解决方案: Spring Cloud 生态! SprintBoot 1.Spring Cloud NetFlix 一站式解决方案 api网关:zuul组件 通信:Feign ---- HttpClient ---- Http通信方式,同步,阻塞 服务注册及发现:Eureka 熔断机制:Hystrix ... 2. Apache Dubbo Zookeeper:半自动!需要整合他人 API网关:不,找第三方组件(如整合)zull组件),或者自己实现 通信:Dubbo 是一个基于Java的高性能的RPC通信框架(性能比Feign强大) 服务注册及发现:Zookeeper 熔断机制:不,需要借助Hystrix Dubbo这个案并不完善 3. Spring Cloud Alibaba:最新的一站式解决方案!以上四个核心问题可以更简单 API网关: 通信: 服务注册及发现: 熔断机制: 万变不离其宗四个问题: 1. API网关 2. HTTP,RPC通信 3. 注册和发现 4. 熔断机制
1.3 常见面试题
1.1、 什么是微服务?
1.2 、如何独立通信微服务?
1.3 、SpringCloud 和 Dubbo有什么区别?
1.4 、SpringBoot 和 SpringCloud,请谈谈你对他们的理解
1.5 、什么是服务熔断?什么是服务降级?
1.6 、微服务的优缺点是什么?说说你在项目开发中遇到的坑。
1.7 、您所知道的微服务技术栈有哪些?列举一二
1.8、 Eureka和Zookeeper您可以提供服务注册和发现的功能。请谈谈两者的区别
2. 微服务概述
2.1 什么是微服务?
微服务(Microservice Architecture) 这是近年来流行的架构思想,关于它的概念很难一言以蔽之。
什么是微服务?这里引用我们ThoughtWorks 公司首席科学家 Martin Fowler 2014年提出的一段:
原文:https://martinfowler.com/articles/microservices.html
汉化:https://www.cnblogs.com/liuning8023/p/4493156.html
-
目前,行业对微服务没有统一的标准定义。
-
但一般来说,微服务架构是一种架构模式,或者是一种架构风格,,每一项服务都在自己独立的过程中运行,服务相互协调,相互配置,为用户提供最终价值,服务之间采用轻量级制()相互沟通,每项服务都围绕着特定的业务建设,可以独立部署到生产环境中,此外,应尽量避免统一、集中的服务管理机制,对于特定的服务,应根据业务,选择合适的语言、工具()为了构建它,可以有一个非常轻的集中管理来协调这些服务,可以用不同的语言编写服务,也可以用不同的数据存储。
-
微服务的核心是传统的一站式应用,根据业务分为服务,完全耦合,每个微服务提供单个业务功能服务,服务做一件事,从技术角度来看是一个小而独立的处理过程,类似的过程概念,可以单独启动或销毁,有自己独立的数据库。
2.2 微服务和微服务架构
它强调服务的大小。它关注某一点。它是一个服务应用程序,具体解决某一问题/提供着陆相应的服务。狭义地说,它可以被视为IDEA微服务项目或Moudel。
IDEA 使用工具Maven独立的小开发Moudel,具体使用SpringBoot一个开发的小模块,专业的事情交给专业的模块,一个模块做一件事。 强调的是一个个体,每个个体务或功能。
新的架构形式,Martin Fowler 于2014年提出。
微服务架构是一种架构模式。其体长将单个应用程序划分为一组小型服务。服务相互协调,相互配合,为用户提供最终价值。每项服务都在其独立的过程中运行,服务和服务之间采用轻量级通信机制相互合作,每项服务都围绕特定业务建设,可以独立部署到生产环境中。此外,应尽量避免统一、集中的服务管理机制。对于特定的服务,应根据业务上下文选择合适的语言和工具构建它。
2.3 微服务的优缺点
-
单一职责原则;
-
每一项服务都足够内聚,足够小,代码易理解,可以关注指定的业务功能或业务需求;
-
开发简单,开发效率高,一项服务可能一件事;
-
小团队可以单独开发微服务,该团队只需要2-5名开发人员;
-
微服务是开发阶段还是部署阶段,微服务都是松耦合和功能性服务;
-
微服务可以用不同的语言开发;
-
通过连续集成工具,如易于与第三方集成,微服务可以轻松灵活地集成自动部署,如jenkins,Hudson,bamboo;
-
微服务易于被开发人员理解、修改和维护,使小团队能够更加关注自己的工作成果,无需合作就能体现价值;
-
允许使用和整合最新技术的微服务;
-
开发人员应处理分布式系统的复杂性;
-
随着服务的增加,运维压力也在增加;
-
系统部署依赖问题;
-
服务间通信成本;
-
数据一致性问题;
-
系统集成测试问题;
-
性能和监控问题;
2.4 微服务技术栈有那些?
落地技术 | |
---|---|
服务开发 | SpringBoot、Spring、SpringMVC等 |
服务配置与管理 | Netfix公司的Archaius、阿里的Diamond等 |
注册和发现服务 | Eureka、Consul、Zookeeper等 |
服务调用 | Rest、PRC、gRPC |
服务熔断器 | Hystrix、Envoy等 |
负载均衡 | Ribbon、Nginx等 |
服务接口调用(客户端调用服务的简化工具) | Fegin等 |
消息队列 | Kafka、RabbitMQ、ActiveMQ等 |
服务配置中心管理 | SpringCloudConfig、Chef等 |
服务路由(API网关) | Zuul等 |
服务监控 | Zabbix、Nagios、Metrics、Specatator等 |
全链路追踪 | Zipkin、Brave、Dapper等 |
数据流操作开发包 | SpringCloud Stream(封装与Redis,Rabbit,Kafka等发送接收消息) |
时间消息总栈 | SpringCloud Bus |
服务部署 | Docker、OpenStack、Kubernetes等 |
-
各微服务框架对比
Netflix/SpringCloud | Motan | gRPC | Thrift | Dubbo/DubboX | |
---|---|---|---|---|---|
功能定位 | 完整的微服务框架 | RPC框架,但整合了ZK或Consul,实现集群环境的基本服务注册发现 | RPC框架 | RPC框架 | 服务框架 |
支持Rest | 是,Ribbon支持多种可拔插的序列号选择 | 否 | 否 | 否 | 否 |
支持RPC | 否 | 是(Hession2) | 是 | 是 | 是 |
支持多语言 | 是(Rest形式) | 否 | 是 | 是 | 否 |
负载均衡 | 是(服务端zuul+客户端Ribbon),zuul-服务,动态路由,云端负载均衡Eureka(针对中间层服务器) | 是(客户端) | 否 | 否 | 是(客户端) |
配置服务 | Netfix Archaius,Spring Cloud Config Server 集中配置 | 是(Zookeeper提供) | 否 | 否 | 否 |
服务调用链监控 | 是(zuul),zuul提供边缘服务,API网关 | 否 | 否 | 否 | 否 |
高可用/容错 | 是(服务端Hystrix+客户端Ribbon) | 是(客户端) | 否 | 否 | 是(客户端) |
典型应用案例 | Netflix | Sina | |||
社区活跃程度 | 高 | 一般 | 高 | 一般 | 2017年后重新开始维护,之前中断了5年 |
学习难度 | 中等 | 低 | 高 | 高 | 低 |
文档丰富程度 | 高 | 一般 | 一般 | 一般 | 高 |
其他 | Spring Cloud Bus为我们的应用程序带来了更多管理端点 | 支持降级 | Netflix内部在开发集成gRPC | IDL定义 | 实践的公司比较多 |
3. SpringCloud入门概述
3.1 SpringCloud是什么?
Spring官网:https://spring.io/
简介
SpringCloud,基于SpringBoot提供了一套微服务解决方案,包括服务注册与发现,配置中心,全链路监控,服务网关,负载均衡,熔断器等组件,除了基于NetFlix的开源组件做高度抽象封装之外,还有一些选型中立的开源组件。
SpringCloud利用SpringBoot的开发便利性,巧妙地简化了分布式系统基础设施的开发,SpringCloud为开发人员提供了快速构建分布式系统的一些工具,,他们都可以用SpringBoot的开发风格做到一键启动和部署。
SpringBoot并没有重复造轮子,它只是将目前各家公司开发的比较成熟,经得起实际考研的服务框架组合起来, 通过SpringBoot风格进行再封装,屏蔽掉了复杂的配置和实现原理,
SpringCloud是分布式微服务架构下的一站式解决方案,是各个微服务架构落地技术的集合体,俗称微服务全家桶。
3.2 SpringCloud和SpringBoot的关系
-
SpringBoot专注于开苏方便的开发单个个体微服务;
-
SpringCloud是关注全局的微服务协调整理治理框架,它将SpringBoot开发的一个个单体微服务,整合并管理起来,为各个微服务之间提供:配置管理、服务发现、断路器、路由、为代理、事件总栈、全局锁、决策竞选、分布式会话等等集成服务;
-
SpringBoot可以离开SpringCloud独立使用,开发项目,但SpringCloud离不开SpringBoot,属于依赖关系;
-
SpringBoot专注于快速、方便的开发单个个体微服务,SpringCloud关注全局的服务治理框架;
3.3 Dubbo 和 SpringCloud技术选型
目前成熟的互联网架构,应用服务化拆分+消息中间件
可以看一下社区活跃度:
https://github.com/dubbo
https://github.com/spring-cloud
Dubbo | SpringCloud | |
---|---|---|
服务注册中心 | Zookeeper | Spring Cloud Netfilx Eureka |
服务调用方式 | RPC | REST API |
服务监控 | Dubbo-monitor | Spring Boot Admin |
断路器 | 不完善 | Spring Cloud Netfilx Hystrix |
服务网关 | 无 | Spring Cloud Netfilx Zuul |
分布式配置 | 无 | Spring Cloud Config |
服务跟踪 | 无 | Spring Cloud Sleuth |
消息总栈 | 无 | Spring Cloud Bus |
数据流 | 无 | Spring Cloud Stream |
批量任务 | 无 | Spring Cloud Task |
严格来说,这两种方式各有优劣。虽然从一定程度上来说,后者牺牲了服务调用的性能,但也避免了上面提到的原生RPC带来的问题。而且REST相比RPC更为灵活,服务提供方和调用方的依赖只依靠一纸契约,不存在代码级别的强依赖,这个优点在当下强调快速演化的微服务环境下,显得更加合适。
很明显,Spring Cloud的功能比DUBBO更加强大,涵盖面更广,而且作为Spring的拳头项目,它也能够与SpringFramework、Spring Boot、Spring Data、Spring Batch等其他Spring项目完美融合,这些对于微服务而言是至关重要的。使用Dubbo构建的微服务架构就像组装电脑,各环节我们的选择自由度很高,但是最终结果很有可能 因为一条内存质量不行就点不亮了,总是让人不怎么放心,但是如果你是一名高手,那这些都不是问题;而SpringCloud就像品牌机,在Spring Source的整合下,做了大量的兼容性测试,保证了机器拥有更高的稳定性,但是如果要在使用非原装组件外的东西,就需要对其基础有足够的了解。
最为重要的是,DUBBO停止了5年左右的更新,虽然2017.7重启了。对于技术发展的新需求,需要由开发者自行拓展升级(比如当当网弄出了DubboX),这对于很多想要采用微服务架构的中小软件组织,显然是不太合适的,中小公司没有这么强大的技术能力去修改Dubbo源码+周边的一整套解决方案,并不是每一个公司都有阿里的大牛+真实的线上生产环境测试过。
3.4 SpringCloud能干嘛?
-
Distributed/versioned configuration 分布式/版本控制配置
-
Service registration and discovery 服务注册与发现
-
Routing 路由
-
Service-to-service calls 服务到服务的调用
-
Load balancing 负载均衡配置
-
Circuit Breakers 断路器
-
Distributed messaging 分布式消息管理
-
…
3.5 SpringCloud下载
官网:http://projects.spring.io/spring-cloud/
版本号有点特别:
spring cloud是一个由众多独立子项目组成的大型综合项目,每个子项目有不同的发行节奏,都维护着自己的发布版木号。spring cloud通过一个资源清单BOM(Bil1 of Materials)来管理每个版木的子项目清单。为避免与子项目的发布号混淆,所以没有采用版本号的方式,而是通过命名的方式。
SpringCloud没有采用数字编号的方式命名版本号,而是采用了伦敦地铁站的名称,,比如最早的Realse版本:Angel,第二个Realse版本:Brixton,然后是Camden、Dalston、Edgware,目前最新的是Hoxton SR4 CURRENT GA通用稳定版。
-
SpringCloud Netflix 中文文档:https://springcloud.cc/spring-cloud-netflix.html
-
SpringCloud 中文API文档(官方文档翻译版):https://springcloud.cc/spring-cloud-dalston.html
-
SpringCloud中国社区:http://springcloud.cn/
-
SpringCloud中文网:https://springcloud.cc
4. SpringCloud Rest
4.1 介绍
-
我们会使用一个Dept部门模块做一个微服务通用案例Consumer消费者(Client)通过REST调用Provider提供者(Server)提供的服务。
-
回顾Spring,SpringMVC,Mybatis等以往学习的知识。
-
Maven的分包分模块架构复习。
一个简单的Maven模块结构是这样的: -- app-parent: 一个父项目(app-parent)聚合了很多子项目(app-util\app-dao\app-web...) |-- pom.xml | |-- app-core ||---- pom.xml | |-- app-web ||---- pom.xml ...... 1234567891011
一个父工程带着多个Moudule子模块
MicroServiceCloud父工程(Project)下初次带着3个子模块(Module)
-
microservicecloud-api 【封装的整体entity/接口/公共配置等】
-
microservicecloud-consumer-dept-80 【服务提供者】
-
microservicecloud-provider-dept-8081 【服务消费者】
4.2 SpringCloud版本选择
SpringBoot | SpringCloud | 关系 |
---|---|---|
1.2.x | Angel版本(天使) | 兼容SpringBoot1.2x |
1.3.x | Brixton版本(布里克斯顿) | 兼容SpringBoot1.3x,也兼容SpringBoot1.4x |
1.4.x | Camden版本(卡姆登) | 兼容SpringBoot1.4x,也兼容SpringBoot1.5x |
1.5.x | Dalston版本(多尔斯顿) | 兼容SpringBoot1.5x,不兼容SpringBoot2.0x |
1.5.x | Edgware版本(埃奇韦尔) | 兼容SpringBoot1.5x,不兼容SpringBoot2.0x |
2.0.x | Finchley版本(芬奇利) | 兼容SpringBoot2.0x,不兼容SpringBoot1.5x |
2.1.x | Greenwich版本(格林威治) |
spring-boot-starter-parent | spring-cloud-dependencles | ||
---|---|---|---|
1.5.2.RELEASE | 2017-03 | Dalston.RC1 | 2017-x |
1.5.9.RELEASE | 2017-11 | Edgware.RELEASE | 2017-11 |
1.5.16.RELEASE | 2018-04 | Edgware.SR5 | 2018-10 |
1.5.20.RELEASE | 2018-09 | Edgware.SR5 | 2018-10 |
2.0.2.RELEASE | 2018-05 | Fomchiey.BULD-SNAPSHOT | 2018-x |
2.0.6.RELEASE | 2018-10 | Fomchiey-SR2 | 2018-10 |
2.1.4.RELEASE | 2019-04 | Greenwich.SR1 | 2019-03 |
4.3 创建工程
4.3.1 创建父工程
测试
-
创建一个空的maven项目,删除掉src,以便于创建子工程
-
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.koko</groupId> <artifactId>SpringCloud</artifactId> <version>1.0-SNAPSHOT</version> <modules> <module>springcloud-api</module> <module>springcloud-provider-dept-8081</module> </modules> <!--打包方式 pom--> <packaging>pom</packaging> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <junit.version>4.12</junit.version> <lombok.version>1.18.12</lombok.version> <log4j.version>1.2.17</log4j.version> </properties> <dependencyManagement> <dependencies> <!--springcloud的依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Hoxton.SR8</version> <type>pom</type> <scope>import</scope> </dependency> <!--springboot的依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.3.1.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <!--数据库--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.20</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.0.9</version> </dependency> <!--springboot启动器--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.3</version> </dependency> <!--日志测试~--> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>${log4j.version}</version> </dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> </dependency> </dependencies> </dependencyManagement> <build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.yml</include> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.yml</include> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources> </build> </project>
4.3.2 创建子模块springcloud-api
测试
-
模拟分布式原理,该子项目提供实体类
-
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>SpringCloud</artifactId> <groupId>com.koko</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>springcloud-api</artifactId> <!--当前的module自己需要的依赖,如果父依赖中已经配置了版本,这里就不用写了--> <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies> </project>
-
创建数据库(Naticat)
-
插入语句
insert into dept(deptno,dname,db_source) values(1,'开发部',DATABASE()); insert into dept(deptno,dname,db_source) values(2,'人事部',DATABASE()); insert into dept(deptno,dname,db_source) values(3,'财务部',DATABASE()); insert into dept(deptno,dname,db_source) values(4,'市场部',DATABASE()); insert into dept(deptno,dname,db_source) values(5,'运维部',DATABASE());
-
编写实体类Dept
@Data @NoArgsConstructor @Accessors(chain = true) public class Dept implements Serializable {//实体类 orm 类表关系映射 private long deptno;//主键 private String dname; //这个数据存在那个数据库的字段,微服务,一个服务对应一个数据库,同一个信息可能存在不同的数据库 private String db_source; public Dept(String dname) { this.dname = dname; } /* * 链式写法: * Dept dept = new Dept(); * * dept.setDeptNo(11).setDname('ssss').setDb_source('db01') * */ }
4.3.3 创建子模块springcloud-provider-dept-8081
-
服务的提供者的编写
测试
-
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>SpringCloud</artifactId> <groupId>com.koko</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>springcloud-provider-dept-8081</artifactId> <dependencies> <!--我们需要拿到实体类,所以要配置api module--> <dependency> <groupId>com.koko</groupId> <artifactId>springcloud-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <!--junit--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <!--test--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-test</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--jetty--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jetty</artifactId> </dependency> <!--热部署工具--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> </dependencies> </project>
-
资源包内容
-
1、application.yml
server: port: 8081 # mybatis的配置 mybatis: type-aliases-package: com.koko.pojo config-location: classpath:mybatis/mybatis-config.xml mapper-locations: classpath:mybatis/mapper/*.xml # spring的配置 spring: application: name: springcloud-provider-dept datasource: type: com.alibaba.druid.pool.DruidDataSource #数据库 driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/db01?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC username: root password: 123456
-
2、mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <setting name="cacheEnabled" value="true"/> </settings> </configuration>
-
3、DeptMapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.koko.dao.DeptDao"> <insert id="addDept" parameterType="Dept"> insert into dept (dname,db_source) values (#{dname},DATABASE()) </insert> <select id="queryById" resultType="Dept" parameterType="Long"> select * from dept where deptno = #{id} </select> <select id="queryAll" resultType="Dept"> select * from dept </select> </mapper>
-
代码包内容
-
1、DeptDao持久层
@Mapper @Repository public interface DeptDao { public boolean addDept(Dept dept); public Dept queryById(Long id); public List<Dept> queryAll(); }
-
2、DeptService业务层接口
public interface DeptService { public boolean addDept(Dept dept); public Dept queryById(Long id); public List<Dept> queryAll(); }
-
3、DeptServiceImpl业务层接口实现类
@Service public class DeptServiceImpl implements DeptService{ @Autowired private DeptDao deptDao; @Override public boolean addDept(Dept dept) { return deptDao.addDept(dept); } @Override public Dept queryById(Long id) { return deptDao.queryById(id); } @Override public List<Dept> queryAll() { return deptDao.queryAll(); } }
-
4、DeptController控制层
//提供Restful服务! @RestController public class DeptController { @Autowired DeptService deptService; @PostMapping("/dept/add") public boolean addDept(Dept dept) { return deptService.addDept(dept); } @GetMapping("/dept/get/{id}") public Dept get(@PathVariable("id") Long id) { return deptService.queryById(id); } @GetMapping("/dept/list") public List<Dept> queryAll() { return deptService.queryAll(); } }
-
5、DeptProvider_8081项目启动类
//启动类 @SpringBootApplication public class DeptProvider_8081 { public static void main(String[] args) { SpringApplication.run(DeptProvider_8081.class,args); } }
启动项目
-
查询某个用户
-
显示全部部门的信息
-
测试成功!!!
4.3.4 创建子模块springcloud-consumer-dept-80
-
客户端访问
测试
-
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>SpringCloud</artifactId> <groupId>com.koko</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>springcloud-consumer-dept-80</artifactId> <dependencies> <dependency> <groupId>com.koko</groupId> <artifactId>springcloud-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <!--提供web支持--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> </project>
-
配置端口:application.yml
server: port: 80
-
ConfigBean
@Configuration public class ConfigBean { //Cofiguration -- spring applicationContext.xml @Bean public RestTemplate getRestTemplate() { return new RestTemplate(); } }
-
控制层:DeptConsumerController
@RestController public class DeptConsumerController { // 理解:消费者,不应该有service层 // RestTemplate ... 供我们直接调用就可以了!注解到spring中 // (url,实体:Map, Class<T> responseType) @Autowired private RestTemplate restTemplate;//提供多种便捷访问Http的方法 private static final String REST_URL_PREFIX = "http://localhost:8081"; @RequestMapping("/consumer/dept/add") public boolean add(Dept dept) { return restTemplate.postForObject(REST_URL_PREFIX+"/dept/add",dept,Boolean.class); } @RequestMapping("/consumer/dept/get/{id}") public Dept get(@PathVariable("id") Long id) { return restTemplate.getForObject(REST_URL_PREFIX+"/dept/get/"+id,Dept.class); } @RequestMapping("/consumer/dept/list") public List<Dept> list() { return restTemplate.getForObject(REST_URL_PREFIX+"/dept/list",List.class); } }
项目运行
-
先运行8081服务端,再运行80客户端(80可以省略)
-
查询某个用户
-
显示全部部门的信息
-
测试成功!!!
5. Eureka服务注册与发现
5.1 什么是Eureka
-
Eureka:怎么读
-
Netflix在涉及Eureka时,遵循的就是API原则.
-
Eureka是Netflix的有个子模块,也是核心模块之一。Eureka是基于REST的服务,用于定位服务,以实现云端中间件层服务发现和故障转移,服务注册与发现对于微服务来说是非常重要的,有了服务注册与发现,只需要使用服务的标识符,就可以访问到服务,而不需要修改服务调用的配置文件了,功能类似于Dubbo的注册中心,比如Zookeeper.
5.2 原理理解
-
-
Springcloud 封装了Netflix公司开发的Eureka模块来实现服务注册与发现 (对比Zookeeper).
-
Eureka采用了C-S的架构设计,EurekaServer作为服务注册功能的服务器,他是服务注册中心.
-
而系统中的其他微服务,使用Eureka的客户端连接到EurekaServer并维持心跳连接。这样系统的维护人员就可以通过EurekaServer来监控系统中各个微服务是否正常运行,Springcloud 的一些其他模块 (比如Zuul) 就可以通过EurekaServer来发现系统中的其他微服务,并执行相关的逻辑.
-
和Dubbo架构对比
-
Eureka 包含两个组件: 和 .
-
Eureka Server 提供服务注册,各个节点启动后,回在EurekaServer中进行注册,这样Eureka Server中的服务注册表中将会储存所有课用服务节点的信息,服务节点的信息可以在界面中直观的看到.
-
Eureka Client 是一个Java客户端,用于简化EurekaServer的交互,客户端同时也具备一个内置的,使用轮询负载算法的负载均衡器。在应用启动后,将会向EurekaServer发送心跳 (默认周期为30秒) 。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除掉 (默认周期为90s).
-
-
-
Eureka Server:提供服务的注册与发现
-
Service Provider:服务生产方,将自身服务注册到Eureka中,从而使服务消费方能狗找到
-
Service Consumer:服务消费方,从Eureka中获取注册服务列表,从而找到消费服务
-
-
目前工程状况
5.3 构建步骤
5.3.1 eureka-server
测试
-
springcloud-eureka-7001 模块建立
-
pom.xml 配置
<dependencies> <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka-server --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> <version>1.4.6.RELEASE</version> </dependency> <!--热部署--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> </dependencies>
-
application.yml
server: port: 7001 # Eureka配置 eureka: instance: hostname: localhost # Eureka服务端的名字 client: register-with-eureka: false # 表示是否想Eureka中心注册自己 fetch-registry: false # fetch-registry如果为false,则表示自己为注册中心 service-url: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
源码中Eureka的默认端口以及访问路径:
-
主启动类EurekaServer_7001
@SpringBootApplication @EnableEurekaServer public class EurekaServer_7001 { public static void main(String[] args) { SpringApplication.run(EurekaServer_7001.class,args); } }
项目运行
启动成功后访问 http://localhost:7001/ 得到以下页面
-
测试成功!!!
问题
//报错内容! org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat
-
首先看SpringBoot与SpringCloud的版本号匹配,网上有!
-
其次看jdk版本是否匹配,我原本为jdk16,失败,改为1.8,成功!
5.3.2 eureka-client
测试
-
pom.xml
<!-- 导入eureka以来 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> <version>1.4.6.RELEASE</version> </dependency>
-
application.yml中新增Eureca配置
# Eureka的配置 eureka: client: service-url: defaultZone: http://localhost:7001/eureka/ instance: instance-id: springcloud-provider-dept-8081 #修改Eureka上的默认描述信息
-
DeptProvider_8081主启动类
-
为主启动类添加@EnableEurekaClient注解
//启动类 @SpringBootApplication @EnableEurekaClient //在服务启动后自动注册到Eureka中 public class DeptProvider_8081 { public static void main(String[] args) { SpringApplication.run(DeptProvider_8081.class,args); } }
测试
先启动7001服务端,后启动8001客户端进行测试,然后访问监控页
-
http://localhost:7001/
-
左右两边的配置:
拓展:实现监控的效果
-
pom.xml中添加依赖
<!--actuator完善监控信息--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
-
application.yml中添加配置
#info配置 info: app.name: koko-springcloud #项目的名称 company.name: com.koko #公司的名称
测试
5.3.3 EureKa自我保护机制
-
好死不如赖活着
概述
一句话总结就是:
-
默认情况下,当eureka server在一定时间内没有收到实例的心跳,便会把该实例从注册表中删除(),但是,如果短时间内丢失大量的实例心跳,便会触发eureka server的自我保护机制,比如在开发测试时,需要频繁地重启微服务实例,但是我们很少会把eureka server一起重启(因为在开发过程中不会修改eureka注册中心),。可以在eureka管理界面看到Renews threshold和Renews(last min),当后者(最后一分钟收到的心跳数)小于前者(心跳阈值)的时候,触发保护机制,会出现红色的警告:从警告中可以看到,eureka认为虽然收不到实例的心跳,但它认为实例还是健康的,eureka会保护这些实例,不会把它们从注册表中删掉。
-
该保护机制的目的是避免网络连接故障,在发生网络故障时,微服务和注册中心之间无法正常通信,但服务本身是健康的,不应该注销该服务,如果eureka因网络故障而把微服务误删了,那即使网络恢复了,该微服务也不会重新注册到eureka server了,因为只有在微服务启动的时候才会发起注册请求,后面只会发送心跳和服务列表请求,这样的话,该实例虽然是运行着,但永远不会被其它服务所感知。所以,eureka server在短时间内丢失过多的客户端心跳时,会进入自我保护模式,该模式下,eureka会保护注册表中的信息,不在注销任何微服务,当网络故障恢复后,eureka会自动退出保护模式。自我保护模式可以让集群更加健壮。
-
但是我们在开发测试阶段,需要频繁地重启发布,如果触发了保护机制,则旧的服务实例没有被删除,这时请求有可能跑到旧的实例中,而该实例已经关闭了,这就导致请求错误,影响开发测试。所以,在开发测试阶段,我们可以把自我保护模式关闭,只需在eureka server配置文件中加上如下配置即可:·
5.3.4 注册进来的微服务,获取一些消息
-
团队开发会用到
-
启动类添加注解
@EnableDiscoveryClient
//启动类 @SpringBootApplication @EnableEurekaClient //在服务启动后自动注册到Eureka中 @EnableDiscoveryClient public class DeptProvider_8081 { public static void main(String[] args) { SpringApplication.run(DeptProvider_8081.class,args); } }
-
DeptController.java新增方法
//获取一些配置的信息,得到具体的微服务! @Autowired private DiscoveryClient client; //注册进来的微服务~,获取一些消息~ @GetMapping("/dept/discovery") public Object discovery() { //获取微服务列表的清单 List<String> services = client.getServices(); System.out.println("discovery=>services:" + services); //得到一个具体的微服务信息,通过具体的微服务id,applicaioinName; List<ServiceInstance> instances = client.getInstances("SPRINGCLOUD-PROVIDER-DEPT"); for (ServiceInstance instance : instances) { System.out.println( instance.getHost() + "\t" + // 主机名称 instance.getPort() + "\t" + // 端口号 instance.getUri() + "\t" + // uri instance.getServiceId() // 服务id ); } return this.client; }
测试
-
localhost:8081/dept/discovery
5.4 Eureka:集群环境配置
5.4.1 初始化
-
新建springcloud-eureka-7002、springcloud-eureka-7003 模块
-
为pom.xml添加依赖 (与springcloud-eureka-7001相同)
<dependencies> <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka-server --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> <version>1.4.6.RELEASE</version> </dependency> <!--热部署--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> </dependencies>
-
application.yml配置(与springcloud-eureka-7001相同)
端口号分别换成7002和7003
-
主启动类(与springcloud-eureka-7001相同)
5.4.2 集群成员相互关联
-
配置一些自定义本机名字,找到本机hosts文件并打开
-
在hosts文件最后加上,要访问的本机名称,默认是localhost
-
修改application.yml的配置,如图为springcloud-eureka-7001配置,springcloud-eureka-7002/springcloud-eureka-7003同样分别修改为其对应的名称即可
-
在集群中使springcloud-eureka-7001关联springcloud-eureka-7002、springcloud-eureka-7003
以7001为例:完整的springcloud-eureka-7001下的application.yml如下
server: port: 7001 # Eureka配置 eureka: instance: hostname: eureka7001.com # Eureka服务端的名字 client: register-with-eureka: false # 表示是否想Eureka中心注册自己 fetch-