网关,外界环境访问Cloud服务中心的服务时,都需要通过网关组件进行访问,相当于代理作用。微服务网关是微服务架构中一个不可或缺的部分。通过服务网关统一向外系统提供REST API的过程中,除了具备服务路由、均衡负载功能之外,它还具备了权限控制等功能。
在Spring cloud体系中,一般上选择zuul或者Gateway网关技术。
Spring Cloud Zuul:Zuul是Netflix开源的微服务网关
可以和Eureka、Ribbon、Hystrix等组件配合使用,
Spring Cloud对Zuul进行了整合与增强,Zuul的主要功能是路由转发和过滤器。
Spring Cloud Gateway:是由spring官方基于Spring5.0,Spring Boot2.0,Project Reactor等技术开发的网关,
提供了一个构建在Spring Ecosystem之上的API网关,旨在提供一种简单而有效的途径来发送API,
并向他们提供交叉关注点,例如:安全性,监控/指标和弹性。
目的是为了替换Spring Cloud Netfilx Zuul的。
SpringCloud Zuul网关的搭建过程如下:
- 创建一个项目,在pom.xml导入spring-boot-starter-parent、spring-cloud-denpendences、spring-cloud-starter-netflix-eureka-client、spring-cloud-starter-netflix-zuul
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<!-- spring-cloud-parent -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<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>
- 在application.properties配置文件定义eureka参数
server.port=9999
spring.application.name=ydma-zuul
eureka.client.serviceUrl.defaultZone=http://localhost:3333/eureka
- 在启动类前使用@EnableZuulProxy、@EnableDiscoveryClient等
@SpringBootApplication
@EnableDiscoveryClient
@EnableZuulProxy//启用zuul代理
public class ZuulRunBoot {
public static void main(String[] args) {
SpringApplication.run(ZuulRunBoot.class, args);
}
}
- 外界访问,通过zuul网关访问eureka注册其他的集群服务,访问格式
http://localhost:9999/服务名/请求
提示:服务名用小写
Zuul Filter
SpringCloud提供了很多内置的filter,进行请求处理。
Zuul过滤器适合做一些权限检查、身份认证等处理。
ZuulFilter的作用:
编写实现类,继承ZuulFilter,重写约定方法
@Component
public class CheckLoginFilter extends ZuulFilter{
@Autowired
private RestTemplate restTemplate;
@Override
public boolean shouldFilter() {
//获取请求URI,不需要登录的就return false;需要登录的return true
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
String uri = request.getRequestURI();
List<String> checkUris = new ArrayList<String>();
checkUris.add("/ydma-video/chapters");
System.out.println("检查"+uri+"是否需要进行拦截检查");
if(checkUris.contains(uri)) {
return true;//true调用filter
}
return false;//false不调用filter
}
@Override
public Object run() throws ZuulException {
//过滤器执行逻辑
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
HttpServletResponse response = ctx.getResponse();
String ticket = request.getParameter("ticket");
if(ticket != null&&!"".equals(ticket)) {
//这里是get请求,如果是post请求的话就比较麻烦,需要进行如下操作:
//String url = "http://YDMA-USER/user/token";
//MultiValueMap<String, String> params=
// new LinkedMultiValueMap<String, String>();
//params.add("token", token);
//YdmaResult checkResult =
// restTemplate.postForObject(url, params, YdmaResult.class);
YdmaResult checkResult = restTemplate.getForObject(
"http://YDMA-USER/user/ticket?ticket="+ticket, YdmaResult.class);
if(checkResult.getCode()==YdmaConstant.SUCCESS) {
ctx.setSendZuulResponse(true);//允许继续路由调用服务
ctx.setResponseStatusCode(200);
return null;
}
}
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "GET,POST");
response.setContentType("text/html;charset=utf-8");
ctx.setSendZuulResponse(false);//阻止继续路由调用服务
ctx.setResponseStatusCode(401);//response.setStatus(401);
ctx.setResponseBody("{\"code\":-1,\"msg\":\"用户身份不合法,未登录\"}");//out.println("{\"code\":-1,\"msg\":\"用户身份不合法,未登录\"}");
return null;
}
@Override
public String filterType() {
return "pre";//过滤器类型,pre、post、error、route
}
@Override
public int filterOrder() {
return 0;//过滤器执行顺序... -3、-2、-1、0、1、2、3
}
}
Zuul Fallback回退机制
我们在项目中使用Spring cloud zuul的时候,在zuul进行路由分发时,如果后端服务没有启动,或者调用超时,这时候我们希望Zuul提供一种hystrix服务降级处理,而不是将异常暴露出来。
Zuul集成了Hystrix,提供一个FallbackProvider回退机制当路由后面的服务发生故障时进行降级处理。
@Component
public class ServiceFallbackProvider implements FallbackProvider {
@Override
// 返回值表示需要针对此微服务做回退处理(该名称一定要是注册进入 eureka 微服务中的那个 serviceId 名称)
public String getRoute() {
return "*"; // api服务id,如果需要所有调用都支持回退,则return "*"或return null
}
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK; // 请求网关成功了,所以是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 {
String msg = "{\"code\":-1,\"msg\":\"服务调用失败\"}";
return new ByteArrayInputStream(msg.getBytes("UTF-8")); // 返回前端的内容
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8); // 设置头
return httpHeaders;
}
};
}
}