이 자료는 주로 초급 웹 프로그래머를 대상으로 합니다.

소개.

데이터를 보호하기 위해 무엇이 필요한지 이해하지 못하고 종종 필터링 기능을 복사하고 작동 방식과 정확히 수행해야 할 작업에 대해 생각하지 않은 초보 웹 프로그래머가 작성한 모듈 또는 자체 작성 CMS를 설치한 클라이언트가 제게 접근합니다. .

여기에서는 가능한 한 자세히 설명하려고 노력할 것입니다. 흔한 실수데이터를 필터링할 때 PHP 스크립트그리고 주다 간단한 팁데이터를 올바르게 필터링하는 방법.

인터넷에 데이터 필터링에 대한 많은 기사가 있지만, 완전한 것이 아니며 자세한 예도 없습니다.

복명.

여과법. 실수 #1
숫자 변수의 경우 다음 검사가 사용됩니다.
$숫자 = $_GET["입력번호"]; if (intval($number)) ( ... SQL 쿼리 실행... )
그것은 왜 SQL 주입? 요점은 사용자가 변수에 지정할 수 있다는 것입니다. 입력 번호의미:
1"+유니온+선택
이러한 경우 검사가 성공적으로 통과됩니다. intval 함수는 변수의 정수 값을 가져옵니다. 1, 하지만 변수 자체에서 $숫자아무것도 변하지 않았으므로 악성 코드 SQL 쿼리로 전달됩니다.
올바른 필터링:
$숫자 = intval($_GET["입력번호"]); if ($number) ( ... SQL 쿼리 실행... )
물론 특정 범위만 가져와야 하는 경우와 같이 조건이 변경될 수 있습니다.
if ($숫자 >= 32 AND $숫자<= 65)

숫자 값으로 확인란 또는 다중 선택을 사용하는 경우 다음을 확인하십시오.
$checkbox_arr = array_map("intval", $_POST["checkbox"]);
array_map
나는 또한 다음과 같은 형식으로 필터링을 만난다.
$number = htmlspecialchars(intval($_GET["입력 번호"]));
html특수문자
또는:
$number = mysql_escape_string(intval($_GET["입력 번호"]));
mysql_escape_string

미소 외에는 아무것도 할 수 없습니다. :)

여과법. 실수 #2.
문자열 변수의 경우 다음 필터링이 사용됩니다.
$input_text = addlashes($_GET["input_text"]);
addslashes 함수는 사양을 이스케이프합니다. 문자이지만 데이터베이스 인코딩을 고려하지 않으며 필터링을 우회할 수 있습니다. 나는 이 취약점을 설명한 저자의 텍스트를 복사하지 않고 단순히 Chris Shiflett에 대한 링크를 제공할 것입니다(Runet에서 번역을 검색할 수 있음).

mysql_escape_string 또는 mysql_real_escape_string 함수를 사용하십시오. 예를 들면 다음과 같습니다.
$input_text = mysql_escape_string($_GET["input_text"]);
입력할 의사가 없는 경우 HTML 태그, 다음 필터링을 수행하는 것이 가장 좋습니다.
$input_text = strip_tags($_GET["input_text"]); $input_text = html특수문자($input_text); $input_text = mysql_escape_string($input_text);
strip_tags - html 태그를 제거합니다.
htmlspecialchars - 특수 변환합니다. html 엔티티의 문자.
이것이 SQL 주입 외에 XSS 공격으로부터 자신을 보호하는 방법입니다.
html 태그가 필요하지만 소스 코드를 표시하기 위한 경우에만 다음을 사용하면 됩니다.
$input_text = htmlspecialchars($_GET["input_text"]); $input_text = mysql_escape_string($input_text);

변수 값이 비어 있지 않은 것이 중요하다면 다음과 같이 trim 함수를 사용하십시오.
$input_text = trim($_GET["input_text"]); $input_text = html특수문자($input_text); $input_text = mysql_escape_string($input_text);

여과법. 실수 #3.
데이터베이스를 검색하는 것입니다.
숫자로 검색하려면 첫 번째 오류에 설명된 필터링을 사용하십시오.
텍스트로 검색하려면 두 번째 오류에 설명된 필터링을 사용하지만 예약이 필요합니다.
사용자가 논리적 오류를 수행하지 못하도록 하려면 특수 문자를 제거하거나 이스케이프해야 합니다. SQL 문자.
추가하지 않은 예. 라인 처리:
$input_text = htmlspecialchars($_GET["input_text"]); // 검색: "%" $input_text = mysql_escape_string($input_text);
결과적으로 다음과 같은 쿼리를 얻습니다.
... WHERE text_row LIKE "%".$input_text."%" ... // WHERE text_row LIKE "%%%"
이렇게하면 받침대의 하중이 크게 증가합니다.
내 스크립트에서 검색에서 원하지 않는 문자를 제거하는 기능을 사용합니다.
function strip_data($text) ( $quotes = 배열("\x27", "\x22", "\x60", "\t", "\n", "\r", "*", "%", "<", ">", "?", "!"); $goodquotes = 배열("-", "+", "#"); $repquotes = 배열("\-", "\+", "\#"); $text = trim(strip_tags($text)); $text = str_replace($quotes, "", $text); $text = str_replace($goodquotes, $repquotes, $text); $text = ereg_replace(" +" , " ", $text); 반환 $text; )
물론 위의 기호가 모두 위험한 것은 아니지만 제 경우에는 필요하지 않으므로 검색하여 교체합니다.
필터링 사용 예:
$input_text = strip_data($_GET["input_text"]); $input_text = html특수문자($input_text); $input_text = mysql_escape_string($input_text);
또한 검색에서 문자 수를 최소 3자 이상으로 제한하는 것이 좋습니다. 데이터베이스에 많은 수의 레코드가 있는 경우 1-2자를 검색하면 데이터베이스의 로드가 크게 증가합니다.
여과법. 실수 #4.
변수 값은 필터링되지 않습니다. $_쿠키. 어떤 사람들은 이 변수가 양식을 통해 전달할 수 없기 때문에 이것이 보안 보장이라고 생각합니다.
이 변수는 사이트의 쿠키를 편집하여 모든 브라우저에서 위조하기 매우 쉽습니다.
예를 들어, 잘 알려진 한 CMS에서 사용된 사이트 템플릿을 확인했습니다.
if (@is_dir (MAIN_DIR . "/template/" . $_COOKIE["skin"]))( $config["skin"] = $_COOKIE["skin"]; ) $tpl->dir = MAIN_DIR . "/템플릿/" . $config["스킨"];
이 경우 변수의 값을 변경할 수 있습니다. $_COOKIE["스킨"]오류가 발생하면 사이트 폴더의 절대 경로가 표시됩니다.
쿠키 값을 사용하여 데이터베이스에 저장한 다음 위에서 설명한 필터링 중 하나를 사용하면 변수에도 동일하게 적용됩니다. $_SERVER.
여과법. 실수 #5.
지시문 포함 레지스터_글로벌. 켜져 있으면 반드시 끄십시오.
어떤 상황에서는 전달되어서는 안 되는 변수의 값을 전달할 수 있습니다. 예를 들어 사이트에 그룹이 있는 경우 그룹 2의 경우 $group 변수는 비어 있거나 0과 같아야 하지만 가짜로 충분합니다. 코드를 추가하여 양식:

PHP 스크립트의 변수 $그룹스크립트에서 기본값으로 선언되지 않은 경우 5와 같습니다.
여과법. 실수 #6.
다운로드한 파일을 확인합니다.
다음을 확인하십시오.
  1. 파일 확장자. 확장자가 php, php3, php4, php5 등인 파일의 로드를 비활성화하는 것이 좋습니다.
  2. 서버에 업로드된 파일은 move_uploaded_file입니까?
  3. 파일 크기
시험. 실수 #1.
AJAX 요청(예: 평판 증가)에 대해 사용자 이름 또는 ID(평판 상승 대상)가 전달되었지만 PHP 자체에서 그러한 사용자의 존재를 확인하지 않는 경우를 보았습니다.
예를 들어:
$user_id = intval($_REQUEST["user_id"]); ... INSERT INTO REPLOG SET uid = "($user_id)", 더하기 = "1" ... ... UPDATE 사용자 SET 평판 = 평판+1 WHERE user_id = "($user_id)" ...
데이터베이스에 레코드를 생성하는 것으로 나타났습니다. 이는 우리에게 전혀 쓸모가 없습니다.
시험. 실수 #2.
데이터에 대한 다양한 작업(추가, 편집, 삭제)을 수행할 때 이 기능에 대한 사용자의 접근 권한을 확인하는 것을 잊지 말고, 추가 기능 (HTML 사용법태그 또는 확인 없이 자료를 게시할 수 있는 기능).

