Советы Nginx для фронтенда

Nginx

Автор: Му Тонг

вышеупомянутый

Nginx знаком большинству разработчиков, корпоративные команды используют его для создания шлюзов запросов, а мы используем его в частном порядке, чтобы «научно выходить в интернет» (предупреждение о ценности). Но для фронтенда большая часть разработки просто сосредоточена на бизнесе в будние дни, и нет необходимости или возможности задействовать контент Nginx, что приводит к тому, что мы очень мало знаем о нем. С ростом популярности безсерверных технологий все больше и больше людей считают, что они могут легко и быстро реализовать свои собственные технические идеи без каких-либо знаний об эксплуатации и обслуживании.

Однако на самом деле это не так.Появление Node заставило фронтенд-инженеров заняться бэкендом.Мы можем поддерживать некоторые сервисы BFF самостоятельно.Даже если это просто какие-то простые приложения, вы необходимо овладеть определенными навыками эксплуатации и обслуживания. С другой стороны, в условиях быстро меняющейся системы разработки программного обеспечения некоторые границы между различными обязанностями становятся все более и более размытыми, а углубление концепции DevOps также заставило нас обратить внимание на эксплуатацию и обслуживание приложений и начать думать о новая система Как построить комплексный проект. Поэтому фронтенд-разработчикам очень необходимо знать некоторые простые и удобные навыки работы с Nginx.

Так называемое «больше навыков не перегружает тело», пока вы еще думаете, учиться или нет, некоторые люди уже закончили обучение.

Что такое Nginx

Nginx — это высокопроизводительное, надежное ПО промежуточного слоя и прокси-сервис с открытым исходным кодом. Nginx (произносится как двигатель x) — это веб-сервер, который также можно использовать в качестве обратного прокси-сервера, балансировщика нагрузки и кэша HTTP.

Это классический обзор. «Высокая производительность» Nginx в основном выражается в поддержке массовых одновременных служб веб-сервера, а «надежность» означает высокую стабильность и высокую отказоустойчивость.В то же время, поскольку архитектура Nginx основана на модулях, мы можем использовать встроенные в модулях и сторонних модулях.Бесплатная комбинация для создания сервисов Nginx, которые адаптируются к вашему бизнесу. Из-за этого Nginx настолько популярен, что может широко появляться в корпоративных командах любого размера и стать важным участником технической системы.

Для Nginx есть много вещей, которые мы можем изучить подробно, но для фронтенд-разработчиков мы можем быть знакомы с освоением и написанием основных файлов конфигурации Nginx.nginx.confна самом деле удалось решить 80% проблемы.

Docker быстро создает сервис Nginx

Классические шаги для ручной установки Nginx — это «четыре подтверждения, две установки и одна инициализация».Процесс громоздкий и простой, но с Docker нам вообще не нужно быть таким хлопотным. Docker — это движок контейнера приложений с открытым исходным кодом, основанный на Golang, который помогает разработчикам упаковывать свои приложения и зависимости в легкий и портативный контейнер-песочницу, поэтому мы можем использовать Docker для простого локального создания службы Nginx, полностью пропустив процесс установки. Я не буду здесь вдаваться в подробности о Docker, заинтересованные студенты могут изучить его самостоятельно.Docker.

Для простоты демонстрации мы используем более эффективный Docker-Compose для создания нашего сервиса Nginx. Docker-Compose — это инструмент командной строки, предоставляемый Docker для определения и запуска приложений, состоящих из нескольких контейнеров. Используя Docker-Compose, мы можем декларативно определить различные службы приложения через файлы YAML и завершить создание и запуск приложения с помощью одной команды.

Для выполнения следующих операций сначала нужно установить Docker, в разных операционных системах по разномуУстановитьСпособ.

После того, как среда на месте, мы создаем новый проектnginx-quick, создайте новый в корневом каталогеdocker-compose.ymlфайл, который является файлом конфигурации для Docker-Compose:

version: "3"

services:
  nginx: # 服务的名称
    image: nginx
    volumes: # 文件夹映射
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf # nginx 配置文件
    ports: # 端口转发
      - "8080:80"

