BlazeDS推送消息抛出IllegalStateException及Flex进度条实现求助
解决BlazeDS推送进度条的IllegalStateException问题并实现功能
咱们先搞定那个烦人的IllegalStateException,这个异常在BlazeDS推送场景里大多和配置、线程上下文有关,再一步步完成进度条的实现。
一、排查并解决IllegalStateException
1. 检查BlazeDS核心配置
首先确认WEB-INF/flex/services-config.xml的配置是否正确开启了消息推送:
<message-broker> <!-- 消息服务配置 --> <services> <service id="message-service" class="flex.messaging.services.MessageService"> <adapters> <adapter-definition id="actionscript" class="flex.messaging.services.messaging.adapters.ActionScriptAdapter" default="true"/> </adapters> <default-channels> <channel ref="my-polling-amf"/> </default-channels> <!-- 进度推送目的地 --> <destination id="jobProgressDestination"> <channels> <channel ref="my-polling-amf"/> </channels> <properties> <server> <allow-subtopics>true</allow-subtopics> <message-time-to-live>0</message-time-to-live> </server> </properties> </destination> </service> </services> <!-- 轮询通道配置(Tomcat6推荐用AMFPolling) --> <channels> <channel-definition id="my-polling-amf" class="mx.messaging.channels.AMFChannel"> <endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/amfpolling" class="flex.messaging.endpoints.AMFEndpoint"/> <properties> <polling-interval-seconds>1</polling-interval-seconds> <!-- 调整轮询频率,按需设置 --> </properties> </channel-definition> </channels> </message-broker>
如果之前没配置jobProgressDestination,或者通道类型不对,很容易触发异常。
2. 修复后端线程上下文问题
这个异常最常见的原因是:Java后台任务线程尝试访问请求线程绑定的HttpSession/ServletContext。BlazeDS的推送线程不属于用户请求线程,默认没有绑定这些上下文对象。
解决办法是在前端触发任务时,把客户端唯一标识(比如UUID)传递给后端,后台任务用这个标识定向推送,而不是在推送线程里获取Session:
// 你的Java服务类(对应Flex的RemoteObject) public class JobService { public void startJobN(String clientId) { // 启动后台线程执行任务,传递clientId用于推送 new Thread(new JobProgressTask(clientId)).start(); } // 后台任务线程类 private class JobProgressTask implements Runnable { private String clientId; public JobProgressTask(String clientId) { this.clientId = clientId; } @Override public void run() { try { MessageBroker broker = MessageBroker.getMessageBroker(null); AsyncMessage progressMsg = new AsyncMessage(); progressMsg.setDestination("jobProgressDestination"); progressMsg.setClientId(clientId); // 定向推送给指定客户端 // 模拟任务进度更新 for (int progress = 0; progress <= 100; progress++) { progressMsg.setBody(new HashMap<String, Object>() {{ put("progress", progress); put("jobId", "JOBN"); }}); broker.routeMessageToService(progressMsg, null); Thread.sleep(500); // 模拟任务耗时 } } catch (InterruptedException e) { e.printStackTrace(); } } } }
⚠️ 绝对不要在后台线程里调用FlexContext.getHttpRequest()或FlexContext.getHttpSession(),这些方法只能在用户请求线程中使用。
3. 确认Jar包部署完整
确保Tomcat的WEB-INF/lib下包含BlazeDS的核心Jar包:blazeds-common.jar、blazeds-core.jar、blazeds-messaging.jar,缺失Jar包也可能导致各种奇怪的异常。
二、完成Flex前端进度条功能
基于你给出的代码,完善前端的订阅、进度接收和UI更新逻辑:
1. 完善DataManager类
import mx.messaging.Consumer; import mx.messaging.events.MessageEvent; import mx.messaging.events.FaultEvent; import mx.rpc.RemoteObject; import mx.utils.UUIDUtil; public class DataManager extends EventDispatcher { public static const JOBN:String = "JOBN"; public static const PROGRESS_UPDATE:String = "PROGRESS_UPDATE"; public static const JOB_COMPLETE:String = "JOB_COMPLETE"; private var _consumer:Consumer; private var _clientId:String; private var _currentProgress:Number = 0; public function DataManager() { super(); _clientId = UUIDUtil.createUUID(); // 生成唯一客户端标识 setupProgressConsumer(); } private function setupProgressConsumer():void { _consumer = new Consumer(); _consumer.destination = "jobProgressDestination"; _consumer.clientId = _clientId; _consumer.addEventListener(MessageEvent.MESSAGE, onProgressMessage); _consumer.addEventListener(FaultEvent.FAULT, onConsumerFault); _consumer.subscribe(); // 订阅进度推送消息 } private function onProgressMessage(event:MessageEvent):void { var msgBody:Object = event.message.body; if(msgBody.jobId == JOBN) { _currentProgress = msgBody.progress; dispatchEvent(new Event(PROGRESS_UPDATE)); if(_currentProgress == 100) { dispatchEvent(new Event(JOB_COMPLETE)); } } } private function onConsumerFault(event:FaultEvent):void { trace("订阅进度推送失败: " + event.fault.message); } public function startJobN():void { // 调用Java后端服务,传递clientId var remoteService:RemoteObject = new RemoteObject("jobService"); // 对应Java的JobService类 remoteService.startJobN(_clientId); remoteService.addEventListener(FaultEvent.FAULT, onServiceFault); } private function onServiceFault(event:FaultEvent):void { trace("启动任务失败: " + event.fault.message); } public function get currentProgress():Number { return _currentProgress; } }
2. 主页面的进度条控制
import mx.controls.ProgressBar; import mx.events.MouseEvent; public class MainView extends Canvas { private var _dm:DataManager; private var _progressBar:ProgressBar; public function MainView() { super(); init(); } private function init():void { _dm = new DataManager(); _dm.addEventListener(DataManager.PROGRESS_UPDATE, onProgressUpdate); _dm.addEventListener(DataManager.JOB_COMPLETE, onJobComplete); // 创建进度条 _progressBar = new ProgressBar(); _progressBar.mode = "manual"; _progressBar.minimum = 0; _progressBar.maximum = 100; _progressBar.x = 50; _progressBar.y = 50; _progressBar.visible = false; addChild(_progressBar); } private function btnN_OnClick(event:MouseEvent):void { _progressBar.visible = true; _progressBar.setProgress(0, 100); _dm.startJobN(); } private function onProgressUpdate(event:Event):void { _progressBar.setProgress(_dm.currentProgress, 100); } private function onJobComplete(event:Event):void { _progressBar.visible = false; trace("任务JOBN执行完成!"); // 这里可以添加任务完成后的UI提示或逻辑 } }
三、额外注意事项
- Tomcat线程池配置:如果有大量用户同时使用推送功能,需要调整Tomcat的
server.xml中线程池的最大线程数,避免线程耗尽。 - 重连机制:前端可以监听
Consumer的DisconnectEvent,在断开后自动重新订阅,提升稳定性。 - 任务取消:如果需要支持用户取消任务,前端可以发送取消请求到后端,后端中断后台线程并停止推送。
内容的提问来源于stack exchange,提问作者Giacomo Chiavarini
相关产品推荐
相关产品推荐

