微服务-API网关实战小结

标签: 微服务 api 网关 | 发表时间:2021-05-02 00:36 | 作者:代码的色彩
出处:https://juejin.cn/backend

 1.为什么需要API网关

        在业务开发中,后台经常要暴露接口给不同的客户端如App端、网页端、小程序端、第三方厂商、设备端等使用,由于技术栈比较统一使用了 Spring boot web开发框架。 所以刚开始统一封装了如鉴权、限流、安全策略、日志记录等集成的工具包,开发中只需要引入该工具包即可实现上述的功能,请求端通过 Nginx后将请求到对应的微服务集群的节点上。

随着业务膨胀,暴露的接口越来越多,也暴露出来一些问题:

  • 针对不同的语言开发不同语言的工具包,如 go/c++语言实现的微服务节点;
  • 不同的请求客户端的安全策略很多,定制化现象严重,导致工具包开发复杂;
  • 当工具包 更新时,会影响很多服务器节点;
  • 并发控制效果不佳,限流降级参数调整需要.

API网关的出现就是为了解决上述的问题的,一个合格的 API网关应该具备以下特性:

  • 高可用: 网关需要是对外的唯一出入口,需持续提供稳定可靠的服务;
  • 高性能: 请求流量都会经过网关层,需要应对高并发请求场景;
  • 高安全性: 能够防止外部的恶意访问,支持数字签名、用户权限校验、黑白名单、防火墙等,确保各个微服务的安全;
  • 高扩展性: 能够提供流量管控、协议转发、日志监控等服务,同时能够为以后对非业务功能的扩展提供良好的兼容性.

这样整个技术架构就加多了 API网关层,请求端通过 Nginx后, 网关将请求流量按照具体的策略/规则转发到对应的微服务集群上。这样开发就可以把把精力集中在具体逻辑代码的开发中,而不是把时间花费在考虑接口与各种请求端交互的问题。

2.网关技术选型

        在这之前我们团队内部做了一个目前微服务网关的技术选型,对当前主流的网关功能特性如限流/鉴权/监控/易用性/可维护性/成熟度上对目前的 Spring Cloud gateway、kong、OpenRestry、Zuul2、Soul网关中间件做了些调研对比,资料内容主要来源于各个官网与技术大牛的相关实践总结。最后基于对各项特性的考量,使用了 Soul网关。

3.网关实战       

3.1 系统技术架构

        当时我们的后台系统架构已实现微服务化,业务与技术都朝着中台方向演进,各种基础设施与组件齐全,在已有的API层上加上了统一网关层,使用的是 Soul网关中间件。

3.2 Soul网关介绍

Soul地址: dromara.org/projects/so…,该网关中间件参考了 KongSpring-Cloud-Gateway等优秀的网关后设计的一个异步的,高性能的,跨语言的,响应式的API网关。该网关具备以下的功能特性:

  • 支持各种语言,无缝集成 Dubbo,SpringCloud;
  • 丰富的插件支持,鉴权,限流,熔断,防火墙等等;
  • 网关多种规则动态配置,支持各种策略配置;
  • 插件热插拔,易扩展;
  • 支持集群部署,支持 A/B Test

3.3 Soul网关的架构设计

       这是 Soul官网介绍,对网关的整个部署架构与方案有完整的说明。

可分为三大部分:

  • soul-admin: 控制管理端,  能管理应用、授权、插件、转发与负载均衡规则、限流调整、服务提供者元数据注册等。

  • soul-client: 主要提供给各个服务节点集成的SDK,并将不同的类型( Spring MVC/Dubbo/SpringCloud)服务节点自动注册到网关控制端上。

  • soul-web: 网关节点是基于 Spring-reactor模型实现,通过控制端实现请求转发、流量负载均衡等功能,为了提高性能,所有的配置信息都是通过订阅更新+本地缓存的方式实现。

4.实现原理

4.1 反应式编程