Определяем набор сервисов nginx, запускающих докер-контейнер. Изображение, соответствующее контейнеру,nginx, порт запуска службы Nginx в контейнере — 80, а порт внешнего доступа — 8080. При этом ставим локальный кастомный файл конфигурации Nginx./nginx/nginx.confСоответствует синхронизации с контейнером/etc/nginx/nginx.confдорожка.

новыйnginx/nginx.conf:

# 全局配置
user  nginx;         # 配置用户或者组
worker_processes  1; # 允许生成的进程数

error_log  /var/log/nginx/error.log warn; # 错误日志路径,warn 代表日志级别,级别越高记录越少
pid        /var/run/nginx.pid;            # Nginx 进程运行文件存放地址

events {
  accept_mutex on;          # 设置网路连接序列化,防止惊群现象发生
  multi_accept on;          # 设置一个进程是否同时接受多个网络连接
  worker_connections  1024; # 每个进程的最大连接数,因此理论上每台 Nginx 服务器的最大连接数 = worker_processes * worker_connections
}

# HTTP 配置
http {
  include       /etc/nginx/mime.types;    # 文件扩展名与文件类型映射表
  default_type  application/octet-stream; # 默认文件类型

  log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"'; # 日志格式

  access_log  /var/log/nginx/access.log  main; # 访问日志路径

  sendfile        on; # 允许 sendfile 方式传输文件

  keepalive_timeout  65; # 连接超时时间

  server {
    listen       80;         # 监听端口
    server_name  localhost;  # 监听地址

    location / {                    # 请求的url过滤,正则匹配
      root   /usr/share/nginx/html; # 根目录
      index  index.html index.htm;  # 默认页
    }
  }
}

Это самая базовая конфигурация Nginx, соответствующие элементы конфигурации и соответствующие подробные пояснения можно увидеть в комментариях, здесь мы просто настраиваемlocalhost:80Мониторинг доступа (обратите внимание, что локальный хост здесь не локальный, он находится внутри контейнера).

воплощать в жизньdocker-compose up -dсоздать сервис, доступlocalhost:8080Вы можете увидеть домашнюю страницу Nginx по умолчанию.Welcome to nginx!.

воплощать в жизньdocker exec -it nginx-quick_nginx_1 bashВойдите внутрь контейнера и выполнитеcat /etc/nginx/nginx.conf, вы можете видеть, что наш пользовательский файл конфигурации Nginx успешно перезаписывает конфигурацию Nginx по умолчанию.

Настройка HTTP для Nginx

Конфигурация HTTP является наиболее важной частью настройки Nginx, а также наиболее часто используемой частью практических навыков работы с Nginx. Конфигурация HTTP Nginx в основном делится на три уровня контекста: http — сервер — местоположение.

http

http в основном хранит конфигурацию на уровне протокола, включая часто используемые конфигурации сетевых соединений, такие как тип файла, ограничение времени соединения, хранилище журнала и формат данных, которые действительны для всех служб.

server

server — это конфигурация виртуального хоста, которая в основном хранит конфигурацию уровня службы, включая адрес службы и порт, формат кодирования, а также корневой каталог по умолчанию и домашнюю страницу службы. Некоторые специальные конфигурации могут принадлежать всем уровням контекста, напримерcharest(Формат кодирования)access_log(журнал доступа) и т.д., поэтому можно отдельно указать журнал доступа сервиса, если нет, то по умолчанию он будет наследоваться вверх.

location

location — это конфигурация на уровне запроса, которая определяет метод обработки запроса посредством регулярного сопоставления URL-адресов, в основном включая конфигурацию прокси-сервера, конфигурацию кеша и т. д. Правила синтаксиса для конфигурации местоположения в основном следующие:

# location [修饰符] 匹配模式 { ... }
location [=|~|~*|^~] pattern { ... }

1) Если модификатор отсутствует, это означает, что префикс пути совпадает. В следующем примере сопоставлениеhttp://www.jd.com/testа такжеhttp://www.jd.com/test/may.

server {
  server_name www.jd.com;
  location /test { }
}

2)=Указывает, что путь точно совпадает, пример ниже соответствует толькоhttp://www.jd.com/test.

server {
  server_name www.jd.com;
  location = /test { }
}