오랫동안 나는 한 포럼 모듈에서 비슷한 오류를 수정했는데, 어떤 사용자라도 관리 메시지를 편집할 수 있었습니다.

시험. 실수 #3.
여러 개 사용할 때 PHP 파일간단한 확인을 합니다.
파일에 index.php(또는 다른 기본 파일에서) 다른 PHP 파일을 포함하기 전에 다음 줄을 작성하십시오.
정의("읽기 파일", true);
다른 PHP 파일의 시작 부분에 다음을 작성하십시오.
if (! defined ("READFILE")) ( exit ("오류, 잘못된 파일 경로.
메인으로 이동."); }
이렇게 하면 파일에 대한 액세스가 제한됩니다.
시험. 실수 #4.
사용자에 대해 해시를 사용합니다. 이것은 XSS에서 특정 함수를 호출하는 것을 방지하는 데 도움이 됩니다.
사용자에 대한 해시 컴파일의 예:
$secret_key = md5(strtolower("http://site.ru/" . $member["이름"] . sha1($password) . date("Ymd"))); // $secret_key는 해시입니다.
다음으로 모든 중요한 형식에서 입력을 사용자의 현재 해시 값으로 대체합니다.

스크립트 실행 중에 다음을 확인하십시오.
if ($_POST["secret_key"] !== $secret_key) ( exit ("오류: secret_key!"); )
시험. 실수 #5.
SQL 오류를 출력할 때 정보에 대한 접근을 간단하게 제한합니다. 예를 들어, GET 변수:
if ($_GET["passsql"] == "password") ( ... SQL 오류 출력... ) else ( ... 오류 정보만 있고 세부 정보 없음... )
이것은 사이트 해킹에 도움이 될 수 있는 해커 정보로부터 숨길 것입니다.
시험. 실수 #5.
외부에서 파일 이름을 가져와서 파일을 포함하지 마십시오.
예를 들어:
if (isset($_GET["file_name"])) ( $_GET["file_name"] .".php"; 포함)
스위치를 사용

나는 사용자가 이름, 나이, 이메일 등을 추가할 수 있는 PHP에서 하나의 간단한 목록을 만들고 있습니다. 삭제 옵션도 추가했지만 사용자가 삭제 버튼을 클릭할 때 확인 메시지를 추가하고 싶습니다.

인터넷 검색을 시도했지만 jQuery 및 JavaScript 솔루션만 찾았습니다. PHP로만이 작업을 수행 할 수있는 방법이 있습니까?

이름나이"; while($query2=mysql_fetch_array($query1)) ( 에코 " ".$query2["이름"].""; 에코" ".$query2["나이"].""; 에코" 편집하다"; 에코" 엑스"; } ?>

삭제.php

안에

PHP에서만 수행하려면 다음과 같이 스크립트에 "단계"를 추가해야 합니다.

1단계(양식 표시) -> 2단계(확인 요청) -> 3단계(확인)

이렇게 하려면 세션을 사용하여 양식의 내용을 저장하고 GET 매개변수를 사용하여 이 단계를 추적할 수 있습니다. 그렇지 않으면 가장 간단한 솔루션자바 스크립트를 사용하는 것입니다 :

에코" 엑스"; // php 내부의 js에 큰따옴표를 사용하세요!

이것이 당신이 필요로하는 것입니다

while($query2=mysql_fetch_array($query1)) ( echo " ".$query2["이름"].""; 에코" ".$query2["나이"].""; 에코" 편집하다"; 에코" 엑스"; ) 동안($query2=mysql_fetch_array($query1)) ( echo " ".$query2["이름"].""; 에코" ".$query2["나이"].""; 에코" 편집하다"; 에코" 엑스"; }

그리고 자바스크립트 함수를 생성

함수 ConfirmDelete(anchor) ( var conf = Confirm("이 레코드를 삭제하시겠습니까?"); if(conf) window.location=anchor.attr("href"); )

저를 믿으십시오, 그것은 일입니다 🙂

onClick 이벤트를 추가하여 대화 상자를 실행하고 javascript:return Confirm("정말 삭제하시겠습니까?");

에코" 엑스";

//onclick 이벤트 추가 onclick="return deleteconfig()"

나를 위해 일하지만 이것을 변경하십시오.

onclick="javascript:confirmationDelete($(this)); return false;"

onclick="confirmationDelete(this); return false;"

다음은 확인 필드를 제공하고 PHP에서 Javascript로, 그리고 다시 PHP로 변수를 전달하는 위의 변형입니다.
이것을 사용하여 파일 목록에서 파일을 제거하는 라디오 버튼을 선택했습니다.
Javascript에서 php $fileName이라는 OnClick 트리거 기능을 참조하고 파일 이름으로 확인하고 그렇다면 $_GET에 대한 변수와 함께 href를 전달하십시오.

PHP/HTML 코드:

$파일이름 $extn $크기 $modtime "; ?>

이 기사는 클러스터에 관한 것이 아니며 복제를 통한 샤딩에 관한 것도 아니며 클라우드에 관한 것도 아닙니다. 이 기사는 사용자의 수와 요청이 눈사태처럼 증가할 수 있는 매우 안정적인 컴퓨팅 아키텍처를 구축하는 방법에 관한 것입니다. 그리고 웹 서비스가 각 요청을 수락하고 올바르게 끝까지 처리하고(일부 구성 요소의 실패 및 실패에 관계 없이) 클라이언트에 응답을 전달하도록 보장하는 것이 비즈니스에 중요합니다. 그리고 물론 장비에 대한 "공간" 비용과 시스템 관리자의 급여가 없습니다.

즉, 우선 "필요한가?"라고 생각하십시오. 누군가 한 달에 100건의 매출을 올리는 말하는 햄스터를 판매하는 온라인 상점이 있다면 아마 아닐 것입니다. 그리고 수십만 및 수백만 명의 사용자를 수용할 수 있고, 많은 양의 계산이 필요하고, 고가치 데이터로 작업하고, 모든 비즈니스 프로세스의 트랜잭션성을 보장하고, 데이터의 병렬 처리가 필요한 비즈니스를 운영할 계획이라면, 이다.

금융 부문, 수십만 개에 달하는 대규모 온라인 상점, 온라인 경매, 호텔 및 항공사 예약 시스템, 광고 캠페인 시작 후 다음 날 백만 사용자 기반을 확보하려는 야망을 가진 새로운 클라우드 또는 소셜 서비스가 있습니다. 웹 서비스를 위한 매우 안정적인 시스템에 잠재적으로 관심이 있습니다.

이 자료의 대상

1. 고부하 및 내결함성 컴퓨팅 서비스 생성에 관심이 있는 대규모 웹 프로젝트 개발자.

2. 사용자 기반의 "폭발적인" 성장을 기대하고 컴퓨팅 부분에 높은 요구를 하는 신규 또는 확장 비즈니스의 소유자.

3. 현 상태에 만족하지 않고 근본적인 개편을 생각하고 있는 대규모 웹 프로젝트의 기술 리더 및 관리자.

"계산"에 대해 왜 그렇게 많은 단어가 있습니까?

대형 웹 프로젝트의 즉각적인 미래는 " 빅 데이터”(“빅 데이터”)는 가상화, 에너지 절약, 모니터링과 함께 2011년에 1위를 차지한 트렌드로, 2013년부터 업계에서 확고히 자리를 잡았으며 심지어 학계의 하나가 되었습니다. 대형 외국 대학의 과목.

비즈니스 문제를 해결하기 위해 처리해야 하는 데이터의 양은 오늘날 지속적으로 증가하고 있으며 미래에는 이 프로세스가 더욱 가속화될 것입니다. 10년 전에는 사용자에게 제품과 함께 "온라인 상점"을 보여주는 것으로 충분했다면 1년 전에는 사이트를 통해 사용자의 경로를 분석하고 엄격하게 관련된 제품(소위 "행동 기술" ), 오늘날에는 키, 체중, 나이, 좋아하는 개의 이름을 포함하여 사용자에 대한 모든 것을 아는 것이 표준으로 간주됩니다.

자연 경쟁의 법칙에 따르면 고객에게 경쟁자보다 더 많은 옵션을 제공하려면 더 많은 데이터와 더 많은 계산이 필요합니다. 그리고 조만간 적절한 접근 방식이 없으면 프로젝트가 물에 빠져들 수 있습니다. 마치 다양한 프로젝트가 지금 모든 곳에서 물에 빠져 있는 것처럼, 누군가는 지원의 복잡성 때문에 누군가는 단순히 잘못된 코드 때문에 누군가는 모듈의 높은 연결성으로 인해 , 신뢰할 수 없는 구성 요소의 사용으로 인해 누군가.

따라서 오늘날 큰 야망을 가지고 웹 프로젝트를 시작하는 모든 사람은 처음에 프로젝트를 프로그램 코드뿐만 아니라 자체 존재 및 개발 법칙이 있는 시스템인 컴퓨팅 환경으로 간주하면 경쟁자보다 더 많은 이점을 얻을 수 있습니다. 더 많은 관심을 기울일수록 컴퓨팅 관리처음에는 앞으로 몇 년 동안 경쟁자를 추월할 가능성이 더 높아집니다.

이 라인의 저자는 로드가 높은 웹 서비스를 구축하기 위한 "고전적인" 현대적인 접근 방식이 여러 가지 심각한 단점을 가지고 있다고 확신합니다. 왜 그런지 봅시다. 먼저 전형적인 현대식 계획을 고려하십시오.

로드가 많은 웹 서비스를 구축하기 위한 고전적인 접근 방식

1. 많은 서버가 역할로 나뉩니다.

2. 서버의 일부(프론트엔드 역할)는 정적 리소스(이미지, CSS, JS 파일)를 반환하고 들어오는 트래픽의 전면을 다운스트림 노드로 "분배"하도록 설계되었습니다. 주요 소프트웨어는 일반적으로 Nginx입니다.

3. 다운스트림 노드(백엔드 역할)는 동적 계산에 참여합니다. 간단히 말해서 일반적인 Apache + PHP 번들일 수 있습니다.

4. 또 다른 서버 그룹은 데이터 저장을 위해 설계되었습니다. MySQL, Memcache, Redis 등이 있습니다.

5. 웹 서비스 코드 자체( 이 예– PHP 코드)는 Apache + PHP가 있는 모든 노드에 동일하게 복사되고 하나 또는 다른 노드에 "떨어지는" 요청을 동일하게 처리합니다.

6. 데이터베이스는 어떤 형태의 샤딩을 사용하여 서버 그룹에 "번져"지며, 유사한 방식으로 부하가 분산됩니다.

주의깊은 독자는 DNS 밸런싱과 CDN이 스키마에 언급되지 않았음을 알 수 있지만 작성자는 스키마를 지나치게 복잡하게 만들지 않기 위해 의도적으로 생략했습니다. 이 조각의 작동 원리는 위와 매우 유사합니다.

고전적 접근의 단점

1. 주요 트렌드는 합병증. 프로젝트 기간 동안 "고전적인" 구성표는 점점 더 복잡해집니다. 각각의 새 서버가 추가되면 이를 트래픽 분배 체계에 입력해야 합니다. 물론 대규모 프로젝트에서 서버를 역할에 도입하는 것은 "원 버튼" 작업이지만 그럼에도 불구하고 지원해야 하는 인프라의 증가입니다. 그리고 훨씬 더 - 데이터베이스의 현재 용량이 더 이상 충분하지 않고 서비스를 중지하지 않고 "살아있는" 상태로 데이터베이스를 새 서버로 전송하거나 배포해야 하는 경우입니다.

그러나 주요 문제는 트래픽 분산 클러스터를 증가한다고 해서 프로그램 코드의 복잡성이 감소하지 않는다는 것입니다. 클러스터를 완벽하게 구축할 수 있지만 코드는 동일하게 유지됩니다.

2. 배포는 원자적이지 않습니다.. 쉽게 말해 레이아웃 새로운 버전전투 서버에 대한 프로젝트는 시간이 걸립니다. N-20 머신에 파일을 물리적으로 다운로드하고, 데이터베이스를 변경하고, 캐시를 재설정하고(많은 다른 캐시) 동시에 모든 서버에서 "이전 코드"로 요청 처리를 완료하고 " 새로운 코드”. 그렇지 않으면 사용자 요청의 일부가 이전 방식으로 처리되고 일부는 새로운 방식으로 처리되고 일부는 "이전 방식"에 따라, 일부는 "새 방식에 따라" 데이터베이스에 들어갈 때 많은 작은 충돌이 발생할 수 있습니다. 곧.

따라서 이상적으로는 모든 사람이 업데이트 기간(몇 초 또는 수십 초) 동안 서비스를 일시 중지한 다음 다시 켜기를 원합니다. 실제로 초당 최소 1000개의 요청이 흐르는 상황에서 아무도 이를 수행하지 않습니다. 사소한 충돌을 "수동으로" 정기적으로 수정하거나 "최대 7세대" 이전 버전과의 호환성을 지원하는 방어 프로그래밍으로 덮는 것을 선호합니다. 정기 지원 방법에 대해 하위 호환성프로그래머의 삶을 복잡하게 만들고 전체 프로젝트 비용을 증가시킵니다. 똑똑한 독자는 스스로 생각할 수 있습니다.

3. HTTP에서 사용. HTTP 프로토콜은 분명히 도덕적으로나 기술적으로 쓸모가 없습니다. 모바일 개발- 더 가벼운 프로토콜로 모든 곳에서 대체되고 있다는 것을 알고 있습니다. 그러나 주요 단점은 다릅니다. "브라우저에서" HTTP 프로토콜은 루프를 완료해야 합니다. 제한된 시간 내에 응답이 필요합니다. 이렇게 하면 서비스가 브라우저에서 허용하는 짧은 시간 제한 내에서 엄격하게 응답을 계산하고 준비해야 합니다. 서비스인 경우 이 순간과부하 - 요청이 영원히 손실됩니다.

따라서 "전형적인 웹 프로젝트"에서는 긴 폴링 또는 기타 형태의 주기적 요청과 같은 다양한 트릭에 의존합니다. 이는 아키텍처를 복잡하게 할 뿐만 아니라 불필요한 "낭비된 풀링"으로 서비스에 과부하를 줍니다.

4. 요청당 스크립트 초기화. 이것은 오랜 전통에 따라 각 요청에 대한 응답으로 다시 시작되는 PHP와 같은 스크립팅 언어 및 HTTP 사용의 결과입니다. 예, 예, 초당 1000개의 요청 각각에 대한 응답으로 PHP 스크립트가 다시 시작되고 모든 변수를 다시 초기화하고 데이터베이스에 대한 연결을 다시 설정합니다. 실제로 요청을 처리하는 데 0.005초가 걸리고 스크립트는 0.05초 순서로 초기화됩니다. 10배 더 길어요!

즉, 서버가 바쁜 시간의 90%는 클라이언트 요청을 처리하지 않고 스크립트를 쓸모없게 초기화하는 것입니다. 돈으로 환산해 보세요. 따라서 이러한 불쾌한 효과를 상쇄하도록 설계된 Memcache 또는 Redis와 같은 로컬 캐시, OPcode 캐싱, 영구 데이터베이스 연결과 같은 많은 해결 방법이 발명되었습니다.

5. 모놀리식 애플리케이션. 애플리케이션을 모듈로 어떻게 나누든, 복잡한 디렉토리 구조에 코드를 아무리 열심히 퍼뜨리려 해도, 어떤 지연 자동 로딩을 사용하든 상관없이, 단 하나의 기준이 있습니다. 업로드하기 위해 전체 애플리케이션을 업로드해야 하는 경우 하나 이상의 변경 사항이 있으면 모놀리식 응용 프로그램이 있습니다. 다른 것은 제공되지 않습니다.

모놀리식 애플리케이션의 단점은 문헌에 많이 설명되어 있습니다. 주요 기능 중 하나를 간단히 언급할 수 있습니다. 가장 작은 기능이라도 한 시간 내에 프로덕션에 적용하려면 전체 프로덕션 체인을 한 시간 안에 맞춰야 합니다. 문제 정의, 구현, 이전 버전과의 호환성 검사, 테스트 작성, 문서 작성, 수동 테스트 부서를 통한 실행, 버그 수정 - 이 모든 것이 1시간 이내에 이루어집니다.

매 시간 정확히 00분에 전체 애플리케이션을 업로드하면 매 시간이 끝날 때까지 전체 응용 프로그램안정된 상태로.

6. 웹 인터페이스는 백엔드에 의해 렌더링됩니다.. 대표적인 경우, 모습프로젝트 페이지의 (따라서 HTML 코드)는 일반적으로 각 요청에 대한 응답으로 백엔드 측에서 렌더링됩니다. 이것은 자원과 돈의 과도하고 부당한 지출입니다.

7. 부서의 정치 분할. 부서 시스템 관리자들어오는 트래픽 전면이 PHP 코드가 실행되는 여러 서버에서 "번져"지는지 확인하는 책임이 있습니다. 프로그래밍 부서는 PHP 코드를 담당합니다. PHP 코드가 특정 요청을 처리할 시간이 없다면 누가 이에 대한 책임이 있는지 명확하지 않습니다. 관리자가 서버에 너무 많은 트래픽을 "허용"하고 과부하를 일으켰는지, 아니면 -최적의 스크립트. 데이터베이스가 느려지기 시작하면 누가 극단적으로 남아 있는지도 불분명합니다. 제 시간에 데이터베이스를 "샤딩"해야 한다는 사실을 깨닫지 못한 관리자와 알아낼 수 있는 프로그래머가 있습니다.

예제가 과장되고 "실생활이 아닌" 것처럼 보이면 작성자가 200개의 서버에서 호스팅되는 프로젝트에서 작업했으며 그 중 60개가 데이터베이스에 의해 점유되었음을 기억하십시오. 이 프로젝트에 봉사하기 위해 얼마나 많은 사람들이 고용되었는지 기억하는 것이 끔찍합니다.

주요 단점

우리는 위의 생각을 반복합니다. 저자에 따르면 고전적인 계획의 주요 단점은 기술 전문가가 잘못된 것을 최적화한다는 것입니다. 그들은 가장 본질적인 컴퓨팅 부분을 최적화하는 대신 들어오는 요청의 전면을 최적화하고 실제로 대규모 시스템 그룹에 걸쳐 요청을 "스미어링(smearing)"합니다. 그리고 보기보다 훨씬 쉽습니다.

이론적 이상

오, 우리가 할 수 있기를:

1. 비싼 서버 무리를 없애고 한두 개의 소규모 그룹을 사용하십시오.

2. Nginx->Apache->PHP 체계를 버리십시오. 비용이 많이 드는 리소스 소모 측면에서 엄청난 "오버헤드"가 있습니다.

3. 같은 이유로 PHP 스크립트를 초기화하는 엄청난 비용을 없애십시오.

4. 백엔드에서 페이지를 "렌더링"할 필요를 거부합니다. 웹 서비스가 불안정하거나 인터넷 연결이 없는 상태에서 작동할 수 있다면(예를 들어, 모바일 네트워크길에서).

5. HTTP 타임아웃 "루프"를 없애고 이 응답이 준비되었을 때만 클라이언트에게 응답을 전달하고 전달을 보장합니다.

6. 중단하지 않고 단일 클라이언트 요청을 잃지 않고 작은 부분으로 프로젝트를 업데이트합니다.

7. 프로젝트의 일부(일부 구성 요소)가 "떨어졌거나" 디버깅 목적으로 일시적으로 꺼진 경우 요청 손실에 대해 걱정하지 마십시오.

언리얼? 용이하게!

탁월함을 향한 첫걸음

먼저, 배우자 무엇수행해야 하므로 논의할 것입니다. 어떻게.

1. 전체 시스템을 SOA로 설계(서비스 지향 아키텍처) ESB(엔터프라이즈 메시징 버스)를 사용하여 모놀리식 접근 방식을 포기하여 비즈니스 로직의 각 독립적인 부분이 별도의 "서비스"에 의해 처리되고 독립적인 교환 버스를 통해 서로 통신합니다.

2. 동시성을 버려라. 예를 들어, 동기식 "요청 - 처리 - 응답" 체계에서 이것은 엄격한 종료 제어가 없고 쉽게 중단될 수 있는 하나의 HTTP 루프입니다. 비동기식에는 요청(전송 및 확인), 처리(실패 시 재시도), 응답 전달(보증 포함)의 세 가지 별도 프로세스가 있습니다.

3. 프로젝트를 두 개의 응용 프로그램으로 나눕니다.- 프론트엔드와 백엔드. 웹 서비스의 경우 프론트엔드는 (일반적으로) JavaScript 애플리케이션입니다. 결론은 응용 프로그램이 비동기식으로 작동하고 서로 분리되어 양방향 통신 프로토콜을 통해 메시지를 교환한다는 것입니다.

4. HTTP 선택 해제 WebSocket에 찬성합니다. WebSocket 프로토콜은 HTTP와 비교할 때 환상적인 성능을 가지고 있으며 "시간 초과가 있는 루프"가 없으며 양방향으로 모든 데이터(바이너리 데이터 포함)를 전송할 수 있습니다.

5. 쿼리 실행을 위한 "저장소" 제공. 클라이언트로부터 요청을 받으면 클라이언트에게 "확인"이라고 말하고 이 요청을 저장합니다. 백엔드가 이전 처리 주기에서 해제되는 즉시 요청을 전달합니다. 요청이 백엔드 노드 사이를 "걸어 다니고" 있는 한 노드 "들어간" 순간부터 유지하고 노드를 "떠나는" 즉시 커밋합니다. 따라서 일부 노드가 "떨어지는" 경우 시스템은 요청을 잃지 않고 즉시 처리로 되돌려 보냅니다. 처리가 끝나면 결과를 클라이언트에 보내고 클라이언트가 "확인"이라고 말할 때까지 저장합니다.

6. 병렬 컴퓨팅 보장비즈니스 로직 측면에서 병렬로 수행할 수 있는 작업에 대해, 그리고 엄격하게 순차적으로 수행되어야 하는 작업에 대해 순서를 지정합니다. 이 단락은 "Captain Obvious"에 대한 주장으로 작성되지 않았지만 어떤 비즈니스 프로세스도 다중 스레드 코드에 "맹목적으로 배치"될 수 없음을 보여주기 위해 작성되었습니다.

7. "데몬"을 위해 스크립팅을 포기하십시오. 데몬은 한 번 시작한 다음 계속해서 메모리에 "매달린" 프로세스입니다. 항상 실행되기 때문에 모든 요청에 ​​대해 다시 초기화하는 데 시간을 할애할 필요가 없습니다. 앞으로 PHP 데몬(최신 버전의 PHP를 사용하는 경우)은 일반 PHP 스크립트와 근본적인 차이점이 없다고 말할 것입니다.

SOA 설계 접근 방식

SOA(서비스 지향 아키텍처)는 새로운 것이 아닙니다. 소프트웨어 개발에 대한 이 모듈식 접근 방식은 지난 세기에 IBM에 의해 시작되었으며 현재 주로 .NET 및 JAVA의 "엔터프라이즈 수준" 제품에서 업계 리더에 의해 지원 및 홍보되고 있습니다.

웹 서비스를 프로그래밍하는 고전적인 접근 방식에서 PHP 언어및 유사 - 디자인은 모델, 해당 속성 및 작업에서 시작됩니다. 모델은 실제 개체를 나타내고 작업은 개체에 대한 작업을 나타냅니다. 그러나 실습에서 알 수 있듯이 실제 세계는 훨씬 더 다면적이고 복잡하며 이벤트 및 그에 대한 반응의 언어로 훨씬 더 효과적으로 설명됩니다(자세한 내용은 설명 및 예가 있는 게시물 #1593 참조).

현실 세계는 대부분 우리의 참여 없이 동시에(프로그래밍 측면에서 - "병렬") 발생하고 다양한 반응이 발생하거나 발생하지 않는 이벤트로 구성됩니다. 반응은 차례로 다음 이벤트를 생성할 수 있습니다. SOA는 이벤트, 이벤트 간의 관계 및 이벤트에 대한 반응으로 작동하는 것이 가장 편리하기 때문에 "실제 프로그래밍"에 이상적입니다. 또한, 에 올바른 접근 PHP와 같은 "단일 스레드" 프로그래밍 언어를 사용하더라도 아키텍처 구성에 대한 이벤트와 반응이 병렬로 발생합니다.

비즈니스 로직에서 발생하는 이벤트, 이벤트가 서로 어떻게 관련되어 있는지 또는 서로 따라야 하는지, 특정 이벤트에 대한 응답으로 어떤 반응이 발생해야 하는지, 누가 이러한 이벤트를 정확히 처리할 것인지에 따라 SOA 제품을 설계해야 합니다. 다른 이벤트. 데이터 모델 및 이에 대한 작업의 구현은 백그라운드로 사라지고("서비스"로 캡슐화됨) "서비스" 목록과 이들 간의 상호 작용 계획(서비스 간 API)이 포그라운드에 놓입니다.

구현: 첫 번째 근사치

1. 독립 애플리케이션으로서의 프론트엔드. 널리 사용되는 모든 JavaScript-MVC-프레임워크가 구현에 적합합니다. 그러나 연습을 통해 "JavaScript, MVC 및 프레임워크"라는 단어를 한 문장으로 결합할 때 이빨이 아프기 시작하면 어렵습니다. 애플리케이션은 백엔드를 참조하지 않고 모든 "화면"을 그릴 수 있어야 하며, 백엔드를 참조하지 않고도 사용자 탐색("화면" 간 전환)을 제공하고, 백엔드와의 양방향 통신 채널을 지원할 수 있어야 합니다.

2. 백엔드에 대한 WebSocket 연결을 위한 진입점. NodeJS에는 긴 폴링 및 이념적으로 구식브라우저. 다른 사람의 서비스와 함께 일부 게이트웨이를 작성해야 하는 경우 순수 HTTP에서도 이 노드에 액세스할 수 있지만 단순화하기 위해 별도의 "순수 HTTP" 노드를 작성할 수 있습니다.

진입점은 프런트 엔드 애플리케이션(즉, 브라우저)과 양방향 통신 채널을 제공하여 요청을 수락하고 이에 대한 응답을 반환합니다.

3. 시스템에 실행 중인 요청 저장. 이를 위해 널리 사용되는 AMQP 서버가 가장 적합하며 메시지 대기열과 그 사이의 라우팅을 제공합니다. 다음 요청이 클라이언트에서 오는 즉시 "수신" 대기열에 넣습니다. 또한 데몬이 이 대기열에서 추출하여 요청 내용을 분석하고 시스템 전체의 "라우팅"으로 보냅니다(실제로 특정 알고리즘에 따라 하나 이상의 대기열로 전송하는 것을 의미함). 비즈니스 로직의 고유한 부분을 처리하는 각 데몬은 "해당" 들어오는 큐에서 특정 메시지를 수신하고 처리하고 "보내는" 큐에 응답을 배치합니다.

인기 있는 RabbitMQ 브로커의 용어에는 나가는 대기열에 대한 개념이 없습니다. 메시지는 라우팅 규칙에 따라 브로커 자체가 특정 대기열로 전송되는 교환기(교환기)에 게시됩니다. 여기에는 응답이 요청자에게 직접 전송되지 않도록 조건부 이해를 위해 작성되었습니다.

4. 악마 통제(감독자). 간단한 PHP 스크립트를 데몬으로 실행하려면 실행 코드를 while(true) (...)로 래핑하고 다음을 입력하십시오. 명령줄"php your-script.php"와 같은 것입니다. 그러나 기본적으로 동일한 작업을 수행하지만 필요한 환경 상태를 제공하고 프로세스 상태를 모니터링하며 더 유용한 작업을 수행하는 적절한 감독자를 사용하는 것이 좋습니다.

실생활에서 PHP 데몬은 조금 더 복잡합니다. 제어 신호와 재구성 메시지를 수신해야 하고, 메모리가 누출되어서는 안 되며, 데이터베이스에 대한 연결을 유지(또는 복원)해야 합니다. 그러나 일반적으로 다음보다 복잡하지 않습니다. 당신이 사용하는 PHP 스크립트.

현실에 한 걸음 더 다가가기: SOA의 이벤트 중심 접근 방식

모듈식 응용 프로그램을 구축하기 위한 일부(저자의 의견으로는 구식) 접근 방식은 원격 프로젝트 구성 요소의 특정 메서드 또는 절차에 대한 직접 호출을 의미하는 RPC(원격 프로시저 호출) 원칙을 기반으로 합니다. 이 접근 방식은 일반적으로 전송 노드와 실행 노드 사이의 직접적이고 단단한 연결을 의미하기 때문에 SOA의 모든 장점을 완전히 파괴합니다. 복잡한 제품의 설계 및 구현에 있어서는 느슨하게 결합된 구성요소의 원칙을 최대한 준수해야 합니다. 왜냐하면 아키텍처와 코드의 복잡성이 궁극적으로 소유 비용(제품의 수정 및 변경)을 결정하기 때문입니다. 출시 후).

SOA의 이벤트 중심 접근 방식은 구성 요소(서비스)가 비동기 이벤트("이벤트", 이벤트라는 단어)를 전송하여 서로 통신한다고 가정합니다. 이벤트는 이름(이름)과 매개변수 세트가 있는 메시지(예: AMQP 용어)입니다. 이벤트는 시스템에 어떤 일이 발생했음을 알리거나 시스템에 "질문"하기 위한 것입니다. 일반적으로 이벤트는 주소 없이 "시스템으로"(더 정확하게는 공통 ESB 버스로) 전송됩니다.

반대로 특정 노드(구성 요소, 서비스)는 응답할 준비가 된 특정 이벤트에 대해 공통 버스를 수신합니다. 이는 서비스가 일부 이벤트를 수신하고 적절한 작업을 수행할 준비가 되었음을 의미할 수 있습니다. 또는 서비스에 약간의 지식이 있고(예: 사용자에 대한 정보가 포함된 데이터베이스 소유) "요청"에 대한 응답으로 제공할 준비가 되어 있습니다. 두 경우 모두 이벤트에 응답한 결과는 다른 관심 서비스에서도 들을 수 있는 새 이벤트(다른 이름과 다른 매개변수 사용)를 생성하는 것입니다.

일반의 적절한 조직으로 ESB 타이어, 서비스는 서로를 기다리지 않고 비동기적으로 이벤트를 보내고 받습니다. 즉, 전송 서비스는 시간 지연 없이 ESB에 이벤트를 얼마든지 보낼 수 있고 다음 작업을 해결하는 단계로 넘어갈 수 있습니다. 이것을 현재 처리 주기에서 응답을 기다리는 것을 의미하는 기존 HTTP와 비교하면 이점을 볼 수 있습니다. 또한 수신 서비스는 이전 이벤트 처리가 완료되는 즉시 서로 독립적으로 새로운 이벤트를 비동기식으로 수신합니다.

코드의 SOA 이벤트 모델

간단히 말해서 코드를 함수(메서드)가 있는 클래스가 아니라 이러한 이벤트에 대한 응답으로 발생하는 이벤트 및 작업으로 봐야 합니다. 게다가 행동의 결과도 사건이다. 논의 중인 아키텍처와 관련하여 로컬 이벤트는 특정 PHP 스크립트 내에서 발생한 이벤트이고 원격 이벤트는 AMQP 대기열에서 이 스크립트로 오는(또는 결과적으로 그곳으로 보내지는) 이벤트라고 말할 수 있습니다. 모든 코드를 이런 식으로 처리하면 즉시 놀랍고 매우 중요한 효과가 나타납니다.

로컬 및 원격 이벤트가 동일한 경우 로컬 및 원격 이벤트 핸들러도 동일합니다!

왜 그렇게 중요합니까? 팀의 프로그래머는 이 이벤트 또는 그 이벤트가 처리될 위치에 대해 생각하지 않고 일반 PHP 코드를 계속 작성하기 때문에 - 바로 여기 또는 인접한 PHP 스크립트 또는 시스템의 다른 쪽 끝, 다른 데몬 적어도 다른 프로그래밍 언어에서는. 공개 API를 사용하여 프로젝트를 만드는 경우 타사 참가자는 자신의 코드를 이벤트에 "서명"(및 처리)할 수 있으며 그 반대의 경우도 마찬가지입니다. 요청(Amazon과 같은 종량제 SAAS 비즈니스 모델을 사용하는 경우 지불).

우리가 고전적인 대형 웹 프로젝트의 주요 단점이라고 부르는 것을 기억하십시오. 지속적으로 성장하는복잡성, 따라서 소유 비용, 지원 및 변경 비용. 이벤트 기반 SOA 아키텍처의 경우 - 복잡성이 지속적으로 감소하고 있습니다., "복잡한 노드"는 독립적인 서비스(이 경우 악마)로 쉽게 나눌 수 있지만 시스템의 원칙은 변경되지 않고 성능만 향상됩니다.

현재 프로세스를 잃지 않고 새 버전 배포

더 이상 모놀리식 시스템이 없으므로 전체 시스템을 배포할 필요가 없습니다. 또한 구성 요소(서비스, 데몬)의 배포는 물론 합리적인 한도 내에서 시간이 걸릴 수 있습니다. 중요한 것은 구성 요소의 배포 시간(수초 또는 수십 초) 동안 전체 프로젝트가 잠시 서비스를 중단하지 않는다는 것입니다. 어떻게 완료되었나요?

업데이트가 필요한 서비스를 끄기만 하면 됩니다. 코드와 데이터베이스 구조를 업데이트한 다음(필요한 경우) 다시 실행합니다. 이 서비스에 대한 모든 현재 요청은 서비스가 나타날 때까지 AMQP 대기열에서 대기합니다. 서비스가 작기 때문에(비즈니스 로직의 작은 부분만 해결하는 데 필요한 소량의 코드) 이것은 전체 모놀리식 애플리케이션을 배포하는 것보다 훨씬 빠릅니다. 그러나 어떤 경우에도 손실은 없을 것입니다.

웹 인터페이스 문제

빠르고 반응이 빠른 웹 인터페이스는 고부하 프로젝트의 전제 조건입니다. 웹 인터페이스가 일반적으로 구현에 대한 고전적인 접근 방식을 사용할 때 "속도가 느려지는" 이유를 살펴보겠습니다.

1. 인터페이스는 백엔드에 그려지며 과부하가 걸리고 느리게 수행됩니다. 페이지 간 전환이 느립니다. AJAX를 사용하더라도 블록 다시 그리기가 너무 느리게 차단됩니다.

2. 인터페이스의 소스 코드(HTML, CSS, JS)는 중복되고 통신 채널을 통해 느리게 전송됩니다. 특히 인터페이스를 통해 사용자가 탐색하는 동안 각 페이지를 로드할 때 수행되는 경우에 그렇습니다.

3. 인터페이스에 최적화되지 않은 JavaScript 로직이 많이 포함되어 있어 약한 장치(주로 모바일 장치)에서 느립니다.

다음 문제를 해결해 보겠습니다.

빠르고 반응이 빠른 웹 인터페이스를 만드는 방법

1. 먼저 가장 중요한 것은 원천인터페이스는 클라이언트에게 한 번 이상 전송되지 않음. 이를 달성하기 위한 유일한 문명화된 방법은 본격적인 JavaScript 애플리케이션을 만드는 것입니다. 클라이언트에 한 번 다운로드되고(이 경우 아름다운 애니메이션 프리로더를 표시할 수 있음) 서비스를 사용하는 전체 시간 동안 클라이언트는 더 이상 다운로드를 기다릴 필요가 없습니다.

2. 웹 인터페이스의 "화면" 사이의 모든 전환은 수행되어야 합니다. 자바스크립트 애플리케이션 내부, 어떠한 경우에도 백엔드에 대한 별도의 요청으로 처리되지 않습니다. "단일 페이지 웹 응용 프로그램"이라는 용어가 있는데, 기본적으로 "화면"을 전환하여 탐색이 수행되는 반면 콘텐츠는 주소 표시 줄동적으로 변경되어 고전적인 "페이지 전환"의 전체 느낌을 만듭니다.

3. 백엔드로 메시지(이벤트)를 보내고 응답을 받는 것은 다음과 같아야 합니다. 서로 및 사용자 탐색에서 분리됨(비동기). 앞서 언급한 WebSocket은 본질적으로 그러한 구현을 "권장"합니다. 특별히 그렇게 하지 않는 한 모든 긴 작업은 인터페이스를 차단하지 않아야 합니다.

따라서 사용자는 응용 프로그램의 초기 다운로드(몇 초) 동안에만 인터넷 연결이 필요합니다. 또한 일시적으로 연결이 끊긴 경우에도 서비스와 함께 작동할 수 있습니다. 인터넷이 나타나자마자 보내야 하므로 같은 방법으로 응답을 받게 됩니다.

당연히 이것은 개발자가 코드를 최적화하고 최소화해야 하는 필요성에서 해방되지 않습니다. 그러나 실습에서 알 수 있듯이(예: Trello 서비스) 이 작업은 다른 작업보다 어렵지 않습니다.

의심스러운 모바일 장치용 웹 서비스 개발자를 위한 참고 사항: 저자의 관행에 따르면 2013년 웹 소켓 전송의 단일 페이지 JavaScript 응용 프로그램은 iPad에서 성공적으로 작동합니다.

여러 장치에서 사용자 작업

직장에서 그는 직장에서 귀하의 서비스를 사용합니다 데스크탑 컴퓨터, 집에 오는 길에 그는 iPhone을 꺼내고 집에서 태블릿을 켭니다. 사용자가 인터페이스에서 서비스로 명령을 보낸 경우 처리 결과에 대한 응답을 기다리고 있습니다. (언제) 처리에 어느 정도 시간이 걸렸다면 응답이 그 순간에 사용자가 사용하는 장치로 보내져야 한다는 것을 이해하기 쉽습니다(말장난 죄송합니다) 응답 전달, 요청 당시가 아닙니다.

문제는 사용자가 이것 또는 저것을 사용을 중단했는지(다시 한 번 죄송합니다) 명확하게 말할 수 없다는 것입니다. 특정 장치. 아마도 그는 브라우저를 닫았을 것입니다. 그의 배터리가 방전되었을 수 있습니다. 아마도 연결이 되지 않는 지하철 터널로 가다가 30분 후에 다시 나타날 것입니다. 옵션이 많고 작성자가 불명 가장 좋은 방법정의. 그러나 다음은 유용할 수 있습니다.

1. (백엔드에서) 모든 사용자의 장치와 각 장치의 마지막 활동 시간을 기록합니다.

2. 사용자에게 보고해야 하는 시스템 이벤트를 활성 장치에만 전달해야 하는 이벤트와 "브로드캐스트"(모든 장치에) 전달해야 하는 이벤트로 분류합니다.

3. 입력 추가 레이어추상화 - 사용자에게 흥미로운 특정 이벤트를 가로채서 메시지를 형성하는 서비스입니다. 따라서 작업의 성공에 대한 동일한 메시지를 여러 유형으로 쉽게 브로드캐스트할 수 있습니다. 휴대 기기, 조금 더 확실한 - 브라우저에 대한 자세한 메시지 - by 이메일.

4. 각 개별 통신 채널(웹 인터페이스, 모바일 장치, 메일)에서 각 사용자에게 보낼 대기열을 제공합니다. 표준 AMQP 기능은 또한 메시지 만료 시간 초과를 도와 특정 시간보다 오래 있지 않고 시스템을 방해하지 않도록 합니다. 사용자가 특정 채널을 통해 온라인 상태가 되면 해당 특정 유형의 보류 중인 최신 메시지가 사용자에게 전달됩니다.

저자는 동일한 시스템을 기반으로 지연된 알림 전송(특정 날짜 이전에 전송됨)과 실제 종이 정기 서신(행위, 지불 등)을 보내는 것이 가능하다고 덧붙일 수 있습니다. ), 그러나 이것은 별도의 기사에 대한 주제입니다.

나는 중요한 것을 강조 할 것입니다. 전달 된 메시지를 Facebook 또는 Vkontakte에서 익숙한 일종의 알림으로 간주하지 마십시오. 전달된 메시지는 쿼리 결과사용자! 백엔드에 대한 일종의 요청을 암시하는 인터페이스의 모든 사용자 작업은 단일 통합 커뮤니케이션 채널을 통해 "전달된 메시지"의 균일한 형태로 응답을 수신합니다. 그런 다음 웹 애플리케이션 알고리즘은 이 메시지나 저 메시지로 수행해야 하는 작업을 이해합니다. 텍스트로 알림을 그리고, 테이블에 한 줄을 추가하고, 인터페이스에서 무언가를 전환하는 등의 작업을 수행합니다.

병렬 및 순차 컴퓨팅

백엔드가 느린 경우 빠른 프론트엔드 웹 인터페이스를 설계하는 것은 쓸모가 없습니다. 아니요, 이것은 스트림에 관한 것이 아니라 포크에 관한 것이 아니라 Erlang에 관한 것이 아닙니다. 우리는 모든 초보자/중급 프로그래머가 사용할 수 있는 일반적인 PHP를 유지합니다.

병렬 처리가 필요한 이유는 무엇입니까? 일반적으로 단일 스레드 언어의 단점에 대해 이야기하지 않더라도 병렬 처리는 작업 계산 속도를 크게 높여 하드웨어 리소스(하드웨어)에 대한 요구 사항을 크게 줄이고 작업에 대한 사용자 만족도를 높입니다. 인터페이스(결과를 더 빨리 얻음).

웹 프로젝트에서 상당히 복잡한 비즈니스 프로세스를 취하여 일련의 단계로 그립니다. 요청에서 응답까지 시스템에서 일련의 작업을 받게 됩니다. 대부분의 경우 먼저 몇 가지 검사를 수행한 다음 기본 작업을 실행한 다음 보조 하위 작업을 수행하고 마지막으로 결과를 출력합니다. 자세히 살펴보십시오. 병렬로 수행할 수 있는 작업이 있습니까?

예를 들어보겠습니다. 사용자가 요금제에 추가 유료 옵션으로 포함된 일부 서비스를 구매하려고 한다고 가정해 보겠습니다. 옵션의 수는 제한되어 있습니다. 옵션이 성공적으로 켜져 있으면 브라우저에서 사용자에게 알림을 보내고, 중복 이메일을 보내고, 청구 계정에서 금액을 공제하고, 고객 부서에 알려야 합니다. 체인 그리기:

1. 시스템이 옵션을 활성화하라는 요청을 받았습니다.
2. 우리는 사용자를 승인하고 그의 요금제를 찾습니다.
3. 이 옵션을 활성화할 수 있는지 여부를 확인합니다. 요금제사용자.
4. 사용자의 계정에 충분한 돈이 있는지 확인합니다.
5. 이 옵션이 다른 설정과 충돌하는지 확인합니다.
6. 모든 것이 정상이면 옵션을 활성화합니다.
7. 브라우저에 알림을 보냅니다.
8. 우편으로 알림을 보냅니다.
9. 청구서에서 돈을 상쇄하십시오.
10. 우리는 고객 부서에 통지합니다.

주의 깊은 독자는 일련의 동작에서 오류를 발견할 수 있지만 저자는 이것이 대략적인 예임을 상기시켜 줍니다.

우리는 무엇을 봅니까? 모든 단계를 순차적으로 수행할 이유가 없습니다. 3,4,5를 3개의 스레드로 "병렬화"하고 결국 7,8,9,10을 4개의 스레드로 "병렬화"하는 것이 훨씬 더 정확합니다.

흐름과 포크에 대해 생각하고 계십니까? 헛되이, 당신은 SOA를 가지고 있습니다!

SOA에서 병렬 컴퓨팅을 수행하는 방법

이 시점까지 기사를 스크롤한 독자를 위해 SOA에서 동일한 작업을 병렬화하는 것이 아님을 설명하겠습니다. 이를 위해 일반적으로 N 인스턴스에서 데몬을 실행하고 다음을 처리하는 것으로 충분합니다. 데이터베이스에 대한 액세스 경합.

따라서 이 예에는 서로 다른 서비스에서 수행하고 병렬로 실행하려는 3-4개의 서로 다른 작업이 있습니다. 병렬 처리로 보내는 것은 어렵지 않습니다. "사용자 이름 사용자가 옵션 X를 활성화할 수 있습니까?"라는 하나의 이벤트를 보내는 것으로 충분하며 이 이벤트를 구독하는 모든 서비스는 이를 포착하고 검사를 수행하고 결과 이벤트를 전달합니다.

문제는 계속 진행하기 위해 작업의 전체 결과가 필요할 때 이러한 결과 이벤트를 수집하는 것입니다. 예를 들어 위의 목록에서 결과 3+4+5가 필요하고 7+8+9+10은 무시할 수 있습니다.

사실 트랜잭션의 필수 완료에 대한 요구 사항이 높기 때문에 각 체인을 끝까지 제어해야하지만 이에 대해서는 나중에 다루겠습니다.

당연히 데몬이 "멈추고 대기"하여 리소스를 소비하는 경우(소위 "유휴"), 이와 같이 부하가 높은 서비스를 구축할 수 없습니다. 요점은 악마가 다른 작업을 해결하고 다른 클라이언트의 다른 요청을 처리하는 반면 세 개의 개별 "스레드"(3,4,5)가 하위 작업을 해결하는 데 관여한다는 것입니다. 결과 이벤트가 임의의 순서로 올 수 있다는 사실로 인해 어려움이 추가됩니다. 그러나 이 모든 것이 쉽고 간단하게 해결됩니다.

저자가 아는 한, 현재 존재하는 즉시 사용 가능한 AMQP 구현 중 어느 것도 여러 이벤트를 예상하고 하나의 결과만 받기 위해 하나로 "접착"하는 것을 허용하지 않습니다. 따라서 다음과 같이 스스로 처리해야 합니다.

1. AMQP에 이벤트를 보내기 전에 빠른 기억(적절한 메모리 내 저장소를 사용) 서비스가 수신할 것으로 예상되는 결과 이벤트의 이름 목록과 결과.

2. 그 후, 서비스는 현재 이벤트의 처리 주기를 종료하고 다음 작업을 위해 해제됩니다.

3. 우리가 메모리에 저장한 목록의 이벤트가 도착하는 즉시, 서비스는 그것을 "풀고" 그 내용을 이벤트 이름에 귀속시켜 메모리에 저장합니다. 동시에 이 목록에 응답이 수신되지 않은 다른 이벤트가 있는지 확인합니다. 그렇다면 루프는 해당 지점에서 끝납니다.

4. 그렇지 않으면, 즉, 목록의 모든 이벤트가 응답을 수신하면 서비스가 이들을 함께 연결하고 이전에 기억한 "R" 이벤트 이름으로 접착된 결과를 전달합니다. 그 후, 메모리를 저장하기 위해 결과가 포함된 목록이 메모리에서 삭제되기만 하면 더 이상 필요하지 않습니다.

5. 동일한 서비스 또는 다른 서비스(시스템 설계자의 재량에 따라)는 병렬 처리의 모든 결과와 함께 결과 이벤트 "R"을 수신합니다. 다음은 분명합니다.

설명에서 이것이 오랜 시간이라고 생각되면 설명하겠습니다. 하나의 평균 서버에서 초당 수천 수만 개의 이벤트 (!)에 대해 이야기하고 있습니다.

인메모리 스토리지의 사용은 서비스가 중단(폴, 업데이트)되더라도 현재 비즈니스 프로세스가 손실되지 않음을 의미합니다. 서비스가 다시 시작된 후 ESB에서 이벤트를 계속 수신하고 위에서 설명한 알고리즘에 따라 처리합니다.

SOA의 트랜잭션, 작업 롤백(롤백) 및 실패 시나리오

SOA에서 피어 이벤트는 ESB를 "통과"하기 때문에 "여기에 있는 이 답변"이 "저쪽에 있는 해당 요청"을 나타냄을 나타내는 일종의 표시가 필요합니다. 여기에서 바퀴를 재발명할 필요가 없습니다. 널리 사용되는 프로토콜의 사양에서 correlation_id와 같은 이름을 가진 매개변수를 찾을 수 있습니다. 일반적으로 이것은 문자열입니다. 이 비즈니스 프로세스에 속하는 메시지 체인을 식별하기 위해 진입에서 퇴장까지 각 개별 비즈니스 프로세스의 모든 이벤트 매개변수에 포함되어야 합니다. 웹 인터페이스 측면에서 보면 웹 애플리케이션은 현재 활성(보낸) 요청을 저장하고 correlation_id를 통해 각 특정 응답이 어떤 요청에 응답했는지 "이해"합니다.

용어를 다루도록 하겠습니다. 트랜잭션성은 여러 작업을 하나의 공통 작업으로 수행하는 시스템의 속성이며, 이는 의미가 있고 완전히 완료되어야 합니다. 병렬 스레드가 있는 분산 시스템에서 여러 작업을 원자적으로 수행하는 것은 물리적으로 불가능하기 때문에 시스템은 소위 실패 시나리오 및 롤백을 제공합니다.

실패 시나리오는 일반적으로 오류가 발생했을 때 취하는 조치입니다. 이 컨텍스트에서 롤백은 궁극적으로 오류로 이어진 일련의 이전 작업을 "실행 취소"하기 위해 수행해야 하는 작업 스크립트입니다. 대략적으로 말하면 롤백은 시스템의 일반 비즈니스 프로세스의 반대 프로세스입니다.

롤백이 항상 필요한 것은 아니며 항상 가능한 것도 아닙니다. 예를 들어, 일부 옵션을 사용자에게 연결한 다음 청구서에 "떨어진" 경우 옵션을 다시 비활성화할 수 있습니다. 그러면 자동으로 또는 사용자의 두 번째 명령으로 다시 시도할 수 있습니다. 그리고 콘텐츠를 물리적으로 삭제했는데 일부 후속 작업이 작동하지 않았다면... 상황이 애매모호하다.

따라서 실패 시나리오 및 롤백에 현명하게 접근해야 합니다. 작성자는 다음 경로를 권장할 수 있습니다. 항상 실패 스크립트를 작성하지만 항상 롤백을 작성하지는 않습니다. 귀하의 실패는 모니터링에 즉시 반영되며 지원 팀은 손으로 상황을 신속하게 수정하고 경험을 쌓고 프로그래머를 위한 기술 요구 사항을 공식화할 수 있습니다. 처음부터 고유하게 올바른 롤백을 작성하는 것은 매우 어려울 수 있습니다.

그럼에도 불구하고 롤백, 롤백, 잘못된 상황 처리는 다른 모든 것과 동일한 비즈니스 프로세스임을 이해해야 합니다. 또한 시스템을 통과하는 이벤트를 기반으로 합니다. 특정 작업에서 구현하기 위해 각 이벤트와 각 핸들러에서 처음에 두 개의 반대 경로(정방향 및 역방향, 성공 및 실패)를 설정하면 좋을 것입니다.

SOA 확장

조만간 모든 시스템을 확장해야 합니다. SOA의 경우 이는 쉽고 자연스럽게 수행됩니다.

1. 중복 진입점. 이것은 기사 시작 부분에서 고려한 것과 동일한 WebSocket 게이트웨이를 나타냅니다. 클라이언트와 클라이언트 간의 통신이 시스템 내부에서 통합 및 분리되고 클라이언트와 시스템 간의 통신이 차례로 클라이언트와의 통신에서 분리되기 때문에 무제한으로 복제할 수 있습니다.

2. 중복 인스턴스(인스턴스) 서비스. 데이터베이스가 필요하지 않거나 데이터베이스에서 "읽기"만 가능한 서비스는 원활하게 복제됩니다. 그리고 RabbitMQ의 일반 기능을 사용하면 N개의 인스턴스를 동일한 대기열에 등록할 수 있습니다. 이 대기열에서 메시지는 하나 또는 다른 인스턴스에 무작위로 도착합니다. 외부 응용 프로그램(데이터베이스, 타사 소프트웨어)을 처리하는 서비스를 복제할 때 이러한 응용 프로그램이 여러 병렬 클라이언트의 트랜잭션 요청을 제공하는 방법을 고려해야 합니다.

3. 데이터 저장소 복제. 여기에서 당신이 알고 있는 모든 샤딩을 자유롭게 사용할 수 있습니다. 천만 명의 사용자를 처리하고 있고 그것이 당신에게 많은 것처럼 보인다면 천만 기반으로 나누십시오(예를 들어, 사용자 로그인의 CRC32 또는 다른 라운드 로빈 방법을 기반으로 함). 한 서비스의 데이터베이스가 지속적으로 증가하고 복잡해지면 두 개의 서비스로 분할하십시오.

4. AMQP 브로커 복제및 인메모리 스토리지. 저자의 연습에서 RabbitMQ와 Redis는 완벽하게 역할을 수행합니다. 둘 이상의 DC 랙에 장비가 있는 경우 네트워크 연결 실패를 허용하는 토끼 모드를 선택하십시오.

5. 전체 기계 복제. 에서 현대 기술가상화(KVM) 및 구성(Chef), "동일한 머신 올리기" 작업은 버튼 하나만 누르면 됩니다.

프론트엔드와 백엔드 간의 트래픽 암호화

SSL을 통해 WebSocket 연결을 구성하는 것이 좋습니다. 또한 HTTP[S]를 제외한 "이상한 트래픽"을 차단하는 백워드 오피스 공급자에 대한 "침투"를 증가시킵니다.

그러나 이것이 충분하지 않다고 생각되면 각 클라이언트 로그인과 함께 키 쌍 생성을 정렬하고(프론트 엔드에 한 쌍, 백엔드에 두 번째 쌍) 공개 키를 교환하고 일반 RSA로 모든 트래픽을 추가로 암호화할 수 있습니다.

DDOS 및 유사한 남용에 대한 보호

수백 기가비트의 SYN 플러딩 및 플러딩 채널과 관련하여 저자는 이에 대해 수백 권의 전문 문헌이 작성되었기 때문에 "낮은 수준" 보호 문제를 의도적으로 생략합니다. 공격자가 수천 개의 이벤트로 시스템(SOA + ESB)을 "플러드"하는 방법을 찾았을 때 논리적 수준에서 이미 내부에 있는 시스템을 보호하는 방법에 대해 이야기해 보겠습니다.

1. 첫 번째 규칙: 유효성이 확인될 때까지 아무 것도 처리해서는 안 됩니다. 입력으로 BASE64로 래핑된 작은 JSON 텍스트를 예상하는 경우 메가바이트보다 긴 수신 문자열은 명시적으로 버려야 합니다. 압축을 풀려고 하지 마십시오. "비라틴어" 문자를 포함하는 문자열도 비슷합니다. 문자열의 압축을 풀었을 때 바로 json_decode를 시도하지 말고 먼저 괄호의 숫자와 쌍을 확인하십시오. 등등.

편집증처럼 보이지만 그렇지 않으면 쉽게 "메모리에서 가득 찰" 수 있습니다. 즉, 서비스가 실패하여 사용 가능한 모든 RAM을 차지하게 됩니다.

2. 들어오는 메시지를 처리하는 서비스는 메모리, 데이터베이스 및 기타 저장소에 아무 것도 쓰지 않아야 합니다. 이유는 동일합니다. 먼저 메시지가 전체적으로 유효한지 확인한 다음 시스템에 "깊이" 들어가도록 합니다.

3. 강제로 "데이터베이스로 이동"할 수 있는 서비스는 캐싱으로 보호해야 하는 경우가 많습니다. 간단한 예는 사용자 인증 서비스입니다. 보호되지 않으면 공격자는 수천 개의 권한 부여 요청을 연속으로 보내 데이터베이스에 과부하를 줄 수 있습니다.

4. 시스템 입구에서 "검은 목록"의 IP 주소와 같은 "의심스러운" 출처의 요청을 거부하는 서비스가 필요합니다.

고전적인 조언처럼 들립니다. 그래서 무슨 일이죠? 주요 문제는 시스템 입구에 분석기 필터링 서비스를 배치하면(클래식 웹 프로젝트에서와 같이) 전체 시스템의 성능이 이 분석기 필터의 성능보다 높지 않을 것입니다. "실시간"으로 의심되는 보고서를 검토하는 것은 매우 비용이 많이 듭니다. 사실 이후에 이 작업을 수행할 수 있고 수행해야 합니다. 방법은 다음과 같습니다.

1. 시스템의 모든 메시지를 "듣는" 서비스를 만드십시오. RabbitMQ에서 이것은 라우팅 키 "#"을 구독함으로써 달성됩니다.

2. "의심스러운" 발신자를 진단할 수 있는 특정 규칙을 이 서비스에 가르칩니다. 예를 들어, 일정 시간 간격으로 너무 많은 유사한 메시지를 보내거나 반복되는 메시지를 보내거나 동일한 사용자를 대신하여 다른 IP 주소에서 메시지를 보내는 발신자입니다... 많은 옵션이 있습니다. 상상력. 동시에 그러한 서비스가 얼마나 빨리 작동하는지 (물론 합리적인 한도 내에서) 중요하지 않습니다. 전체 시스템의 속도에 영향을 미치지 않습니다.

3. 서비스는 이러한 발신자가 의심스러운 것으로 판단되는 즉시 이 이벤트를 시스템에 전달하고 계속해서 업무를 수행합니다.

4. 입력에 매우 간단하고 빠른 데몬을 넣습니다. 필터링 서비스의 작업은 단순히 의심스러운 발신자를 "어리석게" 차단하는 것입니다. 분석, 구문 분석, 추가 비용이 없습니다. 의심스러운 사람이 누구인지 쉽게 추측할 수 있습니다. 서비스는 이전 단락에서 설명한 이벤트를 통해 이에 대해 학습하고 내부 블랙리스트에 추가합니다.

1부 끝. 계속: SOA: 분산 아키텍처 및 유지 관리.

저는 IT 프로젝트의 멘토입니다. 이는 귀하가 소유자 또는 리더라면 제가 새로운 차원으로 나아가도록 도울 수 있음을 의미합니다. 프로세스를 정리하고, 팀의 동기를 이해하고, 도구를 구현하고, 특정 목표를 달성합니다. 나는 장사하는 법을 가르치는 것이 아니라 길에 아낌없이 흩어져 있는 갈퀴를 돌아다니는 데 도움을 줄 뿐입니다. .