37、服务剖析与工匠网关服务实现

服务剖析与工匠网关服务实现

1. 服务管理基础

在不同系统中,服务的管理方式各有不同。在某些系统里,若配置文件处于 launchd 文件的标准位置,可使用以下命令分别启动、重启和停止服务:

launchctl start testdaemon.service
launchctl restart testdaemon.service
launchctl stop testdaemon.service

而在 Linux 系统中,虽然当前管理服务进程的趋势正朝着 systemd/systemctl 发展,但仍可能存在使用 System V 风格初始化脚本的操作系统。以下是一个简单的初始化脚本示例:

#!/bin/sh
# - The action we're concerned with appears as $1 in a standard
#   bash-script
case $1 in
    start)
        echo "Starting $0"
        /usr/bin/python /usr/local/bin/testdaemon.py
        ;;
    stop)
        echo "Stopping $0"
        /usr/bin/pkill -f testdaemon.py
        ;;
    restart)
        echo "Restarting $0"
        /usr/bin/pkill -f testdaemon.py
        /usr/bin/python /usr/local/bin/testdaemon.py
        ;;
esac

在 System V 管理的环境中,服务自身需确保与调用它的进程(如终端会话或操作系统的启动进程)分离,否则服务进程可能在实际执行任务前就终止。为处理这种情况,有一个 BaseDaemonizable 类,它能将服务类实例守护化,还可将进程 ID(PID)写入已知位置的文件。

2. 工匠网关服务概述

工匠网关在系统中充当工匠与中央办公室之间的通信中心,尤其在产品和订单对象方面。可能涉及的操作如下:
| 操作主体 | 操作内容 |
| ---- | ---- |
| 工匠 | 创建新产品数据、更新现有产品数据、删除产品、更新订单状态、更新自身工匠对象信息 |
| 中央办公室员工 | 标记产品可用、修改产品内容、管理工匠对象、管理订单对象 |
| 客户终端用户 | 间接创建订单对象 |

这些操作大多属于产品和/或订单对象的 CRUD 操作,相关子系统中的 _create _update 方法基本能满足需求。数据变更的传输通常包含以下步骤:
1. 在用户级应用程序中进行本地数据变更(创建、更新或删除)。
2. 验证数据变更,确保数据格式正确且符合数据结构要求。
3. (若适用)本地存储数据变更。
4. 将数据变更序列化并传输到工匠网关服务,执行相应操作。

3. 数据传输机制选择

数据传输可选择消息队列或 Web 服务两种方式。编写 Web 服务可能需要处理认证、授权、特定 HTTP 方法和 CRUD 操作的关联等,复杂度较高,可考虑使用现有框架(如 Flask 或 Django)。相比之下,消息队列方式更简单,可编写七个相关函数,通过队列协议的消息调用。为解决认证和授权问题,可给每个工匠分配独立队列,并对消息签名。

4. 数据流动

无论选择哪种传输机制,工匠/产品操作的数据流动大致如下:

graph LR
    A[本地工匠应用程序] --> B[创建消息并传输]
    B --> C[工匠网关服务]
    C --> D[读取、验证消息]
    D --> E[调用相应服务方法]
    E --> F[处理数据存储]

中央办公室用户对产品对象的操作、工匠与订单对象的交互等也有类似的数据流动。

5. 迭代故事

迭代故事涵盖了不同角色的需求,具体如下:
1. 工匠:向工匠网关发送数据变更,创建、更新、删除产品对象,更新订单对象,更新自身工匠对象。
2. 中央办公室用户:向工匠网关发送数据变更,管理工匠和订单对象。
3. 产品经理:激活、停用、更新产品对象。
4. 所有消息发送者:对消息进行签名,以便在处理前进行验证。

6. 消息定义

消息需处理结构化数据,内部以字典表示较为合适,因其易于序列化为 JSON 或 YAML 格式。例如,工匠视角下的产品数据字典以 JSON 格式呈现如下:

{
    "oid": "a7393e5c-c30c-4ea4-8469-e9cd4287714f",
    "modified": "2018-08-19 07:13:43",
    "name": "Example Product",
    "created": "2018-08-19 07:13:43",
    "description": "Description  TBD",
    "metadata":{
        "wood": "Cherry, Oak"
    },
    "available": false,
    "dimensions": "2½\" x 4\" x ¾\"",
    "shipping_weight": 0.5,
    "summary": "Summary TBD"
}

为完整表示消息,还需添加操作和签名信息,示例如下:

{
    "data":{
        "oid": "a7393e5c-c30c-4ea4-8469-e9cd4287714f",
        "modified": "2018-08-19 07:41:56",
        "name": "Example Product",
        "created": "2018-08-19 07:13:43",
        "description": "Cherry and oak business-card holder",
        "metadata": {
            "wood": "Cherry, Oak"
        },
        "available": true,
        "dimensions": "2½\" x 4\" x ¾\"",
        "shipping_weight": 0.5,
        "summary": "Cherry and oak business-card holder"
    },
    "operation":"update",
    "signature":"{Hash hexdigest}"
}
7. DaemonMessage 类实现

为表示发送到或来自工匠网关服务的消息,实现了 DaemonMessage 类,代码如下:

import json
from hashlib import sha512

class DaemonMessage(object):
    """
    Represents a *signed* message being sent to or received from a
    BaseDaemon instance.
    """
    ###################################
    # Class attributes/constants      #
    ###################################
    # - class-constant encoding-type for signature processes
    __encoding = 'utf-8'

    ###################################
    # Property-getter methods         #
    ###################################
    def _get_signature(self) -> str:
        if not self.data:
            raise RuntimeError(
                '%s.signature cannot be calculated because there is '
                'no data to sign' % (self.__class__.__name__)
            )
        if not self.signing_key:
            raise RuntimeError(
                '%s.signature cannot be calculated because there is '
                'no key to sign data with' % (self.__class__.__name__)
            )
        return sha512(
            bytes(
                # - We're using json.dumps to assure a consistent
                #   key-order here...
                json.dumps(self.data, sort_keys=True), self.__encoding
            ) + self.signing_key
        ).hexdigest()

    ###################################
    # Instance property definitions   #
    ###################################
    data = property(
        lambda self: self._data,
        lambda self, value: setattr(self, '_data', value),
        lambda self: delattr(self, '_data'),
        'Gets, sets, or deletes the data/content of the message'
    )

    operation = property(
        lambda self: self._operation,
        lambda self, value: setattr(self, '_operation', value),
        lambda self: delattr(self, '_operation'),
        'Gets, sets, or deletes the operation of the message'
    )

    signature = property(
        _get_signature, None, None,
        'Gets the signature of the message'
    )

    signing_key = property(
        lambda self: self._signing_key,
        lambda self, value: setattr(self, '_signing_key', value),
        lambda self: delattr(self, '_signing_key'),
        'Gets, sets, or deletes the signing_key of the message'
    )

    ###################################
    # Object initialization           #
    ###################################
    def __init__(self,
                 operation:(str,None)=None, data:(dict,None)=None,
                 signing_key:(bytes,str,None)=None
                 ):
        """
        Object initialization.
        self .............. (DaemonMessage instance, required) The instance to
                            execute against
        operation ......... (str, optional, defaults to None) The operation
                            ('create', 'update', 'delete' or 'response') that
                            the message is requesting
        data .............. (dict, optional, defaults to None) The data of the
                            message
        signing_key ....... (bytes|str, optional, defaults to None) The raw
                            data of the signing-key to be used to generate the
                            message-signature.
        """
        # - Call parent initializers if needed
        # - Set default instance property-values using _del_... methods
        self._del_data()
        self._del_operation()
        self._del_signing_key()
        # - Set instance property-values from arguments using
        #   _set_... methods
        if operation:
            self.operation = operation
        if data:
            self.data = data
        if signing_key:
            self.signing_key = signing_key

    def to_message_dict(self):
        return {
            'data': self.data,
            'operation': self.operation,
            'signature': self.signature
        }

    def to_message_json(self):
        return json.dumps(self.to_message_dict())

    @classmethod
    def from_message_dict(cls,
                          message_dict:(dict,), signing_key:(bytes,str)
                          ):
        """
        message_dict ...... (dict, required) The incoming message as a dict,
                            that is expected to have the following structure:
                            {
                                'data':dict,
                                'operation':str, # (create|update|delete|response)
                                'signature':str # (hash hex-digest)
                            }
        signing_key ....... (bytes|str, optional, defaults to None) The raw
                            data of the signing-key to be used to generate the
                            message-signature.
        """
        if type(message_dict) != dict:
            raise TypeError(
                '%s.from_message_dict expects a three-element '
                'message_dict value ({"data":dict, "signature":str, '
                '"operation":str}), but was passed "%s" (%s)' %
                (cls.__name__, message_dict, type(message_dict).__name__)
            )
        if type(signing_key) not in (bytes, str):
            raise TypeError(
                '%s.from_message_dict expects a bytes or str signing_key '
                'value, but was passed "%s" (%s)' %
                (cls.__name__, signing_key, type(signing_key).__name__)
            )
        if type(signing_key) == str:
            signing_key = bytes(signing_key, cls.__encoding)
        _data = message_dict.get('data')
        if not _data:
            raise ValueError(
                '%s.from_message_dict expects a three-element dict '
                '({"data":dict, "signature":str, "operation":str}), '
                'but was passed "%s" (%s) which did not include a '
                '"data" key' %
                (cls.__name__, message_dict, type(message_dict).__name__)
            )
        _signature = message_dict.get('signature')
        if not _signature:
            raise ValueError(
                '%s.from_message_dict expects a three-element dict '
                '({"data":dict, "signature":str, "operation":str}), '
                'but was passed "%s" (%s) which did not include a '
                '"signature" key' %
                (cls.__name__, message_dict, type(message_dict).__name__)
            )
        _operation = message_dict.get('operation')
        if not _operation:
            raise ValueError(
                '%s.from_message_dict expects a three-element dict '
                '({"data":dict, "operation":str, "operation":str}), '
                'but was passed "%s" (%s) which did not include a '
                '"operation" key' %
                (cls.__name__, message_dict, type(message_dict).__name__)
            )
        result = cls(_operation, _data, signing_key)
        if result.signature == _signature:
            return result
        raise InvalidMessageError(
            'The message %s, with a signature of "%s" did not match '
            'the expected signature. Message DENIED' %
            (_data, result.signature)
        )

    @classmethod
    def from_message_json(cls, json_in:(str,), signing_key:(bytes,str)):
        return cls.from_message_dict(json.loads(json_in), signing_key)
8. 签名密钥管理

DaemonMessage 类的签名过程依赖于签名密钥,创建签名密钥时需考虑以下因素:
- 长度 :密钥越长,破解难度越大。
- 字符多样性 :字符越多样,破解难度越大。

可使用 Python 的 os.urandom 函数生成签名密钥,示例如下:

import os
example_key = os.urandom(1024)
print(example_key)
example_key_hex = example_key.hex()
print(example_key_hex)
example_from_hex = bytes.fromhex(example_key_hex)
print(example_from_hex == example_key)

为确保密钥安全,需关注以下几点:
- 静态加密 :可使用库(如 PyCrypto)或配置 MongoDB 企业版的加密存储引擎实现,但可能增加系统复杂度。
- 传输加密 :传输密钥时必须实现加密,消息队列可使用私有证书,Web 服务可能需要公共证书颁发机构的证书。
- 密钥轮换 :定期更换密钥,降低密钥被破解的风险。

服务剖析与工匠网关服务实现

9. 签名密钥的数学原理与安全考量

