学习笔记
作用:api网关,路由转发,过滤器,负载均衡等多种作用
简介:类似Nginx,反向代理的功能,不过netflix自己增加了一些配合其他组件的特性。在微服务架构中,后端服务往往不直接开放给调用端,而是通过一个API网关根据请求的url,路由到相应的服务。当添加API网关后,在第三方调用端和服务提供方之间就创建了一面墙,这面墙直接与调用方通信进行权限控制,后将请求均衡分发给后台服务器。
在 Spring Cloud 微服务系统中,一种常见的负载均衡方式是,客户端的请求首先经过负载均衡(Zuul、Ngnix),再到达服务网关(Zuul 集群),然后再到具体的服务。服务统一注册到高可用的服务注册中心集群,服务的所有的配置文件由分布式配置中心管理,配置中心的配置文件放在 Git 仓库,方便开发人员随时改配置。
Zuul与Nginx区别
相同点:Zuul和Nginx都可以实现负载均衡、反向代理、过滤请求、实现网关效果。
不同点:
Zuul采用java语言编写,采用ribbon+eureka实现本地负载均衡,适合微服务中实现网关。
Nginx采用C语言编写,采用微服务端实现负载均衡,适合于服务端负载均衡,也可以实现网关。Nginx比Zuul功能会更加强大,因为Nginx整合一些脚本语言(Nginx+Lua)。
(建议nginx+zuul实现网关,nginx作用实现反向代理,zuul对微服务实现网关拦截。)
创建路由网关
pom.xml
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.8.RELEASE</version> </parent> <!-- 管理依赖 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Greenwich.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- SpringCloud eureka-server --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> </dependencies> <!-- 注意:这里必须要添加,否则各种依赖有问题 --> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/libs-milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories>
启动类增加 @EnableZuulProxy
注解开启 Zuul 功能
application.yml
spring:
application:
name: mobileshop-zuul
server:
port: 8771
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
zuul:
routes:
api-seller:
path: /api/seller/**
serviceId: mobileshop-api-seller-feign
路由说明:以 /api/seller
开头的请求都转发给 moblieshop-api-seller-feign
服务
测试访问:
配置网关路由失败时的回调
@Component public class ApiSellerFeignbackProvider implements FallbackProvider { @Override public String getRoute() { return "mobileshop-api-seller-feign"; } @Override public ClientHttpResponse fallbackResponse(String route, Throwable cause) { return new ClientHttpResponse() { /** * 网关向 api 服务请求失败了,但是消费者客户端向网关发起的请求是成功的, * 不应该把 api 的 404,500 等问题抛给客户端 * 网关和 api 服务集群对于客户端来说是黑盒 */ @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 { ObjectMapper objectMapper = new ObjectMapper(); Map<String, Object> map = new HashMap<>(); map.put("status", 200); map.put("message", "无法连接,请检查您的网络"); return new ByteArrayInputStream(objectMapper.writeValueAsString(map).getBytes("UTF-8")); } @Override public HttpHeaders getHeaders() { HttpHeaders headers = new HttpHeaders(); // 和 getBody 中的内容编码一致 headers.setContentType(MediaType.APPLICATION_JSON_UTF8); return headers; } }; } }
测试
创建服务过滤器
@Component public class LoginFilter extends ZuulFilter { private static final Logger logger = LoggerFactory.getLogger(LoginFilter.class); /** * 配置过滤类型,有四种不同生命周期的过滤器类型 * 1. pre:路由之前 * 2. routing:路由之时 * 3. post:路由之后 * 4. error:发送错误调用 */ @Override public String filterType() { return "pre"; } /** * 配置过滤的顺序 */ @Override public int filterOrder() { return 0; } /** * 配置是否需要过滤:true/需要,false/不需要 */ @Override public boolean shouldFilter() { return true; } /** * 过滤器的具体业务代码 */ @Override public Object run() throws ZuulException { RequestContext context = RequestContext.getCurrentContext(); HttpServletRequest request = context.getRequest(); String token = request.getParameter("token"); if (token == null) { context.setSendZuulResponse(false); context.setResponseStatusCode(401); try { HttpServletResponse response = context.getResponse(); response.setContentType("text/html;charset=utf-8"); context.getResponse().getWriter().write("非法请求"); } catch (IOException e) { e.printStackTrace(); } } return null; } }
测试过滤器