【问题标题】:Why can an enterprise bean not listen to or accept connections on a socket?为什么企业 bean 不能侦听或接受套接字上的连接?
【发布时间】:2018-01-19 13:36:58
【问题描述】:

我想从像 Wildfly 这样的 Java EE 容器中提供一个套接字,但是在 Oracle site 上发现了这个 EJB 限制:

为什么企业 bean 不能侦听或接受套接字上的连接?

因为如果企业 bean 正在侦听套接字,它就不能被钝化——它必须始终可用。

我向 Java EE 专家提出了同样的问题,他回答“只有有状态的 bean 被钝化,所以从单例无状态启动 bean 提供套接字很好,因为它没有被钝化”。

不过,我随后查阅了 EJB 3.2 规范,发现了另一种解释:

企业 bean 不得尝试侦听套接字,接受连接 离子在套接字上,或使用套接字进行多播

EJB 架构允许企业 bean 实例成为网络套接字客户端,但不允许它成为网络服务器。允许实例成为网络服务器会与企业 bean 的基本功能相冲突——为 EJB 客户端提供服务。

呃。现在我很困惑。我真的不明白。

这个限制过时了吗? 它是从没有单例启动 bean (

类似于以下的方法(使用 Netty)甚至更简单的 Java 的 ServerSocketChannel 方法有什么问题,它在托管线程中接受客户端连接。 (不确定 Netty 是否会产生自己的线程)。

有人可以解释一下这个限制吗?

@Singleton
@Startup
public class SocketServerBean {

    @Resource
    private ManagedExecutorService managedExecutorService;

    private Channel channel;

    @PostConstruct
    private void startup() {
        EventLoopGroup bossGroup = new NioEventLoopGroup(1, managedExecutorService);
        EventLoopGroup workerGroup = new NioEventLoopGroup(1, managedExecutorService);
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            // ...
                        }
                    })
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);

            // Bind and start to accept incoming connections.
            channel = b.bind(6789).channel();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @PreDestroy
    private void shutdown() {
        channel.close();
    }
}

【问题讨论】:

  • 首先,为什么一定是EJB?我没有看到任何需要它成为 EJB 的东西。您真的了解何时使用 EJB 吗?如果只是为了@Startup,那你的理由就错了。
  • 我认为几乎所有容器管理的都是 EJB,但老实说,我不是 Java EE 专家。我想使用在托管环境中使用的连接和消息,例如使用 CDI 和 JPA 来持久化消息。我研究过 GenericServlet,但要与其他无状态 EJB 通信,它也将成为托管 bean。

标签: jakarta-ee ejb netty


【解决方案1】:

为什么企业 bean 不能侦听或接受套接字上的连接?

因为 EJB 必须充当客户端,而不是服务器。这是确认的 EJB 3.2 规范。技术原因是 EJB 本质上是可序列化的,而打开的流(套接字、文件等)当然不可序列化。

满足您的功能需求的正确方法是只使用 CDI 托管 bean,而不是 EJB。

@ApplicationScoped
public class SocketServerBean {

    public void startup(@Observes @Initialized(ApplicationScoped.class) ServletContext context) {
        // ...

    }

    @PreDestroy
    private void shutdown() {
        // ...
    }
}

@Observes @Initialized(ApplicationScoped.class) 将确保它在应用程序初始化期间被构造。

在这个 CDI 托管 bean 中,如果需要,您可以反过来只注入一个 EJB。

另见:


与具体问题无关,手动从 Java EE 应用程序打开一个套接字就很臭。你确定你不会用这种方法遇到另一个XY-problem 吗?也许您真的想使用网络套接字?如果是这样,请改用 JSR-356 @ServerEndpoint

【讨论】:

  • 非常感谢您的想法。我认为这些限制通常适用于几乎所有部署在 JavaEE 容器中的东西,除了 JCA 的东西,即我认为 CDI bean 是 EJB 的子集。问题:我想运行一个 XMPP 聊天服务器,它接受端口 5222 上的普通套接字连接,也接受 WebSocket 连接和 HTTP 长轮询连接 (BOSH)。客户可以选择他们的连接方法,我只想在后端使用抽象。想象一个管理 UI,您可以在其中过滤通过端口 5222 或 WebSockets 连接的所有用户。
  • 实际上,“正确”的方法是使用 JCA 适配器,将连接作为资源提供给 EJB 使用,而不是让 EJB 管理服务。由于上述原因。将问题转移到 CDI bean 并不能缓解问题,因为 CDI bean 是受管理的,您所做的任何非事务性操作都可能阻碍或与管理容器发生冲突。
  • 非事务性有什么问题?据我了解,事务对于数据库中的数据完整性和将多个操作“分组”为一个非常重要。它们要么全部成功,要么全部失败。我在这里想要的只是一个替代方案,比如 WebSocket 端点或 JAX-RS Web 服务。最后,我从所有三个(WS、JAX-RS、TCP 套接字)端点获得了一个消息对象,并将它们委托给某个业务层,然后可能会使用事务。
  • 事务性的意思是没有明确的边界。我打开一个套接字,然后呢? ejb/cdi 容器如何绑定该方法的执行?文件 i/o 也是如此。什么构成了文件读取的开头或结尾(x 字节数?行尾?文件结尾?随机访问呢?等等)。当具有打开套接字的 cdi bean 可以注入到 n 个线程上的 n 个注入点时,边界约束是什么?范围远不止于此,我仅提供此作为示例提及。同样,您上面所描述的正是 JCA 存在的原因。
  • 我认为 CDI bean 无论如何都是非事务性的。这就是为什么 EJB 是事务性的限制套接字的使用,但 CDI bean 没有,正如@BalusC 指出的那样。如果我的应用程序范围的 bean 被注入到其他类中,我不明白这个问题。边界是 CDI 容器的启动和停止。我也没有发现 CDI bean 中文件 IO 的限制,仅针对 EJB 出于同样的原因(事务性)。
猜你喜欢
  • 2011-12-10
  • 2021-12-16
  • 1970-01-01
  • 1970-01-01
  • 2016-07-08
  • 1970-01-01
  • 2016-11-19
  • 2017-04-27
  • 2021-09-13
相关资源
最近更新 更多