签名密钥的安全性基于数学原理。从数学角度看,密钥可能的取值数量可以用公式(每个字符的可能取值数)^(字符串的字符数)来表示。例如,一个 128 字符的签名密钥,每个字符有 255 种可能取值,那么就有大约 1.09 × 10^308 种组合。即使每秒进行 10 亿次计算,每年大约进行 3.15 × 10^16 次计算,要破解这样的签名密钥在实际操作中几乎是不可能的。

在安全方面,静态加密对于当前系统规模可能过于复杂,暂不建议使用。但传输加密是必须的,因为签名密钥属于敏感信息。对于消息队列方式,可以使用私有证书进行加密;而对于 Web 服务,则可能需要从公共证书颁发机构获取证书。此外,定期进行密钥轮换能够降低密钥被破解后造成的风险。

10. 消息传输与加密的决策因素

在决定消息传输方式时,加密是一个重要的考量因素。无论是采用 Web 服务还是本地托管的消息队列,都需要创建加密证书来实现传输中的加密。消息队列方式可能允许使用私有证书,而 Web 服务可能需要公共证书颁发机构的证书。

以下是两种传输方式在加密方面的对比表格:
| 传输方式 | 加密证书要求 | 优点 | 缺点 |
| ---- | ---- | ---- | ---- |
| 消息队列 | 可使用私有证书 | 配置相对灵活,可在内部环境使用 | 对外部访问支持可能有限 |
| Web 服务 | 需要公共证书颁发机构的证书 | 适合外部访问,安全性有公信力 | 证书获取和管理成本较高 |

11. 消息处理流程总结

综合前面的内容,消息处理的完整流程如下:

graph LR
    A[本地数据变更] --> B[验证数据]
    B --> C{数据存储?}
    C -- 是 --> D[本地存储数据]
    C -- 否 --> E[序列化数据]
    D --> E
    E --> F[选择传输方式]
    F -- 消息队列 --> G[消息队列传输]
    F -- Web 服务 --> H[Web 服务传输]
    G --> I[工匠网关接收消息]
    H --> I
    I --> J[验证消息签名]
    J -- 签名有效 --> K[调用相应服务方法]
    J -- 签名无效 --> L[拒绝消息]
    K --> M[处理数据存储]
12. 代码使用示例

以下是一个简单的代码示例,展示如何使用 DaemonMessage 类创建、序列化和反序列化消息:

import os
import json
from hashlib import sha512

class InvalidMessageError(Exception):
    pass

