目录
Eureka
Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。SpringCloud将它集成在其子项目spring-cloud-netflix中,以实现SpringCloud的服务发现功能。
eureka是Netflix的子模块之一,包含2个组件:EurekaServer和EurekaClient。
EurekaServer
Eureka Server提供服务注册服务,各个节点启动后,会在Eureka Server中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。
EurekaServer需要启动一个单独的服务,Spring Cloud对其进行了整合,来帮助Spring Cloud实现“服务的注册与发现”功能。
为了实现高可用,一般会搭建EurekaServer集群,EurekaServer节点之间互相注册,只要有一个节点活着,就可以正常工作。
EurekaClient可以将自己注册到EurekaServer上,Client和Server之间会进行心跳检测,确保服务还能正常运行,Client宕机后,Server会将其从注册中心剔除,Client恢复后,Server又会将其重新注册进来。
eureka只负责提供“服务的注册与发现”,服务之间的调用由ribbon来完成。
EurekaClient
Eureka Client是一个java客户端,用于简化与Eureka Server的交互,客户端同时也就是一个内置的、使用轮询(round-robin)负载算法的负载均衡器。
EurekaClient是需要集成在我们自己的项目里面的,项目启动时,EurekaClient会与EurekaServer通信,将自己的服务信息封装成InstanceInfo,注册到EurekaServer中。
将一个大型项目拆分成多个微服务之后,单个微服务就是一个EurekaClient,为了方便微服务之间互相发现和通信,EurekaServer提供了“注册与发现”功能,所有的微服务都要注册到EurekaServer中。
基本原则
- 服务启动时会生成服务的基本信息对象InstanceInfo,然后在启动时会register到服务治理中心。
- 注册完成后会从服务治理中心拉取所有的服务信息,缓存在本地。
- 之后服务会30s(可配置)发送一个心跳监测,续约服务。
- 如果服务治理中心在90s内没有收到一个服务的心跳,就会认为服务已经挂了,会把服务注册信息剔除。
- 服务停止前,服务会主动发送一个停止请求,服务治理中心会剔除这个服务的信息。
- 如果Eureka Server收到的心跳包不足正常值的85%(可配置)就会进入自我保护模式(可以关闭),在这种模式下,Eureka Server不会删除任何服务信息,但是仍然可以注册,不会同步到其他节点,保证当前节点可用。
搭建Eureka服务集群
Spring Cloud版本高度依赖于Spring Boot版本,参考官网的版本说明,否则版本不兼容服务启动会报错。
笔者使用的版本:2.1.9.RELEASE、Greenwich.SR4
创建三个SpringBoot项目,并按照如下操作:
pom.xml加依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
启动类加注解
@EnableEurekaServer
application.yml
其他节点操作一致,只需稍微修改配置文件即可。
server:
port: 3001
spring:
application:
name: eureka3001 #注册到eureka中的名字
eureka:
instance:
hostname: eureka3001.com
server:
enable-self-preservation: false #关闭自我保护机制
eviction-interval-timer-in-ms: 5000 #5s清理一次服务 默认60s
client:
register-with-eureka: true #是否把自己当做客户端注册
fetch-registry: true #是否需要从注册中心同步其他服务注册信息
service-url:
#需要修改host,不能使用localhost
defaultZone: http://eureka3002.com:3002/eureka,http://eureka3003.com:3003/eureka
笔者搭建的环境:3个EurekaServer节点组成的集群,端口分别为:3001、3002、3003。
项目构建完成后,依次启动然后访问,看到如下页面表示搭建成功:
Eureka客户端注册
创建一个基于SpringBoot的Web项目,EurekaClient必须要对外提供服务。
这里创建一个User项目。
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
启动类加注解
@EnableEurekaClient
application.yml
server:
port: 5001
spring:
application:
name: user-module #注册到eureka的name
eureka:
instance:
hostname: localhost
instance-id: user-1 #服务注册到eureka的唯一标识
prefer-ip-address: true #注册服务时使用IP而不是hostname
lease-renewal-interval-in-seconds: 30 #发送心跳的间隔
lease-expiration-duration-in-seconds: 60 #超过该时间没有发送心跳就表示宕机
client:
service-url:
defaultZone: http://eureka3001.com:3001/eureka #注册到EurekaServer
EurekaClient已经嵌入到User项目中,只需启动User项目,会自动向EurekaServer注册。
Ribbon服务间调用
eureka只负责“服务的注册与发现”,服务之间的调用由ribbon来完成。
spring-cloud-starter-netflix-eureka-client
中已经包含ribbon依赖,无需额外引入。
有三个EurekaClient:User、System-1、System-2,两个System命名相同,EurekaServer会将两个服务注册为集群。
现在User要调用System提供的服务,
注入RestTemplate
@Bean
@LoadBalanced//负载均衡注解 默认轮询
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
通过服务名调用
@RestController
@RequestMapping("user")
public class UserController {
@Autowired
private RestTemplate restTemplate;
private final String SYSTEM_SERVER = "http://system/";
@RequestMapping("system")
public Object system() {
return restTemplate.getForEntity(SYSTEM_SERVER + "system/hello", Map.class);
}
}
这样就可以在User模块中调用System模块提供的服务。
负载均衡策略
Ribbon自带7种负载均衡策略:
-
RoundRobinRule 轮询
-
RandomRule 随机访问
-
RetryRule
默认是轮询,如果节点出现问题会重试几次,还是无响应就会移出轮询列表。 -
WeightedResponseTimeRule
首先是轮询,然后根据轮询的结果来计算节点的权重,之后性能更好的节点会被请求的更多。 -
BestAvailableRule
优先请求并发量最少的一个节点。 -
AvailabilityFilteringRule
-
ZoneAvoidanceRule
自定义负载均衡策略
如果内置的负载均衡策略无法满足需求,可以自定义一个。
例如:根据请求IP地址的哈希值来决定调用哪一个节点。
实现IRule或者继承AbstractLoadBalancerRule。
/**
* @Author: 潘
* @Date: 2019/11/27 21:19
* @Description: 自定义负载均衡策略
*/
public class MyRule implements IRule {
private ILoadBalancer lb;
private Random random = new Random();
@Override
public Server choose(Object o) {
System.out.println("自定义负载均衡策略...");
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String remoteAddr = request.getRemoteAddr();
List<Server> allServers = lb.getAllServers();
int index = remoteAddr.hashCode() % allServers.size();
System.out.println("命中:" + index);
return allServers.get(index);
}
@Override
public void setLoadBalancer(ILoadBalancer iLoadBalancer) {
this.lb = iLoadBalancer;
}
@Override
public ILoadBalancer getLoadBalancer() {
return lb;
}
}
将自定义策略类注册到Spring中
@Bean
public IRule iRule(){
return new MyRule();//随机策略
}
不同服务不同策略
使用@Bean方式注入的负载均衡策略,会被应用于全局。
如果希望不同的服务使用不同的负载均衡策略可以这么做:
创建@Configuration类,但不要被Spring扫描到
@Configuration
public class GoodsRule {
@Bean
public IRule iRule(){
//goods使用随机策略
return new RandomRule();
}
}
@Configuration
public class SystemRule {
@Bean
public IRule iRule(){
//system使用轮询策略
return new RoundRobinRule();
}
}
在启动类上加注解指明具体策略
@RibbonClients({
@RibbonClient(name = "system", configuration = SystemRule.class),
@RibbonClient(name = "goods", configuration = GoodsRule.class)
})
这样就可以了,Goods服务使用随机策略,System服务使用轮询策略。
Feign
Feign是一个声明式WebService客户端,使用Feign能让编写Web Service客户端更加简单。
到目前为止,实现了服务的注册与发现,服务之间的调用也可以完成,但是使用不太友好。
使用Feign可以更加优雅的完成服务的调用,就像调用一个Service一样简单。
引入依赖
<!--feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
启动类加注解
//启用Feign
@EnableFeignClients
//默认扫描启动类的所在目录及子包,如果需要扫描指定包:
@EnableFeignClients(basePackages = {"com.ch.douban.feign"})
编写Feign接口
//服务名 goods
@FeignClient("goods")
public interface GoodsServiceClient {
//调用服务的路径
@RequestMapping("goods/hello")
public Object goodsHello();
//需要接受参数时 @RequestParam String key
}
注入使用即可
@Autowired
private GoodsServiceClient goodsServiceClient;
@RequestMapping("goods")
public Object goods() {
//使用Feign调用服务
return goodsServiceClient.goodsHello();
}
负载均衡啥的依然有效。
Hystrix
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
“熔断器”本身是一个开关装置,可以看成是“保险丝”,当某个微服务节点发生故障时,Hystrix能及时检测到并进行服务降级和熔断,返回一个备选的处理方案,而不是长时间的阻塞或抛出异常,防止服务调用方的线程被长时间的占用,从而避免因为单个服务节点故障导致整个系统瘫痪。
常用配置
# hystrix配置
hystrix:
threadpool:
default:
coreSize: 10 #线程池核心线程数
maxQueueSize: 5 #任务等待队列长度
queueSizeRejectionThreshold: 5 #排队线程数阈值
command:
default:
circuitBreaker:
requestVolumeThreshold: 3 #单位时间内失败的次数达到该值,进行熔断
sleepWindowInMilliseconds: 5000 #熔断后,5s会进行一次服务重试,服务正常后会关闭熔断
#errorThresholdPercentage: 50 #失败率达到50%进行熔断
forceOpen: false #强制打开熔断器,拒绝所有请求 默认false
forceClosed: false #强制关闭熔断器
metrics:
rollingStats:
timeInMilliseconds: 3000 #滑动窗口的大小 3s内失败了requestVolumeThreshold次进行熔断
execution:
timeout:
enabled: true #是否启用超时监听 默认true
isolation:
thread:
timeoutInMilliseconds: 400 #超时时间
interruptOnTimeout: true #方法执行超时是否中断 默认true
interruptOnCancel: true #方法执行取消是否中断 默认true
semaphore:
maxConcurrentRequests: 10 #最大请求并发数 默认10 使用SEMAPHORE策略生效
requestCache:
enabled: true #请求结果缓存 默认true
requestLog:
enabled: true #是否开启请求日志 默认true
fallback:
enabled: true #是否开启方法回退 默认true
添加依赖
<!--hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
启动类加注解
//启用hystrix
@EnableHystrix
服务降级、超时
当某个微服务出现异常或者响应时间过长时,为了不让线程一直阻塞在那里,或者不把错误信息直接返回给客户端,我们可以提供一个备选的降级方法,当服务出现问题时,Hystrix会自动进行服务降级。
服务降级的好处:
- 监听请求是否超时
- 不返回错误信息
- 系统资源不够用时,可以关闭一些不常用的服务,把资源让出来
在需要进行服务降级的方法上加注解
@RequestMapping("goods")
//fallbackMethod:降级的方法名
@HystrixCommand(fallbackMethod = "goodsFallBack")
public Object goods() {
return restTemplate.getForEntity(GOODS_SERVER + "goods/hello", Map.class);
}
//goods降级方法
public Object goodsFallBack(){
//可以进行一些日志的记录...
return "goods暂时不可用";
}
最基本的“服务降级”就配置好了,当“goods服务”抛出异常,或者响应时间过长时,Hystrix会调用降级方法。
熔断
当某个微服务连续调用多次都失败时,Hystrix就会启用熔断。
开启熔断后,Hystrix不会再请求服务,直接返回降级方法,在一定的时间内会对服务进行重试,直到服务正常后,Hystrix就会关闭熔断,然后请求服务。
默认10秒内20次失败就会开启熔断,5秒进行一次服务重试,可以进行自定义配置。
限流
限制微服务的调用量,当某个微服务支持的并发量达不到预期时,可以进行限流,Hystrix会将多余的请求进行服务降级。
Hystrix通过线程池的方式来管理微服务的调用。
@RequestMapping("goods")
//fallbackMethod:降级的方法名
@HystrixCommand(fallbackMethod = "goodsFallBack",
threadPoolKey = "goods",
threadPoolProperties = {
//核心线程数2 等待队列1 最多能处理3个线程,超过3并发的请求会进行降级
@HystrixProperty(name="coreSize",value = "2"),
@HystrixProperty(name="maxQueueSize",value = "1")
}
)
public Object goods() {
return restTemplate.getForEntity(GOODS_SERVER + "goods/hello", Map.class);
}
Feign整合Hystrix
Feign是支持Hystrix的,但在新版本的SpringCloud中默认将其关闭了,需要手动开启。
开启Hystrix
#feign启用Hystrix
feign:
hystrix:
enabled: true
编写服务降级类
/**
* @Author: 潘
* @Date: 2019/11/30 12:27
* @Description: GoodsServiceClient降级方法
*/
@Component
public class GoodsServiceFallBack implements GoodsServiceClient {
@Override
public Object goodsHello() {
return "Goods服务暂时不可用...";
}
}
FeignClient通过fallback指明服务降级类
@FeignClient(value = "goods",fallback = GoodsServiceFallBack.class)
public interface GoodsServiceClient {
//调用服务的路径
@RequestMapping("goods/hello")
public Object goodsHello();
}
当服务出现问题时,Feign就会调用服务降级类中的对应方法。
Zuul
Zuul在SpringCloud家族中扮演着API网关的角色。
在微服务架构中,会有很多个服务提供者,对于服务调用者而言,经常需要从多个服务中获取数据,如果让调用者直接请求相应的服务,那么系统需要对外暴露很多地址,不好管理也不安全。
通过Zuul统一对外暴露一个聚合地址,当调用者请求Zuul时,由Zuul来根据URL进行路由的转发,可以屏蔽微服务内部的细节,更加的安全也方便管理。
搭建Zuul服务
引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
启动类加注解
//启用Zuul
@EnableZuulProxy
配置文件
server:
port: 8001
spring:
application:
name: zuul #注册到eureka的name
eureka:
instance:
hostname: localhost
instance-id: zuul-1 #服务注册到eureka的唯一标识
client:
service-url:
defaultZone: http://eureka3001.com:3001/eureka
#zuul配置
zuul:
ignored-services: "*" #禁止通过服务名调用,只能通过zuul转发
prefix: /api #统一访问前缀
routes: #配置路由
goods: #路由名 自定义
serverId: goods #路由的服务(注册到eureka的服务名)
path: /zuul_goods/** #通过该路径访问路由的服务
过滤器
Zuul定义了4中过滤器的类型:
-
PRE
请求被路由之前调用。可利用这种过滤器实现身份验证、在 集群中选择请求的微服务、记录调试信息等。 -
ROUTING
将请求路由到微服务。用于构建发送给微服务的请求,并使用Apache HttpCIient或Netfilx Ribbon请求微服务。 -
POST
路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。 -
ERROR
发生错误时执行。
SpringCloud内置了一些过滤器,在org.springframework.cloud.netflix.zuul.filters
包下,可以通过配置文件禁用。
自定义过滤器
继承ZuulFilter重写方法即可
/**
* @Author: 潘
* @Date: 2019/11/30 13:05
* @Description: 自定义Zuul过滤器
*/
@Component
public class MyFilter extends ZuulFilter {
//过滤器类型
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
//过滤器调用顺序,不同类型的过滤器允许返回相同类型的顺序
@Override
public int filterOrder() {
return FilterConstants.PRE_DECORATION_FILTER_ORDER;
}
//是否执行过滤器
@Override
public boolean shouldFilter() {
return true;
}
//具体过滤逻辑,比如:记录一下访问的IP和uri
@Override
public Object run() throws ZuulException {
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletRequest request = currentContext.getRequest();
String remoteAddr = request.getRemoteAddr();
String requestURI = request.getRequestURI();
System.out.println(remoteAddr + requestURI);
return null;
}
}
整合Hystrix
Zuul整合了Hystrix和Ribbon,提供降级回退,不同的是Ribbon可以实现API级别的回退,Zuul只能提供服务级别的回退。
实现FallbackProvider编写回退逻辑
/**
* @Description: Zuul回退降级
*/
@Component
public class MyFallbackProvider implements FallbackProvider {
@Override
public String getRoute() {
//服务id *或者null表示所有服务
return "*";
}
/**
* 回退逻辑
* @param route 出错的服务
* @param cause 错误信息
* @return
*/
@Override
public ClientHttpResponse fallbackResponse(String route,final Throwable cause) {
if (cause instanceof HystrixTimeoutException) {
return response(HttpStatus.GATEWAY_TIMEOUT);
} else {
return response(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
private ClientHttpResponse response(final HttpStatus status) {
return new ClientHttpResponse() {
//状态信息
@Override
public HttpStatus getStatusCode() throws IOException {
return status;
}
//状态码
@Override
public int getRawStatusCode() throws IOException {
return status.value();
}
//http的reasonPhrase信息
@Override
public String getStatusText() throws IOException {
return status.getReasonPhrase();
}
//降级服务响应结束后调用的方法
@Override
public void close() {
}
//响应内容
@Override
public InputStream getBody() throws IOException {
return new ByteArrayInputStream("goods服务暂时不可用".getBytes());
}
//设置响应报头header信息
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
};
}
}
Gateway
Gateway也是API网关,性能要好于Zuul1.0,由于Zuul的种种问题,Spring决定自己孵化一个API网关项目,Gateway就此诞生,现在更推荐使用Gateway。
添加依赖
因为要将Gateway注册到Eureka上,所以需要加eureka-client依赖。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
启动类加注解
@EnableEurekaClient
配置文件
server:
port: 8001
spring:
application:
name: gateway #注册到eureka的name
cloud:
gateway:
discovery:
locator:
enabled: true #开启 Gateway 服务注册中心服务发现
#配置路由
routes:
- id: gateway-goods
uri: lb://goods
predicates:
- Path=/api/**
filters:
- StripPrefix=1 #去掉一个前缀,否则转发时会带/api
eureka:
instance:
hostname: localhost
instance-id: gateway-1 #服务注册到eureka的唯一标识
client:
service-url:
defaultZone: http://eureka3001.com:3001/eureka
这样一个简单的Gateway服务就搭建完成了,Gateway还有很多强大的功能:断言、过滤…。
这里不记录,笔者会单独开一片博客记录。
HystrixDashboard
HystrixDashboard是Hystrix提供的一个可视化工具,可以用于监控服务的调用状态。
actuator
Hystrix本身提供了对服务调用的监控,但是需要结合actuator来查看监控信息。
在需要监控的项目中引入依赖
<!--actuator-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
SpringCloud2.0之后的版本需要做以下配置
#暴露Actuator的端点
management:
endpoints:
web:
exposure:
include: '*'
启动服务,访问:项目路径/actuator/hystrix.stream
即可查看到监控信息。
actuator以Json数据返回,不方便查看,HystrixDashbord提供了可视化界面。
HystrixDashboard搭建
秉承“单一职责”原则,就不要将HystrixDashboard放到业务服务里了,单独创建一个监控服务。
创建一个HystrixDashboard项目,引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
启动类加注解
//启用HystrixDashboard
@EnableHystrixDashboard
配置文件配置一下端口,启动服务即可。
访问:IP:port/hystrix
看到如下界面,表示搭建成功。
在输入框中输入需要监控的地址,即可看到监控的图形化界面。
仪表盘解释
实心圆:
- 大小:表示请求数量
- 颜色:表示服务的健康程度
曲线:2分钟内流量的变化。
百分比:10s内错误请求的百分比。
数字:
- 绿色:成功数
- 蓝色:熔断数
- 淡蓝:错误请求
- 黄色:超时数
- 紫色:线程拒绝数
- 红色:失败/异常数
Host:服务请求频率。
Circuit:熔断状态。
Spring Cloud Config
统一配置中心
微服务架构中,随着服务的增多,每个服务都要做独立的配置就变得特别的繁琐,也不方便统一管理,一旦某个服务的信息发生变化,牵扯到的服务也要修改对应的配置,十分麻烦。
为了解决这个问题,就需要有一个“统一配置中心”,对配置进行统一的管理,微服务从上面读取配置即可。
服务端搭建
还是“单一职责”原则,新建一个服务来跑配置中心。
引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
启动类加注解
//启用ConfigServer
@EnableConfigServer
Spring Cloud Config支持许多种读取配置的方式:本地、Git、SVN等。
这里为了方便,用本地配置测试。
创建一个供客户端读取的配置文件:config-dev.yml,这里主要配置eureka。
#eureka统一配置
eureka:
instance:
hostname: localhost
prefer-ip-address: true #是否显示IP
lease-renewal-interval-in-seconds: 30 #发送心跳的间隔
lease-expiration-duration-in-seconds: 60 #超过该时间没有发送心跳就表示宕机
client:
service-url:
defaultZone: http://eureka3001.com:3001/eureka
编写Server服务自身的配置文件
server:
port: 2001
spring:
profiles:
active: native #本地环境
cloud:
config:
server:
native: #本地配置文件模式
search-locations: classpath:/
#git: #通过github读取配置文件 如果私有仓库需要配置用户名、密码
# uri: https://github.com/15797680617/cloud-config.git
配置中心服务搭建完成,启动服务即可。
访问:http://localhost:2001/config-dev.yml
即可查看配置文件。
客户端读取
这里将“goods”服务的配置改为从配置中心读取。
引入依赖
<!--spring-cloud-config-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
删除原application.yml中对eureka的配置。
创建bootstrap.yml配置文件,在这里设置从配置中心读取。
spring cloud有一个“引导上下文"的概念,这是主应用程序的父上下文。bootstrap.yml中的优先级最高,会覆盖application.yml中的配置。
spring:
cloud:
config:
name: config #配置文件名
profile: dev #获取的环境
#label: master 版本
uri: http://localhost:3001/ #configServer的地址
这样就可以了,goods启动时会从服务端读取配置,本地application.yml配置会被覆盖。
Sleuth
在微服务架构中,对于一个客户端的请求,中间可能需要经过很多微服务进行处理,一旦请求异常,就需要对异常进行排查。
微服务异常排查的成本要远高于单机系统,首先要定位到是哪一台服务出了问题,然后再去分析日志。
Sleuth为SpringCloud提供了“分布式链路跟踪”,通过它可以很清楚的看到,对于某一个请求,中间经过了多少服务、耗时多久、之间的依赖关系、异常信息等。
各组件间的关系:
- Sleuth:负责收集数据。
- Zipkin客户端:负责将Sleuth收集的数据发送到Zipkin服务端。
- Zipkin服务端:负责接收数据,并以图形界面的方式展示。
分布式链路跟踪
术语:
Span(跨度)
工作的基本单元,span由一个唯一的64位ID标识。
对于客户端的一个请求,每次在不同的微服务之间调用都会生成一个新的span。span还包含其他数据,比如描述、时间戳事件、键值。
Trance(跟踪)
由一组span构成的树状结构。Trance ID也由一个唯一的64位ID标识。
可以理解为:Trance对应着一个请求的一组调用链。
对于客户端的一个请求,不管它在微服务之间如何流转,整个调用链都共享同一个Trance ID,这可以帮助开发人员定位到一个请求的整个调用情况。
Annotation(标注)
用于及时记录服务之间请求的发起和响应的事件信息。
具体信息可以查看官网。
Sleuth可以追踪10种类型的组件:async、Hystrix,messaging,websocket,rxjava,scheduling,web(Spring MVC Controller,Servlet),webclient(Spring RestTemplate)、Feign、Zuul。
Zipkin Server搭建
Zipkin是 Twitter开源的分布式跟踪系统,基于 Dapper的论文设计而来。它的主要功能是收集系统的时序数据,从而追踪微服务架构的系统延时等问题。 Zipkin还提供了一个非常友好的界面,来帮助分析追踪数据。
Sleuth对于分布式链路跟踪仅仅是收集Json数据,开发人员如果要对Json进行分析就太不方便了,Zipkin可以对Sleuth收集的数据用图形界面的方式友好的展示出来。
同样的“单一职责”,搭建新的服务专门跑Zipkin。
创建一个Maven项目,引入依赖
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-ui</artifactId>
<version>2.8.4</version>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-server</artifactId>
<version>2.8.4</version>
</dependency>
配置文件
server:
port: 1001
management:
metrics:
web:
server:
autoTimeRequests: false #Zipkin2.7之后要加
Zipkin2.7之后,不支持自定义服务器搭建,必须使用官方版本或Docker搭建,但是通过配置仍然可以使用,正式用推荐官方版本。
服务搭建完成,启动后访问看到下图界面表示搭建成功。
此时点击Find Traces是没有数据的,因为还没有客户端上传数据到服务端。
Sleuth整合Zipkin
这里将User和Goods模块整合Sleuth、Zipkin,通过User调用Goods,然后看一下调用的链路数据。
需要被跟踪的服务引入依赖
<!--sleuth-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<!--zipkin-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
配置文件,也可以配置到‘统一配置中心’。
spring:
zipkin:
base-url: http://localhost:1001 #zipkin server地址
sleuth:
sampler:
probability: 1.0 #请求采样率 默认0.1即10%
启动服务,发起一个请求,让User去调用Goods,然后查看Zipkin。
点进去,可以查看调用链的具体信息,包括服务名,耗时、调用的类名、方法名等。
可以查看到具体的报错信息,更快的定位到问题。
数据持久化
Zipkin默认将数据保存在内存中,服务一旦重启收集的数据就没了,可能会丢失重要的信息。
可以整合Elasticsearch、MySQL等做数据的持久化。
以ES为例,引入依赖
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-storage-elasticsearch-http</artifactId>
<version>2.3.1</version>
</dependency>
配置文件
#使用ES做数据持久化
zipkin:
storage:
type: elasticsearch
elasticsearch:
cluster: elasticsearch
hosts: http://localhost:9200
index: zipkin
这样即使服务重启,数据也不会丢失。