3)~Указывает, что обычное сопоставление должно учитывать регистр. В следующем примере сопоставлениеhttp://www.jd.com/test, но не совпадаетhttp://www.jd.com/TEST.

server {
  server_name www.jd.com;
  location ~ ^/test$ { }
}

4)~*Указывает, что для обычного сопоставления не требуется чувствительность к регистру. В следующем примереhttp://www.jd.com/test, что также соответствуетhttp://www.jd.com/TEST.

server {
  server_name www.jd.com;
  location ~* ^/test$ { }
}

5)^~Указывает, что если символ, следующий за символом, является наиболее подходящим, то правило принимается, и последующие поиски не выполняются.

Расположение Nginx имеет собственный набор соответствующих приоритетов:

  • сначала точное совпадение=
  • сопоставление префикса^~
  • Затем следуйте штатному соответствию порядка в файле~или~*
  • Последнее совпадение — префиксное совпадение без каких-либо модификаций.

Пример ниже,http://www.jd.com/test/mayХотя два правила местоположения поражены, но из-за^~Совпадение имеет приоритет над~*совпадают, поэтому второе место будет иметь приоритет.

server {
  server_name www.jd.com;
  location ~* ^/test/may$ { }
  location ^~ /test { }
}

Практические советы по Nginx

Разобравшись с базовым синтаксисом Nginx, давайте взглянем на практические сценарии и навыки, которые Nginx может иметь в руках разработчиков интерфейса.

прямой прокси

Перенаправление прокси — наиболее распространенный сценарий использования Nginx, и прокси-сервер — один из них.

Получая доступ к прокси-службе, клиент пересылает запрос целевой службе, затем принимает ответ на запрос от целевой службы и, наконец, возвращает его клиенту.Это прокси-процесс. «Научный доступ в Интернет» — типичный прямой прокси, в этом процессе Nginx выступает в роли прокси-посредника.

Создаем новый в корневом каталогеweb/каталог, добавьтеindex1.html, в качестве домашней страницы доступа к целевой службе:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Web服务主页</title>
  <style>
  p {
    margin: 80px 0;
    text-align: center;
    font-size: 28px;
  }
  </style>
</head>
<body>
  <p>这是 Web1 服务的首页</p>
</body>
</html>

Исправлятьdocker-compose.yml, добавить службу Nginxweb1В качестве целевой службы используйте пользовательский HTML-код для переопределения HTML-кода домашней страницы по умолчанию, и в то же время мы используемlink: - web1:web1настроить прокси-сервисnginxи целевой сервисweb1Контейнерное соединение между:

version: "3"

services:
  nginx: # 服务的名称
    image: nginx
    links:
      - web1:web1
    volumes: # 文件夹映射
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf # nginx 配置文件
    ports: # 端口转发
      - "8080:80"
  web1:
    image: nginx
    volumes:
      - ./web/index1.html:/usr/share/nginx/html/index.html
    ports:
      - "80"

Измените конфигурацию местоположения Nginx, используйтеproxy_passАтрибут для пересылки запросов доступа к основному пути к целевой службеweb1:

// ...
location / {
  proxy_redirect off;
  proxy_pass http://web1; ## 转发到web1
}
// ...

Перезапустите контейнер, посетитеlocalhost:8080, мы видим, что прокси-служба успешно перенаправила наш запрос на целевую веб-службу:

Балансировка нагрузки

Прокси также включает в себя обратный прокси.Наиболее часто упоминаемая балансировка нагрузки в нашем бизнесе — это типичный обратный прокси. Когда трафик веб-сайта достигает определенного уровня, когда один сервер не может удовлетворить запрос пользователя, необходимо создать кластерную службу с несколькими серверами.В это время несколько серверов будут разумно распределять нагрузку, чтобы избежать возникновение определенной нагрузки на сервер.Большие простои и сервер простаивает.

Используйте возможности NginxupstreamКонфигурация, мы можем просто реализовать балансировку нагрузки. Для балансировки нагрузки требуется несколько целевых сервисов, поэтому мы используемwebновый каталогindex2.htmlа такжеindex3.html, в качестве домашней страницы доступа к недавно добавленной службе.

