文章目录
Eureka的搭建过程
1.新建spring模块
2.修改pom.xml,添加eureka server
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
3.yml配置
禁用自我保护模式
设置主机名:eureka1
针对单台服务器,buxiang自己注册,不从自己拉取注册表
spring:
application:
name: eureka-server
#2001 3001 4001 5001 6001
server:
port: 2001
eureka:
server:
enable-self-preservation: false #禁用自我保护模式
instance:
hostname: eureka1
client:
#针对单台服务器,不向自己注册,不从自己拉取注册表
register-with-eureka: false
fetch-registry: false
4.启动类注解:@EnableEurekaService触发eureka server的自动配置
Eureka的四条运行机制
1. 注册
客户端一次次的反复连接注册中心进行注册,直到注册成功为止
2. 拉取
客户端每30秒拉取一个注册表,刷新注册表
3. 心跳
客户端每30秒发送一次心跳包,如果服务器端连续三次收不到一个服务的心跳,会删除它的注册信息
4. 自我保护模式(特殊情况下)
由于网络故障,15分钟内,85%服务器出现心跳异常,会自动进入自我保护模式(保护所有注册信息都不删除),网络恢复后,自动退出保护模式
【开发调试时,要把保护模式关闭,避免引起测试】
Eureka客户端的配置
1. 修改host文件(位置:C:\Windows\System32\drivers\etc)
127.0.0.1 eureka1
127.0.0.1 eureka2
2. 修改2,3,4三个模块,添加 Eureka client依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
3. 修改2,3,4三个模块的yml模块,添加 Eureka的IP地址
eureka:
client:
service-url:
#默认地点,如果使用的是云服务,可以通过云服务商购买不同地点的注册中心服务器
#自己搭建的注册中心只能使用defaultZone
defaultZone: http://eureka1:2001/eureka
(重要)java -jar java文件 --server port:8001与yml中的server port重复了,生效的是命令行的server port
Eureka高可用配置
1.新建并修改yml文件
新建application-eureka1.yml,并修改yml文件
eureka:
instance:
hostname: eureka1
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka2:2002/eureka
server:
port: 2001
新建application-eureka2.yml,并修改yml文件
eureka:
instance:
hostname: eureka2
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka1:2001/eureka
server:
port: 2002
2.然后在运行,打开Edit Configurations设置启动配置
先改名xxxx-端口号,然后在Environment---->Program arguments写入:
--spring.profiles.active=eureka1
先改名xxxx-端口号,然后在Environment---->Program arguments写入:
--spring.profiles.active=eureka2
修改2,3,4,连接两台eureka服务器
02YML文件的样子:
#向注册中心注册的名字
spring:
application:
name: item-service
#item 8001, user 8101, order 8201
server:
port: 8001
eureka:
client:
service-url:
#默认地点,如果使用的是云服务,可以通过云服务商购买不同地点的注册中心服务器
#自己搭建的注册中心只能使用defaultZone
defaultZone: http://eureka1:2001/eureka,http://eureka2:2002/eureka
03YML文件的样子:
spring:
application:
name: user-service
server:
port: 8101
#自定义配置属性
#用户的demo数据
#"[{7},{8},{9}]"
sp:
user-service:
users: "[{\"id\":7, \"username\":\"abc\",\"password\":\"123\"},
{\"id\":8, \"username\":\"def\",\"password\":\"456\"},
{\"id\":9, \"username\":\"ghi\",\"password\":\"789\"}]"
eureka:
client:
service-url:
#默认地点,如果使用的是云服务,可以通过云服务商购买不同地点的注册中心服务器
#自己搭建的注册中心只能使用defaultZone
defaultZone: http://eureka1:2001/eureka,http://eureka2:2002/eureka
04YML文件的样子:
spring:
application:
name: order-service
server:
port: 8201
#eureka的注册与发现
eureka:
client:
service-url:
#默认地点,如果使用的是云服务,可以通过云服务商购买不同地点的注册中心服务器
#自己搭建的注册中心只能使用defaultZone
defaultZone: http://eureka1:2001/eureka,http://eureka2:2002/eureka
给项目设置远程调用feign
1.给项目添加feign依赖
2.启动类添加注解@EnableFeignClients(触发feign的自动配置,自动扫描远程接口,自动创建动态代理对象)
3.创建一个接口,并在类上加上注解@FeignClient
/*
* 通过注解配置:
* 调用哪个服务
* 调用服务的哪个路径
* 向这个路径提交什么参数
* */
/**
* 通过name发现服务器的地址
* */
@FeignClient(name = "item-service")
public interface ItemClient {
@GetMapping("/{orderId}")
JsonResult<List<Item>> getItem(@PathVariable("orderId") String orderId);
@PostMapping("/decreaseNumber")
JsonResult<?> getDecreaseNumber(@RequestBody List<Item> Item);
}
4.在实现类注入并使用即可
@Autowired
private UserClient userClient;
@Autowired
private ItemClient itemClient;
Feign集成Ribbon
负载均衡——默认启用
重 试——默认启用
Ribbon重试
调用后台服务失败(异常、服务器崩溃、超时),可以自动发起重试调用
重试参数:
ribbon.MaxAutoRetries - 单台服务器的重试次数,默认0
ribbon.MaxAutoRetriesNextServer - 更换服务器的次数,默认1
(公式为x+1*y+1)
ribbon.ReadTimeout - 响应的超时时间,默认1000ms
ribbon.ConnectTimeout - 与后台服务器建立连接等待超时时间,默认1000ms
ribbon.OkToRetryOnAllOperations - 是否对所有类型请求都进行重试,默认只对GET请求重试
ribbon:
MaxAutoRetries: 1
MaxAutoRetriesNextServer: 2
Zuul - API网关
功能:
统一的调用入口
统一的权限校验
集成Ribbon
集成Hystrix
统一的调用入口配置:
- 先建Spring模块
- pom.xml添加依赖:zuul、eureka client
- yml配置路由规则
spring:
application:
name: zuul
#2001,3001,4001,5001,6001
server:
port: 3001
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka,http://eureka2:2002/eureka
zuul:
routes:
# ** 包含深层子路径
# * 包含单层路径
item-service: /item-service/**
user-service: /user-service/**
order-service: /order-service/**
- 启动类添加注解@EnableZuulProxy
Zuul统一权限校验
继承Zuul的过滤器,在过滤器中判断用户权限
实现步骤:
1.新建过滤器:AccessFilter,继承ZuulFilter
2.按照规则实现过滤器
添加注解:@Component
zuul的自动配置,会从Spring容器自动发现过滤器实例,完成自动配置
代码如下:
package cn.tedu.filter;
import cn.tedu.web.util.JsonResult;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@Component
@Slf4j
public class AccessFilter extends ZuulFilter {
//设置过滤器的类型:pre,routing,post,error
//zuul自动配置的回调方法
@Override
public String filterType() {
//return "pre"; 第一种写法
log.info("Zuul自动配置过滤器类型");
return FilterConstants.PRE_TYPE; //第二种写法
}
//过滤器的顺序号
@Override
public int filterOrder() {
/**
* 在默认的第五个过滤器中,向上下文对象放入了serviceId
* 后面过滤器中才能访问这个数据
* */
log.info("Zuul自动配置过滤器的顺序号");
return 6;
}
//针对当前请求进行判断,是否要执行过滤代码
@Override
public boolean shouldFilter() {
//如果客户端调用商品,要判断权限
//否则调用用户或订单,不判断权限
//第一步:获得当前请求的上下文对象
RequestContext context = RequestContext.getCurrentContext();
//第二步:从上下文对象取出正在访问的serviceId
String o = (String) context.get(FilterConstants.SERVICE_ID_KEY);
//判断serviceId是否是“item-Service”
//equalsIgnoreCase忽略大小写进行比较
return "item-Service".equalsIgnoreCase(o);
}
//过滤代码
@Override
public Object run() throws ZuulException {
//第一步:获得当前请求的上下文对象
RequestContext context = RequestContext.getCurrentContext();
//第二步:从上下文对象取出request
HttpServletRequest request = context.getRequest();
//第三步:用request接收token参数
String token = request.getParameter("token");
//第四步:如果token不存在
if (StringUtils.isBlank(token)){
//第五步:阻止继续访问
context.setSendZuulResponse(false);
//第六步:直接返回响应
String json = JsonResult.build().code(500).msg("没有登录").toString();
//响应数据的协议头
context.addZuulResponseHeader("Content-Type", "application/json;charset=UTF-8");
//响应体
context.setResponseBody(json);
}
return null;//当前版本没有使用这个返回值,设置什么数据都可以
}
}
Zuul集成Ribbon
- 默认已经启用ribbon的负载均衡
- 默认不启用ribbon的重试
在网关重试,可能造成后台服务大面积出现压力翻倍
重试功能应尽量往后放 - 启用重试:
(1)添加spring-retry依赖
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
(2)yml配置启用重试:zuul.retryable=true
spring:
application:
name: zuul
#2001,3001,4001,5001,6001
server:
port: 3001
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka,http://eureka2:2002/eureka
zuul:
routes:
item-service: /item-service/**
user-service: /user-service/**
order-service: /order-service/**
retryable: true
#针对所有服务都有效
ribbon:
MaxAutoRetries: 1
#针对商品单独配置重试服务
item-service:
ribbon:
MaxAutoRetries: 0
Hystrix(Zuul默认启动了Hystrix)
容错和限流工具
- 容错 – 通过降级来容错
调用后台服务失败,执行当前模块的一段降级代码,返回降级结果
■错误提示
■缓存数据
■根据具体业务逻辑,返回任何结果皆可 - 限流 – 通过熔断来限制后台服务的流量
■流量过大时,后台服务出现大量错误,会自动触发熔断
■10秒20次请求(秒数和次数都可以设置,必须首先满足)
■50%出错,执行降级
■半开状态
断路器打开一段时间,会进行半开状态
会尝试发送一次客户端调用
调用成功,关闭断路器,恢复正常链路
调用失败,继续保持打开状态
添加降级的配置
- 添加降级类,实现FallbackProvider接口
- 添加@Component
zuul的自动配置,会从Spring容器自动发现降级类实例,完成自动配置
代码如下:
package cn.tedu.fb;
import cn.tedu.web.util.JsonResult;
import com.netflix.appinfo.RefreshableAmazonInfoProvider;
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* 当调用后台的商品服务失败,执行网关中的这个降级类,将客户端返回降级结果
* */
@Component
public class ItemFB implements FallbackProvider {
/*
* 设置当前降级类,针对哪个后台服务降级
* -item-service:只针对商品降级
* *: 对所有服务都应用当前降级类
* null:对所有服务都应用当前降级类
* */
@Override
public String getRoute() {
return "item-service";
}
//发回给客户端的降级响应数据
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.INTERNAL_SERVER_ERROR;
}
@Override
public int getRawStatusCode() throws IOException {
return HttpStatus.INTERNAL_SERVER_ERROR.value();
}
@Override
public String getStatusText() throws IOException {
return HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase();
}
@Override
public void close() {
//用来关闭下面的输入流
//ByteArrayInputStream不占用底层系统资源,不需要关闭
}
@Override
public InputStream getBody() throws IOException {
String json= JsonResult.build().code(500).msg("调用后台服务失败").toString();
return new ByteArrayInputStream(json.getBytes("UTF-8"));
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders h = new HttpHeaders();
h.add("Content-Type", "application/json;charset=UTF-8");
return null;
}
};
}
}
- 复制一下,把类名改了并把服务名也改成对应的服务
Hystrix数据监控
Hystrix利用使用SpringBoot的Actuator工具来暴露自己的监控数据
Actuator
SpringBoot提供的项目监控指标工具,提供了多种监控数据
1.健康状态
2.环境变量、配置参数
3.Spring MVC的映射路径
4.JVM虚拟机堆内存镜像
5.Spring容器中的所有对象
Actuator添加配置
1.添加Actuator依赖
2.yml配置暴露的监控数据