您的位置:首页 > 编程语言 > Java开发

RMI@Spring的常见问题解决

2012-07-02 14:46 295 查看
用 ./shutdown.sh 關閉 rmi 服務器的 tomcat ,然後 ./startup.sh 啓動,客戶端連接總是會導致如下錯誤:

org.springframework.remoting.RemoteLookupFailureException: Lookup of RMI stub failed; nested exception is java.rmi.UnmarshalException: error unmarshalling return; nested exception
is:


java.io.EOFException

com.ffcs.ieie.communicate.ieiemp.IeiempException: org.springframework.remoting.RemoteLookupFailureException: Lookup of RMI st

ub failed; nested exception is java.rmi.UnmarshalException: error unmarshalling return; nested exception is:

java.io.EOFException

[b]RMI 問題 1 分析:[/b]

[b]The cause of the problem is the fact that Spring creates an RMIRegistry with the classloader of the server webapp. Then, when restarting the server, the RMIRegistry is not shut down. After the restart the Registry keeps its references to
the old Stubs which do not exist anymore.

There are two solutions:

1) Start the rmiregistry in a seperate process without the classpath of the server app.

2) (better approach) Let spring start the RMIRegistry throu RmiRegistryFactoryBean, which shuts it down correctly.
[/b]

[b]參考: http://forum.springframework.org/showthread.php?t=33073[/b]
[b][b]RMI 問題 1 解決:[/b][/b]

[b][b]將服務器中的 spring 配置代碼,如下:[/b][/b]

[b][b]< bean id = "incomingService" class = "com.ffcs.ieiemp.ieie.rmi.IncomingService" />[/b][/b]

[b][b]< bean id = "rmiService" class = "org.springframework.remoting.rmi.RmiServiceExporter" >[/b][/b]

[b][b]< property name = "serviceName" value = "IncomingService" />[/b][/b]

[b][b]< property name = "service" ref = "incomingService" />[/b][/b]

[b][b]< property name = "serviceInterface" value = "com.ffcs.ieiemp.ieie.rmi.Incoming" />[/b][/b]

[b][b]< property name = "registryPort" value = "1099" />[/b][/b]

[b][b]</ bean >[/b][/b]

[b][b]替換爲:[/b][/b]

[b][b]<bean id="registry" class="org.springframework.remoting.rmi.RmiRegistryFactoryBean">[/b][/b]

[b][b]<property name="port" value="1099"/>[/b][/b]

[b][b]</bean>[/b][/b]

[b][b]< bean id = "incomingService" class = "com.ffcs.ieiemp.ieie.rmi.IncomingService" />[/b][/b]

[b][b]< bean id = "rmiService" class = "org.springframework.remoting.rmi.RmiServiceExporter" >[/b][/b]

[b][b]< property name = "serviceName" value = "IncomingService" />[/b][/b]

[b][b]< property name = "service" ref = "incomingService" />[/b][/b]

[b][b]< property name = "serviceInterface" value = "com.ffcs.ieiemp.ieie.rmi.Incoming" />[/b][/b]

[b][b]<!-- <property name="registryPort" value="1099"/> -->[/b][/b]

[b][b]<property name="registry" ref="registry"/>[/b][/b]

[b][b]</ bean >[/b][/b]

[b][b][b]RMI 問題 2 背景:[/b][/b][/b]

[b][b][b]RMI [/b][b]服務器重啓,總是會出現客戶端連接拒絕的問題。[/b][/b][/b]

[b][b][b][b]RMI 問題 2 分析:[/b][/b][/b][/b]

[b][b][b][b]服務器重啓會影響到客戶端?說明客戶端有保存着重啓之前的服務器連接相關記錄。經研究發現,客戶端 [/b][/b][b][b]Stub 有緩存,所以只要刷新緩存即可解決問題。參考: http://forum.springsource.org/showthread.php?t=61575[/b][/b][/b][/b]
[b][b][b][b][b]RMI 問題 2 解決:[/b][/b][/b][/b][/b]