Reactive编程模型是微软为了应对高并发时提出来的一种解决方案,此后迅速发展至今,其中在 Java有比较常见 RxjavaAkka框架,响应式编程通常有以下几个特点:

  • 事件驱动: 在一个事件驱动 的应用程序中,组件之间的交互是通过松耦合的生产者和消费者来实现的。这些事件是以异步和非阻塞 的方式发送和接收的。
  • 实时响应: 系统在生产者是在有消息时才推送数据给消费者,而不是通过一种浪费资源方式: 让消费者不断地轮询或等待数据。

其中有几个概念需要解释下:

  • Reactive Streams 是一套反应式编程标准和规范;
  • Reactor 是基于 Reactive Streams 一套 反应式编程框架;
  • WebFluxReactor 为基础,实现 Web 领域的 反应式编程框架。

Sring Boot2.0支持了WebFulx响应式编程,主要有以下几个组件类:

  • Mono: 实现了 org.reactivestreams.Publisher 接口,代表 0 到 1 个元素的 发布者;
  • Flux: 实现了 org.reactivestreams.Publisher 接口,代表 0 到 N 个元素的发表者;
  • Scheduler: 驱动反应式流的调度器,通常由各种线程池实现。

        Spring框架开放了 WebHandler接口,可对服务请求进行自定义处理, SoulWebHandler类定义了 #handle方法,最后返回 Mono事件发布者对象,可以看到在 #handle方法中执行构行插件链,这里的插件链路使用 List存储,在启动时会去控制端拉取插件列表信息。

   public final class SoulWebHandler implements WebHandler {

    ...
    //根据配置或服务器CPU个数初始化调度器的线程数,可适当调高来提高并发性能和吞吐量
    public SoulWebHandler(final List plugins) {
        this.plugins = plugins;
        String schedulerType = System.getProperty("soul.scheduler.type", "fixed");
        if (Objects.equals(schedulerType, "fixed")) {
            int threads = Integer.parseInt(System.getProperty(
                    "soul.work.threads",
                    "" + Math.max((Runtime.getRuntime()
                            .availableProcessors() << 1) + 1, 16)));
            scheduler = Schedulers.newParallel("soul-work-threads", threads);
        } else {
            scheduler = Schedulers.elastic();
        }
    }

    //处理请求
    @Override
    public Mono handle(final ServerWebExchange exchange) {
        //执行插件链路,并将最后的发布结果对象挂在调度器上
        return new DefaultSoulPluginChain(plugins)
            .execute(exchange).subscribeOn(scheduler);
    }
}
复制代码

4.2 插件化设计

        责任链是 OOP开发中一种用于解耦设计的方法,具备灵活的扩展性。 Soul将整个接收前端请求、代理请求后端、接收后端响应、响应前端请求都做了插件化处理,可针对整个过程进行扩展开发。

   private static class DefaultSoulPluginChain implements SoulPluginChain {
    ...
    @Override
    public Mono execute(final ServerWebExchange exchange) {
        return Mono.defer(() -> {
            if (this.index < plugins.size()) {
                //执行插件链
                SoulPlugin plugin = plugins.get(this.index++);
                Boolean skip = plugin.skip(exchange);
                if (skip) {
                    //需要忽略执行,则跳到下一个执行容器节点
                    return this.execute(exchange);
                } else {
                    //需要执行
                    return plugin.execute(exchange, this);
                }
            } else {
                return Mono.empty();
            }
        });
    }
}

public interface SoulPlugin {
    //处理请求
    Mono execute(ServerWebExchange exchange, SoulPluginChain chain);
    //插件类型
    PluginTypeEnum pluginType();
    //插件执行顺序
    int getOrder();
    //插件名字,系统内唯一
    String named();
   //是否该忽略执行
    Boolean skip(ServerWebExchange exchange);
}
复制代码

4.3 服务元数据注册

Soul-Client基于 Spring框架开发了自动注册服务元数据的组件,服务元数据的定义如下:

   @Data
public class MetaData implements Serializable {
    //应用名,唯一
    private String appName;
    //唯一路径
    private String path;
    //远程调用类型 http/dubbo
    private String rpcType;
    //服务名
    private String serviceName;
    //方法名
    private String methodName;
    //方法参数列表
    private String parameterTypes;
    //远程调用额外信息
    private String rpcExt;
    //是否有效
    private Boolean enabled;
}
复制代码

Soul框架目前支持了 Dubbo/Spring MVC等服务提供者的元数据注册。以 Spring MVC提供 http接口为例,使用 Bean后置处理器扫描带有 @Controller注解和 @RestController注解的 bean对象,并提取出服务元数据的相关信息,使用 OkHttpClient发送请求到管理控制端,实现服务元数据注册。

   public class SoulClientBeanPostProcessor implements BeanPostProcessor {   ...
    @Override    public Object postProcessAfterInitialization(@NonNull final Object bean, @NonNull final String beanName) throws BeansException {
        //查找相关的注解参数
        Controller controller = 
                AnnotationUtils.findAnnotation(bean.getClass(), Controller.class);        RestController restController = 
                AnnotationUtils.findAnnotation(bean.getClass(), RestController.class);        RequestMapping requestMapping = 
                AnnotationUtils.findAnnotation(bean.getClass(), RequestMapping.class);

        if (controller != null || restController != null || requestMapping != null) {            String contextPath = soulHttpConfig.getContextPath();            String adminUrl = soulHttpConfig.getAdminUrl();            if (contextPath == null || "".equals(contextPath)                    || adminUrl == null || "".equals(adminUrl)) {                return bean;            }
            //获取方法集合
            final Method[] methods = ReflectionUtils.getUniqueDeclaredMethods(bean.getClass());
            //逐个组装出服务元数据并发送
            for (Method method : methods) {                SoulClient soulClient = AnnotationUtils.findAnnotation(method, SoulClient.class);                if (Objects.nonNull(soulClient)) {
                    //发送构造好的服务元数据
                    executorService.execute(() -> post(buildJsonParams(soulClient, contextPath, bean, method)));                }            }        }        return bean;    }    ...
}
复制代码

