DungCry.
type: channel
link: https://blog.galaxyx.top/
description: 以此热爱,以此谋生。
items: 自研python gui工具快速开发脚手架
items: 小米11 Ultra 背屏盯盘助手
items: 一个jar包启动多个jar服务
items: MySQL中的存储过程和函数
items: PHP初体验
items: 装饰模式
items: Java序列化与反序列化
items: 数据库操作
items: SFC中的问题描述
items: 状态模式

自研python gui工具快速开发脚手架
type: item
date: 2022-10-08T14:03:35+00:00
description: 自研工具框架本工具研发的目的就是在产品需求未出之前，但是仍然有需要的场景下快速实现相关工具的可视化研发1.GUI采用python的亲儿子tkinter来实现，简单快捷。程序主文件为**App.py** 2.使用本框架可以很快速的开发属于自己业务的GUI界面,只需要继承BaseWin即可。 3.本项目git地址:https://github.com/yxx1912008/python-gui.git 1.程序主界面2.程序关于界面3.配置加解密4.ak对应权限查询5.查询token权限界面基础框架完整演示动画1.项目技术栈1.httpx用于进行网络请求 2.PyYAML用于读取yaml文件 3.pyinstaller用于编译exe文件在windows系统执行 4.GUI使用python自带的tkinter框架实现 2.项目运行#建议使用python3.7或者以上版本 pip install -r requirements.txt3.编译成windows支持的exe文件pyinstaller有两种编译模式,一种支持命令行同步显示,一种直接显示，如果不了解，那就直接用第一种模式[编译不带命令行]#1.编译不带命令行 pyinstaller -F -i favicon.ico App.py # 2.编译带命令行 pyinstaller -F -i -w favicon.ico App.py4.内网离线使用因为我们公司的特殊性，很多时候需要在内网开发，因此可以通过下面的方式把相关依赖导出,内网使用。#1.安装组件导出引入的依赖 pip install pipreqs #2.导出安装包到指定目录 pip download -d ./lib -r requirements.txt -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com #3.内网离线导入 pip install --no-index --find-links=lib/ -r requirements.txt #--find-links就是存放安装文件的目录 #-r是指按照requirements.txt这个文件去安装文件目录中找需要的安装包5.python导出依赖清单#requirements.txt用来记录项目所有的依赖包和版本号。 pip freeze >requirements.txt

小米11 Ultra 背屏盯盘助手
type: item
date: 2022-09-24T02:00:00+00:00
description: 盯盘助手正式发布1.成功驱动小米11 ultra那块126*294的屏幕。2.可以使用背屏进行股票盯盘,省电又快捷。3.软件关于页面使用webview直接渲染，可以直达我的博客，后续版本将改成股票资讯页面4.平常太忙了,没时间优化界面美观度,这个空了再优化吧。背屏真实演示效果点击查看演示视频当前最新版本:V1.0.0下载地址: https://www.pgyer.com/JA7O使用方法:先去设置股票,输入股票信息即可搜索到你需要的股票中途可能需要使用邮箱登录，使用邮箱登录即可点击小窗显示，点击投屏到小窗，因为系统需要初始化,所以要等待十秒反过来手机,双击小窗，可以看到股票信息各位老铁帮忙多点点广告，开发不易。你的支持是我前进的动力！我的博客: https://blog.huochuankeji.com