class DaemonMessage(object):
    """
    Represents a *signed* message being sent to or received from a
    BaseDaemon instance.
    """
    ###################################
    # Class attributes/constants      #
    ###################################
    # - class-constant encoding-type for signature processes
    __encoding = 'utf-8'

    ###################################
    # Property-getter methods         #
    ###################################
    def _get_signature(self) -> str:
        if not self.data:
            raise RuntimeError(
                '%s.signature cannot be calculated because there is '
                'no data to sign' % (self.__class__.__name__)
            )
        if not self.signing_key:
            raise RuntimeError(
                '%s.signature cannot be calculated because there is '
                'no key to sign data with' % (self.__class__.__name__)
            )
        return sha512(
            bytes(
                # - We're using json.dumps to assure a consistent
                #   key-order here...
                json.dumps(self.data, sort_keys=True), self.__encoding
            ) + self.signing_key
        ).hexdigest()

    ###################################
    # Instance property definitions   #
    ###################################
    data = property(
        lambda self: self._data,
        lambda self, value: setattr(self, '_data', value),
        lambda self: delattr(self, '_data'),
        'Gets, sets, or deletes the data/content of the message'
    )

    operation = property(
        lambda self: self._operation,
        lambda self, value: setattr(self, '_operation', value),
        lambda self: delattr(self, '_operation'),
        'Gets, sets, or deletes the operation of the message'
    )

    signature = property(
        _get_signature, None, None,
        'Gets the signature of the message'
    )

    signing_key = property(
        lambda self: self._signing_key,
        lambda self, value: setattr(self, '_signing_key', value),
        lambda self: delattr(self, '_signing_key'),
        'Gets, sets, or deletes the signing_key of the message'
    )

    ###################################
    # Object initialization           #
    ###################################
    def __init__(self,
                 operation:(str,None)=None, data:(dict,None)=None,
                 signing_key:(bytes,str,None)=None
                 ):
        """
        Object initialization.
        self .............. (DaemonMessage instance, required) The instance to
                            execute against
        operation ......... (str, optional, defaults to None) The operation
                            ('create', 'update', 'delete' or 'response') that
                            the message is requesting
        data .............. (dict, optional, defaults to None) The data of the
                            message
        signing_key ....... (bytes|str, optional, defaults to None) The raw
                            data of the signing-key to be used to generate the
                            message-signature.
        """
        # - Call parent initializers if needed
        # - Set default instance property-values using _del_... methods
        self._del_data()
        self._del_operation()
        self._del_signing_key()
        # - Set instance property-values from arguments using
        #   _set_... methods
        if operation:
            self.operation = operation
        if data:
            self.data = data
        if signing_key:
            self.signing_key = signing_key

    def to_message_dict(self):
        return {
            'data': self.data,
            'operation': self.operation,
            'signature': self.signature
        }

    def to_message_json(self):
        return json.dumps(self.to_message_dict())

    @classmethod
    def from_message_dict(cls,
                          message_dict:(dict,), signing_key:(bytes,str)
                          ):
        """
        message_dict ...... (dict, required) The incoming message as a dict,
                            that is expected to have the following structure:
                            {
                                'data':dict,
                                'operation':str, # (create|update|delete|response)
                                'signature':str # (hash hex-digest)
                            }
        signing_key ....... (bytes|str, optional, defaults to None) The raw
                            data of the signing-key to be used to generate the
                            message-signature.
        """
        if type(message_dict) != dict:
            raise TypeError(
                '%s.from_message_dict expects a three-element '
                'message_dict value ({"data":dict, "signature":str, '
                '"operation":str}), but was passed "%s" (%s)' %
                (cls.__name__, message_dict, type(message_dict).__name__)
            )
        if type(signing_key) not in (bytes, str):
            raise TypeError(
                '%s.from_message_dict expects a bytes or str signing_key '
                'value, but was passed "%s" (%s)' %
                (cls.__name__, signing_key, type(signing_key).__name__)
            )
        if type(signing_key) == str:
            signing_key = bytes(signing_key, cls.__encoding)
        _data = message_dict.get('data')
        if not _data:
            raise ValueError(
                '%s.from_message_dict expects a three-element dict '
                '({"data":dict, "signature":str, "operation":str}), '
                'but was passed "%s" (%s) which did not include a '
                '"data" key' %
                (cls.__name__, message_dict, type(message_dict).__name__)
            )
        _signature = message_dict.get('signature')
        if not _signature:
            raise ValueError(
                '%s.from_message_dict expects a three-element dict '
                '({"data":dict, "signature":str, "operation":str}), '
                'but was passed "%s" (%s) which did not include a '
                '"signature" key' %
                (cls.__name__, message_dict, type(message_dict).__name__)
            )
        _operation = message_dict.get('operation')
        if not _operation:
            raise ValueError(
                '%s.from_message_dict expects a three-element dict '
                '({"data":dict, "operation":str, "operation":str}), '
                'but was passed "%s" (%s) which did not include a '
                '"operation" key' %
                (cls.__name__, message_dict, type(message_dict).__name__)
            )
        result = cls(_operation, _data, signing_key)
        if result.signature == _signature:
            return result
        raise InvalidMessageError(
            'The message %s, with a signature of "%s" did not match '
            'the expected signature. Message DENIED' %
            (_data, result.signature)
        )

    @classmethod
    def from_message_json(cls, json_in:(str,), signing_key:(bytes,str)):
        return cls.from_message_dict(json.loads(json_in), signing_key)

# 生成签名密钥
signing_key = os.urandom(1024)

