2012-12-10 15 views
10

एप्लिकेशन को फिर से लॉन्च करने का सबसे अच्छा तरीका क्या है जहां यह एक टीसीपी पोर्ट सुन रहा था? समस्या यह है: यदि मैं एप्लिकेशन को तुरंत लॉन्च करता हूं तो इसे विफल करता है क्योंकि सॉकेट जो सुन रहा था वह पहले से ही उपयोग में है।पायथन - एप्लिकेशन को फ्लाई पर एप्लिकेशन को फिर से लॉन्च करने के लिए कैसे करें जबकि एप्लिकेशन को सुनने मोड में एक टीसीपी पोर्ट है?

ऐसे मामले में सुरक्षित रूप से फिर से लॉन्च कैसे करें?

socket.error: [Errno 98] Address already in use 

कोड:

#!/usr/bin/python 
import sys,os 
import pygtk, gtk, gobject 
import socket, datetime, threading 
import ConfigParser 
import urllib2 
import subprocess 

def server(host, port): 
    sock = socket.socket() 
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
    sock.bind((host, port)) 
    sock.listen(1) 
    print "Listening... " 
    gobject.io_add_watch(sock, gobject.IO_IN, listener) 


def listener(sock, *args): 
    conn, addr = sock.accept() 
    print "Connected" 
    gobject.io_add_watch(conn, gobject.IO_IN, handler) 
    return True 

def handler(conn, *args): 
    line = conn.recv(4096) 
    if not len(line): 
    print "Connection closed." 
    return False 
    else: 
    print line 
    if line.startswith("unittest"): 
     subprocess.call("/var/tmp/runme.sh", shell=True) 
    else: 
     print "not ok" 
    return True 

server('localhost', 8080) 
gobject.MainLoop().run() 

runme.sh

#!/bin/bash 
ps aux | grep py.py | awk '{print $2}' | xargs kill -9; 
export DISPLAY=:0.0 && lsof -i tcp:58888 | grep LISTEN | awk '{print $2}' | xargs kill -9; 
export DISPLAY=:0.0 && java -cp Something.jar System.V & 
export DISPLAY=:0.0 && /var/tmp/py.py & 

संपादित करें: ध्यान दें कि, मैं जावा और अजगर का उपयोग कर रहा दो परत के साथ एक आवेदन के रूप में एक साथ। तो runme.sh एक ही समय में दोनों ऐप्स लॉन्च करने के लिए मेरी स्टार्टअप स्क्रिप्ट है। जावा से मैं पायथन रिलांच बटन दबाता हूं। लेकिन पाइथन फिर से लॉन्च नहीं होता क्योंकि हत्या बाश के माध्यम से की जाती है।

+1

तो क्या आपको पता चला कि आपका कोड 'SO_REUSEADDR' क्यों सेट नहीं कर रहा था? –

+0

@ मैथ्यूएडम्स: अभी तक नहीं। अभी भी यह विफल रहता है। – YumYumYum

+1

मैंने अब इसी मुद्दे के बारे में अन्य प्रश्नों पर एक टन देखा है, और ऐसा लगता है कि ईजेपी 'SO_REUSEADDR' के बारे में बिल्कुल सही है। मुझे अभी भी नहीं पता है कि आपका कोड तत्काल क्यों कनेक्ट नहीं हो सकता है क्योंकि ऐसा लगता है कि आप 'SO_REUSEADDR'' सेट कर रहे हैं ... –

उत्तर

4

आपको इसे बांधने से पहले सॉकेट पर SO_REUSEADDR सेट करने के पाइथन समकक्ष को ढूंढना होगा। सॉकेट को अन्य उत्तरों में अनुशंसित करने के लिए बाहर निकलने पर बंद करना न तो आवश्यक है और न ही पर्याप्त है, क्योंकि (ए) प्रक्रिया समाप्त होने पर ओएस द्वारा सॉकेट बंद हो जाती है, और (बी) आपको अभी भी TIME_WAIT राज्य में स्वीकृत कनेक्शन को पार करना होगा, जो केवल SO_REUSEADDR कर सकते हैं।

+1

आप निश्चित रूप से सॉकेट विचार को बंद करना चाहते हैं, है ना? इसने हमेशा मेरे लिए समस्या को हल किया है और यह अच्छा अभ्यास की तरह लगता है ... –

+1

@MatthewAdams प्रक्रिया समाप्त होने पर सॉकेट ओएस द्वारा बंद हो जाती है। हर तरह से इसे अपने सामान्य कोड में बंद करें, लेकिन आपके उत्तर में उल्लिखित वीर लंबाई तक जाने की आवश्यकता नहीं है। – EJP

+1

(+1) आह। हालांकि निष्पक्ष होने के बावजूद, "वीर लम्बाई" कोड की केवल तीन पंक्तियां थीं और एक अलग 'मार' ध्वज का उपयोग कर ... –

2

मेरा अनुमान है: मार असीमित है। यह सिर्फ कर्नेल को प्रक्रिया को संकेत भेजने के लिए कहता है, यह संकेत देने और संभालने के लिए भी इंतजार नहीं करता है। प्रक्रिया को पुनरारंभ करने से पहले आपको 'प्रतीक्षा' कमांड का उपयोग करना चाहिए।

$ wait $PID 
1

संभावित समाधान # 1: फोर्क और पुरानी से अपनी पायथन लिपि की नई प्रति निष्पादित करें। यह सुनने की सॉकेट का वारिस होगा। फिर, अगर वांछित है, तो इसे माता-पिता से अलग करें और माता-पिता को मार दें (या बाहर निकलें)। ध्यान दें कि अभिभावक (पुराना संस्करण) किसी भी मौजूदा अनुरोध की सेवा समाप्त कर सकता है, भले ही बच्चा (नया संस्करण) किसी नए आने वाले अनुरोधों को संभालता है।

संभव समाधान # 2: सिग्नल वर्ष चल रहा स्क्रिप्ट sendmsg() और SCM_RIGHTS साथ नई स्क्रिप्ट के लिए सॉकेट सौंपने के लिए, तो पुरानी स्क्रिप्ट मार डालते हैं। This sample code "फाइल डिस्क्रिप्टर" के बारे में बात करता है लेकिन सॉकेट के साथ भी ठीक काम करता है। देखें: How to hand-over a TCP listening socket with minimal downtime?

संभावित समाधान # 3: यदि bind() EADDRINUSE लौटाता है, थोड़ी देर प्रतीक्षा करें और इसे सफल होने तक पुनः प्रयास करें। यदि आपको स्क्रिप्ट को जल्दी से शुरू करने की आवश्यकता है और बीच में कोई डाउनटाइम नहीं है, तो यह निश्चित रूप से काम नहीं करेगा :)

संभावित समाधान # 4: हत्या -9 के साथ अपनी प्रक्रिया को मत मारो। इसके बजाए इसे किसी अन्य सिग्नल के साथ मार दें, उदाहरण के लिए SIGTERMSIGTERM पर पकड़ें और जब आप इसे प्राप्त करते हैं तो gobject.MainLoop.quit() पर कॉल करें।

संभावित समाधान # 5: सुनिश्चित करें कि आपकी पायथन लिपि की मूल प्रक्रिया (उदाहरण के लिए खोल) wait एस है। यदि स्क्रिप्ट की मूल प्रक्रिया नहीं चल रही है, या यदि स्क्रिप्ट को डिमनीकृत किया गया है, तो SIGKILL के साथ मारे गए, तो init इसके माता-पिता बन जाएगा। init कॉल wait समय-समय पर लेकिन इसमें कुछ समय लग सकता है, शायद यह है कि आप क्या चल रहे हैं। यदि आप SIGKILL का उपयोग करना चाहते हैं, लेकिन आप तेजी से सफाई चाहते हैं तो बस wait पर कॉल करें।

समाधान 4 & 5 पुरानी लिपि को रोकने और नए शुरू करने के बीच कुछ बहुत छोटा लेकिन nonzero समय है। समाधान 3 में संभावित रूप से महत्वपूर्ण समय है, लेकिन यह बहुत आसान है। समाधान 1 & 2 सचमुच कोई डाउनटाइम के साथ ऐसा करने के तरीके हैं: कोई कनेक्ट कॉल सफल हो जाएगा और पुरानी या नई चल रही स्क्रिप्ट प्राप्त होगी।

पीएस विभिन्न प्लेटफार्मों पर SO_REUSEADDR के व्यवहार के बारे में अधिक विस्तार से: SO_REUSEADDR doesn't have the same semantics on Windows as on Unix

Windows पर, तथापि, कि विकल्प वास्तव में कुछ काफी अलग मतलब है। इसका मतलब है कि पता किसी भी प्रक्रिया से चोरी किया जाना चाहिए जो इस समय इसका उपयोग कर रहा है।

मुझे यकीन नहीं है कि यह आप क्या चल रहे हैं, लेकिन ध्यान दें कि जैसा कि वर्णन किया गया है यूनिक्स के विभिन्न संस्करणों पर व्यवहार कुछ अलग है।

3

1.

आप एक समस्या की हत्या अपने अजगर

air:~ dima$ ps aux | grep i-dont-exist.py | awk '{print $2}' 
34198 

जिसका मतलब है कि आपके grep प्रक्रिया में जाने और अपनी पुनः आरंभ तर्क ने मार डाला जाता है।

लिनक्स पर आप इसके बजाय पिडॉफ़ का उपयोग कर सकते हैं।

