您的位置:首页 > 其它

关于EJB异步调用遇到的问题解决

2017-01-09 15:59 363 查看
近日看到ejb异步调用相关文章,突然有兴趣试下,做个示例程序都是磕磕绊绊的,所幸最后都解决了,在这里记录下遇到的问题和解决版本,希望对大家有所帮助。

异步ejb?

Asynchronous Session BeanEJB 3.1 规范定义可以采用异步的机制,将原本需要长时间执行的工作并行化。无论客户端是本地还是远程的,都可以声明 EJB 的业务方法与客户端异步执行。缺省情况下,业务方法相对于客户端是同步的。

这个新特性主要为了提高性能和可扩展性,允许客户端请求由多个线程 (thread) 处理。客户端可以调用多个异步的 EJB 方法利用多线程并发处理工作,同时客户端线程也能继续处理请求。

有两种方式的异步 EJB 方法调用:

”fire and forget”:声明异步方法返回值为 void,客户端无法知道异步方法调用何时结束。这种方式适用于执行一些“可有可无”、其是否完成无关紧要的场景。通过这种方式的异步请求,主线程上重要操作的性能因此而得到提高。

“fire and return results”:声明异步方法有个 Future 类型的返回参数。该异步方法执行前,会立即先返回一个 Future 实例给应用程序客户端,然后客户端可以用 Future 实例的 get() 方法来检查异步方法完成与否。

示例程序

这里只是为了测试ejb的远程调用,所以示例程序写的比较简单。

远程接口类 RemoteInface.java:

public interface RemoteInface {
public String sayHello(String str);
public void sayHello1(String str);
public void sayBye(String str);
public Future<String> sayBye1(String str);

}


远程接口实现类 RemoteServer.java:

@Remote
@Stateless(mappedName="Remote")
public class RemoteServer implements RemoteInface {

/* (non-Javadoc)
* <p>Title:sayHello</p>
* <p>Description:</p>
* @param str
* @return
* @see com.css.sword.ejb.RemoteInface#sayHello(java.lang.String)
*/
@Override
public String sayHello(String str) {
// TODO Auto-generated method stub
System.out.println("接收到的信息为:"+str+new Date());
return str;
}
public void sayHello1(String str) {
// TODO Auto-generated method stub
System.out.println("接收到的信息为:"+str+new Date());
}
@Asynchronous
public void sayBye(String str) {
// TODO Auto-generated method stub
System.out.println("接收到的信息为:"+str+new Date());
}

@Asynchronous
public Future<String> sayBye1(String str) {
// TODO Auto-generated method stub
System.out.println("接收到的信息为:"+str+new Date());
return new AsyncResult<String>(str);
}


可以看到,异步ejb实现很简单,只需在想异步调用的方法上加上注释 @Asynchronous即可

注: @Asynchronous这个注解是ejb3.1以上提供的,如果编译时找不到这个注解,注意ejb包的版本!!!

客户端测试程序 EjbClient.java—在weblogic11g下测试:

public class EjbClient {

public static void main(String[] args) throws InterruptedException, ExecutionException{
String connectFactory = "weblogic.jndi.WLInitialContextFactory";
String url = "t3://127.0.0.1:7001";

try {
Hashtable<Object,Object> env = new Hashtable<Object,Object>();
env.put(Context.INITIAL_CONTEXT_FACTORY, connectFactory);
env.put(Context.PROVIDER_URL,url);
Context ctx =new InitialContext(env);
RemoteInface inface = (RemoteInface) ctx.lookup("Romate.EjbServer");

long startTime = System.currentTimeMillis();
System.out.println(inface.sayHello("你好,世界"));
inface.sayHello1("你好,世界");
inface.sayBye("你好,世界"+new Date());
Future<String> futrue=inface1.sayBye1("你好,世界"+new Date());
System.out.println(futrue.get());
long endTime = System.currentTimeMillis();
System.out.println(endTime-startTime);
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}


前面几个方法调用都没有问题,但是调用inface1.sayBye1时总报错:

java.io.NotSerializableException: javax.ejb.AsyncResult如下图所示:



可以看出问题主要原因是AsyncResult没有序列化,可是这是ejb3实现类,主要是为了远程调用,序列化的问题不可能没有想到。起初我猜想是不是包的版本太低,后来下了几个包后发现代码没有变化,这时我在一篇文章中看到这么一句话:

注意:javax.ejb.AsyncResult 对象是 Future 的一个简单实现,只用于向容器传送对象,该对象不会被传到客户端。

所以我猜想,可能是容器的问题,我使用的是weblogic11g,可能该版本还没有完全兼容javaee6。也有网友给出了这样的结论

WebLogic 11g is fully Java EE 5 compliant [1], but WebLogic 11gR1 PS3 (10.3.4) included some Java EE 6 technologies [2,3]

所以我决定换个应用服务器验证下,WebLogic 12c已经完成兼容了Java EE 6,但是想到weblogic付费及一系列问题,所以我使用了更轻量级的JBoss来测试,本人第一次使用jboss,有不足之处希望请大家批评指正。

JBoss版本jboss-as-7.1.0.Final(7.0以上完全兼容了Java EE 6)。第一次使用jboss发现果然很简单,这里就不赘述那些安装配置了,在部署好ejb后,继续运行客户端,还是遇到了问题……,这里顺便记录下JBoss调用ejb的过程。

客户端添加代码用来获取上下文:

/**
* @name:getJboss7Context
* @Description: Jboss6、7通过jndi查找ejb的方法和jboss5有点不一样,这里示例的是jboss7
* @author yuanxj
* @date 2017-1-9 上午11:01:28
* @param appName   这里是.EAR包的名称,如果你打包成JAR发布的话,这里则留空
*
c305
@param moduleName 这里是你发布的JAR文件名(.jar,.war),如helloworld.jar,则这里应该为helloworld。去掉后缀即可
* @param distinctName   如果没有定义其更详细的名称,则这里留空
* @param beanClass 实现类
* @param InterfaceClass 接口类
* @return
* @throws NamingException
*/
private static  Map<String,Object> getJboss7Context(String appName,String moduleName,String distinctName,Class beanClass,Class InterfaceClass) throws NamingException{
Map<String,Object> resultMap = new HashMap<String,Object>();
final Hashtable<String, Object> jndiProperties = new Hashtable<String, Object>();
jndiProperties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
//jndiProperties.put("jboss.naming.client.ejb.context", true);

final Context context = new InitialContext(jndiProperties);
final String beanName = beanClass.getSimpleName();           //这里为实现类的名称
final String viewClassName = InterfaceClass.getName();        //这里为你的接口名称

/*
*      For stateless beans:
ejb:<app-name>/<module-name>/<distinct-name>/<bean-name>!<fully-qualified-classname-of-the-remote-interface>
For stateful beans:
ejb:<app-name>/<module-name>/<distinct-name>/<bean-name>!<fully-qualified-classname-of-the-remote-interface>?stateful
* */
String jndi = "ejb:" + appName + "/" + moduleName + "/"
+ distinctName + "/" + beanName + "!" + viewClassName;
System.out.println("jndi名为:"+jndi);
resultMap.put("context", context);
resultMap.put("jndi", jndi);
return resultMap;
}


要注意JBoss5和JBoss6、7调用方法不一样,这里只示例JBoss7使用。

客户端改下:

public static void main(String[] args) throws InterruptedException, ExecutionException{
try {
Map<String,Object> jbossMap=getJboss7Context("","myEjbTest","",RemoteServer.class,RemoteInface.class);
Context ctx1 = (Context) jbossMap.get("context");
RemoteInface inface1 = (RemoteInface) ctx1.lookup((String)jbossMap.get("jndi"));
inface1.sayHello("你好,世界");
inface1.sayHello1("你好,世界");
Future<String> futrue=inface1.sayBye1("你好,世界"+new Date());
System.out.println(futrue.get());
long endTime = System.currentTimeMillis();
System.out.println(endTime-startTime);
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}


在客户端引入包:jboss-client-7.1.0.Final.jar(在jboss安装目录/bin/client下),

创建 jboss-ejb-client.properties文件

remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false
remote.connections=default
remote.connection.default.host=localhost
remote.connection.default.port=4447
remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false


默认的remote端口是4447。

测试通过,可以通过future.get()取到值。

可能会遇到的问题:

No EJB receiver available for handling [appName:,modulename:myEjbTest,distinctname:] combination



原因1:host和端口没有写对,记得端口是远程端口

原因2:工程中有其它jndi文件,加载时覆盖了jboss-ejb-client.properties文件(可以跟断点查看context内容)

原因3:没有jboss-ejb-client.properties配置文件
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ejb 异步
相关文章推荐