Исправлятьdocker-compose.yml, добавьте две услугиweb2а такжеweb3и установить соединение с контейнером:

# ...

services:
  nginx: # 服务的名称
    # ...
    links:
      # ...
      - web2:web2
      - web3:web3

  # ...

  web2:
    image: nginx
    volumes:
      - ./web/index2.html:/usr/share/nginx/html/index.html
    ports:
      - "80"
  web3:
    image: nginx
    volumes:
      - ./web/index3.html:/usr/share/nginx/html/index.html
    ports:
      - "80"

nginx.conf, мы создали восходящую конфигурациюweb-app,web-appНастроено три целевых сервиса, поэтому наш запрос пройдетweb-appПрокси к целевой службе. Nginx поставляется с различными стратегиями балансировки нагрузки, включая метод опроса по умолчанию, метод взвешивания, метод ip_hash на основе распределения IP-адресов, метод наименьшего_коннирования с наименьшим количеством подключений и т. д. Выбор стратегии зависит от различных сценариев бизнеса и параллелизма. Здесь мы используемleast_connСтратегии обработки отправки запросов.

// ...
upstream web-app {
  least_conn;   # 最少连接,选取活跃连接数与权重weight的比值最小者,为下一个处理请求的server
  server web1 weight=10 max_fails=3 fail_timeout=30s;
  server web2 weight=10 max_fails=3 fail_timeout=30s;
  server web3 weight=10 max_fails=3 fail_timeout=30s;
}

server {
  listen       80;         # 监听端口
  server_name  localhost;  # 监听地址

  location / {
    proxy_redirect off;
    proxy_pass http://web-app; ## 转发到web-app
  }
}
// ...

Перезапустив контейнер, вы обнаружите, что служба прокси перенаправляется на разные целевые веб-службы при выполнении нескольких запросов:

Server-side Include

Включение на стороне сервера (сокращенно SSI) — это простой интерпретируемый язык сценариев на стороне сервера, что означает, что при извлечении страницы сервер может выполнять синтаксический анализ инструкций SSI и добавлять динамически сгенерированный контент на существующую HTML-страницу. SSI является важным средством реализации модуляризации раннего Интернета. Он подходит для различных операционных сред и имеет более высокую эффективность синтаксического анализа, чем JSP. Он до сих пор широко используется на некоторых крупных веб-сайтах.

Использование SSI в HTML выглядит так:

<!--#include virtual="/global/foot.html"-->

Строка комментария, проанализированная SSI сервера, будет заменена на/global/foot.htmlСодержимое виртуального может быть абсолютным или относительным путем.

Nginx может легко и быстро поддерживать SSI, позволяя вашим страницам динамически импортировать другой HTML-контент. мы вwebСоздайте новую HTML-страницу в каталогеsinclude.html:

<style>
* {
  color: red;
}
</style>

Исправлятьweb/index1.html, а также команда SSI для представления фрагментов страницы./sinclude.html:

<head>
  <!-- ... -->
  <!--#include virtual="./sinclude.html"-->
</head>

Исправлятьdocker-compose.yml,Пучокsinclude.htmlТакже поместите его в корневой каталог доступа веб-сервиса:

version: "3"

services:
  nginx: # 服务的名称
    image: nginx
    links:
      - web1:web1
    volumes: # 文件夹映射
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf # nginx 配置文件
    ports: # 端口转发
      - "8080:80"
  web1:
    image: nginx
    volumes:
      - ./web/index1.html:/usr/share/nginx/html/index.html
      - ./web/sinclude.html:/usr/share/nginx/html/sinclude.html
    ports:
      - "80"

Наконец вnginx.confПросто настройте следующие два свойства в Nginx, чтобы включить поддержку Nginx SSI, среди которыхssi_silent_errorsУказывает, что при обработке ошибки файла SSI необходимо вывести сообщение об ошибке:

location / {
  ssi on;
  ssi_silent_errors on; # 处理 SSI 文件出错时输出错误提示,默认 off
  
  proxy_redirect off;
  proxy_pass http://web1; ## 转发到web1
}

Эффект следующий, Nginx успешно разбирает инструкцию SSI и вставляет слайс страницы в HTML-страницу:

Следует отметить, что если здесь используется обратный прокси-сервер и имеется несколько веб-служб, убедитесь, что каждая веб-служба существует.sinclude.htmlфайл и путь одинаковы, потому что получитьindex.htmlИ доступ кsinclude.htmlСуществует два дистрибутива.Если не используется стратегия ip_hash, он может быть перенаправлен на две разные службы, что приведет к отсутствию файлов фрагментов страниц.

GZIP-сжатие

Передача HTTP в основном основана на тексте, большое количество которых представляет собой некоторые статические файлы ресурсов, включая JS/CSS/HTML/IMAGE и т. д. Сжатие GZIP может сжимать содержимое во время передачи, снижать нагрузку на полосу пропускания и повышать скорость доступа пользователей.Это эффективное средство оптимизации производительности веб-страницы.

Использование NginxgzipКонфигурация свойства для включения GZIP-сжатия содержимого ответа:

location / {
  # ...
  gzip on;
  gzip_min_length 1k; # 大于1K的文件才会压缩
  
  # ...
}

gzip_min_lengthУкажите минимальный размер данных, допускающий сжатие. Указанное выше значение составляет менее 1 КБ без сжатия. Заголовок ответа на сжатый запрос содержит большеContent-Encoding: gzip.我们可以给 HTML 文件中多放点内容,这样才能让压缩效果更加明显,下边是 GZIP 开启前和开启后的效果对比:

1) до сжатия размер HTML 3,3 КБ

2) После включения сжатия GZIP размер HTML составляет 555 байт.

противоугонная цепь

В некоторых случаях мы не хотим, чтобы наши файлы ресурсов использовались внешними веб-сайтами. Например, иногда я копирую ссылки на изображения из службы изображений JD непосредственно в GitHub для использования. В настоящее время, если JD хочет отключить изображение доступ с GitHub, что я могу сделать? Это просто:

location ~* \.(gif|jpg|png|webp)$ {
   valid_referers none blocked server_names jd.com *.jd.com;
   if ($invalid_referer) {
    return 403;
   }
   return 200 "get image success\n";
}

Мы используем собственныйvalid_referersинструкция, проверка реферера выполняется для всех запросов изображения, толькоjd.comТолько запрос изображения под своим поддоменом может быть успешным, а остальные 403 запрещены, переменная$invalid_refererЗначение - результат проверки. Давайте проверим результаты доступа, и мы можем обнаружить, что запросы для нелегальных рефереров заблокированы и запрещены:

ECCMAC-48ed2e556:nginx-quick hankle$ curl -H 'referer: http://jd.com' http://localhost:8080/test.jpg
get image success
ECCMAC-48ed2e556:nginx-quick hankle$ curl -H 'referer: http://wq.jd.com' http://localhost:8080/test.jpg
get image success
ECCMAC-48ed2e556:nginx-quick hankle$ curl -H 'referer: http://baidu.com' http://localhost:8080/test.jpg
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.17.5</center>
</body>
</html>

HTTPS

Все знакомы с HTTPS. Это технология, которая вводит уровень SSL на основе HTTP для создания безопасного канала. Благодаря шифрованию и аутентификации содержимого передачи данные предотвращаются от захвата, подделки или хищения посредниками в процессе передачи. . Начиная с версии Chrome 62, HTTP-сайты с входными данными и все HTTP-сайты, просматриваемые в режиме инкогнито, автоматически помечаются как «небезопасные» сайты.Видно, что в условиях популяризации стандартов сетевой безопасности HTTPS является основной тенденцией будущих веб-сайтов.

Nginx может легко и быстро создать службу HTTPS, которая должна полагаться на модуль http_ssl_module.nginx -VМожно перечислить параметры компиляции Nginx, чтобы увидеть, установлен ли модуль http_ssl_module.

Чтобы построить HTTPS-сервис, вам нужно сгенерировать ключ и самоподписанный SSL-сертификат (для тестирования необходимо официально подписать сторонний доверенный SSL-сертификат), и нам нужно использовать библиотеку openssl. новыйnginx/ssl_certсодержание:

1) Сгенерировать ключ.key

openssl genrsa -out nginx_quick.key 1024

2) Создайте файл запроса подписи сертификата.csr