वैकल्पिक रूप से स्टार्ट-स्टॉप-डिमन और पिड फ़ाइल का उपयोग करें।

2.

आप पहले से ही पता उपयोग, इसलिए मेरा अनुमान है अपने अजगर काफी तेजी से मर नहीं करता है।

एक त्वरित परीक्षण के लिए, फिर से पाइथन शुरू करने से पहले नींद जोड़ें।

यदि यह मदद करता है, तो हत्या आदेश के बाद नींद-प्रतीक्षा लूप जोड़ें और केवल नए पायथन शुरू करें जब आपको यकीन है कि पुराना पायथन अब नहीं चल रहा है।

3

क्या कोई पाइथन प्रोग्राम अन्य प्रक्रियाओं को जन्म देता है? जैसे कांटा, subprocess या os.system के माध्यम से?

यह संभव है कि आपके सुनने फ़ाइल वर्णनकर्ता पैदा की प्रक्रिया द्वारा विरासत में मिली है: सॉकेट बिना

os.system ("नींद 1000") #:

ls -l /proc/`pidof sleep`/fd 
total 0 
lrwx------ 1 user user 64 2012-12-19 19:52 0 -> /dev/pts/0 
lrwx------ 1 user user 64 2012-12-19 19:52 1 -> /dev/pts/0 
l-wx------ 1 user user 64 2012-12-19 19:52 13 -> /dev/null 
lrwx------ 1 user user 64 2012-12-19 19:52 2 -> /dev/pts/0 

सॉकेट(); setsockopt(); बाँध(); बात सुनो(); os.system ("नींद 1000") # सॉकेट के साथ:

ls -l /proc/`pidof sleep`/fd 
total 0 
lrwx------ 1 user user 64 2012-12-19 19:49 0 -> /dev/pts/0 
lrwx------ 1 user user 64 2012-12-19 19:49 1 -> /dev/pts/0 
l-wx------ 1 user user 64 2012-12-19 19:49 13 -> /dev/null 
lrwx------ 1 user user 64 2012-12-19 19:49 2 -> /dev/pts/0 
lrwx------ 1 user user 64 2012-12-19 19:49 5 -> socket:[238967] 
lrwx------ 1 user user 64 2012-12-19 19:49 6 -> socket:[238969] 

शायद अपने अजगर स्क्रिप्ट की मृत्यु हो गई है, लेकिन इसकी बच्चों नहीं था, बाद सॉकेट और इस तरह नए अजगर प्रक्रिया सुन एक ही पते के लिए बाध्य नहीं कर सकते हैं के संदर्भ में रहते हैं।

2

आप प्री-निष्पादन परीक्षण और सफाई करने के लिए अपनी स्टार्टअप स्क्रिप्ट में अधिक तर्क जोड़ सकते हैं।

#!/bin/bash 
export DISPLAY=:0.0 

# If py.py is found running 
if pgrep py.py; then 
for n in $(seq 1 9); do 
    # kill py.py starting at kill -1 and increase to kill -9 
    if ! pgrep py.py; then 
    # if no running py.py is found break out of this loop 
    break 
    fi 
    pkill -${n} py.py 
    sleep .5 
done 
fi 

# Verify nothing has tcp/58888 open in a listening state 
if lsof -t -i tcp:58888 -stcp:listen; then 
echo process with pid $(lsof -t -i tcp:58888 -stcp:listen) still listening on port 58888, exiting 
exit 
fi 

java -cp Something.jar System.V & 
/var/tmp/py.py & 

आखिरकार आप शायद एक पूर्ण उड़ा हुआ init स्क्रिप्ट का उपयोग करना चाहते हैं और उन प्रक्रियाओं को डिमनीकृत किया है। उदाहरण के लिए http://www.thegeekstuff.com/2012/03/lsbinit-script/ देखें, यद्यपि यदि आपकी प्रक्रियाएं एक अप्रतिबंधित उपयोगकर्ता के रूप में चल रही हैं जो कार्यान्वयन को थोड़ा सा बदल देगी, लेकिन समग्र अवधारणाएं समान हैं।

0

मैंने कभी भी कोशिश नहीं की है। तो जोखिम को कम करने के लिए मैंने फ़ाइल सिस्टम को सॉकेट उदाहरण के रूप में उपयोग करना शुरू किया:

# Echo server program 
import socket,os 

s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 
try: 
    os.remove("/tmp/socketname") 
except OSError: 
    pass 
s.bind("/tmp/socketname") 
s.listen(1) 
conn, addr = s.accept() 
while 1: 
    data = conn.recv(1024) 
    if not data: break 
    conn.send(data) 
conn.close() 


# Echo client program 
import socket 

s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 
s.connect("/tmp/socketname") 
s.send('Hello, world') 
data = s.recv(1024) 
s.close() 
print 'Received', repr(data)