在学习完前面的知识后,微服务架构已经初具雏形。但还有一些问题:不同的微服务一般会有不同的网络地址,客户端在访问这些微服务时必须记住几十甚至几百个地址,这对于客户端方来说太复杂也难以维护。如下图:
如果让客户端直接与各个微服务通讯,可能会有很多问题:
1、易于监控
2、易于认证
3、减少了客户端与各个微服务之间的交互次数
API网关是一个服务器,是系统对外的唯一入口。
API网关封装了系统内部架构,为每个客户端提供一个定制的API。
API网关方式的核心要点是,所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理所有的非业务功能。
通常,网关也是提供REST/HTTP的访问API。服务端通过API-GW注册和管理服务。
网关具有的职责,如身份验证、监控、负载均衡、缓存、请求分片与管理、静态响应处理。当然,最主要的职责还是与“外界联系”。
正向代理,“它代理的是客户端,代客户端发出请求”,是一个位于客户端和原始服务器(origin server)之间的服务器,
为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。
客户端必须要进行一些特别的设置才能使用正向代理。
多个客户端给服务器发送的请求,Nginx服务器接收到之后,按照一定的规则分发给了后端的业务处理服务器进行处理了。
此时~请求的来源也就是客户端是明确的,但是请求具体由哪台服务器处理的并不明确了,Nginx扮演的就是一个反向代理角色。
客户端是无感知代理的存在的,反向代理对外都是透明的,访问者并不知道自己访问的是一个代理。因为客户端不需要任何配置就可以访问。
反向代理,“它代理的是服务端,代服务端接收请求”,主要用于服务器集群分布式部署的情况下,反向代理隐藏了服务器的信息如果只是单纯的需要一个最基础的具备转发功能的网关,那么使用Ngnix是一个不错的选择。
使用第一次的工程来练习项目
您可以直接下载并导入我的项目
源代码下载:https://download.csdn.net/download/qq_44757034/52708487
运行项目打开
访问:http://localhost:9002/order/buy/1
访问:http://localhost:9002/order/1
访问:http://localhost:9001/product/1
location /api-product{
proxy_pass http://127.0.0.1:9001/;
}
location /api-order {
proxy_pass http://127.0.0.1:9002/;
}
访问:http://localhost/api-product/product/1
http://localhost/api-order/order/1
访问:http://localhost/api-order/order/buy/1
ZUUL是Netflix开源的微服务网关,它可以和Eureka、Ribbon、Hystrix等组件配合使用,Zuul组件的核心是一系列的过滤器,这些过滤器可以完成以下功能:
Spring Cloud对Zuul进行了整合和增强
在IDEA中创建ZUUL网关工程 zuul_server ,并添加响应依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
</dependencies>
package cn.itbluebox.zuul;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@SpringBootApplication
//开启Zuul
@EnableZuulProxy
public class ZuulServerApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulServerApplication.class,args);
}
}
@EnableZuulProxy : 通过 @EnableZuulProxy 注解开启Zuul网管功能
创建配置文件 application.yml ,并添加相应配置
server:
port: 8080 #服务端口
spring:
application:
name: server-zuul #指定服务名
最直观的理解:“路由”是指根据请求URL,将请求分配到对应的处理程序。在微服务体系中,Zuul负责接收所有的请求。
根据不同的URL匹配规则,将不同的请求转发到不同的微服务处理。
zuul:
routes:
product-service: # 这里是路由id,随意写
path: /product-service/** # 这里是映射路径
url: http://127.0.0.1:9001 # 映射路径对应的实际url地址
只需要在application.yml文件中配置路由规则即可:
/product-service/
的请求,转发到http://127.0.0.1:9002
处理运行测试,运行测试前将nginx关闭掉
运行测试
访问:http://localhost:8080/product-service/product/1
微服务一般是由几十、上百个服务组成,对于一个URL请求,最终会确认一个服务实例进行处理。
如果对每个服务实例手动指定一个唯一访问地址,然后根据URL去手动实现请求匹配,这样做显然就不合理。
Zuul支持与Eureka整合开发,根据ServiceID自动的从注册中心中获取服务地址并转发请求,这样做的好处不仅可以通过单个端点来访问应用的所有服务,而且在添加或移除服务实例的时候不用修改Zuul的路由配置。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
@SpringBootApplication
//开启Zuul
@EnableZuulProxy
//eureka的服务发现
@EnableDiscoveryClient
public class ZuulServerApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulServerApplication.class,args);
}
}
server:
port: 8080 #服务端口
spring:
application:
name: server-zuul #指定服务名
zuul:
routes:
# 以商品微服务举例子
product-service: # 这里是路由id,随意写
path: /product-service/** # 这里是映射路径 localhost:8080/product-service/xxxx
#url: http://127.0.0.1:9001 # 映射路径对应的实际url地址
serviceId: service-product # 配置转发的微服务的服务名称
#配置Eureka
eureka:
client:
service-url:
defaultZone: http://localhost:9000/eureka/
instance:
prefer-ip-address: true #使用ip地址注册
访问:http://localhost:8080/product-service/product/1
在刚才的配置中,我们的规则是这样的:
zuul.routes.<route>.path=/xxx/**
: 来指定映射路径。 是自定义的路由名zuul.routes.<route>.serviceId=/product-service
:来指定服务名。而大多数情况下,我们的 路由名称往往和服务名会写成一样的。因此Zuul就提供了一种简化的配置语法: zuul.routes.<serviceId>=<path>
上面的配置可以简化为一条:
server:
port: 8080 #服务端口
spring:
application:
name: server-zuul #指定服务名
zuul:
routes:
#如果路由id和对应微服务的serviceid一致的话
service-product: /product-service/**
#配置Eureka
eureka:
client:
service-url:
defaultZone: http://localhost:9000/eureka/
instance:
prefer-ip-address: true #使用ip地址注册
运行测试
访问:http://localhost:8080/product-service/product/1
在使用Zuul的过程中,上面讲述的规则已经大大的简化了配置项。但是当服务较多时,配置也是比较繁琐的。因此Zuul就指定了默认的路由规则:
默认情况下,一切服务的映射路径就是服务名本身。
例如服务名为: service-product
,则默认的映射路径就是: /service-product/**
# zuul中默认的路由配置
# 如果当前的微服务名称 service-order,默认的请求的映射路径/service-order/**
访问:http://localhost:8080/service-order/order/1
通过之前的学习,我们得知Zuul它包含了两个核心功能:对请求的路由和过滤。
其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础;
而过滤器功能则负责对请求的处理过程进行干预,是实现请求校验、服务聚合等功能的基础。其实,路由功能在真正运行时,它的路由映射和请求转发同样也由几个不同的过滤器完成的。
所以,过滤器可以说是Zuul实现API网关功能最为核心的部件,每一个进入Zuul的HTTP请求都会经过一系列的过滤器处理链得到请求响应并返回给客户端。
那么接下来,我们重点学习的就是Zuul的第二个核心功能:过滤器。
Zuul 中的过滤器跟我们之前使用的 javax.servlet.Filter 不一样,javax.servlet.Filter 只有一种类型,可以通过配置 urlPatterns 来拦截对应的请求。而 Zuul 中的过滤器总共有 4 种类型,且每种类型都有对应的使用场景。
正常流程:
请求到达首先会经过pre类型过滤器,而后到达routing类型,进行路由,请求就到达真正的服务提供者,执行请求,返回结果后,会到达post过滤器。而后返回响应。
异常流程:
整个过程中,pre或者routing过滤器出现异常,都会直接进入error过滤器,再error处理完毕后,会将请求交给POST过滤器,最后返回给用户。
如果是error过滤器自己出现异常,最终也会进入POST过滤器,而后返回。
如果是POST过滤器出现异常,会跳转到error过滤器,但是与pre和routing不同的时,请求不会再到达POST过滤器了。
不同过滤器的场景:
请求鉴权:一般放在pre类型,如果发现没有访问权限,直接就拦截了
异常处理:一般会在error类型和post类型过滤器中结合来处理。
服务调用时长统计:pre和post结合使用。
ZuulFilter是过滤器的顶级父类。在这里我们看一下其中定义的4个最重要的方法
shouldFilter :返回一个 Boolean 值,判断该过滤器是否需要执行。返回true执行,返回false不执行。
run :过滤器的具体业务逻辑。
filterType :返回字符串,代表过滤器的类型。包含以下4种:
pre :请求在被路由之前执行
routing :在路由请求时调用
post :在routing和errror过滤器之后调用
error :处理请求时发生错误调用
filterOrder :通过返回的int值来定义过滤器的执行顺序,数字越小优先级越高。
package cn.itbluebox.zuul.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.stereotype.Component;
/* 自定义的zull过滤器 继承抽象的父类 */
@Component
public class LoginFilter extends ZuulFilter {
//定义过滤器类型 pre routing post error
@Override
public String filterType() {
return "pre";
}
//指定过滤器的执行顺序 返回值越小执行顺序越高
@Override
public int filterOrder() {
return 1;
}
//当前过滤器是否生效 true :使用此过滤器 false:不使用此过滤器
@Override
public boolean shouldFilter() {
return true;
}
//执行过滤器当中业务逻辑
@Override
public Object run() throws ZuulException {
System.out.println("执行了过滤器!!!!!");
return null;
}
}
运行测试
访问测试:http://localhost:8080/service-order/order/1
查看控制台
完善LoginFilter
package cn.itbluebox.zuul.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
/* 自定义的zull过滤器 继承抽象的父类 */
@Component
public class LoginFilter extends ZuulFilter {
//定义过滤器类型 pre routing post error
@Override
public String filterType() {
return "pre";
}
//指定过滤器的执行顺序 返回值越小执行顺序越高
@Override
public int filterOrder() {
return 1;
}
//当前过滤器是否生效 true :使用此过滤器 false:不使用此过滤器
@Override
public boolean shouldFilter() {
return true;
}
//执行过滤器当中业务逻辑
/* 身份认证: 1、所有的请求都需要携带参数:access-token 2、获取request请求 3、通过request获取参数:access-token 4、判断token是否为空 1 token == null : 身份验证失败 2 token != null : 执行后续操作 在Zuul网关当中,通过RequestContext的上下文对象,可以获取request对象 */
@Override
public Object run() throws ZuulException {
System.out.println("执行了过滤器!!!!!");
//1、获取Zuul提供上下文对象RequestContext
RequestContext ctx = RequestContext.getCurrentContext();
//2、从RequestContext当中获取request
HttpServletRequest request = ctx.getRequest();
//3、获取请求参数access
String token = request.getParameter("access-token");
//4、判断
//4.1 如果token==null ,拦截请求,返回认证失败
if(token == null){
ctx.setSendZuulResponse(false);//拦截请求
ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());//设置状态码
}
//4.2 如果token!=null ,继续后续操作
return null;
}
}
运行测试
不携带哦token直接访问:http://localhost:8080/product-service/product/1
写到token访问http://localhost:8080/product-service/product/1?access-token=1
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/qq_44757034/article/details/121610837
内容来源于网络,如有侵权,请联系作者删除!