openssl req -new -key nginx_quick.key -out nginx_quick.csr

3) Создайте файл подписи сертификата.crt

openssl x509 -req -days 3650 -in nginx_quick.csr -signkey nginx_quick.key -out nginx_quick.crt

После выполнения этих трех шагов мы также сгенерировали ключ и SSL-сертификат, необходимые для HTTPS, и настроили их непосредственно наnginx.confсередина:

# ...
server {
  listen       443 ssl;    # 监听端口
  server_name  localhost;  # 监听地址

  ssl_certificate /etc/nginx/ssl_cert/nginx_quick.crt;
  ssl_certificate_key /etc/nginx/ssl_cert/nginx_quick.key;

  # ...
}

Исправлятьdocker-compose.yml, и перенесите файл пользовательского сертификата по соответствующему пути Nginx:

services:
  nginx: # 服务的名称
    # ...
    volumes: # 文件夹映射
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf # nginx 配置文件
      - ./nginx/ssl_cert:/etc/nginx/ssl_cert # 证书文件
    ports: # 端口转发
      - "443:443"

доступ после перезагрузкиhttps://localhost, и обнаружил, что страница помечена как небезопасный доступ Chrome, это связано с тем, что самозаверяющий сертификат является недействительным сертификатом, нажмите «Продолжить», чтобы получить доступ к странице в обычном режиме:

кеш страницы

Мы часто говорим, что кэширование страниц в основном делится на три категории: кэширование на стороне клиента, кэширование на прокси-сервере и кэширование на стороне сервера.

Когда Nginx выступает в качестве прокси, если большая часть полученных запросов представляет собой некоторые запросы, данные ответов которых не сильно меняются, например запросы статических ресурсов, использование кеша Nginx значительно улучшит скорость запросов. Кэширование в Nginx реализовано как иерархическое хранилище данных в файловой системе с настраиваемыми ключами кеша, а для управления тем, что попадает в кеш, можно использовать различные параметры запроса.

Использование Nginxproxy_cache_pathа такжеproxy_cacheЧтобы включить кэширование контента, первый используется для установки пути и конфигурации кеша, а второй используется для включения кеша:

http {
  # ...
  proxy_cache_path /data/nginx/cache levels=1:2 keys_zone=mycache:10m max_size=10g inactive=60m;

  server {
    # ...

    proxy_cache mycache;

    # ...
  }
}

Выше настраиваем кешmycacheи включите его на сервере:

1)/data/nginx/cacheУказан корневой каталог локального кеша;

2)levelУказывает, что структура каталогов кеша является двухуровневой, с максимум тремя уровнями, а число представляет длину имени, например1:2будет генерировать что-то вроде/data/nginx/cache/w/0dкаталог, для большого количества сценариев кэширования необходим разумный иерархический кэш;

3)keys_zoneУстанавливается общая область памяти,10mПредставляет размер области памяти, которая используется для хранения ключей кеша и метаданных, чтобы гарантировать, что Nginx может быстро определить, попал ли кеш, не извлекая диск;

4)max_sizeУстанавливается верхний предел кеша, по умолчанию нет предела;

5)inactiveУстанавливает максимальное время, в течение которого кэш может храниться, когда к нему нет доступа, то есть время деактивации.

конечные слова

Выше приведены некоторые простые и практичные сценарии применения Nginx и навыки использования.Для фронтенд-разработки Nginx по-прежнему необходимо иметь глубокое понимание. Однако перед лицом громоздкой и сложной конфигурации Nginx и неприглядных официальных документов многим приходится жаловаться, и даже если грамматика хорошо написана, они будут тратить много времени на устранение ошибок из-за различных проблем, таких как трудности отладки. . Вот рекомендацияИнструмент онлайн-генерации конфигурации Nginx, вы можете легко и быстро создать то, что вам нужноnginx.confКонфигурация, моя мама больше не должна беспокоиться о моем плохом изучении Nginx!


Если вы считаете, что этот контент ценен для вас, пожалуйста, поставьте лайк и подпишитесь на нашуОфициальный сайтА на нашем официальном аккаунте WeChat (WecTeam) каждую неделю публикуются качественные статьи:

WecTeam