Patrones de diseño en la práctica GoF: el patrón proxy

Resumen: el   patrón de proxy proporciona un proxy para que un objeto controle el acceso a ese objeto.

Este artículo es compartido por la comunidad HUAWEI CLOUD " [Implementación de Go] Práctica 23 Patrones de diseño de GoF: patrón de proxy ", autor: Yuan Runzi.

Introducción

GoF define el patrón de proxy de la siguiente manera:

Proporcione un sustituto o marcador de posición para otro objeto para controlar el acceso a él.

Es decir, el patrón de proxy proporciona un proxy para que un objeto controle el acceso a ese objeto .

Es un patrón de diseño muy usado, y también es muy común en la vida real. Por ejemplo, revendedores de entradas de conciertos. Supongamos que necesitas ver un concierto, pero las entradas en el sitio web oficial se han agotado, así que fui al lugar y compré una a un precio alto a través de revendedores. En este ejemplo, los revendedores equivalen al agente de entradas de conciertos, en el caso de que las entradas no se puedan comprar a través de los canales oficiales, has completado el objetivo a través del agente.

Del ejemplo de las entradas para conciertos, también podemos ver que la clave para usar el modo proxy es proporcionar un objeto proxy para controlar el acceso a un objeto cuando es inconveniente para el Cliente acceder directamente a un objeto . El cliente realmente accede al objeto proxy, y el objeto proxy transferirá la solicitud del cliente al objeto de ontología para su procesamiento.

estructura UML

contexto de la escena

En un sistema de aplicación distribuido simple (proyecto de código de muestra), el módulo db se usa para almacenar información de registro y monitoreo del servicio, y es una base de datos de clave-valor. Para mejorar el rendimiento del acceso a la base de datos, decidimos agregarle una capa de caché:

Además, esperamos que el cliente no sea consciente de la existencia de la memoria caché cuando utiliza la base de datos, y el modo proxy puede hacerlo.

Código

// demo/db/cache.go
package db
// 关键点1: 定义代理对象,实现被代理对象的接口
type CacheProxy struct {
 // 关键点2: 组合被代理对象,这里应该是抽象接口,提升可扩展性
 db    Db
    cache sync.Map // key为tableName,value为sync.Map[key: primaryId, value: interface{}]
    hit   int
 miss  int
}
// 关键点3: 在具体接口实现上,嵌入代理本身的逻辑
func (c *CacheProxy) Query(tableName string, primaryKey interface{}, result interface{}) error {
    cache, ok := c.cache.Load(tableName)
 if ok {
 if record, ok := cache.(*sync.Map).Load(primaryKey); ok {
 c.hit++
            result = record
 return nil
 }
 }
 c.miss++
 if err := c.db.Query(tableName, primaryKey, result); err != nil {
 return err
 }
 cache.(*sync.Map).Store(primaryKey, result)
 return nil
}
func (c *CacheProxy) Insert(tableName string, primaryKey interface{}, record interface{}) error {
 if err := c.db.Insert(tableName, primaryKey, record); err != nil {
 return err
 }
    cache, ok := c.cache.Load(tableName)
 if !ok {
 return nil
 }
 cache.(*sync.Map).Store(primaryKey, record)
 return nil
}
...
// 关键点4: 代理也可以有自己特有方法,提供一些辅助的功能
func (c *CacheProxy) Hit() int {
 return c.hit
}
func (c *CacheProxy) Miss() int {
 return c.miss
}
...

El cliente lo usa así:

// 客户端只看到抽象的Db接口
func client(db Db) {
 table := NewTable("region").
 WithType(reflect.TypeOf(new(testRegion))).
 WithTableIteratorFactory(NewRandomTableIteratorFactory())
 db.CreateTable(table)
 table.Insert(1, &testRegion{Id: 1, Name: "region"})
 result := new(testRegion)
 db.Query("region", 1, result)
}
func main() {
 // 关键点5: 在初始化阶段,完成缓存的实例化,并依赖注入到客户端
 cache := NewCacheProxy(&memoryDb{tables: sync.Map{}})
 client(cache)
}

En este ejemplo, Subject es la interfaz Db, Proxy es el objeto CacheProxy y SubjectImpl es el objeto memoryDb:

Resuma varios puntos clave para implementar el patrón de proxy:

  1. Defina un objeto proxy que implemente la interfaz del objeto proxy. En este ejemplo, el primero es el objeto CacheProxy y el segundo es la interfaz Db.
  2. El objeto proxy combina el objeto proxy, y la combinación aquí debería ser una interfaz abstracta, lo que hace que el proxy sea más escalable. En este ejemplo, el objeto CacheProxy combina la interfaz Db.
  3. El objeto proxy incorpora la lógica del propio proxy en la implementación de interfaz específica. En este ejemplo, CacheProxy agrega la lógica de lectura y escritura de caché sync.Map en métodos como Query e Insert.
  4. Los objetos proxy también pueden tener sus propios métodos exclusivos para proporcionar algunas funciones auxiliares. En este ejemplo, CacheProxy agrega métodos como Hit and Miss para contar las tasas de aciertos de caché.
  5. Finalmente, en la fase de inicialización, se realiza la instanciación del proxy y se inyecta la dependencia en el cliente. Esto requiere que los clientes confíen en interfaces abstractas en lugar de implementaciones concretas, de lo contrario, el proxy no será transparente.

expandir

Proxies inversos en la biblioteca estándar de Go

El escenario de aplicación más típico del modo proxy es el proxy remoto , entre los cuales el proxy inverso es el más utilizado.

Tomando una aplicación web como ejemplo, el proxy inverso se encuentra frente al servidor web y reenvía la solicitud del cliente (por ejemplo, navegador web) al servidor web back-end. Los proxies inversos a menudo se usan para ayudar a mejorar la seguridad, el rendimiento y la confiabilidad , como el equilibrio de carga, los enlaces seguros SSL.

El paquete de red de la biblioteca estándar de Go también proporciona un proxy inverso, ReverseProxy, ubicado en net/http/httputil/reverseproxy.go, que implementa la interfaz http.Handler. http.Handler brinda la capacidad de procesar solicitudes Http, que es equivalente al servidor Http. Luego, correspondiente al diagrama de estructura UML, http.Handler es el Asunto y ReverseProxy es el Proxy:

Algunos códigos centrales de ReverseProxy se enumeran a continuación:

// net/http/httputil/reverseproxy.go
package httputil
type ReverseProxy struct {
 // 修改前端请求,然后通过Transport将修改后的请求转发给后端
    Director func(*http.Request)
 // 可理解为Subject,通过Transport来调用被代理对象的ServeHTTP方法处理请求
    Transport http.RoundTripper
 // 修改后端响应,并将修改后的响应返回给前端
 ModifyResponse func(*http.Response) error
 // 错误处理
 ErrorHandler func(http.ResponseWriter, *http.Request, error)
 ...
}
func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
 // 初始化transport
 transport := p.Transport
 if transport == nil {
        transport = http.DefaultTransport
 }
 ...
 // 修改前端请求
 p.Director(outreq)
 ...
 // 将请求转发给后端
    res, err := transport.RoundTrip(outreq)
 ...
 // 修改后端响应
 if !p.modifyResponse(rw, res, outreq) {
 return
 }
 ...
 // 给前端返回响应
    err = p.copyResponse(rw, res.Body, p.flushInterval(res))
 ...
}

ReverseProxy es una implementación de modo de proxy típica, en la que el proxy remoto no puede referirse directamente a la referencia del objeto del backend. Por lo tanto, al introducir el Transporte para acceder de forma remota al servicio del backend, el Transporte puede entenderse como el Sujeto.

ReverseProxy se puede usar así:

func proxy(c *gin.Context) {
    remote, err := url.Parse("https://yrunz.com")
 if err != nil {
 panic(err)
 }
 proxy := httputil.NewSingleHostReverseProxy(remote)
 proxy.Director = func(req *http.Request) {
 req.Header = c.Request.Header
 req.Host = remote.Host
 req.URL.Scheme = remote.Scheme
 req.URL.Host = remote.Host
 req.URL.Path = c.Param("proxyPath")
 }
 proxy.ServeHTTP(c.Writer, c.Request)
}
func main() {
 r := gin.Default()
 r.Any("/*proxyPath", proxy)
 r.Run(":8080")
}

Escenarios de aplicación típicos

  • Proxy remoto (proxy remoto), el proxy remoto es adecuado para el objeto que proporciona el servicio en la máquina remota. El servicio no se puede usar a través de llamadas de función ordinarias, y debe hacerse a través del proxy remoto. Debido a que no se puede acceder directamente al objeto de ontología, todos los objetos proxy remotos generalmente no contienen directamente la referencia del objeto de ontología, sino la dirección de la máquina remota para acceder al objeto de ontología a través del protocolo de red .
  • Proxy virtual (proxy virtual), a menudo hay algunos objetos de servicio pesados ​​en el diseño del programa. Si la instancia del objeto se mantiene todo el tiempo, consumirá una gran cantidad de recursos del sistema. En este momento, el proxy virtual se puede usar para retrasar la inicialización del objeto
  • Proxy de protección , que se usa para controlar el acceso a los objetos de ontología y, a menudo, se usa en escenarios donde se requiere verificación de permisos para el acceso del cliente.
  • Proxy de caché (cache proxy), el proxy de caché agrega principalmente una capa de caché entre el Cliente y el objeto de ontología para acelerar el acceso del objeto de ontología, lo cual es común en el escenario de conexión a la base de datos.
  • Referencia inteligente (referencia inteligente), la referencia inteligente proporciona acciones adicionales para el acceso del objeto de ontología. La implementación común es el puntero inteligente en C++, que proporciona una función de conteo para el acceso del objeto. Cuando el conteo del objeto accedido es 0, el objeto se destruye. .

Ventajas y desventajas

ventaja

  • Es posible controlar objetos de acceso, como acceso remoto, aumento de caché, seguridad, etc., sin la percepción del cliente.
  • De acuerdo con el principio abierto-cerrado , se puede agregar un nuevo proxy sin modificar el cliente y el objeto proxy; el objeto proxy también se puede reemplazar sin modificar el cliente y el proxy.

defecto

  • Al actuar como proxy remoto, la demora de la solicitud se verá afectada por un reenvío más.

Enlaces a otros esquemas

Desde un punto de vista estructural, el patrón de decoración  y el patrón proxy tienen una gran similitud, pero los puntos enfatizados por los dos son diferentes. El primero enfatiza agregar nuevas funciones a los objetos de ontología, mientras que el segundo enfatiza el control de acceso a los objetos de ontología .

articulo con imagenes

Puede  encontrar el método de dibujo del artículo en el estilo dibujado a mano con Keynote . 

Referirse a

[1] [Implementación de Go] Práctica 23 Patrones de diseño de GoF: Principio SOLID , Yuan Runzi 

[2] [Implementación de Go] Practique los 23 patrones de diseño de GoF: patrones decorativos , Yuan Runzi 

[3] Patrones de diseño, Capítulo 4. Patrones estructurales, GoF

[4] Modo proxy,  refactoringguru.cn

[5] ¿Qué es un proxy inverso? , llamarada de nube

 

Haga clic en Seguir para conocer las nuevas tecnologías de HUAWEI CLOUD por primera vez~

{{o.nombre}}
{{m.nombre}}

Supongo que te gusta

Origin my.oschina.net/u/4526289/blog/5584867
Recomendado
Clasificación