一个中心 :eureka
两个配置:hystrix ribbon
三个集成工具:feign zuul config
四个监控工具:tubbin ,hystrix dashboard
zuul API网关
相当于一个代理,
1 统一的调用入口
2 统一的权限校验(过滤器)
将权限的判断放在网关,后台只负责代码逻辑的编写
3 可以集成hystrix和ribbon
统一的调用入口
相当于一个代理
1 依赖
1.添加zuul依赖
2 eureka client依赖
3 common依赖
2 yml配置
spring:
application:
name: zuul
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/**
配置路由转达规则:
zuul:
routes:
item-service: /item-service/**
user-service: /user-service/**
order-service: /order-service/**
默认配置路径 根据注册表的注册信息,自动配置 一般需要手动配置,因为注册表的注册信息可能不全(有一些服务可能没有启动)
# XX服务名:/子路径 /XX *单层子路径,**深层路径
3 主程序添加注解
添加 @EnableZuulProxy 和 @EnableDiscoveryClient 注解
4 启动服务,访问测试
http://eureka1:2001
http://localhost:3001/item-service/35
http://localhost:3001/item-service/decreaseNumber
使用postman,POST发送以下格式数据:
[{“id”:1, “name”:“abc”, “number”:23},{“id”:2, “name”:“def”, “number”:11}]
http://localhost:3001/user-service/7
http://localhost:3001/user-service/7/score?score=100
http://localhost:3001/order-service/123abc
http://localhost:3001/order-service/
zuul + ribbon 负载均衡
zuul 已经集成了 ribbon,默认已经实现了负载均衡,没有重试
zuul + ribbon 重试
1 pom.xml 添加 spring-retry 依赖
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
2 配置 zuul 开启重试,并配置 ribbon 重试参数
需要开启重试,默认不开启,重试有默认参数
默认一次重试,跟换服务器一次
spring:
application:
name: zuul
server:
port: 3001
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
zuul:
retryable: true
# routes:
# item-service: /item-service/**
# user-service: /user-service/**
# order-service: /order-service/**
ribbon:
ConnectTimeout: 1000
ReadTimeout: 1000
MaxAutoRetriesNextServer: 1
MaxAutoRetries: 1
zuul + hystrix 降级
创建降级类
getRoute() 方法中指定应用此降级类的服务id,星号或null值可以通配所有服务
ItemServiceFallback
package cn.tedu.sp11.fallback;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
import cn.tedu.web.util.JsonResult;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Component
public class ItemServiceFallback implements FallbackProvider {
@Override
public String getRoute() {
//当执行item-service失败,
//应用当前这个降级类
return "item-service";
//星号和null都表示所有微服务失败都应用当前降级类
//"*"; //null;
}
//该方法返回封装降级响应的对象
//ClientHttpResponse中封装降级响应
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
return response();
}
private ClientHttpResponse response() {
return new ClientHttpResponse() {
//下面三个方法都是协议号
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}
@Override
public int getRawStatusCode() throws IOException {
return HttpStatus.OK.value();
}
@Override
public String getStatusText() throws IOException {
return HttpStatus.OK.getReasonPhrase();
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
log.info("fallback body");
String s = JsonResult.err().msg("后台服务错误").toString();
return new ByteArrayInputStream(s.getBytes("UTF-8"));
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
};
}
}
OrderServiceFallback
package cn.tedu.sp11.fallback;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
import cn.tedu.web.util.JsonResult;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Component
public class OrderServiceFallback implements FallbackProvider {
@Override
public String getRoute() {
return "order-service"; //"*"; //null;
}
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
return response();
}
private ClientHttpResponse response() {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}
@Override
public int getRawStatusCode() throws IOException {
return HttpStatus.OK.value();
}
@Override
public String getStatusText() throws IOException {
return HttpStatus.OK.getReasonPhrase();
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
log.info("fallback body");
String s = JsonResult.err().msg("后台服务错误").toString();
return new ByteArrayInputStream(s.getBytes("UTF-8"));
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
};
}
}
package cn.tedu.sp11.fallback;
import cn.tedu.web.util.JsonResult;
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;
import java.nio.charset.StandardCharsets;
@Component
public class ItemFB implements FallbackProvider {
@Override
public String getRoute() {
//返回一个serviceId 针对指定服务进行降级处理
//只针对商品降级 不对order和user进行降级
//返回“*” 或null,对所有服务都应用这个降级类
return "item-service";
}
//降级的响应,返回一个封装响应的response对象
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}
@Override
public int getRawStatusCode() throws IOException {
return HttpStatus.OK.value();
}
@Override
public String getStatusText() throws IOException {
return HttpStatus.OK.getReasonPhrase();
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
//设置协议体 JsonResult{code:401,msg:调用商品服务失败,data:null}
String json=
JsonResult.err().msg("商品调用失败").toString();
return new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8));
}
@Override
public HttpHeaders getHeaders() {
//设置协议头 Content-Type:application/json;charset=UTF-8
HttpHeaders h=new HttpHeaders();
h.add("Content-type","application/json;charset=UTF-8");
return h;
}
} ;
}
}
订单降级类
package cn.tedu.sp11.fallback;
import cn.tedu.web.util.JsonResult;
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;
import java.nio.charset.StandardCharsets;
@Component
public class OrderFB implements FallbackProvider {
@Override
public String getRoute() {
//返回一个serviceId 针对指定服务进行降级处理
//只针对商品降级 不对order和user进行降级
//返回“*” 或null,对所有服务都应用这个降级类
return "order-service";
}
//降级的响应,返回一个封装响应的response对象
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}
@Override
public int getRawStatusCode() throws IOException {
return HttpStatus.OK.value();
}
@Override
public String getStatusText() throws IOException {
return HttpStatus.OK.getReasonPhrase();
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
//设置协议体 JsonResult{code:401,msg:调用商品服务失败,data:null}
String json=
JsonResult.err().msg("订单调用失败").toString();
return new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8));
}
@Override
public HttpHeaders getHeaders() {
//设置协议头 Content-Type:application/json;charset=UTF-8
HttpHeaders h=new HttpHeaders();
h.add("Content-type","application/json;charset=UTF-8");
return h;
}
} ;
}
}
降低 hystrix 超时时间,以便测试降级
spring:
application:
name: zuul
server:
port: 3001
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
zuul:
retryable: true
ribbon:
ConnectTimeout: 1000
ReadTimeout: 2000
MaxAutoRetriesNextServer: 1
MaxAutoRetries: 1
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 500
启动服务,测试降级
http://localhost:3001/item-service/35
zuul + hystrix 数据监控
暴露 hystrix.stream 监控端点
zuul 已经包含 actuator 依赖
management:
endpoints:
web:
exposure:
include: hystrix.stream
turbbin上加暴露的服务:
启动服务,查看暴露的监控端点
http://localhost:3001/actuator
http://localhost:3001/actuator/hystrix.stream
开启监控
启动 sp08-hystrix-dashboard,填入 zuul 的监控端点路径,开启监控
http://localhost:4001/hystrix
填入监控端点:
http://localhost:3001/actuator/hystrix.stream
使用turbine进行联合数据监控:测试代码顺序
监控数据显示:
必须通过zuul网关访问后台服务才会长生监控数据
http://localhost:3001/item-service/35
http://localhost:3001/item-service/decreaseNumber
使用postman,POST发送以下格式数据:
[{“id”:1, “name”:“abc”, “number”:23},{“id”:2, “name”:“def”, “number”:11}]
http://localhost:3001/user-service/7
http://localhost:3001/user-service/7/score?score=100
http://localhost:3001/order-service/123abc
http://localhost:3001/order-service/
zuul + turbine 聚合监控
修改 turbine 项目,聚合 zuul 服务实例
spring:
application:
name: turbin
server:
port: 5001
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
turbine:
app-config: order-service, zuul
cluster-name-expression: new String("default")
使用hystrix仪表盘, 对 turbine 监控端点进行监控, 此端点聚合了订单服务和zull网关服务的监控数据
http://localhost:5001/turbine.stream
熔断测试
ab -n 20000 -c 50 http://localhost:3001/order-service/123abc
统一的权限校验
通过zuul提供的过滤器判断用户权限,后台服务不需要判断权限,值关注业务就可以
zuul提供一个过滤器,可以继承过滤器,在子类中添加权限判断
package cn.tedu.sp11.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 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
public class AccessFilter extends ZuulFilter {
//过滤器类型 pre,post,routing,error 前置,后置,路由,错误
@Override
public String filterType() {
//前置过滤器 前置过滤器中有五个默认过滤器,将自定义过滤器放在第六个
return FilterConstants.PRE_TYPE;
}
//过滤器插入的位置
@Override
public int filterOrder() {
return 6;
}
//返回一个布尔值,对用户的请求进行判断,是否执行过滤器
@Override
public boolean shouldFilter() {
/**
* 只对item-service 调用进行过滤
* 如果user-service order-service 不进行过滤代码
* */
//获取调用的服务id
RequestContext ctx=RequestContext.getCurrentContext();
String serviceId = (String)ctx.get(FilterConstants.SERVICE_ID_KEY);
return "item-service".equalsIgnoreCase(serviceId);
}
//执行过滤代码
@Override
public Object run() throws ZuulException {
//获取request对象
RequestContext ctx=RequestContext.getCurrentContext();
HttpServletRequest request=ctx.getRequest();
//获取token参数
String token=request.getParameter("token");
//如果没有token,直接访问,并直接向客户端返回响应
if(StringUtils.isBlank(token)){
//阻止继续访问
ctx.setSendZuulResponse(false);
//向客户端的响应
String json=
JsonResult.
err()
.code(JsonResult.NOT_LOGIN)
.msg("not login")
.toString();
ctx.addZuulResponseHeader("Content-Type", "application/json;charset:UTF-8");
ctx.setResponseBody(json);
}
return null;
}
}
访问测试
没有token参数不允许访问
http://localhost:3001/item-service/35
有token参数可以访问
http://localhost:3001/item-service/35?token=1234
自定义过滤器必须加载第六个,在第五个过滤器中在上下文对象中添加了serviceId,后面的过滤器才能访问serviceId
zuul和feign
尽量在zuul中加hystrix??
尽量不要在所有后台处理服务上加hystrix,会造成混乱,不知道服务究竟在哪里发生的熔断降级
应该在最前面加hystrix,比如zuul中
为什么zuul中不加重试??
会加大访问压力,每次失败都会重新连接,加上新的访问,后台所有访问压力倍增,所以重试尽量加载最后面
配置中心 Config
所有服务的配置文件都集中存放在配置中心,所有服务启动时都需要先连接配置中心进行下载
配置中心都放在git仓库,配置中心启动时先连接git仓库,clone下配置文件
yml 配置文件保存到 git 服务器,例如 github.com 或 gitee.com
微服务启动时,从服务器获取配置文件
git仓库存放所有的配置文件
2,3,4,11项目的application.yml放在git仓库
准备本地仓库
1.新建Directory:config
2. 复制2,3,4,11项目的application.yml到config目录下
—item-service-dev.yml dev profile
—user-service-dev.yml
—order-service-dev.yml
—zuul-dev.yml
3 .添加配置,防止注册中心的注册信息覆盖本地命令行配置
配置中心的配置优先级最高,会覆盖本地配置和本地命令行配置 此时8002端口配置失效
#以本地配置为主
spring:
......
cloud:
config:
override-none: true
仓库的设置:
1 将工程设置为本地仓库 springcloud1
2 将文件夹设置为本地仓管 这样就会一个仓库中有多个project springcloud1和springcloud2和springcloud3在一个仓库
新建远程仓库:
复制远程仓库地址:
在idea把本地库推送到远地
搭建配置中心
1.添加config server依赖
2 yml配置
eureka注册
配置git仓库地址,和仓库里的子目录
3.启动类注解 @Enable
创建项目
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.tedu</groupId>
<artifactId>sp12-config</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>sp12-config</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.RELEASE</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<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-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</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>
</project>
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri: https://github.com/yinxia1/springcloud1
searchPaths: config
#username: your-username
#password: your-password
server:
port: 6001
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
主程序添加 @EnableConfigServer 和 @EnableDiscoveryClient
package cn.tedu.sp12;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.config.server.EnableConfigServer;
@EnableConfigServer
@EnableDiscoveryClient
@SpringBootApplication
public class Sp12ConfigApplication {
public static void main(String[] args) {
SpringApplication.run(Sp12ConfigApplication.class, args);
}
}
启动,访问测试
访问 item-service-dev.yml 可以使用以下形式:
http://localhost:6001/item-service-dev.yml
http://localhost:6001/item-service/dev
测试其他文件
http://localhost:6001/user-service/dev
http://localhost:6001/zuul/dev
http://localhost:6001/order/dev
config 客户端
修改以下项目,从配置中心获取配置信息
sp02-itemservice
sp03-userservice
sp04-orderservice
sp11-zuul
pom.xml 添加 config 客户端依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
在四个项目中添加 bootstrap.yml
bootstrap.yml,引导配置文件,先于 application.yml 加载
item-service
spring:
cloud:
config:
discovery:
enabled: true
service-id: config-server
name: item-service
profile: dev
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
user-service
spring:
cloud:
config:
discovery:
enabled: true
service-id: config-server
name: user-service
profile: dev
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
order-service
spring:
cloud:
config:
discovery:
enabled: true
service-id: config-server
name: order-service
profile: dev
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
zuul
spring:
cloud:
config:
discovery:
enabled: true
service-id: config-server
name: zuul
profile: dev
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
启动服务,观察从配置中心获取配置信息的日志
虚拟机
systemctl stop NetworkManager
systemctl disable NetworkManager