Modelo de cita a ciegas y máquina de estados finitos

El artículo " Modelo de cita a ciegas y máquina de estados finitos " se publicó por primera vez en: blog.ihypo.net/16573725253…

Si alguien te pregunta cuántos pasos necesitas para poner el elefante en el refrigerador, la sonrisa de la tía Song vendrá a tu mente y te dirá: 3 pasos.

Si alguien te pregunta cuántos pasos se necesitan para pasar de la soltería a la vida, realmente necesitas contar con los dedos.

modelo de cita a ciegas

Una vez que te metes en la programación, es como el mar, y el final del código es una cita a ciegas. Algunas personas mayores han dicho que los programadores que no han tenido una cita a ciegas están incompletos, e incluso el código que escriben es menos profundo. Aunque yo mismo no lo he experimentado, si estás contando tu vida con los dedos, puedes empezar con una cita a ciegas.

Comenzando con una cita a ciegas

Te aclaraste la garganta, tragaste y rápidamente frotaste las esquinas del menú con tus dos dedos, mirando a la extraña y hermosa dama al otro lado de la mesa: "¿Qué quieres comer?"

"Simplemente come lo que quieras".

Continúas enterrando tu cabeza en el menú, pides algunos platos al azar y te olvidas de pedir bebidas a toda prisa.

"Bueno, déjame presentarme primero." Presionaste el menú y pensaste que era un gran problema ser una entrevista de recursos humanos, así que comenzaste a presentarte y, por cierto, no escribiste Java.

Cuando llegué a casa después de la cena, recordé los detalles de la comida y surgió la vergüenza, pero afortunadamente, la conversación fue muy amena, como si fuera un drama. Una vez que no haya terminado, abre una aplicación de chat verde, hace clic en el avatar familiar y desconocido y comienza la segunda mitad.

Al mes siguiente, volvió a ser el restaurante. Decides perforar esa capa de papel de la ventana, y el café y el guion de matar han dejado un buen recuerdo en común, así que ha llegado el momento.

Te aclaraste la garganta, tragaste y rápidamente frotaste la esquina del menú con tus dos dedos, mirando a la joven al otro lado de la mesa: "¿Qué quieres comer?"

Diagrama de flujo de estado de cita a ciegas

Me temo que solo un cojo como yo puede escribir una historia así sin altibajos, ¿por qué? Porque la emoción es una de las cosas más complicadas del mundo, y la cita a ciegas que utiliza el chat como forma de juego y promueve el desarrollo emocional como lógica subyacente también es una batalla con la mayoría de las variables. Aunque es imposible enumerar el proceso de la cita a ciegas, si trata de abstraer y cuantificar el proceso de la escena de la cita a ciegas, seguramente podrá obtener un diagrama de flujo de estado complejo.

La imagen de arriba, Yue Lao la miró y lo llamó experto. Solo hay muchos estados intermedios en el proceso de una cita a ciegas, entonces, ¿cuántos pasos se necesitan para pasar de ser soltero a conformarse con la vida?

Máquina de estados finitos

Una máquina de estados finitos (FSM) es un modelo matemático de la cibernética. Se utiliza para representar comportamientos como transiciones y acciones entre estados de tipo enumerable. El habla humana se utiliza para controlar los cambios en el estado de una máquina.

concepto basico

从相亲的模型中可以看到,一个有限状态机包含『状态』和『行为』两大基本概念。

『状态』包括两个核心元素:

  • 第一个是 State 即状态,一个状态机至少要包含两个状态。比如最简单的状态机就是电灯,包含了 “亮” 和 “灭” 两个状态。
  • 第二个是 Transition 即状态流转,也就是从一个状态变化为另一个状态。比如 “开灯的过程” 就是一个状态流转。

『行为』也包含两个核心元素

  • 第一个是 Event 即事件,事件就是执行某个操作,也可以看做外部系统的输入。比如 “按下灯开关” 就是一个事件。
  • 第二个是 Action 即动作,事件发生后引发的变更,也可以看做状态机对外部的输出。比如 “按下灯开关” 事件后,会触发 “灯泡亮起” 这个动作。

适用场景

有限状态机的适用场景很多,尤其是状态复杂的场景,比如订单、任务管理等。有限状态机的本质是维护状态流转图,使得在复杂的用户输入中,依然保持状态的合法和安全。

(图来自《京东京麦交易平台设计与实现》)

除了复杂状态流转的场景,当状态无法明确的情况下,有限状态机也可以被考虑。仿佛和『有限』这个字眼相悖,这里的无法明确是指需求无法明确,也许今天 3 个状态就满足需求了,但下个版本可能需要 5 个状态。对于有限状态机来说,多加两种状态只不过是在状态流转图了多几条边而已。

有限状态机实现

本节的实现以 GoFlow 为例,GoFlow 是一个用 Golang 实现的任务引擎 代码可见: github.com/basenana/go…

MIT 数位系统概论中总结了一套实现 FSM 的方法论:

  • 明确状态类型(identify distinct states)
  • 创建状态流转图(create state transition diagram)
  • 编写状态流转逻辑(write for next-state logic)
  • 编写输出信号逻辑(write for output signals)

我们就从这个四个角度讲下 GoFlow 的 FSM 实现。

枚举状态类型

首先是明确『有限』的状态,对于 GoFlow 这种任务管理系统来说,状态肯定是围绕任务展开的:

const (
	CreatingStatus     fsm.Status = "creating"
	InitializingStatus            = "initializing"
	RunningStatus                 = "running"
	SucceedStatus                 = "succeed"
	FailedStatus                  = "failed"
	ErrorStatus                   = "error"
	PausedStatus                  = "paused"
	CanceledStatus                = "canceled"
)

状态流转图

状态流转图是一个有向图,每个状态都是一个点,两点之间的边即是事件,边的方向是流转方向。最简单存储方式有两种:

  1. 十字链表法:以当前状态为维度维护指向其前驱节点的边。也就是以当前状态为 Key,保存的 Value 即是事件类型和可以流转的下一级状态
  2. 边存储法:以事件为维度维护所有的边。也就是事件类型为 Key,Value 是上级状态和下级状态

两种存储方式各有好处,因为 GoFlow 状态流转图的边的数量比较少,也就选择了第二种方式:基于 Map 以事件类型为 Key,存储所有的边。这种存储方式可以快速通过一个事件,找到能执行的所有操作。

type edge struct {
	from Status
	to   Status
	when EventType
	do   Handler
	next *edge
}

type FSM struct {
	obj   Stateful
	graph map[EventType]*edge

	crtBuilder *edgeBuilder
	mux        sync.Mutex
	logger     log.Logger
}

从 GoFlow 的状态枚举可以看出当前的引擎相对简单,因此它的状态流转图也并不复杂:

构建状态流转图的逻辑:

func buildFlowTaskFSM(r *runner, t flow.Task) *fsm.FSM {
	m := fsm.New(fsm.Option{
		Obj:    t,
		Logger: r.logger.With(fmt.Sprintf("task.%s.fsm", t.Name())),
	})

	m.From([]fsm.Status{flow.InitializingStatus}).
		To(flow.RunningStatus).
		When(flow.TaskTriggerEvent).
		Do(r.handleTaskRun)

	m.From([]fsm.Status{flow.RunningStatus}).
		To(flow.SucceedStatus).
		When(flow.TaskExecuteFinishEvent).
		Do(r.handleTaskSucceed)

	m.From([]fsm.Status{flow.RunningStatus}).
		To(flow.PausedStatus).
		When(flow.TaskExecutePauseEvent).
		Do(r.handleTaskPaused)

	m.From([]fsm.Status{flow.PausedStatus}).
		To(flow.RunningStatus).
		When(flow.TaskExecuteResumeEvent).
		Do(r.handleTaskResume)

	m.From([]fsm.Status{flow.InitializingStatus, flow.RunningStatus, flow.PausedStatus}).
		To(flow.CanceledStatus).
		When(flow.TaskExecuteCancelEvent).
		Do(r.handleTaskCanceled)

	m.From([]fsm.Status{flow.InitializingStatus, flow.RunningStatus}).
		To(flow.FailedStatus).
		When(flow.TaskExecuteErrorEvent).
		Do(r.handleTaskFailed)

	return m
}

