098:Gateway高可用集群与动态网关
1 网关部署实现集群设计思路
课程内容:
- Gageway如何实现集群
- Gateway集群部署方案
- Gateway如何实现动态网关
- Gateway动态网关部署方案
如果网关宕机,会出现什么情况?如何解决?
导致整个微服务无法通讯。网关实现集群。
网关实现集群如何访问?
使用Nginx或者lvs虚拟vip。
2 基于Nginx部署GateWay集群环境
环境配置:
网关1:127.0.0.1:81
网关2:127.0.0.1:82
Nginx服务器:127.0.0.1:80
网关的请求头中放端口号
@Component
public class TokenGlobalFilter implements GlobalFilter {
@Value("${server.port}")
private String serverPort;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 如何获取参数
String token = exchange.getRequest().getQueryParams().getFirst("token");
if (StringUtils.isEmpty(token)) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
String msg = "token not is null ";
DataBuffer buffer = response.bufferFactory().wrap(msg.getBytes());
return response.writeWith(Mono.just(buffer));
}
// 在请求头中存放serverPort
ServerHttpRequest request = exchange.getRequest().mutate().header("serverPort", this.serverPort).build();
// 直接转发到真实服务
return chain.filter(exchange.mutate().request(request).build());
}
}
会员服务接收网关请求
@RestController
public class MemberServiceImpl implements MemberService {
@Value("${server.port}")
private String serverPort;
@Override
public String getUser(Integer userId) {
return "我是会员服务,端口号为:" + serverPort;
}
@Override
public String member(HttpServletRequest request) {
String serverPort = request.getHeader("serverPort");
return "this is member,网关端口号为:" + serverPort;
}
}
修改hosts文件(C:\Windows\System32\drivers\etc)
127.0.0.1 gw.mayikt.com
修改Nginx相关配置
http {
upstream mayiktgwadds {
server 127.0.0.1:81;
server 127.0.0.1:82;
}
server {
listen 80;
server_name gw.mayikt.com;
location / {
proxy_pass http://mayiktgwadds/;
}
}
}
测试效果:
3 部署动态GateWay的思路
动态网关:任何配置都实现不用重启网关服务器就可以及时刷新网关配置。
实现思路:
- 分布式配置中心 不建议使用,需要定义json格式配置,阅读性差
- 基于数据库表结构设计 建议
注意:配置中心实现维护性比较差,建议采用数据库形式设计。
4 基于数据库形式构建动态网关
基于数据库表形式设计
网关已经提供了api接口
1.直接新增
2.直接修改
思路:
- 默认加载时,当网关服务启动的时候,从数据库查询网关的配置,将数据库的内容读取到网关内存中。
- 网关配置更新时,更新数据库,调用网关api更新接口。
网关服务相关表
CREATE TABLE `gateway`.`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;
INSERT INTO `gateway`.`mayikt_gateway`(`id`, `route_id`, `route_name`, `route_pattern`, `route_type`, `route_url`) VALUES (1, 'member', 'mayiktmember', '/member/**', '0', 'mayikt-member');
INSERT INTO `gateway`.`mayikt_gateway`(`id`, `route_id`, `route_name`, `route_pattern`, `route_type`, `route_url`) VALUES (2, 'mayikt', 'mayikt', '/mayikt/**', '1', 'http://www.mayikt.com');
5 代码实现创建动态网关实现
maven依赖
<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>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>0.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<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>
</dependencies>
代码实现
@Data
public class GatewayEntity {
private Long id;
private String routeId;
private String routeName;
private String routePattern;
private String routeType;
private String routeUrl;
}
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);
}
@RestController
public class GatewayController {
@Autowired
private GatewayService gatewayService;
@RequestMapping("/synGatewayConfig")
public String synGatewayConfig(){
return gatewayService.loadAllLoadRoute();
}
}
@Service
public class GatewayService implements ApplicationEventPublisherAware {
private ApplicationEventPublisher publisher;
@Resource
private RouteDefinitionWriter routeDefinitionWriter;
@Resource
private MayiktGatewayMapper mayiktGatewayMapper;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}
public String loadAllLoadRoute() {
List<GatewayEntity> gateWayEntities = mayiktGatewayMapper.gateWayAll();
for (GatewayEntity gb :
gateWayEntities) {
loadRoute(gb);
}
return "success";
}
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);
URI uri = null;
if ("0".equals(gateWayEntity.getRouteType())) {
// 如果配置路由type为0的话 则从注册中心获取服务地址
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";
}
}
@SpringBootApplication
@MapperScan("com.mayikt.mapper")
public class AppGateWay {
public static void main(String[] args) {
SpringApplication.run(AppGateWay.class);
}
}
测试效果: