(本文尚未完工! 本文尚未完工! )

何为线程模型?

说到线程模型,可能是指操作系统如何实现用户级线程,比如many to many,many to one,one to one;也可能是指编程语言对多线程的支持方式,或者直接提供对操作系统线程API的访问,或者在其之上提供一层抽象以屏蔽平台间的差异,或者干脆不提供并发和线程,等等。

而这里讨论的线程模型,是另外一种情况,指的是应用程序或者框架对线程的使用情况:

  • 有哪些线程
  • 如何管理这些线程
  • 线程的生命周期(何时创建何时销毁)
  • 各线程的职责

为什么要了解线程模型?

借用Netty in Action中的一段话:

How and when threads are created obviously has a significant impact on the execution of application code, so developers need to understand the trade-offs associated with different models. This is true whether they choose the model themselves or acquire it implicitly via the adoption of a language or framework.

而我之所以想去了解Netty的线程模型正是因为黑体部分,因为我现在所参与的项目不仅仅是把Netty当做一个网络框架,更把它当做一个业务容器来使用,可以说Netty的线程模型就是我们业务应用的线程模型,90%的业务逻辑都是由Netty的IO线程来驱动的(这种方式是好是坏这里不做讨论)。

还有一段有点“墨菲定律”的意思:

As we pointed out at the start of the chapter, a threading model specifies how code is going to be executed. Because we must always guard against the possible side effects of concurrent execution, it’s important to understand the implications of the model being applied (there are single-thread models as well). Ignoring these matters and merely hoping for the best is tantamount to gambling—with the odds definitely against you.

分析Netty 4 的线程模型

下面通过调试以及阅读相关源码的方式,以一个实际的Netty应用为例,来分析Netty 4的线程模型。

示例所使用的Netty版本为4.1.37.Final。因为我目前所参与的项目的原因,示例代码没有选择使用nio,而是选择了Linux专用的epoll,两者在线程模型上应该是没区别的。

从一个最简单的Netty应用开始:

package com.wallenwang.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.socket.SocketChannel;

public final class Server {
    public static void main(String[] args) throws Exception {
        EventLoopGroup bossGroup = new EpollEventLoopGroup(2);
        EventLoopGroup workerGroup = new EpollEventLoopGroup(4);
        ServerBootstrap b = new ServerBootstrap();

        b.group(bossGroup, workerGroup)
         .channel(EpollServerSocketChannel.class)
         .childHandler(new ChannelInitializer<SocketChannel>() {
             @Override
             public void initChannel(SocketChannel ch) throws Exception {
             }
         });

        // Start the server.
        ChannelFuture f = b.bind(9).sync();

        // Wait until the server socket is closed.
        f.channel().closeFuture().sync();

        // Shut down all event loops to terminate all threads.
        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();
    }
}
继续阅读

错误的理解

ChannelInboundHandlerChannelOutboundHandler,这里的inbound和outbound是什么意思呢?inbound对应IO输入,outbound对应IO输出?这是我看到这两个名字时的第一反应,但当我看到ChannelOutboundHandler接口中有read方法时,就开始疑惑了,应该是理解错了,如果outbound对应IO输出,为什么这个接口里会有明显表示IO输入的read方法呢?

正确的理解

直到看到了Stack Overflow上Netty作者Trustin Lee对inbound和outbound的解释,疑团终于解开:

众所周知,Netty是事件驱动的,而事件分为两大类:inboud和outbound,分别由ChannelInboundHandlerChannelOutboundHandler负责处理。所以,inbound和outbound并非指IO的输入和输出,而是指事件类型。

那么什么样的事件属于inbound,什么样的事件属于outbound呢?也就是说,事件类型的划分依据是什么?

答案是:触发事件的源头

继续阅读

随意记录学习netty的过程中遇到的大大小小的问题和思考。

如何理解inbound和outbound?

EventLoop的epoll实现为什么要使用timerfd?

epoll_wait的函数原型:

int epoll_wait(int epfd, struct epoll_event *events,
                      int maxevents, int timeout);

Netty为什么不使用epoll_wait自身提供的timeout参数,而是使用timerfd来控制epoll_wait的超时呢?

原因参见这次提交的comment:Unify KQueue and Epoll wait timeout approach

即:

  • timerfd(内核timer对象,纳秒级超时)可以提供比epoll_wait(毫秒级超时)更细粒度的超时控制。
  • 与EventLoop的KQueue实现保持一致。

Netty 4 线程模型

最近的工作中用到了WebSocket协议,研究了下它的数据帧格式,发现其payload length的表示方式很适合用来优化以前用到的网络库。

简单来说就是把固定4字节表示包大小的方式,改为用可变字节数表示包大小,大部分包的大小可以用1个字节表示,可表示的大小范围为0 ~ 253字节,稍微大点的包用1+2字节表示,大小范围为0 ~ 64KB,再大的包才用1+3字节表示,大小范围为0 ~ 16MB。对于游戏来说,16MB的包大小足够用了。如果不够,也是有对策的。

先来看下WebSocket的数据帧格式。

WebSocket数据帧格式

RFC 6455 给出了 WebSocket 协议的详细规范,其中第5.1节说到,在WebSocket协议中,数据是使用一系列frame来传输的,随后5.2节详细介绍了frame的格式:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len |    Extended payload length    |
|I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
|N|V|V|V|       |S|             |   (if payload len==126/127)   |
| |1|2|3|       |K|             |                               |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
|     Extended payload length continued, if payload len == 127  |
+ - - - - - - - - - - - - - - - +-------------------------------+
|                               |Masking-key, if MASK set to 1  |
+-------------------------------+-------------------------------+
| Masking-key (continued)       |          Payload Data         |
+-------------------------------- - - - - - - - - - - - - - - - +
:                     Payload Data continued ...                :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|                     Payload Data continued ...                |
+---------------------------------------------------------------+
继续阅读

本文没啥技术含量,纯粹是个人探索为啥一个简单的java程序也有这么多线程的过程记录。时间宝贵的各位看官老爷可以无视本文了。

问题

工作需要,转战Java。又熟悉又陌生,熟悉是因为大学里接触过,陌生是因为工作中没用过。

比起C/C++,Java给我的感觉是不够“直白”,因为总隔着一层JVM。慢慢来吧~

写个HelloWorld观察下在Linux里跑起来是什么样子的:

public class HelloWorld
{  
    public static void main(String[] args) throws InterruptedException
    { 
        System.out.println("Hello, World!");
        Thread.sleep(600000);
        System.out.println("Goodbye, World!");
        System.exit(0);
    }   
}
继续阅读

想起来就写一点,不定期更新。

定义术语

先定义几个不算特别专业的术语,方便后面的讨论,平时工作中都是这样叫的:

  • 主角玩家
    • 将游戏客户端中玩家自己的角色称之为主角玩家,简称主角,主角由玩家来控制。
  • 第三方玩家
    • 将游戏客户端中玩家自己的角色以外的其他角色称之为第三方玩家,简称第三方,第三方由服务器发送的同步消息来控制。
  • 服务器玩家
    • 家在服务器中对应的对象,有时根据上下文简称为服务器。
继续阅读

本文还未完成!

本文还未完成!

本文还未完成!

本文谈谈IncServer如何管理游戏场景中的对象(玩家、怪物、机关等)及其AOI实现。

为了简化讨论,本文略去了游戏对象类型和patch粒度两个维度,在文章末尾会做一个简单的补充说明。

继续阅读

写在前面

本文首先简单介绍TCMalloc及其使用方法,然后解释TCMalloc替代系统的内存分配函数的原理,再从宏观上讨论其内存分配的策略,在此之后再深入讨论实现细节。

有几点需要说明:

  • 本文只讨论gperftools中TCMalloc部分的代码,对应版本gperftools-2.7
  • 本文是根据TCMalloc源码以及简短的官方介绍作出的个人理解,难免有纰漏之处,且难以覆盖TCMalloc的方方面面,不足之处还请各位看官留言指正。
  • 除非特别说明,以下讨论均以32位系统、TCMalloc默认page大小(8KB)为基础,不同架构或不同page大小,有些相关数值可能不一样,但基本原理是相似的。
  • 为了控制篇幅,我会尽量少贴大段代码,只给出关键代码的位置或函数名,各位看官可自行参阅TCMalloc相关代码。
继续阅读