찾다
백엔드 개발PHP 튜토리얼Keepalive 및 파이프라인 요청 처리에 대한 nginx 분석

원본 기사, 재인쇄 시 표시해 주세요: Reprinted from pagefault

이 기사의 링크 주소: keepalive 및 파이프라인 요청 처리에 대한 nginx 분석

이번에는 주로 nginx를 살펴보세요. Keepalive 및 파이프라인 처리와 관련하여 여기에 개념을 소개할 필요가 없습니다. nginx가 어떻게 작동하는지 직접 살펴보겠습니다. 먼저 Keepalive 처리 과정을 살펴보겠습니다. 클라이언트가 명시적으로 연결 헤더를 close로 지정하지 않는 한, keepalive가 http 1.1의 기본값이라는 것을 알고 있습니다. 다음은 keepalive가 필요한지 여부를 결정하는 nginx의 코드입니다.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

void

ngx_http_handler(ngx_http_request_t *r)

{

.........................................

        switch(r->headers_in.connection_type) {

        case0:

//如果版本大于1.0则默认是keepalive

            r->keepalive = (r->http_version > NGX_HTTP_VERSION_10);

            break;

        caseNGX_HTTP_CONNECTION_CLOSE:

//如果指定connection头为close则不需要keepalive

            r->keepalive = 0;

            break;

        caseNGX_HTTP_CONNECTION_KEEP_ALIVE:

            r->keepalive = 1;

            break;

        }

..................................

}

12 3456789101112131415161718192021
void ngx_http_handler(ngx_http_request_t *r){.... ................................. 스위치(r->headers_in.connection_type) { case 0://버전이 1.0보다 큰 경우 기본값은 연결 유지입니다 r - >keepalive = (r->http_version > NGX_HTTP_VERSION_10);      break; caseNGX_HTTP_CONNECTION_CLOSE://연결 헤더가 close로 지정된 경우 no keepalive가 필요합니다. r->keepalive = 0; break 코드>; 케이스NGX_HTTP_CONNECTION_KEEP_ALIVE: /code>r->keepalive = 1; break;; 코드> }.......... ..... ..}

그러면 keepalive는 현재 http 요청이 실행된 후 바로 현재 연결이 닫히지 않는다는 것을 의미하므로 nginx의 keepalive 관련 처리도 요청 기능을 정리합니다. nginx가 요청을 정리하는 함수는 ngx_http_finalize_request입니다. 이 함수에서는 연결을 해제하기 위해 ngx_http_finalize_connection이 호출되며, keepalive에 대한 관련 판단이 이 함수에 있습니다.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

staticvoid

ngx_http_finalize_connection(ngx_http_request_t *r)

{

    ngx_http_core_loc_conf_t  *clcf;

    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

.....................................................................

//可以看到如果设置了keepalive,并且timeout大于0,就进入keepalive的处理。

    if(!ngx_terminate

         && !ngx_exiting

         && r->keepalive

         && clcf->keepalive_timeout > 0)

    {

        ngx_http_set_keepalive(r);

        return;

    } elseif(r->lingering_close && clcf->lingering_timeout > 0) {

        ngx_http_set_lingering_close(r);

        return;

    }

    ngx_http_close_request(r, 0);

}

12 34567891011121314151617181920212223
staticvoidngx_http_finalize_connection(ngx_http_request_t *r){ ngx_http_core_loc_conf_t *clcf; clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);.......................... ................................. //할 수 있습니다 Keepalive가 설정되고 시간 제한이 0보다 큰 경우 Keepalive 처리가 시작됩니다. if(!ngx_terminate && !ngx_exiting && r->keepalive && clcf->keepalive_timeout > 0) { ngx_http_set_keepalive(r); return; 코드> 코드>} elseif(r->lingering_close && clcf->lingering_timeout > 0) { ngx_http_set_lingering_close(r); 반환; } ngx_http_close_request(r, 0);}
위에서 ngx_http_set_keepalive를 통해 keepalive가 설정되어 있음을 알 수 있습니다. 다음으로 이 기능에 대해 자세히 살펴보겠습니다. 이 함수에서는 파이프라인 요청이 부수적으로 처리되므로 함께 살펴보겠습니다. 먼저 nginx가 클라이언트에서 읽은 데이터에 더 많은 데이터가 포함되어 있다고 가정합니다. 즉, 현재 요청을 구문 분석한 후에도 일부 데이터가 아직 남아 있어 파이프라인 요청으로 간주됩니다. 또 다른 매우 중요한 점은 http_connection입니다. 이전 블로그에서 큰 헤더를 할당해야 할 경우 hc->free에서 먼저 가져온 다음 생성된다는 것을 알고 있습니다. hc->busy를 관리하도록 하세요. 그리고 이 buf는 여기서 재사용됩니다. 왜냐하면 큰 buf가 크면 두 번째로 다시 할당해야 하기 때문입니다. 여기서 buf를 재사용하면 할당이 하나 줄어듭니다.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

    hc = r->http_connection;

    b = r->header_in;

//一般情况下,当解析完header_in之后,pos会设置为last。也就是读取到的数据刚好是一个完整的http请求.当pos小于last,则说明可能是一个pipeline请求。

    if(b->pos last) {

        /* the pipelined request */

        if(b != c->buffer) {

            /*

             * If the large header buffers were allocated while the previous

             * request processing then we do not use c->buffer for

             * the pipelined request (see ngx_http_init_request()).

             *

             * Now we would move the large header buffers to the free list.

             */

            cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);

//如果free为空,则新建

            if(hc->free== NULL) {

//可以看到是large_client_headers的个数

                hc->free= ngx_palloc(c->pool,

                  cscf->large_client_header_buffers.num * sizeof(ngx_buf_t *));

                if(hc->free== NULL) {

                    ngx_http_close_request(r, 0);

                    return;

                }

            }

//然后清理当前的request的busy

            for(i = 0; i nbusy - 1; i++) {

                f = hc->busy[i];

                hc->free[hc->nfree++] = f;

                f->pos = f->start;

                f->last = f->start;

            }

//保存当前的header_in buf,以便与下次给free使用。

            hc->busy[0] = b;

            hc->nbusy = 1;

        }

    }

다음 부분은 무료 요청 및 연결 유지 타이머 설정입니다.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

    r->keepalive = 0;

    ngx_http_free_request(r, 0);

    c->data = hc;

//设置定时器

    ngx_add_timer(rev, clcf->keepalive_timeout);

//然后设置可读事件

    if(ngx_handle_read_event(rev, 0) != NGX_OK) {

        ngx_http_close_connection(c);

        return;

    }

    wev = c->write;

    wev->handler = ngx_http_empty_handler;

12 3

4

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

    if(b->pos last) {

        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "pipelined request");

#if (NGX_STAT_STUB)

        (void) ngx_atomic_fetch_add(ngx_stat_reading, 1);

#endif

//设置标记。

        hc->pipeline = 1;

        c->log->action = "reading client pipelined request line";

//然后扔进post queue,继续进行处理.

        rev->handler = ngx_http_init_request;

        ngx_post_event(rev, &ngx_posted_events);

        return;

    }

56789 10 1112131415
r->keepalive = 0;ngx_http_free_request(r, 0); c->data = hc;//타이머 설정ngx_add_timer(rev, clcf ->keepalive_timeout);//읽을 수 있는 이벤트를 설정합니다if (ngx_handle_read_event(rev, 0) != NGX_OK) {ngx_http_close_connection(c); 반환 ;wev = c->write;wev->handler = ngx_http_empty_handler; 그런 다음 다음 부분은 파이프라인 처리입니다.
12 3456789101112131415 if(b->pos last) {ngx_log_debug0( NGX_LOG_DEBUG_HTTP, c->로그, 0, "파이프라인 요청");#if (NGX_STAT_STUB) (void) ngx_atomic_fetch_add(ngx_stat_reading, 1);#endif//태그를 설정합니다. hc->파이프라인 = 1;c->로그->액션 = "클라이언트 파이프라인 요청 라인 읽기";//그런 다음 게시 대기열에 넣고 처리를 계속합니다. code >rev->handler = ngx_http_init_request;ngx_post_event(rev, &ngx_posted_events);반환; 이 맨 아래에 도달하면 파이프라인 요청이 아니라는 뜻이므로 요청과 http_connection 정리를 시작합니다.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

    if(ngx_pfree(c->pool, r) == NGX_OK) {

        hc->request = NULL;

    }

    b = c->buffer;

    if(ngx_pfree(c->pool, b->start) == NGX_OK) {

        /*

         * the special note for ngx_http_keepalive_handler() that

         * c->buffer's memory was freed

         */

        b->pos = NULL;

    } else{

        b->pos = b->start;

        b->last = b->start;

    }

.....................................................................

    if(hc->busy) {

        for(i = 0; i nbusy; i++) {

            ngx_pfree(c->pool, hc->busy[i]->start);

            hc->busy[i] = NULL;

        }

        hc->nbusy = 0;

    }

12 3

4

1

2

3

4

5

6

7

8

9

//后面会详细分析这个函数

    rev->handler = ngx_http_keepalive_handler;

    if(wev->active && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) {

        if(ngx_del_event(wev, NGX_WRITE_EVENT, 0) != NGX_OK) {

            ngx_http_close_connection(c);

            return;

        }

    }

567891011121314151617181920212223 24252627282930
if(ngx_pfree(c->pool, r) == NGX_OK) { hc->request = NULL; } b = c->buffer; if(ngx_pfree (c->풀, b->시작) == NGX_OK) {   /* * ngx_http_keepalive_handler()에 대한 특별 참고 사항 * c->버퍼의 메모리가 해제되었습니다 코드> 코드> */ b->pos = NULL; 코드> } else{    b->pos = b->start; code>else code><code> b->last = b->start; }.......................... ......... ................................ if(hc->busy) {    for code>(i = 0; i nbusy; i++) {    ngx_pfree(c->pool, hc->busy[i] ->start); hc->busy[i] = NULL; } hc->nbusy = 0; <code>}
Keepalive 처리기를 설정합니다.
12 3456789 //이 함수는 나중에 자세히 분석할 예정입니다. rev->handler = ngx_http_keepalive_handler ; if(wev->active && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) { if(ngx_del_event(wev, NGX_WRITE_EVENT, 0) != NGX_OK) { code><code>ngx_http_close_connection(c);    return; } }
마지막 단계는 tcp push 처리입니다. 지금은 여기서 소개하지 않겠습니다. 다음으로 nginx의 tcp push 동작을 소개하는 특별한 블로그를 준비하겠습니다. 그런 다음 ngx_http_keepalive_handler 함수를 살펴보겠습니다. 이 함수는 연결에 다시 읽을 수 있는 이벤트가 있을 때 호출됩니다. 이 핸들러는 비교적 간단합니다. 새 buf를 생성한 다음 http 요청 실행을 다시 시작합니다(ngx_http_init_request 호출).

그런 다음 데이터가 있으면 읽어 보십시오. 읽을 수 있는 데이터가 없으면 읽기 가능한 이벤트에 핸들이 다시 추가됩니다

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

    b = c->buffer;

    size = b->end - b->start;

    if(b->pos == NULL) {

        /*

         * The c->buffer's memory was freed by ngx_http_set_keepalive().

         * However, the c->buffer->start and c->buffer->end were not changed

         * to keep the buffer size.

         */

//重新分配buf

        b->pos = ngx_palloc(c->pool, size);

        if(b->pos == NULL) {

            ngx_http_close_connection(c);

            return;

        }

        b->start = b->pos;

        b->last = b->pos;

        b->end = b->pos + size;

    }

12 3

4

1

2

3

4

5

6

7

8

9

n = c->recv(c, b->last, size);

c->log_error = NGX_ERROR_INFO;

if(n == NGX_AGAIN) {

    if(ngx_handle_read_event(rev, 0) != NGX_OK) {

        ngx_http_close_connection(c);

    }

    return;

}

567

1

ngx_http_init_request(rev);

89101112131415161718192021
 b = c->버퍼; 크기 = b->end - b->start; 코드> 코드> if(b->pos == NULL) { /* * c->버퍼의 메모리는 ngx_http_set_keepalive()에 의해 해제되었습니다. * 단, c->buffer->start와 c->buffer->end는 변경되지 않았습니다.                                                                                                                                                             code>//buf 재할당 b->pos = ngx_palloc(c->pool, size) ; code><code> if(b->pos == NULL) { ngx_http_close_connection(c);                                                                                              } b->start = b->pos;                                                                                                                                                >위치 + 크기 }
table>드디어 데이터를 읽어오면 요청 처리에 들어갑니다.
1234 56789 n = c->recv(c, b->마지막, 크기); code>c->log_error = NGX_ERROR_INFO;if(n == NGX_AGAIN) { if(ngx_handle_read_event(rev, 0 ) != NGX_OK) { ngx_http_close_connection(c); } 반환;}
1 ngx_http_init_request(rev);
마지막으로 ngx_http_init_request 함수를 살펴보겠습니다. 이번에는 파이프라인이 요청될 때 nginx가 요청을 재사용하는 방법을 주로 살펴보겠습니다.
여기에서 hc->busy[0]에 주의하세요. 파이프라인 요청인 경우 이전에 구문 분석되지 않은 요청 header_in을 저장한다는 것을 이미 알고 있습니다. 이는 두 번째 헤더를 읽었을 수 있기 때문입니다. 파이프라인 요청의 일부 헤더입니다.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

//取得request,这里我们知道,在pipeline请求中,我们会保存前一个request.

    r = hc->request;

    if(r) {

//如果存在,则我们重用前一个request.

        ngx_memzero(r, sizeof(ngx_http_request_t));

        r->pipeline = hc->pipeline;

//如果nbusy存在

        if(hc->nbusy) {

//则保存这个header_in,然后下面直接解析。

            r->header_in = hc->busy[0];

        }

    } else{

        r = ngx_pcalloc(c->pool, sizeof(ngx_http_request_t));

        if(r == NULL) {

            ngx_http_close_connection(c);

            return;

        }

        hc->request = r;

    }

//保存请求

    c->data = r;

12 3

4

567891011121314151617181920212223 2425
//요청 가져오기, 여기서 우리는 파이프라인 요청에서 이전 요청을 저장할 것이라는 것을 알고 있습니다.<code> r = hc->request; if code >(r) {//존재하는 경우 이전 요청을 재사용합니다. ngx_memzero(r, sizeof(ngx_http_request_t)); r->pipeline = hc-> 파이프라인;//nbusy가 존재하는 경우 if(hc ->nbusy) {//이 header_in을 저장한 후 바로 아래에서 구문 분석합니다. r->header_in = hc->busy[0]; 코드> 코드>} } else{ r = ngx_pcalloc(c->pool, 크기(ngx_http_request_t)); if(r == NULL) { ngx_http_close_connection(c); 반환; } hc->request = r; }//요청 저장 c->data = r;
위 코드와 이전 블로그를 통해 큰 헤더가 주로 파이프라인용이라는 것을 알 수 있습니다. 왜냐하면 파이프라인에서는 이전 요청이 다음 요청보다 더 많이 읽는 경우 헤더가 몇 개 있는 경우이기 때문입니다. , 다음 번에 구문 분석할 때 원래 할당된 client_header_buffer_size를 초과할 수 있습니다. 이때 큰 헤더인 헤더를 재할당해야 하므로 여기서 httpconnection은 주로 파이프라인 상황을 위한 것이며 Keepalive 연결이 다음과 같은 경우입니다. 파이프라인 요청이 아닙니다. 메모리를 절약하기 위해 이전 요청이 해제됩니다. 위 내용은 관련 측면을 포함하여 keepalive 및 파이프라인 요청 처리에 대한 nginx의 분석을 소개합니다. PHP 튜토리얼에 관심이 있는 친구들에게 도움이 되기를 바랍니다.
성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
PHP 실행 : 실제 예제 및 응용 프로그램PHP 실행 : 실제 예제 및 응용 프로그램Apr 14, 2025 am 12:19 AM

PHP는 전자 상거래, 컨텐츠 관리 시스템 및 API 개발에 널리 사용됩니다. 1) 전자 상거래 : 쇼핑 카트 기능 및 지불 처리에 사용됩니다. 2) 컨텐츠 관리 시스템 : 동적 컨텐츠 생성 및 사용자 관리에 사용됩니다. 3) API 개발 : 편안한 API 개발 및 API 보안에 사용됩니다. 성능 최적화 및 모범 사례를 통해 PHP 애플리케이션의 효율성과 유지 보수 성이 향상됩니다.

PHP : 대화식 웹 컨텐츠를 쉽게 만들 수 있습니다PHP : 대화식 웹 컨텐츠를 쉽게 만들 수 있습니다Apr 14, 2025 am 12:15 AM

PHP를 사용하면 대화식 웹 컨텐츠를 쉽게 만들 수 있습니다. 1) HTML을 포함하여 컨텐츠를 동적으로 생성하고 사용자 입력 또는 데이터베이스 데이터를 기반으로 실시간으로 표시합니다. 2) 프로세스 양식 제출 및 동적 출력을 생성하여 htmlspecialchars를 사용하여 XSS를 방지합니다. 3) MySQL을 사용하여 사용자 등록 시스템을 작성하고 Password_Hash 및 전처리 명세서를 사용하여 보안을 향상시킵니다. 이러한 기술을 마스터하면 웹 개발의 효율성이 향상됩니다.

PHP 및 Python : 두 가지 인기있는 프로그래밍 언어를 비교합니다PHP 및 Python : 두 가지 인기있는 프로그래밍 언어를 비교합니다Apr 14, 2025 am 12:13 AM

PHP와 Python은 각각 고유 한 장점이 있으며 프로젝트 요구 사항에 따라 선택합니다. 1.PHP는 웹 개발, 특히 웹 사이트의 빠른 개발 및 유지 보수에 적합합니다. 2. Python은 간결한 구문을 가진 데이터 과학, 기계 학습 및 인공 지능에 적합하며 초보자에게 적합합니다.

PHP의 지속적인 관련성 : 여전히 살아 있습니까?PHP의 지속적인 관련성 : 여전히 살아 있습니까?Apr 14, 2025 am 12:12 AM

PHP는 여전히 역동적이며 현대 프로그래밍 분야에서 여전히 중요한 위치를 차지하고 있습니다. 1) PHP의 단순성과 강력한 커뮤니티 지원으로 인해 웹 개발에 널리 사용됩니다. 2) 유연성과 안정성은 웹 양식, 데이터베이스 작업 및 파일 처리를 처리하는 데 탁월합니다. 3) PHP는 지속적으로 발전하고 최적화하며 초보자 및 숙련 된 개발자에게 적합합니다.

PHP의 현재 상태 : 웹 개발 동향을 살펴보십시오PHP의 현재 상태 : 웹 개발 동향을 살펴보십시오Apr 13, 2025 am 12:20 AM

PHP는 현대 웹 개발, 특히 컨텐츠 관리 및 전자 상거래 플랫폼에서 중요합니다. 1) PHP는 Laravel 및 Symfony와 같은 풍부한 생태계와 강력한 프레임 워크 지원을 가지고 있습니다. 2) Opcache 및 Nginx를 통해 성능 최적화를 달성 할 수 있습니다. 3) PHP8.0은 성능을 향상시키기 위해 JIT 컴파일러를 소개합니다. 4) 클라우드 네이티브 애플리케이션은 Docker 및 Kubernetes를 통해 배포되어 유연성과 확장 성을 향상시킵니다.

PHP 대 기타 언어 : 비교PHP 대 기타 언어 : 비교Apr 13, 2025 am 12:19 AM

PHP는 특히 빠른 개발 및 동적 컨텐츠를 처리하는 데 웹 개발에 적합하지만 데이터 과학 및 엔터프라이즈 수준의 애플리케이션에는 적합하지 않습니다. Python과 비교할 때 PHP는 웹 개발에 더 많은 장점이 있지만 데이터 과학 분야에서는 Python만큼 좋지 않습니다. Java와 비교할 때 PHP는 엔터프라이즈 레벨 애플리케이션에서 더 나빠지지만 웹 개발에서는 더 유연합니다. JavaScript와 비교할 때 PHP는 백엔드 개발에서 더 간결하지만 프론트 엔드 개발에서는 JavaScript만큼 좋지 않습니다.

PHP vs. Python : 핵심 기능 및 기능PHP vs. Python : 핵심 기능 및 기능Apr 13, 2025 am 12:16 AM

PHP와 Python은 각각 고유 한 장점이 있으며 다양한 시나리오에 적합합니다. 1.PHP는 웹 개발에 적합하며 내장 웹 서버 및 풍부한 기능 라이브러리를 제공합니다. 2. Python은 간결한 구문과 강력한 표준 라이브러리가있는 데이터 과학 및 기계 학습에 적합합니다. 선택할 때 프로젝트 요구 사항에 따라 결정해야합니다.

PHP : 웹 개발의 핵심 언어PHP : 웹 개발의 핵심 언어Apr 13, 2025 am 12:08 AM

PHP는 서버 측에서 널리 사용되는 스크립팅 언어이며 특히 웹 개발에 적합합니다. 1.PHP는 HTML을 포함하고 HTTP 요청 및 응답을 처리 할 수 ​​있으며 다양한 데이터베이스를 지원할 수 있습니다. 2.PHP는 강력한 커뮤니티 지원 및 오픈 소스 리소스를 통해 동적 웹 컨텐츠, 프로세스 양식 데이터, 액세스 데이터베이스 등을 생성하는 데 사용됩니다. 3. PHP는 해석 된 언어이며, 실행 프로세스에는 어휘 분석, 문법 분석, 편집 및 실행이 포함됩니다. 4. PHP는 사용자 등록 시스템과 같은 고급 응용 프로그램을 위해 MySQL과 결합 할 수 있습니다. 5. PHP를 디버깅 할 때 error_reporting () 및 var_dump ()와 같은 함수를 사용할 수 있습니다. 6. 캐싱 메커니즘을 사용하여 PHP 코드를 최적화하고 데이터베이스 쿼리를 최적화하며 내장 기능을 사용하십시오. 7

See all articles

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

인기 기사

R.E.P.O. 에너지 결정과 그들이하는 일 (노란색 크리스탈)
3 몇 주 전By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 최고의 그래픽 설정
3 몇 주 전By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 아무도들을 수없는 경우 오디오를 수정하는 방법
3 몇 주 전By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25 : Myrise에서 모든 것을 잠금 해제하는 방법
4 몇 주 전By尊渡假赌尊渡假赌尊渡假赌

뜨거운 도구

Eclipse용 SAP NetWeaver 서버 어댑터

Eclipse용 SAP NetWeaver 서버 어댑터

Eclipse를 SAP NetWeaver 애플리케이션 서버와 통합합니다.

Atom Editor Mac 버전 다운로드

Atom Editor Mac 버전 다운로드

가장 인기 있는 오픈 소스 편집기

ZendStudio 13.5.1 맥

ZendStudio 13.5.1 맥

강력한 PHP 통합 개발 환경

VSCode Windows 64비트 다운로드

VSCode Windows 64비트 다운로드

Microsoft에서 출시한 강력한 무료 IDE 편집기

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경