2024年11月

大家好,我是汤师爷~

什么是权限?

权限,简单来说,是系统中控制用户行为的一套规则和机制,用来限制每个用户在系统中可以访问的页面、功能和查看的信息。

权限系统通过设定不同的用户角色,并将权限分配给这些角色,来控制用户在系统中可使用的功能和可查看的信息。这是企业进行权限管理的有效工具。

权限的设置通常基于用户的角色和职责。例如,在新零售 SaaS 系统中,运营人员需要管理商品和订单,但他们不需要也不应该访问财务数据。相反,财务人员需要查看交易和财务报表,但不需要操作商品、库存。

通过权限控制,系统确保每个用户只能在其职责范围内操作,既提高了工作效率,又保护了敏感信息。

为什么需要权限系统

在 SaaS 系统中,如果没有权限管理,所有用户都可以随意访问和修改系统中的数据,这将导致混乱和安全隐患。

企业的数据通常包含财务报表、客户资料、商业机密等敏感信息。如果所有员工都能访问这些数据,可能会导致信息泄露,甚至被出售给商家的竞争对手,给企业带来严重后果。

权限系统有助于规范业务流程,提高员工工作效率。不同岗位有不同的职责和权限,例如,财务人员需要查看和处理财务数据,而销售人员则需要管理客户信息。如果没有明确的权限划分,员工可能会接触到与自身职责无关的工作,导致职责不清,影响工作效率。

权限系统还便于审计和追责。当出现问题时,企业可以通过权限日志追踪到具体的操作人员,明确责任归属。

总的来说,权限系统是企业信息安全和规范管理的重要保障。它确保不同岗位的员工只能在授权范围内操作,既提高了工作效率,又保护了企业的核心利益。

因此,构建一个完善的权限系统对于任何注重安全和效率的企业来说都至关重要。

权限模型方案

设计权限系统时,我们可以借鉴多种技术模型,每种模型都有其独特的特点和适用场景。

常见的权限模型包括
ACL
(访问控制列表)、
RBAC
(基于角色的访问控制)等。这些模型各有优劣,适用于不同规模和复杂程度的系统。

在实际应用中,我们需要深入分析业务需求,权衡各种模型的利弊,并根据系统的具体情况灵活设计和调整。接下来,让我们一起探讨几种常见的权限模型。

ACL 模型

首先,让我们探讨一下
ACL
模型,全称为
Access Control List
,即访问控制列表。这是一种直接而简洁的权限管理方式。ACL模型主要包含两个关键元素:

  • 用户(User)
    :系统的实际使用者,可以是个人、组织或系统实体。
  • 权限(Permission)
    :明确定义用户可以执行的操作或访问的资源,如查看报表、编辑文档等。

ACL模型特别适合权限需求相对简单、直接的系统环境。当系统功能点较少,用户与权限之间可以建立清晰、直接的对应关系时,ACL模型能够提供高效、易于管理的权限控制方案。

RBAC0 模型

接下来,介绍一下
RBAC0
模型。作为角色权限控制的基础模型,RBAC 代表
Role-Based Access Control
,即基于角色的访问控制。这种模型通过引入"角色"的概念,巧妙地解决了用户与权限之间的复杂关系。

在 RBAC0 模型中,我们不再直接将权限赋予用户,而是通过角色这个中间层来实现权限分配。这种设计带来了极大的灵活性和可管理性。

例如,当一个新员工加入企业时,我们只需要为其分配适当的角色,而不必逐一设置权限。同样,当某个角色的权限需要调整时,我们只需修改该角色的权限设置,所有拥有该角色的用户都会自动更新权限。RBAC0 模型的核心组成元素:

  • 用户(User)
    :系统的实际使用者,可以是个人、组织或系统实体。
  • 角色(Role)
    :角色是一系列权限的集合,它像一座桥梁,连接了用户和权限。系统管理员可以根据业务需求创建不同的角色,如"运营经理"、"门店店长"等。一个角色可以拥有多种权限,而一个用户也可以被赋予多个角色,这种多对多的关系大大增强了系统的灵活性。
  • 权限(Permission)
    :定义了用户可以在系统中执行的具体操作。权限可以是粗粒度的,如访问某个模块的权限;也可以是细粒度的,如对某条数据的增删改查权限。权限的设计需要充分考虑业务需求和安全性,既要保证用户能够高效工作,又要防止越权操作。常见的权限类型包括页面访问权限、功能操作权限、数据查看权限等。

