
Open edX Credentials 应用解析LMS 与 Credentials IDA 的证书数据同步机制【免费下载链接】openedx-platformThe Open edX LMS Studio, powering education sites around the world!项目地址: https://gitcode.com/GitHub_Trending/ed/openedx-platform导读本篇文章以 openedx/core/djangoapps/credentials/README.rst 为主线深入剖析 Open edX 平台中 Credentials 应用的设计定位、配置模型与数据同步链路。Credentials 应用负责与独立的 credentials 服务IDA通信该服务是学习者 Program Certificates 的权威记录系统system of record并在平台启用后承担全部学习者证书记录的存取职责。读完本文你将掌握 Credentials 的启用配置、信号驱动同步、Celery 任务链路、以及notify_credentials等管理命令的完整用法。Credentials 应用在 Open edX 中的定位README 中明确标注了该应用当前的状态为Maintenance维护模式即功能已趋于稳定仅进行必要的缺陷修复与安全更新不再进行大规模功能开发。其核心职责描述如下The Credentials app is responsible (along with the programs app) for communicating with the credentials service, which is the system of record for a learners Program Certificates, and which (when enabled by the edX instance) is the system of record for accessing all of a learners credentials.拆解这句话可以得到三个关键事实协同职责Credentials 应用并非孤军奋战它与 programs 应用 协同完成与 credentials 服务之间的通信。programs 应用负责程序Program维度的事件处理如课程证书颁发、变更、撤销后触发程序证书的授予/撤销逻辑而 credentials 应用负责将这些事件转化为对 credentials 服务的数据推送。权威记录系统credentials 服务独立部署的 IDA即独立 Django 应用是学习者 Program Certificates 的权威数据源。LMS 本身并不持有最终证书记录而是通过 API 与异步任务将数据同步给该服务。可选的全量记录能力当被 Open edX 实例启用时credentials 服务将成为访问学习者全部证书记录的入口而不仅仅局限于 Program Certificates。README 的 See Also 部分还给出了两个需要关联阅读的模块lms/djangoapps/certificatesLMS 侧课程证书的生成、管理与状态机是 Credentials 数据的重要来源之一openedx/core/djangoapps/programs程序级证书逻辑与信号处理与 Credentials 应用共同组成对 credentials 服务的完整通信链路。与 credentials 服务交互的配置体系配置模型CredentialsApiConfig在 openedx/core/djangoapps/credentials/models.py 中CredentialsApiConfig继承自config_models.ConfigurationModel是典型的可热更新配置模型——通过 Django Admin 即可修改无需重启服务即可生效。它管理着 LMS 连接 credentials 服务并使用其 API 的全部关键参数配置字段类型默认值说明internal_service_urlURLField无内部服务 URL。已废弃改用设置项CREDENTIALS_INTERNAL_SERVICE_URLpublic_service_urlURLField无对外服务 URL。已废弃改用设置项CREDENTIALS_PUBLIC_SERVICE_URLenable_learner_issuanceBooleanFieldFalse是否通过 credentials 服务颁发证书Learner Issuanceenable_studio_authoringBooleanFieldFalse是否允许在 Studio 中创作 credentials 服务侧的证书cache_ttlPositiveIntegerField0API 响应缓存时长秒大于 0 才启用缓存模型内还通过API_VERSION v2定义了当前使用的 credentials API 版本并提供了几个重要的计算属性internal_api_url/public_api_url基于站点配置site_configuration的get_value或 Django 设置拼接出/api/v2/形式的 API 根地址public_records_url学习者记录Learner Records页面的公开根地址/records/仅当ENABLE_LEARNER_RECORDS功能开启时才返回非空值is_learner_issuance_enabled综合判断配置已启用且允许颁发这是后续所有同步逻辑的总开关is_cache_enabledcache_ttl 0时为真。Django 设置项应用通过插件机制PluginSettings注入自身的默认设置见 openedx/core/djangoapps/credentials/settings/common.pydef plugin_settings(settings): # Credentials Settings settings.CREDENTIALS_INTERNAL_SERVICE_URL http://localhost:8008 settings.CREDENTIALS_PUBLIC_SERVICE_URL http://localhost:8008即默认假定 credentials 服务运行在localhost:8008。实际部署中应在lms/envs/下各环境设置文件中覆盖为真实地址。另有CREDENTIALS_SERVICE_USERNAME发送成绩数据时使用的服务账号用户名与NOTIFY_CREDENTIALS_FREQUENCY--auto模式下自动运行的窗口长度等配套设置。站点级开关ENABLE_LEARNER_RECORDSopenedx/core/djangoapps/credentials/helpers.py 中定义了ENABLE_LEARNER_RECORDS开关SettingToggle默认True并在 openedx/core/djangoapps/credentials/docs/site_config.rst 中给出了完整说明控制 LMS 是否集成 credentials 服务中的 Learner Records 功能具体表现为开启后会启用一些指向学习者记录页面的 Web 按钮并允许将相关数据传递给 credentials若此前功能处于关闭状态现在决定开启必须回填backpopulate关闭期间未发送的数据——具体做法是运行notify_credentials管理命令下文详述需要注意credentials 服务侧也存在同名配置项需与 LMS 侧保持同步该开关支持站点级、组织级覆盖get_value/get_value_for_org。开发者可以通过openedx.core.djangoapps.credentials.api中的is_credentials_enabled()便捷函数判断本实例是否启用了 credentials 服务def is_credentials_enabled(): return CredentialsApiConfig.current().is_learner_issuance_enabled信号驱动的数据同步机制信号注册openedx/core/djangoapps/credentials/apps.py 通过 Django 插件信号机制PluginSignals注册了两个监听器仅作用于 LMS 项目handle_grade_change→ 监听COURSE_GRADE_CHANGED成绩变化信号handle_cert_change→ 监听COURSE_CERT_CHANGED证书变化信号。对应实现在 openedx/core/djangoapps/credentials/signals.pydef handle_grade_change(user, course_grade, course_key, **kwargs): send_grade_if_interesting( user, course_key, None, None, course_grade.letter_grade, course_grade.percent, verbosekwargs.get(verbose, False) ) def handle_cert_change(user, course_key, mode, status, **kwargs): send_grade_if_interesting(user, course_key, mode, status, None, None, verbosekwargs.get(verbose, False))两个处理器都汇入同一个函数send_grade_if_interesting其设计意图是在入队前进行过滤避免用海量无关信号淹没任务队列这是该应用被标记为 Maintenance 的一个典型业务逻辑特征——Credentials 业务逻辑渗入了 LMS。send_grade_if_interesting的过滤逻辑该函数位于 openedx/core/djangoapps/credentials/tasks/v1/tasks.py依次执行以下判定任一不满足则跳过凭证服务是否启用is_credentials_enabled()为假则直接返回组织级 Learner Records 开关is_learner_records_enabled_for_org(org)为假则跳过补齐 mode/status若信号未携带则从get_generated_certificate()读取学习者的证书记录无证书记录则跳过有趣模式与状态判定INTERESTING_MODES CourseMode.CERTIFICATE_RELEVANT_MODESINTERESTING_STATUSES [CertificateStatuses.notpassing, CertificateStatuses.downloadable]。这里的注释很有价值证书记录只要存在无论是否通过/颁发就足以记录一次verified attempt因为 credentials 服务会统计学习者对某课程 run 的尝试次数但应排除 legacy Audit 课程等 credentials 不关心的更新是否属于某个 Programis_course_run_in_a_program()会遍历所有站点及其程序缓存确认该 course run 隶属于某个 Program——因为 credentials 服务主要管理 Program 级证书补齐成绩数据若缺少 letter grade / percent grade / 更新时间则通过CourseGradeFactory().read()读取全部通过后调用send_grade_to_credentials.delay(...)异步推送。发送任务send_grade_to_credentialssend_grade_to_credentials是带自动重试的 Celery 任务其关键装饰器参数展示了生产级可靠性设计shared_task( bindTrue, ignore_resultTrue, autoretry_for(Exception,), max_retries10, retry_backoff30, retry_backoff_max600, retry_jitterTrue, )最多重试 10 次加上首次尝试共 11 次退避时间从 30 秒指数增长至上限 600 秒并启用随机抖动避免任务同时重试。任务执行时以CREDENTIALS_SERVICE_USERNAME服务账号构造带 JWT 的 API 客户端get_credentials_api_clientscopes 为[email, profile, user_id]按课程所属组织course_key.org解析内部 API 地址POST 到grades/端点载荷包含username、course_run、letter_grade、percent_grade、verified、lms_last_updated_at等字段。管理命令数据回填与再同步的实战工具notify_credentials手动重放证书/成绩变更该命令的定位在 openedx/core/djangoapps/credentials/management/commands/notify_credentials.py 的模块注释中说明得很清楚LMS 中多处通过信号通知 credentials 服务但有时需要无视数据库中的实际变更重建数据——例如从缺陷中恢复或首次上线新功能时做引导bootstrap。该命令会手动触发感兴趣的信号接收器不触发全部接收器因为这些是繁忙信号。命令自带完整示例用法# 处理指定课程的证书/成绩变更 $ ./manage.py lms --settingsdevstack notify_credentials \ --courses course-v1:edXDemoXDemo_Course # 处理指定时间范围内的证书/成绩变更 $ ./manage.py lms --settingsdevstack notify_credentials \ --start-date 2018-06-01 --end-date 2018-07-31全部命令行参数一览来自add_arguments比 README 更完整参数类型说明--dry-runflag仅预览将要处理的内容不真正发送--sitestr指定站点域名配合 course_org_filter 使用不指定则通知所有站点--coursesnargs仅发送指定 course run 的信息--program_uuidsnargs根据 Program UUID 找出其包含的所有 course run 并处理--start-date/--end-datedatetime仅处理该时间范围内发生变更的证书或成绩--delayfloat默认 0每次分页查询之间休眠的秒数避免打爆任务队列--page-sizeint默认 100每批次查询的记录数--autoflag周期性自动运行模式基于NOTIFY_CREDENTIALS_FREQUENCY自动推导起止时间--args-from-databaseflag从NotifyCredentialsConfig模型读取参数而非命令行供 Jenkins 等定时任务使用--verboseflag以详细模式运行成绩/证书变更信号--notify_programsflag在课程通知任务中同时发送 Program 授予通知--user_idsnargs仅处理指定用户--revoke_program_certsflag检查是否需要撤销学习者的 Program 证书约束与行为细节必须至少提供一个过滤条件--courses、--program_uuids、--start-date、--end-date或--user_ids否则抛出CommandErrorcourse run key 会经CourseKey.from_string校验非法 key 同样抛出CommandError命令本身不直接干活而是调用handle_notify_credentials.delay(options, course_run_keys)将工作交给 Celery--dry-run模式下任务会打印前 10 条证书与成绩记录作为预览输出形如DRY-RUN: This command would have handled changes for... 3 Certificates: course-v1:edXRecordsSelfPaced1 for user 14 course-v1:edXRecordsSelfPaced1 for user 17 course-v1:edXRecordsSelfPaced1 for user 18 3 Grades: course-v1:edXRecordsSelfPaced1 for user 14 course-v1:edXRecordsSelfPaced1 for user 17 course-v1:edXRecordsSelfPaced1 for user 18底层任务handle_notify_credentialstasks/v1/tasks.py的处理流程按--site读取站点配置 → 通过get_recently_modified_certificates/get_recently_modified_grades分别拉取证书与成绩 → 先逐条触发handle_course_cert_changed课程证书按需触发handle_course_cert_awarded/handle_course_cert_revokedProgram 证书授予/撤销→ 再逐条调用send_grade_if_interesting处理成绩。其中paged_query生成器按page_size分块迭代查询集并支持delay节流还针对 MySQL 本地执行时的OperationalError做了兼容处理。--args-from-database与NotifyCredentialsConfigopenedx/core/djangoapps/credentials/models.py 中的NotifyCredentialsConfig同样继承ConfigurationModel用于存储一次notify_credentials运行的参数快照arguments models.TextField( blankTrue, help_textUseful for manually running a Jenkins job. Specify like --start-date2018 --courses A B., default, )通过 Django Admin 设置arguments字段例如--start-date2018 --courses A B再以--args-from-database运行命令即可让 Jenkins 等定时任务无需修改命令行即可调整参数。注意若配置未启用enabledFalse而请求--args-from-database命令会抛出CommandError。其他管理命令create_credentials_api_configurationmanagement/commands/create_credentials_api_configuration.py创建一条enabledTrue, enable_learner_issuanceTrue的CredentialsApiConfig记录主要用于 devstack 开发环境中快速启用 credentials 功能update_credentials_available_datemanagement/commands/update_credentials_available_date.py用法为$ ./manage.py lms update_credentials_available_date入队backfill_date_for_all_course_runs任务为系统中每个 CourseOverview 派发update_certificate_available_date_on_course_update子任务向 credentials 服务校正certificate_available_date每处理 10 个休眠 3 秒以控制负载。面向 LMS 内部的 API 封装openedx/core/djangoapps/credentials/utils.py 提供了供其他 in-process 应用调用的 Python API其中最常用的是get_credentialsdef get_credentials( user, program_uuidNone, credential_typeNone, raise_on_errorFalse, ) - List[Dict]:它以当前用户身份构造 JWT 认证的 API 客户端向 credentials 服务请求查询参数固定为username、statusawarded、only_visibleTrue可按需追加program_uuid与typecourse-run或program缓存策略仅当cache_ttl 0且用户不是 staff时启用缓存staff 用户可能在生成证书后需要立即看到结果因此绕过缓存缓存 key 为credentials.api.data.username指定 program 时追加.program_uuidraise_on_errorFalse时API 出错默认返回空列表而非抛异常调用方需自行处理查无数据的情形。get_credentials_records_url(program_uuidNone)则用于生成 Learner Records 页面地址未传入 UUID 返回记录列表根地址传入 UUID 时会将 UUID 中的连字符去除credentials 服务期望无连字符格式并拼接为programs/uuid路径。若 Learner Records 功能被禁用返回None。相关模块联动一览Credentials 应用处于一条完整的数据链路中与之强相关的模块包括lms/djangoapps/certificates课程证书的颁发、状态管理与最近修改证书查询get_recently_modified_certificates、get_generated_certificate是notify_credentials与信号处理的主要数据来源openedx/core/djangoapps/programs提供程序证书授予/撤销信号处理函数handle_course_cert_awarded等及证书可用日期更新任务credentials 数据同步的 Program 维度逻辑在此实现openedx/core/djangoapps/catalog提供 Program 及 course run 的目录缓存查询get_programs、get_programs_from_cache_by_uuid支撑is_course_run_in_a_program与--program_uuids参数展开。小结Credentials 应用是 Open edX 平台证书即服务架构的关键一环它以CredentialsApiConfig配置为中心通过信号监听成绩与证书变化、以send_grade_if_interesting做业务过滤、以 Celery 任务可靠推送至 credentials 服务并通过notify_credentials系列管理命令支持数据回填与批量再同步。理解这条链路是部署多租户证书服务、排查证书不同步问题、以及扩展 Learner Records 功能的基础。【免费下载链接】openedx-platformThe Open edX LMS Studio, powering education sites around the world!项目地址: https://gitcode.com/GitHub_Trending/ed/openedx-platform创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考