Spring cloud相关组件:
注册中心:eureka
版本选择:
创建springboot项目依照自己的cloud版本来选择boot的版本,cloudF以上的版本是用springboot2.x(以下是它支持的一些版本)
springcloud和springboot对照的版本要求
本次使用的是springboot 1.5.9 cloud使用的是D 版本,使用的项目为自己的简易练习项目
了解Eureka:
Eureka Server:用法相当于zookeeper
Eureka Client:用法相当于Provider和Consumer
Eureka Server集群之间通过注册表进行数据的同步,注册表保存的是ip+端口
Eureka Client每30秒会向Eureka Server发送一次心跳请求证明当前Client存在
Eureka Server在90秒内没有收到Client的心跳请求,就会认为当前Client消亡
自我保护机制:Eureka Server在很短的时间内,统计到大量的Client没有发送心跳,Server会默认为是网络异常,进行自我保护,不再剔除没有发送心跳的Client数据,此自我保护在心跳请求恢复后会自动退出
Eureka Client会定时从Server里全量/增量更新注册表,并缓存到本地
Eureka由于是定时拉取注册表,所以无法保证数据的安全性,但是保证了可用性
Zookeeper与Eureka相反,可用性相对差,安全性高
CAP理论:一个分布式系统不可能同时满足:C:一致性,A:可用性,P:分布式容错Zookeeper满足CP,Eureka满足AP
项目流程:
创建Eureka server项目
创建项目后导入springcloud和Eureka server依赖:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
****Erueka Server:****
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-eureka-server</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
配置文件使用的是yml的格式,加入如下配置(注意缩进):
eureka:
client:
service-url:
#设置交互的地址
defaultZone: http://localhost:7776/eureka
#是否从Eureka获取注册信息,当前是单点,不需要同步其他所以设置为false
fetch-registry: false
#是否向注册中心注册自己
register-with-eureka: false
在启动类加入注解:@EnableEurekaServer,此时即可启动,启动后访问localhost:7776即可进入eureka界面,可查看当前注册信息
创建Eureka Client-provider项目
引入springCloud依赖和Eureka client依赖:(注意:springCloud依赖与server相同,而client依赖与server不同)
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
****Erueka Client:****
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
在yml文件中进行文件配置:
eureka:
client:
service-url:
#设置交互的地址
defaultZone: http://localhost:7776/eureka
spring:
application:
name: 服务名字(provider)
在启动类加入注解:@EnableDiscoveryClient或者@EnableEurekaClient
使用@EnableEurekaClient的情景:就是在服务采用eureka作为注册中心的时候,使用场景较为单一,这里多数用@EnableDiscoveryClient
Eureka自我保护机制介绍:
默认情况下,如果Eureka Server在一定时间内没有接收到某个微服务实例的心跳,Eureka Server将会注销该实例(默认90秒)。但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,以上行为可能变得非常危险了——因为微服务本身其实是健康的,此时本不应该注销这个微服务。
Eureka通过“自我保护模式”来解决这个问题——当Eureka-Server节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。一旦进入该模式,Eureka-Server就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该Eureka Server节点会自动退出自我保护模式。
综上,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留),也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka集群更加的健壮、稳定。
关闭自我保护机制的方法(测试用,在项目上线后可以重新打开):
在Eureka server的yml文件下配置:
server:
#禁用自我保护模式
enable-self-preservation: false
# 续期时间,即扫描失效服务的间隔时间
eviction-interval-timer-in-ms: 2000
在Eureka Client的yml文件下配置:
instance:
#表示eureka-server至上一次收到client的心跳之后,等待下一次心跳的超时时间,在这个时间内若#没收到下一次心跳,则将移除该instance。默认为90秒
lease-expiration-duration-in-seconds: 2
#表示eureka—client发送心跳给server端的频率。如果在leaseExpirationDurationInSeconds后,#server端没有收到client的心跳,则将摘除instance除此之外,如果该instance实现了HealthChec#kCallback 并决定让自己unavailable的话,则该instance也不会接收到流量。默认30秒
lease-renewal-interval-in-seconds: 2
#配置以ip地址注册到注册中心
instance-id: ${spring.cloud.client.ipAddress}:${server.port}
Eureka加入用户验证:
为什么需要用户验证:没有用户验证的Eureka Server只要知道地址所有人都可以访问,为了加强安全性一般会为其添加安全认证
添加认证是基于Spring的Security组件,只需要添加依赖和配置即可
security.basic.enabled设置为true开启认证
security.user.name:认证用户名
security.user.password:认证密码
此时通过浏览器重新访问就需要输入用户名和密码
server端加入如下包:
<!--用户验证 start-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--用户验证 end-->
server端yml加入如下配置:
security:
#服务端验证
basic:
#开启验证
enabled: true
user:
name: root
password: root
此时使用原来的交互地址就无法访问了!
client端交互地址改为如下所示即可:
defaultZone: http://root:root@localhost:7776/eureka
使用Feign完成客户端互相调用:
Feign是简化我们接口调用的工具,并且也整合了对其他一些组件的支持。整体调用是基于接口的形式
创建consumer,yml配置文件与pom导包与provider相同。
provider端编写service类(实为controller)举例如下:
@RestController
public class UserService {
@RequestMapping(value = "/login")
public String login(){
return "登录成功1";
}
}
在consumer端pom导入feign依赖包:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
创建client目录,添加client类,举例如下:
@FeignClient(name = "eureka-provider-demo")
public interface UserClient {
@RequestMapping(value = "/login")
public String login();
}
//注:这里的eureka-provider-demo是provider项目配置的服务名字
创建service类,引用client类,再创建controller类,引入service,即可调用(实际就是通过
@FeignClient注解的name调用对应client的对应方法)
consumer启动类加入@EnableFeignClients即可运行调用
容错介绍:
在微服务架构中,我们将系统拆分成了很多服务单元,各单元的应用间通过服务注册与订阅的方式相互依赖。但由于每个单元都在不同的进程中运行,一来通过远程调用的方式执行,这样就有可能因为网络原因或是依赖服务自身问题出现调用故障或延迟,而这些问题会直接导致调用方的对外服务也出现延迟,若此时调用方的请求不断增加,最后就会因等待出现故障的依赖方响应形成任务积压,最终导致自身服务的瘫痪。
如何容错:
1,超时重试
2,熔断器
3,限流
容错代码实现:
创建一个实现类去实现consumer的接口:在实现得对应方法中放入容错得信息,并把这个类注入到spring中
举例如下:
/**
* 服务降级后执行内容
*/
@Component
public class UserFallback implements UserClient {
@Override
public String login() {
return "超时请重试";
}
}
在接口信息中得@FeignClient注解中加入 fallback=“上一步实现类.class”,示例如下:
@FeignClient(name = "eureka-provider-demo",fallback = UserFallback.class)
在application. yml中添加配置开启容错:
feign:
hystrix:
enabled: true
此时启动服务,若获取provider出现问题则会跳到指定fallback接口
容错可视化:
consumer的pom引入相关依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
在启动类添加注解:@EnableCircuitBreaker
访问/hystrix.stream端点即可
使用Hystrix Dashboard实现可视化监控:
创建一个新的springcloud项目
引入如下依赖包:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
启动类添加注解:@EnableHystrixDashboard
访问 ip+服务端口+hystrix
在弹出的页面中输入监控地址即可
聚合监控 turbine
创建springcloud项目启动类添加注解:
@EnableTurbine和@EnableDiscoveryClient
在yml中配置如下:
server:
port: 8085
eureka:
client:
service-url:
#设置交互的地址
defaultZone: http://root:root@localhost:7776/eureka
instance:
#表示eureka server至上一次收到client的心跳之后,等待下一次心跳的超时时间,在这个时间内若没收到
#下一次心跳,则将移除该instance。默认为90秒
lease-expiration-duration-in-seconds: 2
#表示eureka client发送心跳给server端的频率。如果在leaseExpirationDurationInSeconds后,s
#erver端没有收到client的心跳,则将摘除instance除此之外,如果该instance实现了HealthCheckCallback
#并决定让自己unavailable的话,则该instance也不会接收到流量。默认30秒
lease-renewal-interval-in-seconds: 2
instance-id: ${spring.cloud.client.ipAddress}:${server.port}
spring:
application:
name: hystrix-turbine
turbine:
#指定聚合哪些集群
cluster-name-expression: " 'default' "
#此配置默认为false,则服务是以host进行区分,若设置为true则以host+port进行区分
combine-host-port: true
#配置Eureka中的serviceId列表,表明监控哪些服务,多个服务间用逗号隔开
app-config: eureka-consumer-demo,eureka-consumer-demo1
导入pom依赖如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-turbine</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
查看监控
1.启动项目turbine,dashboard
2.访问接口产生数据
3.访问dashboard主页
4.url:ip+端口+turbine.stream
Ribbon实现负载均衡
在consumer添加pom依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
//(因为项目中已经添加“spring-cloud-starter-eureka-server”依赖,在这里已经包含了Ribbon,所以不用再单独引入)
yml中添加配置如下:
eureka-provider-demo:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
配置多个provider,name名起成一样的,并与consumer中的client绑定的name相同,就可以造成一个consumer配多个provider
根据策略配置不同,实现的效果也不同:(不配置的时候会挨个轮上一遍)
WeightedResponseTimeRule 根据响应时间分配一个weight(权重),响应时间越长,weight越小,被选中的可能性越低
RoundRobinRule 轮询选择server(此为默认的负载均衡策略)
RandomRule 随机选择一个server
ZoneAvoidanceRule 复合判断server所在区域的性能和server的可用性选择server
RetryRule 在一个配置时间段内,当选择server不成功时一直尝试重新选择
BestAvailableRule 选择一个并发请求最小的server
AvailabilityFilteringRule 过滤掉那些因为一直连接失败而被标记为circuit tripped的server,并过滤掉那些高并发的server(active connections 超过配置的阈值
Zuul实现微服务网关
为什么需要网关:
客户端多次请求不同的微服务,增加客户端的复杂性
每个服务都需要独立认证,过于复杂
存在跨域请求,在特殊场景下处理比较复杂
难以重构
Zuul:
身份认证和安全
审查与监控
动态路由
静态响应处理
弹性负载均衡
搭建Zuul微服务网关:
创建一个新的springcloud项目
导入pom依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
编写yml配置文件:
server:
port: 8086
eureka:
client:
service-url:
#设置交互的地址
defaultZone: http://root:root@localhost:7776/eureka
instance:
lease-expiration-duration-in-seconds: 2
lease-renewal-interval-in-seconds: 2
instance-id: ${spring.cloud.client.ipAddress}:${server.port}
spring:
application:
name: zuul-server
#动态路由
zuul:
routes:
#eureka-consumer-demo是微服务在Eureka #Server中的注册名称,后面是说通过网关访问时前缀要加上/user,比如访问
#/login就是localhost:8086/user/login
eureka-consumer-demo: /user/**
启动类添加注解:@EnableZuulProxy即可
网关过滤器:
为什么要网关过滤器:
每个请求都需要转发到具体的微服务后再判断,然后将判断的结果回转给网关,效率较低
如果请求发到网关,网关验证不符合要求直接返回给用户,就避免了来回转发的消耗,能很大提升系统的体验,所以过滤器非常必要
过滤器在网关转发前就行进过滤处理
过滤器分类:
PRE
请求被路由转发到微服务之前调用的过滤器
ROUTING
请求被路由转发到微服务时调用的过滤器
POST
请求被路由转发到微服务之后调用的过滤器
ERROR
在其他阶段发生错误时调用的过滤器
创建过滤器,继承ZuulFilter
重写方法
filterType():过滤器类型
filterOrder():过滤顺序
shouldFilter():是否进行拦截过滤
run():确定需要拦截之后执行的方法
filterType代表过滤器类型,这里返回“pre”
filterOrder代表过滤顺序,这里直接返回0即可
shouldFilter代表是否进行拦截过滤,设置为返回true。可以设置为false看是否还会过滤请求
举例如下:
@Component
public class PreFilter extends ZuulFilter {
public String filterType() {
return "pre";
}
public int filterOrder() {
return 0;
}
public boolean shouldFilter() {
return true;
}
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
String token = request.getHeader("token");
//访问链接
String requestUrl = request.getRequestURI().toString();
//判断访问链接(也就是/user后)是不是/p,是的话返回pass,用这种方法进行拦截
if (requestUrl.split("/")[3].equals("p")){
return "pass";
}
return null;
}
}
修改启动类,添加方法:
@Bean
public PreFilter preFilter() {
return new PreFilter();
}
或者在创建的过滤器内添加@Component也可以
禁用过滤器 :
zuul.<SimpleClassName>.<filterType>.disable=true
分布式配置
为什么需要分布式配置:
每个服务都有自己的配置文件,很多公共的配置信息
每次更改配置信息都需要重新发布服务
Spring Cloud Config
公共信息都保存到远程仓库
连接Config Server,无需重启更新配置参数
统一加密解密
配置管理工具
本地存储
Subversion
Git /Gitee
体系
Config Server
Config Client
用法示例如下:
创建配置文件
在码云中创建项目env-project
在env-project项目中创建sprint2.0_dev分支
在sprint2.0_dev分支下创建config-repo文件夹
在config-repo下创建文件client-dev.properties
在client-dev.properties填入以下内容:eureka.port=9006
创建config-server项目
添加springCloud依赖以及所需依赖包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
启动类添加注解:
@EnableConfigServer
@EnableDiscoveryClient
appiaction.yml配置如下:
server:
port: 7900
spring:
cloud:
config:
server:
git:
//gitee项目地址
uri: https://gitee.com/deroser/env-project.git
//gitee账号
username: Deroser
//gitee密码
password: liwenjun958488
//查找配置文件的根目录
search-paths: config-repo
application:
name: dm-config-server
eureka:
client:
service-url:
defaultZone: http://root:root@localhost:7776/eureka
instance:
lease-expiration-duration-in-seconds: 2
lease-renewal-interval-in-seconds: 2
instance-id: ${spring.cloud.client.ipAddress}:${server.port}
启动服务(启动前要先启动eureka-server)
访问端点:http://localhost:7900/client/dev/sprint2.0_dev
会获取对应文件的数据,以json字符串的形式
映射规则:/{application}/{profile}/{label}
{application}:Git仓库中文件名的前缀, 通常使用微服务名称
{profile}:{application}-后面的数值
在同一个分支下可以有多个{application}名称相同的文件
{label}:Git仓库的分支名,默认为master
修改远程配置文件名为
zuul-server-dev.properties
打开Zuul网关项目,为其添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
创建配置文件
在dm-gateway-zuul项目中创建文件bootstrap.yml
(bootstrap.yml和application.yml一样都是Spring Boot项目的配置文件,只是bootstrap.yml侧重配置需要更早进行读取的信息,如可以用来配置application.yml中使用到参数等。application.yml侧重配置应用程序信息,例如可以用来配置程序中需要的公共参数等。bootstrap.yml会先于application.yml被程序加载)
配置如下
spring:
application:
name: zuul-server(对应映射规则中的{application}部分)
cloud:
config:
label: sprint2.0_dev(对应映射规则中的{label}部分)
uri: http://localhost:7900/(对应Config Server的地址)
profile: dev(对应映射规则中的{profile}部分)
eureka:
client:
service-url:
defaultZone: http://root:123456@localhost:7776/eureka/
在zuul项目内添加controller,具体代码示例如下:
@RestController
public class InfoController {
@Value("${eureka.port}")
private String port;
@GetMapping("/port")
public String getPort(){
return "配置文件中的端口为:"+this.port;
}
}
访问地址http://localhost:7600/port后会获取到之前gitee中文件编写的port(9006)
数据加密解密
为什么需要加密解密:数据在配置文件中明文保存,有安全隐患
数据存储加密,使用时解密
Spring Cloud Config
JCE
JCE介绍
JRE中自带,默认有长度限制(可以安装不限制长度版本)
安装
下载JCE(对应JDK版本)
local_policy.jar
US_export_policy.jar
覆盖JDK或JRE的security目录下原文件
开启加密
启动Config Server
/encrypt/status:查看加密功能状态的端点
/key:查看密钥的端点
/encrypt:对请求的Body内容进行加密的端点
/decrypt:对请求的Body内容进行解密的端点
添加配置文件:bootstrap.yml
在bootstrap.yml中添加密钥配置
encrypt:
key: dm
验证
http://localhost:7900/encrypt/status
未添加key的时候会显示nokey,添加后会显示ok
post的方式向 http://localhost:7900/encrypt 发送加密请求
例如发送123456,会返回加密后的长串字符,通过同样的方式向http://localhost:7900/decrypt
发送加密的字符串,会返回123456(解密)
存储和解析加密数据
修改远程仓库中的eureka.port为:{cipher}+加密后的字符串
{cipher}代表当前数据为加密值
配置文件如果是yml文件,tokenValidation的值必须加单引号,如果是properties文件,
则不能有单引号,否则不能正常解析
即使是相同的值,每次加密后的结果也可能不一样,以实际加密结果为准
开启zuul项目,获取远程值,即可获取解密后的数据(123456)
手动刷新配置
自动刷新Spring Cloud 也提供了组件来实现(Bus),但是很少会用
为zuul项目添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
controller类添加注解:@RefreshScope
在application.yml中添加配置:
management:
security:
enabled: false
启动服务后访问http://localhost:7600/port,此时获取的是修改前的值
修改远程配置文件
post方式访问刷新端点http://localhost:7900/refresh
此时访问http://localhost:7600/port,获取的是修改后的值(不需要重启即可刷新)
为config-server添加用户认证:与eureka用户认证相同
在config-server中添加用户依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
在application.yml中添加配置:
security:
basic:
enabled: true
user:
password: 123456
name: root
客户端链接配置修改如下(这里修改zuul项目的配置文件):
spring:
cloud:
config:
uri: http://root:123456@localhost:7900/
运行服务即可