公司代理背后的特使

Neu*_*ann 5 proxy http-proxy docker envoyproxy

我们计划在我的项目中使用 Envoy,所以我开始摆弄 Envoy github 上提供的简单示例,并且我在公司代理方面遇到了困难。

我尝试的第一件事非常简单,通过 Envoy 提供一个网站 www.onisep.fr(我特意选择了一个没有 HTTPS 的网站)。

我的单个 Envoy docker 镜像:

FROM envoyproxy/envoy:latest

ENV HTTP_PROXY http://mycompany.proxy:8080
ENV HTTPS_PROXY http://mycompany.proxy:8080

CMD apt-get update && apt-get -y install curl -y
CMD /usr/local/bin/envoy -c /etc/front-envoy.yaml --service-cluster front-proxy --log-level trace
Run Code Online (Sandbox Code Playgroud)

注意:注意代理配置

我的特使配置:

static_resources:
  listeners:
  - address:
      socket_address:
        address: 0.0.0.0
        port_value: 80
    filter_chains:
    - filters:
      - name: envoy.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager
          codec_type: auto
          stat_prefix: ingress_http
          route_config:
            name: local_route
            virtual_hosts:
            - name: simple_route
              domains:
              - "*"
              routes:
              - match:
                  prefix: "/"
                route:
                  prefix_rewrite: "/"
                  cluster: onisep
          http_filters:
          - name: envoy.router
            typed_config: {}
  clusters:
  - name: onisep
    connect_timeout: 10.0s
    lb_policy: round_robin
    type: strict_dns
    hosts:
      socket_address:
        address: onisep.fr
        port_value: 80
admin:
  access_log_path: "/dev/null"
  address:
    socket_address:
      address: 0.0.0.0
      port_value: 8001
Run Code Online (Sandbox Code Playgroud)

当我转发到公司网络上的网站时,该配置工作正常。但是一旦我使用该网络之外的地址,我就会遇到问题:

在我的浏览器中:

upstream connect error or disconnect/reset before headers. reset reason: connection failure
Run Code Online (Sandbox Code Playgroud)

在特使日志中:

front-envoy_1  | [2019-04-08 09:11:42.544][13][debug][main] [source/server/connection_handler_impl.cc:257] [C0] new connection
front-envoy_1  | [2019-04-08 09:11:42.544][13][trace][connection] [source/common/network/connection_impl.cc:440] [C0] socket event: 2
front-envoy_1  | [2019-04-08 09:11:42.544][13][trace][connection] [source/common/network/connection_impl.cc:508] [C0] write ready
front-envoy_1  | [2019-04-08 09:11:42.546][14][debug][main] [source/server/connection_handler_impl.cc:257] [C1] new connection
front-envoy_1  | [2019-04-08 09:11:42.546][14][trace][connection] [source/common/network/connection_impl.cc:440] [C1] socket event: 2
front-envoy_1  | [2019-04-08 09:11:42.546][14][trace][connection] [source/common/network/connection_impl.cc:508] [C1] write ready
front-envoy_1  | [2019-04-08 09:11:42.557][13][trace][connection] [source/common/network/connection_impl.cc:440] [C0] socket event: 3
front-envoy_1  | [2019-04-08 09:11:42.557][13][trace][connection] [source/common/network/connection_impl.cc:508] [C0] write ready
front-envoy_1  | [2019-04-08 09:11:42.557][13][trace][connection] [source/common/network/connection_impl.cc:478] [C0] read ready
front-envoy_1  | [2019-04-08 09:11:42.557][13][trace][connection] [source/common/network/raw_buffer_socket.cc:23] [C0] read returns: 718
front-envoy_1  | [2019-04-08 09:11:42.557][13][trace][connection] [source/common/network/raw_buffer_socket.cc:37] [C0] read error: Resource temporarily unavailable
front-envoy_1  | [2019-04-08 09:11:42.557][13][trace][http] [source/common/http/http1/codec_impl.cc:363] [C0] parsing 718 bytes
front-envoy_1  | [2019-04-08 09:11:42.557][13][trace][http] [source/common/http/http1/codec_impl.cc:476] [C0] message begin
front-envoy_1  | [2019-04-08 09:11:42.557][13][debug][http] [source/common/http/conn_manager_impl.cc:243] [C0] new stream
front-envoy_1  | [2019-04-08 09:11:42.557][13][trace][http] [source/common/http/http1/codec_impl.cc:331] [C0] completed header: key=Host value=server.mycompany.com
front-envoy_1  | [2019-04-08 09:11:42.559][13][trace][http] [source/common/http/http1/codec_impl.cc:331] [C0] completed header: key=Connection value=keep-alive
front-envoy_1  | [2019-04-08 09:11:42.559][13][trace][http] [source/common/http/http1/codec_impl.cc:331] [C0] completed header: key=Cache-Control value=max-age=0
front-envoy_1  | [2019-04-08 09:11:42.559][13][trace][http] [source/common/http/http1/codec_impl.cc:331] [C0] completed header: key=Upgrade-Insecure-Requests value=1
front-envoy_1  | [2019-04-08 09:11:42.559][13][trace][http] [source/common/http/http1/codec_impl.cc:331] [C0] completed header: key=User-Agent value=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36
front-envoy_1  | [2019-04-08 09:11:42.559][13][trace][http] [source/common/http/http1/codec_impl.cc:331] [C0] completed header: key=Accept value=text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
front-envoy_1  | [2019-04-08 09:11:42.559][13][trace][http] [source/common/http/http1/codec_impl.cc:331] [C0] completed header: key=Accept-Encoding value=gzip, deflate
front-envoy_1  | [2019-04-08 09:11:42.559][13][trace][http] [source/common/http/http1/codec_impl.cc:331] [C0] completed header: key=Accept-Language value=fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7
front-envoy_1  | [2019-04-08 09:11:42.559][13][trace][http] [source/common/http/http1/codec_impl.cc:442] [C0] headers complete
front-envoy_1  | [2019-04-08 09:11:42.559][13][trace][http] [source/common/http/http1/codec_impl.cc:331] [C0] completed header: key=Cookie value=_ga=GA1.2.1808164165.1542377204; _ga=GA1.4.1808164165.1542377204; sidebar_collapsed=false; grafana_user=admin; grafana_remember=11717b28d1f4b3391c33bc15fe3afcabd3cea6d0406b9f558aa868734280f02917; _gid=GA1.2.1626649956.1554713026
front-envoy_1  | [2019-04-08 09:11:42.559][13][trace][http] [source/common/http/http1/codec_impl.cc:463] [C0] message complete
front-envoy_1  | [2019-04-08 09:11:42.559][13][debug][http] [source/common/http/conn_manager_impl.cc:580] [C0][S16022300470282068410] request headers complete (end_stream=true):
front-envoy_1  | ':authority', 'server.mycompany.com'
front-envoy_1  | ':path', '/'
front-envoy_1  | ':method', 'GET'
front-envoy_1  | 'connection', 'keep-alive'
front-envoy_1  | 'cache-control', 'max-age=0'
front-envoy_1  | 'upgrade-insecure-requests', '1'
front-envoy_1  | 'user-agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'
front-envoy_1  | 'accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3'
front-envoy_1  | 'accept-encoding', 'gzip, deflate'
front-envoy_1  | 'accept-language', 'fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7'
front-envoy_1  | 'cookie', '_ga=GA1.2.1808164165.1542377204; _ga=GA1.4.1808164165.1542377204; sidebar_collapsed=false; grafana_user=admin; grafana_remember=11717b28d1f4b3391c33bc15fe3afcabd3cea6d0406b9f558aa868734280f02917; _gid=GA1.2.1626649956.1554713026'
front-envoy_1  |
front-envoy_1  | [2019-04-08 09:11:42.559][13][debug][http] [source/common/http/conn_manager_impl.cc:1037] [C0][S16022300470282068410] request end stream
front-envoy_1  | [2019-04-08 09:11:42.559][13][debug][router] [source/common/router/router.cc:320] [C0][S16022300470282068410] cluster 'onisep' match for URL '/'
front-envoy_1  | [2019-04-08 09:11:42.559][13][debug][router] [source/common/router/router.cc:381] [C0][S16022300470282068410] router decoding headers:
front-envoy_1  | ':authority', 'server.mycompany.com'
front-envoy_1  | ':path', '/'
front-envoy_1  | ':method', 'GET'
front-envoy_1  | ':scheme', 'http'
front-envoy_1  | 'cache-control', 'max-age=0'
front-envoy_1  | 'upgrade-insecure-requests', '1'
front-envoy_1  | 'user-agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'
front-envoy_1  | 'accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3'
front-envoy_1  | 'accept-encoding', 'gzip, deflate'
front-envoy_1  | 'accept-language', 'fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7'
front-envoy_1  | 'cookie', '_ga=GA1.2.1808164165.1542377204; _ga=GA1.4.1808164165.1542377204; sidebar_collapsed=false; grafana_user=admin; grafana_remember=11717b28d1f4b3391c33bc15fe3afcabd3cea6d0406b9f558aa868734280f02917; _gid=GA1.2.1626649956.1554713026'
front-envoy_1  | 'x-forwarded-proto', 'http'
front-envoy_1  | 'x-request-id', '74c2bfa9-ee81-4f42-b0e4-483341a0bd29'
front-envoy_1  | 'x-envoy-expected-rq-timeout-ms', '15000'
front-envoy_1  | 'x-envoy-original-path', '/'
front-envoy_1  |
front-envoy_1  | [2019-04-08 09:11:42.559][13][debug][pool] [source/common/http/http1/conn_pool.cc:88] creating a new connection
front-envoy_1  | [2019-04-08 09:11:42.559][13][debug][client] [source/common/http/codec_client.cc:26] [C2] connecting
front-envoy_1  | [2019-04-08 09:11:42.559][13][debug][connection] [source/common/network/connection_impl.cc:644] [C2] connecting to 213.162.50.177:80
front-envoy_1  | [2019-04-08 09:11:42.559][13][debug][connection] [source/common/network/connection_impl.cc:653] [C2] connection in progress
front-envoy_1  | [2019-04-08 09:11:42.559][13][debug][pool] [source/common/http/conn_pool_base.cc:20] queueing request due to no available connections
front-envoy_1  | [2019-04-08 09:11:42.559][13][trace][http] [source/common/http/conn_manager_impl.cc:814] [C0][S16022300470282068410] decode headers called: filter=0x2bf19f0 status=1
front-envoy_1  | [2019-04-08 09:11:42.559][13][trace][http] [source/common/http/http1/codec_impl.cc:384] [C0] parsed 718 bytes
front-envoy_1  | [2019-04-08 09:11:42.559][13][trace][connection] [source/common/network/connection_impl.cc:282] [C0] readDisable: enabled=true disable=true
front-envoy_1  | [2019-04-08 09:11:42.559][13][trace][connection] [source/common/network/connection_impl.cc:440] [C0] socket event: 2
front-envoy_1  | [2019-04-08 09:11:42.559][13][trace][connection] [source/common/network/connection_impl.cc:508] [C0] write ready
front-envoy_1  | [2019-04-08 09:11:52.557][13][debug][pool] [source/common/http/http1/conn_pool.cc:332] [C2] connect timeout
front-envoy_1  | [2019-04-08 09:11:52.557][13][debug][connection] [source/common/network/connection_impl.cc:101] [C2] closing data_to_write=0 type=1
front-envoy_1  | [2019-04-08 09:11:52.557][13][debug][connection] [source/common/network/connection_impl.cc:183] [C2] closing socket: 1
front-envoy_1  | [2019-04-08 09:11:52.557][13][debug][client] [source/common/http/codec_client.cc:82] [C2] disconnect. resetting 0 pending requests
front-envoy_1  | [2019-04-08 09:11:52.557][13][debug][pool] [source/common/http/http1/conn_pool.cc:129] [C2] client disconnected, failure reason:
front-envoy_1  | [2019-04-08 09:11:52.557][13][debug][pool] [source/common/http/http1/conn_pool.cc:164] [C2] purge pending, failure reason:
front-envoy_1  | [2019-04-08 09:11:52.557][13][debug][router] [source/common/router/router.cc:644] [C0][S16022300470282068410] upstream reset: reset reason connection failure
front-envoy_1  | [2019-04-08 09:11:52.558][13][debug][http] [source/common/http/conn_manager_impl.cc:1278] [C0][S16022300470282068410] encoding headers via codec (end_stream=false):
front-envoy_1  | ':status', '503'
front-envoy_1  | 'content-length', '91'
front-envoy_1  | 'content-type', 'text/plain'
front-envoy_1  | 'date', 'Mon, 08 Apr 2019 09:11:52 GMT'
front-envoy_1  | 'server', 'envoy'
front-envoy_1  |
front-envoy_1  | [2019-04-08 09:11:52.558][13][trace][connection] [source/common/network/connection_impl.cc:376] [C0] writing 134 bytes, end_stream false
front-envoy_1  | [2019-04-08 09:11:52.558][13][trace][http] [source/common/http/conn_manager_impl.cc:1407] [C0][S16022300470282068410] encoding data via codec (size=91 end_stream=true)
front-envoy_1  | [2019-04-08 09:11:52.558][13][trace][connection] [source/common/network/connection_impl.cc:376] [C0] writing 91 bytes, end_stream false
front-envoy_1  | [2019-04-08 09:11:52.558][13][trace][main] [source/common/event/dispatcher_impl.cc:133] item added to deferred deletion list (size=1)
front-envoy_1  | [2019-04-08 09:11:52.558][13][trace][connection] [source/common/network/connection_impl.cc:282] [C0] readDisable: enabled=false disable=false
front-envoy_1  | [2019-04-08 09:11:52.558][13][trace][main] [source/common/event/dispatcher_impl.cc:133] item added to deferred deletion list (size=2)
front-envoy_1  | [2019-04-08 09:11:52.558][13][trace][main] [source/common/event/dispatcher_impl.cc:53] clearing deferred deletion list (size=2)
front-envoy_1  | [2019-04-08 09:11:52.558][13][trace][connection] [source/common/network/connection_impl.cc:440] [C0] socket event: 2
front-envoy_1  | [2019-04-08 09:11:52.558][13][trace][connection] [source/common/network/connection_impl.cc:508] [C0] write ready
front-envoy_1  | [2019-04-08 09:11:52.558][13][trace][connection] [source/common/network/raw_buffer_socket.cc:66] [C0] write returns: 225
front-envoy_1  | [2019-04-08 09:11:52.576][14][trace][connection] [source/common/network/connection_impl.cc:440] [C1] socket event: 3
front-envoy_1  | [2019-04-08 09:11:52.576][14][trace][connection] [source/common/network/connection_impl.cc:508] [C1] write ready
front-envoy_1  | [2019-04-08 09:11:52.576][14][trace][connection] [source/common/network/connection_impl.cc:478] [C1] read ready
front-envoy_1  | [2019-04-08 09:11:52.576][14][trace][connection] [source/common/network/raw_buffer_socket.cc:23] [C1] read returns: 0
front-envoy_1  | [2019-04-08 09:11:52.576][14][debug][connection] [source/common/network/connection_impl.cc:502] [C1] remote close
front-envoy_1  | [2019-04-08 09:11:52.576][14][debug][connection] [source/common/network/connection_impl.cc:183] [C1] closing socket: 0
front-envoy_1  | [2019-04-08 09:11:52.577][14][debug][main] [source/server/connection_handler_impl.cc:68] [C1] adding to cleanup list
front-envoy_1  | [2019-04-08 09:11:52.577][14][trace][main] [source/common/event/dispatcher_impl.cc:133] item added to deferred deletion list (size=1)
front-envoy_1  | [2019-04-08 09:11:52.577][14][trace][main] [source/common/event/dispatcher_impl.cc:53] clearing deferred deletion list (size=1)
front-envoy_1  | [2019-04-08 09:11:52.596][13][trace][connection] [source/common/network/connection_impl.cc:440] [C0] socket event: 3
front-envoy_1  | [2019-04-08 09:11:52.596][13][trace][connection] [source/common/network/connection_impl.cc:508] [C0] write ready
front-envoy_1  | [2019-04-08 09:11:52.596][13][trace][connection] [source/common/network/connection_impl.cc:478] [C0] read ready
front-envoy_1  | [2019-04-08 09:11:52.596][13][trace][connection] [source/common/network/raw_buffer_socket.cc:23] [C0] read returns: 688
front-envoy_1  | [2019-04-08 09:11:52.596][13][trace][connection] [source/common/network/raw_buffer_socket.cc:37] [C0] read error: Resource temporarily unavailable
front-envoy_1  | [2019-04-08 09:11:52.596][13][trace][http] [source/common/http/http1/codec_impl.cc:363] [C0] parsing 688 bytes
front-envoy_1  | [2019-04-08 09:11:52.596][13][trace][http] [source/common/http/http1/codec_impl.cc:476] [C0] message begin
front-envoy_1  | [2019-04-08 09:11:52.596][13][debug][http] [source/common/http/conn_manager_impl.cc:243] [C0] new stream
front-envoy_1  | [2019-04-08 09:11:52.596][13][trace][http] [source/common/http/http1/codec_impl.cc:331] [C0] completed header: key=Host value=server.mycompany.com
front-envoy_1  | [2019-04-08 09:11:52.596][13][trace][http] [source/common/http/http1/codec_impl.cc:331] [C0] completed header: key=Connection value=keep-alive
front-envoy_1  | [2019-04-08 09:11:52.596][13][trace][http] [source/common/http/http1/codec_impl.cc:331] [C0] completed header: key=Pragma value=no-cache
front-envoy_1  | [2019-04-08 09:11:52.596][13][trace][http] [source/common/http/http1/codec_impl.cc:331] [C0] completed header: key=Cache-Control value=no-cache
front-envoy_1  | [2019-04-08 09:11:52.596][13][trace][http] [source/common/http/http1/codec_impl.cc:331] [C0] completed header: key=User-Agent value=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36
front-envoy_1  | [2019-04-08 09:11:52.596][13][trace][http] [source/common/http/http1/codec_impl.cc:331] [C0] completed header: key=Accept value=image/webp,image/apng,image/*,*/*;q=0.8
front-envoy_1  | [2019-04-08 09:11:52.596][13][trace][http] [source/common/http/http1/codec_impl.cc:331] [C0] completed header: key=Referer value=http://server.mycompany.com/
front-envoy_1  | [2019-04-08 09:11:52.596][13][trace][http] [source/common/http/http1/codec_impl.cc:331] [C0] completed header: key=Accept-Encoding value=gzip, deflate
front-envoy_1  | [2019-04-08 09:11:52.596][13][trace][http] [source/common/http/http1/codec_impl.cc:331] [C0] completed header: key=Accept-Language value=fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7
front-envoy_1  | [2019-04-08 09:11:52.596][13][trace][http] [source/common/http/http1/codec_impl.cc:442] [C0] headers complete
front-envoy_1  | [2019-04-08 09:11:52.596][13][trace][http] [source/common/http/http1/codec_impl.cc:331] [C0] completed header: key=Cookie value=_ga=GA1.2.1808164165.1542377204; _ga=GA1.4.1808164165.1542377204; sidebar_collapsed=false; grafana_user=admin; grafana_remember=11717b28d1f4b3391c33bc15fe3afcabd3cea6d0406b9f558aa868734280f02917; _gid=GA1.2.1626649956.1554713026
front-envoy_1  | [2019-04-08 09:11:52.596][13][trace][http] [source/common/http/http1/codec_impl.cc:463] [C0] message complete
front-envoy_1  | [2019-04-08 09:11:52.596][13][debug][http] [source/common/http/conn_manager_impl.cc:580] [C0][S14808485588688949153] request headers complete (end_stream=true):
front-envoy_1  | ':authority', 'server.mycompany.com'
front-envoy_1  | ':path', '/favicon.ico'
front-envoy_1  | ':method', 'GET'
front-envoy_1  | 'connection', 'keep-alive'
front-envoy_1  | 'pragma', 'no-cache'
front-envoy_1  | 'cache-control', 'no-cache'
front-envoy_1  | 'user-agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'
front-envoy_1  | 'accept', 'image/webp,image/apng,image/*,*/*;q=0.8'
front-envoy_1  | 'referer', 'http://server.mycompany.com/'
front-envoy_1  | 'accept-encoding', 'gzip, deflate'
front-envoy_1  | 'accept-language', 'fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7'
front-envoy_1  | 'cookie', '_ga=GA1.2.1808164165.1542377204; _ga=GA1.4.1808164165.1542377204; sidebar_collapsed=false; grafana_user=admin; grafana_remember=11717b28d1f4b3391c33bc15fe3afcabd3cea6d0406b9f558aa868734280f02917; _gid=GA1.2.1626649956.1554713026'
front-envoy_1  |
front-envoy_1  | [2019-04-08 09:11:52.596][13][debug][http] [source/common/http/conn_manager_impl.cc:1037] [C0][S14808485588688949153] request end stream
front-envoy_1  | [2019-04-08 09:11:52.596][13][debug][router] [source/common/router/router.cc:320] [C0][S14808485588688949153] cluster 'onisep' match for URL '/favicon.ico'
front-envoy_1  | [2019-04-08 09:11:52.596][13][debug][router] [source/common/router/router.cc:381] [C0][S14808485588688949153] router decoding headers:
front-envoy_1  | ':authority', 'server.mycompany.com'
front-envoy_1  | ':path', '/favicon.ico'
front-envoy_1  | ':method', 'GET'
front-envoy_1  | ':scheme', 'http'
front-envoy_1  | 'pragma', 'no-cache'
front-envoy_1  | 'cache-control', 'no-cache'
front-envoy_1  | 'user-agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'
front-envoy_1  | 'accept', 'image/webp,image/apng,image/*,*/*;q=0.8'
front-envoy_1  | 'referer', 'http://server.mycompany.com/'
front-envoy_1  | 'accept-encoding', 'gzip, deflate'
front-envoy_1  | 'accept-language', 'fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7'
front-envoy_1  | 'cookie', '_ga=GA1.2.1808164165.1542377204; _ga=GA1.4.1808164165.1542377204; sidebar_collapsed=false; grafana_user=admin; grafana_remember=11717b28d1f4b3391c33bc15fe3afcabd3cea6d0406b9f558a

小智 1

当您使用单个 docker 映像对其进行测试时,我建议您传递 docker 容器 IP。请按照以下示例更改配置。

〜docker ps -q | xargs -n 1 docker检查 --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}} {{ .Config.Hostname }} {{ .Config.Image }}' | sed 's/ // /'

=> 172.19.0.2 e7b03f46f33e parjun8840/flaskapp:v3

更改文件内容:front-envoy.yaml

clusters:
  - name: onisep
    connect_timeout: 10.0s
    lb_policy: round_robin
    type: STATIC
    hosts:
      socket_address:
        address: 172.19.0.2
        port_value: 80
Run Code Online (Sandbox Code Playgroud)

请告诉我,这是否适合您?