RBAC1 模型

RBAC1 模型是 RBAC0 模型的进阶版本,引入了角色继承这一关键概念。这一扩展为权限系统带来了更高的灵活性和效率。

RBAC1 模型允许角色之间建立层级关系。在这种结构中,高级角色不仅拥有自身的特定权限,还能自动继承低级角色的所有权限。

这种设计模拟了现实世界中的组织结构,使得权限系统更贴近实际需求。

RBAC2 模型

RBAC2模型在RBAC0模型的基础上引入了角色约束控制机制,增加了责任分离关系。

这种模型规定了在分配权限给角色、将角色赋予用户,以及用户激活某个角色时必须遵守的强制性规则。RBAC2模型主要包含以下三种约束:

1、互斥关系角色

这种约束确保同一用户不能同时拥有相互制约的角色。例如,在运营部门中,用户运营和渠道运营可能被设置为互斥角色。

一个用户只能被分配其中一个角色,不能同时担任两者。这种设置体现了职责分离的原则,有助于防止权力过度集中和潜在的利益冲突。

2、基数约束

这种约束限制了角色分配的数量和范围。它可以限制一个角色可以分配给的用户数量,控制单个用户可以拥有的角色数目,以及一个角色可以拥有的权限数量。

通过这种方式,系统可以有效地控制高级权限的分配,防止权限过度扩散,从而增强系统的安全性和可管理性。

3、先决条件角色

这种约束建立了角色之间的依赖关系。如果用户想要获得某个高级角色,必须先获得其下级角色。这种设计确保了用户在获得更高权限之前,已经具备了必要的经验和资格。

总体来说,不同的权限模型有不同的适用场景:

  • ACL 模型:适用于小型、简单的系统,权限需求不复杂。
  • RBAC0 模型:引入角色,方便管理,适用于一般的权限需求。
  • RBAC1 模型:增加角色继承,适用于权限层级分明的系统。
  • RBAC2 模型:增加角色约束控制,适用于对权限管理要求高的系统。

选择合适的权限模型,需要根据系统的规模、复杂程度和安全需求来决定。

本文已收录于,我的技术网站:
tangshiye.cn
里面有,算法Leetcode详解,面试八股文、BAT面试真题、简历模版、架构设计,等经验分享。

一、概述

上篇文章介绍了木舟如何上传模块热部署,那么此篇文章将介绍如何利用HTTP网络组件接入设备,那么有些人会问木舟又是什么,是什么架构为基础,能做什么呢?

木舟 (Kayak) 是什么?

木舟(Kayak)是基于.NET6.0软件环境下的surging微服务引擎进行开发的, 平台包含了微服务和物联网平台。支持异步和响应式编程开发,功能包含了物模型,设备,产品,网络组件的统一管理和微服务平台下的注册中心,服务路由,模块,中间服务等管理。还有多协议适配(TCP,MQTT,UDP,CoAP,HTTP,Grpc,websocket,rtmp,httpflv,webservice,等),通过灵活多样的配置适配能够接入不同厂家不同协议等设备。并且通过设备告警,消息通知,数据可视化等功能。能够让你能快速建立起微服务物联网平台系统。

那么下面就为大家介绍如何从创建组件、协议、设备网关,设备到设备网关接入,再到设备数据上报,把整个流程通过此篇文章进行阐述。

二、网络组件

1.编辑创建HTTP协议的网络组件,可以选择共享配置和独立配置(独立配置是集群模式),然后可以选择开启swagger和webservice.

开启成功后,可以看看swagger 是否可以访问

又或者是访问一下中间服务,以上篇文章上传的Testapi 模块为例:

三、自定义协议

  • 如何创建自定义协议模块

如果是网络编程开发,必然会涉及到协议报文的编码解码处理,那么对于平台也是做到了灵活处理,首先是协议模块创建,通过以下代码看出协议模块可以添加协议说明md文档, 身份鉴权处理,HTTP路由,消息编解码,元数据配置。下面一一介绍如何进行编写

  public classDemo5ProtocolSupportProvider : ProtocolSupportProvider
{
public override IObservable<ProtocolSupport>Create(ProtocolContext context)
{
      var support = new ComplexProtocolSupport();
    support.Id = "demo5";
    support.Name = "演示协议5";
    support.Description = "演示协议5";
support.AddDocument(MessageTransport.Http,
"Document/document-http.md");
    support.AddAuthenticator(MessageTransport.Http,
newDemo5Authenticator());
     support.AddRoutes(MessageTransport.Http,
new List<BasicMessageCodec>() {
   BasicMessageCodec.DeviceOnline,
   BasicMessageCodec.ReportProperty,
  BasicMessageCodec.WriteProperty,
  BasicMessageCodec.ReadProperty,
BasicMessageCodec.Event
}.Select(p
=>HttpDescriptor.Instance(p.Pattern)
.GroupName(p.Route.GroupName())
.HttpMethod(p.Route.HttpMethod())
.Path(p.Pattern)
.ContentType(MediaType.ToString(MediaType.ApplicationJson))
.Description(p.Route.Description())
.Example(p.Route.Example())
).ToList());
support.AddMessageCodecSupport(MessageTransport.Http, ()
=> Observable.Return(newHttpDeviceMessageCodec()));
support.AddConfigMetadata(MessageTransport.Http, _httpConfig);
returnObservable.Return(support);

}

}

1. 添加协议说明文档如代码:
support.AddDocument(MessageTransport.Http,
"
Document/document-http.md
"
);,文档仅支持
markdown
文件,如下所示

### 使用HTTP推送设备数据

上报属性例子:

POST
/{productId}/{deviceId}/properties/report
Authorization:{产品或者设备中配置的Token}
Content
-Type: application/json

{
"properties":{"temp":11.5}
}

上报事件例子:

POST
/{productId}/{deviceId}/event/{eventId}
Authorization:{产品或者设备中配置的Token}
Content
-Type: application/json

{
"data":{"createtime": ""}
}

2. 添加身份鉴权如代码:
support.AddAuthenticator(MessageTransport.Http, new Demo5Authenticator()) ,自定义身份鉴权
Demo5Authenticator
代码如下:

       public classDemo5Authenticator : IAuthenticator
{
public IObservable<AuthenticationResult>Authenticate(IAuthenticationRequest request, IDeviceOperator deviceOperator)
{
var result = Observable.Return<AuthenticationResult>(default);if (request isDefaultAuthRequest)
{
var authRequest = request asDefaultAuthRequest;
deviceOperator.GetConfig(authRequest.GetTransport()
==MessageTransport.Http?"token": "key").Subscribe( config =>{var password = config.Convert<string>();if(authRequest.Password.Equals(password))
{
result
=result.Publish(AuthenticationResult.Success(authRequest.DeviceId));
}
else{
result
= result.Publish(AuthenticationResult.Failure(StatusCode.CUSTOM_ERROR, "验证失败,密码错误"));
}
});
}
elseresult= Observable.Return<AuthenticationResult>(AuthenticationResult.Failure(StatusCode.CUSTOM_ERROR, "不支持请求参数类型"));returnresult;
}
public IObservable<AuthenticationResult>Authenticate(IAuthenticationRequest request, IDeviceRegistry registry)
{
var result = Observable.Return<AuthenticationResult>(default);var authRequest = request asDefaultAuthRequest;
registry
.GetDevice(authRequest.DeviceId)
.Subscribe(
async p =>{var config= await p.GetConfig(authRequest.GetTransport() == MessageTransport.Http ? "token" : "key");var password= config.Convert<string>();if(authRequest.Password.Equals(password))
{
result
=result.Publish(AuthenticationResult.Success(authRequest.DeviceId));
}
else{
result
= result.Publish(AuthenticationResult.Failure(StatusCode.CUSTOM_ERROR, "验证失败,密码错误"));
}
});
returnresult;
}
}

3. 添加Http路由代码
support.AddRoutes,那么如何配置呢,代码如下:

    public static BasicMessageCodec ReportProperty =>
 new BasicMessageCodec("/*/properties/report", typeof(ReadPropertyMessage), route => route.GroupName("属性上报")
.HttpMethod(
"Post")
.Description(
"上报物模型属性数据")
.Example(
"{\"properties\":{\"属性ID\":\"属性值\"}}"));

4.添加消息编解码代码
support.AddMessageCodecSupport(MessageTransport.Http, () => Observable.Return(
new
HttpDeviceMessageCodec())), 可以自定义编解码,
HttpDeviceMessageCodec
代码如下:

  public classHttpDeviceMessageCodec : DeviceMessageCodec
{
private readonlyMessageTransport _transport;public HttpDeviceMessageCodec() : this(MessageTransport.Http)
{
}
private staticDefaultHttpResponseMessage Unauthorized(String msg)
{
return newDefaultHttpResponseMessage()
.ContentType(MediaType.ApplicationJson)
.Body(
"{\"success\":false,\"code\":\"unauthorized\",\"message\":\"" + msg + "\"}")
.Status(HttpStatus.AuthorizationFailed);
}
private staticDefaultHttpResponseMessage BadRequest()
{
return newDefaultHttpResponseMessage()
.ContentType(MediaType.ApplicationJson)
.Body(
"{\"success\":false,\"code\":\"bad_request\"}")
.Status(HttpStatus.RequestError);
}
publicHttpDeviceMessageCodec(MessageTransport transport)
{
_transport
=transport;
}
public override IObservable<IDeviceMessage>Decode(MessageDecodeContext context)
{
if (context.GetMessage() isHttpRequestMessage)
{
returnDecodeHttpRequestMessage(context);
}
return Observable.Return<IDeviceMessage>(default);
}
public override IObservable<IEncodedMessage>Encode(MessageEncodeContext context)
{
return Observable.Return<IEncodedMessage>(default);
}
private IObservable<IDeviceMessage>DecodeHttpRequestMessage(MessageDecodeContext context)
{
var result = Observable.Return<IDeviceMessage>(default);var message =(HttpExchangeMessage)context.GetMessage();

Header
? header = message.Request.GetHeader("Authorization");if (header == null || header.Value == null || header.Value.Length == 0)
{
message
.Response(Unauthorized(
"Authorization header is required")).ToObservable()
.Subscribe(p
=> result = result.Publish(default));returnresult;
}
var httpToken = header.Value[0];var paths = message.Path.Split("/");if (paths.Length == 0)
{
message.Response(BadRequest()).ToObservable()
.Subscribe(p
=> result = result.Publish(default));returnresult;
}
String deviceId
= paths[1];
context.GetDevice(deviceId).Subscribe(
async deviceOperator =>{var config = deviceOperator==null?null: await deviceOperator.GetConfig("token");var token = config?.Convert<string>();if (token == null || !httpToken.Equals(token))
{
awaitmessage
.Response(Unauthorized(
"Device not registered or authentication failed"));
}
else{var deviceMessage = awaitDecodeBody(message, deviceId);if (deviceMessage != null)
{
await message.Success("{\"success\":true,\"code\":\"success\"}");
result
=result.Publish(deviceMessage);
}
else{awaitmessage.Response(BadRequest());
}
}
});
returnresult;
}
private async Task<IDeviceMessage> DecodeBody(HttpExchangeMessage message,stringdeviceId)
{
byte[] body = new byte[message.Payload.ReadableBytes];
message.Payload.ReadBytes(body);
var deviceMessage = awaitTopicMessageCodec.Dodecode(message.Path, body);
deviceMessage.DeviceId
=deviceId;returndeviceMessage;
}
}

5.添加元数据配置代码
support.AddConfigMetadata(MessageTransport.Http, _httpConfig);
_httpConfig
代码如下

        private readonly DefaultConfigMetadata _httpConfig = newDefaultConfigMetadata("Http认证配置","token为http认证令牌")
.Add(
"token", "token", "http令牌", StringType.Instance);
  • 如何加载协议模块,协议模块包含了协议模块支持添加引用加载和上传热部署加载。

引用加载模块

上传热部署协议模块

四、设备网关

创建设备网关

五、产品管理

以下是添加产品。

设备接入

六、设备管理

添加设备

HTTP认证配置

创建告警阈值

七、测试

利用Postman 进行测试,以调用http://127.0.0.1:168/{productid}/{deviceid}/properties/report 为例,Authorization设置:123456

1.正常数据测试

2. 如果是选用Get方式调用,会因为找不到ServiceRoute而返回错误。

3. 把Authorization改成1111,会返回错误
Device not registered or authentication failed
,从而上报数据失败

以上上传的数据可以在设备信息-》运行状态中查看

告警信息可以在超临界数据中查看

七、总结

以上是基于HTTP网络组件设备接入,现有平台网络组件可以支持TCP,MQTT,UDP,CoAP,HTTP,Grpc,websocket,rtmp,httpflv,webservice,tcpclient, 而设备接入支持TCP,UDP,HTTP网络组件,后面会陆续添加支持所有网络组件接入,后面我也会陆续介绍其它网路组件设备接入 ,  然后定于11月20日发布1.0测试版平台。也请大家到时候关注捧场。

如果你的
Streamlit App
中使用的数据的比较敏感,那么,保护这个
App
及其背后的数据免受未授权访问变得至关重要。

无论是出于商业机密的保护、用户隐私的维护,还是为了满足日益严格的合规要求,确保只有经过验证的用户才能访问特定的数据和功能,已成为大部分应用的一个基本需求。

登录认证
,作为访问控制的基础,是保护
Streamlit App
安全的第一道防线。

通过实施登录认证机制,我们可以确保只有合法的用户才能访问敏感数据、执行关键操作或查看特定页面。

本文将探讨如何在
Streamlit
多页应用中添加登录认证功能。

从为什么需要登录认证,到如何实现这一功能,最后再构建一个示例来演示如何在自己的
Streamlit App
中添加登录认证功能。

1. 为什么需要登录认证

在构建
Streamlit
多页应用时,添加登录认证功能并不是多余的步骤,而是确保应用安全、高效和用户友好的重要一环。

一般来说,我们会在以下一些场景时需要登录认证功能。

1.1. 有数据安全性要求

如果我们的
Streamlit App
所处理的数据包含敏感或机密信息,比如客户数据、财务数据或研究数据。

那么,未授权的访问可能会导致数据泄露,带来不必要的麻烦。

这时,通过登录认证,至少可以确保只有经过验证的用户才能访问这些数据,能有效降低了数据泄露的风险。

另外,许多行业(如金融、医疗、教育)都有严格的数据保护规定,要求对个人信息和敏感数据进行加密存储和访问控制。登录认证是实现这些合规要求的关键组成部分。

1.2. 有用户管理需求

如果你的应用有不同角色的用户(比如分了管理员,编辑者和查看者等等),每种角色有不同的权限。

那么,首先就要实现登录认证的功能,才能进一步将你的
Streamlit App

RBAC(Role Based Access Control)
系统对接,实现基于角色的访问控制。

1.3. 提高用户体验

当用户看到
Streamlit App
采取了登录认证等安全措施时,他们会更加信任该应用,更愿意分享个人信息或使用敏感功能。

此外,登录认证允许
Streamlit App
识别并记住用户,从而提供个性化的体验。

例如,可以根据用户的偏好设置界面主题、保存用户的工作进度等等。

2. 如何实现登录认证

实现一个登录认证功能,主要包含以下4个部分:

  1. 认证方法
    :常见有用户名+密码;邮箱或手机接受验证码;基于第三方的认证(
    OAuth
    /
    OpenID
    )等等
  2. 用户信息数据库
    :一般用关系数据库来保存用户信息,用户信息一般包含用户ID、用户名、密码哈希、角色/权限等字段
  3. 登录页面
    :根据选择的
    认证方法
    ,用
    Streamlit
    实现一个页面来处理用户的输入和登录请求
  4. 后端逻辑
    :根据选择的
    认证方法
    ,后端实现对用户输入信息是否合法的判断

后端逻辑
中,除了判断用户输入信息的合法性之外,有时候为了更高的安全性要求,还会加入一些密码策略(比如要求密码长度、包含特殊字符、定期更换密码等),防暴力破解的机制(比如限制登录频率,登录失败过多锁定账户等),以及其他一些攻击手段的预防。

3. 登录认证示例

最后,通过一个简化的示例演示如何在
Streamlit App
加入登录认证机制。

本示例主要演示
Streamlit
是如何限制未登录认证的用户访问具体功能页面的,不包含数据库和安全性的部分。

首先,构建一个多页应用,先不加登录功能。

工程目录结构如下:

$ tree /A /F .
登录认证
|   app.py
|
\---func_pages
        page1.py
        page2.py
        __init__.py

app.py

import streamlit as st

page1 = st.Page("pages/page1.py", title="查看数据集")
page2 = st.Page("pages/page2.py", title="绘制折线图")

pg = st.navigation({"主要功能": [page1, page2]})
pg.run()

page1.py

page2.py
分别模拟了不同的功能页面。

运行效果如下:

接下来添加登录认证的功能,为了简化,登录的用户名和密码固定写死,登录状态放在
session
中。


app.py
改造如下

# 初始化会话状态
if "logged_in" not in st.session_state:
    st.session_state.logged_in = False


# 默认用户
USERNAME = "admin"
PASSWORD = "adminadmin"


# 登录页面
def login():
    st.header("登录")
    st.divider()

    username = st.text_input("用户名")
    password = st.text_input("密码", type="password")

    if st.button("Login"):
        if username == USERNAME and password == PASSWORD:
            st.session_state.logged_in = True
            st.success("登录成功!")
            time.sleep(0.5)
            st.rerun()
        else:
            st.error("用户名或密码错误")


page1 = st.Page("func_pages/page1.py", title="查看数据集")
page2 = st.Page("func_pages/page2.py", title="绘制折线图")
login_page = st.Page(login, title="登录")

# 默认只有login页面
pg = st.navigation([login_page])

if st.session_state.logged_in:
    pg = st.navigation({"主要功能": [page1, page2]})

pg.run()

通过状态
st.session_state.logged_in
来判断用户是否已经登录,

登录成功后进入【主要功能】页面,否则停留在【登录】页面。

再添加一个退出登录的函数,基于上面的代码修改如下:

def logout():
    if st.button("Logout"):
        st.session_state.logged_in = False
        st.rerun()

logout_page = st.Page(logout, title="退出登录")

if st.session_state.logged_in:
    pg = st.navigation(
        {
            "账户管理": [logout_page],
            "主要功能": [page1, page2],
        }
    )

运行效果如下:

4. 总结

本文只提供了一个基本的登录认证实现示例,实际应用中可能需要根据具体需求进行定制和扩展。

例如,可以考虑添加多因素认证、用户注册和找回密码功能、以及与第三方身份提供者(如
OAuth
)的集成等。

原文链接

代码:快速使用kappa

首先的首先,可以先去了解一下
lambda架构

Abstract

在本文中提出了Kappa,一个简化无服务器开发的框架。它使用
检查点
来处理lambda函数超时,并提供
并发机制
,实现并行计算和协调

1 Introduction

无服务器计算是一种新的云范例,在这种范例中,租户不是配置虚拟机(VM),而是向平台注册事件处理程序(例如 Python 函数)。当事件发生时,平台会在 lambda 函数(一种短暂的无状态执行环境)上调用处理程序。lambda 函数在终止前可以执行一段有界的时间(例如在 AWS 上为 15 分钟)
存在两个主要挑战:(1)程序员必须手动划分计算以适应 lambda 函数的时间限制;(2)程序员没有可用的并发或同步原语(如线程、锁、信号量等),因此必须要么实现这样的原语,要么限制自己使用无共享并行性,要么避免使用为简化开发而开发的并行 lambda 函数

2 Background and Motivation

2.1 Comparison to Existing Framework

2.2 Lambda Function Time Limit

无服务器计算中的Lambda函数存在时间限制的原因主要是为了优化运营商的任务分配和资源管理,运营商不再需要预测任务的完成时间或进行复杂的迁移操作,能够更灵活地进行任务分配和资源利用

3 Kappa Design

Kappa 有三个组成部分:(1)一个协调器,负责启动和恢复任务以及实现 Kappa 的并发原语;(2)一个编译器,负责生成检查点所需的代码;(3)一个库,供任务用于检查点、并发处理和同步

3.1 Coordinator

Kappa 协调器负责在 lambda 函数上安排任务、实现同步和跨任务通信、跟踪任务元数据(包括检查点)以及提供容错功能,协调器本身通过跨备份存储(例如,Redis 集群)复制其状态来支持容错,通过管理远程过程调用 (RPC) 来实现这一点,其中包括生成新任务、检查点、消息排队和检索任务结果等操作。

3.2 Checkpointing

Kappa使用检查点来容忍lambda函数超时并防止RPC重复,在任务执行的某些关键点创建“检查点”,将任务当前的运行状态(包括变量、控制流等)记录下来。使用了一种称为“continuations(延续)”的技术来创建检查点,延续是一种保存程序控制流的方式,这种方式无需依赖传统服务器持久化状态,而是通过序列化将任务的状态数据保存到外部存储(如Redis或S3)中,提供同步和异步两种检查点模式。同步检查点在保存状态时会短暂停止任务执行,而异步检查点则允许任务继续执行,检查点数据在后台保存,检查点数据分布存储在多个节点上,并支持多任务同时创建检查点


(b) 显示了由编译器生成的延续函数,用来保存检查点后的执行流程,(c) 使用异常处理机制在暂停点恢复,确保任务可以从中断处继续执行

3.3 Concurrency API

两种基本并发机制:任务启动和任务同步

任务启动 (spawn) 并行机制:spawn RPC 用于启动一个新任务,以并行的方式执行某个函数调用(如f(args)),并返回一个Future对象,用于跟踪任务的结果。工作机制是通过创建一个初始检查点。当系统恢复这个检查点时,会执行相应的函数调用。此时,协调器(Coordinator)会调用一个新的lambda函数,从该检查点恢复并执行任务。

先进先出(FIFO)队列机制:如果一个任务尝试向已满的队列入队或从空队列出队,任务将被阻塞。这个机制不仅可以用来实现任务间通信,还可以作为锁和信号量,控制资源的并发访问。

4 Implementation

编译器是用Python编写的,通过Python的pickle库进行状态序列化,每个任务由一个Go协程(goroutine)管理,任务间的同步使用Go的通道(channel)实现,通过锁机制和Redis事务来确保状态更新的原子性

5 Evaluation