[b][b][b][/b][b][b][b]在客戶端連接代碼中增加紅色標識的代碼:[/b][/b][/b][b][/b][/b][/b]

[b][b][b][/b][b][b][b]RmiProxyFactoryBean factory= [/b][/b][/b][b][b][b][b]new RmiProxyFactoryBean();[/b][/b][/b][/b][/b][/b]

[b][b][b][/b][b][b][b]factory.setServiceInterface(Incoming. [/b][/b][/b][b][b][b][b]class );[/b][/b][/b][/b][/b][/b]

[b][b][b][/b][b][b][b]factory.setServiceUrl(url);[/b][/b][/b][b][/b][/b][/b]

[b][b][b][/b][b][b][b]// [/b][/b][/b][b][b][b][b]XXX vincan: 解決重啓 rmi 的服務器後會出現拒絕連接或找不到服務對象的錯誤[/b][/b][/b][/b][/b][/b]

[b][b][b][/b][b][b][b]factory.setLookupStubOnStartup([b]false );[/b][/b][/b][/b][b][/b][/b][/b]

[b][b][b][/b][b][b][b]factory.setRefreshStubOnConnectFailure([b]true );[/b][/b][/b][/b][b][/b][/b][/b]

[b][b][b][/b][b][b][b]factory.afterPropertiesSet();[/b][/b][/b][b][/b][/b][/b]

[b][b][b][/b][b][b][b]Incoming service=(Incoming)factory.getObject();[/b][/b][/b][/b][/b]

因爲RMI stub被連接到特定的端點,不僅僅是爲每個調用打開一個給定的目標地址的連接,所以如果重新啓動RMI端點主機的服務器,那麼就需要重新註冊這些stub,並且客戶端需要再次查詢它們。

雖然目標服務的重新註冊在重新啓動時通常會自動發生,不過此時客戶端保持的stub將會變的陳舊,且客戶端不會注意這些,除非他們再次嘗試調用stub上的方法,而這也將throw一個連接失敗的異常。

爲了避免這種情形,Spring的RmiProxyFactoryBean提供了一個refreshStubOnConnectFailure的bean屬性,如果調用失敗,並且連接異常的話,將它設定爲true來強制重新自動查詢stub。

<bean id="reportService"

class="org.springframework.remoting.rmi.RmiProxyFactoryBean">

<property name="serviceUrl">

<value>serviceurl</value>

</property>

<property name="serviceInterface">

<value>ServiceIntfc</value>

</property>

<property name="refreshStubOnConnectFailure">

<value>true</value>

</property>

</bean>

stub查詢的另一個問題是,目標RMI服務器和RMI註冊項在查詢時要爲可用的。如果客戶端在服務器啓動之前,嘗試查詢和緩存該服務stub,那麼客戶端的啓動將會失敗(即使還不需要該服務)。

爲了能夠惰性查詢服務stub,設定RmiProxyFactoryBean的lookupStubOnStarup標誌爲false。然後在第一次訪問 時查詢該stub,也就是說,當代理上的第一個方法被調用的時候去主動查詢stub,同時被緩存。這也有一個缺點,就是直到第一次調用,否則無法確認目標 服務是否實際存在。

<bean id="reportService"

class="org.springframework.remoting.rmi.RmiProxyFactoryBean">

<property name="serviceUrl">

<value>serviceurl</value>

</property>

<property name="serviceInterface">

<value>Service</value>

</property>

<property name="lookupStubOnStartup">

<value>false</value>

</property>

<property name="refreshStubOnConnectFailure">

<value>true</value>

</property>

</bean> 還有一個屬性就是cacheStub,當它設置爲false的時候,就完全避免了stub的緩存,但影響了性能。需要的時候還是可以試試

[b][b][b][b][b][b]RMI 問題 3 背景:[/b][/b][/b][/b][/b][/b]

