前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >高阶程序员必备技能:Fizz网关的二次开发

高阶程序员必备技能:Fizz网关的二次开发

原创
作者头像
openapplus
修改2020-10-30 14:14:53
1.1K0
修改2020-10-30 14:14:53
举报
文章被收录于专栏:OpenApplusOpenApplus

一、概述

在使用 fizz 过程中,可能会碰到:

  • 需要定制http serve
  • 需要额外的http client
  • 需要自定义http filte
  • 需要访问mysql、redis/codis、mongo、kafka 等

等问题,下面依次介绍解决办法,同时其它二次开发问题亦可参考。

二、定制http serve

fizz 采用 webflux 官方默认亦是最优的 http server 实现,并通过 WebFluxConfig 暴露,以方便外界进行细粒度的控制。

不建议创建多个 http server,即使它们共享同一端口。

webflux 默认基于 reactor-netty 实现 http server,可通过 NettyReactiveWebServerFactory 进行定制和扩展,包括 tcp、http、reactor-netty、netty、系统资源等层面,fizz 的 WebFluxConfig 含 NettyReactiveWebServerFactory bean,可修改或创建新的 NettyReactiveWebServerFactory bean 以定制 http server,下面是创建NettyReactiveWebServerFactory bean 的例子。

代码语言:txt
复制
    @Bean
    public NettyReactiveWebServerFactory nettyReactiveWebServerFactory(ServerProperties serverProperties) {
        NettyReactiveWebServerFactory httpServerFactory = new NettyReactiveWebServerFactory();
        httpServerFactory.setResourceFactory(null);
        LoopResources lr = LoopResources.create("fizz-el", 1, Runtime.getRuntime().availableProcessors(), true);
        httpServerFactory.addServerCustomizers(
                httpServer -> (
                        httpServer.tcpConfiguration(
                                tcpServer -> {
                                    return (
                                            tcpServe
                                                    .runOn(lr, false) // 指定运行 server 的 eventloop 资源
                                                    .port(8080)       // server 监听的端口
                                                    .bootstrap(
                                                            serverBootstrap -> (
                                                                    // 下面定制 netty 并调整 tcp 参数
                                                                    serverBootstrap
                                                                            .option(ChannelOption.ALLOCATOR, UnpooledByteBufAllocator.DEFAULT)
                                                                            .childOption(ChannelOption.ALLOCATOR, UnpooledByteBufAllocator.DEFAULT)
                                                                            .childOption(ChannelOption.SO_KEEPALIVE, true)
                                                                            .childOption(ChannelOption.TCP_NODELAY,  true)
                                                            )
                                                    )
                                    );
                                }
                        )
                )
        );
        return httpServerFactory;
    }

netty 的默认配置适用绝大多数场景,尽量少调整。

三、创建额外的 http client

对外 http 交互,可直接使用 fizz 的 FizzWebClient 或 proxyWebClient,proxyWebClient 就是一个 org.springframework.web.reactive.function.client.WebClient,

FizzWebClient 也是基于 proxyWebClient,提供了与 eureka 注册中心服务交互的便利。

尽量共享 FizzWebClient 或 proxyWebClient 进行 http 操作,不建议引入 apache httpclient、feign 等 http 客户端,即使它们是异步、响应式的,确实需要创建额外的 WebClient 时,可参考 proxyWebClientConfig 的做法,然后尽量共享新建的 WebClient,例如:

代码语言:txt
复制
    private ConnectionProvider getConnectionProvider() {
        return ConnectionProvider.builder("fizz-cp").maxConnections(2_000)
                                                    .pendingAcquireTimeout(Duration.ofMillis(6_000))
                                                    .maxIdleTime(Duration.ofMillis(40_000))
                                                    .build();
    }
    private LoopResources getLoopResources() {
        LoopResources lr = LoopResources.create("fizz-wc-el", Runtime.getRuntime().availableProcessors(), true);
        lr.onServer(false);
        return lr;
    }
    public WebClient webClient() {
        ConnectionProvider cp = getConnectionProvider(); // 客户端连接池
        LoopResources lr = getLoopResources();           // 运行客户端的 eventloop 资源
        HttpClient httpClient = HttpClient.create(cp).compress(false).tcpConfiguration(
                tcpClient -> {
                    return tcpClient.runOn(lr, false)
                                    // 定制客户端底层 netty
                                    // .bootstrap(
                                    //         bootstrap -> (
                                    //                 bootstrap.channel(NioSocketChannel.class)
                                    //         )
                                    // )
                                    .doOnConnected(
                                            connection -> {
                                                connection.addHandlerLast(new ReadTimeoutHandler( 20_000, TimeUnit.MILLISECONDS))
                                                          .addHandlerLast(new WriteTimeoutHandler(20_000, TimeUnit.MILLISECONDS));
                                            }
                                    )
                                    .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 20_000)
                                    .option(ChannelOption.TCP_NODELAY,            true)
                                    .option(ChannelOption.SO_KEEPALIVE,           true)
                                    .option(ChannelOption.ALLOCATOR,              UnpooledByteBufAllocator.DEFAULT);
                }
        );
        return WebClient.builder().exchangeStrategies(ExchangeStrategies.builder().codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(-1)).build())
                                  .clientConnector(new ReactorClientHttpConnector(httpClient)).build();
    }

WebClient 的配置和 http server 是类似的。

四、自定义 http filte

如果需要在请求处理的流水线上加入逻辑,可通过插件机制实现,具体可参考插件章节,尽量避免自定义 WebFilter,如果需要,应该继承 ProxyAggrFilter:

