SpringCloudAlibaba--06——geteway
参考地址:蚂蚁课堂
* SpringCloud-GateWay
什么是微服务网关
微服务网关是整个微服务API请求的入口,可以实现日志拦截、权限控制、解决跨域问题、
限流、熔断、负载均衡、黑名单与白名单拦截、授权等。
微服务中的架构模式都是采用前后端分离,但是前端调用的接口都可以通过F12控制台给抓包分析到,有被攻击到的风险。所以要加个网关,所有的请求都必须经过网关,然后反向代理到真实的请求路径
这时候有一个问题,前端调用后端接口存在一个跨域问题,浏览器会有个自己的安全检验,这时候只需要添加一个注解@CrossOrigin 让接口允许被访问就ok了
这种做法非常冗余,如果接口非常多,几千几万个呢?所以这时候在网关上做个处理就好了。
过滤器与网关的区别
过滤器用于拦截单个服务
网关拦截整个的微服务
Zuul与Gateway有那些区别
Zuul网关属于netfix公司开源的产品属于第一代微服务网关
Gateway属于SpringCloud自研发的第二代微服务网关
相比来说SpringCloudGateway性能比Zuul性能要好:
注意:Zuul基于Servlet实现的,阻塞式的Api, 不支持长连接。
SpringCloudGateway基于Spring5构建,能够实现响应式非阻塞式的Api,支持长连接,能够更好的整合Spring体系的产品。
Gateway环境快速搭建
GateWay 是一个单独的项目,单独创建一个项目。
创建一个SpringBoot 项目,添加依赖
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.0.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> <version>2.0.0.RELEASE</version> </dependency> </dependencies>
application配置
server:
port: 80
####服务网关名称
spring:
application:
name: mayikt-gateway
cloud:
gateway:
discovery:
locator:
####开启以服务id去注册中心上获取转发地址
enabled: true
###路由策略
routes:
###路由id
- id: mayikt
####转发http://www.mayikt.com/ 要加 /
uri: http://www.mayikt.com/
###匹配规则,默认是false
predicates:
- Path=/mayikt/**
这样是不行的,要去注册中心拿到服务然后转发
要添加Nacos 依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <version>0.2.2.RELEASE</version> </dependency>
配置:
server:
port: 80
####服务网关名称
spring:
application:
name: mayikt-gateway
cloud:
gateway:
discovery:
locator:
####开启以服务id去注册中心上获取转发地址
enabled: true
###路由策略
routes:
###路由id
- id: mayikt
####转发http://www.mayikt.com/
uri: http://www.mayikt.com/
###匹配规则
predicates:
- Path=/mayikt/**
###路由id
- id: member
#### 基于lb负载均衡形式转发
uri: lb://mayikt-member
filters:
- StripPrefix=1
###匹配规则
predicates:
- Path=/member/**
nacos:
discovery:
server-addr: 127.0.0.1:8848
一定要加:
filters:
- StripPrefix=1
* 网关 和 Nginx 区别
相同点:
都是可以实现对api接口的拦截,负载均衡、反向代理、请求过滤等,可以实现和网关一样的效果。
不同点:
Nginx采用C语言编写的
微服务都是自己语言编写的 比如Gateway就是java写的
毕竟Gateway属于Java语言编写的, 能够更好对微服务实现扩展功能,相比Nginx如果想实现扩展功能需要结合Nginx+Lua语言等,相比的话成本比较高。
Nginx实现负载均衡的原理:属于服务器端负载均衡器。
Gateway实现负载均衡原理:采用本地负载均衡器的形式。
* GateWay 全局过滤
@Component public class TokenFilter implements GlobalFilter { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { String token = exchange.getRequest().getQueryParams().getFirst("token"); if (token == null || token.isEmpty()) { ServerHttpResponse response = exchange.getResponse(); response.setStatusCode(HttpStatus.BAD_REQUEST); String msg = "token not is null "; DataBuffer buffer = response.bufferFactory().wrap(msg.getBytes()); return response.writeWith(Mono.just(buffer)); } // 使用网关过滤 return chain.filter(exchange); } }
正常开发肯定不会这样判断token,会去判断这个token对不对,这只是官方的一个简单demo
* 网关部署实现集群 设计思想
思考问题:如果网关宕机了,怎么办?
所以,网关也要搭建集群!那么,这么网关肯定配置也都是一样的。客户先通过nginx访问,然后轮询到网关,那么nginx宕机呢?一样的,nginx也做集群,客户通过lvs访问到nginx就好了
所以,网关集群是基于nginx 实现的。
服务获取的是网关的请求地址:(服务获取的都是网关转发的地址,不可能是客户端请求过来的地址)
网关获取客户端的请求地址
这个是会员服务获取网关的请求地址:
然后就可以搭建网关的集群:
找到hosts文件:
然后配置nginx:
然后就可以测试集群轮询效果了。
* 动态部署GetWay
意思就是:任何配置都实现可以不用重启网关服务就能即时刷新网关配置
实现思路:2种
1、分布式配置中心(不建议使用,阅读性差)定义的json 格式
2、基于数据库表结构设计(特别建议,阅读性高)
CREATE TABLE `mayikt_gateway` ( `id` int(11) NOT NULL AUTO_INCREMENT, `route_id` varchar(11) DEFAULT NULL, `route_name` varchar(255) DEFAULT NULL, `route_pattern` varchar(255) DEFAULT NULL, `route_type` varchar(255) DEFAULT NULL, `route_url` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;
思路:默认加载:当网关服务启动时,从数据库查询网关的配置,把数据库的内容读取到网关内存中
伪代码:1、更新数据库,2、调用网关api更新
代码:
先添加依赖:
<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.1.1</version> </dependency> <!-- mysql 依赖 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- 阿里巴巴数据源 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.0.14</version> </dependency>
打开数据库:
datasource: url: jdbc:mysql://localhost:3306/meite_gateWay?useUnicode=true&characterEncoding=UTF-8 username: root password: root driver-class-name: com.mysql.jdbc.Driver
扫到数据库访问层:
业务逻辑层代码:
代码
@Service public class GatewayService implements ApplicationEventPublisherAware { private ApplicationEventPublisher publisher; @Autowired private RouteDefinitionWriter routeDefinitionWriter; @Autowired private MayiktGatewayMapper mayiktGateway; @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.publisher = applicationEventPublisher; } public void initAllRoute() { // 从数据库查询配置的网关配置 List<GateWayEntity> gateWayEntities = mayiktGateway.gateWayAll(); for (GateWayEntity gw : gateWayEntities) { loadRoute(gw); } } public String loadRoute(GateWayEntity gateWayEntity) { RouteDefinition definition = new RouteDefinition(); Map<String, String> predicateParams = new HashMap<>(8); PredicateDefinition predicate = new PredicateDefinition(); FilterDefinition filterDefinition = new FilterDefinition(); Map<String, String> filterParams = new HashMap<>(8); // 如果配置路由type为0的话 则从注册中心获取服务 URI uri = null; if (gateWayEntity.getRouteType().equals("0")) { uri = uri = UriComponentsBuilder.fromUriString("lb://" + gateWayEntity.getRouteUrl() + "/").build().toUri(); } else { uri = UriComponentsBuilder.fromHttpUrl(gateWayEntity.getRouteUrl()).build().toUri(); } // 定义的路由唯一的id definition.setId(gateWayEntity.getRouteId()); predicate.setName("Path"); //路由转发地址 predicateParams.put("pattern", gateWayEntity.getRoutePattern()); predicate.setArgs(predicateParams); // 名称是固定的, 路径去前缀 filterDefinition.setName("StripPrefix"); filterParams.put("_genkey_0", "1"); filterDefinition.setArgs(filterParams); definition.setPredicates(Arrays.asList(predicate)); definition.setFilters(Arrays.asList(filterDefinition)); definition.setUri(uri); routeDefinitionWriter.save(Mono.just(definition)).subscribe(); this.publisher.publishEvent(new RefreshRoutesEvent(this)); return "success"; } }
实体类&访问层
public interface MayiktGatewayMapper { @Select("SELECT ID AS ID, route_id as routeid, route_name as routeName,route_pattern as routePattern\n" + ",route_type as routeType,route_url as routeUrl\n" + " FROM mayikt_gateway\n") public List<GateWayEntity> gateWayAll(); @Update("update mayikt_gateway set route_url=#{routeUrl} where route_id=#{routeId};") public Integer updateGateWay(@Param("routeId") String routeId, @Param("routeUrl") String routeUrl); }
controller:
导入service 类,然后调用它的 initAllRoute 方法,返回
然后测试:
先去调用一下这个配置方法,就这个controller
显示sucess 成功以后,再去访问服务,就好了,如果直接访问服务,是会报错的,404
我们正常都是要自己写个接口,修改数据库,页面也要自己设计写一个页面。。。a
如果有想法的朋友,可以自己写个框架,来扩展GatWay,就写一个界面嘛,来动态的管理GatWay