#Dubbo框架和Websphere部署若干问题及解决办法

#Dubbo框架和Websphere部署若干问题及解决办法

前言:Rest协议(等同于dubbo协议的语义,而http、grizzly、mina、netty是技术范畴的协议)是支持http协议和netty协议的(通过RestServerFactory生成),而http协议dubbo提供了三种servlet容器实现,分别为jetty容器、tomcat容器、外部应用服务器servlet容器。其中,jetty容器和tomcat容器是dubbo内置,而外部应用服务器servlet容器则是依赖于web.xml加载启动DispatcherServlet(在实现的service方法调用传入的handler方法)并由(resteay框架的HttpServletDispatcher,实现service方法)转发到servlet容器。需要说明的是,内置的jetty容器和tomcat容器也是会转发到resteay框架的HttpServletDispatcher,最后转发到上述容器。Netty协议是通过resteay框架的NettyJaxrsServer实现容器。Resteasy框架做的是服务发布的注解解析和请求转发,是最靠近应用服务的那一层,是应用服务和web容器的中间层。

一、嵌入式servlet容器配置(tomcat)和外部应用服务器的servlet容器配置(外部servlet容器配置,如websphere)


  1. 其中port可任意指定,server支持jetty,tomcat,netty,tjws,sunhttp5种嵌入式rest server方式。如果不配置server属性,rest协议默认选用jetty。jetty是非常成熟的java servlet容器,并和dubbo已经有较好的集成(目前5种嵌入式server中只有jetty和后面所述的tomcat、tjws,与dubbo监控系统等完成了无缝的集成),所以,如果你的dubbo系统是单独启动的进程,可以直接默认采用jetty即可。
  2. 如果dubbo系统不是单独启动的进程,而是部署到了Java应用服务器中,则建议采用以下配置:

    其中port必须与servlet容器的端口相同。如项目中需将应用部署到Websphere,Websphere端口为9080,则此处port需与外部端口一致,如若不一致,则配置的端口将不会监听。contextpath为协议的上下文路径,名字需与servlet应用的上下文路径相同。
    通过将server设置为servlet,dubbo将采用外部应用服务器的servlet容器来做rest server。同时,还要在dubbo系统的web.xml中添加如下配置:

    com.alibaba.dubbo.remoting.http.servlet.BootstrapListener

    org.springframework.web.context.ContextLoaderListener


    dispatcher com.alibaba.dubbo.remoting.http.servlet.DispatcherServlet
    1


    dispatcher
    /*

即必须将dubbo的BootstrapListener和DispatherServlet添加到web.xml,以完成dubbo的REST功能与外部servlet容器的集成。
注意:如果你是用spring的ContextLoaderListener来加载spring,则必须保证BootstrapListener(dubbo用来维护serveltcontext和port的)配置在ContextLoaderListener(启动dubbo的)之前,否则dubbo初始化会出错。
其实,这种场景下你依然可以坚持用嵌入式server,但外部应用服务器的servlet容器往往比嵌入式server更加强大(特别是如果你是部署到更健壮更可伸缩的WebLogic,WebSphere等),另外有时也便于在应用服务器做统一管理、监控等等。

二、Dubbo中rest协议暴露端口的销毁解决办法(当dubbo没启用外部servlet容器的时候,一旦dubbo启用外部容器,这不存在该问题)
当dubbo服务在容器(如tomcat,wehsphere)启动时,暴露的端口号是随着容器的停止而销毁的。例如,当项目部署到Wabsphere,停止应用后,暴露的端口号始终还是处于监听状态,会导致重新启动应用后报端口已占用的错误,需将Wabsphere重启才能销毁端口。此处解决办法为,监听容器的销毁,调用dubbo服务的销毁方法,操作如下:
新建类DubboServiceListener :

import com.alibaba.dubbo.config.ProtocolConfig;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class DubboServiceListener implements ServletContextListener {

@Override
public void contextInitialized(ServletContextEvent sce) {
    System.out.println("初始化");
}

@Override
public void contextDestroyed(ServletContextEvent sce) {
    System.out.println("销毁dubbo实例中....");
    ProtocolConfig.destroyAll();
    System.out.println ("销毁dubbo服务完成!");
}

}

web.xml 增加配置:

com. **.DubboServiceListener

三、Websphere环境下遇到的java.lang.NoSuchMethodError问题及解决方法
项目在tomcat部署运行没有问题,但到Websphere部署运行后,外部PC机访问后出现如下错误:
java.lang.NoSuchMethodError: javax.ws.rs.core.HttpHeaders.getHeaderString(Ljava/lang/String;)Ljava/lang/String;
at org.jboss.resteasy.core.interception.PreMatchContainerRequestContext.getHeaderString(PreMatchContainerRequestContext.java:201)
at com.alibaba.dubbo.rpc.protocol.rest.RpcContextFilter.filter(RpcContextFilter.java:54)
at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:256)
at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:242)
……

NoSuchMethodError问题一般为jar包冲突问题。如上述错误所示,
PreMatchContainerRequestContext在调用javax.ws.rs.core.HttpHeaders的getHeaderString时,容器中存在一个jar包有javax.ws.rs.core.HttpHeaders包名类名都一样的类,但是这个类里面没有getHeaderString这个方法,且被优先加载了,实际需要加载的javax.ws.rs-api-2.0.jar里面的HttpHeaders类就没有被加载,所以报出上述错误。
出现这个错误有两种情况:
1.项目中jar包互相冲突,存在javax.ws.rs这个jar包的不同版本
2.项目中jar包和容器的jar包冲突,存在包名类名一样的类(本情况属于这一种)

首先确定是否为第一种情况。在工程依赖文件pom.xml的本地路径下,进入cmd,执行mvn dependency:tree >t.txt,打开t.txt可看到项目依赖jar包间接引用情况。查看是否存在javax.ws.rs-api.jar的不同版本。(或者自己猜测是某个包可能出现javax.ws.rs.core.HttpHeaders这个类,再检查有无getHeaderString这个方法)。若出现了冲突jar包,则通过maven的exclusions排除。本项目遇到的情况为:引入的resteasy-jaxrs.jar间接引入了jaxrs-api.jar,而jaxrs-api.jar中存在javax.ws.rs.core.HttpHeaders,也没有getHeaderString方法。于是通过maven exclusions掉,但是报错依旧,故不是这种情况引起。
由于项目为websphere(版本号8.1)部署,进lib下查看所有启动时加载jar包,反编译查看后发现j2ee.jar也存在javax.ws.rs.core.HttpHeaders,且没有getHeaderString方法。但是lib下的jar不可以随便做更改。这里可以用websphere引用共享库的方式,让websphere优先去加载javax.ws.rs-api-2.0.jar。具体操作如下:

  1. 应用服务器上新建共享库的目录(如果是集群部署,务必保证多个节点共享库的目录路径一致),将需要优先加载的jar包拷贝至该目录下(此处拷贝javax.ws.rs-api-2.0.jar)。
  2. 在websphere管理控制台中新建共享库(注意:这里如果是集群部署,作用域应选为集群):

点击“新建”按钮后,输入共享库名称,这个自己随便定义,但是类路径需要填写之前所建的共享库目录。注意需勾选“请对此共享库使用隔离的类装入器”。点击“应用”后保存。
注意:此处最好重启一下was,实际项目中没有重启was遇到若干错误,最后发现重启后得以解决。
3) 在应用中引入共享库:

进入Websphere企业应用,点击需要引用共享库的应用,选择共享库引用,勾选应用程序或者模块(此处我们勾选模块),点击引用共享库,将刚才新建的共享库引用后确定。

4)进入“类装入和更新检测”,将“类装入器顺序”改为“类已装入并且是先使用本地类装入器(父类最后)”,点击确定,保存,完成所有配置。启动应用重新访问,问题解决。

你可能感兴趣的:(云计算)