第一章微服务架构介绍
(Spring Cloud 初级)
一、 单体架构
单体架构也称之为单体系统或者是单体应用。就是一种把系统中所有的功能、模块耦合
在一个应用中的架构方式。
1 单体架构特点
1.1打包成一个独立的单元(导成一个唯一的 jar 包或者是 war 包)
1.2会一个进程的方式来运行
2 单体架构的优点、缺点
2.1优点
2.1.1项目易于管理
2.1.2部署简单
2.2缺点
2.2.1测试成本高
- 所有的功能都在一个项目中,比如项目要被迭代,功能要被改变,由于是一个整体,一个部分发送改变,所有部分都要进行测试。不敢保证这次跟新会不会对整体造成影响。
2.2.2可伸缩性差
- 单体架构项目是以一个单进程来运行的,很具备局限性,水平扩展不容易。比如现在对整个系统进行扩展,比如其中一个模块,如商品模块,访问需求量过大,我们要对她进行集群部署,进行水平扩展。单体架构很难做到,我们只能对整个项目进行集群部署。
2.2.3可靠性差
- 当前系统,某个模块出现bug,导致整个系统不可用。
2.2.4迭代困难
- 由于所有的模块都在一个项目中,会导致日常迭代非常困难,比如说互联网项目,每个月都会进行一次迭代。单体架构会导致分支过多,分支过多在合并代码的时候,会非常痛苦。
2.2.5跨语言程度差
- 由于我们现在系统的体系越来越庞大,需求越来越大。这时候单依靠java一门语言,显得有些力不从心。这时候我们可能还会依靠其他的语言来对项目进行支持。单体架构这时候想加入其他语言就比较难,因为单体架构是耦合在一起的。
2.2.6团队协作难
- 整个系统是由一个团队来开发的。单体架构,里面的内容变得非常的庞大的时候,我们每个成员就要进行大量的代码。整个团队之间,进行沟通和协作就很困难。不像分布式架构,每个团队只负责一个模块,不像单体架构沟通那么困难。
二、 微服务架构
1 什么是微服务
微服务是一种架构风格。一个大型的复杂软件应用,由一个或多个微服务组成。系统中
的各个微服务可被独立部署,各个微服务之间是松耦合的。每个微服务仅关注于完成一件任
务并很好的完成该任务。
2 架构风格
项目的一种设计模式。
2.1常见的架构风格
- 2.1.1客户端与服务端的
- 2.1.2基于组件模型的架构(EJB)
- 2.1.3分层架构(MVC)
- 2.1.4面向服务架构(SOA)
3 微服务特点:
3.1系统是由多个服务构成
3.2每个服务可以单独独立部署
3.3每个服务之间是松耦合的。服务内部是高内聚的,外部是低耦
合的。高内聚就是每个服务只关注完成一个功能。
4 微服务的优点、缺点
4.1优点
4.1.1测试容易
- 每个服务的组件都是灵活的,能独立部署,所以说测试起来的时候,对哪个服务进行了迭代,就可以征对性取进行测试。
4.1.2可伸缩性强
- 内部高内聚,外部松耦合。每个服务都能进行相应扩展。
4.1.3可靠性强
- 之前讲单体架构也说过,一单项目某个模块出现问题,受影响的是整个项目。但是微服务则不然,并不会因为某个bug而导致整个服务全部宕机。受影响的是某个服务,而不是整个项目。
4.1.4跨语言程度会更加灵活
- 微服务的架构跟语言无关,我们可以根据自己的选择或者项目的特点去选择不同的语言和工具,高效的去完成这个项目。
4.1.5团队协作容易
- 只做自己某一块的服务,并不需要去关注其他的服务。降低了程序员的学习成本和沟通成本。
4.1.6系统迭代容易
- 每个微服务可以根据每个团队进行相应的独立开发。系统迭代只正对当前的服务就可以了。
4.2缺点
4.2.1运维成本过高,部署数量较多
- 不像单体架构,只有一个项目,我们只要放入tomcat中运行就可以了。现在把一个项目拆分成多个服务,服务越多,运维的成本就越高。
4.2.2接口兼容多版本
- 我们需要考虑到接口兼容多版本的问题。面向服务开发就是面向接口开发,服务的提供方式就是通过接口来提供的。接口的变更必然会导致多个客户端跟着改,这样就必须做接口的多版本开发。以便于适用于接口的变更。
4.2.3分布式系统的复杂性
- 本来是一个系统,现在把他拆分成多个服务。由于网络的延迟性,网络的不稳定,服务的容错性,服务的负载均衡等等,都是我们我们在做微服务架构需要考虑到的一个问题。
4.2.4分布式事务
- 我们在做微服务开发的时候,最大的难题就是对分布事务的处理。
三、 MVC、RPC、SOA、微服务架构之间的区别
- 1 MVC 架构
其实 MVC 架构就是一个单体架构。
代表技术:Struts2、SpringMVC、Spring、Mybatis 等等。
- 2 RPC 架构
RPC(Remote Procedure Call):远程过程调用。他一种通过网络从远程计算机程序上请求
服务,而不需要了解底层网络技术的协议。
代表技术:Thrift、Hessian 等等
- 3 SOA 架构
SOA(Service oriented Architecture):面向服务架构
ESB(Enterparise Servce Bus):企业服务总线,服务中介。主要是提供了一个服务于服务之间的交互。
ESB 包含的功能如:负载均衡,流量控制,加密处理,服务的监控,异常处理,监控告急等等。
代表技术:Mule(不开源)、WSO2(开源,免费)
- 4 微服务架构
微服务就是一个轻量级的服务治理方案。
注册中心:比ESB更轻量一些。
代表技术:SpringCloud、dubbo 等等
四、 如何设计微服务以及设计原则
1) AKF 拆分原则
2) 前后端分离原则
3) 无状态服务
4) RestFul 的通信风格
1 AKF 拆分原则
业界对于可扩展的系统架构设计有一个朴素的理念,就是:
通过加机器就可以解决容量和可用性问题。(如果一台不行那就两台)。
我是个段子:(世界上没有什么事是一顿烧烤不能解决的。如果有,那就两
顿。)
这一理念在“云计算”概念疯狂流行的今天,得到了广泛的认可!对于一个规模
迅速增长的系统而言,容量和性能问题当然是首当其冲的。但是随着时间的向前,
系统规模的增长,除了面对性能与容量的问题外,还需要面对功能与模块数量上
的增长带来的系统复杂性问题以及业务的变化带来的提供差异化服务问题。而许
多系统,在架构设计时并未充分考虑到这些问题,导致系统的重构成为常态,从
而影响业务交付能力,还浪费人力财力!对此,《可扩展的艺术》一书提出了一
个更加系统的可扩展模型—— AKF 可扩展立方 (Scalability Cube)。这个立方
体中沿着三个坐标轴设置分别为:X、Y、Z。
Y 轴(功能) —— 关注应用中功能划分,基于不同的业务拆分
X 轴(水平扩展) —— 关注水平扩展,也就是”加机器解决问题”
Z 轴(数据分区) —— 关注服务和数据的优先级划分,如按地域划分
1.1 Y 轴(功能)
Y 轴扩展会将庞大的整体应用拆分为多个服务。每个服务实现一组相关的功
能,如订单管理、客户管理等。在工程上常见的方案是 服务化架构(SOA) 。比
如对于一个电子商务平台,我们可以拆分成不同的服务,组成下面这样的架构:
但通过观察上图容易发现,当服务数量增多时,服务调用关系变得复杂。为
系统添加一个新功能,要调用的服务数也变得不可控,由此引发了服务管理上的
混乱。所以,一般情况下,需要采用服务注册的机制形成服务网关来进行服务治
理。系统的架构将变成下图所示:
1.2 X 轴(水平扩展)
X 轴扩展与我们前面朴素理念是一致的,通过绝对平等地复制服务与数据,
以解决容量和可用性的问题。其实就是将微服务运行多个实例,做集群加负载均
衡的模式。
为了提升单个服务的可用性和容量, 对每一个服务进行 X 轴扩展划分 。
1.3 Z 轴(数据分区)
Z 轴扩展通常是指基于请求者或用户独特的需求,进行系统划分,并使得划
分出来的子系统是相互隔离但又是完整的。以生产汽车的工厂来举例:福特公司
为了发展在中国的业务,或者利用中国的廉价劳动力,在中国建立一个完整的子
工厂,与美国工厂一样,负责完整的汽车生产。这就是一种 Z 轴扩展。
1.3.1工程领域常见的 Z 轴扩展有以下两种方案:
1.3.1.1 单元化架构
在分布式服务设计领域,一个单元(Cell)就是满足某个分区所有业务操作
的自包含闭环。如上面我们说到的 Y 轴扩展的 SOA 架构,客户端对服务端节点
的选择一般是随机的,但是,如果在此加上 Z 轴扩展,那服务节点的选择将不再
是随机的了,而是每个单元自成一体。如下图:
1.3.1.2 数据分区
为了性能数据安全上的考虑,我们将一个完整的数据集按一定的维度划分出
不同的子集。 一个分区(Shard),就是是整体数据集的一个子集。比如用尾号
来划分用户,那同样尾号的那部分用户就可以认为是一个分区。数据分区为一般
包括以下几种数据划分的方式:
数据类型(如:业务类型)
数据范围(如:时间段,用户 ID)
数据热度(如:用户活跃度,商品热度)
按读写分(如:商品描述,商品库存)
2 前后端分离原则
何为前后端分离?前后端本来不就分离么?
这要从尴尬的 jsp 讲起。分工精细化从来都
是蛋糕做大的原则,多个领域工程师最好在不需要接触其他领域知识的情况下合作,才可能
使效率越来越高,维护也会变得简单。jsp 的模板技术融合了 html 和 java 代码,使得传统
MVC 开发中的前后端在这里如胶似漆,前端做好页面,后端转成模板,发现问题再找前端,
前端又看不懂 java 代码......前后端分离的目的就是将这尴尬局面打破。
前后端分离原则,简单来讲就是前端和后端的代码分离,我们推荐的模式是最好采用物
理分离的方式部署,进一步促使更彻底的分离。如果继续直接使用服务端模板技术,如:jsp,
把 java、js、html、css 都堆到一个页面里,稍微复杂一点的页面就无法维护了。
这种分离方式有几个好处:
- 1) 前后端技术分离,可以由各自的专家来对各自的领域进行优化,这样前段的用户体验优化效果更好。
- 2) 分离模式下,前后端交互界面更清晰,就剩下了接口模型,后端的接口简洁明了,更容易维护。
- 3) 前端多渠道集成场景更容易实现,后端服务无需变更,采用统一的数据和模型,可以支持多个前端:例如:微信 h5 前端、PC 前端、安卓前端、IOS 前端。
3 无状态服务
对于无状态服务,首先说一下什么是状态:如果一个数据需要被多个服务共
享,才能完成一笔交易,那么这个数据被称为状态。进而依赖这个“状态”数据的
服务被称为有状态服务,反之称为无状态服务。
那么这个无状态服务原则并不是说在微服务架构里就不允许存在状态,表达
的真实意思是要把有状态的业务服务改变为无状态的计算类服务,那么状态数据
也就相应的迁移到对应的“有状态数据服务”中。
场景说明:例如我们以前在本地内存中建立的数据缓存、Session 缓存,到
现在的微服务架构中就应该把这些数据迁移到分布式缓存中存储,让业务服务变
成一个无状态的计算节点。迁移后,就可以做到按需动态伸缩,微服务应用在运
行时动态增删节点,就不再需要考虑缓存数据如何同步的问题。
4 RestFul 的通讯风格
作为一个原则来讲本来应该是个“无状态通信原则”,在这里我们直接推荐一
个实践优选的 Restful 通信风格 ,因为他有很多好处:
1) 无状态协议 HTTP,具备先天优势,扩展能力很强。例如需要安全加密,有
现成的成熟方案 HTTPS 即可。
2) JSON 报文序列化,轻量简单,人与机器均可读,学习成本低,搜索引擎友
好。
第二章 SpringCloud 入门
(Spring Cloud 初级)
一、 什么是 SpringCloud
什么是 SpringCloud:是一个服务治理平台,提供了一些服务框架。包含了:服务注册
与发现、配置中心、消息中心 、负载均衡、数据监控等等。
1 概念定义
Spring Cloud 是一个微服务框架,相比 Dubbo 等 RPC 框架, Spring Cloud 提
供的全套的分布式系统解决方案。
Spring Cloud 对微服务基础框架 Netflix 的多个开源组件进行了封装,同时又实现
了和云端平台以及和 Spring Boot 开发框架的集成。
Spring Cloud 为微服务架构开发涉及的配置管理,服务治理,熔断机制,智能路由,
微代理,控制总线,一次性 token,全局一致性锁,leader 选举,分布式 session,集
群状态管理等操作提供了一种简单的开发方式。
Spring Cloud 为开发者提供了快速构建分布式系统的工具,开发者可以快速的启动
服务或构建应用、同时能够快速和云平台资源进行对接。
2 Spring Cloud 的项目的位置
Sping Cloud 是 Spring 的一个顶级项目与 Spring Boot、Spring Data 位于同一位置。
3 Spring Cloud 的子项目
Spring Cloud 包含了很多子项目,如:
3.1Spring Cloud Config:配置管理工具,支持使用 Git 存储配置内容,支持应
用配置的外部化存储,支持客户端配置信息刷新、加解密配置内容等
3.2 Spring Cloud Bus:事件、消息总线,用于在集群(例如,配置变化事件)中
传播状态变化,可与 Spring Cloud Config 联合实现热部署。
3.3Spring Cloud Netflix:针对多种 Netflix 组件提供的开发工具包,其中包括
Eureka、Hystrix、Zuul、Archaius 等。
3.3.1Netflix Eureka:一个基于 rest 服务的服务治理组件,包括服务注册中
心、服务注册与服务发现机制的实现,实现了云端负载均衡和中间层服务器
的故障转移。
3.3.2Netflix Hystrix:容错管理工具,实现断路器模式,通过控制服务的节点,
从而对延迟和故障提供更强大的容错能力。
3.3.3Netflix Ribbon:客户端负载均衡的服务调用组件。
3.3.4Netflix Feign:基于 Ribbon 和 Hystrix 的声明式服务调用组件。
3.3.5Netflix Zuul:微服务网关,提供动态路由,访问过滤等服务。
3.3.6Netflix Archaius:配置管理 API,包含一系列配置管理 API,提供动
态类型化属性、线程安全配置操作、轮询框架、回调机制等功能。
3.4Spring Cloud for Cloud Foundry:通过 Oauth2 协议绑定服务到
CloudFoundry,CloudFoundry 是 VMware 推出的开源 PaaS 云平台。
3.5Spring Cloud Sleuth:日志收集工具包,封装了 Dapper,Zipkin 和 HTrace
操作。
3.6Spring Cloud Data Flow:大数据操作工具,通过命令行方式操作数据流。
3.7Spring Cloud Security:安全工具包,为你的应用程序添加安全控制,主要
是指 OAuth2。
3.8Spring Cloud Consul:封装了 Consul 操作,consul 是一个服务发现与配
置工具,与 Docker 容器可以无缝集成。
3.9Spring Cloud Zookeeper :操作 Zookeeper 的 工 具 包 , 用 于 使 用
zookeeper 方式的服务注册和发现。
3.10Spring Cloud Stream:数据流操作开发包,封装了与 Redis,Rabbit、
Kafka 等发送接收消息。
3.11Spring Cloud CLI:基于 Spring Boot CLI,可以让你以命令行方式快速
建立云组件。
二、 SpringCloud 与 Dubbo 的区别
三、 Spring Cloud 版本说明
1 常见版本号说明
软件版本号:2.0.2.RELEASE
2:主版本号。当功能模块有较大更新或者整体架构发生变化时,主版本号会更新
0:次版本号。次版本表示只是局部的一些变动。
2:修改版本号。一般是 bug 的修复或者是小的变动
RELEASE:希腊字母版本号。次版本号用户标注当前版本的软件处于哪个开发阶段
1.1希腊字母版本号
Base:设计阶段。只有相应的设计没有具体的功能实现。Alpha:软件的初级版本。存在较多的 bug
Bate:表示相对 alpha 有了很大的进步,消除了严重的 bug,还存在一些潜在的 bug。
Release:该版本表示最终版。
2 Spring Cloud 版本号说明
2.1为什么 Spring Cloud 版本用的是单词而不是数字?
设计的目的是为了更好的管理每个 Spring Cloud 的子项目的清单。避免子的版本号与子
项目的版本号混淆。
2.2版本号单词的定义规则
采用伦敦的地铁站名称来作为版本号的命名,根据首字母排序,字母顺序靠后的版本号
越大。
2.3版本发布计划说明
3 Spring Cloud 与子项目版本兼容说明
第五章 Eureka 服务注册中心
(Spring Cloud 初级)
一、 什么是服务注册中心
服务注册中心是服务实现服务化管理的核心组件,类似于目录服务的作用,主要用来
存储服务信息,譬如提供者 url 串、路由信息等。服务注册中心是 SOA 架构中最基础的设
施之一。
1 服务注册中心的作用
1,服务的注册
2,服务的发现
2 常见的注册中心有哪些
1,Dubbo 的注册中心 Zookeeper
2,Sringcloud 的注册中心 Eureka
3 服务注册中心解决了什么问题
1. 服务管理
2. 服务的依赖关系管理
4 什么是 Eureka 注册中心
Eureka 是 Netflix 开发的服务发现组件,本身是一个基于 REST 的服务。Spring Cloud
将它集成在其子项目 spring-cloud-netflix 中,以实现 Spring Cloud 的服务注册于发现,同时
还提供了负载均衡、故障转移等能力。
5 Eureka 注册中心三种角色
5.1Eureka Server
通过 Register、Get、Renew 等接口提供服务的注册和发现。
5.2Application Service (Service Provider)
服务提供方
把自身的服务实例注册到 Eureka Server 中
5.3Application Client (Service Consumer)
服务调用方
通过 Eureka Server 获取服务列表,消费服务。
二、 Eureka 入门案例
1 创建项目
2 修改 pom 文件添加依赖
<?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.bjsxt</groupId> <artifactId>springcloud-eureka-server</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>springcloud-eureka-server</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.13.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEn coding> <project.reporting.outputEncoding>UTF-8</project.reporting. outputEncoding> <java.version>1.8</java.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.SR5</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
3 修改启动类
@EnableEurekaServer @SpringBootApplication public class EurekaApplication { public static void main(String[] args) { SpringApplication.run(EurekaApplication.class, args); } }
4 修改 application.properties 全局配置文件
spring.application.name=eureka-server server.port=8761 #是否将自己注册到 Eureka-Server 中,默认的为 true eureka.client.registerWithEureka=false #是否冲 Eureka-Server 中获取服务注册信息,默认为 true eureka.client.fetchRegistry=false
5 通过浏览器访问 Eureka-Server 服务管理平台
三、 搭建高可用 Eureka 注册中心(Eureka 集群)
2 配置文件
#设置 eureka 实例名称,与配置文件的变量为主 eureka.instance.hostname=eureka2 #设置服务注册中心地址,指向另一个注册中心 eureka.client.serviceUrl.defaultZone=http://eureka1:8761/eu reka/
在搭建 Eureka 集群时,需要添加多个配置文件,并且使用 SpringBoot 的多环境配置方
式。集群中需要多少节点就添加多少个配置文件。
3 在配置文件中配置集群节点
3.1eureka1
spring.application.name=eureka-server server.port=8761 #设置 eureka 实例名称,与配置文件的变量为主 eureka.instance.hostname=eureka1 #设置服务注册中心地址,指向另一个注册中心 eureka.client.serviceUrl.defaultZone=http://eureka2:8761/eu reka/
3.2eureka2
spring.application.name=eureka-server server.port=8761 #设置 eureka 实例名称,与配置文件的变量为主 eureka.instance.hostname=eureka2 #设置服务注册中心地址,指向另一个注册中心 eureka.client.serviceUrl.defaultZone=http://eureka1:8761/eu reka/
4 添加 logback 日志配置文件
5 Eureka 集群部署
部署环境:需要安装 jdk1.8,正确配置环境变量。
注意:需要关闭 linux 的防火墙,或者是开放 8761 端口
5.1将项目打包
Maven install
5.2上传实例
在/usr/local/创建一个 eureka 的目录
将项目的 jar 包拷贝到/usr/local/eureka
6 编写一个启动脚本文件
#!/bin/bash cd `dirname $0` CUR_SHELL_DIR=`pwd` CUR_SHELL_NAME=`basename ${BASH_SOURCE}` JAR_NAME="springcloud-eureka-server-ha-0.0.1-SNAPSHOT.jar" JAR_PATH=$CUR_SHELL_DIR/$JAR_NAME #JAVA_MEM_OPTS=" -server -Xms1024m -Xmx1024m -XX:PermSize=128m" JAVA_MEM_OPTS="" SPRING_PROFILES_ACTIV="-Dspring.profiles.active=eureka1" #SPRING_PROFILES_ACTIV="" LOG_DIR=$CUR_SHELL_DIR/logs LOG_PATH=$LOG_DIR/${JAR_NAME%..log echo_help() { echo -e "syntax: sh $CUR_SHELL_NAME start|stop" } if [ -z $1 ];then echo_help() { exit 1 } fi if [ ! -d "$LOG_DIR" ];then mkdir "$LOG_DIR" fi if [ ! -f "$LOG_PATH" ];then touch "$LOG_DIR" fi if [ "$1" == "start" ];then # check server PIDS=`ps --no-heading -C java -f --width 1000 | grep $JAR_NAME | awk '{print $2}'` if [ -n "$PIDS" ]; then echo -e "ERROR: The $JAR_NAME already started and the PID is ${PIDS}." exit 1 fi echo "Starting the $JAR_NAME..." # start nohup java $JAVA_MEM_OPTS -jar $SPRING_PROFILES_ACTIV $JAR_PATH >> $LOG_PATH 2>&1 & COUNT=0 while [ $COUNT -lt 1 ]; do sleep 1 COUNT=`ps --no-heading -C java -f --width 1000 | grep "$JAR_NAME" | awk '{print $2}' | wc -l` if [ $COUNT -gt 0 ]; then break fi done PIDS=`ps --no-heading -C java -f --width 1000 | grep "$JAR_NAME" | awk '{print $2}'` echo "${JAR_NAME} Started and the PID is ${PIDS}." echo "You can check the log file in ${LOG_PATH} for details." elif [ "$1" == "stop" ];then PIDS=`ps --no-heading -C java -f --width 1000 | grep $JAR_NAME | awk '{print $2}'` if [ -z "$PIDS" ]; then echo "ERROR:The $JAR_NAME does not started!" exit 1 fi echo -e "Stopping the $JAR_NAME..." for PID in $PIDS; do kill $PID > /dev/null 2>&1 done COUNT=0 while [ $COUNT -lt 1 ]; do sleep 1 COUNT=1 for PID in $PIDS ; do PID_EXIST=`ps --no-heading -p $PID` if [ -n "$PID_EXIST" ]; then COUNT=0 break fi done done echo -e "${JAR_NAME} Stopped and the PID is ${PIDS}." else echo_help exit 1 fi
6.1设置启动脚本的运行权限
Chmod -R 755 server.sh
7 修改 linux 的 host 文件
Vim /etc/hosts
192.168.70.134 eureka1
192.168.70.135 eureka2
8 启动 eureka 注册中心
./server.sh start 启动
./server.sh stop 停止
9 通过浏览器访问注册中心的管理页面
四、 在高可用的 Eureka 注册中心中构建 provider 服务
2 修改 pom 文件
<?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.bjsxt</groupId> <artifactId>springcloud-eureka-provider</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>springcloud-eureka-provider</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.13.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEn coding> <project.reporting.outputEncoding>UTF-8</project.reporting. outputEncoding> <java.version>1.8</java.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.SR5</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
3 修改启动类
@EnableEurekaClient @SpringBootApplication public class EurekaApplication { public static void main(String[] args) { SpringApplication.run(EurekaApplication.class, args); } }
4 修改 provider 的配置文件
spring.application.name=eureka-provider server.port=9090 #设置服务注册中心地址,指向另一个注册中心 eureka.client.serviceUrl.defaultZone=http://eureka1:8761/eu reka/,http://eureka2:8761/eureka/
5 修改 windows 的 host 文件
路径:C:WindowsSystem32driversetc
192.168.70.134 eureka1
192.168.70.135 eureka2
6 编写服务接口
6.1创建接口
@RestController public class UserController { @RequestMapping("/user") public List<User> getUsers(){ List<User> list = new ArrayList<>(); list.add(new User(1,"zhangsan",20)); list.add(new User(2,"lisi",22)); list.add(new User(3,"wangwu",20)); return list; } }
6.2创建 pojo
public class User { private int userid; private String username; private int userage; public int getUserid() { return userid; } public void setUserid(int userid) { this.userid = userid; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public int getUserage() { return userage; } public void setUserage(int userage) { this.userage = userage; } public User(int userid, String username, int userage) { super(); this.userid = userid; this.username = username; this.userage = userage; } public User() { super(); // TODO Auto-generated constructor stub } }
五、 在高可用的 Eureka 注册中心中构建 consumer 服务
1 创建项目
2 Consumer 的配置文件
spring.application.name=eureka-consumer
server.port=9091
#设置服务注册中心地址,指向另一个注册中心
eureka.client.serviceUrl.defaultZone=http://eureka1:8761/eu
reka/,http://eureka2:8761/eureka/
3 在 Service 中完成服务的调用
@Service public class UserService { @Autowired private LoadBalancerClient loadBalancerClient;//ribbon 负 载均衡器 public List<User> getUsers(){ //选择调用的服务的名称 //ServiceInstance 封装了服务的基本信息,如 IP,端口 ServiceInstance si = this.loadBalancerClient.choose("eureka-provider"); //拼接访问服务的 URL StringBuffer sb = new StringBuffer(); //http://localhost:9090/user sb.append("http://").append(si.getHost()).append(":").appen d(si.getPort()).append("/user"); //springMVC RestTemplate RestTemplate rt = new RestTemplate(); ParameterizedTypeReference<List<User>> type = new ParameterizedTypeReference<List<User>>() {}; //ResponseEntity:封装了返回值信息 ResponseEntity<List<User>> response = rt.exchange(sb.toString(),HttpMethod.GET, null, type); List<User> list =response.getBody(); return list; } }
4 Controller
@RestController public class UserController { @Autowired private UserService userService; @RequestMapping("/consumer") public List<User> getUsers(){ return this.userService.getUsers(); } }
Register(服务注册):把自己的 IP 和端口注册给 Eureka。
Renew(服务续约):发送心跳包,每 30 秒发送一次。告诉 Eureka 自己还活着。
Cancel(服务下线):当 provider 关闭时会向 Eureka 发送消息,把自己从服务列表中删除。防
止 consumer 调用到不存在的服务。
Get Registry(获取服务注册列表):获取其他服务列表。
Replicate(集群中数据同步):eureka 集群中的数据复制与同步。
Make Remote Call(远程调用):完成服务的远程调用。
七、 基于分布式 CAP 定理,分析注册中心两大主流框架:Eureka
与 Zookeeper 的区别
1 什么是 CAP 原则
CAP 原则又称 CAP 定理,指的是在一个分布式系统中,Consistency(一致性)、
Availability(可用性)、Partition tolerance(分区容错性),三者不可兼得。
CAP 由 Eric Brewer 在 2000 年 PODC 会议上提出。该猜想在提出两年后被证明成
立,成为我们熟知的 CAP 定理
2 Zookeeper 与 Eureka 的区别
八、 Eureka 优雅停服
1 在什么条件下,Eureka 会启动自我保护?
什么是自我保护模式
1,自我保护的条件 一般情况下,微服务在 Eureka 上注册后,会每 30 秒发送心跳包,Eureka 通过心跳来 判断服务时候健康,同时会定期删除超过 90 秒没有发送心跳服务。 2,有两种情况会导致 Eureka Server 收不到微服务的心跳 a.是微服务自身的原因 b.是微服务与 Eureka 之间的网络故障 通常(微服务的自身的故障关闭)只会导致个别服务出现故障,一般不会出现大面积 故障,而(网络故障)通常会导致 Eureka Server 在短时间内无法收到大批心跳。 考虑到这个区别,Eureka 设置了一个阀值,当判断挂掉的服务的数量超过阀值时, Eureka Server 认为很大程度上出现了网络故障,将不再删除心跳过期的服务。 3,那么这个阀值是多少呢? 15 分钟之内是否低于 85%; Eureka Server 在运行期间,会统计心跳失败的比例在 15 分钟内是否低于 85% 这种算法叫做 Eureka Server 的自我保护模式
2 为什么要启动自我保护
3 如何关闭自我保护
修改 Eureka Server 配置文件
#关闭自我保护:true 为开启自我保护,false 为关闭自我保护 eureka.server.enableSelfPreservation=false #清理间隔(单位:毫秒,默认是 60*1000) eureka.server.eviction.interval-timer-in-ms=60000
4 如何优雅停服
4.1不需要再 Eureka Server 中配置关闭自我保护
4.2需要再服务中添加 actuator.jar 包
<?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.bjsxt</groupId> <artifactId>springcloud-eureka-provider</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>springcloud-eureka-provider</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.13.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEn coding> <project.reporting.outputEncoding>UTF-8</project.reporting. outputEncoding> <java.version>1.8</java.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.SR5</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
4.3修改配置文件
#启用 shutdown endpoints.shutdown.enabled=true #禁用密码验证 endpoints.shutdown.sensitive=false
4.4发送一个关闭服务的 URL 请求
public class HttpClientUtil { public static String doGet(String url, Map<String, String> param) { // 创建 Httpclient 对象 CloseableHttpClient httpclient = HttpClients.createDefault(); String resultString = ""; CloseableHttpResponse response = null; try { // 创建 uri URIBuilder builder = new URIBuilder(url); if (param != null) { for (String key : param.keySet()) { builder.addParameter(key, param.get(key)); } } URI uri = builder.build(); // 创建 http GET 请求 HttpGet httpGet = new HttpGet(uri); // 执行请求 response = httpclient.execute(httpGet); // 判断返回状态是否为 200 if (response.getStatusLine().getStatusCode() == 200) { resultString = EntityUtils.toString(response.getEntity(), "UTF-8"); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (response != null) { response.close(); } httpclient.close(); } catch (IOException e) { e.printStackTrace(); } } return resultString; } public static String doGet(String url) { return doGet(url, null); } public static String doPost(String url, Map<String, String> param) { // 创建 Httpclient 对象 CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse response = null; String resultString = ""; try { // 创建 Http Post 请求 HttpPost httpPost = new HttpPost(url); // 创建参数列表 if (param != null) { List<NameValuePair> paramList = new ArrayList<>(); for (String key : param.keySet()) { paramList.add(new BasicNameValuePair(key, param.get(key))); } // 模拟表单 UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList,"utf-8"); httpPost.setEntity(entity); } // 执行 http 请求 response = httpClient.execute(httpPost); resultString = EntityUtils.toString(response.getEntity(), "utf-8"); } catch (Exception e) { e.printStackTrace(); } finally { try { response.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return resultString; } public static String doPost(String url) { return doPost(url, null); } public static String doPostJson(String url, String json) { // 创建 Httpclient 对象 CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse response = null; String resultString = ""; try { // 创建 Http Post 请求 HttpPost httpPost = new HttpPost(url); // 创建请求内容 StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON); httpPost.setEntity(entity); // 执行 http 请求 response = httpClient.execute(httpPost); resultString = EntityUtils.toString(response.getEntity(), "utf-8"); } catch (Exception e) { e.printStackTrace(); } finally { try { response.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return resultString; } public static void main(String[] args) { String url ="http://127.0.0.1:9090/shutdown"; //该 url 必须要使用 dopost 方式来发送 HttpClientUtil.doPost(url); } }
九、 如何加强 Eureka 注册中心的安全认证
1 在 Eureka Server 中添加 security 包
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
2 修改 Eureka Server 配置文件
#开启 http basic 的安全认证 security.basic.enabled=true security.user.name=user security.user.password=123456
3 修改访问集群节点的 url
eureka.client.serviceUrl.defaultZone=http://user:123456@eur
eka2:8761/eureka/
4 修改微服务的配置文件添加访问注册中心的用户名与密码
spring.application.name=eureka-provider server.port=9090 #设置服务注册中心地址,指向另一个注册中心 eureka.client.serviceUrl.defaultZone=http://user:123456@eur eka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/ #启用 shutdown endpoints.shutdown.enabled=true #禁用密码验证 endpoints.shutdown.sensitive=false