一个jar包启动多个jar服务
type: item
date: 2022-03-09T06:11:00+00:00
description: 在公司开发中,遇到一个很有意思的需求。我们需要使用一个jar包，启动jar包后读取包内jar资源，释放出来并启动其他服务。期间涉及到jar包内读取资源,并且释放出来，很简单。1.将需要捆绑的包放入程序目录新建一个与src目录同级目录将捆绑的包和启动脚本放入工程目录启动脚本这里只是一个demo,以自己的项目为准#!/bin/sh source /etc/profile active=$1 jar_path=$2 echo "start other app" nohup java -jar -Dspring.profiles.active=${active} -Dfile.encoding=utf-8 ${jar_path} >otherApp.log 2>&1 & echo "app start is ok"2.pom文件增加配置增加配置是用来打包的时候把第三方jar打包进去 <build> <resources> <resource> <directory>此处填写 第一个步骤创建的目录名称</directory> <filtering>false</filtering> <includes> <include>*.jar</include> <include>*.sh</include> <include>*.bat</include> </includes> </resource> </resources> </build>3.添加示例代码该代码支持linux和windows启动jar包后再启动其他的jar服务,主要就是使用了hutool现成的工具类，很简单package cn.com.xxx.xxx.xxx.config; import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.resource.ResourceUtil; import cn.hutool.core.thread.ThreadUtil; import cn.hutool.core.util.RuntimeUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.system.SystemUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import java.io.File; import java.net.URL; import java.util.List; /** * 启动后启动其他捆绑服务 * * @author yuaxx * @Date 2022/2/27 0027 9:13 */ @Component @Slf4j public class SatrtOtherApp { /** * windows启动脚本 */ private static final String WIN_BAT = "cmd /c start \"{}\" cmd /k \"java -jar -Dfile.encoding=utf-8 -Dspring.profiles.active={} {} \""; /** * linux启动脚本 */ private static final String LINUX_SHELL_START = "start-other.sh"; /** * 描述:是否开启多jar启动 * 时间:2022/2/27 0027 9:13 */ @Value("${hyit.openOther:true}") private Boolean openOtherServer; /** * 当前激活的环境 * 时间:2022/2/27 0027 9:13 */ @Value("${spring.profiles.active:dev}") private String active; /** * 需要绑定的其他服务的名称 * 时间:2022/2/27 0027 9:13 */ @Value("${hyit.otherName:fin-mp-auth-exec.jar}") private String otherName; @PostConstruct public void init() { if (!openOtherServer) { log.error("其他服务不允许被启动"); return; }…

MySQL中的存储过程和函数
type: item
date: 2021-02-16T04:56:00+00:00
description: 存储过程和函数简单的说，存储过程就是一条或者多条SQL语句的集合。可以视为批文件，但是其作用不仅仅局限于批处理。本文主要介绍如何创建存储过程和存储函数，以及如何调用、查看、修改、删除存储过程和存储函数等。创建存储过程和函数 存储程序分为存储过程和存储函数。Mysql创建存储过程和存储函数的语句分别为CREATE PROCEDURE和CREATE FUNCTION。使用CALL语句来调用存储过程，只能用输出变量返回值。存储函数可以从语句外调用（通过引用函数名），也能返回标量值。存储过程也可以调用其他存储过程。废话少说，如下步入正文。创建存储过程 创建存储过程的基本语法格式为：CREATE PROCEDURE sp_name([proc_parameter]) [characteristics] routine_body 其中CREATE PROCEDURE为创建存储过程的关键字，sp_name为存储过程的名称，pro_parameter为指定存储过程的参数列表，其中参数列表如下：[IN|OUT|INOUT] param_name type 其中，IN表述输入参数，OUT表示输出参数，INOUT表示即可输入也可输出；param_name表述参数名称；type表示参数类型，该类型可以是MySQL数据库中的任意类型。characteristics指定存储过程的特性，有以下取值：LANGUAGE SQL:说明routine_body部分是由SQL语句组成的，当前系统支持的语言为SQL，SQL是LANGUAGE特性的唯一值。[NOT] DETERMINISTIC:指明存储过程执行的结果是否确定。DETERMINISTIC表示结果是确定的，每次执行存储过程时，相同的输入会得到相同的输出，NOT DETERMINISTIC表示结果是不确定的，相同的输入可能得到不同的输出，如果没有指定任意一个值，默认为NOT DETERMINISTIC。[CONTAINS SQL|NO SQL|READS SQL DATA|MODIFIES SQL DATA]：指明子程序使用SQL语句的限制。CONTAINS SQL表明子程序包含SQL语句，但不包含读写数据语句；NO SQL表明子程序不包含SQL语句；READS SQL DATA说明子程序包含读数据的语句；MODIFIES SQL DATA表名子程序包含写数据的语句。默认情况下，系统会指定为CONTAINS SQL。SQL SECURITY[DEFINER|INVOKER]：指明谁有权限来执行。DEFINER表示只有定义着才能执行。INVOKER表示用友权限的调用者可以执行。默认情况下，系统指定为DEFINER。COMMENT 'string'：注释信息，用来描述存储过程或函数。routine_body是SQL代码的内容，可以用BEGIN...END来表示SQL代码的开始和结束。 我们从最简单的存储过程开始说起，如下是不包含任何参数的存储过程，代码为：CREATE PROCEDURE Proc() BEGIN SELECT * FROM tb_score; END; 我们定义了一个名称为Proc的存储过程，该过程是用来查询tb_score（该表接上篇博客，已存在）数据库表中的所有数据。 第一张图是通过执行sql语句查询到tb_score表中的数据。第二张图是创建存储过程，其中第一句DELIMITER //是将MySQL的结束符设置为//，因为MySQL默认的结束符为分号，为了避免与存储过程中SQL语句结束符相冲突，需要DELIMITER改变存储过程的结束符，并以“END //”结束存储过程。第三张图是调用存储过程，在调用存储过程之前先将MySQL结束符恢复为默认的分号（DELIMITER ;）然后通过CALL Proc()调用。 接下来我们讲解MySQL存储过程中的参数IN、OUT、INOUT，IN作为输入，将输入作为参数传输到存储过程的执行当中去；OUT作为输出，将存储过程的输出通过参数传出来，而INOUT参数可以同时作为输入和输出。 还是通过存储过程查询tb_score表，不过这次我们要查询课程号为1（cID=1）的所有学生的成绩，存储过程定义为：CREATE PROCEDURE Proc_cID(IN classID INT) BEGIN SELECT * FROM tb_score WHERE cID=classID; END; 如我们需要查询课程号为1的学生的人数和平均成绩，则存储过程定义如下：CREATE PROCEDURE Proc_AVG(IN classID INT,OUT total INT,OUT a_s FLOAT) BEGIN SELECT COUNT(*),AVG(grade) I…

PHP初体验
type: item
date: 2020-11-16T14:58:38+00:00
description: PHP初体验 提笔写初体验总不知道从何说起，直接聊PHP中的函数、PHP网络技术、数据库操作、PHP模板等感觉又不是初体验。最后还是决定从PHP的面向对象、PHP的魔术方法、PHP的反射、PHP中的异常和错误这4个方面简单介绍一下。PHP面向对象的“形”与“本” 这里我们就不给面向对象下定义了，不过我们还是要说一下类和对象的。类是对象的抽象组织，对象是类的具体存在。接下来我们就拿PHP为例，来探讨一下对象的“形”与“本”的问题。 在PHP中，每个类的定义都是以关键字class开头，后面是类名和一对花括号，括号中包含类成员和方法的定义。如下是一个简单类的定义： class Person {public $name; private $age; private $sex; public static $information = "I come from the earth"; public function __construct($name="zhangsan", $age=23, $sex="male") { $this->sex = $sex; $this->age = $age; $this->name = $name; } public function sayHello(){ echo "My name is $this->name and I am $this->age years old. I am a $this->sex.\r\n"; echo self::$information; echo " "; }}$person = new Person();$person->name = 'Lisi';$person->sayHello();echo serialize($person);//输出结果如下：//My name is Lisi and I am 23 years old. I am a male. I come from the earth//O:6:"Person":3:{s:4:"name";s:4:"Lisi";s:11:"Personage";i:23;s:11:"Personsex";s:4:"male";}Person类 当把类对象序列化输出时，可以看出类对象在存储时类似于数组的形式。那么类对象与数组从本质上又有什么区别与联系呢？接下来从对象“本”来分析一下PHP对对象的底层实现。如下是PHP源码中对变量的定义：变量定义typedef struct _zval_struct zeal; //存储变量的类型struct _zval_struct { /* Variable information */ zvalue_value value; /* 存储变量的值 */ zend_uint refcount; /* 变量引用数 */ zend_uchar type; /* 变量类型 */ zend_uchar is_ref; /* 变量是否被引用 */};typedef union _zvalue_value { long lval; /* 长整型 */ double dval; /* double型 */ struct { char *val; int len; } str; /* String型 */ HashTable *ht; /* array型 */ zend_object_value obj; /* 对象型 */} zvalue_value;对象底层实现typedef struct _zend_object { zend_class_entry *ce; //类入口，存储该对象的类结构，相当于类指针 HashTable *properties; //对象属性组成的HashTable HashTable *guards; /* 阻止递归调用 */} zend_object;typedef struct _zend_guard { zend_bool in_get; zend_bool in_set; zend_bool in_unset; zend_bool in_isset; zend_bool dummy; /* sizeof(zend_guard) must not be equal to sizeof(void*) */} zend_guard;//由于zend_class_entry源码较多且复杂，因此此处省略。PHP源码中变量、对象的定义 通过上面的代码我们也对PHP如何存储对象有了初步的认识，那对象与数组又是什么关系呢？通过PHP的源码可得，zvalue_object结构中有一个HashTable的类型，它就是存储数组的。PHP对象的结构体中不仅有HashTable（用…

