Todos os produtos
Search
Central de documentação

Alibaba Cloud Service Mesh:Personalize o tratamento de requisições e respostas com o filtro ext_proc do Envoy

Última atualização: Jun 28, 2026

O filtro External Processing (ext_proc) do Envoy encaminha o processamento de requisições e respostas HTTP para um serviço gRPC externo. Em vez de escrever um plug-in Wasm ou incorporar lógica ao Envoy, execute um servidor gRPC independente que inspeciona e modifica cabeçalhos, corpos e trailers conforme o tráfego flui pela malha.

O ext_proc é ideal quando você precisa:

  • Adicionar, remover ou reescrever cabeçalhos HTTP com base em lógica de negócios

  • Implementar verificações personalizadas de autenticação ou autorização fora do sidecar

  • Enriquecer requisições com dados de sistemas externos antes de chegarem ao serviço upstream

  • Usar uma linguagem ou framework incompatível com Wasm

Como funciona

O filtro ext_proc intercepta o tráfego no sidecar e o encaminha ao serviço gRPC externo para processamento. Esse filtro oferece suporte a seis etapas independentes, cada uma controlada por um modo de processamento:

Etapa

Direção

Descrição

Cabeçalhos da requisição

Entrada

Inspeciona ou modifica cabeçalhos antes de a requisição chegar ao upstream

Corpo da requisição

Entrada

Inspeciona ou modifica o corpo da requisição

Trailers da requisição

Entrada

Inspeciona ou modifica trailers de requisição HTTP/2

Cabeçalhos da resposta

Saída

Inspeciona ou modifica cabeçalhos antes de a resposta chegar ao downstream

Corpo da resposta

Saída

Inspeciona ou modifica o corpo da resposta

Trailers da resposta

Saída

Inspeciona ou modifica trailers de resposta HTTP/2

Por padrão, nenhuma dessas etapas é enviada ao processador externo. Ative explicitamente cada etapa definindo seu modo de processamento como SEND na configuração do EnvoyFilter.

O diagrama a seguir ilustra o fluxo completo de requisição e resposta:

Envoy external processing flow

  1. Um serviço downstream envia uma requisição. O Envoy a intercepta e a encaminha ao serviço de processamento externo.

  2. O serviço de processamento externo inspeciona ou modifica a requisição e retorna o resultado ao Envoy.

  3. O Envoy aplica as modificações e encaminha a requisição ao serviço upstream.

  4. O serviço upstream retorna uma resposta. O Envoy a intercepta e a encaminha ao serviço de processamento externo.

  5. O serviço de processamento externo inspeciona ou modifica a resposta e retorna o resultado ao Envoy.

  6. O Envoy aplica as modificações e encaminha a resposta ao serviço downstream.

Pré-requisitos

Antes de começar, verifique se você tem:

Etapa 1: Escreva o serviço de processamento externo

Crie um servidor gRPC que implemente a interface de serviço ExternalProcessor do Envoy. O servidor recebe um fluxo de mensagens ProcessingRequest do Envoy e deve retornar uma ProcessingResponse correspondente para cada uma.

O trecho Go a seguir mostra a lógica principal. Para obter o projeto executável completo, consulte ext-proc-demo no GitHub. Para a especificação completa da API, consulte a referência proto do ext_proc do Envoy.

Visualize a lógica principal de processamento (Go)

func NewServer() *Server {
	return &Server{}
}

// Server implements the Envoy external processing server interface
// See https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/ext_proc/v3/external_processor.proto
type Server struct{}

func (s *Server) Process(srv extProcPb.ExternalProcessor_ProcessServer) error {
	klog.Infof("Processing")
	ctx := srv.Context()
	for {
		select {
		case <-ctx.Done():
			klog.Infof("context done")
			return ctx.Err()
		default:
		}

		req, err := srv.Recv()
		if err == io.EOF {
			// envoy has closed the stream. Don't return anything and close this stream entirely
			return nil
		}
		if err != nil {
			return status.Errorf(codes.Unknown, "cannot receive stream request: %v", err)
		}

		resp := &extProcPb.ProcessingResponse{}
		switch v := req.Request.(type) {
        // Requests that require processing of request headers
		case *extProcPb.ProcessingRequest_RequestHeaders:
			klog.Infof("Got RequestHeaders")
			h := req.Request.(*extProcPb.ProcessingRequest_RequestHeaders)
			resp = handleRequestHeaders(h)
        // Requests that require processing of response headers
        case *extProcPb.ProcessingRequest_ResponseHeaders:
			klog.Infof("Got ResponseHeaders")
			h := req.Request.(*extProcPb.ProcessingRequest_ResponseHeaders)
			resp = handleResponseHeaders(h)
        // Requests that require processing of request headers, not currently implemented
		case *extProcPb.ProcessingRequest_RequestBody:
			klog.Infof("Got RequestBody (not currently handled)")
        // Requests that require processing of request trailers, not currently implemented
		case *extProcPb.ProcessingRequest_RequestTrailers:
			klog.Infof("Got RequestTrailers (not currently handled)")
        // Requests that require processing of response body, not currently implemented
		case *extProcPb.ProcessingRequest_ResponseBody:
			klog.Infof("Got ResponseBody (not currently handled)")
        // Requests that require processing of response trailers, not currently implemented
		case *extProcPb.ProcessingRequest_ResponseTrailers:
			klog.Infof("Got ResponseTrailers (not currently handled)")

		default:
			klog.Infof("Unknown Request type %v", v)
		}
        // Return the processing required for the request
		klog.Infof("Sending ProcessingResponse: %+v", resp)
		if err := srv.Send(resp); err != nil {
			klog.Infof("send error %v", err)
			return err
		}
	}
}

// Add x-ext-proc-header=hello-to-asm to request headers
func handleRequestHeaders(req *extProcPb.ProcessingRequest_RequestHeaders) *extProcPb.ProcessingResponse {
	klog.Infof("handle request headers: %+v\n", req)

	resp := &extProcPb.ProcessingResponse{
		Response: &extProcPb.ProcessingResponse_RequestHeaders{
			RequestHeaders: &extProcPb.HeadersResponse{
				Response: &extProcPb.CommonResponse{
					HeaderMutation: &extProcPb.HeaderMutation{
						SetHeaders: []*configPb.HeaderValueOption{
							{
								Header: &configPb.HeaderValue{
									Key:      "x-ext-proc-header",
									RawValue: []byte("hello-to-asm"),
								},
							},
						},
					},
				},
			},
		},
	}

	return resp
}

// Add x-ext-proc-header=hello-from-asm to response headers
func handleResponseHeaders(req *extProcPb.ProcessingRequest_ResponseHeaders) *extProcPb.ProcessingResponse {
	klog.Infof("handle response headers: %+v\n", req)

	resp := &extProcPb.ProcessingResponse{
		Response: &extProcPb.ProcessingResponse_ResponseHeaders{
			ResponseHeaders: &extProcPb.HeadersResponse{
				Response: &extProcPb.CommonResponse{
					HeaderMutation: &extProcPb.HeaderMutation{
						SetHeaders: []*configPb.HeaderValueOption{
							{
								Header: &configPb.HeaderValue{
									Key:      "x-ext-proc-header",
									RawValue: []byte("hello-from-asm"),
								},
							},
						},
					},
				},
			},
		},
	}

	return resp
}

Este exemplo trata duas etapas de processamento:

  • Cabeçalhos da requisição: Adiciona x-ext-proc-header: hello-to-asm a todas as requisições de entrada.

  • Cabeçalhos da resposta: Adiciona x-ext-proc-header: hello-from-asm a todas as respostas de saída.

Nota

Empacote seu servidor gRPC como uma imagem de contêiner usando um Dockerfile e envie-a para um registro de contêineres antes da implantação.

Etapa 2: Implante o serviço de processamento externo

Esta etapa usa a imagem de exemplo ext_proc fornecida pelo ASM. O serviço de exemplo adiciona o cabeçalho x-ext-proc-header: hello-to-asm às requisições e x-ext-proc-header: hello-from-asm às respostas.

  1. Crie um arquivo chamado ext.yaml com o seguinte conteúdo:

        apiVersion: v1
        kind: Service
        metadata:
          name: ext-proc
          labels:
            app: ext-proc
            service: ext-proc
        spec:
          ports:
          # gRPC listener port for the external processing service
          - name: grpc
            port: 9002
            targetPort: 9002
          selector:
            app: ext-proc
        ---
        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: ext-proc
        spec:
          replicas: 1
          selector:
            matchLabels:
              app: ext-proc
              version: v1
          template:
            metadata:
              labels:
                app: ext-proc
                version: v1
            spec:
              containers:
              - image: registry-cn-hangzhou.ack.aliyuncs.com/ack-demo/ext-proc:v0.2
                imagePullPolicy: IfNotPresent
                name: ext-proc
                ports:
                - containerPort: 9002
  2. Aplique o manifesto e verifique se o serviço está em execução:

        kubectl apply -f ext.yaml
  3. Verifique os logs do pod para confirmar a inicialização do servidor gRPC. Saída esperada: Este log confirma que o serviço de processamento externo está em execução e ouvindo na porta 9002.

        kubectl logs deploy/ext-proc
        I1126 06:41:25.467033       1 main.go:52] Starting gRPC server on port :9002

Etapa 3: Configure o EnvoyFilter

Crie um EnvoyFilter para inserir o filtro ext_proc na cadeia de filtros HTTP do sidecar. Essa configuração roteia os cabeçalhos de requisição e resposta do sidecar do HTTPBin para o seu serviço de processamento externo.

  1. Faça login no console do ASM. No painel de navegação à esquerda, escolha Service Mesh > Mesh Management.

  2. Na página Mesh Management, clique em nome da instância do ASM. No painel de navegação à esquerda, escolha Plugin Extension Center > EnvoyFilter Template.

  3. Crie um EnvoyFilter chamado httpbin-ext-proc com o seguinte conteúdo:

    Campos principais:

    Campo

    Descrição

    context: SIDECAR_INBOUND

    Aplica o filtro ao tráfego de entrada no proxy sidecar

    portNumber: 80

    Direciona o listener HTTP na porta 80

    operation: INSERT_BEFORE

    Insere o filtro ext_proc antes do filtro de roteador na cadeia

    cluster_name

    O endpoint do cluster Envoy para o serviço ext-proc, no formato `outbound

    `

    request_header_mode: SEND

    Envia cabeçalhos de requisição ao processador externo. O padrão é SKIP, ou seja, os cabeçalhos não são enviados

    response_header_mode: SEND

    Envia cabeçalhos de resposta ao processador externo. O padrão é SKIP, ou seja, os cabeçalhos não são enviados

        apiVersion: networking.istio.io/v1alpha3
        kind: EnvoyFilter
        spec:
          configPatches:
            - applyTo: HTTP_FILTER
              match:
                context: SIDECAR_INBOUND
                listener:
                  portNumber: 80
                  filterChain:
                    filter:
                      name: envoy.filters.network.http_connection_manager
                proxy:
                  proxyVersion: ^MIN_VERSION-MAX_VERSION.*
              patch:
                operation: INSERT_BEFORE
                value:
                  name: envoy.filters.http.ext_proc
                  typed_config:
                    '@type': >-
                      type.googleapis.com/envoy.extensions.filters.http.ext_proc.v3.ExternalProcessor
                    grpc_service:
                      envoy_grpc:
                        cluster_name: outbound|9002||ext-proc.default.svc.cluster.local
                        authority: ext-proc.default.svc.cluster.local
                    processing_mode:
                      request_header_mode: SEND
                      response_header_mode: SEND

Verifique a configuração

Envie uma requisição pela malha e confirme se o processador externo adicionou os cabeçalhos esperados.

No pod sleep, execute:

kubectl exec -it deploy/sleep -- curl httpbin:8000/headers -i

Procure estes dois cabeçalhos na saída:

  • Cabeçalho de resposta x-ext-proc-header: hello-from-asm -- adicionado pelo processador externo antes de a resposta chegar ao downstream

  • Cabeçalho de requisição X-Ext-Proc-Header: hello-to-asm (visível no corpo JSON) -- adicionado pelo processador externo antes de a requisição chegar ao upstream

Saída esperada:

HTTP/1.1 200 OK
server: envoy
date: Wed, 11 Dec 2024 06:47:59 GMT
content-type: application/json
content-length: 564
access-control-allow-origin: *
access-control-allow-credentials: true
x-envoy-upstream-service-time: 3
x-ext-proc-header: hello-from-asm

{
  "headers": {
    "Accept": "*/*",
    "Host": "httpbin:8000",
    "User-Agent": "curl/8.1.2",
    "X-B3-Parentspanid": "5c6dd2cc9312d6bb",
    "X-B3-Sampled": "1",
    "X-B3-Spanid": "1153a2737cee4434",
    "X-B3-Traceid": "baba86b696edc75a5c6dd2cc9312d6bb",
    "X-Envoy-Attempt-Count": "1",
    "X-Ext-Proc-Header": "hello-to-asm",
    "X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/default/sa/httpbin;Hash=69d8f267c3c00b4396a83e12d14520acc9dadb1492d660e10f77e94dcad7cb06;Subject=\"\";URI=spiffe://cluster.local/ns/default/sa/sleep"
  }
}

A presença de ambos os cabeçalhos confirma que o serviço de processamento externo intercepta e modifica corretamente o tráfego pelo sidecar do Envoy.