状态流转逻辑与触发动作

状态的流转是 FSM 的核心,GoFlow 的状态流转,基本流程:

  1. 根据当前事件获取该事件影响的边
  2. 基于边的初始状态与当前状态匹配
  3. 如果存在匹配的边,则把当前状态机状态置为下级状态
  4. 状态转换完成后,执行对应的动作
func (m *FSM) Event(event Event) error {
	m.mux.Lock()
	defer m.mux.Unlock()

	m.logger.Debugf("handler fsm event: %s", event.Type)
	head := m.graph[event.Type]
	if head == nil {
		return nil
	}

	defer func() {
		if panicErr := recover(); panicErr != nil {
			m.logger.Errorf("event %s handle panic: %v", event, panicErr)
		}
	}()

	for head != nil {
		if m.obj.GetStatus() == head.from {
			m.logger.Infof("change obj status from %s to %s with event: %s", head.from, head.to, event.Type)
			m.obj.SetStatus(head.to)
			if event.Message != "" {
				m.obj.SetMessage(event.Message)
			}

			if head.do != nil {
				if handleErr := head.do(event); handleErr != nil {
					m.logger.Errorf("event %s handle failed: %s", event.Type, handleErr.Error())
					return handleErr
				}
			}
			return nil
		}
		head = head.next
	}
	return fmt.Errorf("get event %s and current status is %s, no change path matched", event.Type, m.obj.GetStatus())
}

有状态的状态机

GoFlow 的状态机是一种非常简单或者说偷懒的实现,主要问题在于这是一个有状态的状态机。听起来很奇怪,状态机本来不就是用来维护状态的吗。这里的状态其实指的是状态机本身的状态。简单来说,当 FSM 被实例化之后,这个用来维护任务的状态的 FSM 只能在当前进程(或者说副本)中使用,因为其他副本并没有这个 FSM 实例对象。

对于一个长时间运行的任务引擎来说,这件事情本身也无可厚非,但是换个场景,比如一个电商订单被创建之后,状态机实例也随之创建。如果这个订单只能被创建该订单的副本进程执行,其他副本均无法处理该订单,这是不可接受的。

状态机的无状态化有两种实现,一种是『集中处理』的方式,一种是『用时构造』的方式。

El "procesamiento centralizado" es adecuado para tareas de cola larga, como motores de tareas, donde existe una máquina de estado durante mucho tiempo. Este método permite que múltiples réplicas seleccionen una réplica específica para instanciar el FSM seleccionando el maestro.Cuando otras réplicas necesitan realizar una transferencia de estado, las réplicas que contienen el FSM son procesadas por RPC o eventos. La implementación basada en eventos es óptima, después de todo, la máquina de estado en sí misma es un modelo controlado por eventos. La ventaja de este diseño es que es simple y fácil de entender. Por supuesto, la desventaja también es obvia. Se basa en componentes de mensajes distribuidos.

"Time Construct" es adecuado para tareas a corto plazo, como solicitudes de API para pedidos de comercio electrónico. La máquina de estado solo necesita existir cuando se usa. Este método es que la máquina de estado ya no mantiene el estado actual, sino que solo almacena el gráfico de flujo de estado.Cuando se requiere el flujo de estado, la máquina de estado se reconstruye de acuerdo con el estado actual en tiempo real, y la máquina de estado se destruye después el flujo de estado termina.

Al final

La máquina de estado en sí misma no es un modelo complejo, ni siquiera simple. Sin embargo, las condiciones adicionales extendidas basadas en la máquina de estado son engorrosas, como el anidamiento de estados, el paralelismo de estados, etc. Muchos proyectos de código abierto cubren escenarios más completos, pero también hacen que su implementación sea muy compleja e ineficiente. La implementación pesada en realidad dificulta el coraje de usar la máquina de estado en escenas pequeñas. Después de todo, usar la metodología de la NASA para hacer un banco de madera es algo extraño. Así que es mejor intentarlo y escribir un FSM que se acerque a tu propia escena. Te aconsejo que te dejes engañar, déjate engañar una vez.

no-qr.jpg

Supongo que te gusta

Origin juejin.im/post/7118405104369139719
Recomendado
Clasificación