broadcast 广播
介绍
广播是通讯方式之一,广播也称推送。常用于主动给一个、多个或全服的用户发送一些数据。比如:
- 一个 : 给指定的在线玩家发送一些奖励。
 - 多个 : 给在同一个房间内的玩家广播一些数据,如某一个玩家射击子弹,把这子弹的数据广播给房间内的其他玩家。
 - 全服 : 给全服的所有在线玩家广播消息,如广播公告、即将停服维护等。
 
广播上下文提供了两种类型
- BroadcastOrderContext 严格顺序的 广播上下文(单线程的)
 - BroadcastContext 默认的广播上下文 (多线程的)
 
使用广播需要的两个参数,分别是:1. 路由地址、2.需要广播的业务数据。
public void testBroadcastOrderContext() {
    var broadcastOrderContext = BrokerClientHelper.getBroadcastOrderContext();
    for (int i = 0; i < 10; i++) {
        var data = IntValue.of(i);
        broadcastOrderContext.broadcastOrder(cmdInfo, data);
    }
}
public void testBroadcastContext() {
    var broadcastContext = BrokerClientHelper.getBroadcastContext();
    broadcastContext.broadcast(cmdInfo, data);
}
如果没有特殊业务需求,建议使用性能更好的 BroadcastContext。 因为实际的业务中,关于战斗中的广播都是会设置一个类似帧率的参数,只要不是太密集基本都没问题。 可以巧妙的利用帧率间隔来达到顺序广播。
BroadcastContext 与 BroadcastOrderContext 的使用方式上基本是一致的。
通过 FlowContext 广播
FlowContext 内置了一些广播的便捷方法。
@ActionController(1)
public class MyBroadcastAction {
    ...
    @ActionMethod(1)
    public void hello(FlowContext flowContext) {
        flowContext.broadcast(cmdInfo1, StringValue.of("hello"));
        flowContext.broadcast(cmdInfo2, StringValue.of("hello userId"), userId);
        flowContext.broadcast(cmdInfo3, StringValue.of("hello userIdList"), userIdList);
        flowContext.broadcastMe(cmdInfo4, StringValue.of("hello broadcastMe"));
    }
}
BroadcastContext 与 FlowContext 的区别
默认情况下,广播会给所有的游戏对外服发送消息,因为我们的架构是支持启动多个游戏对外服的。
FlowContext 记录了玩家的信息,能知道玩家所属的游戏对外服,这在广播时可以节约一些机器资源的占用。
使用 FlowContext.broadcastMe 进行广播时,内部会设置好这些信息。
在使用 BroadcastContext 时,你也可以手动设置游戏对外服信息。
...
private static void extracted(String externalId) {
    var bizData = new DemoBroadcastMessage();
    broadcastMessage.msg = "broadcast hello!";
    CmdInfo cmdInfo = ...;
    ResponseMessage responseMessage = BarMessageKit.createResponseMessage(cmdInfo, bizData);
    HeadMetadata headMetadata = responseMessage.getHeadMetadata();
    int sourceClientId = HashKit.hash32(externalId);
    headMetadata.setSourceClientId(sourceClientId);
    BroadcastContext broadcastContext = BrokerClientHelper.getBroadcastContext();
    broadcastContext.broadcast(responseMessage);
}
@ToString
@ProtobufClass
public class DemoBroadcastMessage {
    public String msg;
}
RangeBroadcast 范围广播便捷工具
RangeBroadcast 范围内的广播功能,这个范围指的是,可指定某些用户进行广播。
RangeBroadcast 提供了一些便捷的方法
- 添加一些需要广播的用户
 - 删除一些不需要接收广播的用户
 - 可通过重写 logic、trick 方法来做一些额外扩展
 - 提供了便捷的装箱、拆箱方法
 
创建
var rangeBroadcaster = RangeBroadcaster.of(flowContext);
// or
var brokerClient = BrokerClientHelper.getBrokerClient();
var aggregationContext = brokerClient.getCommunicationAggregationContext();
var rangeBroadcaster = RangeBroadcaster.of(aggregationContext);
下面两个方法是等价的
void test1() {
    RangeBroadcaster.of(flowContext)
        .setResponseMessage(cmdInfo, StringValue.of("hello"))
        .addUserId(1)
        .execute();
}
void test1() {
    RangeBroadcaster.of(flowContext)
        .setResponseMessage(cmdInfo, "hello")
        .addUserId(1)
        .execute();
}
关于 RangeBroadcaster 更详细的介绍请阅读 javadoc。
开启广播日志
在构建业务框架时,可以通过配置来开启广播的日志。
public class DemoBroadcastServer extends AbstractBrokerClientStartup {
    @Override
    public BarSkeleton createBarSkeleton() {
        var config = new BarSkeletonBuilderParamConfig()
            .scanActionPackage(BroadcastMessageAction.class)
            .setBroadcastLog(true);
        ...
    }
}
console
┏━━━━━ 广播. [(DemoBroadcastApplication.java:40)] ━━━ [cmd:7 - subCmd:0 - cmdMerge:458752]
┣ userId: 全服广播
┣ 广播数据: DemoBroadcastMessage(msg=broadcast hello ,253)
┣ 广播时间: 2022-05-19 21:31:47.728
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
┏━━━━━ 广播. [(DemoBroadcastApplication.java:40)] ━━━ [cmd:7 - subCmd:0 - cmdMerge:458752]
┣ userId: 1
┣ 广播数据: DemoBroadcastMessage(msg=broadcast hello ,1)
┣ 广播时间: 2022-05-19 21:32:42.732
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
┏━━━━━ 广播. [(TankAction.java:116)] ━━━ [cmd:2 - subCmd:7 - cmdMerge:131079]
┣ userId: [1,5,7]
┣ 广播数据: BarHelloPb(amount=7)
┣ 广播时间: 2022-07-14 18:31:02.253
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
注意事项
指定玩家列表广播时,玩家列表不能使用 List.of(),而需要使用 ArrayList ,否则会导致 bolt RpcClient 序列化报错无法广播。
引发异常的示例
var data = StringValue.of("biz data");
// error
List<Long> userIdList = List.of(1L, 2L, 3L);
broadcastContext.broadcast(cmdInfo, data, userIdList);
Example Source Code
see https://github.com/iohao/ioGameExamples
path : SimpleExample/example/example-broadcast
- DemoBroadcastApplication