# 创建消息
data = {
    "oid": "a7393e5c-c30c-4ea4-8469-e9cd4287714f",
    "modified": "2018-08-19 07:41:56",
    "name": "Example Product",
    "created": "2018-08-19 07:13:43",
    "description": "Cherry and oak business-card holder",
    "metadata": {
        "wood": "Cherry, Oak"
    },
    "available": true,
    "dimensions": "2½\" x 4\" x ¾\"",
    "shipping_weight": 0.5,
    "summary": "Cherry and oak business-card holder"
}
message = DaemonMessage(operation="update", data=data, signing_key=signing_key)

# 序列化消息
json_message = message.to_message_json()
print("序列化后的消息:", json_message)

# 反序列化消息
received_message = DaemonMessage.from_message_json(json_message, signing_key)
print("反序列化后的消息数据:", received_message.data)
12. 总结

通过对服务管理、数据传输、消息处理和签名密钥管理等方面的详细分析,我们构建了一个完整的工匠网关服务体系。在实际应用中,需要根据具体的业务需求和安全要求,选择合适的消息传输方式和加密策略。同时,要重视签名密钥的管理,确保消息的安全性和完整性。通过对 DaemonMessage 类的使用,可以方便地实现消息的创建、序列化和反序列化,为数据的传输和处理提供了有效的工具。

根据原作 https://pan.quark.cn/s/459657bcfd45 的源码改编 Classic-ML-Methods-Algo 引言 建立这个项目,是为了梳理和总结传统机器学习(Machine Learning)方法(methods)或者算法(algo),和各位同仁相互学习交流. 现在的深度学习本质上来自于传统的神经网络模型,很大程度上是传统机器学习的延续,同时也在不少时候需要结合传统方法来实现. 任何机器学习方法基本的流程结构都是通用的;使用的评价方法也基本通用;使用的一些数学知识也是通用的. 本文在梳理传统机器学习方法算法的同时也会顺便补充这些流程,数学上的知识以供参考. 机器学习 机器学习是人工智能(Artificial Intelligence)的一个分支,也是实现人工智能最重要的手段.区别于传统的基于规则(rule-based)的算法,机器学习可以从数据中获取知识,从而实现规定的任务[Ian Goodfellow and Yoshua Bengio and Aaron Courville的Deep Learning].这些知识可以分为四种: 总结(summarization) 预测(prediction) 估计(estimation) 假想验证(hypothesis testing) 机器学习主要关心的是预测[Varian在Big Data : New Tricks for Econometrics],预测的可以是连续性的输出变量,分类,聚类或者物品之间的有趣关联. 机器学习分类 根据数据配置(setting,是否有标签,可以是连续的也可以是离散的)和任务目标,我们可以将机器学习方法分为四种: 无监督(unsupervised) 训练数据没有给定...
本系统采用微信小程序作为前端交互界面,结合Spring BootVue.js框架实现后端服务及管理后台的构建,形成一套完整的电子商务解决方案。该系统架构支持单一商户独立运营,亦兼容多商户入驻的平台模式,具备高度的灵活性扩展性。 在技术实现上,后端以Java语言为核心,依托Spring Boot框架提供稳定的业务逻辑处理数据接口服务;管理后台采用Vue.js进行开发,实现了直观高效的操作界面;前端微信小程序则为用户提供了便捷的移动端购物体验。整套系统各模块间紧密协作,功能链路完整闭环,已通过严格测试优化,符合商业应用的标准要求。 系统设计注重业务场景的全面覆盖,不仅包含商品展示、交易流程、订单处理等核心电商功能,还集成了会员管理、营销工具、数据统计等辅助模块,能够满足不同规模商户的日常运营需求。其多店铺支持机制允许平台方对入驻商户进行统一管理,同时保障各店铺在品牌展示、商品销售及客户服务方面的独立运作空间。 该解决方案强调代码结构的规范性可维护性,遵循企业级开发标准,确保了系统的长期稳定运行后续功能迭代的可行性。整体而言,这是一套技术选型成熟、架构清晰、功能完备且可直接投入商用的电商平台系统。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值