前言:本章是我记录的SpringCloud实战模拟业务场景系列第一篇,其主要讲解了SpringCloud各个服务之间是如何治理的,各个服务是如何注册到注册中心,是如何发现服务,以及高可用安全的服务注册中心架构模式。
一、项目骨架搭建、版本介绍
1、构建项目
因为是基于模拟实战业务场景,所以要先吧项目骨架搭建起来,这里我们采用maven来构建多模块父子依赖项目骨架,spring为我们提供了一个非常方便快速构建项目的工具 https://start.spring.io/ 进入网页后界面如下
因为我们需要构建SpringCloud依赖子模块项目结构,在 Dependencies 输入框中 输入cloud 选择 Cloud Bootstrap,然后构建并下载项目然后使用idea打开即可,打开后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>
<packaging>pom</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.springCloudExample</groupId>
<artifactId>example</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>example</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
</project>
2、版本介绍
现在我相信很多人应该有一些疑问,springboot和SpringCloud的关系倒是怎样的,他们的版本是如何对应的?为什么SpringCloud的版本不是数字形式的?,官网文档地址-> http://spring.io/projects/spring-cloud 一直滑到最底部如下图
至于为什么SpringCloud的版本不采用数字形式,这个就得去问springcloud的研发者们了。
二、Eureka介绍
网上对eureka介绍的文章有很多,这里我就不详细介绍了,主要围绕这几点来简单总结一下:
-
eureka能做什么?
其实 eureka最主要的功能就两大点 服务注册 、服务发现,
eureka服务注册
会将所有eureka的client端进行统一注册到eureka服务端,然后对所有注册了的客户端进行心跳检查并标记或者排除掉不可用的客户端。
eureka服务发现
eureka客户端会获取所有服务端已注册的服务信息,服务信息包括注册时候的服务名、实际ip地址,而且eureka客户端结合Ribbon、或者Feign可直接使用服务名来对该服务进行请求调用。 -
为什么需要eureka?
至于为什么需要eureka呢,其实这个问题很简单,比方说我们这个模拟业务里面的商品服务,在实际的场景中我们可能会部署多个商品服务的实例,那么如果我们不采用注册中心的话,所有依赖商品服务的其他服务就必须在各自的配置里面配置商品服务的地址,然后再采用一些负载均衡的算法来对商品服务进行请求,并且也无法检测各个实例的存活情况,当然你也可以自己实现一套心跳机制... 好了 不说了 相信各位小伙伴应该已经能想到用eureka有多方便了,其实不止eureka可以做服务注册与发现 还有consul、zookeeper等,后续也会有专门的文章对其进行讲解。 -
eureka和其他同类产品优势是什么?
这个疑问我相信也是大家比较想知道的,至于为什么服务注册与发现首选Eureka,而不用consul、zookeeper呢?
在分布式系统领域我们始终摆脱不了CAP理论(C:一致性,A:服务可用性,P分区容错性),其中P是必须成立的,如果大家不清楚CAP理论的话,请自行google一下吧,这里就不再对其进行介绍了;eureka保证了AP,consul和zookeeper保证了CP。- 采用eureka作为服务中心,我们可以保证服务可用性,通俗点来讲就是如果往eureka服务集群的某个eureka服务节点进行注册的话,eureka不会强制将其全部都复制到集群中的其他节点上去,也不保证注册信息有没有复制成功,虽然说可能会出现各个eureka服务节点的数据不一致性问题,但这并不影响其正常使用,我们的业务服务可以对所有eureka服务节点都进行注册当有其他业务服务注册上来、或者业务服务注册信息有变化了,请求中一个eureka服务节点可能会查看不到最新的注册数据,但请求另一个eureka服务节点就能查看到最新的注册数据,也就是说eureka舍弃了 强一致性 来保证了整体eureka服务的 高可用性。
而且eureka还有一个特点,就是当出现一些网络问题导致eureka心跳机制响应的缓慢或者丢失心跳,eureka并不会马上吧这些暂时不可用的服务移除掉,而是会对暂时不可用的服务开启保护模式,但是如果持续多少秒后(默认90秒)还没有接收到该某些服务的心跳的话,则会吧对应服务移除掉。 - 对比于consul、zookeeper来说,这两者更偏向于保证 数据的一致性 ,从而降低了 可用性;consul的服务注册相比 Eureka 会稍慢一些。因为 Consul 的 raft 协议要求必须过半数的节点都写入成功才认为注册成功,而且当consul集群的Leader 挂掉时,重新选举期间整个 Consul集群是不可用的,所以其保证了强一致性但牺牲了可用性。zookeeper也是保证了CP,也就是说随便访问zookeeper集群的几个节点都能一致的数据,但是zookeeper并不能保证请求的 可用性 ,因为在一些特殊的极端场景,zookeeper会舍弃掉一部分请求,客户端需要重新请求才可能能获取到数据,而且zookeeper的master挂掉也需要重新选取新的master,并且这期间zookeeper集群是处于不可用状态的,而且最致命的就是,zookeeper的选举耗时会比较长,如果一单遇到网络波动,都会导致进行master的选举,从而增加zookeeper的不可用时间。
- 采用eureka作为服务中心,我们可以保证服务可用性,通俗点来讲就是如果往eureka服务集群的某个eureka服务节点进行注册的话,eureka不会强制将其全部都复制到集群中的其他节点上去,也不保证注册信息有没有复制成功,虽然说可能会出现各个eureka服务节点的数据不一致性问题,但这并不影响其正常使用,我们的业务服务可以对所有eureka服务节点都进行注册当有其他业务服务注册上来、或者业务服务注册信息有变化了,请求中一个eureka服务节点可能会查看不到最新的注册数据,但请求另一个eureka服务节点就能查看到最新的注册数据,也就是说eureka舍弃了 强一致性 来保证了整体eureka服务的 高可用性。
总的来说Eureka更加符合服务注册、发现的场景,虽然说现在Eureka2.x闭源了,但是Eureka1.x目前还是有很多大公司在商用了,而且最主要的服务注册与发现功能也得到了很多用户的证实,所以对于后续只是单纯的做服务注册与发现的话 Eureka至少还是个不二的选择,当然为了更长远的考虑,或者不只是单纯的想要服务注册于发现功能的话,也可以使用consul来搭建服务注册中心,并且consul还支持K-V键值对存储以及其他扩展功能;但是zookeeper的话,因为其中一些特性个人觉得还是不太适合做服务注册中心。
二、eureka服务端、客户端搭建 (单机版)
eureka服务端
打开刚才下载的骨架,创建一个 eureka-server module,并添依赖
<?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>example</artifactId>
<groupId>com.springCloudExample</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>eureka-server</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
</project>
1、yml配置文件
在resource资源文件夹下创建application.yml配置文件,内容如下
spring:
application:
name: eureka-server
server:
port: 8761
eureka:
client:
register-with-eureka: false #不去eureka服务注册信息,因为本身就是eureka server,除非需要eureka集群
fetch-registry: false #是否从eureka服务获取注册表信息,默认为true,因为是单eureka server 不存在别的eureka服务 所以不需要开启
2、eureka-server启动类
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
/**
* @Description
* @Author Joey
* Date 2019/3/3 下午2:16
* Version 1.0.0
**/
@SpringBootApplication
@EnableEurekaServer //开启Eureka服务端
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
eureka客户端
这里的eureka我们就先拿模拟业务场景中的 用户服务、商品服务 来作为eureka客户端;先创建两个module,user-server、goods-server , pom依赖如下
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
1、user-server、goods-server yml配置
spring:
application:
name: provider-user-service #服务名称
server:
port: 8001
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/ #eureka注册地址,默认注册地址也是这个
instance:
prefer-ip-address: true #将自己的ip注册到eureka 服务,如果为false 则将hostname注册到eureka服务
goods-server的话 配置类似,将服务名,端口号改一下即可。
2、user-server、goods-server 启动类
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
* @Description
* @Author Joey
* Date 2019/3/3 下午2:16
* Version 1.0.0
**/
@SpringBootApplication
@EnableEurekaClient //标记为Eureka客户端
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
启动测试
为了方便起见,我们不采用打包的形式运行,直接在idea里面配置多个run,并且设置Xms,Xmx参数来限制一下堆空间,这样即使我们的电脑内存不大也能运行很多个服务,并不会占用太多的内存。
添加一个springboot run
配置启动类
限制内存大小
启动所有服务
查看 http://localhost:8761/ 可看到eureka管理界面,可以看到两个eureka客户端已经成功的注册了
三、eureka高可用、安全问题
当然,上面都是基于eureka单节点来的,在实际的生产环境中我们是不可能只有一台注册中心,而且目前eureka服务是没有任何安全措施的,也就是说谁都可以来进行注册以及做一些其他的操作,接下来将进行eureka服务改造为安全的高可用集群。
eureka-server 增加如下依赖
<!--整合 spring security 用于设置注册到eureka服务的 用户名和密码-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
eureka-server 增加spring Security配置文件
springBoot 2.x后 security默认开启了 csrf校验,在eureka进行 安全注册的时候 需要关闭
package com.example;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* @Description spring SecurityConf配置类
* @Author Joey
* Date 2019/1/27 下午2:48
* Version 1.0.0
**/
@EnableWebSecurity
public class SecurityConf extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable(); //关闭csrf校验
super.configure(http);
}
}
eureka-server yml配置文件改为如下
eureka服务高可用 其实就是把自己注册到别的eureka服务上,从而达到相互注册高可用的目的,并且也会将各自的注册表进行同步
所以在这里我们要准备多个profile, 启动的时候 只需要指定不同的profiles配置即可;完成eureka安全配置还差一步,就是设置spring security的用户名和密码,因为后续所有服务进行注册以及其他操作都需要用户名与密码进行basic验证。
spring:
application:
name: eureka-server
security: #springBoot2.X 默认就开启了 http basic认证, 网上大部分都停留在springboot以前的版本配置开启 basic认证
user:
name: user #用户名
password: 123 #密码
---
server:
port: 8761
spring:
profiles: eureka1
eureka:
client:
service-url:
#注册需要加上用户名密码 格式为 http://用户名:密码@地址:端口;注册多个可用 , 隔开
defaultZone: http://user:123@localhost:8762/eureka/,http://user:123@localhost:8763/eureka/
instance:
prefer-ip-address: true
---
server:
port: 8762
spring:
profiles: eureka2
eureka:
client:
service-url:
defaultZone: http://user:123@localhost:8761/eureka/,http://user:123@localhost:8763/eureka/
instance:
prefer-ip-address: true
---
server:
port: 8763
spring:
profiles: eureka3
eureka:
client:
service-url:
defaultZone: http://user:123@localhost:8761/eureka/,http://user:123@localhost:8762/eureka/
instance:
prefer-ip-address: true
修改一下run config,添加多个eureka-server的run,并且分别指定不同的profiles
eureka 客户端修改
客户端只需要将defaultZone配置参数改一下即可,其他参数不变;而且defaultZone注册地址注册一个就好了,eureka会进行同步到其他eureka服务,但是为了避免在极端网络环境下可能会出现问题,这里建议将所有eureka服务都进行注册。
service-url:
defaultZone: http://user:123@localhost:8761/eureka/,http://user:123@localhost:8762/eureka/,http://user:123@localhost:8763/eureka/
测试
启动所有eureka-server和客户端,随便打开一个eureka-server端输入用户名密码进入即可看到注册信息。