[b][b][b][b][b][b]兩臺服務器, JDK 都是 1.6 ,在一個局域網內,內網 IP 分別爲 192.168.39.11 , 192.168.39.164 ,對應的的還有外網 IP 。寫了一個 RMI 服務器和客戶端,在本地調試沒有問題。把服務器端布署到 11 這臺服務器上後,在 164 客戶端連接卻總是拋錯:

java.rmi.ConnectException: Connection refused to host:127.0.0.1; nested exception is:

java.net.ConnectException: Connection refused: connect

at sun.rmi.transport.tcp.TCPEndpoint.newSocket(Unknown Source)

at sun.rmi.transport.tcp.TCPChannel.createConnection(Unknown Source)

at sun.rmi.transport.tcp.TCPChannel.newConnection(Unknown Source)

at sun.rmi.server.UnicastRef.invoke(Unknown Source)

at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod

(Unknown Source)

at java.rmi.server.RemoteObjectInvocationHandler.invoke(Unknown Source)

at $Proxy0.call(Unknown Source)

at RMI.Client.callRMI(Client.java:39)

at RMI.Client.main(Client.java:53)

Caused by: java.net.ConnectException: Connection refused: connect

at java.net.PlainSocketImpl.socketConnect(Native Method)

at java.net.PlainSocketImpl.doConnect(Unknown Source)

at java.net.PlainSocketImpl.connectToAddress(Unknown Source)

at java.net.PlainSocketImpl.connect(Unknown Source)

at java.net.SocksSocketImpl.connect(Unknown Source)

at java.net.Socket.connect(Unknown Source)

at java.net.Socket.connect(Unknown Source)

at java.net.Socket.<init>(Unknown Source)

at java.net.Socket.<init>(Unknown Source)

at sun.rmi.transport.proxy.RMIDirectSocketFactory.createSocket

(Unknown Source)

at sun.rmi.transport.proxy.RMIMasterSocketFactory.createSocket

(Unknown Source)

... 9 more

也就是 IP 被指向 127.0.0.1 了,而客戶端發起連接的時候 IP 絕對是寫的 IP 是 192.168.39.11 。
[/b][/b][/b][/b][/b][/b]

[b][b][b][b][b][b][b]RMI 問題 3 分析:[/b][/b][/b][/b][/b][/b][/b]

[b][b][b][b][b][b][b]這就是典型的服務器有多個 [/b][/b][/b][/b][/b][b][b][b][b][b]ip 引起的 rmi 連接問題。[/b][/b][/b][/b][/b][/b][/b]

[b][b][b][b][b][b][b][b]RMI 問題 3 解決:[/b][/b][/b][/b][/b][/b][/b][/b]

[b][b][b][b][b][b][b][b]解決方法有三:[/b][/b][/b][/b][/b][/b][/b][/b]

[b][b][b][b][b][b][b][b]1. [/b][/b][/b][/b][/b][/b][b][b][b][b][b][b]服務器端添加代碼: [/b][/b][/b][/b][/b][/b][b][b][b][b][b][b]System.setProperty("java.rmi.server.hostname" , "192.168.39.11" );[/b][/b][/b][/b][/b][/b][/b][/b]

[b][b][b][b][b][b][b][b]可寫在全局監聽器裏成爲全局變量。[/b][/b][/b][/b][/b][/b][b][b][b][b][/b][/b][/b][/b][/b][/b]

[b][b][b][b][b][b][b][b]2. [/b][/b][/b][/b][/b][/b][b][b][b][b][b][b]在 [/b][/b][/b][/b][/b][/b][b][b][b][b][b][b]RMI
服務器上 root 身份登錄,輸入 Vi /etc/hosts ,在第一行添加 192.168.39.11 ieie
[/b][/b][/b][/b][/b][/b][/b][/b]

[b][b][b][b][b][b][b][b]3. [/b][/b][/b][/b][/b][/b][b][b][b][b][b][b]若是用 [/b][/b][/b][/b][/b][/b][b][b][b][b][b][b]spring,
則在 RmiServiceExporter 中添加屬性 <property name="registryHost" value="192.168.39.11" />
[/b][/b][/b][/b][/b][/b][/b][/b]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: