Skip to content
This repository has been archived by the owner on Jul 16, 2024. It is now read-only.

RSocket Routing

linux_china edited this page Dec 5, 2021 · 18 revisions

目前RSocket的路由策略是基于官方标准的Routing Metadata Extension, 详细可以参考 io.rsocket.metadata.RoutingMetadata

文本型的RoutingMetadata

目前的路由设计通常要支持以下一些信息:

  • group: 服务所在的分组,如机房,或者机房内的虚拟集群等,如服务在不同的K8S集群等
  • version: 服务的版本,如1.0.0,出于A/B测试等,可能有不同的版本划分,如灰度测试测试对应"1.0.0-beta", 性能测试版本对应"1.0.0-performance",同一服务不同的集群也可以使用版本号区分,如"1.0.0-cluster1"等。
  • service name: 这个是服务的全名,如com.alibaba.user.UserService,这个主要是服务名称的定位,包括命名空间 + 服务名
  • method: 也就是要最终的处理函数,如Java Interface中的函数,gRPC中的rpc函数等
  • endpoint(接入点): 指定目标服务器,这个主要用于开发调试、A/B测试等场景,也可以做一对一的调试

目前大多数路由都是基于文本匹配的,如果Spring RSocket中使用@MessageMapping的value和路由Key的匹配。在Alibaba RSocket中,路由key的格式如下:

dc1!com.alibaba.user.UserService.findById:1.0.0

group和service之间用感叹号分隔,服务和version之间以冒号分隔,如果group和version为空,则为"com.alibaba.user.UserService.findById",这个和Spring RSocket兼容。

二进制RoutingMetadata

考虑到使用文本解析比较消耗性能,所以针对broker场景设计了一个Binary Routing,将路由文本进行hashcode化,然后基于hashcode的int值进行逻辑判断,这样就免去在broker上的RSocket协议解析,做到性能机制。 详细信息,请参考 BinaryRoutingMetadata

BinaryRoutingMetadata主要包括四个部分:

  • service id: 处理服务路由
  • handler id: 处理服务调用
  • flags: 路由打标信息,如sticky等
  • service routing text: 原始的路由文本信息,方便调试

目前hashcode的算法为MurmurHash3,各种语言都有对应的实现,请注意默认的DEFAULT_SEED为104729,确保不同语言生成的hashcode是一致的。

目前RSocket Broker同时支持两套路由策略,这样可以保证注入Spring RSocket、HTTP Gateway等都能顺利接入。

Endpoint说明

接入点是指请求方指定服务处理的目标服务器,如IP、服务ID或者服务元信息。 在构建RSocket ServiceStub的RSocketRemoteServiceBuilder中你可以指定endpoint,典型支持如下: 注意: tag的key和value使用冒号分隔,而不是等号,这个要注意一下

ipv4:192.168.1.33
routeId:xxxxxx
clusterName:1

ipv4和routeId比较容易理解。 clusterName是处理数据级别分组的问题,如同样的UserService,如果你数据库设计采用了分库分表的设计,可能就有这个clusterName信息。 所有这些信息,你可以在RSocket控制台查找到。

还有一种是基于元信息查找,如在RSocket服务端应用的application.properties文件包含RSocket metadata元信息,如下述开发者为leijuan:

rsocket.metadata.userName=leijuan

那么你将endpoint调整"userName:leijuan",就可以将请求路由到包含对应metadata的服务提供方,方便你调试服务。

关于接入点的标准名称,建议采用Spring RSocket Broker推荐的Well-known Keys: https://github.com/rsocket-broker/rsocket-broker-spec/blob/master/RSocketBrokerSpecification.md#well-known-keys

注意: 目前endpoint并不支持多个规则的处理逻辑,如多个元信息匹配,在使用的时候注意一下。

Spring RSocket标准路由

Spring RSocket使用的是RSocket的标准路由规范,这个和RSocket Broker都是兼容的,如果你要使用Spring RSocket的RSocketRequester,你可以进行如下设置:

    @Bean
    public RSocketRequester rsocketRequester(UpstreamManager upstreamManager) {
        LoadBalancedRSocket loadBalancedRSocket = upstreamManager.findBroker().getLoadBalancedRSocket();
        RSocketStrategies rSocketStrategies = RSocketStrategies.builder()
                .encoder(new HessianEncoder())
                .decoder(new HessianDecoder())
                .build();
        return RSocketRequester.wrap(loadBalancedRSocket,
                MimeType.valueOf("application/x-hessian"),
                MimeType.valueOf("message/x.rsocket.composite-metadata.v0"),
                rSocketStrategies);
    }

然后你直接使用Spring RSocket相关标准接口即可:

Mono<User> user = requester.route("com.alibaba.user.UserService.findById").data(id).retrieveMono(User.class);

粘滞会话(Sticky Session)

Sticky Session是指客户端的请求始终由某一服务提供者处理,不会做在多个服务提供者随机分发。 Sticky Session在RSocket Broker场景下,是指客户端调用的一个service的所有API请求始终由某一服务提供者处理,不会做在多个服务提供者随机分发,从而形成Service Sticky Session概念。

在一些业务场景中,这一特性比较重要,如下:

  • 分拆数据发送的场景: 客户端将数据分拆,然后以多个需求发送给服务方,但是希望是同一服务方,这样方便数据处理
  • 多次操作的一致性:如果你一个调用链,涉及到多个操作,希望这些操作都会被同一服务提供方处理,方便做一些如JVM缓存,就比较合适
  • 数据一致性的提升: 数据一致性是值同一时间显示相同的数据,举一个例子,如在分布式存储系统中,如果你在一台服务器上写入数据,但是在另外一台服务器上读取数据,可能会有一定的延迟,主要是数据同步延迟的问题。但是如果写入和读取的某一数据都在一台服务器上,这个情况就可以极大避免,这个就是sticky session发挥的作用。

Sticky session的信息保存在长连接对象上,能保证service调用一致性。 另外不同的连接会绑定到不同的服务提供方,通常不用担心负载均衡不均的问题。

注意问题

  • sticky session是基于service接口级别的,如果你要保证几个method调用形成调用链,由一个服务提供方处理,请将这些接口都放在同一个Service接口下。
  • 负载不均衡的场景: 考虑到实际的情况,如服务方重新发布或者扩容等,这个时候会发生服务绑定不均衡的情况,新上线的服务得不到sticky session绑定的机会。 需要考虑一个优雅的切换方案。

使用也比较简单,如果使用RSocketRemoteServiceBuilder构建RSocketService,可以调用sticky方法,如下:

    @Bean
    public UserService userService(UpstreamManager upstreamManager) {
        return RSocketRemoteServiceBuilder
                .client(UserService.class)
                .sticky(true)
                .upstreamManager(upstreamManager)
                //.endpoint("ip:192.168.1.2") //for testing
                .build();
    }

如果你是服务编写这,你只需在服务接口上添加 @ServiceMapping annotation, @ServiceMapping 上包含对应的sticky配置。

References

Routing Metadata Extension: https://github.com/rsocket/rsocket/blob/master/Extensions/Routing.md

RSocket

Network Protocol

  • Binary: byte stream
  • Async message
  • Multi transports
  • Reactive Semantics

Symmetric interactions

  • request/response
  • request/stream
  • fire-and-forget
  • channel

Transports

  • TCP+TLS
  • WebSocket+TLS
  • UDP(Aeron)
  • RDMA

Polyglot

Clone this wiki locally