4.4 请求代理实现

         所有的网关设计都是基于代理模式实现的,把前端的请求经过网关处理然后代理转发到后端服务节点上。 前端请求格式都需要遵循如下的请求格式。

   public class RequestDTO implements Serializable {
    //模块名
    private String module;
    //方法名
    private String method;
    //远程调用类型
    private String rpcType;
    //http方法
    private String httpMethod;
    //签名内容
    private String sign;
    //请求时间戳
    private String timestamp;
    //应用key
    private String appKey;
    //http请求的路径
    private String path;
    //应用上下文路径
    private String contextPath;
    //真实的请求路径,在经过请求转发处理插件后填充
    private String realUrl;
    //服务元数据,在经过请求转发处理插件后填充
    private MetaData metaData;
    //dubbo请求参数,在经过请求转发处理插件后填充
    private String dubboParams;
    //请求开始时间戳,在经过请求转发处理插件后填充
    private LocalDateTime startDateTime;
    ...
}
复制代码

        路由插件中会到找上游节点的管理模块 UpstreamCacheManager中查找上游节点真实请求路径,并将请求的 url拼接入请求的上下文中给下一个插件节点使用。

   public class DividePlugin extends AbstractSoulPlugin {

    private final UpstreamCacheManager upstreamCacheManager;
    ...

    @Override
    protected Mono doExecute(final ServerWebExchange exchange, 
            final SoulPluginChain chain, 
            final SelectorData selector, 
            final RuleData rule) {
        ...
        final DivideRuleHandle ruleHandle = GsonUtils.getInstance().fromJson(rule.getHandle(), DivideRuleHandle.class);
        //获取可选择的上游服务器节点列表
        final List upstreamList =
                upstreamCacheManager.findUpstreamListBySelectorId(selector.getId());
        ...
        //获取真实调用节点的ip
        final String ip = Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getAddress().getHostAddress();
        DivideUpstream divideUpstream =
                LoadBalanceUtils.selector(upstreamList, ruleHandle.getLoadBalance(), ip);
        ...
        //设置一下 http url
        String domain = buildDomain(divideUpstream);
        String realURL = buildRealURL(domain, requestDTO, exchange);
        //设置下超时时间
        return chain.execute(exchange);
    }
}
复制代码

      其中 UpStreamCacheManager中主要是对上游服务器节点的管理,在节点容器中可通过选择器的id获取缓存的节点列表,节点容器可接收事件动态更新存储的服务节点地址列表。

   public class UpstreamCacheManager {

    private static final BlockingQueue BLOCKING_QUEUE 
            = new LinkedBlockingQueue<>(1024);
    //一个唯一请求服务对应多个服务节点
    private static final Map> UPSTREAM_MAP 
            = Maps.newConcurrentMap();
    ...
    public void execute(final SelectorData selectorData) {
        final List upstreamList =
                GsonUtils.getInstance().fromList(selectorData.getHandle(), DivideUpstream.class);
        if (CollectionUtils.isNotEmpty(upstreamList)) {
            UPSTREAM_MAP.put(selectorData.getId(), upstreamList);
        } else {
            UPSTREAM_MAP.remove(selectorData.getId());
        }
    }
}
复制代码

        http请求端有两种 NettyClientWebClient客户端实现,主要的请求过程在 WebClientPluginNettyHttpClientPlugin的两个容器类中实现,主要是将前端的请求信息重新填充构建一个一摸一样的请求然后再请求到后端的服务节点上,并将返回的 Mono挂到调度器上。每个服务提供出来的服务都用 MetaData标记。

   public class WebClientPlugin implements SoulPlugin {

    @Override
    public Mono execute(final ServerWebExchange exchange, final SoulPluginChain chain) {
        final RequestDTO requestDTO = exchange.getAttribute(Constants.REQUESTDTO);
        assert requestDTO != null;
        //在请求上下文中获取到url
        String urlPath = exchange.getAttribute(Constants.HTTP_URL);
        //获取请求方法
        HttpMethod method = HttpMethod.valueOf(exchange.getRequest().getMethodValue());
        //获取请求参数
        WebClient.RequestBodySpec requestBodySpec = webClient.method(method).uri(urlPath);
       ....
       //构建新的请求
        return handleRequestBody(requestBodySpec, exchange, timeout, chain, userJson);
    }
}
复制代码

        相对与 HttpClient,dubbo请求代理实现较复杂,需要做一些泛化调用的处理。泛化接口调用方式主要用于服务消费端没有 API 接口类及模型类元(比如入参和出参的 pojo类)的情况,参数及返回值中均用 Map 表示。

   public class DubboPlugin extends AbstractSoulPlugin {

    ...
    @Override
    protected Mono doExecute(final ServerWebExchange exchange, 
                final SoulPluginChain chain, 
                final SelectorData selector, final RuleData rule) {
        final String body = exchange.getAttribute(Constants.DUBBO_PARAMS);
        final RequestDTO requestDTO = exchange.getAttribute(Constants.REQUESTDTO);
        assert requestDTO != null;
        //构建泛化服务
        final Object result = dubboProxyService.genericInvoker(body, requestDTO.getMetaData());
        //执行责任链
        return chain.execute(exchange);
    }
复制代码

         整个 Dubbo请求-响应过程是通过 DubboProxyService实现的,先构造泛化的服务消费端然后在进行服务请求后返回结果。

   public class DubboProxyService {
    ...
    public Object genericInvoker(final String body, final MetaData metaData) throws SoulException {
        ReferenceConfig reference;
        GenericService genericService;
        try {
            //
            reference = ApplicationConfigCache.getInstance().get(metaData.getServiceName());
            ...
            genericService = reference.get();
        } catch (Exception ex) {
            ...
        }
        try {
            ...
            return genericService.$invoke(metaData.getMethodName(), new String[]{}, new Object[]{});
            ...
        } catch (GenericException e) {
            ...
        }
    }
}
复制代码

        Dubbo服务泛化调用使用手动构造服务消费端的方式,为了提高性能, ApplicationConfigCache将已构好的服务消费端做了缓存处理。

   public final class ApplicationConfigCache {

    //这里根据类/方法/参数确定服务接口的唯一性,因为每个Dubbo消费端
    //的构建成本较高,这里用了缓存池来缓存已构建的消费端
    private final LoadingCache> cache = CacheBuilder.newBuilder()
            ....;

    //加载Dubbo泛化代理的相关配置
    public void init(final String register) {
        if (applicationConfig == null) {
            applicationConfig = new ApplicationConfig("soul_proxy");
        }
        if (registryConfig == null) {
            registryConfig = new RegistryConfig();
            registryConfig.setProtocol("dubbo");
            registryConfig.setId("soul_proxy");
            registryConfig.setRegister(false);
            registryConfig.setAddress(register);
        }
    }

    //初始化泛化服务
    public ReferenceConfig initRef(final MetaData metaData) {
        try {
            ReferenceConfig referenceConfig = cache.get(metaData.getServiceName());
            if (StringUtils.isNoneBlank(referenceConfig.getInterface())) {
                return referenceConfig;
            }
        } catch (Exception e) {
            LOG.error("init dubbo ref ex:{}", e.getMessage());
        }
        return build(metaData);
    }

    //按照服务元数据构建Dubbo泛化服务
    public ReferenceConfig build(final MetaData metaData) {
        ReferenceConfig reference = new ReferenceConfig<>();        String rpcExt = metaData.getRpcExt();
        try {
            //从前端请求中解析元数据
            DubboParamExt dubboParamExt = GsonUtils.getInstance()
                .fromJson(rpcExt, DubboParamExt.class);
            ...
        } catch (Exception e) {
            ...
        }
        try {
            //初始化
            Object obj = reference.get();
            if (obj != null) {
                //存储缓冲池中
                cache.put(metaData.getServiceName(), reference);
            }
        } catch (Exception ex) {
            ...
        }
        return reference;
    }
    ...
}
复制代码

4.5 分布式限流实现

     框架自带了基于 redis的限流实现,使用了 Spring框架去加载 Lua脚本,然后通过 redis客户端去操作。

   public class RedisRateLimiter {

    public Mono isAllowed(final String id, 
                final double replenishRate, final double burstCapacity) {
      
        try {
            List keys = getKeys(id);
            List scriptArgs = Arrays.asList(replenishRate + "", burstCapacity + "",
                    Instant.now().getEpochSecond() + "", "1");
            Flux> resultFlux = 
                Singleton.INST.get(ReactiveRedisTemplate.class)
                    .execute(this.script, keys, scriptArgs);
            return resultFlux.onErrorResume(throwable -> 
                    Flux.just(Arrays.asList(1L, -1L)))
                    .reduce(new ArrayList(), (longs, l) -> {
                        longs.addAll(l);
                        return longs;
                    }).map(results -> {
                        boolean allowed = results.get(0) == 1L;
                        Long tokensLeft = results.get(1);
                        RateLimiterResponse rateLimiterResponse = new RateLimiterResponse(allowed, tokensLeft);
                        LogUtils.debug(LOGGER, "RateLimiter response:{}", rateLimiterResponse::toString);
                        return rateLimiterResponse;
                    });
        } catch (Exception e) {
            ...
        }
        return Mono.just(new RateLimiterResponse(true, -1));
    }
复制代码

基于时间窗口原理的 Lua脚本。

   local delta = math.max(0, now-last_refreshed)
local filled_tokens = math.min(capacity, last_tokens+(delta*rate))
local allowed = filled_tokens >= requested
local new_tokens = filled_tokens
local allowed_num = 0
if allowed then
  new_tokens = filled_tokens - requested
  allowed_num = 1
end
复制代码

4.6 分布式配置缓存

        网关节点为了高性能特定设计成本地缓存+通知事件的缓存架构,其中网关节点的本地缓存中存储了容器名<->容器信息、容器名<->选择器,选择器<->规则数据等映射关系。

   public abstract class AbstractLocalCacheManager implements LocalCacheManager {

    /**
     * pluginName -> PluginData.
     */
    static final ConcurrentMap PLUGIN_MAP = Maps.newConcurrentMap();

    /**
     * pluginName -> SelectorData.
     */
    static final ConcurrentMap> SELECTOR_MAP = Maps.newConcurrentMap();

    /**
     * selectorId -> RuleData.
     */
    static final ConcurrentMap> RULE_MAP = Maps.newConcurrentMap();

    /**
     * appKey -> AppAuthData.
     */
    static final ConcurrentMap AUTH_MAP = Maps.newConcurrentMap();
复制代码

 框架可支持 WebSocket、Zookeeper等技术进行本地缓存更改的同步。

   public class ZookeeperSyncCache extends CommonCacheHandler implements CommandLineRunner, DisposableBean {

    ...

    //监听各种节点变更事件
    @Override
    public void run(final String... args) {
        watcherData();
        watchAppAuth();
        watchMetaData();
    }

    private void watcherData() {
        final String pluginParent = ZkPathConstants.PLUGIN_PARENT;
        //如果
        if (!zkClient.exists(pluginParent)) {
            zkClient.createPersistent(pluginParent, true);
        }
        //获取插件节点的子节点
        List pluginZKs = zkClient.getChildren(ZkPathConstants.buildPluginParentPath());
        for (String pluginName : pluginZKs) {
            loadPlugin(pluginName);
        }
        //订阅zk节点事件
        zkClient.subscribeChildChanges(pluginParent, (parentPath, currentChildren) -> {
            if (CollectionUtils.isNotEmpty(currentChildren)) {
                for (String pluginName : currentChildren) {
                    loadPlugin(pluginName);
                }
            }
        });
    }
 }
复制代码

        以上就是 Soul网关实现主要技术实现原理,其中还有日志监控等高性能的实现设计,目前还是应用在边缘服务上了并逐步接入更多的服务,从生产环境的表现来说,其中有很多的设计思路简单易懂,值得认真研读。

5.总结

        本文是我在前东家在做微服务架构优化时做的一些实战总结,主要讲述了微服务API网关的作用与技术选型,当时的后台系统的技术架构还有正在使用的 Soul网关主要实现原理,

参考文献

blog.csdn.net/daniel7443/… spring reactor入门

blog.csdn.net/tianyaleixi… 网关技术选型

zhuanlan.zhihu.com/p/45351651 Spring reactor入门

相关 [微服务 api 网关] 推荐:

微服务-API网关实战小结

- - 掘金 后端
 1.为什么需要API网关.         在业务开发中,后台经常要暴露接口给不同的客户端如App端、网页端、小程序端、第三方厂商、设备端等使用,由于技术栈比较统一使用了 Spring boot web开发框架. 所以刚开始统一封装了如鉴权、限流、安全策略、日志记录等集成的工具包,开发中只需要引入该工具包即可实现上述的功能,请求端通过 Nginx后将请求到对应的微服务集群的节点上.

怎么用API网关构建微服务|架构

- - 企业架构 - ITeye博客
转于http://www.tuicool.com/articles/bMnEbmv. 当选择将应用程序构建为一组微服务时,需要确定应用程序客户端如何与微服务交互. 在单体应用程序中,只有一组(通常是重复的、负载均衡的)端点. 然而,在微服务架构中,每个微服务都会暴露一组通常是细粒度的端点. 在本文中,我们将讨论一下这对客户端与应用程序之间的通信有什么影响,并提出一种使用API网关的方法.

微服务和API网关限流熔断实现关键逻辑思路

- - DockOne.io
本文谈下微服务架构和API网关中的限流熔断,当前可以看到对于Spring Cloud框架本身也提供了Hystrix,主流的开源API网关产品类似Kong网关本身也包括了限流熔断能力. 当然也有完全较为独立的限流熔断开源实现,比如阿里的Sentinel即是我们经常会用到的限流熔断开源产品,而且可以和Dubbo,SpringCloud等各种微服务框架无缝集成.

API 网关 Kong

- - IT瘾-tuicool
所谓网关,主要作用就是连接两个不同网络的设备,而今天所讲的 API 网关是指承接和分发客户端所有请求的网关层. 最初是单体服务时,客户端发起的所有请求都可以直接请求到该服务,但随着产品用户越来越多,单体应用存在显而易见的单点问题,除此之外,当单体应用大小升至几个 G 时,持续发布将会非常缓慢,所以服务的拆分成为了必然趋势.

来自京东、宅急送对微服务编排、API网关、持续集成的实践分享(下)

- - 企业架构 - ITeye博客
架构师小组交流会:每期选一个时下最热门的技术话题进行实践经验分享. 微服务架构以其高度的弹性、灵活性和效率的巨大提升,快速受到各领域架构师和技术决策者的关注. 它的基本理念是将一个肥大的系统拆分成若干小的服务组件,组件之间的通讯采用轻量的协议完成. 我们本期小组交流会来探讨一下,现在互联网公司的微服务实践情况.

来自京东、唯品会对微服务编排、API网关、持续集成的实践分享(上)

- - 企业架构 - ITeye博客
架构师小组交流会:每期选一个时下最热门的技术话题进行实践经验分享. 微服务架构以其高度的弹性、灵活性和效率的巨大提升,快速受到各领域架构师和技术决策者的关注. 它的基本理念是将一个肥大的系统拆分成若干小的服务组件,组件之间的通讯采用轻量的协议完成. 我们本期小组交流会来探讨一下,现在互联网公司的微服务实践情况.

微服务五种开源API网关实现组件对比_Kubernetes中文社区

- -
微服务架构是当下比较流行的一种架构风格,它是一种以业务功能组织的服务集合,可以持续交付、快速部署、更好的可扩展性和容错能力,而且还使组织更容易去尝试新技术栈. 现在很多公司企业想将自己的单体应用架构迁移到微服务架构,在这个问题上,Martin Fowler提出了3个前提,而Phil Calcado对其进行了扩展如下:.

http api服务网关

- - 互联网 - ITeye博客
http服务网关系统是一个对http服务进行治理的系统,通过该系统可以对调用方进行身份认证、服务授权许可(许可调用方使用某个http服务)、服务鉴权(是否能调用http服务)、请求流控、失败恢复、调用度量数据统计分析、服务依赖视图等. 整个系统架构如下图,分为三大部分:api网关、网关控制台、度量数据采集分析(这个未在图中体现).

API网关那些儿

- - zzm
为什么需要API Gateway. 使用Zuul实现API网关. 现在越来越多的技术团队开始尝试采纳微服务架构进行产品开发. 而基于微服务架构后后端服务通常而言是动态的,为了简化前端的调用逻辑,通常会引入API Gateway作为轻量级网关,同时API Gateway中也会实现相关的认证逻辑从而简化内部服务之间相互调用的复杂度,这边文章我们就来聊聊API Gateway的那些事.

搭建微服务器:express+https+api代理 - 馒头加梨子 - 博客园

- -
service worker,但是service worker只能在https下跑,所以查资料自己用纯express搭建了一个微服务器,把过程记录下来,供以后开发时参考,相信对其他人也有用. 首先我们用express搭建一个. http服务器,很简单,看看官方文档就可以搭建出来了. 理想状况是,在项目目录下建立一个server文件夹,然后在server文件夹里面启动服务器,加载项目目录下的dist文件夹.