装饰模式
type: item
date: 2020-07-04T22:13:37+00:00
description: 装饰模式装饰模式：一种常见的模式，动态地给一个对象添加一些额外的职责，就增加功能来看，装饰模式比生成子类更加灵活。装饰模式的通用类图如下： Component抽象构件：Component是一个接口或者是抽象类，就是我们需要装饰的核心对象。在装饰模式中，必然有一个最基本、最核心、最原始的接口或抽象类充当Component构件。ConcreteComponent是Component接口或抽象类的实现。Decrator装饰角色：一般是一个抽象类，实现接口或者抽象方法，其属性必然有一个private变量指向Component抽象构件。ConcreteDecrator具体装饰类。其通用类图源码如下：public abstract class Component {public abstract void doSomething();}public class ConcreteComponent extends Component{@Override public void doSomething() { // TODO Auto-generated method stub System.out.println("this is a ConcreteComponent"); }}public abstract class Decrator extends Component{private Component component = null; public Decrator(Component com){ this.component = com; } @Override public void doSomething(){ this.component.doSomething(); }}public class ConcreteDecrator1 extends Decrator{public ConcreteDecrator1(Component com) { super(com); // TODO Auto-generated constructor stub } public void doSomething() { // TODO Auto-generated method stub this.method1(); super.doSomething(); } private void method1(){ System.out.println("this is a ConcreteDecrator"); }}public class ConcreteDecrator2 extends Decrator{public ConcreteDecrator2(Component com) { super(com); // TODO Auto-generated constructor stub } public void doSomething(){ super.doSomething(); this.method2(); } private void method2(){ System.out.println("this is another ConcreteDecrator"); }}public class Client {/** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Component component = new ConcreteComponent(); component = new ConcreteDecrator1(component); component = new ConcreteDecrator2(component); component.doSomething(); }}View Code装饰模式的应用最近刚刚过了双十一的网上购物狂潮，购买的商品也万里迢迢的飞奔而来，拿到快递那一刻的喜悦简直感觉自己就是世界上最幸福的，然而如果你买的是服装，相信你一定深有体会，为什么自己穿上就没有那种感觉了呢，冥思苦想，应该是商家对服装的装饰比较到位，下面我们就解析一下这种“差距”。首先我们先看一下类图： Clothes类是一个抽象类，是我们在网上看到的各种服装的总称，它有一个展示其参数的方法parameter()，提供给买家参考。Coat类是服装中的具体的一件上衣。Decrator是抽象的装饰类，其中包括一个Clothes对象。Model具体装饰类，让时装模特穿上这个外套。Scarf具体的装饰类，为外套装饰一条围巾。如下是类图的通用源码实现：public abstract …

Java序列化与反序列化
type: item
date: 2020-07-01T11:47:42+00:00
description: Java序列化与反序列化Java提供了两种对象持久化的方式，分别为序列化和外部序列化序列化 在分布式环境下，当进行远程通信时，无论是何种类型的数据，都会以二进制序列的形式在网络上传输。序列化是一种将对象以一连串的字节描述的过程，用于解决在对对象流进行读写操作时所引发的问题。序列化可以将对象的状态写在流里进行网络传输，或者保存到文件、数据库等系统中，并在需要时把该流读取出来重新构造一个相同的对象。 所有实现序列化的类都必须实现Serializable接口，Serializable接口位于java.lang包中，没有任何实现方法，使用一个输出流（例如FileOutputStream）来构造一个ObjectOutputStream对象，紧接着使用该对象的writeObject(Object obj)方法就可以将obj对象写出，要恢复时可以使用其对应的输入流ObjectInputStream.序列化有以下两个特点：如果一个类能被序列化，那么它的子类也能够被序列化。由于static(静态)代表类的成员，transient（Java关键字，如果用transient声明一个实例变量，当对象存储时，它的值不需要维持）代表对象的临时数据，因此被声明为这两种类型的数据成员是不能够被序列化的。 如下是序列化的代码，首先声明一个Student类继承Serializable接口，由代码中的输出（注释即为输出内容）可得静态变量SCHOOLNAME和SCHOOLID的值发生了变化，难道是静态变量也被序列化了吗？其实不是的，因为在当前的运行程序中，Student类的静态变量的值已经发生了变化，如果真的已经序列化了，那么我们将序列化的那个函数去掉，让程序从SourceFile/Student文件中反序列化，那么得到的SCHOOLNAME应该为HeBei University，SCHOOLID的值为2，然而当我们去掉序列化代码，直接从文件反序列化，输出SCHOOLNAME是Beijing University of Post and Telecommunications，SCHOOLID为1，说明static变量并没有被序列化。public class Student implements Serializable{/** * */ private static final long serialVersionUID = 1L; public String name; public int id; public static String SCHOOLNAME = "Beijing University of Post and Telecommunications"; private static int SCHOOLID = 1; public Student(){ name = "zhangsan"; id = 2018111846; } public Student(String name, int id){ this.name = name; this.id = id; } public static void setSchool(String school_name, int school_id){ SCHOOLID = school_id; SCHOOLNAME = school_name; } public static int getSchoolID(){ return SCHOOLID; }}public class Test {/** * @param args */ public static void main(String[] args) { Student student = new Student(); student.setSchool("Hebei University",2); serialize(student); deserialize(); } /** * 序列化student对象 * @param student */ private static void serialize(Student student){ try { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("SourceFile/Student")); oos.writeObject(student); oos.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { /…

数据库操作
type: item
date: 2020-06-07T10:01:51+00:00
description: 连接数据库操作： string s = "Data Source=服务器名称;Initial Catalog=数据库名称;Integrated Security=True"; //连接数据库所需要的字符串 SqlConnection con = new SqlConnection(); con.ConnectionString = s; con.Open();将数据库信息显示在datagridview中 string selectCommandText = "select * from 治疗信息(表名)"; DataSet ds = new DataSet(); con.ConnectionString = s; sda = new SqlDataAdapter(selectCommandText, con); sda.Fill(ds); dataGridView1.DataSource = ds.Tables[0]; //完成数据绑定，datagridview控件可以显示DataGridView动态更新数据库： string s = "Data Source=A3135;Initial Catalog=TotalNumber;Integrated Security=True"; SqlConnection con = new SqlConnection(); SqlDataAdapter sda = new SqlDataAdapter(); BindingSource bs = new BindingSource(); DataTable dt = new DataTable(); 绑定数据库按钮： con.ConnectionString = s; string selectCommandText = "select * from 数据源"; sda = new SqlDataAdapter(selectCommandText, con); sda.Fill(dt); bs.DataSource = dt; //绑定BindingSource dataGridView1.DataSource = dt; //完成数据绑定，datagridview可以显示数据动态更新数据库按钮： SqlCommandBuilder cb = new SqlCommandBuilder(sda); sda.Update((DataTable)bs.DataSource); 获取当前行的信息： RowNumber = SearchAnswer.CurrentCell.RowIndex; //SearchAnswer为DataGridview名 SickNumber = (int)SearchAnswer.Rows[RowNumber].Cells[0].Value; string name = (string)SearchAnswer.Rows[RowNumber].Cells[1].Value; 设置当前选中行为一整行： dgv.SelectionMode = DataGridViewSelectionMode.FullRowSelect; //选中状态为当前行 设置当前列不可排序: dgv.Columns[i].SortMode = DataGridViewColumnSortMode.NotSortable; //禁止对列进行排序DataGridView实现数据库的修改 删除选中行： int RowNumber = dataGridView1.CurrentCell.RowIndex; //获取当前选中行的行号 dataGridView1.Rows.RemoveAt(RowNumber); SqlCommandBuilder cb = new SqlCommandBuilder(sda); //更新数据库 sda.Update((DataTable)bs.DataSource); 动态添加行，显示DataTable中的内容： private void SetupColumns(DataGridView dgv, DataTable dt) { dgv.AutoGenerateColumns = false; //设置datagridview不可自动创建列 //手动为datagridview添加新列 dgv.Columns.Clear(); DataGridViewTextBoxColumn nameColumn = new DataGridViewTextBoxColumn(); nameColumn.HeaderText = "病历名称"; //列名称 nameColumn.ValueType = typeof(string); //…

SFC中的问题描述
type: item
date: 2020-04-10T21:14:31+00:00
description: 本文主要描述了在大规模的网络环境中部署服务功能存在的一些问题，还提出了几个关键领域，即SFC工作组将要探讨的关于SFC结构、工作协议、相关文档。1.问题描述 SFC工作组致力于解决的几个服务部署中存在的问题 1.1 拓扑相互依赖 网络服务的部署依赖于网络拓扑（物理网络、虚拟网络、混合网络），例如，当流量需要通过防火墙，则需要在网络路径上部署防火墙，或者引导网络流通过防火墙。服务传送上的限制可能抑制网络运营商的资源利用率、降低灵活性。拓扑的耦合限制了服务功能的部署和选择：服务功能在拓扑中的位置是固定的，因此部署和选择服务功能需要考虑拓扑信息。如下是一个简单例子：网络服务将服务负载均衡器作为一个默认网关，当网络服务响应流量负载不均衡时，所有通过服务的流量必须经过负载均衡器，强制网络管理员创建一个复杂的路由机制或者额外的接口来提供另一种拓扑结构。 1.2 配置复杂 1.3 限制高可用性 1.4服务功能顺序的一致性 服务功能本身是相互独立的，然而对于管理员来说，许多服务功能有一个严格的安放顺序。管理员没有一致的规则放置这些服务功能，因此选择链的顺序是复杂、繁琐的。 1.5 应用服务策略 服务功能依据拓扑信息来选择服务策略，由于扩展、延迟、复杂性的原因，收集拓扑信息越来越不可行，中心拓扑信息并不包含详细信息，这也迫使服务功能单独执行细粒度的分类，换句话说，拓普信息太粗粒度。 1.6 传输依赖 服务功能和拓扑的耦合性需要服务功能支持许多传输封装或者传输网关功能。 1.7 弹性服务交付 1.8 流量选择标准 特定段的所有流量强制通过该段内的所有服务不管该流量是否需要该服务，这是由于转发拓扑决定了转发规则。在一些部署中，通过路由策略或者访问控制过滤来细粒度流量选择，但这也会导致配置复杂，缺乏灵活性。 1.9 限制端到端服务的透明性 1.10 每一个服务功能的分类/重新分类 每一个服务功能前都需要有分类器，分类器间是相互独立的，不同服务功能的分类器功能是不同的，服务功能可能无法利用其他服务功能的分类结果。 1.11 对称的流量 服务功能链根据服务功能的要求决定是单向还是双向的，许多服务功能如DPI、防火墙都是双向的为了确保流状态的一致性。 1.12 多供应商的服务功能服务功能链 服务功能链希望通过服务部署解决上述问题，SFC工作组针对如下方面探讨解决方案。 2.1 服务覆盖网络 服务功能链利用特定的覆盖网络创建服务拓扑，覆盖服务提供服务功能的连接。创建一个上层的网络拓扑，该拓扑允许操作者利用覆盖层或者底层网络创建服务功能路径，依据需要在网络上部署服务功能。 从服务拓扑的角度，服务功能可以被看做资源消耗，按照实际需要的顺序连接服务功能（资源），不需要底层网络的改变，很容易的添加网络功能。 服务层能够提供特定服务的信息来进行服务相关的故障诊断。 2.2 业务划分 分类器选择哪些流进入服务网络，分类规则可根据设备能力、顾客需求、售后服务。分类器决定流通过哪个服务功能链，后续的分类根据服务功能链中的重新分类器改变服务功能的顺序。 2.3 SFC封装 SFC的封装可以在数据平面创建服务功能链，以及传送链的信息，如链标识、OAM状态。SFC的封装包含数据平面的元数据，元数据可用逻辑分类结点和服务功能以及服务功能间的信息交换，元数据不是用来传递数据包的。元数据可以包含先前分类器的分类结果、外部的资源信息，服务功能根据元数据进行本地策略决策。 元数据除了共享信息外，从网络拓扑上解耦策略，移除了每个服务功能前的分类器。3 安全考虑 服务覆盖网络：依据存在的传输协议创建，因此服务覆盖网络需要传输协议的安全机制。如操作者需要认证或者加密，可以使用传输协议的安全机制提供了这样的功能。

状态模式
type: item
date: 2020-04-06T15:25:43+00:00
description: 状态模式状态模式：当一个对象内在状态改变时允许改变行为，这个对象看起来像改变了其类（Allow an object to alter its behavior when its internal state changes. The object will appear to change its class）。状态模式的核心是封装，状态的变更引起了行为的变更，从外部看起来就好像这个对象对应的类发生了改变。状态模式的具体应用本篇采用设计模式之禅中的实例，关于电梯状态改变，从而引发电梯行为改变的例子。我们首先对假设电梯有四种状态：敞门状态、闭门状态、运行状态、停止状态，电梯有四个动作：开门、关门、运行、停止，我们通过下表表示电梯状态和动作之间的关系： 开门（open）关门（close）运行（run）停止（stop）敞门状态●★●●闭门状态★●★★运行状态●●●★停止状态★●★●电梯动作和状态对应表（●表示不允许，★表示允许） 当然我们在程序中可以通过设置电梯的状态，从而执行每个动作时判断当前电梯处于什么状态，从而决定电梯执行的动作，然而程序中会存在大量的switch......case语句。接下来我们直接介绍通过状态模式的方式实现电梯状态与动作的对应关系。通用类图如下：public abstract class LiftState {private Context context; public void setContext(Context text){ this.context = text; } public Context getContext(){ return this.context; } public abstract void open(); public abstract void close(); public abstract void run(); public abstract void stop();}public class OpenningState extends LiftState{@Override public void open() { // TODO Auto-generated method stub System.out.println("电梯门开启......"); } @Override public void close() { // TODO Auto-generated method stub super.getContext().setLiftState(Context.closingState); super.getContext().getLiftState().close(); } @Override public void run() { // TODO Auto-generated method stub } @Override public void stop() { // TODO Auto-generated method stub } }public class ClosingState extends LiftState{@Override public void open() { // TODO Auto-generated method stub super.getContext().setLiftState(Context.openningState); super.getContext().getLiftState().open(); } @Override public void close() { // TODO Auto-generated method stub System.out.println("电梯门关闭......"); } @Override public void run() { // TODO Auto-generated method stub super.getContext().setLiftState(Context.runningState); super.getContext().getLiftState().run(); } @Override public void stop() { // TODO Auto-generated method stub super.getContext().setLiftState(Context.stoppingState); super.getContext().getLiftState().stop(); } }public class RunningState extends LiftState{@Override public void open() { /…