SpringCloud集成Zuul服务网关
摘要:
现有很多微服务模块,多个模块都需要做登录校验,如果每个模块都单独写一套登录检查逻辑,这样代码量高、耦合度也高,非常不利于来发,我们需要将登录状态验证的逻辑抽取出来,而Zuul就是做这个事的,它本质上是一个WebServlet,是一个服务网关,是访问所有微服务的大门,我们在项目上线的时候只会将Zuul这一个微服务暴露在外网中,而其他的服务都是部署在内网中
一:引入依赖
<dependencies>
<!--引入Eureka的客户端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--引入springboot-start-web依赖,必须引入-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Zull依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
</dependencies>
二:启动类上打上注解@EnableZuulProxy开启Zuul支持
三:配置application.yml
server:
port: 10005 #user服务端口号
eureka:
client: #Eureka客户端配置,指向注册中心地址
serviceUrl:
defaultZone:http://localhost:10001/eureka/
instance:
prefer-ip-address:true #开启使用IP地址进行注册
instance-id:ZuulServer:10005 #修改实例Id
spring:
application: #指定此服务的应用名称
name:ZuulServer
zuul:
prefix:"/apis"#统一访问前缀
ignoredServices:"*"#禁用掉使用浏览器通过服务名的方式访问服务
routes:
pay-server:"/pay/**"#指定pay-server这个服务使用/pay路径来访问-别名
order-server:"/order/**"#指定order-server这个服务使用/order路径来访问
四:发送请求的说明
在没有使用Zuul之前,我们可以通过微服务的端口加接口的地址进行访问,而现在我们需要通过zuul来访问,即:http://localhost:10005/apis/目标服务的接口地址
五:Zuul自定义前置拦截器案例
package cn.ybl.filters;
import com.alibaba.fastjson.JSONObject;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.HashMap;
/**
* 自定义Zuul拦截器
*/
@Component
public class LoginCheckFilter extends ZuulFilter {
//用来指定filter的类型的,例如返回pre那么就是前置filter
@Override
public String filterType() {
return "pre";
}
//filter的执行顺序,越小越先执行
@Override
public int filterOrder() {
return 0;
}
//父接口为IZuulFilter的方法,返回值true就执行run方法
@Override
public boolean shouldFilter() {
RequestContext currentContext = RequestContext.getCurrentContext(); //获取上下文对象
String uri = currentContext.getRequest().getRequestURI(); //得到当前请求的uri
//判断当前的uri是否包含login或者register,包含就不做校验返回false,否则就要校验
if(StringUtils.endsWithIgnoreCase(uri,"login") || StringUtils.endsWithIgnoreCase(uri,"register")){
return false;
}
return true;
}
//父接口为IZuulFilter的方法,该方法是Filter的核心业务方法
@Override
public Object run() throws ZuulException {
try {
//从请求头获取token
String token = RequestContext.getCurrentContext().getRequest().getHeader("token");
//获取响应体
HttpServletResponse response = RequestContext.getCurrentContext().getResponse();
response.setContentType("application/json;Charset=UTF-8");
if(StringUtils.isEmpty(token)) {
HashMap<Object, Object> map = new HashMap<>();
map.put("success", false);
map.put("msg", "nologin");
PrintWriter writer = response.getWriter();
//将map转为json字符串返回给浏览器,提示未登录,也可以手动拼接
writer.println(JSONObject.parse(JSONObject.toJSONString(map)));
//阻止后续拦截器执行
RequestContext.getCurrentContext().setSendZuulResponse(false);
}
} catch (Exception e) {
e.printStackTrace();
}
// 如果要放行,这里返回null即可,不用管
return null;
}
}
六:Zuul熔断器配置案例
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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;
@Component
public class PayServerFallback implements FallbackProvider {
// 得到日志对象
private final Logger logger = LoggerFactory.getLogger(FallbackProvider.class);
// 指定要处理的服务。
@Override
public String getRoute() {
return "pay-server"; //"*"代表所有服务都有作用
}
/**
* @param route :服务的路由
* @param cause : 异常
* @return ClientHttpResponse:熔断后的换回值
*/
@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 200;
}
@Override
public String getStatusText() throws IOException {
return "OK";
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
return new ByteArrayInputStream("抱歉,服务不可用".getBytes());
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
};
}
}