代码语言:txt
复制
public abstract class ProxyAggrFilter implements WebFilter {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        String serviceId = WebUtils.getServiceId(exchange); // 即请求的 path 是以 /proxy 开头
        if (serviceId == null) {
            return chain.filter(exchange);
        } else {
            return doFilter(exchange, chain);
        }
    }

    public abstract Mono<Void> doFilter(ServerWebExchange exchange, WebFilterChain chain);
}

实现 doFilter 方法即可,注意 filter 的执行顺序,需在 fizz 的 PreFilter 和 RouteFilter 之间。

五、访问 mysql、redis/codis、mongo、kafka 等

不建议在 fizz 中直接与 mysql 等传统数据库交互,因为它们没有原生的异步客户端,尽量把数据转移到分布式或本地缓存中,如 redis。

对 redis/codis、mongo、kafka 等操作,应使用 spring 官方提供的响应式客户端,注意客户端版本要与 spring boot 版本一致,

客户端使用可参官方文档,至于与 fizz 的整合,包括涉及多服务端的场景等,可参考 fizz 内置的 redis 交互逻辑设计和实现,RedisReactiveConfig 及子类AggregateRedisConfig。

比如有个 biz0 redis 库,在 fizz 中可按如下方式定义与其交互的逻辑:

在 application.yml 中加入:

代码语言:txt
复制
biz0.redis.host: biz0 的 ip
biz0.redis.port: 6379
biz0.redis.password: 123456
biz0.redis.database: 0

定义对应 yml 的配置 bean,及与其交互的 redistemplate:

代码语言:txt
复制
@Configuration
public class Biz0RedisConfig extends RedisReactiveConfig {
    
    static final String BIZ0_REACTIVE_REDIS_PROPERTIES         = "biz0ReactiveRedisProperties";
    static final String BIZ0_REACTIVE_REDIS_CONNECTION_FACTORY = "biz0ReactiveRedisConnectionFactory";
    static final String BIZ0_REACTIVE_REDIS_TEMPLATE           = "biz0ReactiveRedisTemplate";

    @ConfigurationProperties(prefix = "biz0.redis")
    @Configuration(BIZ0_REACTIVE_REDIS_PROPERTIES) // 此 bean 对应上面 yml 配置
    public static class biz0RedisReactiveProperties extends RedisReactiveProperties {
    }

    public Biz0RedisConfig(@Qualifier(BIZ0_REACTIVE_REDIS_PROPERTIES) RedisReactiveProperties properties) {
        super(properties);
    }

    @Override
    @Bean(BIZ0_REACTIVE_REDIS_CONNECTION_FACTORY)
    public ReactiveRedisConnectionFactory lettuceConnectionFactory() {
        return super.lettuceConnectionFactory();
    }

    @Override
    @Bean(BIZ0_REACTIVE_REDIS_TEMPLATE) // 与 biz0 redis 交互的 template
    public ReactiveStringRedisTemplate reactiveStringRedisTemplate(
            @Qualifier(BIZ0_REACTIVE_REDIS_CONNECTION_FACTORY) ReactiveRedisConnectionFactory factory) {
        return super.reactiveStringRedisTemplate(factory);
    }
}

RedisReactiveConfig 是 fizz 抽象的通用 redis/codis 配置,支持多服务端场景,并能在各对应客户端间共享资源:

代码语言:txt
复制
public abstract class RedisReactiveConfig {

    protected static final Logger log = LoggerFactory.getLogger(RedisReactiveConfig.class);

    // this should not be changed unless there is a truly good reason to do so
    private static final int ps = Runtime.getRuntime().availableProcessors();
    private static final ClientResources clientResources = DefaultClientResources.builder()
            .ioThreadPoolSize(ps)
            .computationThreadPoolSize(ps)
            .build();

    private RedisReactiveProperties redisReactiveProperties;

    // 子类覆盖并定义配置
    public RedisReactiveConfig(RedisReactiveProperties properties) {
        redisReactiveProperties = properties;
    }

    // 子类覆盖,创建与特定 redis/codis 交互的 template
    public ReactiveStringRedisTemplate reactiveStringRedisTemplate(ReactiveRedisConnectionFactory fact) {
        return new ReactiveStringRedisTemplate(fact);
    }

    // 子类覆盖,指定客户端连接池,通常子类不用调整此逻辑
    public ReactiveRedisConnectionFactory lettuceConnectionFactory() {

        log.info("connect to " + redisReactiveProperties);

        RedisStandaloneConfiguration rcs = new RedisStandaloneConfiguration(redisReactiveProperties.getHost(), redisReactiveProperties.getPort());
        String password = redisReactiveProperties.getPassword();
        if (password != null) {
            rcs.setPassword(password);
        }
        rcs.setDatabase(redisReactiveProperties.getDatabase());

        LettucePoolingClientConfiguration ccs = LettucePoolingClientConfiguration.builder()
                .clientResources(clientResources)
                .clientOptions(ClientOptions.builder().publishOnScheduler(true).build())
                .poolConfig(new GenericObjectPoolConfig())
                .build();

        return new LettuceConnectionFactory(rcs, ccs);
    }
}

介绍

作者:hongqiaowei

Fizz Gateway开源地址:https://github.com/wehotel/fizz-gateway-community

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、概述
  • 二、定制http serve
  • 三、创建额外的 http client
  • 四、自定义 http filte
  • 五、访问 mysql、redis/codis、mongo、kafka 等
    • 介绍
    相关产品与服务
    API 网关
    腾讯云 API 网关(API Gateway)是腾讯云推出的一种 API 托管服务,能提供 API 的完整生命周期管理,包括创建、维护、发布、运行、下线等。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档