检查点开销测试:通过让Lambda函数每100ms创建一次检查点,测量同步和异步检查点的延迟。同步检查点会暂停应用处理直到检查点数据持久化;异步检查点在后台完成持久化操作,允许前台的计算继续

并发操作性能测试:使用多生产者多消费者FIFO队列的任务间消息传递,以评估任务通信的延迟

端到端应用评估:测试包括五个Kappa应用场景:TPC-DS SQL查询、字数统计(Word Count)、并行Grep(Parallel Grep)、流处理(Streaming),以及网络爬虫(Web Crawler)

6 Limitations

Kappa编译器尚未完全支持Python的一些特性,包括try/except、yield、async/await、嵌套函数定义和全局变量,只能在由其编译器转换的代码中进行检查点操作,垃圾回收机制,缺乏静态检查可序列化。

上一篇我们介绍了如何在本地部署 ollama 运行 llama3 大模型。过程是相当简单的。但是现在给大模型交流只能在命令行窗口进行。这样的话就只能你自己玩了。独乐乐不如众乐乐嘛。我们接下来说一下如何部署 open-webui 给 ollama 加一个 webui,这样用户就可以通过浏览器访问我们的本地大模型了,体验非常类似 chatGPT。

Open-WebUI

Open-WebUI 是一个开源的用户界面框架,旨在提供简便的工具和接口,帮助用户轻松地访问和管理各种深度学习模型,尤其是大规模预训练语言模型。以下是对Open-WebUI的简要介绍:

  • 开源框架: Open-WebUI 是一个开源项目,提供了灵活且可定制的用户界面,用于与各种深度学习模型进行交互。

  • 模型管理: 通过 Open-WebUI,用户可以方便地加载、配置和管理多个深度学习模型,包括 GPT-4、BERT 等大规模预训练模型。

  • 用户友好: 它提供了直观的界面,简化了模型使用过程,使非技术用户也能轻松上手进行自然语言处理任务。

  • 集成支持: Open-WebUI 支持与多种后端深度学习框架(如 TensorFlow、PyTorch)集成,提供高效的推理和训练功能。

扩展性强: 用户可以根据需求自定义和扩展界面功能,以适应不同的应用场景和任务需求。
总之,Open-WebUI 为用户提供了一个高效、直观的界面,使得大规模深度学习模型的使用更加便捷和高效。

地址:
https://github.com/open-webui/open-webui

使用 Docker 部署

使用 Docker 部署非常简单。如果 ollama 跟 open-webui 部署在同一个机器上,那么只需要运行一下代码就可以。

docker run -d -p 3000:8080 --add-host=host.docker.internal:host-gateway -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:main

如果 ollama 部署在其他服务器就用如下命令:

docker run -d -p 3000:8080 -e OLLAMA_BASE_URL=https://example.com -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:main

OLLAMA_BASE_URL 是指 ollama 暴露的API地址,一般为服务器地址加 11434。如:OLLAMA_BASE_URL=http://192.168.0.111:11434

使用 Open-WebUI

部署完之后,我们在浏览器里打开
http://localhost:3000
,就会出现 Open-WebUI 的界面。看起来跟 chatGPT 不能说一模一样么,也是毫无区别。随便填写一个邮箱后就可以注册第一个账户。

在右上角可以选择已经存在的模型。也可以搜索其他模型,然后直接安装,这个就非常方便了。

让我们下载一个传说中巨牛比的国产大模型 Qwen2 试试。随便问个问题,好像还不错。

总结

这一篇内容比较短,就是演示了一下如何使用 Open-WebUI 项目搭建一个本地的 chat 服务。这样就可以把本地大模型共享出去。这样你全家人都可以访问你部署的大模型了。当然你要是部署到外网的服务器上那就是给全世界人用了。

当然本地大模型所能回答的问题都是公开领域的知识,比如你问它你们家有几口人肯定是不知道的。下次我们会将如何让大模型学习你的私有知识,也就是搭建一个本地的知识库。

关注我的公众号一起玩转技术