



  

<!DOCTYPE html>
<html
  lang="en"
  
  data-color-mode="auto" data-light-theme="light" data-dark-theme="dark"
  data-a11y-animated-images="system" data-a11y-link-underlines="true"
  
  >




  <head>
    <meta charset="utf-8">
  <link rel="dns-prefetch" href="https://github.githubassets.com">
  <link rel="dns-prefetch" href="https://avatars.githubusercontent.com">
  <link rel="dns-prefetch" href="https://github-cloud.s3.amazonaws.com">
  <link rel="dns-prefetch" href="https://user-images.githubusercontent.com/">
  <link rel="preconnect" href="https://github.githubassets.com" crossorigin>
  <link rel="preconnect" href="https://avatars.githubusercontent.com">

<script type="importmap">{"imports":{"react":"https://github.githubassets.com/assets/react-e27d1b3e03961e68.js","react-dom":"https://github.githubassets.com/assets/react-dom-e5fd46a22d5c4058.js","react-dom/client":"https://github.githubassets.com/assets/react-dom-client-1b4a3ee065998cea.js","react-is":"https://github.githubassets.com/assets/react-is-e0b593954b4706d8.js","react-reconciler":"https://github.githubassets.com/assets/react-reconciler-8e99e505c4429605.js","react/compiler-runtime":"https://github.githubassets.com/assets/react-compiler-runtime-4610bd6d3de9c049.js","react/jsx-dev-runtime":"https://github.githubassets.com/assets/react-jsx-dev-runtime-ea55d68667d559e5.js","react/jsx-runtime":"https://github.githubassets.com/assets/react-jsx-runtime-4915cb0f5b3aff04.js","scheduler":"https://github.githubassets.com/assets/scheduler-58b860b049ca307c.js"}}</script>
<meta name="react-profiling" content="0" data-turbo-transient="true" />
<meta name="react-import-map" content="react,react-dom,react-dom/client,react-dom/profiling,react-is,react-reconciler,react/compiler-runtime,react/jsx-dev-runtime,react/jsx-runtime,scheduler@777f63f87cd0" data-turbo-track="reload" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/react-e27d1b3e03961e68.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/react-compiler-runtime-4610bd6d3de9c049.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/scheduler-58b860b049ca307c.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/react-dom-e5fd46a22d5c4058.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/react-dom-client-1b4a3ee065998cea.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/react-is-e0b593954b4706d8.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/react-jsx-runtime-4915cb0f5b3aff04.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/react-reconciler-8e99e505c4429605.js" />

  


  <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/light-5c4e9fc574bf49f3.css" /><link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/light_high_contrast-fb37c309e0603a69.css" /><link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/dark-afff6c53aef9b9d1.css" /><link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/dark_high_contrast-c409873d7987dffa.css" /><link data-color-theme="light" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/light-5c4e9fc574bf49f3.css" /><link data-color-theme="light_high_contrast" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/light_high_contrast-fb37c309e0603a69.css" /><link data-color-theme="light_colorblind" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/light_colorblind-0f910d8806d5761b.css" /><link data-color-theme="light_colorblind_high_contrast" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/light_colorblind_high_contrast-a7abd4d4c49ac593.css" /><link data-color-theme="light_tritanopia" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/light_tritanopia-5fc4c4a9cb41e596.css" /><link data-color-theme="light_tritanopia_high_contrast" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/light_tritanopia_high_contrast-ed0cee9214dedabd.css" /><link data-color-theme="dark" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark-afff6c53aef9b9d1.css" /><link data-color-theme="dark_high_contrast" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark_high_contrast-c409873d7987dffa.css" /><link data-color-theme="dark_colorblind" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark_colorblind-78a2491d538dab5a.css" /><link data-color-theme="dark_colorblind_high_contrast" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark_colorblind_high_contrast-c26b63d7c532d0a2.css" /><link data-color-theme="dark_tritanopia" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark_tritanopia-a6f60cea68c08405.css" /><link data-color-theme="dark_tritanopia_high_contrast" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark_tritanopia_high_contrast-f66faf28b2ea18b6.css" /><link data-color-theme="dark_dimmed" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark_dimmed-6701f6218c53cf5b.css" /><link data-color-theme="dark_dimmed_high_contrast" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark_dimmed_high_contrast-7037fa46ae124d97.css" />

  <style type="text/css">
    :root {
      --tab-size-preference: 4;
    }

    pre, code {
      tab-size: var(--tab-size-preference);
    }
  </style>

    <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-primitives-97df7784617ce1ea.css" />
    <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-9be9fe6313f476af.css" />
    <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/global-62747e27e61258dc.css" />
    <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/github-b04ca6fccda778e2.css" />
  <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/repository-72bd7d974b5ac1cc.css" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/code-4c5c0895f723f870.css" />

  

  <script type="application/json" id="client-env">{"locale":"en","featureFlags":["actions_caches_react_shell","actions_enable_background_steps","actions_new_hosted_runner_image_select_sizes_and_versions","actions_runners_react_shell","agent_author_search_expansion","agent_author_search_expansion_ui_pulls","alternate_user_config_repo","async_conversion_coverage_enabled","billing_billable_licenses_cost_center_bucket_fix","billing_budget_expiration","billing_cost_center_list_assigned_resources","billing_discount_threshold_notification","code_quality_enablement_banner_targeting","code_quality_remove_preview","code_view_raf_sticky_lines","codespaces_prebuild_region_target_update","coding_agent_third_party_model_ui","copilot_agent_snippy","copilot_ahp_tool_call_timing","copilot_api_agentic_issue_marshal_yaml","copilot_automation_repo_mcp_servers","copilot_automations_pagination","copilot_base_model_policy_row","copilot_chat_auto_mode_v2","copilot_chat_clear_model_selection_for_default_change","copilot_chat_early_task_provisioning","copilot_chat_persist_session_drafts","copilot_chat_vision_dotcom_chat_ga_gate","copilot_css_textarea_autosize","copilot_custom_copilots","copilot_custom_copilots_feature_preview","copilot_duplicate_thread","copilot_extensions_removal_on_marketplace","copilot_fix_failed_workflows_all_skus","copilot_hide_hovercard","copilot_immersive_task_hyperlinking","copilot_mc_cli_resume_any_users_task","copilot_mission_control_agent_merge_fix_ci","copilot_mission_control_agent_merge_resolve_conflicts","copilot_mission_control_awps_batching","copilot_mission_control_early_stop","copilot_mission_control_managed_sandbox_environments","copilot_mission_control_needs_attention","copilot_mission_control_reasoning_effort","copilot_mission_control_sandbox_remote_bypass","copilot_mission_control_task_alive_updates","copilot_mission_control_task_sharing","copilot_org_policy_page_focus_mode","copilot_share_active_subthread","copilot_spaces_ga","copilot_spaces_individual_policies_ga","copilot_swe_agent_authorization_status_ui","copilot_swe_agent_automation_resource_scoped_writes","copilot_swe_agent_discussion_comment_trigger","copilot_swe_agent_discussion_opened_trigger","copilot_swe_agent_discussion_updated_trigger","copilot_swe_agent_hide_model_picker_if_only_auto","copilot_swe_agent_issue_assigned_trigger","copilot_swe_agent_issue_comment_trigger","copilot_swe_agent_issue_labeled_trigger","copilot_swe_agent_pr_comment_model_picker","copilot_swe_agent_pull_request_assigned_trigger","copilot_swe_agent_pull_request_comment_trigger","copilot_swe_agent_pull_request_labeled_trigger","copilot_swe_agent_pull_request_merged_trigger","copilot_swe_agent_pull_request_opened_trigger","copilot_swe_agent_pull_request_ready_for_review_trigger","copilot_swe_agent_pull_request_review_requested_trigger","copilot_swe_agent_pull_request_review_submitted_trigger","copilot_swe_agent_pull_request_synchronize_trigger","copilot_swe_agent_sub_issue_added_trigger","copilot_swe_agent_use_subagents","copilot_task_api_github_rest_style","copilot_task_scoped_alive_channel","copilot_token_based_billing","copilot_unconfigured_is_inherited","copilot_user_can_upgrade_plan_field","copilot_web_integration_cutover","copilot_workbench_sunset_redirect","dashboard_agents_module_auth_token_check","dashboard_indexeddb_caching","fgpat_permissions_selector_redesign","glc_code_quality_repo_settings_workflow_config","hyperspace_2025_logged_out_batch_1","hyperspace_2025_logged_out_batch_2","hyperspace_2025_logged_out_batch_3","in_product_messaging_datadog_monitoring","ipm_ubb_individual_budget_banner","issue_fields_multi_select","issue_inline_avatars","issue_pinned_views","issue_pinned_views_team_vs_personal","issue_relative_time_micro","issue_viewer_paved_path","issues_expanded_file_types","issues_hide_closed_sub_issues","issues_lazy_load_comment_box_suggestions","issues_react_chrome_container_query_fix","labels_archiving","labels_archiving_info","landing_pages_ninetailed","lifecycle_label_name_updates","marketing_cookie_consent_banner","marketing_pages_search_explore_provider","memex_default_issue_create_repository","memex_live_update_hovercard","memex_mwl_filter_field_delimiter","memex_remove_deprecated_type_issue","merge_queue_restricted_pushers_warning","merge_status_header_feedback","milestone_closed_issues_prioritization","octocaptcha_origin_optimization","primer_react_css_anchor_positioning","primer_react_merged_forwarded_refs","primer_react_timeline_list_semantics","prs_copilot_app_open_action","prs_css_anchor_positioning","pull_request_copilot_attribution_header","pull_request_overview_merge_control","pull_request_overview_panel_edit_description","pull_request_persister","pull_request_stacks_navigation_shortcuts","pull_request_virtualization_image_estimate","pull_request_virtualization_loader_batching","pull_request_virtualization_scroll_compensation","pull_request_virtualization_scroll_intent","quick_search_lazy_suggestions","react_blob_isolate_code_lines","react_blob_ssr_content_visibility","react_data_router_tanstack_allowed","react_logged_out_repository_header","react_query_props_with_key","react_sandbox_future_tanstack","repo_app_turbo","repo_issues_sidebar_layout","repo_pulls_dashboard_declutter","repo_pulls_dashboard_ga","repo_pulls_dashboard_persistence","repos_contributors_limited_default_range","review_involves_filter","rule_ignored_file_paths","rulesets_actor_list_editor","sample_network_conn_type","security_center_artifact_filters_popover","see_who_reacted","semantic_similarity_duplicate_issue_detection","session_logs_ungroup_reasoning_text","set_sha256_on_repo_creation_form","site_banner_desktop_copilot_app","site_ghca_pixel_mona","site_github_app_ga_page","site_github_app_ga_page_highlight","site_github_app_mobile_native_share","site_global_banner_dev_days_attendee","site_global_nav_spark_models_removed","speculation_rules_ui_service","suggest_custom_property_values_copilot","suppress_automated_browser_vitals","swp_forms_disable_octocaptcha","thread_resolution_reason","update_issue_suggestions","viewscreen_sandbox","warn_inaccessible_attachments","webp_support","workstream_plugin_bootstrap"],"githubDomain":"https://github.com","copilotApiOverrideUrl":"https://api.githubcopilot.com","cmcApiUrl":"https://api.github.com/cmc_internal/api"}</script>
<script crossorigin="anonymous" type="module" src="https://github.githubassets.com/assets/high-contrast-cookie-3663bcaacc724f4c.js"></script>
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/wp-runtime-721126455a2225e3.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/app-foundation-156c6259e31cc551.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/app-runtime-f6ce38f47c2e1d56.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/fetch-utilities-804a14996977c014.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/ser-fb5d13278eb42203.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/tp-ea613009a991e211.js" />
<script crossorigin="anonymous" type="module" src="https://github.githubassets.com/assets/environment-107a7753389a8a2d.js" defer="defer"></script>
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/app-runtime.25915bc98fb322a6.module.css" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/catalyst-52ed81112a548e10.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/selector-observer-e8810f64c443fb9b.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/relative-time-element-fe7e72f699fdf493.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/by-9da36843ba9d191d.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/ja9-9a1160b939f2cd52.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/3h-55903cf2303047ad.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/hk-0d35a1220587d888.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/jz5-c964b1a3671f2a92.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/hj-1d800d09e9a8720a.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/j0-1ad12374da9777f2.js" />
<script crossorigin="anonymous" type="module" src="https://github.githubassets.com/assets/github-elements-531d35538b61fd0d.js" defer="defer"></script>
<script crossorigin="anonymous" type="module" src="https://github.githubassets.com/assets/element-registry-bf5e3b2aee197122.js" defer="defer"></script>
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/runtime-helpers-5e0b3ae3c4036207.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/aria-live-752acdc868e2da64.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/hotkey-e87ffadced20a3c9.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/react-core-75bc55d9971828b7.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/a9-34620c7b13885011.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/ur-643d2814115561a9.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/4t6-5d0869970ac0aac0.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/pb0-a5c5ec36d311aeef.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/6n-afcf86ccabc4cad2.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/zj-f08c728cf00018aa.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/o6-2a1c68d269710ee9.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/h9a-c6405aa090de0e84.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/vy6-3c3e6e96629c2ce4.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/js-4eb97d5803e657c0.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/g7-baf9e817ae102b01.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/unc-96389cade7bad821.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/nu-65322a6997c6c93c.js" />
<script crossorigin="anonymous" type="module" src="https://github.githubassets.com/assets/behaviors-5dd3809b5c8bac3c.js" defer="defer"></script>
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/react-core.3792087592bf207a.module.css" />
<script crossorigin="anonymous" type="module" src="https://github.githubassets.com/assets/code-menu-4318f0f4c608f3dd.js" defer="defer"></script>
  
  <link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/primer-react-7afef75d393cf294.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/op-acddf38478d45ef9.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/ncx-461e4b09f2e67ee5.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/qmp-c7cfba40cf5a93b0.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/j6-148a74299e5a01ab.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/t7l-a52ba56147f77909.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/4u-ec4874f70bfd5d76.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/s0-481bd34f90bc2bd7.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/y5-ed17e73b4c0bd15e.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/n4-be595475cdbf7d4b.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/j8-23a3c5ff8eef412d.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/la-27679061f16e1495.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/iu-9806a0291a782cbe.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/ftx-39fb490e3c1d778f.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/zi-32a0c3c081ea5cce.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/sg-7a5d50475874c0e1.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/pd-6a8710840033e042.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/86g-ec28c25ed7d5de06.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/sn-1bab7384818953b7.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/iq-6d4ecf6d2fb6993c.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/p2-7d8e7cee5b46cb45.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/7d-2e3cc25d719f3a95.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/4d-76d8acdf21a33266.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/tsb-e0f6288ab985d9d2.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/lf-910ef220ce85c660.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/2j-2f19c1afd2ceaf93.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/me-f1476ec44a29f34a.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/p2u-e654a57b1a70c47d.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/5d-d97cc006d4da4d9b.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/r5-38f013bc2c0fd298.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/r6s-9039f5619230a653.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/9e-cece06724bf6ea3b.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/x9-3e077a3a37a73f9f.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/pj-87629cb5389d7663.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/eq-3764fd5f66494289.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/13-d889936dff7e5c0f.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/44-8193eea4af825ead.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/v4-9c1327d212e52a0d.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/id-7f39706ba9007db6.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/ah-271362a1ed227d03.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/s1g-d434d712bca59d3a.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/vz-d7f4a583896f872e.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/47-c8c0ce2700acc5be.js" />
<script crossorigin="anonymous" type="module" src="https://github.githubassets.com/assets/code-view-4118a687d0150749.js" defer="defer"></script>
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-react-css.2d881b4d4a503d1c.module.css" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/pj.75627bfeb9ceb1da.module.css" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/ah.2623f7c6ecbe2ecd.module.css" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/47.78770c57a48a810c.module.css" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/code-view.37656c95a9729fb9.module.css" />


  <title>dongting/README_CN.md at master · dtprj/dongting · GitHub</title>



  <meta name="route-pattern" content="/:user_id/:repository/blob/*name(/*path)" data-turbo-transient>
  <meta name="route-controller" content="blob" data-turbo-transient>
  <meta name="route-action" content="show" data-turbo-transient>
  <meta name="fetch-nonce" content="v2:a13e6e80-b514-c319-bbe4-d708946c52e8">

    
  <meta name="current-catalog-service-hash" content="f3abb0cc802f3d7b95fc8762b94bdcb13bf39634c40c357301c4aa1d67a256fb">


  <meta name="request-id" content="932E:307768:BCFCE9A:F52F11A:6ACA666F" data-pjax-transient="true"/><meta name="html-safe-nonce" content="8c6db2517c5caba056053b1302880078e72a7893355519e11c142826af3f8862" data-pjax-transient="true"/><meta name="visitor-payload" content="eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiI5MzJFOjMwNzc2ODpCQ0ZDRTlBOkY1MkYxMUE6NkFDQTY2NkYiLCJ2aXNpdG9yX2lkIjoiMjY1MzU0NDE2NzIxNzE5NDYwNyIsInJlZ2lvbl9lZGdlIjoiZnJhIiwicmVnaW9uX3JlbmRlciI6ImZyYSJ9" data-pjax-transient="true"/><meta name="visitor-hmac" content="a0115e3af8634ae17ed4906584bf0d63e0953564dbb0cca0302f8e9fcccf6e46" data-pjax-transient="true"/>


    <meta name="hovercard-subject-tag" content="repository:516941115" data-turbo-transient>


  <meta name="github-keyboard-shortcuts" content="repository,source-code,file-tree,copilot" data-turbo-transient="true" />
  

  <meta name="selected-link" value="repo_source" data-turbo-transient>
  <link rel="assets" href="https://github.githubassets.com/">

    <meta name="google-site-verification" content="Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I">

<meta name="octolytics-url" content="https://collector.github.com/github/collect" />





  <meta name="analytics-location" content="/&lt;user-name&gt;/&lt;repo-name&gt;/blob/show" data-turbo-transient="true" />

  




    <meta name="user-login" content="">

  

    <meta name="viewport" content="width=device-width">

    

      <meta name="description" content="Java raft/config/mq/rpc engine, zero dependencies, 10X faster - dongting/README_CN.md at master · dtprj/dongting">

      <link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="GitHub">

    <link rel="fluid-icon" href="https://github.com/fluidicon.png" title="GitHub">
    <meta property="fb:app_id" content="1401488693436528">
    <meta name="apple-itunes-app" content="app-id=1477376905, app-argument=https://github.com/dtprj/dongting/blob/master/README_CN.md" />

      <meta name="twitter:image" content="https://opengraph.githubassets.com/1962245dedd0d388193e678be1a900db3cf6c608001153193f72b6d423bf2e84/dtprj/dongting" /><meta name="twitter:site" content="@github" /><meta name="twitter:card" content="summary_large_image" /><meta name="twitter:title" content="dongting/README_CN.md at master · dtprj/dongting" /><meta name="twitter:description" content="Java raft/config/mq/rpc engine, zero dependencies, 10X faster - dtprj/dongting" />
  <meta property="og:image" content="https://opengraph.githubassets.com/1962245dedd0d388193e678be1a900db3cf6c608001153193f72b6d423bf2e84/dtprj/dongting" /><meta property="og:image:alt" content="Java raft/config/mq/rpc engine, zero dependencies, 10X faster - dtprj/dongting" /><meta property="og:image:width" content="1200" /><meta property="og:image:height" content="600" /><meta property="og:site_name" content="GitHub" /><meta property="og:type" content="object" /><meta property="og:title" content="dongting/README_CN.md at master · dtprj/dongting" /><meta property="og:url" content="https://github.com/dtprj/dongting/blob/master/README_CN.md" /><meta property="og:description" content="Java raft/config/mq/rpc engine, zero dependencies, 10X faster - dtprj/dongting" />
  




      <meta name="hostname" content="github.com">



        <meta name="expected-hostname" content="github.com">


  <meta http-equiv="x-pjax-version" content="d05be8f8b6905b6460fc264e80447721dfee1e32cf9e4291c4bf58f160c9f705" data-turbo-track="reload">
  <meta http-equiv="x-pjax-csp-version" content="c4a65e47b0c850e1157ae8e66298070851d7e3b558038de5a3b4ff7a93e630ed" data-turbo-track="reload">
  <meta http-equiv="x-pjax-css-version" content="a80b0545b169f440b929ce2b977c76b6988f014cca6513fb83c5df6581635fb3" data-turbo-track="reload">
  <meta http-equiv="x-pjax-js-version" content="af51c6f9d659be0324665373df3ee542f817dbd7f66f352a087935f2ceabe545" data-turbo-track="reload">

  <meta name="turbo-cache-control" content="no-preview" data-turbo-transient="">

      <meta name="turbo-cache-control" content="no-cache" data-turbo-transient>

    <meta data-hydrostats="publish">

  <meta name="go-import" content="github.com/dtprj/dongting git https://github.com/dtprj/dongting.git">

  <meta name="octolytics-dimension-user_id" content="109834165" /><meta name="octolytics-dimension-user_login" content="dtprj" /><meta name="octolytics-dimension-repository_id" content="516941115" /><meta name="octolytics-dimension-repository_nwo" content="dtprj/dongting" /><meta name="octolytics-dimension-repository_public" content="true" /><meta name="octolytics-dimension-repository_is_fork" content="false" /><meta name="octolytics-dimension-repository_network_root_id" content="516941115" /><meta name="octolytics-dimension-repository_network_root_nwo" content="dtprj/dongting" />
  



    

    <meta name="turbo-body-classes" content="logged-out env-production page-responsive">
  <meta name="disable-turbo" content="false">


  <meta name="browser-stats-url" content="https://api.github.com/_private/browser/stats">


  <meta name="browser-errors-url" content="https://api.github.com/_private/browser/errors">

  <meta name="release" content="3b1bb5b10f5c7fd1769d56e38b15dfddb34052a8" data-turbo-track="reload">
  <meta name="ui-target" content="full">

  <link rel="mask-icon" href="https://github.githubassets.com/assets/pinned-octocat-093da3e6fa40.svg" color="#000000">
  <link rel="alternate icon" class="js-site-favicon" type="image/png" href="https://github.githubassets.com/favicons/favicon.png">
  <link rel="icon" class="js-site-favicon" type="image/svg+xml" href="https://github.githubassets.com/favicons/favicon.svg" data-base-href="https://github.githubassets.com/favicons/favicon">

<meta name="theme-color" content="#1e2327">
<meta name="color-scheme" content="light dark" />


  <link rel="manifest" href="/manifest.json" crossOrigin="use-credentials">

  </head>

  <body class="logged-out env-production page-responsive" style="word-wrap: break-word;" >
    <div data-turbo-body class="logged-out env-production page-responsive" style="word-wrap: break-word;" >
      <div id="__primerPortalRoot__" style="z-index: 1000; position: absolute; width: 100%;" data-turbo-permanent></div>
      

    <div class="position-relative header-wrapper js-header-wrapper ">
      <a href="#start-of-content" data-skip-target-assigned="false" class="px-2 tmp-py-4 color-bg-accent-emphasis color-fg-on-emphasis show-on-focus js-skip-to-content">Skip to content</a>

      <span data-view-component="true" class="progress-pjax-loader Progress position-fixed width-full">
    <span style="width: 0%;" data-view-component="true" class="Progress-item progress-pjax-loader-bar left-0 top-0 color-bg-accent-emphasis"></span>
</span>      
      <link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/19-c660652a0a1639be.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/keyboard-shortcuts-dialog-47c5223d8d1af8ef.js" fetchpriority="low" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-react-css.2d881b4d4a503d1c.module.css" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/keyboard-shortcuts-dialog.d53d993320d57e14.module.css" />

<react-partial
  partial-name="keyboard-shortcuts-dialog"
  data-ssr="false"
  data-attempted-ssr="false"
  data-react-profiling="false"
>
  
  <script type="application/json" data-target="react-partial.embeddedData">{"props":{"docsUrl":"https://docs.github.com/get-started/accessibility/keyboard-shortcuts"}}</script>
  <div data-target="react-partial.reactRoot"></div>
</react-partial>





      

          <link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/app-install-banner-partial-3c87deb7f0f20434.js" fetchpriority="low" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-react-css.2d881b4d4a503d1c.module.css" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/app-install-banner-partial.7b357012e82003e7.module.css" />

<react-partial
  partial-name="app-install-banner-partial"
  data-ssr="false"
  data-attempted-ssr="false"
  data-react-profiling="false"
>
  
  <script type="application/json" data-target="react-partial.embeddedData">{"props":{}}</script>
  <div data-target="react-partial.reactRoot"></div>
</react-partial>


          

                <link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/et9-7eeec247c287fca4.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/94v-e20946127faa8f6e.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/fz5-892a9e99cc92858d.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/jag-441e702bfd4c7904.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/hw-8ce7888b57d0a23c.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/rq-0ec82a12cf538a4d.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/1m-a7a4c022483b2713.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/marketing-header-70703fe17422eb58.js" fetchpriority="low" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-react-css.2d881b4d4a503d1c.module.css" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-react-brand-css.fbc35b3d20e988ff.module.css" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/1m.fec8b575d0e5234f.module.css" />

<react-partial
  partial-name="marketing-header"
  data-ssr="true"
  data-attempted-ssr="true"
  data-react-profiling="false"
>
  
  <script type="application/json" data-target="react-partial.embeddedData">{"props":{"color_mode":"dark","logged_in":false,"marketing_page":false,"home_path":"/","login_path":"/login?return_to=https%3A%2F%2Fgithub.com%2Fdtprj%2Fdongting%2Fblob%2Fmaster%2FREADME_CN.md","signup_path":"/signup?ref_cta=Sign+up\u0026ref_loc=header+logged+out\u0026ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E%2Fblob%2Fshow\u0026source=header-repo\u0026source_repo=dtprj%2Fdongting","signup_enabled":true,"is_signup_controller":false,"show_search_and_nav":true,"hide_search":false,"private_mode_enabled":false,"should_use_dotcom_links":true,"auth_hydro_click":"{\"event_type\":\"authentication.click\",\"payload\":{\"location_in_page\":\"site header menu\",\"repository_id\":null,\"auth_type\":\"SIGN_UP\",\"originating_url\":\"https://github.com/dtprj/dongting/blob/master/README_CN.md\",\"user_id\":null}}","auth_hydro_click_hmac":"8ea653bff8f51c0085c9e11b98b8702786b8d77d5092df0ef6b8cdb3ef2621d8","overlay":false,"fixed":false}}</script>
  <div data-target="react-partial.reactRoot"><div data-color-mode="dark" data-light-theme="light" data-dark-theme="dark"><header class="MarketingHeader-module__root__Tk7n3 HeaderMktg header-logged-out" role="banner" data-marketing-header="true" data-color-mode="dark" data-light-theme="light" data-dark-theme="dark" data-is-top="true"><h2 class="MarketingHeader-module__visuallyHidden__sqKsl">Navigation Menu</h2><button type="button" class="MarketingHeader-module__backdrop__sw4RU" aria-label="Close navigation menu"></button><div class="MarketingHeader-module__bar__mBSyE"><div class="MarketingHeader-module__topRow__yeury"><div class="MarketingHeader-module__toggleSlot__hDxbh"><button type="button" class="HeaderMenuToggle-module__toggle__i8EiC" aria-label="Toggle navigation" aria-expanded="false"><span class="HeaderMenuToggle-module__toggleBar__jVN0H"></span><span class="HeaderMenuToggle-module__toggleBar__jVN0H"></span><span class="HeaderMenuToggle-module__toggleBar__jVN0H"></span></button></div><a href="/" aria-label="Homepage" class="HeaderLogo-module__logo__UFyHI" data-analytics-event="{&quot;action&quot;:&quot;homepage&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;logo&quot;,&quot;location&quot;:&quot;header&quot;,&quot;label&quot;:&quot;homepage_link_logo_header&quot;}"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-mark-github" viewBox="0 0 24 24" width="32" height="32" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M10.226 17.284c-2.965-.36-5.054-2.493-5.054-5.256 0-1.123.404-2.336 1.078-3.144-.292-.741-.247-2.314.09-2.965.898-.112 2.111.36 2.83 1.01.853-.269 1.752-.404 2.853-.404 1.1 0 1.999.135 2.807.382.696-.629 1.932-1.1 2.83-.988.315.606.36 2.179.067 2.942.72.854 1.101 2 1.101 3.167 0 2.763-2.089 4.852-5.098 5.234.763.494 1.28 1.572 1.28 2.807v2.336c0 .674.561 1.056 1.235.786 4.066-1.55 7.255-5.615 7.255-10.646C23.5 6.188 18.334 1 11.978 1 5.62 1 .5 6.188.5 12.545c0 4.986 3.167 9.12 7.435 10.669.606.225 1.19-.18 1.19-.786V20.63a2.9 2.9 0 0 1-1.078.224c-1.483 0-2.359-.808-2.987-2.313-.247-.607-.517-.966-1.034-1.033-.27-.023-.359-.135-.359-.27 0-.27.45-.471.898-.471.652 0 1.213.404 1.797 1.235.45.651.921.943 1.483.943.561 0 .92-.202 1.437-.719.382-.381.674-.718.944-.943"></path></svg></a><div class="AuthCTAs-module__mobileActions__NNzeV"><a class="Primer_Brand__Button-module__Button___scH9Z Primer_Brand__Button-module__Button--subtle___F7pEE Primer_Brand__Button-module__Button--size-small___zQrEw AuthCTAs-module__cta__WpwQq" href="/login?return_to=https%3A%2F%2Fgithub.com%2Fdtprj%2Fdongting%2Fblob%2Fmaster%2FREADME_CN.md" data-analytics-event="{&quot;action&quot;:&quot;sign_in&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;auth_cta&quot;,&quot;location&quot;:&quot;header&quot;,&quot;label&quot;:&quot;sign_in_link_auth_cta_header&quot;}" data-hydro-click="{&quot;event_type&quot;:&quot;authentication.click&quot;,&quot;payload&quot;:{&quot;location_in_page&quot;:&quot;site header menu&quot;,&quot;repository_id&quot;:null,&quot;auth_type&quot;:&quot;SIGN_UP&quot;,&quot;originating_url&quot;:&quot;https://github.com/dtprj/dongting/blob/master/README_CN.md&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="8ea653bff8f51c0085c9e11b98b8702786b8d77d5092df0ef6b8cdb3ef2621d8"><span class="Primer_Brand__Button-module__Button__text___ED0bX"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ Primer_Brand__Button-module__Button--label___qrkyz Primer_Brand__Button-module__Button--label-subtle___8ndWH">Sign in</span></span></a><button class="Primer_Brand__Button-module__Button___scH9Z Primer_Brand__Button-module__Button--subtle___F7pEE Primer_Brand__Button-module__Button--size-small___zQrEw HeaderAppearanceSettings-module__trigger__hUheK" type="button" aria-haspopup="dialog" aria-labelledby="_R_3dd_"><span class="Primer_Brand__Button-module__Button__leading-visual___jjtTe" data-testid="Button-leading-visual"><svg data-component="Octicon" focusable="false" aria-hidden="true" class="octicon octicon-sliders Primer_Brand__Button-module__Button__icon-visual____qybb" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M15 2.75a.75.75 0 0 1-.75.75h-4a.75.75 0 0 1 0-1.5h4a.75.75 0 0 1 .75.75Zm-8.5.75v1.25a.75.75 0 0 0 1.5 0v-4a.75.75 0 0 0-1.5 0V2H1.75a.75.75 0 0 0 0 1.5H6.5Zm1.25 5.25a.75.75 0 0 0 0-1.5h-6a.75.75 0 0 0 0 1.5h6ZM15 8a.75.75 0 0 1-.75.75H11.5V10a.75.75 0 1 1-1.5 0V6a.75.75 0 0 1 1.5 0v1.25h2.75A.75.75 0 0 1 15 8Zm-9 5.25v-2a.75.75 0 0 0-1.5 0v1.25H1.75a.75.75 0 0 0 0 1.5H4.5v1.25a.75.75 0 0 0 1.5 0v-2Zm9 0a.75.75 0 0 1-.75.75h-6a.75.75 0 0 1 0-1.5h6a.75.75 0 0 1 .75.75Z"></path></svg></span><span class="Primer_Brand__Button-module__Button__text___ED0bX"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ Primer_Brand__Button-module__Button--label___qrkyz Primer_Brand__Button-module__Button--label-subtle___8ndWH"></span></span></button><div class="Primer_Brand__Tooltip-module__Tooltip___0Eipx" data-direction="s" aria-hidden="true" id="_R_3dd_">Appearance settings</div></div></div><div class="MarketingHeader-module__menu__GIy3y"><div class="MarketingHeader-module__menuWrapper__owstH"><nav class="MarketingNavigation-module__nav__W0KYY" aria-label="Global"><ul class="MarketingNavigation-module__list__tFbMb"><li><div class="NavDropdown-module__container__l2YeI"><button type="button" class="NavDropdown-module__button__PEHWX" aria-expanded="false" aria-controls="_R_nd_">Platform<svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-triangle-right NavDropdown-module__buttonIcon__Tkl8_" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m6.427 4.427 3.396 3.396a.25.25 0 0 1 0 .354l-3.396 3.396A.25.25 0 0 1 6 11.396V4.604a.25.25 0 0 1 .427-.177Z"></path></svg></button><div id="_R_nd_" class="NavDropdown-module__dropdown__xm1jd"><ul class="NavDropdown-module__list__zuCgG"><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_5knd_">AI CODE CREATION</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_5knd_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/features/copilot" data-analytics-event="{&quot;action&quot;:&quot;github_copilot&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;github_copilot_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-copilot NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M7.998 15.035c-4.562 0-7.873-2.914-7.998-3.749V9.338c.085-.628.677-1.686 1.588-2.065.013-.07.024-.143.036-.218.029-.183.06-.384.126-.612-.201-.508-.254-1.084-.254-1.656 0-.87.128-1.769.693-2.484.579-.733 1.494-1.124 2.724-1.261 1.206-.134 2.262.034 2.944.765.05.053.096.108.139.165.044-.057.094-.112.143-.165.682-.731 1.738-.899 2.944-.765 1.23.137 2.145.528 2.724 1.261.566.715.693 1.614.693 2.484 0 .572-.053 1.148-.254 1.656.066.228.098.429.126.612.012.076.024.148.037.218.924.385 1.522 1.471 1.591 2.095v1.872c0 .766-3.351 3.795-8.002 3.795Zm0-1.485c2.28 0 4.584-1.11 5.002-1.433V7.862l-.023-.116c-.49.21-1.075.291-1.727.291-1.146 0-2.059-.327-2.71-.991A3.222 3.222 0 0 1 8 6.303a3.24 3.24 0 0 1-.544.743c-.65.664-1.563.991-2.71.991-.652 0-1.236-.081-1.727-.291l-.023.116v4.255c.419.323 2.722 1.433 5.002 1.433ZM6.762 2.83c-.193-.206-.637-.413-1.682-.297-1.019.113-1.479.404-1.713.7-.247.312-.369.789-.369 1.554 0 .793.129 1.171.308 1.371.162.181.519.379 1.442.379.853 0 1.339-.235 1.638-.54.315-.322.527-.827.617-1.553.117-.935-.037-1.395-.241-1.614Zm4.155-.297c-1.044-.116-1.488.091-1.681.297-.204.219-.359.679-.242 1.614.091.726.303 1.231.618 1.553.299.305.784.54 1.638.54.922 0 1.28-.198 1.442-.379.179-.2.308-.578.308-1.371 0-.765-.123-1.242-.37-1.554-.233-.296-.693-.587-1.713-.7Z"></path><path d="M6.25 9.037a.75.75 0 0 1 .75.75v1.501a.75.75 0 0 1-1.5 0V9.787a.75.75 0 0 1 .75-.75Zm4.25.75v1.501a.75.75 0 0 1-1.5 0V9.787a.75.75 0 0 1 1.5 0Z"></path></svg>GitHub Copilot</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Write better code with AI</span></span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/features/ai/github-app" data-analytics-event="{&quot;action&quot;:&quot;github_copilot_app&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;github_copilot_app_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-mark-github NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M6.766 11.328c-2.063-.25-3.516-1.734-3.516-3.656 0-.781.281-1.625.75-2.188-.203-.515-.172-1.609.063-2.062.625-.078 1.468.25 1.968.703.594-.187 1.219-.281 1.985-.281.765 0 1.39.094 1.953.265.484-.437 1.344-.765 1.969-.687.218.422.25 1.515.046 2.047.5.593.766 1.39.766 2.203 0 1.922-1.453 3.375-3.547 3.64.531.344.89 1.094.89 1.954v1.625c0 .468.391.734.86.547C13.781 14.359 16 11.53 16 8.03 16 3.61 12.406 0 7.984 0 3.563 0 0 3.61 0 8.031a7.88 7.88 0 0 0 5.172 7.422c.422.156.828-.125.828-.547v-1.25c-.219.094-.5.156-.75.156-1.031 0-1.64-.562-2.078-1.609-.172-.422-.36-.672-.719-.719-.187-.015-.25-.093-.25-.187 0-.188.313-.328.625-.328.453 0 .844.281 1.25.86.313.452.64.655 1.031.655s.641-.14 1-.5c.266-.265.47-.5.657-.656"></path></svg>GitHub Copilot app</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Direct agents from issue to merge</span></span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/mcp" data-analytics-event="{&quot;action&quot;:&quot;mcp_registry&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;mcp_registry_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-mcp NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M5.52 1.12a3.578 3.578 0 0 1 6.078 2.98 3.578 3.578 0 0 1 2.982 6.08l-3.292 3.293a.252.252 0 0 0 0 .354l.843.843a.749.749 0 1 1-1.06 1.06l-.844-.843a1.75 1.75 0 0 1 0-2.474L13.52 9.12a2.08 2.08 0 0 0 0-2.94 2.08 2.08 0 0 0-2.94 0L7.731 9.03A.75.75 0 0 1 6.67 7.97l2.85-2.85a2.08 2.08 0 0 0 0-2.94 2.08 2.08 0 0 0-2.94 0l-4.799 4.8A.75.75 0 0 1 .72 5.92Z"></path><path d="M7.52 3.12a.749.749 0 1 1 1.06 1.06L5.731 7.03A2.079 2.079 0 0 0 8.67 9.97l2.85-2.85a.749.749 0 1 1 1.06 1.06l-2.849 2.85A3.578 3.578 0 0 1 4.67 5.97Z"></path></svg>MCP Registry</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Integrate external tools</span></span></a></li></ul></div></li><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_9knd_">DEVELOPER WORKFLOWS</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_9knd_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/features/actions" data-analytics-event="{&quot;action&quot;:&quot;actions&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;actions_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-workflow NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M0 1.75C0 .784.784 0 1.75 0h3.5C6.216 0 7 .784 7 1.75v3.5A1.75 1.75 0 0 1 5.25 7H4v4a1 1 0 0 0 1 1h4v-1.25C9 9.784 9.784 9 10.75 9h3.5c.966 0 1.75.784 1.75 1.75v3.5A1.75 1.75 0 0 1 14.25 16h-3.5A1.75 1.75 0 0 1 9 14.25v-.75H5A2.5 2.5 0 0 1 2.5 11V7h-.75A1.75 1.75 0 0 1 0 5.25Zm1.75-.25a.25.25 0 0 0-.25.25v3.5c0 .138.112.25.25.25h3.5a.25.25 0 0 0 .25-.25v-3.5a.25.25 0 0 0-.25-.25Zm9 9a.25.25 0 0 0-.25.25v3.5c0 .138.112.25.25.25h3.5a.25.25 0 0 0 .25-.25v-3.5a.25.25 0 0 0-.25-.25Z"></path></svg>Actions</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Automate any workflow</span></span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/features/codespaces" data-analytics-event="{&quot;action&quot;:&quot;codespaces&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;codespaces_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-codespaces NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M0 11.25c0-.966.784-1.75 1.75-1.75h12.5c.966 0 1.75.784 1.75 1.75v3A1.75 1.75 0 0 1 14.25 16H1.75A1.75 1.75 0 0 1 0 14.25Zm2-9.5C2 .784 2.784 0 3.75 0h8.5C13.216 0 14 .784 14 1.75v5a1.75 1.75 0 0 1-1.75 1.75h-8.5A1.75 1.75 0 0 1 2 6.75Zm1.75-.25a.25.25 0 0 0-.25.25v5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-5a.25.25 0 0 0-.25-.25Zm-2 9.5a.25.25 0 0 0-.25.25v3c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25v-3a.25.25 0 0 0-.25-.25Z"></path><path d="M7 12.75a.75.75 0 0 1 .75-.75h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1-.75-.75Zm-4 0a.75.75 0 0 1 .75-.75h.5a.75.75 0 0 1 0 1.5h-.5a.75.75 0 0 1-.75-.75Z"></path></svg>Codespaces</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Instant dev environments</span></span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/features/issues" data-analytics-event="{&quot;action&quot;:&quot;issues&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;issues_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-issue-opened NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M8 9.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z"></path><path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Z"></path></svg>Issues</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Plan and track work</span></span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/features/code-review" data-analytics-event="{&quot;action&quot;:&quot;code_review&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;code_review_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-code NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m11.28 3.22 4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734L13.94 8l-3.72-3.72a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215Zm-6.56 0a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042L2.06 8l3.72 3.72a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L.47 8.53a.75.75 0 0 1 0-1.06Z"></path></svg>Code Review</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Manage code changes</span></span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/features/code-quality" data-analytics-event="{&quot;action&quot;:&quot;code_quality&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;code_quality_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-codescan-checkmark NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M10.28 6.28a.75.75 0 1 0-1.06-1.06L6.25 8.19l-.97-.97a.75.75 0 0 0-1.06 1.06l1.5 1.5a.75.75 0 0 0 1.06 0l3.5-3.5Z"></path><path d="M7.5 15a7.5 7.5 0 1 1 5.807-2.754l2.473 2.474a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215l-2.474-2.473A7.472 7.472 0 0 1 7.5 15Zm0-13.5a6 6 0 1 0 4.094 10.386.748.748 0 0 1 .293-.292 6.002 6.002 0 0 0 1.117-6.486A6.002 6.002 0 0 0 7.5 1.5Z"></path></svg>Code Quality</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Enforce quality at merge</span></span></a></li></ul></div></li><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_dknd_">APPLICATION SECURITY</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_dknd_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/security/advanced-security" data-analytics-event="{&quot;action&quot;:&quot;github_advanced_security&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;github_advanced_security_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-shield-check NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m8.533.133 5.25 1.68A1.75 1.75 0 0 1 15 3.48V7c0 1.566-.32 3.182-1.303 4.682-.983 1.498-2.585 2.813-5.032 3.855a1.697 1.697 0 0 1-1.33 0c-2.447-1.042-4.049-2.357-5.032-3.855C1.32 10.182 1 8.566 1 7V3.48a1.75 1.75 0 0 1 1.217-1.667l5.25-1.68a1.748 1.748 0 0 1 1.066 0Zm-.61 1.429.001.001-5.25 1.68a.251.251 0 0 0-.174.237V7c0 1.36.275 2.666 1.057 3.859.784 1.194 2.121 2.342 4.366 3.298a.196.196 0 0 0 .154 0c2.245-.957 3.582-2.103 4.366-3.297C13.225 9.666 13.5 8.358 13.5 7V3.48a.25.25 0 0 0-.174-.238l-5.25-1.68a.25.25 0 0 0-.153 0ZM11.28 6.28l-3.5 3.5a.75.75 0 0 1-1.06 0l-1.5-1.5a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215l.97.97 2.97-2.97a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Z"></path></svg>GitHub Advanced Security</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Find and fix vulnerabilities</span></span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/security/advanced-security/code-security" data-analytics-event="{&quot;action&quot;:&quot;code_security&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;code_security_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-code-square NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v12.5A1.75 1.75 0 0 1 14.25 16H1.75A1.75 1.75 0 0 1 0 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25V1.75a.25.25 0 0 0-.25-.25Zm7.47 3.97a.75.75 0 0 1 1.06 0l2 2a.75.75 0 0 1 0 1.06l-2 2a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734L10.69 8 9.22 6.53a.75.75 0 0 1 0-1.06ZM6.78 6.53 5.31 8l1.47 1.47a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215l-2-2a.75.75 0 0 1 0-1.06l2-2a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Z"></path></svg>Code security</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Secure your code as you build</span></span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/security/advanced-security/secret-protection" data-analytics-event="{&quot;action&quot;:&quot;secret_protection&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;secret_protection_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-lock NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M4 4a4 4 0 0 1 8 0v2h.25c.966 0 1.75.784 1.75 1.75v5.5A1.75 1.75 0 0 1 12.25 15h-8.5A1.75 1.75 0 0 1 2 13.25v-5.5C2 6.784 2.784 6 3.75 6H4Zm8.25 3.5h-8.5a.25.25 0 0 0-.25.25v5.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-5.5a.25.25 0 0 0-.25-.25ZM10.5 6V4a2.5 2.5 0 1 0-5 0v2Z"></path></svg>Secret protection</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Stop leaks before they start</span></span></a></li></ul></div></li><li><div class="NavGroup-module__group__W8SqJ NavGroup-module__hasSeparator__FnMrN"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_hknd_">EXPLORE</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_hknd_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/why-github" data-analytics-event="{&quot;action&quot;:&quot;why_github&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;why_github_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Why GitHub</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--is-external___xsncV" href="https://docs.github.com" data-analytics-event="{&quot;action&quot;:&quot;documentation&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;documentation_link_platform_navbar&quot;}" target="_blank" rel="noreferrer"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Documentation</span><svg data-component="Octicon" focusable="false" aria-label="External link" class="octicon octicon-link-external" role="img" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path></svg></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--is-external___xsncV" href="https://github.blog" data-analytics-event="{&quot;action&quot;:&quot;blog&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;blog_link_platform_navbar&quot;}" target="_blank" rel="noreferrer"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Blog</span><svg data-component="Octicon" focusable="false" aria-label="External link" class="octicon octicon-link-external" role="img" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path></svg></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--is-external___xsncV" href="https://github.blog/changelog" data-analytics-event="{&quot;action&quot;:&quot;changelog&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;changelog_link_platform_navbar&quot;}" target="_blank" rel="noreferrer"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Changelog</span><svg data-component="Octicon" focusable="false" aria-label="External link" class="octicon octicon-link-external" role="img" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path></svg></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/marketplace" data-analytics-event="{&quot;action&quot;:&quot;marketplace&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;marketplace_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Marketplace</span></a></li></ul></div></li></ul><div class="NavDropdown-module__trailingLinkContainer__VgJGL"><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--arrow-end___esdN8" href="https://github.com/features" data-analytics-event="{&quot;action&quot;:&quot;view_all_features&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;view_all_features_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">View all features</span><svg class="Primer_Brand__ExpandableArrow-module__ExpandableArrow___aaZs9 Primer_Brand__Link-module__Link-arrow___yd78i" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true" focusable="false"><path fill="currentColor" d="M7.28033 3.21967C6.98744 2.92678 6.51256 2.92678 6.21967 3.21967C5.92678 3.51256 5.92678 3.98744 6.21967 4.28033L7.28033 3.21967ZM11 8L11.5303 8.53033C11.8232 8.23744 11.8232 7.76256 11.5303 7.46967L11 8ZM6.21967 11.7197C5.92678 12.0126 5.92678 12.4874 6.21967 12.7803C6.51256 13.0732 6.98744 13.0732 7.28033 12.7803L6.21967 11.7197ZM6.21967 4.28033L10.4697 8.53033L11.5303 7.46967L7.28033 3.21967L6.21967 4.28033ZM10.4697 7.46967L6.21967 11.7197L7.28033 12.7803L11.5303 8.53033L10.4697 7.46967Z"></path><path class="Primer_Brand__ExpandableArrow-module__ExpandableArrow-stem___0K8Hz" stroke="currentColor" d="M1.75 8H11" stroke-width="1.5" stroke-linecap="round"></path></svg></a></div></div></div></li><li><div class="NavDropdown-module__container__l2YeI"><button type="button" class="NavDropdown-module__button__PEHWX" aria-expanded="false" aria-controls="_R_17d_">Solutions<svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-triangle-right NavDropdown-module__buttonIcon__Tkl8_" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m6.427 4.427 3.396 3.396a.25.25 0 0 1 0 .354l-3.396 3.396A.25.25 0 0 1 6 11.396V4.604a.25.25 0 0 1 .427-.177Z"></path></svg></button><div id="_R_17d_" class="NavDropdown-module__dropdown__xm1jd"><ul class="NavDropdown-module__list__zuCgG"><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_5l7d_">BY COMPANY SIZE</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_5l7d_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/enterprise" data-analytics-event="{&quot;action&quot;:&quot;enterprises&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;enterprises_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Enterprises</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/team" data-analytics-event="{&quot;action&quot;:&quot;small_and_medium_teams&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;small_and_medium_teams_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Small and medium teams</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/enterprise/startups" data-analytics-event="{&quot;action&quot;:&quot;startups&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;startups_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Startups</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/solutions/industry/nonprofits" data-analytics-event="{&quot;action&quot;:&quot;nonprofits&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;nonprofits_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Nonprofits</span></a></li></ul></div></li><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_9l7d_">BY USE CASE</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_9l7d_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/solutions/use-case/app-modernization" data-analytics-event="{&quot;action&quot;:&quot;app_modernization&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;app_modernization_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">App Modernization</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/solutions/use-case/devsecops" data-analytics-event="{&quot;action&quot;:&quot;devsecops&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;devsecops_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">DevSecOps</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/solutions/use-case/devops" data-analytics-event="{&quot;action&quot;:&quot;devops&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;devops_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">DevOps</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/solutions/use-case/ci-cd" data-analytics-event="{&quot;action&quot;:&quot;ci/cd&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;ci/cd_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">CI/CD</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--arrow-end___esdN8" href="https://github.com/solutions/use-case" data-analytics-event="{&quot;action&quot;:&quot;view_all_use_cases&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;view_all_use_cases_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">View all use cases</span><svg class="Primer_Brand__ExpandableArrow-module__ExpandableArrow___aaZs9 Primer_Brand__Link-module__Link-arrow___yd78i" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true" focusable="false"><path fill="currentColor" d="M7.28033 3.21967C6.98744 2.92678 6.51256 2.92678 6.21967 3.21967C5.92678 3.51256 5.92678 3.98744 6.21967 4.28033L7.28033 3.21967ZM11 8L11.5303 8.53033C11.8232 8.23744 11.8232 7.76256 11.5303 7.46967L11 8ZM6.21967 11.7197C5.92678 12.0126 5.92678 12.4874 6.21967 12.7803C6.51256 13.0732 6.98744 13.0732 7.28033 12.7803L6.21967 11.7197ZM6.21967 4.28033L10.4697 8.53033L11.5303 7.46967L7.28033 3.21967L6.21967 4.28033ZM10.4697 7.46967L6.21967 11.7197L7.28033 12.7803L11.5303 8.53033L10.4697 7.46967Z"></path><path class="Primer_Brand__ExpandableArrow-module__ExpandableArrow-stem___0K8Hz" stroke="currentColor" d="M1.75 8H11" stroke-width="1.5" stroke-linecap="round"></path></svg></a></li></ul></div></li><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_dl7d_">BY INDUSTRY</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_dl7d_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/solutions/industry/healthcare" data-analytics-event="{&quot;action&quot;:&quot;healthcare&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;healthcare_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Healthcare</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/solutions/industry/financial-services" data-analytics-event="{&quot;action&quot;:&quot;financial_services&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;financial_services_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Financial services</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/solutions/industry/manufacturing" data-analytics-event="{&quot;action&quot;:&quot;manufacturing&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;manufacturing_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Manufacturing</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/solutions/industry/government" data-analytics-event="{&quot;action&quot;:&quot;government&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;government_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Government</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--arrow-end___esdN8" href="https://github.com/solutions/industry" data-analytics-event="{&quot;action&quot;:&quot;view_all_industries&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;view_all_industries_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">View all industries</span><svg class="Primer_Brand__ExpandableArrow-module__ExpandableArrow___aaZs9 Primer_Brand__Link-module__Link-arrow___yd78i" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true" focusable="false"><path fill="currentColor" d="M7.28033 3.21967C6.98744 2.92678 6.51256 2.92678 6.21967 3.21967C5.92678 3.51256 5.92678 3.98744 6.21967 4.28033L7.28033 3.21967ZM11 8L11.5303 8.53033C11.8232 8.23744 11.8232 7.76256 11.5303 7.46967L11 8ZM6.21967 11.7197C5.92678 12.0126 5.92678 12.4874 6.21967 12.7803C6.51256 13.0732 6.98744 13.0732 7.28033 12.7803L6.21967 11.7197ZM6.21967 4.28033L10.4697 8.53033L11.5303 7.46967L7.28033 3.21967L6.21967 4.28033ZM10.4697 7.46967L6.21967 11.7197L7.28033 12.7803L11.5303 8.53033L10.4697 7.46967Z"></path><path class="Primer_Brand__ExpandableArrow-module__ExpandableArrow-stem___0K8Hz" stroke="currentColor" d="M1.75 8H11" stroke-width="1.5" stroke-linecap="round"></path></svg></a></li></ul></div></li></ul><div class="NavDropdown-module__trailingLinkContainer__VgJGL"><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--arrow-end___esdN8" href="https://github.com/solutions" data-analytics-event="{&quot;action&quot;:&quot;view_all_solutions&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;view_all_solutions_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">View all solutions</span><svg class="Primer_Brand__ExpandableArrow-module__ExpandableArrow___aaZs9 Primer_Brand__Link-module__Link-arrow___yd78i" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true" focusable="false"><path fill="currentColor" d="M7.28033 3.21967C6.98744 2.92678 6.51256 2.92678 6.21967 3.21967C5.92678 3.51256 5.92678 3.98744 6.21967 4.28033L7.28033 3.21967ZM11 8L11.5303 8.53033C11.8232 8.23744 11.8232 7.76256 11.5303 7.46967L11 8ZM6.21967 11.7197C5.92678 12.0126 5.92678 12.4874 6.21967 12.7803C6.51256 13.0732 6.98744 13.0732 7.28033 12.7803L6.21967 11.7197ZM6.21967 4.28033L10.4697 8.53033L11.5303 7.46967L7.28033 3.21967L6.21967 4.28033ZM10.4697 7.46967L6.21967 11.7197L7.28033 12.7803L11.5303 8.53033L10.4697 7.46967Z"></path><path class="Primer_Brand__ExpandableArrow-module__ExpandableArrow-stem___0K8Hz" stroke="currentColor" d="M1.75 8H11" stroke-width="1.5" stroke-linecap="round"></path></svg></a></div></div></div></li><li><div class="NavDropdown-module__container__l2YeI"><button type="button" class="NavDropdown-module__button__PEHWX" aria-expanded="false" aria-controls="_R_1nd_">Resources<svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-triangle-right NavDropdown-module__buttonIcon__Tkl8_" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m6.427 4.427 3.396 3.396a.25.25 0 0 1 0 .354l-3.396 3.396A.25.25 0 0 1 6 11.396V4.604a.25.25 0 0 1 .427-.177Z"></path></svg></button><div id="_R_1nd_" class="NavDropdown-module__dropdown__xm1jd"><ul class="NavDropdown-module__list__zuCgG"><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_5lnd_">EXPLORE BY TOPIC</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_5lnd_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/resources/articles?topic=ai" data-analytics-event="{&quot;action&quot;:&quot;ai&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;ai_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">AI</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/resources/articles?topic=software-development" data-analytics-event="{&quot;action&quot;:&quot;software_development&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;software_development_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Software Development</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/resources/articles?topic=devops" data-analytics-event="{&quot;action&quot;:&quot;devops&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;devops_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">DevOps</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/resources/articles?topic=security" data-analytics-event="{&quot;action&quot;:&quot;security&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;security_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Security</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--arrow-end___esdN8" href="https://github.com/resources/articles" data-analytics-event="{&quot;action&quot;:&quot;view_all_topics&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;view_all_topics_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">View all topics</span><svg class="Primer_Brand__ExpandableArrow-module__ExpandableArrow___aaZs9 Primer_Brand__Link-module__Link-arrow___yd78i" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true" focusable="false"><path fill="currentColor" d="M7.28033 3.21967C6.98744 2.92678 6.51256 2.92678 6.21967 3.21967C5.92678 3.51256 5.92678 3.98744 6.21967 4.28033L7.28033 3.21967ZM11 8L11.5303 8.53033C11.8232 8.23744 11.8232 7.76256 11.5303 7.46967L11 8ZM6.21967 11.7197C5.92678 12.0126 5.92678 12.4874 6.21967 12.7803C6.51256 13.0732 6.98744 13.0732 7.28033 12.7803L6.21967 11.7197ZM6.21967 4.28033L10.4697 8.53033L11.5303 7.46967L7.28033 3.21967L6.21967 4.28033ZM10.4697 7.46967L6.21967 11.7197L7.28033 12.7803L11.5303 8.53033L10.4697 7.46967Z"></path><path class="Primer_Brand__ExpandableArrow-module__ExpandableArrow-stem___0K8Hz" stroke="currentColor" d="M1.75 8H11" stroke-width="1.5" stroke-linecap="round"></path></svg></a></li></ul></div></li><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_9lnd_">EXPLORE BY TYPE</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_9lnd_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/customer-stories" data-analytics-event="{&quot;action&quot;:&quot;customer_stories&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;customer_stories_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Customer stories</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/resources/events" data-analytics-event="{&quot;action&quot;:&quot;events__webinars&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;events__webinars_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Events &amp; webinars</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/resources/whitepapers" data-analytics-event="{&quot;action&quot;:&quot;ebooks__reports&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;ebooks__reports_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Ebooks &amp; reports</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/solutions/executive-insights" data-analytics-event="{&quot;action&quot;:&quot;business_insights&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;business_insights_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Business insights</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--is-external___xsncV" href="https://skills.github.com" data-analytics-event="{&quot;action&quot;:&quot;github_skills&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;github_skills_link_resources_navbar&quot;}" target="_blank" rel="noreferrer"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">GitHub Skills</span><svg data-component="Octicon" focusable="false" aria-label="External link" class="octicon octicon-link-external" role="img" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path></svg></a></li></ul></div></li><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_dlnd_">SUPPORT &amp; SERVICES</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_dlnd_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--is-external___xsncV" href="https://docs.github.com" data-analytics-event="{&quot;action&quot;:&quot;documentation&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;documentation_link_resources_navbar&quot;}" target="_blank" rel="noreferrer"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Documentation</span><svg data-component="Octicon" focusable="false" aria-label="External link" class="octicon octicon-link-external" role="img" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path></svg></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--is-external___xsncV" href="https://support.github.com" data-analytics-event="{&quot;action&quot;:&quot;customer_support&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;customer_support_link_resources_navbar&quot;}" target="_blank" rel="noreferrer"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Customer support</span><svg data-component="Octicon" focusable="false" aria-label="External link" class="octicon octicon-link-external" role="img" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path></svg></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/orgs/community/discussions" data-analytics-event="{&quot;action&quot;:&quot;community_forum&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;community_forum_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Community forum</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/trust-center" data-analytics-event="{&quot;action&quot;:&quot;trust_center&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;trust_center_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Trust center</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/partners" data-analytics-event="{&quot;action&quot;:&quot;partners&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;partners_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Partners</span></a></li></ul></div></li></ul><div class="NavDropdown-module__trailingLinkContainer__VgJGL"><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--arrow-end___esdN8" href="https://github.com/resources" data-analytics-event="{&quot;action&quot;:&quot;view_all_resources&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;view_all_resources_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">View all resources</span><svg class="Primer_Brand__ExpandableArrow-module__ExpandableArrow___aaZs9 Primer_Brand__Link-module__Link-arrow___yd78i" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true" focusable="false"><path fill="currentColor" d="M7.28033 3.21967C6.98744 2.92678 6.51256 2.92678 6.21967 3.21967C5.92678 3.51256 5.92678 3.98744 6.21967 4.28033L7.28033 3.21967ZM11 8L11.5303 8.53033C11.8232 8.23744 11.8232 7.76256 11.5303 7.46967L11 8ZM6.21967 11.7197C5.92678 12.0126 5.92678 12.4874 6.21967 12.7803C6.51256 13.0732 6.98744 13.0732 7.28033 12.7803L6.21967 11.7197ZM6.21967 4.28033L10.4697 8.53033L11.5303 7.46967L7.28033 3.21967L6.21967 4.28033ZM10.4697 7.46967L6.21967 11.7197L7.28033 12.7803L11.5303 8.53033L10.4697 7.46967Z"></path><path class="Primer_Brand__ExpandableArrow-module__ExpandableArrow-stem___0K8Hz" stroke="currentColor" d="M1.75 8H11" stroke-width="1.5" stroke-linecap="round"></path></svg></a></div></div></div></li><li><div class="NavDropdown-module__container__l2YeI"><button type="button" class="NavDropdown-module__button__PEHWX" aria-expanded="false" aria-controls="_R_27d_">Open Source<svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-triangle-right NavDropdown-module__buttonIcon__Tkl8_" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m6.427 4.427 3.396 3.396a.25.25 0 0 1 0 .354l-3.396 3.396A.25.25 0 0 1 6 11.396V4.604a.25.25 0 0 1 .427-.177Z"></path></svg></button><div id="_R_27d_" class="NavDropdown-module__dropdown__xm1jd"><ul class="NavDropdown-module__list__zuCgG"><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_5m7d_">COMMUNITY</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_5m7d_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/open-source/sponsors" data-analytics-event="{&quot;action&quot;:&quot;github_sponsors&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;open_source&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;github_sponsors_link_open_source_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-sponsor-tiers NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M10.586 1C12.268 1 13.5 2.37 13.5 4.25c0 1.745-.996 3.359-2.622 4.831-.166.15-.336.297-.509.438l1.116 5.584a.75.75 0 0 1-.991.852l-2.409-.876a.25.25 0 0 0-.17 0l-2.409.876a.75.75 0 0 1-.991-.852L5.63 9.519a13.78 13.78 0 0 1-.51-.438C3.497 7.609 2.5 5.995 2.5 4.25 2.5 2.37 3.732 1 5.414 1c.963 0 1.843.403 2.474 1.073L8 2.198l.112-.125a3.385 3.385 0 0 1 2.283-1.068L10.586 1Zm-3.621 9.495-.718 3.594 1.155-.42a1.75 1.75 0 0 1 1.028-.051l.168.051 1.154.42-.718-3.592c-.199.13-.37.235-.505.314l-.169.097a.75.75 0 0 1-.72 0 9.54 9.54 0 0 1-.515-.308l-.16-.105ZM10.586 2.5c-.863 0-1.611.58-1.866 1.459-.209.721-1.231.721-1.44 0C7.025 3.08 6.277 2.5 5.414 2.5 4.598 2.5 4 3.165 4 4.25c0 1.23.786 2.504 2.128 3.719.49.443 1.018.846 1.546 1.198l.325.21.076-.047.251-.163a13.341 13.341 0 0 0 1.546-1.198C11.214 6.754 12 5.479 12 4.25c0-1.085-.598-1.75-1.414-1.75Z"></path></svg>GitHub Sponsors</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Fund open source developers</span></span></a></li></ul></div></li><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_9m7d_">PROGRAMS</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_9m7d_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--is-external___xsncV" href="https://securitylab.github.com" data-analytics-event="{&quot;action&quot;:&quot;security_lab&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;open_source&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;security_lab_link_open_source_navbar&quot;}" target="_blank" rel="noreferrer"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Security Lab</span><svg data-component="Octicon" focusable="false" aria-label="External link" class="octicon octicon-link-external" role="img" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path></svg></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--is-external___xsncV" href="https://maintainers.github.com" data-analytics-event="{&quot;action&quot;:&quot;maintainer_community&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;open_source&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;maintainer_community_link_open_source_navbar&quot;}" target="_blank" rel="noreferrer"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Maintainer Community</span><svg data-component="Octicon" focusable="false" aria-label="External link" class="octicon octicon-link-external" role="img" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path></svg></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--is-external___xsncV" href="https://stars.github.com" data-analytics-event="{&quot;action&quot;:&quot;github_stars&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;open_source&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;github_stars_link_open_source_navbar&quot;}" target="_blank" rel="noreferrer"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">GitHub Stars</span><svg data-component="Octicon" focusable="false" aria-label="External link" class="octicon octicon-link-external" role="img" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path></svg></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--is-external___xsncV" href="https://archiveprogram.github.com" data-analytics-event="{&quot;action&quot;:&quot;archive_program&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;open_source&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;archive_program_link_open_source_navbar&quot;}" target="_blank" rel="noreferrer"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Archive Program</span><svg data-component="Octicon" focusable="false" aria-label="External link" class="octicon octicon-link-external" role="img" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path></svg></a></li></ul></div></li><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_dm7d_">REPOSITORIES</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_dm7d_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/topics" data-analytics-event="{&quot;action&quot;:&quot;topics&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;open_source&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;topics_link_open_source_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Topics</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/trending" data-analytics-event="{&quot;action&quot;:&quot;trending&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;open_source&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;trending_link_open_source_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Trending</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/collections" data-analytics-event="{&quot;action&quot;:&quot;collections&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;open_source&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;collections_link_open_source_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Collections</span></a></li></ul></div></li></ul></div></div></li><li><div class="NavDropdown-module__container__l2YeI"><button type="button" class="NavDropdown-module__button__PEHWX" aria-expanded="false" aria-controls="_R_2nd_">Enterprise<svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-triangle-right NavDropdown-module__buttonIcon__Tkl8_" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m6.427 4.427 3.396 3.396a.25.25 0 0 1 0 .354l-3.396 3.396A.25.25 0 0 1 6 11.396V4.604a.25.25 0 0 1 .427-.177Z"></path></svg></button><div id="_R_2nd_" class="NavDropdown-module__dropdown__xm1jd"><ul class="NavDropdown-module__list__zuCgG"><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_5mnd_">ENTERPRISE SOLUTIONS</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_5mnd_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/enterprise" data-analytics-event="{&quot;action&quot;:&quot;enterprise_platform&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;enterprise&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;enterprise_platform_link_enterprise_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-stack NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M7.122.392a1.75 1.75 0 0 1 1.756 0l5.003 2.902c.83.481.83 1.68 0 2.162L8.878 8.358a1.75 1.75 0 0 1-1.756 0L2.119 5.456a1.251 1.251 0 0 1 0-2.162ZM8.125 1.69a.248.248 0 0 0-.25 0l-4.63 2.685 4.63 2.685a.248.248 0 0 0 .25 0l4.63-2.685ZM1.601 7.789a.75.75 0 0 1 1.025-.273l5.249 3.044a.248.248 0 0 0 .25 0l5.249-3.044a.75.75 0 0 1 .752 1.298l-5.248 3.044a1.75 1.75 0 0 1-1.756 0L1.874 8.814A.75.75 0 0 1 1.6 7.789Zm0 3.5a.75.75 0 0 1 1.025-.273l5.249 3.044a.248.248 0 0 0 .25 0l5.249-3.044a.75.75 0 0 1 .752 1.298l-5.248 3.044a1.75 1.75 0 0 1-1.756 0l-5.248-3.044a.75.75 0 0 1-.273-1.025Z"></path></svg>Enterprise platform</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">AI-powered developer platform</span></span></a></li></ul></div></li><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_9mnd_">AVAILABLE ADD-ONS</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_9mnd_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/security/advanced-security" data-analytics-event="{&quot;action&quot;:&quot;github_advanced_security&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;enterprise&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;github_advanced_security_link_enterprise_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-shield-check NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m8.533.133 5.25 1.68A1.75 1.75 0 0 1 15 3.48V7c0 1.566-.32 3.182-1.303 4.682-.983 1.498-2.585 2.813-5.032 3.855a1.697 1.697 0 0 1-1.33 0c-2.447-1.042-4.049-2.357-5.032-3.855C1.32 10.182 1 8.566 1 7V3.48a1.75 1.75 0 0 1 1.217-1.667l5.25-1.68a1.748 1.748 0 0 1 1.066 0Zm-.61 1.429.001.001-5.25 1.68a.251.251 0 0 0-.174.237V7c0 1.36.275 2.666 1.057 3.859.784 1.194 2.121 2.342 4.366 3.298a.196.196 0 0 0 .154 0c2.245-.957 3.582-2.103 4.366-3.297C13.225 9.666 13.5 8.358 13.5 7V3.48a.25.25 0 0 0-.174-.238l-5.25-1.68a.25.25 0 0 0-.153 0ZM11.28 6.28l-3.5 3.5a.75.75 0 0 1-1.06 0l-1.5-1.5a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215l.97.97 2.97-2.97a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Z"></path></svg>GitHub Advanced Security</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Enterprise-grade security features</span></span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/features/copilot/copilot-business" data-analytics-event="{&quot;action&quot;:&quot;copilot_for_business&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;enterprise&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;copilot_for_business_link_enterprise_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-copilot NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M7.998 15.035c-4.562 0-7.873-2.914-7.998-3.749V9.338c.085-.628.677-1.686 1.588-2.065.013-.07.024-.143.036-.218.029-.183.06-.384.126-.612-.201-.508-.254-1.084-.254-1.656 0-.87.128-1.769.693-2.484.579-.733 1.494-1.124 2.724-1.261 1.206-.134 2.262.034 2.944.765.05.053.096.108.139.165.044-.057.094-.112.143-.165.682-.731 1.738-.899 2.944-.765 1.23.137 2.145.528 2.724 1.261.566.715.693 1.614.693 2.484 0 .572-.053 1.148-.254 1.656.066.228.098.429.126.612.012.076.024.148.037.218.924.385 1.522 1.471 1.591 2.095v1.872c0 .766-3.351 3.795-8.002 3.795Zm0-1.485c2.28 0 4.584-1.11 5.002-1.433V7.862l-.023-.116c-.49.21-1.075.291-1.727.291-1.146 0-2.059-.327-2.71-.991A3.222 3.222 0 0 1 8 6.303a3.24 3.24 0 0 1-.544.743c-.65.664-1.563.991-2.71.991-.652 0-1.236-.081-1.727-.291l-.023.116v4.255c.419.323 2.722 1.433 5.002 1.433ZM6.762 2.83c-.193-.206-.637-.413-1.682-.297-1.019.113-1.479.404-1.713.7-.247.312-.369.789-.369 1.554 0 .793.129 1.171.308 1.371.162.181.519.379 1.442.379.853 0 1.339-.235 1.638-.54.315-.322.527-.827.617-1.553.117-.935-.037-1.395-.241-1.614Zm4.155-.297c-1.044-.116-1.488.091-1.681.297-.204.219-.359.679-.242 1.614.091.726.303 1.231.618 1.553.299.305.784.54 1.638.54.922 0 1.28-.198 1.442-.379.179-.2.308-.578.308-1.371 0-.765-.123-1.242-.37-1.554-.233-.296-.693-.587-1.713-.7Z"></path><path d="M6.25 9.037a.75.75 0 0 1 .75.75v1.501a.75.75 0 0 1-1.5 0V9.787a.75.75 0 0 1 .75-.75Zm4.25.75v1.501a.75.75 0 0 1-1.5 0V9.787a.75.75 0 0 1 1.5 0Z"></path></svg>Copilot for Business</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Enterprise-grade AI features</span></span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/enterprise/premium-support" data-analytics-event="{&quot;action&quot;:&quot;premium_support&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;enterprise&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;premium_support_link_enterprise_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-comment-discussion NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.75 1h8.5c.966 0 1.75.784 1.75 1.75v5.5A1.75 1.75 0 0 1 10.25 10H7.061l-2.574 2.573A1.458 1.458 0 0 1 2 11.543V10h-.25A1.75 1.75 0 0 1 0 8.25v-5.5C0 1.784.784 1 1.75 1ZM1.5 2.75v5.5c0 .138.112.25.25.25h1a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h3.5a.25.25 0 0 0 .25-.25v-5.5a.25.25 0 0 0-.25-.25h-8.5a.25.25 0 0 0-.25.25Zm13 2a.25.25 0 0 0-.25-.25h-.5a.75.75 0 0 1 0-1.5h.5c.966 0 1.75.784 1.75 1.75v5.5A1.75 1.75 0 0 1 14.25 12H14v1.543a1.458 1.458 0 0 1-2.487 1.03L9.22 12.28a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215l2.22 2.22v-2.19a.75.75 0 0 1 .75-.75h1a.25.25 0 0 0 .25-.25Z"></path></svg>Premium Support</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Enterprise-grade 24/7 support</span></span></a></li></ul></div></li></ul></div></div></li><li><a href="https://github.com/pricing" data-analytics-event="{&quot;action&quot;:&quot;pricing&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;pricing&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;pricing_link_pricing_navbar&quot;}" class="MarketingNavigation-module__navLink__hUomM">Pricing</a></li></ul></nav><div class="MarketingHeader-module__ctaContainer__tBmPz"><div class="HeaderSearch-module__searchSlot__oVOUS"><button class="Primer_Brand__Button-module__Button___scH9Z Primer_Brand__Button-module__Button--subtle___F7pEE Primer_Brand__Button-module__Button--size-small___zQrEw HeaderSearch-module__trigger__zsF9q" type="button" aria-haspopup="dialog" aria-expanded="false" aria-label="Search or jump to, type / to search" data-analytics-event="{&quot;action&quot;:&quot;searchbar&quot;,&quot;tag&quot;:&quot;input&quot;,&quot;context&quot;:&quot;global&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;searchbar_input_global_navbar&quot;}"><span class="Primer_Brand__Button-module__Button__text___ED0bX"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ Primer_Brand__Button-module__Button--label___qrkyz Primer_Brand__Button-module__Button--label-subtle___8ndWH"><span class="HeaderSearch-module__content__kMpxU"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-search HeaderSearch-module__icon__wcrHX" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M10.68 11.74a6 6 0 0 1-7.922-8.982 6 6 0 0 1 8.982 7.922l3.04 3.04a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215ZM11.5 7a4.499 4.499 0 1 0-8.997 0A4.499 4.499 0 0 0 11.5 7Z"></path></svg><span class="HeaderSearch-module__label__d1iWG">Search</span><kbd class="HeaderSearch-module__kbd__HNG0o" aria-hidden="true">/</kbd></span></span></span></button><div class="d-none"></div></div><div class="AuthCTAs-module__signInWrap__q2P60"><a class="Primer_Brand__Button-module__Button___scH9Z Primer_Brand__Button-module__Button--subtle___F7pEE Primer_Brand__Button-module__Button--size-small___zQrEw AuthCTAs-module__cta__WpwQq AuthCTAs-module__desktopActionGap__UZuXT AuthCTAs-module__hiddenBelowLg__BfKBw" href="/login?return_to=https%3A%2F%2Fgithub.com%2Fdtprj%2Fdongting%2Fblob%2Fmaster%2FREADME_CN.md" data-analytics-event="{&quot;action&quot;:&quot;sign_in&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;auth_cta&quot;,&quot;location&quot;:&quot;header&quot;,&quot;label&quot;:&quot;sign_in_link_auth_cta_header&quot;}" data-hydro-click="{&quot;event_type&quot;:&quot;authentication.click&quot;,&quot;payload&quot;:{&quot;location_in_page&quot;:&quot;site header menu&quot;,&quot;repository_id&quot;:null,&quot;auth_type&quot;:&quot;SIGN_UP&quot;,&quot;originating_url&quot;:&quot;https://github.com/dtprj/dongting/blob/master/README_CN.md&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="8ea653bff8f51c0085c9e11b98b8702786b8d77d5092df0ef6b8cdb3ef2621d8"><span class="Primer_Brand__Button-module__Button__text___ED0bX"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ Primer_Brand__Button-module__Button--label___qrkyz Primer_Brand__Button-module__Button--label-subtle___8ndWH">Sign in</span></span></a></div><a class="Primer_Brand__Button-module__Button___scH9Z Primer_Brand__Button-module__Button--secondary___gHnw_ Primer_Brand__Button-module__Button--size-small___zQrEw AuthCTAs-module__cta__WpwQq" href="/signup?ref_cta=Sign+up&amp;ref_loc=header+logged+out&amp;ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E%2Fblob%2Fshow&amp;source=header-repo&amp;source_repo=dtprj%2Fdongting" data-analytics-event="{&quot;action&quot;:&quot;sign_up&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;auth_cta&quot;,&quot;location&quot;:&quot;header&quot;,&quot;label&quot;:&quot;sign_up_link_auth_cta_header&quot;}" data-hydro-click="{&quot;event_type&quot;:&quot;authentication.click&quot;,&quot;payload&quot;:{&quot;location_in_page&quot;:&quot;site header menu&quot;,&quot;repository_id&quot;:null,&quot;auth_type&quot;:&quot;SIGN_UP&quot;,&quot;originating_url&quot;:&quot;https://github.com/dtprj/dongting/blob/master/README_CN.md&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="8ea653bff8f51c0085c9e11b98b8702786b8d77d5092df0ef6b8cdb3ef2621d8"><span class="Primer_Brand__Button-module__Button__text___ED0bX"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ Primer_Brand__Button-module__Button--label___qrkyz Primer_Brand__Button-module__Button--label-secondary___eJ0_a">Sign up</span></span></a><button class="Primer_Brand__Button-module__Button___scH9Z Primer_Brand__Button-module__Button--subtle___F7pEE Primer_Brand__Button-module__Button--size-small___zQrEw HeaderAppearanceSettings-module__trigger__hUheK" type="button" aria-haspopup="dialog" aria-labelledby="_R_fbd_"><span class="Primer_Brand__Button-module__Button__leading-visual___jjtTe" data-testid="Button-leading-visual"><svg data-component="Octicon" focusable="false" aria-hidden="true" class="octicon octicon-sliders Primer_Brand__Button-module__Button__icon-visual____qybb" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M15 2.75a.75.75 0 0 1-.75.75h-4a.75.75 0 0 1 0-1.5h4a.75.75 0 0 1 .75.75Zm-8.5.75v1.25a.75.75 0 0 0 1.5 0v-4a.75.75 0 0 0-1.5 0V2H1.75a.75.75 0 0 0 0 1.5H6.5Zm1.25 5.25a.75.75 0 0 0 0-1.5h-6a.75.75 0 0 0 0 1.5h6ZM15 8a.75.75 0 0 1-.75.75H11.5V10a.75.75 0 1 1-1.5 0V6a.75.75 0 0 1 1.5 0v1.25h2.75A.75.75 0 0 1 15 8Zm-9 5.25v-2a.75.75 0 0 0-1.5 0v1.25H1.75a.75.75 0 0 0 0 1.5H4.5v1.25a.75.75 0 0 0 1.5 0v-2Zm9 0a.75.75 0 0 1-.75.75h-6a.75.75 0 0 1 0-1.5h6a.75.75 0 0 1 .75.75Z"></path></svg></span><span class="Primer_Brand__Button-module__Button__text___ED0bX"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ Primer_Brand__Button-module__Button--label___qrkyz Primer_Brand__Button-module__Button--label-subtle___8ndWH"></span></span></button><div class="Primer_Brand__Tooltip-module__Tooltip___0Eipx" data-direction="s" aria-hidden="true" id="_R_fbd_">Appearance settings</div></div></div></div></div><div class="MarketingHeader-module__bottomBorder__uZT38" aria-hidden="true"></div></header></div></div>
</react-partial>



      <div hidden="hidden" data-view-component="true" class="js-stale-session-flash stale-session-flash flash flash-warn flash-full">
  
        <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-alert">
    <path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path>
</svg>
        <span class="js-stale-session-flash-signed-in" hidden>You signed in with another tab or window. <a class="Link--inTextBlock" href="">Reload</a> to refresh your session.</span>
        <span class="js-stale-session-flash-signed-out" hidden>You signed out in another tab or window. <a class="Link--inTextBlock" href="">Reload</a> to refresh your session.</span>
        <span class="js-stale-session-flash-switched" hidden>You switched accounts on another tab or window. <a class="Link--inTextBlock" href="">Reload</a> to refresh your session.</span>

    <button id="icon-button-2896c4b6-bc03-482f-8cef-6c6a340ac514" aria-labelledby="tooltip-e5c4901b-6914-447c-b4c2-a7fb5f91df18" type="button" data-view-component="true" class="Button Button--iconOnly Button--invisible Button--medium flash-close js-flash-close">  <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-x Button-visual">
    <path d="M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l3.22 3.22a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-3.22 3.22a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z"></path>
</svg>
</button><tool-tip id="tooltip-e5c4901b-6914-447c-b4c2-a7fb5f91df18" for="icon-button-2896c4b6-bc03-482f-8cef-6c6a340ac514" popover="manual" data-direction="s" data-type="label" data-view-component="true" class="sr-only position-absolute">Dismiss alert</tool-tip>


  
</div>
    </div>

  <div id="start-of-content" class="show-on-focus"></div>








    <div id="js-flash-container" class="flash-container" data-turbo-replace>






  <template class="js-flash-template">
    
<div class="flash flash-full   {{ className }}">
  <div >
    <button autofocus class="flash-close js-flash-close" type="button" aria-label="Dismiss this message">
      <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-x">
    <path d="M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l3.22 3.22a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-3.22 3.22a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z"></path>
</svg>
    </button>
    <div aria-atomic="true" role="alert" class="js-flash-alert">
      
      <div>{{ message }}</div>

    </div>
  </div>
</div>
  </template>
</div>


    






  <div
    class="application-main "
    data-commit-hovercards-enabled
    data-discussion-hovercards-enabled
    data-issue-and-pr-hovercards-enabled
    data-project-hovercards-enabled
  >
        <div itemscope itemtype="http://schema.org/SoftwareSourceCode" class="">
    <main id="js-repo-pjax-container" >
      
      
    


    








  

    <link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/ym-6bc2c9bf8112c038.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/sk2-8deeef9351be3911.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/ni-3e194d222c7770bf.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/7x-71d30cb240cd901f.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/wwk-ad869086822937b1.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/a1-9f79841ecac1da8e.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/wzg-40a3213b3e83400b.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/5f-64f8e7d5bcbd4787.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/z5-b53a93078d026f1e.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/8h-cef26f68c1331011.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/fq-3ce13a77e863807a.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/a7-596b507bd20acddc.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/xfx-c82d8352413aea0a.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/j4-b3a9e87fa7878a98.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/global-nav-bar-29c7aef68ee9db41.js" fetchpriority="low" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-react-css.2d881b4d4a503d1c.module.css" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/pj.75627bfeb9ceb1da.module.css" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/global-nav-bar.2f2560cba06ab359.module.css" />

<react-partial
  partial-name="global-nav-bar"
  data-ssr="true"
  data-attempted-ssr="true"
  data-react-profiling="false"
>
  
  <script type="application/json" data-target="react-partial.embeddedData">{"props":{"contextRegion":{"crumbs":[{"crumb_type":"organization","label":"dtprj","is_root":false,"href":"/dtprj"},{"crumb_type":"repository","label":"dongting","is_root":false,"href":"/dtprj/dongting"}],"localNavigation":[{"id":"code","icon":"code","label":"Code","href":"/dtprj/dongting","selectedLinks":["repo_source","repo_downloads","repo_commits","repo_releases","repo_tags","repo_branches","repo_packages","repo_deployments","repo_attestations"],"popoverTarget":false,"commandId":"repositories:go-to-code","reactNav":{"appTarget":"code-view","anchor":"code-view-repo-link"},"turboNav":{"frame":"repo-content-turbo-frame"}},{"id":"issues","icon":"issue-opened","label":"Issues","href":"/dtprj/dongting/issues","selectedLinks":["repo_issues","repo_labels","repo_milestones"],"count":0,"popoverTarget":false,"commandId":"repositories:go-to-issues","reactNav":{"appTarget":"repo","anchor":null},"turboNav":{"frame":"repo-content-turbo-frame"}},{"id":"pull-requests","icon":"git-pull-request","label":"Pull requests","href":"/dtprj/dongting/pulls","selectedLinks":["repo_pulls","checks"],"count":0,"popoverTarget":false,"commandId":"repositories:go-to-pull-requests","reactNav":{"appTarget":null,"anchor":null},"turboNav":{"frame":"repo-content-turbo-frame"}},{"id":"discussions","icon":"comment-discussion","label":"Discussions","href":"/dtprj/dongting/discussions","selectedLinks":["repo_discussions"],"popoverTarget":false,"commandId":"repositories:go-to-discussions","reactNav":{"appTarget":null,"anchor":null},"turboNav":{"frame":"repo-content-turbo-frame"}},{"id":"actions","icon":"play","label":"Actions","href":"/dtprj/dongting/actions","selectedLinks":["repo_actions"],"popoverTarget":false,"commandId":"repositories:go-to-actions","reactNav":{"appTarget":"actions-workflows","anchor":null},"turboNav":{"frame":"repo-content-turbo-frame"}},{"id":"projects","icon":"table","label":"Projects","href":"/dtprj/dongting/projects","selectedLinks":["repo_projects","new_repo_project","repo_project"],"popoverTarget":false,"commandId":"repositories:go-to-projects","reactNav":{"appTarget":"repo","anchor":null},"turboNav":{"frame":"repo-content-turbo-frame"}},{"id":"security-and-quality","icon":"shield","label":"Security and quality","href":"/dtprj/dongting/security","selectedLinks":["security","overview","alerts","policy","token_scanning","code_scanning"],"count":0,"popoverTarget":false,"commandId":"repositories:go-to-security","reactNav":{"appTarget":null,"anchor":null},"turboNav":{"frame":"repo-content-turbo-frame"}},{"id":"insights","icon":"graph","label":"Insights","href":"/dtprj/dongting/pulse","selectedLinks":["repo_graphs","repo_contributors","dependency_graph","dependabot_updates","pulse","people","community"],"popoverTarget":false,"commandId":"repositories:go-to-insights","reactNav":{"appTarget":null,"anchor":null},"turboNav":{"frame":"repo-content-turbo-frame"}}],"localNavigationUpdateChannel":null,"selectedLink":"repo_source"},"owner":null,"headerLogo":{"href":"/","aria-label":"Homepage "},"notifications":{"indicatorMode":"disabled","websocketChannel":null,"fetchIndicatorSrc":"/notifications/indicator","fetchIndicatorEnabled":false},"issues":{"href":"/issues"},"pulls":{"href":"/pulls"},"contributedRepos":{"href":"/repos"},"copilot":{"show":false,"showAgentsButton":false,"showRelaunchAnnouncement":false,"copilotChatUrl":null,"copilotApiUrl":"https://api.githubcopilot.com"},"search":{"show":true,"showCommandPalette":false,"isSearchPage":false,"isJumpToSearch":false,"searchContext":{"scope":"repo:dtprj/dongting","current_repo_name":"dongting","current_repo_nwo":"dtprj/dongting","current_repo_org":"dtprj","user_id":"dtprj"}},"commandPalette":null,"enterpriseBar":{"show":false},"globalTransactionalMessage":[],"payloadsUrl":"/_global-navigation/payloads.json?can_toggle_site_admin_and_employee_status=0\u0026is_admin_mode_on=0\u0026is_ui_opted_out=0\u0026show_ui_opt_out=0\u0026v=7","contextRegionUrl":"/_global-navigation/context-region.json"}}</script>
  <div data-target="react-partial.reactRoot"><header aria-label="Global navigation menu" data-component="Stack" class="GlobalNav styles-module__appHeader__YzYWk prc-Stack-Stack-UQ9k6" data-gap="none" data-direction="vertical" data-align="stretch" data-wrap="nowrap" data-justify="start" data-padding="none"><div data-component="Stack" class="prc-Stack-Stack-UQ9k6" data-direction="horizontal" data-align="center" data-wrap="nowrap" data-justify="center" data-padding="none"><div data-testid="top-nav-center" data-component="Stack" class="styles-module__center__R3QRv styles-module__withLocalNavigation__rjTJ_ GlobalNavBar-module__loggedOut__Jv1rk prc-Stack-Stack-UQ9k6" data-gap="condensed" data-direction="horizontal" data-align="stretch" data-wrap="nowrap" data-justify="start" data-padding="normal"><nav class="styles-module__contextRegion__VbSp2 prc-Breadcrumbs-BreadcrumbsBase-3Gb-B" aria-label="Breadcrumbs" data-overflow="menu" data-variant="normal" data-component="Breadcrumbs"><ol class="prc-Breadcrumbs-BreadcrumbsList-BKjpe"><li class="prc-Breadcrumbs-ItemWrapper-k0NLn"><a class="styles-module__contextCrumb__IzGIq prc-Breadcrumbs-Item-jcraJ" data-component="Breadcrumbs.Item" href="/dtprj" data-discover="true"><span class="">dtprj</span></a></li><li class="prc-Breadcrumbs-ItemWrapper-k0NLn"><a class="styles-module__contextCrumb__IzGIq prc-Breadcrumbs-Item-jcraJ" data-component="Breadcrumbs.Item" href="/dtprj/dongting" data-discover="true"><span class="styles-module__contextCrumbLast__tI2e3">dongting</span></a></li></ol></nav></div><div data-testid="top-nav-right" data-component="Stack" class="styles-module__right__mlBQg styles-module__withLocalNavigation__rjTJ_ styles-module__rightWithResponsiveCreateButton__SKn2W prc-Stack-Stack-UQ9k6" data-gap="condensed" data-direction="horizontal" data-align="center" data-wrap="nowrap" data-justify="start" data-padding="normal"></div></div><h2 class="prc-src-InternalVisuallyHidden-2YaI6">Repository navigation</h2><nav class="prc-components-UnderlineWrapper-eT-Yj prc-UnderlineNav-UnderlineWrapper-GWONT LocalNavigation-module__LocalNavigation__b0Xc0" aria-label="Repository" data-variant="inset" data-overflow-mode="wrap" data-hide-icons-breakpoint="medium"><ul class="prc-UnderlineNav-ItemsList-oj8gN prc-components-UnderlineItemList-xKlKC" role="list"><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li class="prc-UnderlineNav-UnderlineNavItem-syRjR"><a aria-current="page" data-tab-item="code" data-react-nav="code-view" data-react-nav-anchor="code-view-repo-link" data-turbo-frame="repo-content-turbo-frame" class="prc-components-UnderlineItem-7fP-n" href="/dtprj/dongting" data-discover="true"><span data-component="icon"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-code" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m11.28 3.22 4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734L13.94 8l-3.72-3.72a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215Zm-6.56 0a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042L2.06 8l3.72 3.72a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L.47 8.53a.75.75 0 0 1 0-1.06Z"></path></svg></span><span data-component="text" data-content="Code">Code</span></a></li><li class="prc-UnderlineNav-UnderlineNavItem-syRjR"><a data-tab-item="issues" data-react-nav="repo" data-turbo-frame="repo-content-turbo-frame" class="prc-components-UnderlineItem-7fP-n" href="/dtprj/dongting/issues" data-discover="true"><span data-component="icon"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-issue-opened" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M8 9.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z"></path><path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Z"></path></svg></span><span data-component="text" data-content="Issues">Issues</span></a></li><li class="prc-UnderlineNav-UnderlineNavItem-syRjR"><a data-tab-item="pull-requests" data-turbo-frame="repo-content-turbo-frame" class="prc-components-UnderlineItem-7fP-n" href="/dtprj/dongting/pulls" data-discover="true"><span data-component="icon"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-git-pull-request" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.5 3.25a2.25 2.25 0 1 1 3 2.122v5.256a2.251 2.251 0 1 1-1.5 0V5.372A2.25 2.25 0 0 1 1.5 3.25Zm5.677-.177L9.573.677A.25.25 0 0 1 10 .854V2.5h1A2.5 2.5 0 0 1 13.5 5v5.628a2.251 2.251 0 1 1-1.5 0V5a1 1 0 0 0-1-1h-1v1.646a.25.25 0 0 1-.427.177L7.177 3.427a.25.25 0 0 1 0-.354ZM3.75 2.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm0 9.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm8.25.75a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Z"></path></svg></span><span data-component="text" data-content="Pull requests">Pull requests</span></a></li><li class="prc-UnderlineNav-UnderlineNavItem-syRjR"><a data-tab-item="discussions" data-turbo-frame="repo-content-turbo-frame" class="prc-components-UnderlineItem-7fP-n" href="/dtprj/dongting/discussions" data-discover="true"><span data-component="icon"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-comment-discussion" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.75 1h8.5c.966 0 1.75.784 1.75 1.75v5.5A1.75 1.75 0 0 1 10.25 10H7.061l-2.574 2.573A1.458 1.458 0 0 1 2 11.543V10h-.25A1.75 1.75 0 0 1 0 8.25v-5.5C0 1.784.784 1 1.75 1ZM1.5 2.75v5.5c0 .138.112.25.25.25h1a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h3.5a.25.25 0 0 0 .25-.25v-5.5a.25.25 0 0 0-.25-.25h-8.5a.25.25 0 0 0-.25.25Zm13 2a.25.25 0 0 0-.25-.25h-.5a.75.75 0 0 1 0-1.5h.5c.966 0 1.75.784 1.75 1.75v5.5A1.75 1.75 0 0 1 14.25 12H14v1.543a1.458 1.458 0 0 1-2.487 1.03L9.22 12.28a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215l2.22 2.22v-2.19a.75.75 0 0 1 .75-.75h1a.25.25 0 0 0 .25-.25Z"></path></svg></span><span data-component="text" data-content="Discussions">Discussions</span></a></li><li class="prc-UnderlineNav-UnderlineNavItem-syRjR"><a data-tab-item="actions" data-react-nav="actions-workflows" data-turbo-frame="repo-content-turbo-frame" class="prc-components-UnderlineItem-7fP-n" href="/dtprj/dongting/actions" data-discover="true"><span data-component="icon"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-play" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Zm4.879-2.773 4.264 2.559a.25.25 0 0 1 0 .428l-4.264 2.559A.25.25 0 0 1 6 10.559V5.442a.25.25 0 0 1 .379-.215Z"></path></svg></span><span data-component="text" data-content="Actions">Actions</span></a></li><li class="prc-UnderlineNav-UnderlineNavItem-syRjR"><a data-tab-item="projects" data-react-nav="repo" data-turbo-frame="repo-content-turbo-frame" class="prc-components-UnderlineItem-7fP-n" href="/dtprj/dongting/projects" data-discover="true"><span data-component="icon"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-table" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v12.5A1.75 1.75 0 0 1 14.25 16H1.75A1.75 1.75 0 0 1 0 14.25ZM6.5 6.5v8h7.75a.25.25 0 0 0 .25-.25V6.5Zm8-1.5V1.75a.25.25 0 0 0-.25-.25H6.5V5Zm-13 1.5v7.75c0 .138.112.25.25.25H5v-8ZM5 5V1.5H1.75a.25.25 0 0 0-.25.25V5Z"></path></svg></span><span data-component="text" data-content="Projects">Projects</span></a></li><li class="prc-UnderlineNav-UnderlineNavItem-syRjR"><a data-tab-item="security-and-quality" data-turbo-frame="repo-content-turbo-frame" class="prc-components-UnderlineItem-7fP-n" href="/dtprj/dongting/security" data-discover="true"><span data-component="icon"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-shield" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M7.467.133a1.748 1.748 0 0 1 1.066 0l5.25 1.68A1.75 1.75 0 0 1 15 3.48V7c0 1.566-.32 3.182-1.303 4.682-.983 1.498-2.585 2.813-5.032 3.855a1.697 1.697 0 0 1-1.33 0c-2.447-1.042-4.049-2.357-5.032-3.855C1.32 10.182 1 8.566 1 7V3.48a1.75 1.75 0 0 1 1.217-1.667Zm.61 1.429a.25.25 0 0 0-.153 0l-5.25 1.68a.25.25 0 0 0-.174.238V7c0 1.358.275 2.666 1.057 3.86.784 1.194 2.121 2.34 4.366 3.297a.196.196 0 0 0 .154 0c2.245-.956 3.582-2.104 4.366-3.298C13.225 9.666 13.5 8.36 13.5 7V3.48a.251.251 0 0 0-.174-.237l-5.25-1.68ZM8.75 4.75v3a.75.75 0 0 1-1.5 0v-3a.75.75 0 0 1 1.5 0ZM9 10.5a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path></svg></span><span data-component="text" data-content="Security and quality">Security and quality</span></a></li><li class="prc-UnderlineNav-UnderlineNavItem-syRjR"><a data-tab-item="insights" data-turbo-frame="repo-content-turbo-frame" class="prc-components-UnderlineItem-7fP-n" href="/dtprj/dongting/pulse" data-discover="true"><span data-component="icon"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-graph" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.5 1.75V13.5h13.75a.75.75 0 0 1 0 1.5H.75a.75.75 0 0 1-.75-.75V1.75a.75.75 0 0 1 1.5 0Zm14.28 2.53-5.25 5.25a.75.75 0 0 1-1.06 0L7 7.06 4.28 9.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.25-3.25a.75.75 0 0 1 1.06 0L10 7.94l4.72-4.72a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Z"></path></svg></span><span data-component="text" data-content="Insights">Insights</span></a></li></ul><div class="prc-UnderlineNav-MoreButtonContainer-Dnrq6"><div class="prc-UnderlineNav-MoreButtonDivider-dN0a-"></div><button data-component="overflow-menu-button" type="button" aria-haspopup="true" aria-expanded="false" tabindex="0" class="prc-Button-ButtonBase-9n-Xk prc-UnderlineNav-MoreButton-Y8soj" data-loading="false" data-size="medium" data-variant="invisible" id="_R_2ktp_"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-Iohp5"><span data-component="text" class="prc-Button-Label-FWkx3"><span>More<span class="prc-src-InternalVisuallyHidden-2YaI6"> items</span></span></span></span><span data-component="trailingAction" class="prc-Button-Visual-YNt2F prc-Button-VisualWrap-E4cnq"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-triangle-down" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m4.427 7.427 3.396 3.396a.25.25 0 0 0 .354 0l3.396-3.396A.25.25 0 0 0 11.396 7H4.604a.25.25 0 0 0-.177.427Z"></path></svg></span></button></div></nav><div class="d-none"></div></header></div>
</react-partial>


  



<turbo-frame id="repo-content-turbo-frame" target="_top" data-turbo-action="advance" class="">
    <div id="repo-content-pjax-container" class="repository-content " >
    



    
      
    








<react-app
  app-name="code-view"
  initial-path="/dtprj/dongting/blob/master/README_CN.md"
  style="display: block; min-height: calc(100vh - 64px);"
  data-attempted-ssr="true"
  data-ssr="true"
  data-lazy="false"
  data-alternate="false"
  data-data-router-enabled="true"
  data-react-profiling="false"
>
  
  <script type="application/json" data-target="react-app.embeddedData">{"payload":{"codeViewBlobRoute":{"csv":null,"csvError":null,"headerInfo":{"toc":[{"level":2,"text":"目录","anchor":"目录","htmlText":"目录"},{"level":1,"text":"项目介绍","anchor":"项目介绍","htmlText":"项目介绍"},{"level":1,"text":"10倍吞吐","anchor":"10倍吞吐","htmlText":"10倍吞吐"},{"level":1,"text":"零依赖","anchor":"零依赖","htmlText":"零依赖"},{"level":1,"text":"TRY IT","anchor":"try-it","htmlText":"TRY IT"},{"level":2,"text":"构建项目","anchor":"构建项目","htmlText":"构建项目"},{"level":2,"text":"启动服务器","anchor":"启动服务器","htmlText":"启动服务器"},{"level":2,"text":"运行基准测试","anchor":"运行基准测试","htmlText":"运行基准测试"},{"level":2,"text":"客户端使用","anchor":"客户端使用","htmlText":"客户端使用"},{"level":2,"text":"客户端完整例子","anchor":"客户端完整例子","htmlText":"客户端完整例子"},{"level":2,"text":"server配置","anchor":"server配置","htmlText":"server配置"},{"level":3,"text":"config.properties","anchor":"configproperties","htmlText":"config.properties"},{"level":3,"text":"Raft group公共配置（可选）","anchor":"raft-group公共配置可选","htmlText":"Raft group公共配置（可选）"},{"level":3,"text":"servers.properties","anchor":"serversproperties","htmlText":"servers.properties"},{"level":3,"text":"client.properties","anchor":"clientproperties","htmlText":"client.properties"},{"level":1,"text":"集群运维","anchor":"集群运维","htmlText":"集群运维"},{"level":2,"text":"配置一个多节点集群","anchor":"配置一个多节点集群","htmlText":"配置一个多节点集群"},{"level":2,"text":"运行管理工具","anchor":"运行管理工具","htmlText":"运行管理工具"},{"level":2,"text":"集群变更","anchor":"集群变更","htmlText":"集群变更"},{"level":3,"text":"例1：从group中删除一个node","anchor":"例1从group中删除一个node","htmlText":"例1：从group中删除一个node"},{"level":3,"text":"例2：为group增加一个node","anchor":"例2为group增加一个node","htmlText":"例2：为group增加一个node"},{"level":1,"text":"进阶","anchor":"进阶","htmlText":"进阶"},{"level":2,"text":"将项目导入IDE","anchor":"将项目导入ide","htmlText":"将项目导入IDE"},{"level":2,"text":"通过代码构建 raft 服务器","anchor":"通过代码构建-raft-服务器","htmlText":"通过代码构建 raft 服务器"},{"level":2,"text":"使用 JLink 裁剪 JRE","anchor":"使用-jlink-裁剪-jre","htmlText":"使用 JLink 裁剪 JRE"},{"level":1,"text":"关于我","anchor":"关于我","htmlText":"关于我"}]},"issueTemplate":null,"discussionTemplate":null,"richText":"\u003carticle class=\"markdown-body entry-content container-lg\" itemprop=\"text\"\u003e\u003cp dir=\"auto\"\u003e\u003ca href=\"https://github.com/dtprj/dongting/actions/workflows/maven.yml\"\u003e\u003cimg src=\"https://github.com/dtprj/dongting/actions/workflows/maven.yml/badge.svg\" alt=\"Java CI with Maven\" style=\"max-width: 100%;\"\u003e\u003c/a\u003e\n\u003ca href=\"https://app.codecov.io/github/dtprj/dongting\" rel=\"nofollow\"\u003e\u003cimg src=\"https://camo.githubusercontent.com/ee7bb20d97cc8706a17d41695c4a6995acd2714f0180c1f36a209a382efa01c1/68747470733a2f2f636f6465636f762e696f2f6769746875622f647470726a2f646f6e6774696e672f6272616e63682f6d61737465722f67726170682f62616467652e737667\" alt=\"codecov\" data-canonical-src=\"https://codecov.io/github/dtprj/dongting/branch/master/graph/badge.svg\" style=\"max-width: 100%;\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e\u003ca href=\"/dtprj/dongting/blob/master/README.md\"\u003eEnglish\u003c/a\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp dir=\"auto\"\u003e\u003cstrong\u003e注意\u003c/strong\u003e：正在开发中的 0.9 版本可能会和 0.8.x 不兼容。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e目录\u003c/h2\u003e\u003ca id=\"user-content-目录\" class=\"anchor\" aria-label=\"Permalink: 目录\" href=\"#目录\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ca href=\"#%E9%A1%B9%E7%9B%AE%E4%BB%8B%E7%BB%8D\"\u003e项目介绍\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#10%E5%80%8D%E5%90%9E%E5%90%90\"\u003e10倍吞吐\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#%E9%9B%B6%E4%BE%9D%E8%B5%96\"\u003e零依赖\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#try-it\"\u003eTRY IT\u003c/a\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ca href=\"#%E6%9E%84%E5%BB%BA%E9%A1%B9%E7%9B%AE\"\u003e构建项目\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#%E5%90%AF%E5%8A%A8%E6%9C%8D%E5%8A%A1%E5%99%A8\"\u003e启动服务器\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#%E8%BF%90%E8%A1%8C%E5%9F%BA%E5%87%86%E6%B5%8B%E8%AF%95\"\u003e运行基准测试\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#%E5%AE%A2%E6%88%B7%E7%AB%AF%E4%BD%BF%E7%94%A8\"\u003e客户端使用\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%AE%8C%E6%95%B4%E4%BE%8B%E5%AD%90\"\u003e客户端完整例子\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#server%E9%85%8D%E7%BD%AE\"\u003eserver配置\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#%E9%9B%86%E7%BE%A4%E8%BF%90%E7%BB%B4\"\u003e集群运维\u003c/a\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ca href=\"#%E9%85%8D%E7%BD%AE%E4%B8%80%E4%B8%AA%E5%A4%9A%E8%8A%82%E7%82%B9%E9%9B%86%E7%BE%A4\"\u003e配置一个多节点集群\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#%E8%BF%90%E8%A1%8C%E7%AE%A1%E7%90%86%E5%B7%A5%E5%85%B7\"\u003e运行管理工具\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#%E9%9B%86%E7%BE%A4%E5%8F%98%E6%9B%B4\"\u003e集群变更\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#%E8%BF%9B%E9%98%B6\"\u003e进阶\u003c/a\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ca href=\"#%E5%B0%86%E9%A1%B9%E7%9B%AE%E5%AF%BC%E5%85%A5ide\"\u003e将项目导入IDE\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#%E9%80%9A%E8%BF%87%E4%BB%A3%E7%A0%81%E6%9E%84%E5%BB%BA-raft-%E6%9C%8D%E5%8A%A1%E5%99%A8\"\u003e通过代码构建 raft 服务器\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#%E4%BD%BF%E7%94%A8-jlink-%E8%A3%81%E5%89%AA-jre\"\u003e使用 JLink 裁剪 JRE\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#%E5%85%B3%E4%BA%8E%E6%88%91\"\u003e关于我\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch1 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e项目介绍\u003c/h1\u003e\u003ca id=\"user-content-项目介绍\" class=\"anchor\" aria-label=\"Permalink: 项目介绍\" href=\"#项目介绍\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eDongting是一个高性能的分布式引擎，集成了RAFT共识算法、配置服务器和消息队列功能。\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e主要特性如下：\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003cstrong\u003eMulti RAFT 组支持\u003c/strong\u003e：在同一进程内运行多个RAFT组，支持动态添加、删除和更新RAFT group（分片），实现集群的动态扩展。状态机可以在raft框架中自定义。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e分布式配置服务器 DtKV\u003c/strong\u003e：树形结构，支持线性一致性的通用K/V操作、监听(watch)、TTL过期和分布式锁，类似于etcd\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003eDtKV\u003c/code\u003e是内存型数据库，因此数据总量不能太大，但它使用raft log作为redo日志，定期做快照，掉电也不会丢失一条数据。\u003c/li\u003e\n\u003cli\u003e原生支持树状目录，很多操作的复杂度是O(1)，比如在目录上进行watch，非常高效\u003c/li\u003e\n\u003cli\u003e支持临时目录，在TTL过期后自动删除整个目录，该删除操作为原子操作\u003c/li\u003e\n\u003cli\u003e不支持事务，但是提供了CAS和非常易用的分布式锁\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e（开发中）MQ\u003c/strong\u003e：使用RAFT日志作为消息队列日志。\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch1 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e10倍吞吐\u003c/h1\u003e\u003ca id=\"user-content-10倍吞吐\" class=\"anchor\" aria-label=\"Permalink: 10倍吞吐\" href=\"#10倍吞吐\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eDongting使用面向性能编程（POP）的方式开发。\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e在简单的同机测试中，现代高性能PC使用服务器默认配置即可轻松通过基准测试程序实现超过100万TPS，且响应时间(RT)也不会太大。\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch1 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e零依赖\u003c/h1\u003e\u003ca id=\"user-content-零依赖\" class=\"anchor\" aria-label=\"Permalink: 零依赖\" href=\"#零依赖\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e你还在为一传十、十传百的依赖问题烦恼吗？你曾经因为1GB的镜像大小而被嘲笑吗？现在你有救了！\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eDongting没有依赖。它不依赖第三方jar文件（只有SLF4J是可选的，如果类路径中不存在，项目将使用JDK logging）。\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eDongting核心只有两个JAR包，客户端和服务器端加起来不到1MB，你可以轻松将它嵌入到你的程序中，不管是client还是server。\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eDongting对JDK的要求也不高：客户端仅需Java 8，服务器端仅需Java 11。\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eDongting不需要使用高性能硬件，如RDMA或Optane什么的，它也不依赖Amazon或其他云服务提供商提供的任何第三方服务，如存储服务。\n它甚至可以在 HDD 硬盘和树莓派上良好运行。\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eDongting不需要调整Linux内核参数以达到最佳性能（生成环境你甚至可能没有权限这样做）。\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eDongting是完全模块化的（同时client仍兼容java8），服务端部署时，可以用jlink裁剪一个最小化的部署JRE。\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch1 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eTRY IT\u003c/h1\u003e\u003ca id=\"user-content-try-it\" class=\"anchor\" aria-label=\"Permalink: TRY IT\" href=\"#try-it\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e构建项目\u003c/h2\u003e\u003ca id=\"user-content-构建项目\" class=\"anchor\" aria-label=\"Permalink: 构建项目\" href=\"#构建项目\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e首先构建项目，构建产物位于target/dongting-dist目录：\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"mvn clean package -DskipUTs\"\u003e\u003cpre\u003emvn clean package -DskipUTs\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e如果你想要安装maven artifact：\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"mvn clean install -Dmaven.test.skip=true\"\u003e\u003cpre\u003emvn clean install -Dmaven.test.skip=true\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e构建后的目录结构如下：\u003c/p\u003e\n\u003cdiv class=\"snippet-clipboard-content notranslate position-relative overflow-auto\" data-snippet-clipboard-copy-content=\"dongting-dist/\n├── bin/                     # 脚本目录\n│   ├── benchmark.sh         # 基准测试脚本\n│   ├── benchmark.bat        # Windows基准测试脚本\n│   ├── start-dongting.sh    # 启动服务器脚本（Linux/Mac）\n│   ├── start-dongting.bat   # 启动服务器脚本\n│   ├── stop-dongting.sh     # 停止服务器脚本\n│   ├── stop-dongting.bat    # 停止服务器脚本\n│   ├── dongting-admin.sh    # 管理工具脚本（Linux/Mac）\n│   └── dongting-admin.bat   # 管理工具脚本\n├── lib/                     # JAR包目录\n│   ├── dongting-client-x.y.z-SNAPSHOT.jar\n│   ├── dongting-server-x.y.z-SNAPSHOT.jar\n│   ├── dongting-dist-x.y.z-SNAPSHOT.jar # 引导jar，如果使用代码构建raft server，它不是必须的\n│   ├── slf4j-api-x.y.z.jar\n│   ├── logback-x.y.z.jar\n│   └── logback-x.y.z.jar\n├── conf/                     # 配置文件目录\n│   ├── config.properties\n│   ├── servers.properties\n│   ├── client.properties\n│   ├── logback-server.xml\n│   ├── logback-admin.xml\n│   └── logback-benchmark.xml\n├── docs/                     # 文档目录\n├── data/                     # 数据目录（运行时生成）\n└── logs/                     # 日志目录\n    ├── dongting-server.log   # 服务器日志（运行时生成）\n    └── dongting-stats.log    # 统计日志（运行时生成）\"\u003e\u003cpre class=\"notranslate\"\u003e\u003ccode\u003edongting-dist/\n├── bin/                     # 脚本目录\n│   ├── benchmark.sh         # 基准测试脚本\n│   ├── benchmark.bat        # Windows基准测试脚本\n│   ├── start-dongting.sh    # 启动服务器脚本（Linux/Mac）\n│   ├── start-dongting.bat   # 启动服务器脚本\n│   ├── stop-dongting.sh     # 停止服务器脚本\n│   ├── stop-dongting.bat    # 停止服务器脚本\n│   ├── dongting-admin.sh    # 管理工具脚本（Linux/Mac）\n│   └── dongting-admin.bat   # 管理工具脚本\n├── lib/                     # JAR包目录\n│   ├── dongting-client-x.y.z-SNAPSHOT.jar\n│   ├── dongting-server-x.y.z-SNAPSHOT.jar\n│   ├── dongting-dist-x.y.z-SNAPSHOT.jar # 引导jar，如果使用代码构建raft server，它不是必须的\n│   ├── slf4j-api-x.y.z.jar\n│   ├── logback-x.y.z.jar\n│   └── logback-x.y.z.jar\n├── conf/                     # 配置文件目录\n│   ├── config.properties\n│   ├── servers.properties\n│   ├── client.properties\n│   ├── logback-server.xml\n│   ├── logback-admin.xml\n│   └── logback-benchmark.xml\n├── docs/                     # 文档目录\n├── data/                     # 数据目录（运行时生成）\n└── logs/                     # 日志目录\n    ├── dongting-server.log   # 服务器日志（运行时生成）\n    └── dongting-stats.log    # 统计日志（运行时生成）\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e启动服务器\u003c/h2\u003e\u003ca id=\"user-content-启动服务器\" class=\"anchor\" aria-label=\"Permalink: 启动服务器\" href=\"#启动服务器\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e在bin目录下，运行以下命令启动服务器：\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"./start-dongting.sh\"\u003e\u003cpre\u003e./start-dongting.sh\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e服务器将启动并监听 9331 端口（用于server内部通信，如raft复制）和 9332 端口（服务端口）。\n默认会启动一个\u003ccode\u003eDtKV\u003c/code\u003e实例，groupId为0。\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e运行下面的命令可以（根据data目录下的pid文件找到进程）停止服务器：\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"./stop-dongting.sh\"\u003e\u003cpre\u003e./stop-dongting.sh\u003c/pre\u003e\u003c/div\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e运行基准测试\u003c/h2\u003e\u003ca id=\"user-content-运行基准测试\" class=\"anchor\" aria-label=\"Permalink: 运行基准测试\" href=\"#运行基准测试\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e运行以下命令启动基准测试客户端：\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"./benchmark.sh -g 0\"\u003e\u003cpre\u003e./benchmark.sh -g 0\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e你可能需要调整参数以获得最大吞吐量，例如：\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"./benchmark.sh -g 0 --max-pending 10000 --client-count 2\"\u003e\u003cpre\u003e./benchmark.sh -g 0 --max-pending 10000 --client-count 2\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e尝试使用Java 21虚拟线程进行多线程压测（需要 Java 21）：\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"./benchmark.sh -g 0 --sync --thread-count 4000\"\u003e\u003cpre\u003e./benchmark.sh -g 0 --sync --thread-count 4000\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e看看我的测试结果吧（AMD 9700X 6C12T，致态 TiPro 9000 运行在 PCI-E 4.0 模式）：\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-powershell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"PS D:\\dongting-dist\\bin\u0026gt; .\\benchmark.bat -g 0\nConfiguration:\n  Config file: D:\\dongting-dist\\conf\\client.properties\n  Servers: 1,127.0.0.1:9332\n  Group ID: 0\n\nBenchmark config:\n  Java 21, async put, 10000 keys, 256 bytes value, 2000 total maxPending\n  1 clients, one thread per client\n\nWarming up for 3 seconds...\n\n[Warmup] TPS: 1,133,471, Success: 1,133,471, Fail: 0, Avg: 1,754 us, Max: 30,000 us\n[Warmup] TPS: 1,355,100, Success: 1,355,100, Fail: 0, Avg: 1,483 us, Max: 13,000 us\n[Warmup] TPS: 1,349,302, Success: 1,349,302, Fail: 0, Avg: 1,456 us, Max: 14,000 us\nWarmup complete, starting benchmark...\n\n[Now] TPS: 1,389,229, Success: 1,389,229, Fail: 0, Avg: 1,440 us, Max: 13,000 us\n[Now] TPS: 1,331,667, Success: 1,331,667, Fail: 0, Avg: 1,494 us, Max: 13,000 us\n[Now] TPS: 1,324,262, Success: 1,324,262, Fail: 0, Avg: 1,504 us, Max: 12,000 us\n[Now] TPS: 1,369,651, Success: 1,369,651, Fail: 0, Avg: 1,453 us, Max: 13,000 us\n[Now] TPS: 1,380,549, Success: 1,380,549, Fail: 0, Avg: 1,461 us, Max: 13,000 us\n[Now] TPS: 1,344,967, Success: 1,344,967, Fail: 0, Avg: 1,479 us, Max: 19,000 us\n[Now] TPS: 1,327,548, Success: 1,327,548, Fail: 0, Avg: 1,499 us, Max: 24,000 us\n[Now] TPS: 1,363,815, Success: 1,363,815, Fail: 0, Avg: 1,458 us, Max: 12,000 us\n[Now] TPS: 1,359,154, Success: 1,359,154, Fail: 0, Avg: 1,472 us, Max: 13,000 us\n[Now] TPS: 1,345,889, Success: 1,345,889, Fail: 0, Avg: 1,476 us, Max: 13,000 us\n\nBenchmark config:\n  Java 21, async put, 10000 keys, 256 bytes value, 2000 total maxPending\n  1 clients, one thread per client\n\n[Final] TPS: 1,353,905, Success: 13,539,049, Fail: 0, Avg: 1,473 us, Max: 24,000 us\n\nPS D:\\dongting-dist\\bin\u0026gt;\"\u003e\u003cpre\u003ePS D:\\dongting\u003cspan class=\"pl-k\"\u003e-\u003c/span\u003edist\\bin\u003cspan class=\"pl-k\"\u003e\u0026gt;\u003c/span\u003e .\\\u003cspan class=\"pl-c1\"\u003ebenchmark.bat\u003c/span\u003e \u003cspan class=\"pl-k\"\u003e-\u003c/span\u003eg \u003cspan class=\"pl-c1\"\u003e0\u003c/span\u003e\nConfiguration:\n  Config file: D:\\dongting\u003cspan class=\"pl-k\"\u003e-\u003c/span\u003edist\\conf\\client.properties\n  Servers: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e127.0\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003e0.1\u003c/span\u003e:\u003cspan class=\"pl-c1\"\u003e9332\u003c/span\u003e\n  Group ID: \u003cspan class=\"pl-c1\"\u003e0\u003c/span\u003e\n\nBenchmark config:\n  Java \u003cspan class=\"pl-c1\"\u003e21\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e async put\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e10000\u003c/span\u003e keys\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e256\u003c/span\u003e bytes value\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e2000\u003c/span\u003e total maxPending\n  \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e clients\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e one thread per client\n\nWarming up \u003cspan class=\"pl-k\"\u003efor\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e3\u003c/span\u003e seconds...\n\n[\u003cspan class=\"pl-k\"\u003eWarmup\u003c/span\u003e] TPS: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e133\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e471\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Success: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e133\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e471\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Fail: \u003cspan class=\"pl-c1\"\u003e0\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Avg: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e754\u003c/span\u003e us\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Max: \u003cspan class=\"pl-c1\"\u003e30\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e000\u003c/span\u003e us\n[\u003cspan class=\"pl-k\"\u003eWarmup\u003c/span\u003e] TPS: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e355\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e100\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Success: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e355\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e100\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Fail: \u003cspan class=\"pl-c1\"\u003e0\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Avg: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e483\u003c/span\u003e us\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Max: \u003cspan class=\"pl-c1\"\u003e13\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e000\u003c/span\u003e us\n[\u003cspan class=\"pl-k\"\u003eWarmup\u003c/span\u003e] TPS: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e349\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e302\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Success: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e349\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e302\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Fail: \u003cspan class=\"pl-c1\"\u003e0\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Avg: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e456\u003c/span\u003e us\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Max: \u003cspan class=\"pl-c1\"\u003e14\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e000\u003c/span\u003e us\nWarmup complete\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e starting benchmark...\n\n[\u003cspan class=\"pl-k\"\u003eNow\u003c/span\u003e] TPS: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e389\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e229\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Success: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e389\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e229\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Fail: \u003cspan class=\"pl-c1\"\u003e0\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Avg: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e440\u003c/span\u003e us\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Max: \u003cspan class=\"pl-c1\"\u003e13\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e000\u003c/span\u003e us\n[\u003cspan class=\"pl-k\"\u003eNow\u003c/span\u003e] TPS: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e331\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e667\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Success: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e331\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e667\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Fail: \u003cspan class=\"pl-c1\"\u003e0\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Avg: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e494\u003c/span\u003e us\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Max: \u003cspan class=\"pl-c1\"\u003e13\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e000\u003c/span\u003e us\n[\u003cspan class=\"pl-k\"\u003eNow\u003c/span\u003e] TPS: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e324\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e262\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Success: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e324\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e262\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Fail: \u003cspan class=\"pl-c1\"\u003e0\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Avg: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e504\u003c/span\u003e us\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Max: \u003cspan class=\"pl-c1\"\u003e12\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e000\u003c/span\u003e us\n[\u003cspan class=\"pl-k\"\u003eNow\u003c/span\u003e] TPS: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e369\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e651\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Success: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e369\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e651\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Fail: \u003cspan class=\"pl-c1\"\u003e0\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Avg: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e453\u003c/span\u003e us\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Max: \u003cspan class=\"pl-c1\"\u003e13\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e000\u003c/span\u003e us\n[\u003cspan class=\"pl-k\"\u003eNow\u003c/span\u003e] TPS: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e380\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e549\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Success: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e380\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e549\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Fail: \u003cspan class=\"pl-c1\"\u003e0\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Avg: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e461\u003c/span\u003e us\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Max: \u003cspan class=\"pl-c1\"\u003e13\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e000\u003c/span\u003e us\n[\u003cspan class=\"pl-k\"\u003eNow\u003c/span\u003e] TPS: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e344\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e967\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Success: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e344\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e967\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Fail: \u003cspan class=\"pl-c1\"\u003e0\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Avg: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e479\u003c/span\u003e us\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Max: \u003cspan class=\"pl-c1\"\u003e19\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e000\u003c/span\u003e us\n[\u003cspan class=\"pl-k\"\u003eNow\u003c/span\u003e] TPS: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e327\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e548\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Success: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e327\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e548\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Fail: \u003cspan class=\"pl-c1\"\u003e0\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Avg: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e499\u003c/span\u003e us\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Max: \u003cspan class=\"pl-c1\"\u003e24\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e000\u003c/span\u003e us\n[\u003cspan class=\"pl-k\"\u003eNow\u003c/span\u003e] TPS: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e363\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e815\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Success: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e363\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e815\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Fail: \u003cspan class=\"pl-c1\"\u003e0\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Avg: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e458\u003c/span\u003e us\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Max: \u003cspan class=\"pl-c1\"\u003e12\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e000\u003c/span\u003e us\n[\u003cspan class=\"pl-k\"\u003eNow\u003c/span\u003e] TPS: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e359\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e154\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Success: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e359\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e154\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Fail: \u003cspan class=\"pl-c1\"\u003e0\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Avg: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e472\u003c/span\u003e us\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Max: \u003cspan class=\"pl-c1\"\u003e13\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e000\u003c/span\u003e us\n[\u003cspan class=\"pl-k\"\u003eNow\u003c/span\u003e] TPS: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e345\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e889\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Success: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e345\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e889\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Fail: \u003cspan class=\"pl-c1\"\u003e0\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Avg: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e476\u003c/span\u003e us\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Max: \u003cspan class=\"pl-c1\"\u003e13\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e000\u003c/span\u003e us\n\nBenchmark config:\n  Java \u003cspan class=\"pl-c1\"\u003e21\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e async put\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e10000\u003c/span\u003e keys\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e256\u003c/span\u003e bytes value\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e2000\u003c/span\u003e total maxPending\n  \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e clients\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e one thread per client\n\n[\u003cspan class=\"pl-k\"\u003eFinal\u003c/span\u003e] TPS: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e353\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e905\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Success: \u003cspan class=\"pl-c1\"\u003e13\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e539\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e049\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Fail: \u003cspan class=\"pl-c1\"\u003e0\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Avg: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e473\u003c/span\u003e us\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e Max: \u003cspan class=\"pl-c1\"\u003e24\u003c/span\u003e\u003cspan class=\"pl-k\"\u003e,\u003c/span\u003e\u003cspan class=\"pl-c1\"\u003e000\u003c/span\u003e us\n\nPS D:\\dongting\u003cspan class=\"pl-k\"\u003e-\u003c/span\u003edist\\bin\u003cspan class=\"pl-k\"\u003e\u0026gt;\u003c/span\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e客户端使用\u003c/h2\u003e\u003ca id=\"user-content-客户端使用\" class=\"anchor\" aria-label=\"Permalink: 客户端使用\" href=\"#客户端使用\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eDongting内置的DtKV支持以下功能：\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003eget/batchGet\u003c/li\u003e\n\u003cli\u003eput/batchPut\u003c/li\u003e\n\u003cli\u003eremove/batchRemove\u003c/li\u003e\n\u003cli\u003elist\u003c/li\u003e\n\u003cli\u003emkdir\u003c/li\u003e\n\u003cli\u003ecompareAndSet\u003c/li\u003e\n\u003cli\u003eputTemp/makeTempDir（带 TTL 的节点）\u003c/li\u003e\n\u003cli\u003ecreateLock/createAutoRenewalLock\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003eDtKV\u003c/code\u003e 使用 \u003ccode\u003e.\u003c/code\u003e 作为 key 的分隔符。例如，您可以使用 \u003ccode\u003e\"dir1.key1\"\u003c/code\u003e 访问 \u003ccode\u003edir1\u003c/code\u003e 目录下的 \u003ccode\u003ekey1\u003c/code\u003e，value不能为空值或空串。\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e要使用\u003ccode\u003eDtKV\u003c/code\u003e客户端，需要引入 \u003ccode\u003edongting-client.jar\u003c/code\u003e（300+KB），无其它依赖。\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e以下是初始化\u003ccode\u003eDtKV\u003c/code\u003e客户端的简单示例：\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"// dongting 支持multi-raft，因此需要指定group id，默认group id是0\nint groupId = 0;\nKvClient kvClient = new KvClient();\nkvClient.start();\n// 在运行时添加node定义，每个node有一个唯一的正整数id 和一个 host:servicePort 地址\nkvClient.getRaftClient().clientAddNode(\u0026quot;1,127.0.0.1:9332\u0026quot;);\n// kvClient.getRaftClient().clientAddNode(\u0026quot;1,192.168.0.1:9332;2,192.168.0.2:9332;3,192.168.0.3:9332\u0026quot;);\n// 在运行时添加group定义，这里添加一个 groupId 为 0 的组，包含 id 为 1、2、3 的 3 个节点\nkvClient.getRaftClient().clientAddOrUpdateGroup(groupId, new int[]{1,2,3});\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c\"\u003e// dongting 支持multi-raft，因此需要指定group id，默认group id是0\u003c/span\u003e\n\u003cspan class=\"pl-smi\"\u003eint\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003egroupId\u003c/span\u003e = \u003cspan class=\"pl-c1\"\u003e0\u003c/span\u003e;\n\u003cspan class=\"pl-smi\"\u003eKvClient\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ekvClient\u003c/span\u003e = \u003cspan class=\"pl-k\"\u003enew\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eKvClient\u003c/span\u003e();\n\u003cspan class=\"pl-s1\"\u003ekvClient\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003estart\u003c/span\u003e();\n\u003cspan class=\"pl-c\"\u003e// 在运行时添加node定义，每个node有一个唯一的正整数id 和一个 host:servicePort 地址\u003c/span\u003e\n\u003cspan class=\"pl-s1\"\u003ekvClient\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003egetRaftClient\u003c/span\u003e().\u003cspan class=\"pl-en\"\u003eclientAddNode\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"1,127.0.0.1:9332\"\u003c/span\u003e);\n\u003cspan class=\"pl-c\"\u003e// kvClient.getRaftClient().clientAddNode(\"1,192.168.0.1:9332;2,192.168.0.2:9332;3,192.168.0.3:9332\");\u003c/span\u003e\n\u003cspan class=\"pl-c\"\u003e// 在运行时添加group定义，这里添加一个 groupId 为 0 的组，包含 id 为 1、2、3 的 3 个节点\u003c/span\u003e\n\u003cspan class=\"pl-s1\"\u003ekvClient\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003egetRaftClient\u003c/span\u003e().\u003cspan class=\"pl-en\"\u003eclientAddOrUpdateGroup\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003egroupId\u003c/span\u003e, \u003cspan class=\"pl-k\"\u003enew\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eint\u003c/span\u003e[]{\u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e,\u003cspan class=\"pl-c1\"\u003e2\u003c/span\u003e,\u003cspan class=\"pl-c1\"\u003e3\u003c/span\u003e});\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e请确保指定正确的端口。每个raft node开放两个端口：\n一个是\u003cstrong\u003e复制端口\u003c/strong\u003e，默认9331，用于 raft 节点之间的内部通信，如复制。\u003ccode\u003eAdminRaftClient\u003c/code\u003e 也连接到此端口。\n另一个是\u003cstrong\u003e服务端口\u003c/strong\u003e，默认9332，用于 \u003ccode\u003eKvClient\u003c/code\u003e 等客户端的连接。\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003eKvClient\u003c/code\u003e 提供同步和异步接口。对于单个 \u003ccode\u003eKvClient\u003c/code\u003e，异步操作可以获得最大吞吐量，而同步操作需要多个线程（或虚拟线程）才能达到较高吞吐量。\n需要注意的是，异步操作的回调可能在 raft 线程或 IO 线程上执行。\n因此，你不应在这些回调中执行任何阻塞或 CPU 密集型操作。 如果你不确定或缺乏高级技能，强烈建议使用同步接口。\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e以下是使用 \u003ccode\u003eKvClient\u003c/code\u003e 的简单示例：\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-java notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"// 同步put\nkvClient.put(groupId, \u0026quot;key1\u0026quot;.getBytes(), \u0026quot;value1\u0026quot;.getBytes());\nkvClient.put(groupId, \u0026quot;key1\u0026quot;.getBytes(), \u0026quot;value1\u0026quot;.getBytes(), (raftIndex, ex) -\u0026gt; {\n    // 执行一些既不阻塞也不消耗 CPU 的操作\n});\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c\"\u003e// 同步put\u003c/span\u003e\n\u003cspan class=\"pl-s1\"\u003ekvClient\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003eput\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003egroupId\u003c/span\u003e, \u003cspan class=\"pl-s\"\u003e\"key1\"\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003egetBytes\u003c/span\u003e(), \u003cspan class=\"pl-s\"\u003e\"value1\"\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003egetBytes\u003c/span\u003e());\n\u003cspan class=\"pl-s1\"\u003ekvClient\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003eput\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003egroupId\u003c/span\u003e, \u003cspan class=\"pl-s\"\u003e\"key1\"\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003egetBytes\u003c/span\u003e(), \u003cspan class=\"pl-s\"\u003e\"value1\"\u003c/span\u003e.\u003cspan class=\"pl-en\"\u003egetBytes\u003c/span\u003e(), (\u003cspan class=\"pl-s1\"\u003eraftIndex\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003eex\u003c/span\u003e) -\u0026gt; {\n    \u003cspan class=\"pl-c\"\u003e// 执行一些既不阻塞也不消耗 CPU 的操作\u003c/span\u003e\n});\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e有关 \u003ccode\u003eKvClient\u003c/code\u003e 类的详细用法，请参阅 Javadocs。\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e客户端完整例子\u003c/h2\u003e\u003ca id=\"user-content-客户端完整例子\" class=\"anchor\" aria-label=\"Permalink: 客户端完整例子\" href=\"#客户端完整例子\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ca href=\"/dtprj/dongting/blob/master/demos/src/main/java/com/github/dtprj/dongting/demos/kvclient/SimpleDemo.java\"\u003eSimpleDemo\u003c/a\u003e - 基本 put/get/list 操作\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"/dtprj/dongting/blob/master/demos/src/main/java/com/github/dtprj/dongting/demos/kvclient/BatchDemo.java\"\u003eBatchDemo\u003c/a\u003e - 批量读写删\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"/dtprj/dongting/blob/master/demos/src/main/java/com/github/dtprj/dongting/demos/kvclient/CasDemo.java\"\u003eCasDemo\u003c/a\u003e - CAS原子操作\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"/dtprj/dongting/blob/master/demos/src/main/java/com/github/dtprj/dongting/demos/kvclient/TempValueDemo.java\"\u003eTempValueDemo\u003c/a\u003e - 临时值(TTL)\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"/dtprj/dongting/blob/master/demos/src/main/java/com/github/dtprj/dongting/demos/kvclient/TempDirDemo.java\"\u003eTempDirDemo\u003c/a\u003e - 临时目录(TTL)\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"/dtprj/dongting/blob/master/demos/src/main/java/com/github/dtprj/dongting/demos/kvclient/UpdateTtlDemo.java\"\u003eUpdateTtlDemo\u003c/a\u003e - 更新临时节点的TTL\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"/dtprj/dongting/blob/master/demos/src/main/java/com/github/dtprj/dongting/demos/kvclient/WatchDemo.java\"\u003eWatchDemo\u003c/a\u003e - key/目录变化监听\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"/dtprj/dongting/blob/master/demos/src/main/java/com/github/dtprj/dongting/demos/kvclient/DistributedLockDemo.java\"\u003eDistributedLockDemo\u003c/a\u003e - 分布式锁\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"/dtprj/dongting/blob/master/demos/src/main/java/com/github/dtprj/dongting/demos/kvclient/AutoRenewalLockDemo.java\"\u003eAutoRenewalLockDemo\u003c/a\u003e - 自动续约分布式锁\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eserver配置\u003c/h2\u003e\u003ca id=\"user-content-server配置\" class=\"anchor\" aria-label=\"Permalink: server配置\" href=\"#server配置\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e服务器配置主要包含两个配置文件：\u003ccode\u003econfig.properties\u003c/code\u003e 和 \u003ccode\u003eservers.properties\u003c/code\u003e。\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003econfig.properties\u003c/h3\u003e\u003ca id=\"user-content-configproperties\" class=\"anchor\" aria-label=\"Permalink: config.properties\" href=\"#configproperties\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e此文件配置 Raft 服务器的基本参数：\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003cstrong\u003enodeId\u003c/strong\u003e：每个服务器必须有一个唯一的正整数 node ID，注意是从1开始，如果只有一个node通常就设置为1\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003ereplicatePort\u003c/strong\u003e：用于服务器内部通信的端口，例如 Raft 复制（默认值：9331）\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eservicePort\u003c/strong\u003e：用于客户端-服务器通信的端口（默认值：9332）\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eelectTimeout\u003c/strong\u003e：Raft 选举超时时间，单位毫秒（默认值：15000）\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eheartbeatInterval\u003c/strong\u003e：Raft 心跳间隔时间，单位毫秒（默认值：2000）\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eblockIoThreads\u003c/strong\u003e：处理阻塞 IO 的线程数（如果没有设置，默认值运行时根据CPU核心数计算生成）\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eRaft group公共配置（可选）\u003c/h3\u003e\u003ca id=\"user-content-raft-group公共配置可选\" class=\"anchor\" aria-label=\"Permalink: Raft group公共配置（可选）\" href=\"#raft-group公共配置可选\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e以下配置在 \u003ccode\u003econfig.properties\u003c/code\u003e 中，影响 Raft group 的行为：\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003cstrong\u003edataDir\u003c/strong\u003e：数据目录（默认值指向dongting-dist目录下的data目录）\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003esyncForce\u003c/strong\u003e：如果为 true，任何操作都会在响应 leader 和计算法定人数之前持久化（fsync）到磁盘。此选项对性能有显著影响（默认值：true）\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003esaveSnapshotSeconds\u003c/strong\u003e：保存快照的间隔秒数（默认值：3600）\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003emaxKeepSnapshots\u003c/strong\u003e：最多保留的快照数量（默认值：2）\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003esaveSnapshotWhenClose\u003c/strong\u003e：关闭时（通常是服务器关闭时）是否保存快照（默认值：true）\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003edeleteLogsAfterTakeSnapshot\u003c/strong\u003e：创建快照后是否删除不再需要的raft日志文件（默认值：true）\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eservers.properties\u003c/h3\u003e\u003ca id=\"user-content-serversproperties\" class=\"anchor\" aria-label=\"Permalink: servers.properties\" href=\"#serversproperties\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e此文件配置集群拓扑和 Raft group：\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003e\u003cstrong\u003eservers\u003c/strong\u003e：node 列表，格式为 \u003ccode\u003enodeId,ip:replicatePort\u003c/code\u003e。多个 node 用分号分隔。示例：\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-ini notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"# 单节点示例\nservers = 1,127.0.0.1:9331\n\n# 多节点示例\nservers = 1,192.168.0.1:9331;2,192.168.0.2:9331;3,192.168.0.3:9331\n\n# 本地多节点示例（使用不同端口隔离）\nservers = 1,127.0.0.1:4001;2,127.0.0.1:4002;3,127.0.0.1:4003\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e 单节点示例\u003c/span\u003e\n\u003cspan class=\"pl-k\"\u003eservers\u003c/span\u003e = 1,127.0.0.1:9331\n\n\u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e 多节点示例\u003c/span\u003e\n\u003cspan class=\"pl-k\"\u003eservers\u003c/span\u003e = 1,192.168.0.1:9331\u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e;\u003c/span\u003e2,192.168.0.2:9331;3,192.168.0.3:9331\u003c/span\u003e\n\n\u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e#\u003c/span\u003e 本地多节点示例（使用不同端口隔离）\u003c/span\u003e\n\u003cspan class=\"pl-k\"\u003eservers\u003c/span\u003e = 1,127.0.0.1:4001\u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e;\u003c/span\u003e2,127.0.0.1:4002;3,127.0.0.1:4003\u003c/span\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003e\u003cstrong\u003eRaft group member 配置\u003c/strong\u003e：格式为 \u003ccode\u003egroup.\u0026lt;groupId\u0026gt;.nodeIdOfMembers = nodeId1,nodeId2,...\u003c/code\u003e。node ID 必须在 \u003ccode\u003eservers\u003c/code\u003e 属性中定义。\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-ini notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"group.0.nodeIdOfMembers = 1,2,3\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003egroup.0.nodeIdOfMembers\u003c/span\u003e = 1,2,3\u003c/pre\u003e\u003c/div\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003e\u003cstrong\u003eRaft group observer 配置\u003c/strong\u003e：格式为 \u003ccode\u003egroup.\u0026lt;groupId\u0026gt;.nodeIdOfObservers = nodeId1,nodeId2,...\u003c/code\u003e。observer 不会参与 leader 选举。\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-ini notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"group.0.nodeIdOfObservers = 4\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003egroup.0.nodeIdOfObservers\u003c/span\u003e = 4\u003c/pre\u003e\u003c/div\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp dir=\"auto\"\u003eobserver会接收leader的数据复制，但是不会参与raft投票。\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eclient.properties\u003c/h3\u003e\u003ca id=\"user-content-clientproperties\" class=\"anchor\" aria-label=\"Permalink: client.properties\" href=\"#clientproperties\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eclient.properties文件被benchmark使用，注意里面配置是的servers参数连接\u003cstrong\u003e服务端口\u003c/strong\u003e。\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch1 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e集群运维\u003c/h1\u003e\u003ca id=\"user-content-集群运维\" class=\"anchor\" aria-label=\"Permalink: 集群运维\" href=\"#集群运维\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e配置一个多节点集群\u003c/h2\u003e\u003ca id=\"user-content-配置一个多节点集群\" class=\"anchor\" aria-label=\"Permalink: 配置一个多节点集群\" href=\"#配置一个多节点集群\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e默认情况下，直接运行start-dongting脚本会启动一个单节点集群监听本机9331端口和9332端口。\n如果需要启动多节点集群，以3节点为例，需要做以下工作：\u003c/p\u003e\n\u003col dir=\"auto\"\u003e\n\u003cli\u003e准备3份dongting-dist目录，如果是自己做测试，在同一个机器上也可以，但要修改端口。\u003c/li\u003e\n\u003cli\u003e修改每个config.properties文件中的nodeId。nodeId从1开始，不同node必须不一样。\u003cstrong\u003e注意服务器一旦启动，nodeId就不能再修改了（除非清空data目录系的数据）\u003c/strong\u003e。\u003c/li\u003e\n\u003cli\u003e（可选）同一个机器上做测试的话，要修改每个config.properties文件中的端口。\u003c/li\u003e\n\u003cli\u003e修改servers.properties文件中的servers参数，3节点的话可能是\u003ccode\u003e1,192.168.0.1:9331;2,192.168.0.2:9331;3,192.168.0.3:9331\u003c/code\u003e或者\u003ccode\u003e1,127.0.0.1:4001;2,127.0.0.1:4002;3,127.0.0.1:4003\u003c/code\u003e，根据你的ip和端口配置而定\u003c/li\u003e\n\u003cli\u003e修改servers.properties文件中的\u003ccode\u003egroup.0.nodeIdOfMembers\u003c/code\u003e参数，3节点的话可能是\u003ccode\u003e1,2,3\u003c/code\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp dir=\"auto\"\u003e以上2、3每个node配置不一样，而4、5每个节点配置是一样的。\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e如果要做benchmark，还要修改client.properties文件中的servers参数，连接\u003cstrong\u003e服务端口\u003c/strong\u003e（默认9332）。\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e配置好以后就可以分别启动了，可以随意杀掉一个进程，看看集群的表现。\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e运行管理工具\u003c/h2\u003e\u003ca id=\"user-content-运行管理工具\" class=\"anchor\" aria-label=\"Permalink: 运行管理工具\" href=\"#运行管理工具\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003ebin目录下的 dongting-admin 脚本是一个服务器管理工具，可用于：\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e更改raft group member/observer\u003c/li\u003e\n\u003cli\u003e转移leader\u003c/li\u003e\n\u003cli\u003e添加/删除组（multi-raft）\u003c/li\u003e\n\u003cli\u003e添加/删除集群节点\u003c/li\u003e\n\u003cli\u003e查询服务器状态\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp dir=\"auto\"\u003e运行时不带参数可查看使用方法。\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e可以通过\u003ccode\u003eAdminRaftClient\u003c/code\u003e类执行所有的管理功能，记得连接到复制端口。\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e集群变更\u003c/h2\u003e\u003ca id=\"user-content-集群变更\" class=\"anchor\" aria-label=\"Permalink: 集群变更\" href=\"#集群变更\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eDongting实现了标准的raft joint consensus，变更分为两个阶段，先prepare，再commit（或abort）。\n这3个操作都实现了幂等，如果因为网络原因失败了，可以放心重试。\n在dongting中，prepare阶段完成以后，c-old保存在members/observers中，c-new保存在preparedMembers/preparedObservers中，\ncommit或abort后，preparedMembers/preparedObservers会被清空。\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e集群变更的一个原因可能是发生了故障，新配置可能无法下发到故障node，造成集群不同node的配置不一致。接下来描述的概念会有助于你了解应该怎么办。\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e在Dongting中，prepare/commit/abort操作也是一条raft日志，其中包含raft group的members/observers/preparedMembers/preparedObservers配置信息。\n状态机保存snapshot时（默认关闭时会保存一次），也会将这些信息存到snapshot中，raft group启动时，会先恢复快照（如果有），\n然后从检查点重做日志，使用其中最新的group配置信息，因此servers.properties中的nodeIdOfMembers/nodeIdOfObservers其实只是用来引导的，\n如果有snapshot或者raft log中有prepare/commit信息，servers.properties中的nodeIdOfMembers/nodeIdOfObservers就被忽略了。尽管如此，\n还是有必要维护正确的nodeIdOfMembers/nodeIdOfObservers。为了简化，servers.properties中没有preparedMembers/preparedObservers。\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e为了避免错误，server会拒绝非members/preparedMembers发过来的投票和复制请求，如果一个node的配置数据太旧，导致它不认新的leader，\n可以删除它的数据，将servers.properties的group信息维护正确，然后重新加入集群。\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e注意，以上提到的group信息，都只定义了id，它必须在servers列表中定义，否则会出错，增加新node前，要在每个node上都更新定义。\ndongting-server模块需要以一个配置来启动，并且可以在运行时修改，但它不负责维护配置的持久化，是dongting-dist（dongting-dist.jar）模块\n负责servers.properties的读取和更新，使用dongting-admin脚本addNode/removeNode以后，它先更新内存，然后更新servers.properties，\n这不是一个原子操作，你需要关注脚本的输出，确保配置已正确更新（dongting-admin脚本还提供了一个幂等的sync-config子命令用来手工同步）。\n如果你没有使用dongting-dist的脚本，而是自己通过代码来组装嵌入式dongting-server，那这些配置的持久化就由你自己来保证。\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e例1：从group中删除一个node\u003c/h3\u003e\u003ca id=\"user-content-例1从group中删除一个node\" class=\"anchor\" aria-label=\"Permalink: 例1：从group中删除一个node\" href=\"#例1从group中删除一个node\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e假设你有一个3节点的集群，节点ID为1、2、3，group ID为0，现在需要删除节点3。操作步骤如下：\u003c/p\u003e\n\u003col dir=\"auto\"\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003e首先查询一下集群状态，随便找一个node，从返回的信息中找到leader：\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"./dongting-admin.sh query-status --node-id 1 --group-id 0\"\u003e\u003cpre\u003e./dongting-admin.sh query-status --node-id 1 --group-id 0\u003c/pre\u003e\u003c/div\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003e准备配置变更，将成员从1、2、3改为1、2（以下prepare/commit/abort应该在leader上调用）：\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"./dongting-admin.sh prepare-change --group-id 0 --old-members 1,2,3 --new-members 1,2\"\u003e\u003cpre\u003e./dongting-admin.sh prepare-change --group-id 0 --old-members 1,2,3 --new-members 1,2\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e这会返回一个prepare-index，记录下来用于下一步。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003e提交配置变更：\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"./dongting-admin.sh commit-change --group-id 0 --prepare-index \u0026lt;返回的prepare-index\u0026gt;\"\u003e\u003cpre\u003e./dongting-admin.sh commit-change --group-id 0 --prepare-index \u003cspan class=\"pl-k\"\u003e\u0026lt;\u003c/span\u003e返回的prepare-index\u003cspan class=\"pl-k\"\u003e\u0026gt;\u003c/span\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003e集群配置已更新，节点3已经从group中移除。你可以通过以下命令验证：\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"./dongting-admin.sh query-status --node-id 1 --group-id 0\"\u003e\u003cpre\u003e./dongting-admin.sh query-status --node-id 1 --group-id 0\u003c/pre\u003e\u003c/div\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003e如果需要，可以删除节点3的定义（此操作应在所有节点1、2上都执行）：\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"./dongting-admin.sh remove-node --node-id 1 --remove-node-id 3\"\u003e\u003cpre\u003e./dongting-admin.sh remove-node --node-id 1 --remove-node-id 3\u003c/pre\u003e\u003c/div\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e例2：为group增加一个node\u003c/h3\u003e\u003ca id=\"user-content-例2为group增加一个node\" class=\"anchor\" aria-label=\"Permalink: 例2：为group增加一个node\" href=\"#例2为group增加一个node\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e假设你有一个3节点的集群，节点ID为1、2、3，group ID为0，现在需要添加节点4。操作步骤如下：\u003c/p\u003e\n\u003col dir=\"auto\"\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003e首先启动新节点4的服务器进程：\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"cd node4/bin\n./start-dongting.sh\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c1\"\u003ecd\u003c/span\u003e node4/bin\n./start-dongting.sh\u003c/pre\u003e\u003c/div\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003e添加节点4的定义（此操作应在所有现有节点上执行）：\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"./dongting-admin.sh add-node --node-id 1 --add-node-id 4 --host theHost --port thePort\"\u003e\u003cpre\u003e./dongting-admin.sh add-node --node-id 1 --add-node-id 4 --host theHost --port thePort\u003c/pre\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"./dongting-admin.sh add-node --node-id 2 --add-node-id 4 --host theHost --port thePort\"\u003e\u003cpre\u003e./dongting-admin.sh add-node --node-id 2 --add-node-id 4 --host theHost --port thePort\u003c/pre\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"./dongting-admin.sh add-node --node-id 3 --add-node-id 4 --host theHost --port thePort\"\u003e\u003cpre\u003e./dongting-admin.sh add-node --node-id 3 --add-node-id 4 --host theHost --port thePort\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e注意：port应该是新节点的复制端口，不是服务端口。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003e确认节点4已添加：\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"./dongting-admin.sh list-nodes --node-id 1\"\u003e\u003cpre\u003e./dongting-admin.sh list-nodes --node-id 1\u003c/pre\u003e\u003c/div\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003e准备配置变更，将成员从1、2、3改为1、2、3、4（prepare/commit/abort应该在leader上调用）：\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"./dongting-admin.sh prepare-change --group-id 0 --old-members 1,2,3 --new-members 1,2,3,4\"\u003e\u003cpre\u003e./dongting-admin.sh prepare-change --group-id 0 --old-members 1,2,3 --new-members 1,2,3,4\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e这会返回一个prepare-index，记录下来用于下一步。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003e提交配置变更：\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"./dongting-admin.sh commit-change --group-id 0 --prepare-index \u0026lt;返回的prepare-index\u0026gt;\"\u003e\u003cpre\u003e./dongting-admin.sh commit-change --group-id 0 --prepare-index \u003cspan class=\"pl-k\"\u003e\u0026lt;\u003c/span\u003e返回的prepare-index\u003cspan class=\"pl-k\"\u003e\u0026gt;\u003c/span\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp dir=\"auto\"\u003e集群配置已更新，节点4已加入group。你可以通过以下命令验证：\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"./dongting-admin.sh query-status --node-id 1 --group-id 0\"\u003e\u003cpre\u003e./dongting-admin.sh query-status --node-id 1 --group-id 0\u003c/pre\u003e\u003c/div\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp dir=\"auto\"\u003e注意：新节点加入后会从leader同步所有数据，这可能需要一些时间，取决于数据量。现在既然新加了一个节点，那这个集群的投票quorum就变成了3了，\n所以，生成环境上，建议新节点先作为observer加入，等数据同步完成后，再做一次变更将observer提升为member。\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch1 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e进阶\u003c/h1\u003e\u003ca id=\"user-content-进阶\" class=\"anchor\" aria-label=\"Permalink: 进阶\" href=\"#进阶\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e将项目导入IDE\u003c/h2\u003e\u003ca id=\"user-content-将项目导入ide\" class=\"anchor\" aria-label=\"Permalink: 将项目导入IDE\" href=\"#将项目导入ide\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e将项目导入后，要设置 IDE，可以参考 \u003ca href=\"/dtprj/dongting/blob/master/docs/developer_CN.md\"\u003e开发指南\u003c/a\u003e。\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e通过代码构建 raft 服务器\u003c/h2\u003e\u003ca id=\"user-content-通过代码构建-raft-服务器\" class=\"anchor\" aria-label=\"Permalink: 通过代码构建 raft 服务器\" href=\"#通过代码构建-raft-服务器\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e所有示例都在 \u003ccode\u003edemos\u003c/code\u003e 目录中。它们不需要配置，直接执行 \u003ccode\u003emain\u003c/code\u003e 方法即可运行。\n建议在 IDE 中运行，以便更方便地设置断点和观察。\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e\u003ca href=\"/dtprj/dongting/blob/master/demos/src/main/java/com/github/dtprj/dongting/demos/advanced/cluster\"\u003ecluster\u003c/a\u003e 目录包含一个运行 3 节点 raft 集群的示例。\n分别运行 \u003ccode\u003eDemoServer1\u003c/code\u003e、\u003ccode\u003eDemoServer2\u003c/code\u003e 和 \u003ccode\u003eDemoServer3\u003c/code\u003e，raft 集群通常会在一秒内就绪。\n运行 \u003ccode\u003eDemoClient\u003c/code\u003e，它将发送 100 万次 put 和 get 请求，同时记录完成时间。\n运行 \u003ccode\u003ePeriodPutClient\u003c/code\u003e，它每秒持续发送一个 put 请求，不会停止。\n您可以随时关闭一个服务器，\u003ccode\u003ePeriodPutClient\u003c/code\u003e 将不受影响。\n此外，执行 \u003ccode\u003eChangeLeader\u003c/code\u003e 将 Raft leader 切换到指定节点。\n正在运行的 \u003ccode\u003ePeriodPutClient\u003c/code\u003e 将完全不受影响。\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e\u003ca href=\"/dtprj/dongting/blob/master/demos/src/main/java/com/github/dtprj/dongting/demos/advanced/standalone\"\u003estandalone\u003c/a\u003e 目录包含一个运行单节点 raft 组的示例。\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e\u003ca href=\"/dtprj/dongting/blob/master/demos/src/main/java/com/github/dtprj/dongting/demos/advanced/embedded\"\u003eembedded\u003c/a\u003e 目录包含一个将 3 个服务器和 1 个客户端嵌入到单个进程中的示例。\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e\u003ca href=\"/dtprj/dongting/blob/master/demos/src/main/java/com/github/dtprj/dongting/demos/advanced/configchange\"\u003econfigchange\u003c/a\u003e 目录包含在运行时动态更改 Raft 成员的示例。\n首先，运行 \u003ccode\u003eConfigChangeDemoServer1\u003c/code\u003e、\u003ccode\u003eConfigChangeDemoServer2\u003c/code\u003e、\u003ccode\u003eConfigChangeDemoServer3\u003c/code\u003e 和 \u003ccode\u003eConfigChangeDemoServer4\u003c/code\u003e。\n默认情况下，将启动包含节点 1、2、3 的 Raft 组。\n执行 \u003ccode\u003eChangeTo234Client\u003c/code\u003e 将 Raft 成员更改为节点 2、3、4。\n执行 \u003ccode\u003eChangeTo123Client\u003c/code\u003e 将 Raft 成员恢复为节点 1、2、3。\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e\u003ca href=\"/dtprj/dongting/blob/master/demos/src/main/java/com/github/dtprj/dongting/demos/advanced/multiraft\"\u003emultiraft\u003c/a\u003e 目录包含运行多 raft 的示例，通常用于分片或动态分片。\n运行 \u003ccode\u003eMultiRaftDemoServer1\u003c/code\u003e、\u003ccode\u003eMultiRaftDemoServer2\u003c/code\u003e 和 \u003ccode\u003eMultiRaftDemoServer3\u003c/code\u003e 默认启动两个（静态）raft 组，ID 为 101 和 102。\n执行 \u003ccode\u003ePeriodPutClient\u003c/code\u003e 将每秒向 raft 组 101、102 和 103 发送一个 put 请求。\n由于组 103 不存在，每秒将有两个成功的 put 操作和一个失败的操作。\n运行 \u003ccode\u003eAddGroup103Demo\u003c/code\u003e 在运行时添加 raft 组 103，之后 \u003ccode\u003ePeriodPutClient\u003c/code\u003e 将每秒输出三个成功的操作。\n执行 \u003ccode\u003eRemoveGroup103Demo\u003c/code\u003e 将删除 raft 组 103。\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e\u003ca href=\"/dtprj/dongting/blob/master/demos/src/main/java/com/github/dtprj/dongting/demos/advanced/watch\"\u003ewatch\u003c/a\u003e 目录包含使用客户端监控指定 key 变化的示例，同时演示如何监控目录，客户端将收到其子节点任何变化的通知。\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e\u003ca href=\"/dtprj/dongting/blob/master/demos/src/main/java/com/github/dtprj/dongting/demos/advanced/ttl\"\u003ettl\u003c/a\u003e 目录包含使用客户端设置带 TTL 的 key 的示例，TTL 过期后，key 将被自动删除。\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e\u003ca href=\"/dtprj/dongting/blob/master/demos/src/main/java/com/github/dtprj/dongting/demos/advanced/lock\"\u003elock\u003c/a\u003e 目录包含使用分布式锁的示例。分布式锁可以手动使用 tryLock/unlock 操作，也可以使用 tryLock/updateLease 完全自动化（可用于业务代码中的 leader 选举）。\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e使用 JLink 裁剪 JRE\u003c/h2\u003e\u003ca id=\"user-content-使用-jlink-裁剪-jre\" class=\"anchor\" aria-label=\"Permalink: 使用 JLink 裁剪 JRE\" href=\"#使用-jlink-裁剪-jre\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e使用 \u003ccode\u003ejlink\u003c/code\u003e 创建裁剪后的精简 JRE（约 30-45 MB）。参见 \u003ca href=\"/dtprj/dongting/blob/master/docs/jlink-cut_CN.md\"\u003ejlink-cut 指南\u003c/a\u003e。\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch1 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e关于我\u003c/h1\u003e\u003ca id=\"user-content-关于我\" class=\"anchor\" aria-label=\"Permalink: 关于我\" href=\"#关于我\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e\u003ca href=\"https://weibo.com/dtprj\" rel=\"nofollow\"\u003ehttps://weibo.com/dtprj\u003c/a\u003e\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e微信公众号：\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e\u003ca target=\"_blank\" rel=\"noopener noreferrer\" href=\"/dtprj/dongting/blob/master/docs/imgs/qrcode_wechat.jpg\"\u003e\u003cimg src=\"/dtprj/dongting/raw/master/docs/imgs/qrcode_wechat.jpg\" alt=\"公众号\" style=\"max-width: 100%;\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003c/article\u003e","richTextTruncated":false,"renderedFileInfo":null,"symbols":{"timed_out":false,"not_analyzed":false,"symbols":[{"name":"目录","fully_qualified_name":"目录","kind":"section_2","ident_start":394,"ident_end":400,"extent_start":391,"extent_end":1157,"ident_utf16":{"start":{"line_number":7,"utf16_col":3},"end":{"line_number":7,"utf16_col":5}},"extent_utf16":{"start":{"line_number":7,"utf16_col":0},"end":{"line_number":29,"utf16_col":0}}},{"name":"项目介绍","fully_qualified_name":"项目介绍","kind":"section_1","ident_start":1159,"ident_end":1171,"extent_start":1157,"extent_end":2179,"ident_utf16":{"start":{"line_number":29,"utf16_col":2},"end":{"line_number":29,"utf16_col":6}},"extent_utf16":{"start":{"line_number":29,"utf16_col":0},"end":{"line_number":43,"utf16_col":0}}},{"name":"10倍吞吐","fully_qualified_name":"10倍吞吐","kind":"section_1","ident_start":2181,"ident_end":2192,"extent_start":2179,"extent_end":2428,"ident_utf16":{"start":{"line_number":43,"utf16_col":2},"end":{"line_number":43,"utf16_col":7}},"extent_utf16":{"start":{"line_number":43,"utf16_col":0},"end":{"line_number":49,"utf16_col":0}}},{"name":"零依赖","fully_qualified_name":"零依赖","kind":"section_1","ident_start":2430,"ident_end":2439,"extent_start":2428,"extent_end":3457,"ident_utf16":{"start":{"line_number":49,"utf16_col":2},"end":{"line_number":49,"utf16_col":5}},"extent_utf16":{"start":{"line_number":49,"utf16_col":0},"end":{"line_number":66,"utf16_col":0}}},{"name":"TRY IT","fully_qualified_name":"TRY IT","kind":"section_1","ident_start":3459,"ident_end":3465,"extent_start":3457,"extent_end":13790,"ident_utf16":{"start":{"line_number":66,"utf16_col":2},"end":{"line_number":66,"utf16_col":8}},"extent_utf16":{"start":{"line_number":66,"utf16_col":0},"end":{"line_number":302,"utf16_col":0}}},{"name":"构建项目","fully_qualified_name":"构建项目","kind":"section_2","ident_start":3470,"ident_end":3482,"extent_start":3467,"extent_end":5268,"ident_utf16":{"start":{"line_number":68,"utf16_col":3},"end":{"line_number":68,"utf16_col":7}},"extent_utf16":{"start":{"line_number":68,"utf16_col":0},"end":{"line_number":113,"utf16_col":0}}},{"name":"启动服务器","fully_qualified_name":"启动服务器","kind":"section_2","ident_start":5271,"ident_end":5286,"extent_start":5268,"extent_end":5672,"ident_utf16":{"start":{"line_number":113,"utf16_col":3},"end":{"line_number":113,"utf16_col":8}},"extent_utf16":{"start":{"line_number":113,"utf16_col":0},"end":{"line_number":127,"utf16_col":0}}},{"name":"运行基准测试","fully_qualified_name":"运行基准测试","kind":"section_2","ident_start":5675,"ident_end":5693,"extent_start":5672,"extent_end":7814,"ident_utf16":{"start":{"line_number":127,"utf16_col":3},"end":{"line_number":127,"utf16_col":9}},"extent_utf16":{"start":{"line_number":127,"utf16_col":0},"end":{"line_number":183,"utf16_col":0}}},{"name":"客户端使用","fully_qualified_name":"客户端使用","kind":"section_2","ident_start":7817,"ident_end":7832,"extent_start":7814,"extent_end":10048,"ident_utf16":{"start":{"line_number":183,"utf16_col":3},"end":{"line_number":183,"utf16_col":8}},"extent_utf16":{"start":{"line_number":183,"utf16_col":0},"end":{"line_number":232,"utf16_col":0}}},{"name":"客户端完整例子","fully_qualified_name":"客户端完整例子","kind":"section_2","ident_start":10051,"ident_end":10072,"extent_start":10048,"extent_end":11148,"ident_utf16":{"start":{"line_number":232,"utf16_col":3},"end":{"line_number":232,"utf16_col":10}},"extent_utf16":{"start":{"line_number":232,"utf16_col":0},"end":{"line_number":244,"utf16_col":0}}},{"name":"server配置","fully_qualified_name":"server配置","kind":"section_2","ident_start":11151,"ident_end":11163,"extent_start":11148,"extent_end":13790,"ident_utf16":{"start":{"line_number":244,"utf16_col":3},"end":{"line_number":244,"utf16_col":11}},"extent_utf16":{"start":{"line_number":244,"utf16_col":0},"end":{"line_number":302,"utf16_col":0}}},{"name":"config.properties","fully_qualified_name":"config.properties","kind":"section_3","ident_start":11266,"ident_end":11283,"extent_start":11262,"extent_end":11956,"ident_utf16":{"start":{"line_number":248,"utf16_col":4},"end":{"line_number":248,"utf16_col":21}},"extent_utf16":{"start":{"line_number":248,"utf16_col":0},"end":{"line_number":259,"utf16_col":0}}},{"name":"Raft group公共配置（可选）","fully_qualified_name":"Raft group公共配置（可选）","kind":"section_3","ident_start":11960,"ident_end":11994,"extent_start":11956,"extent_end":12721,"ident_utf16":{"start":{"line_number":259,"utf16_col":4},"end":{"line_number":259,"utf16_col":22}},"extent_utf16":{"start":{"line_number":259,"utf16_col":0},"end":{"line_number":270,"utf16_col":0}}},{"name":"servers.properties","fully_qualified_name":"servers.properties","kind":"section_3","ident_start":12725,"ident_end":12743,"extent_start":12721,"extent_end":13659,"ident_utf16":{"start":{"line_number":270,"utf16_col":4},"end":{"line_number":270,"utf16_col":22}},"extent_utf16":{"start":{"line_number":270,"utf16_col":0},"end":{"line_number":298,"utf16_col":0}}},{"name":"client.properties","fully_qualified_name":"client.properties","kind":"section_3","ident_start":13663,"ident_end":13680,"extent_start":13659,"extent_end":13790,"ident_utf16":{"start":{"line_number":298,"utf16_col":4},"end":{"line_number":298,"utf16_col":21}},"extent_utf16":{"start":{"line_number":298,"utf16_col":0},"end":{"line_number":302,"utf16_col":0}}},{"name":"集群运维","fully_qualified_name":"集群运维","kind":"section_1","ident_start":13792,"ident_end":13804,"extent_start":13790,"extent_end":20927,"ident_utf16":{"start":{"line_number":302,"utf16_col":2},"end":{"line_number":302,"utf16_col":6}},"extent_utf16":{"start":{"line_number":302,"utf16_col":0},"end":{"line_number":438,"utf16_col":0}}},{"name":"配置一个多节点集群","fully_qualified_name":"配置一个多节点集群","kind":"section_2","ident_start":13809,"ident_end":13836,"extent_start":13806,"extent_end":15108,"ident_utf16":{"start":{"line_number":304,"utf16_col":3},"end":{"line_number":304,"utf16_col":12}},"extent_utf16":{"start":{"line_number":304,"utf16_col":0},"end":{"line_number":321,"utf16_col":0}}},{"name":"运行管理工具","fully_qualified_name":"运行管理工具","kind":"section_2","ident_start":15111,"ident_end":15129,"extent_start":15108,"extent_end":15494,"ident_utf16":{"start":{"line_number":321,"utf16_col":3},"end":{"line_number":321,"utf16_col":9}},"extent_utf16":{"start":{"line_number":321,"utf16_col":0},"end":{"line_number":335,"utf16_col":0}}},{"name":"集群变更","fully_qualified_name":"集群变更","kind":"section_2","ident_start":15497,"ident_end":15509,"extent_start":15494,"extent_end":20927,"ident_utf16":{"start":{"line_number":335,"utf16_col":3},"end":{"line_number":335,"utf16_col":7}},"extent_utf16":{"start":{"line_number":335,"utf16_col":0},"end":{"line_number":438,"utf16_col":0}}},{"name":"例1：从group中删除一个node","fully_qualified_name":"例1：从group中删除一个node","kind":"section_3","ident_start":18044,"ident_end":18078,"extent_start":18040,"extent_end":19161,"ident_utf16":{"start":{"line_number":359,"utf16_col":4},"end":{"line_number":359,"utf16_col":22}},"extent_utf16":{"start":{"line_number":359,"utf16_col":0},"end":{"line_number":390,"utf16_col":0}}},{"name":"例2：为group增加一个node","fully_qualified_name":"例2：为group增加一个node","kind":"section_3","ident_start":19165,"ident_end":19196,"extent_start":19161,"extent_end":20927,"ident_utf16":{"start":{"line_number":390,"utf16_col":4},"end":{"line_number":390,"utf16_col":21}},"extent_utf16":{"start":{"line_number":390,"utf16_col":0},"end":{"line_number":438,"utf16_col":0}}},{"name":"进阶","fully_qualified_name":"进阶","kind":"section_1","ident_start":20929,"ident_end":20935,"extent_start":20927,"extent_end":24190,"ident_utf16":{"start":{"line_number":438,"utf16_col":2},"end":{"line_number":438,"utf16_col":4}},"extent_utf16":{"start":{"line_number":438,"utf16_col":0},"end":{"line_number":484,"utf16_col":0}}},{"name":"将项目导入IDE","fully_qualified_name":"将项目导入IDE","kind":"section_2","ident_start":20940,"ident_end":20958,"extent_start":20937,"extent_end":21051,"ident_utf16":{"start":{"line_number":440,"utf16_col":3},"end":{"line_number":440,"utf16_col":11}},"extent_utf16":{"start":{"line_number":440,"utf16_col":0},"end":{"line_number":444,"utf16_col":0}}},{"name":"通过代码构建 raft 服务器","fully_qualified_name":"通过代码构建 raft 服务器","kind":"section_2","ident_start":21054,"ident_end":21087,"extent_start":21051,"extent_end":24046,"ident_utf16":{"start":{"line_number":444,"utf16_col":3},"end":{"line_number":444,"utf16_col":18}},"extent_utf16":{"start":{"line_number":444,"utf16_col":0},"end":{"line_number":480,"utf16_col":0}}},{"name":"使用 JLink 裁剪 JRE","fully_qualified_name":"使用 JLink 裁剪 JRE","kind":"section_2","ident_start":24049,"ident_end":24072,"extent_start":24046,"extent_end":24190,"ident_utf16":{"start":{"line_number":480,"utf16_col":3},"end":{"line_number":480,"utf16_col":18}},"extent_utf16":{"start":{"line_number":480,"utf16_col":0},"end":{"line_number":484,"utf16_col":0}}},{"name":"关于我","fully_qualified_name":"关于我","kind":"section_1","ident_start":24192,"ident_end":24201,"extent_start":24190,"extent_end":24290,"ident_utf16":{"start":{"line_number":484,"utf16_col":2},"end":{"line_number":484,"utf16_col":5}},"extent_utf16":{"start":{"line_number":484,"utf16_col":0},"end":{"line_number":491,"utf16_col":0}}}]}},"codeViewLayoutRoute":{"repo":{"id":516941115,"defaultBranch":"master","name":"dongting","ownerLogin":"dtprj","currentUserCanPush":false,"isFork":false,"isEmpty":false,"createdAt":"2022-07-23T02:48:54.000Z","ownerAvatar":"https://avatars.githubusercontent.com/u/109834165?v=4","public":true,"private":false,"isOrgOwned":true,"isArchived":false},"currentUser":null,"uploadToken":"tC1p-r6Cw1IR751NNdU_9FN8iFscbfjVK7eFyRPjI9kkUAXVFkUCQIgXuMHAcXPkwZ344w0J0mK574GW9Na9Vg","allShortcutsEnabled":false,"treeExpanded":true,"path":"README_CN.md","symbolsExpanded":false,"refInfo":{"name":"master","listCacheKey":"v0:1785483438.0","canEdit":false,"currentOid":"0d366e7090d1b703cedc3ea65452e36623c46ec6"},"helpUrl":"https://docs.github.com","githubDevUrl":null},"codeViewFileTreeLayoutRoute":{"fileTree":{"":{"items":[{"name":".agents","path":".agents","contentType":"directory"},{"name":".github","path":".github","contentType":"directory"},{"name":"benchmark","path":"benchmark","contentType":"directory"},{"name":"client-ex","path":"client-ex","contentType":"directory"},{"name":"client","path":"client","contentType":"directory"},{"name":"demos","path":"demos","contentType":"directory"},{"name":"devlogs","path":"devlogs","contentType":"directory"},{"name":"dist","path":"dist","contentType":"directory"},{"name":"docs","path":"docs","contentType":"directory"},{"name":"it-test","path":"it-test","contentType":"directory"},{"name":"report","path":"report","contentType":"directory"},{"name":"server","path":"server","contentType":"directory"},{"name":"test-support","path":"test-support","contentType":"directory"},{"name":".gitattributes","path":".gitattributes","contentType":"file"},{"name":".gitignore","path":".gitignore","contentType":"file"},{"name":"AGENTS.md","path":"AGENTS.md","contentType":"file"},{"name":"LICENSE","path":"LICENSE","contentType":"file"},{"name":"README.md","path":"README.md","contentType":"file"},{"name":"README_CN.md","path":"README_CN.md","contentType":"file"},{"name":"pom.xml","path":"pom.xml","contentType":"file"}],"totalCount":20}},"fileTreeProcessingTime":41.998148,"foldersToFetch":[]},"codeViewBlobLayoutRoute":{"codeLineWrapEnabled":false,"refInfo":{"name":"master","listCacheKey":"v0:1785483438.0","canEdit":false,"refType":"branch","currentOid":"0d366e7090d1b703cedc3ea65452e36623c46ec6","canEditOnDefaultBranch":false,"fileExistsOnDefault":true},"path":"README_CN.md","blob":{"copilotSWEAgentEnabled":false,"dependabotInfo":{"showConfigurationBanner":false,"configFilePath":null,"networkDependabotPath":"/dtprj/dongting/network/updates","dismissConfigurationNoticePath":"/settings/dismiss-notice/dependabot_configuration_notice","configurationNoticeDismissed":null},"displayName":"README_CN.md","displayUrl":"https://github.com/dtprj/dongting/blob/master/README_CN.md?raw=true","headerInfo":{"blobSize":"23.7 KB","deleteTooltip":"You must be signed in to make or propose changes","editTooltip":"You must be signed in to make or propose changes","ghDesktopPath":"https://desktop.github.com","isGitLfs":false,"onBranch":true,"shortPath":"d2be589","siteNavLoginPath":"/login?return_to=https%3A%2F%2Fgithub.com%2Fdtprj%2Fdongting%2Fblob%2Fmaster%2FREADME_CN.md","isCSV":false,"isRichtext":true,"lineInfo":{"truncatedLoc":"491","truncatedSloc":"366"},"mode":"file"},"image":false,"isCodeownersFile":null,"isPlain":false,"isValidLegacyIssueTemplate":false,"isIssueTemplate":false,"isDiscussionTemplate":false,"language":"Markdown","languageID":222,"large":false,"planSupportInfo":{"repoIsFork":null,"repoOwnedByCurrentUser":null,"requestFullPath":"/dtprj/dongting/blob/master/README_CN.md","showFreeOrgGatedFeatureMessage":null,"showPlanSupportBanner":null,"upgradeDataAttributes":null,"upgradePath":null},"publishBannersInfo":{"dismissActionNoticePath":"/settings/dismiss-notice/publish_action_from_dockerfile","releasePath":"/dtprj/dongting/releases/new?marketplace=true","showPublishActionBanner":false},"rawBlobUrl":"https://github.com/dtprj/dongting/raw/refs/heads/master/README_CN.md","renderImageOrRaw":false,"shortPath":null,"symbolsEnabled":true,"tabSize":4,"topBannersInfo":{"overridingGlobalFundingFile":false,"globalPreferredFundingPath":null,"showInvalidCitationWarning":false,"citationHelpUrl":"https://docs.github.com/github/creating-cloning-and-archiving-repositories/creating-a-repository-on-github/about-citation-files","actionsOnboardingTip":null},"truncated":false,"viewable":true,"workflowRedirectUrl":null},"copilotInfo":null,"copilotAccessAllowed":false,"copilotSpacesEnabled":false,"modelsAccessAllowed":false,"modelsRepoIntegrationEnabled":false,"isMarketplaceEnabled":true},"codeViewBlobLayoutRoute.StyledBlob":{"rawLines":["[![Java CI with Maven](https://github.com/dtprj/dongting/actions/workflows/maven.yml/badge.svg)](https://github.com/dtprj/dongting/actions/workflows/maven.yml)","[![codecov](https://codecov.io/github/dtprj/dongting/branch/master/graph/badge.svg)](https://app.codecov.io/github/dtprj/dongting)","","[English](README.md)","","\u003e **注意**：正在开发中的 0.9 版本可能会和 0.8.x 不兼容。","","## 目录","","- [项目介绍](#项目介绍)","- [10倍吞吐](#10倍吞吐)","- [零依赖](#零依赖)","- [TRY IT](#try-it)","  - [构建项目](#构建项目)","  - [启动服务器](#启动服务器)","  - [运行基准测试](#运行基准测试)","  - [客户端使用](#客户端使用)","  - [客户端完整例子](#客户端完整例子)","  - [server配置](#server配置)","- [集群运维](#集群运维)","  - [配置一个多节点集群](#配置一个多节点集群)","  - [运行管理工具](#运行管理工具)","  - [集群变更](#集群变更)","- [进阶](#进阶)","  - [将项目导入IDE](#将项目导入ide)","  - [通过代码构建 raft 服务器](#通过代码构建-raft-服务器)","  - [使用 JLink 裁剪 JRE](#使用-jlink-裁剪-jre)","- [关于我](#关于我)","","# 项目介绍","","Dongting是一个高性能的分布式引擎，集成了RAFT共识算法、配置服务器和消息队列功能。","","主要特性如下：","","* **Multi RAFT 组支持**：在同一进程内运行多个RAFT组，支持动态添加、删除和更新RAFT group（分片），实现集群的动态扩展。状态机可以在raft框架中自定义。","* **分布式配置服务器 DtKV**：树形结构，支持线性一致性的通用K/V操作、监听(watch)、TTL过期和分布式锁，类似于etcd","  * `DtKV`是内存型数据库，因此数据总量不能太大，但它使用raft log作为redo日志，定期做快照，掉电也不会丢失一条数据。","  * 原生支持树状目录，很多操作的复杂度是O(1)，比如在目录上进行watch，非常高效","  * 支持临时目录，在TTL过期后自动删除整个目录，该删除操作为原子操作","  * 不支持事务，但是提供了CAS和非常易用的分布式锁","* **（开发中）MQ**：使用RAFT日志作为消息队列日志。","","# 10倍吞吐","","Dongting使用面向性能编程（POP）的方式开发。","","在简单的同机测试中，现代高性能PC使用服务器默认配置即可轻松通过基准测试程序实现超过100万TPS，且响应时间(RT)也不会太大。","","# 零依赖","","你还在为一传十、十传百的依赖问题烦恼吗？你曾经因为1GB的镜像大小而被嘲笑吗？现在你有救了！","","Dongting没有依赖。它不依赖第三方jar文件（只有SLF4J是可选的，如果类路径中不存在，项目将使用JDK logging）。","","Dongting核心只有两个JAR包，客户端和服务器端加起来不到1MB，你可以轻松将它嵌入到你的程序中，不管是client还是server。","","Dongting对JDK的要求也不高：客户端仅需Java 8，服务器端仅需Java 11。","","Dongting不需要使用高性能硬件，如RDMA或Optane什么的，它也不依赖Amazon或其他云服务提供商提供的任何第三方服务，如存储服务。","它甚至可以在 HDD 硬盘和树莓派上良好运行。","","Dongting不需要调整Linux内核参数以达到最佳性能（生成环境你甚至可能没有权限这样做）。","","Dongting是完全模块化的（同时client仍兼容java8），服务端部署时，可以用jlink裁剪一个最小化的部署JRE。","","# TRY IT","","## 构建项目","","首先构建项目，构建产物位于target/dongting-dist目录：","```sh","mvn clean package -DskipUTs","```","","如果你想要安装maven artifact：","```sh","mvn clean install -Dmaven.test.skip=true","```","","构建后的目录结构如下：","```","dongting-dist/","├── bin/                     # 脚本目录","│   ├── benchmark.sh         # 基准测试脚本","│   ├── benchmark.bat        # Windows基准测试脚本","│   ├── start-dongting.sh    # 启动服务器脚本（Linux/Mac）","│   ├── start-dongting.bat   # 启动服务器脚本","│   ├── stop-dongting.sh     # 停止服务器脚本","│   ├── stop-dongting.bat    # 停止服务器脚本","│   ├── dongting-admin.sh    # 管理工具脚本（Linux/Mac）","│   └── dongting-admin.bat   # 管理工具脚本","├── lib/                     # JAR包目录","│   ├── dongting-client-x.y.z-SNAPSHOT.jar","│   ├── dongting-server-x.y.z-SNAPSHOT.jar","│   ├── dongting-dist-x.y.z-SNAPSHOT.jar # 引导jar，如果使用代码构建raft server，它不是必须的","│   ├── slf4j-api-x.y.z.jar","│   ├── logback-x.y.z.jar","│   └── logback-x.y.z.jar","├── conf/                     # 配置文件目录","│   ├── config.properties","│   ├── servers.properties","│   ├── client.properties","│   ├── logback-server.xml","│   ├── logback-admin.xml","│   └── logback-benchmark.xml","├── docs/                     # 文档目录","├── data/                     # 数据目录（运行时生成）","└── logs/                     # 日志目录","    ├── dongting-server.log   # 服务器日志（运行时生成）","    └── dongting-stats.log    # 统计日志（运行时生成）","```","","## 启动服务器","","在bin目录下，运行以下命令启动服务器：","```sh","./start-dongting.sh","```","服务器将启动并监听 9331 端口（用于server内部通信，如raft复制）和 9332 端口（服务端口）。","默认会启动一个`DtKV`实例，groupId为0。","","运行下面的命令可以（根据data目录下的pid文件找到进程）停止服务器：","```sh","./stop-dongting.sh","```","","## 运行基准测试","","运行以下命令启动基准测试客户端：","```sh","./benchmark.sh -g 0","```","","你可能需要调整参数以获得最大吞吐量，例如：","```sh","./benchmark.sh -g 0 --max-pending 10000 --client-count 2","```","","尝试使用Java 21虚拟线程进行多线程压测（需要 Java 21）：","```sh","./benchmark.sh -g 0 --sync --thread-count 4000","```","","看看我的测试结果吧（AMD 9700X 6C12T，致态 TiPro 9000 运行在 PCI-E 4.0 模式）：","```powershell","PS D:\\dongting-dist\\bin\u003e .\\benchmark.bat -g 0","Configuration:","  Config file: D:\\dongting-dist\\conf\\client.properties","  Servers: 1,127.0.0.1:9332","  Group ID: 0","","Benchmark config:","  Java 21, async put, 10000 keys, 256 bytes value, 2000 total maxPending","  1 clients, one thread per client","","Warming up for 3 seconds...","","[Warmup] TPS: 1,133,471, Success: 1,133,471, Fail: 0, Avg: 1,754 us, Max: 30,000 us","[Warmup] TPS: 1,355,100, Success: 1,355,100, Fail: 0, Avg: 1,483 us, Max: 13,000 us","[Warmup] TPS: 1,349,302, Success: 1,349,302, Fail: 0, Avg: 1,456 us, Max: 14,000 us","Warmup complete, starting benchmark...","","[Now] TPS: 1,389,229, Success: 1,389,229, Fail: 0, Avg: 1,440 us, Max: 13,000 us","[Now] TPS: 1,331,667, Success: 1,331,667, Fail: 0, Avg: 1,494 us, Max: 13,000 us","[Now] TPS: 1,324,262, Success: 1,324,262, Fail: 0, Avg: 1,504 us, Max: 12,000 us","[Now] TPS: 1,369,651, Success: 1,369,651, Fail: 0, Avg: 1,453 us, Max: 13,000 us","[Now] TPS: 1,380,549, Success: 1,380,549, Fail: 0, Avg: 1,461 us, Max: 13,000 us","[Now] TPS: 1,344,967, Success: 1,344,967, Fail: 0, Avg: 1,479 us, Max: 19,000 us","[Now] TPS: 1,327,548, Success: 1,327,548, Fail: 0, Avg: 1,499 us, Max: 24,000 us","[Now] TPS: 1,363,815, Success: 1,363,815, Fail: 0, Avg: 1,458 us, Max: 12,000 us","[Now] TPS: 1,359,154, Success: 1,359,154, Fail: 0, Avg: 1,472 us, Max: 13,000 us","[Now] TPS: 1,345,889, Success: 1,345,889, Fail: 0, Avg: 1,476 us, Max: 13,000 us","","Benchmark config:","  Java 21, async put, 10000 keys, 256 bytes value, 2000 total maxPending","  1 clients, one thread per client","","[Final] TPS: 1,353,905, Success: 13,539,049, Fail: 0, Avg: 1,473 us, Max: 24,000 us","","PS D:\\dongting-dist\\bin\u003e","```","","## 客户端使用","","Dongting内置的DtKV支持以下功能：","","* get/batchGet","* put/batchPut","* remove/batchRemove","* list","* mkdir","* compareAndSet","* putTemp/makeTempDir（带 TTL 的节点）","* createLock/createAutoRenewalLock","","`DtKV` 使用 `.` 作为 key 的分隔符。例如，您可以使用 `\"dir1.key1\"` 访问 `dir1` 目录下的 `key1`，value不能为空值或空串。","","要使用`DtKV`客户端，需要引入 `dongting-client.jar`（300+KB），无其它依赖。","","以下是初始化`DtKV`客户端的简单示例：","```java","// dongting 支持multi-raft，因此需要指定group id，默认group id是0","int groupId = 0;","KvClient kvClient = new KvClient();","kvClient.start();","// 在运行时添加node定义，每个node有一个唯一的正整数id 和一个 host:servicePort 地址","kvClient.getRaftClient().clientAddNode(\"1,127.0.0.1:9332\");","// kvClient.getRaftClient().clientAddNode(\"1,192.168.0.1:9332;2,192.168.0.2:9332;3,192.168.0.3:9332\");","// 在运行时添加group定义，这里添加一个 groupId 为 0 的组，包含 id 为 1、2、3 的 3 个节点","kvClient.getRaftClient().clientAddOrUpdateGroup(groupId, new int[]{1,2,3});","```","","请确保指定正确的端口。每个raft node开放两个端口：","一个是**复制端口**，默认9331，用于 raft 节点之间的内部通信，如复制。`AdminRaftClient` 也连接到此端口。","另一个是**服务端口**，默认9332，用于 `KvClient` 等客户端的连接。","","`KvClient` 提供同步和异步接口。对于单个 `KvClient`，异步操作可以获得最大吞吐量，而同步操作需要多个线程（或虚拟线程）才能达到较高吞吐量。","需要注意的是，异步操作的回调可能在 raft 线程或 IO 线程上执行。","因此，你不应在这些回调中执行任何阻塞或 CPU 密集型操作。 如果你不确定或缺乏高级技能，强烈建议使用同步接口。","","以下是使用 `KvClient` 的简单示例：","```java","// 同步put","kvClient.put(groupId, \"key1\".getBytes(), \"value1\".getBytes());","kvClient.put(groupId, \"key1\".getBytes(), \"value1\".getBytes(), (raftIndex, ex) -\u003e {","    // 执行一些既不阻塞也不消耗 CPU 的操作","});","```","","有关 `KvClient` 类的详细用法，请参阅 Javadocs。","","## 客户端完整例子","","- [SimpleDemo](demos/src/main/java/com/github/dtprj/dongting/demos/kvclient/SimpleDemo.java) - 基本 put/get/list 操作","- [BatchDemo](demos/src/main/java/com/github/dtprj/dongting/demos/kvclient/BatchDemo.java) - 批量读写删","- [CasDemo](demos/src/main/java/com/github/dtprj/dongting/demos/kvclient/CasDemo.java) - CAS原子操作","- [TempValueDemo](demos/src/main/java/com/github/dtprj/dongting/demos/kvclient/TempValueDemo.java) - 临时值(TTL)","- [TempDirDemo](demos/src/main/java/com/github/dtprj/dongting/demos/kvclient/TempDirDemo.java) - 临时目录(TTL)","- [UpdateTtlDemo](demos/src/main/java/com/github/dtprj/dongting/demos/kvclient/UpdateTtlDemo.java) - 更新临时节点的TTL","- [WatchDemo](demos/src/main/java/com/github/dtprj/dongting/demos/kvclient/WatchDemo.java) - key/目录变化监听","- [DistributedLockDemo](demos/src/main/java/com/github/dtprj/dongting/demos/kvclient/DistributedLockDemo.java) - 分布式锁","- [AutoRenewalLockDemo](demos/src/main/java/com/github/dtprj/dongting/demos/kvclient/AutoRenewalLockDemo.java) - 自动续约分布式锁","","## server配置","","服务器配置主要包含两个配置文件：`config.properties` 和 `servers.properties`。","","### config.properties","","此文件配置 Raft 服务器的基本参数：","","- **nodeId**：每个服务器必须有一个唯一的正整数 node ID，注意是从1开始，如果只有一个node通常就设置为1","- **replicatePort**：用于服务器内部通信的端口，例如 Raft 复制（默认值：9331）","- **servicePort**：用于客户端-服务器通信的端口（默认值：9332）","- **electTimeout**：Raft 选举超时时间，单位毫秒（默认值：15000）","- **heartbeatInterval**：Raft 心跳间隔时间，单位毫秒（默认值：2000）","- **blockIoThreads**：处理阻塞 IO 的线程数（如果没有设置，默认值运行时根据CPU核心数计算生成）","","### Raft group公共配置（可选）","","以下配置在 `config.properties` 中，影响 Raft group 的行为：","","- **dataDir**：数据目录（默认值指向dongting-dist目录下的data目录）","- **syncForce**：如果为 true，任何操作都会在响应 leader 和计算法定人数之前持久化（fsync）到磁盘。此选项对性能有显著影响（默认值：true）","- **saveSnapshotSeconds**：保存快照的间隔秒数（默认值：3600）","- **maxKeepSnapshots**：最多保留的快照数量（默认值：2）","- **saveSnapshotWhenClose**：关闭时（通常是服务器关闭时）是否保存快照（默认值：true）","- **deleteLogsAfterTakeSnapshot**：创建快照后是否删除不再需要的raft日志文件（默认值：true）","","### servers.properties","","此文件配置集群拓扑和 Raft group：","","- **servers**：node 列表，格式为 `nodeId,ip:replicatePort`。多个 node 用分号分隔。示例：","  ```properties","  # 单节点示例","  servers = 1,127.0.0.1:9331","","  # 多节点示例","  servers = 1,192.168.0.1:9331;2,192.168.0.2:9331;3,192.168.0.3:9331","","  # 本地多节点示例（使用不同端口隔离）","  servers = 1,127.0.0.1:4001;2,127.0.0.1:4002;3,127.0.0.1:4003","  ```","","- **Raft group member 配置**：格式为 `group.\u003cgroupId\u003e.nodeIdOfMembers = nodeId1,nodeId2,...`。node ID 必须在 `servers` 属性中定义。","  ```properties","  group.0.nodeIdOfMembers = 1,2,3","  ```","","- **Raft group observer 配置**：格式为 `group.\u003cgroupId\u003e.nodeIdOfObservers = nodeId1,nodeId2,...`。observer 不会参与 leader 选举。","  ```properties","  group.0.nodeIdOfObservers = 4","  ```","","observer会接收leader的数据复制，但是不会参与raft投票。","","### client.properties","","client.properties文件被benchmark使用，注意里面配置是的servers参数连接**服务端口**。","","# 集群运维","","## 配置一个多节点集群","","默认情况下，直接运行start-dongting脚本会启动一个单节点集群监听本机9331端口和9332端口。","如果需要启动多节点集群，以3节点为例，需要做以下工作：","","1. 准备3份dongting-dist目录，如果是自己做测试，在同一个机器上也可以，但要修改端口。","2. 修改每个config.properties文件中的nodeId。nodeId从1开始，不同node必须不一样。**注意服务器一旦启动，nodeId就不能再修改了（除非清空data目录系的数据）**。","3. （可选）同一个机器上做测试的话，要修改每个config.properties文件中的端口。","4. 修改servers.properties文件中的servers参数，3节点的话可能是`1,192.168.0.1:9331;2,192.168.0.2:9331;3,192.168.0.3:9331`或者`1,127.0.0.1:4001;2,127.0.0.1:4002;3,127.0.0.1:4003`，根据你的ip和端口配置而定","5. 修改servers.properties文件中的`group.0.nodeIdOfMembers`参数，3节点的话可能是`1,2,3`","","以上2、3每个node配置不一样，而4、5每个节点配置是一样的。","","如果要做benchmark，还要修改client.properties文件中的servers参数，连接**服务端口**（默认9332）。","","配置好以后就可以分别启动了，可以随意杀掉一个进程，看看集群的表现。","","## 运行管理工具","","bin目录下的 dongting-admin 脚本是一个服务器管理工具，可用于：","","* 更改raft group member/observer","* 转移leader","* 添加/删除组（multi-raft）","* 添加/删除集群节点","* 查询服务器状态","","运行时不带参数可查看使用方法。","","可以通过`AdminRaftClient`类执行所有的管理功能，记得连接到复制端口。","","## 集群变更","","Dongting实现了标准的raft joint consensus，变更分为两个阶段，先prepare，再commit（或abort）。","这3个操作都实现了幂等，如果因为网络原因失败了，可以放心重试。","在dongting中，prepare阶段完成以后，c-old保存在members/observers中，c-new保存在preparedMembers/preparedObservers中，","commit或abort后，preparedMembers/preparedObservers会被清空。","","集群变更的一个原因可能是发生了故障，新配置可能无法下发到故障node，造成集群不同node的配置不一致。接下来描述的概念会有助于你了解应该怎么办。","","在Dongting中，prepare/commit/abort操作也是一条raft日志，其中包含raft group的members/observers/preparedMembers/preparedObservers配置信息。","状态机保存snapshot时（默认关闭时会保存一次），也会将这些信息存到snapshot中，raft group启动时，会先恢复快照（如果有），","然后从检查点重做日志，使用其中最新的group配置信息，因此servers.properties中的nodeIdOfMembers/nodeIdOfObservers其实只是用来引导的，","如果有snapshot或者raft log中有prepare/commit信息，servers.properties中的nodeIdOfMembers/nodeIdOfObservers就被忽略了。尽管如此，","还是有必要维护正确的nodeIdOfMembers/nodeIdOfObservers。为了简化，servers.properties中没有preparedMembers/preparedObservers。","","为了避免错误，server会拒绝非members/preparedMembers发过来的投票和复制请求，如果一个node的配置数据太旧，导致它不认新的leader，","可以删除它的数据，将servers.properties的group信息维护正确，然后重新加入集群。","","注意，以上提到的group信息，都只定义了id，它必须在servers列表中定义，否则会出错，增加新node前，要在每个node上都更新定义。","dongting-server模块需要以一个配置来启动，并且可以在运行时修改，但它不负责维护配置的持久化，是dongting-dist（dongting-dist.jar）模块","负责servers.properties的读取和更新，使用dongting-admin脚本addNode/removeNode以后，它先更新内存，然后更新servers.properties，","这不是一个原子操作，你需要关注脚本的输出，确保配置已正确更新（dongting-admin脚本还提供了一个幂等的sync-config子命令用来手工同步）。","如果你没有使用dongting-dist的脚本，而是自己通过代码来组装嵌入式dongting-server，那这些配置的持久化就由你自己来保证。","","### 例1：从group中删除一个node","","假设你有一个3节点的集群，节点ID为1、2、3，group ID为0，现在需要删除节点3。操作步骤如下：","","1. 首先查询一下集群状态，随便找一个node，从返回的信息中找到leader：","   ```sh","   ./dongting-admin.sh query-status --node-id 1 --group-id 0","   ```","","2. 准备配置变更，将成员从1、2、3改为1、2（以下prepare/commit/abort应该在leader上调用）：","   ```sh","   ./dongting-admin.sh prepare-change --group-id 0 --old-members 1,2,3 --new-members 1,2","   ```","","   这会返回一个prepare-index，记录下来用于下一步。","","3. 提交配置变更：","   ```sh","   ./dongting-admin.sh commit-change --group-id 0 --prepare-index \u003c返回的prepare-index\u003e","   ```","","4. 集群配置已更新，节点3已经从group中移除。你可以通过以下命令验证：","   ```sh","   ./dongting-admin.sh query-status --node-id 1 --group-id 0","   ```","","5. 如果需要，可以删除节点3的定义（此操作应在所有节点1、2上都执行）：","   ```sh","   ./dongting-admin.sh remove-node --node-id 1 --remove-node-id 3","   ```","","### 例2：为group增加一个node","","假设你有一个3节点的集群，节点ID为1、2、3，group ID为0，现在需要添加节点4。操作步骤如下：","","1. 首先启动新节点4的服务器进程：","   ```sh","   cd node4/bin","   ./start-dongting.sh","   ```","","2. 添加节点4的定义（此操作应在所有现有节点上执行）：","   ```sh","   ./dongting-admin.sh add-node --node-id 1 --add-node-id 4 --host theHost --port thePort","   ```","   ```sh","   ./dongting-admin.sh add-node --node-id 2 --add-node-id 4 --host theHost --port thePort","   ```","   ```sh","   ./dongting-admin.sh add-node --node-id 3 --add-node-id 4 --host theHost --port thePort","   ```","","   注意：port应该是新节点的复制端口，不是服务端口。","","3. 确认节点4已添加：","   ```sh","   ./dongting-admin.sh list-nodes --node-id 1","   ```","","4. 准备配置变更，将成员从1、2、3改为1、2、3、4（prepare/commit/abort应该在leader上调用）：","   ```sh","   ./dongting-admin.sh prepare-change --group-id 0 --old-members 1,2,3 --new-members 1,2,3,4","   ```","","   这会返回一个prepare-index，记录下来用于下一步。","","5. 提交配置变更：","   ```sh","   ./dongting-admin.sh commit-change --group-id 0 --prepare-index \u003c返回的prepare-index\u003e","   ```","","6. 集群配置已更新，节点4已加入group。你可以通过以下命令验证：","   ```sh","   ./dongting-admin.sh query-status --node-id 1 --group-id 0","   ```","","注意：新节点加入后会从leader同步所有数据，这可能需要一些时间，取决于数据量。现在既然新加了一个节点，那这个集群的投票quorum就变成了3了，","所以，生成环境上，建议新节点先作为observer加入，等数据同步完成后，再做一次变更将observer提升为member。","","# 进阶","","## 将项目导入IDE","","将项目导入后，要设置 IDE，可以参考 [开发指南](docs/developer_CN.md)。","","## 通过代码构建 raft 服务器","","所有示例都在 `demos` 目录中。它们不需要配置，直接执行 `main` 方法即可运行。","建议在 IDE 中运行，以便更方便地设置断点和观察。","","[cluster](demos/src/main/java/com/github/dtprj/dongting/demos/advanced/cluster) 目录包含一个运行 3 节点 raft 集群的示例。","分别运行 `DemoServer1`、`DemoServer2` 和 `DemoServer3`，raft 集群通常会在一秒内就绪。","运行 `DemoClient`，它将发送 100 万次 put 和 get 请求，同时记录完成时间。","运行 `PeriodPutClient`，它每秒持续发送一个 put 请求，不会停止。","您可以随时关闭一个服务器，`PeriodPutClient` 将不受影响。","此外，执行 `ChangeLeader` 将 Raft leader 切换到指定节点。","正在运行的 `PeriodPutClient` 将完全不受影响。","","[standalone](demos/src/main/java/com/github/dtprj/dongting/demos/advanced/standalone) 目录包含一个运行单节点 raft 组的示例。","","[embedded](demos/src/main/java/com/github/dtprj/dongting/demos/advanced/embedded) 目录包含一个将 3 个服务器和 1 个客户端嵌入到单个进程中的示例。","","[configchange](demos/src/main/java/com/github/dtprj/dongting/demos/advanced/configchange) 目录包含在运行时动态更改 Raft 成员的示例。","首先，运行 `ConfigChangeDemoServer1`、`ConfigChangeDemoServer2`、`ConfigChangeDemoServer3` 和 `ConfigChangeDemoServer4`。","默认情况下，将启动包含节点 1、2、3 的 Raft 组。","执行 `ChangeTo234Client` 将 Raft 成员更改为节点 2、3、4。","执行 `ChangeTo123Client` 将 Raft 成员恢复为节点 1、2、3。","","[multiraft](demos/src/main/java/com/github/dtprj/dongting/demos/advanced/multiraft) 目录包含运行多 raft 的示例，通常用于分片或动态分片。","运行 `MultiRaftDemoServer1`、`MultiRaftDemoServer2` 和 `MultiRaftDemoServer3` 默认启动两个（静态）raft 组，ID 为 101 和 102。","执行 `PeriodPutClient` 将每秒向 raft 组 101、102 和 103 发送一个 put 请求。","由于组 103 不存在，每秒将有两个成功的 put 操作和一个失败的操作。","运行 `AddGroup103Demo` 在运行时添加 raft 组 103，之后 `PeriodPutClient` 将每秒输出三个成功的操作。","执行 `RemoveGroup103Demo` 将删除 raft 组 103。","","[watch](demos/src/main/java/com/github/dtprj/dongting/demos/advanced/watch) 目录包含使用客户端监控指定 key 变化的示例，同时演示如何监控目录，客户端将收到其子节点任何变化的通知。","","[ttl](demos/src/main/java/com/github/dtprj/dongting/demos/advanced/ttl) 目录包含使用客户端设置带 TTL 的 key 的示例，TTL 过期后，key 将被自动删除。","","[lock](demos/src/main/java/com/github/dtprj/dongting/demos/advanced/lock) 目录包含使用分布式锁的示例。分布式锁可以手动使用 tryLock/unlock 操作，也可以使用 tryLock/updateLease 完全自动化（可用于业务代码中的 leader 选举）。","","## 使用 JLink 裁剪 JRE","","使用 `jlink` 创建裁剪后的精简 JRE（约 30-45 MB）。参见 [jlink-cut 指南](docs/jlink-cut_CN.md)。","","# 关于我","","https://weibo.com/dtprj","","微信公众号：","","![公众号](docs/imgs/qrcode_wechat.jpg)"],"stylingDirectives":[[[0,1,"pl-s"],[1,3,"pl-s"],[21,22,"pl-s"],[22,23,"pl-s"],[23,94,"pl-corl"],[94,96,"pl-s"],[96,97,"pl-s"],[97,158,"pl-corl"],[158,159,"pl-s"]],[[0,1,"pl-s"],[1,3,"pl-s"],[10,11,"pl-s"],[11,12,"pl-s"],[12,82,"pl-corl"],[82,84,"pl-s"],[84,85,"pl-s"],[85,129,"pl-corl"],[129,130,"pl-s"]],[],[[0,1,"pl-s"],[8,9,"pl-s"],[9,10,"pl-s"],[10,19,"pl-corl"],[19,20,"pl-s"]],[],[[0,37,"pl-ent"],[0,2,"pl-ent"],[2,4,"pl-s"],[6,8,"pl-s"]],[[0,0,"pl-ent"]],[[0,5,"pl-mh"],[3,5,"pl-en"]],[],[[0,1,"pl-v"],[2,3,"pl-s"],[7,8,"pl-s"],[8,9,"pl-s"],[9,14,"pl-corl"],[14,15,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[8,9,"pl-s"],[9,10,"pl-s"],[10,16,"pl-corl"],[16,17,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[6,7,"pl-s"],[7,8,"pl-s"],[8,12,"pl-corl"],[12,13,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[9,10,"pl-s"],[10,11,"pl-s"],[11,18,"pl-corl"],[18,19,"pl-s"]],[[2,3,"pl-v"],[4,5,"pl-s"],[9,10,"pl-s"],[10,11,"pl-s"],[11,16,"pl-corl"],[16,17,"pl-s"]],[[2,3,"pl-v"],[4,5,"pl-s"],[10,11,"pl-s"],[11,12,"pl-s"],[12,18,"pl-corl"],[18,19,"pl-s"]],[[2,3,"pl-v"],[4,5,"pl-s"],[11,12,"pl-s"],[12,13,"pl-s"],[13,20,"pl-corl"],[20,21,"pl-s"]],[[2,3,"pl-v"],[4,5,"pl-s"],[10,11,"pl-s"],[11,12,"pl-s"],[12,18,"pl-corl"],[18,19,"pl-s"]],[[2,3,"pl-v"],[4,5,"pl-s"],[12,13,"pl-s"],[13,14,"pl-s"],[14,22,"pl-corl"],[22,23,"pl-s"]],[[2,3,"pl-v"],[4,5,"pl-s"],[13,14,"pl-s"],[14,15,"pl-s"],[15,24,"pl-corl"],[24,25,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[7,8,"pl-s"],[8,9,"pl-s"],[9,14,"pl-corl"],[14,15,"pl-s"]],[[2,3,"pl-v"],[4,5,"pl-s"],[14,15,"pl-s"],[15,16,"pl-s"],[16,26,"pl-corl"],[26,27,"pl-s"]],[[2,3,"pl-v"],[4,5,"pl-s"],[11,12,"pl-s"],[12,13,"pl-s"],[13,20,"pl-corl"],[20,21,"pl-s"]],[[2,3,"pl-v"],[4,5,"pl-s"],[9,10,"pl-s"],[10,11,"pl-s"],[11,16,"pl-corl"],[16,17,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[5,6,"pl-s"],[6,7,"pl-s"],[7,10,"pl-corl"],[10,11,"pl-s"]],[[2,3,"pl-v"],[4,5,"pl-s"],[13,14,"pl-s"],[14,15,"pl-s"],[15,24,"pl-corl"],[24,25,"pl-s"]],[[2,3,"pl-v"],[4,5,"pl-s"],[20,21,"pl-s"],[21,22,"pl-s"],[22,38,"pl-corl"],[38,39,"pl-s"]],[[2,3,"pl-v"],[4,5,"pl-s"],[20,21,"pl-s"],[21,22,"pl-s"],[22,38,"pl-corl"],[38,39,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[6,7,"pl-s"],[7,8,"pl-s"],[8,12,"pl-corl"],[12,13,"pl-s"]],[],[[0,6,"pl-mh"],[2,6,"pl-en"]],[],[],[],[],[],[[0,1,"pl-v"],[2,4,"pl-s"],[18,20,"pl-s"]],[[0,1,"pl-v"],[2,4,"pl-s"],[17,19,"pl-s"]],[[2,3,"pl-v"],[4,5,"pl-s"],[5,9,"pl-c1"],[9,10,"pl-s"]],[[2,3,"pl-v"]],[[2,3,"pl-v"]],[[2,3,"pl-v"]],[[0,1,"pl-v"],[2,4,"pl-s"],[11,13,"pl-s"]],[],[[0,7,"pl-mh"],[2,7,"pl-en"]],[],[],[],[],[],[[0,5,"pl-mh"],[2,5,"pl-en"]],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[[0,8,"pl-mh"],[2,8,"pl-en"]],[],[[0,7,"pl-mh"],[3,7,"pl-en"]],[],[],[[0,3,"pl-s"],[3,5,"pl-en"]],[],[[0,3,"pl-s"]],[],[],[[0,3,"pl-s"],[3,5,"pl-en"]],[],[[0,3,"pl-s"]],[],[],[[0,3,"pl-s"],[3,3,"pl-c1"]],[[0,14,"pl-c1"]],[[0,35,"pl-c1"]],[[0,37,"pl-c1"]],[[0,44,"pl-c1"]],[[0,49,"pl-c1"]],[[0,38,"pl-c1"]],[[0,38,"pl-c1"]],[[0,38,"pl-c1"]],[[0,48,"pl-c1"]],[[0,37,"pl-c1"]],[[0,37,"pl-c1"]],[[0,42,"pl-c1"]],[[0,42,"pl-c1"]],[[0,75,"pl-c1"]],[[0,27,"pl-c1"]],[[0,25,"pl-c1"]],[[0,25,"pl-c1"]],[[0,38,"pl-c1"]],[[0,25,"pl-c1"]],[[0,26,"pl-c1"]],[[0,25,"pl-c1"]],[[0,26,"pl-c1"]],[[0,25,"pl-c1"]],[[0,29,"pl-c1"]],[[0,36,"pl-c1"]],[[0,43,"pl-c1"]],[[0,36,"pl-c1"]],[[0,44,"pl-c1"]],[[0,43,"pl-c1"]],[[0,0,"pl-c1"],[0,3,"pl-s"]],[],[[0,8,"pl-mh"],[3,8,"pl-en"]],[],[],[[0,3,"pl-s"],[3,5,"pl-en"]],[],[[0,3,"pl-s"]],[],[[7,8,"pl-s"],[8,12,"pl-c1"],[12,13,"pl-s"]],[],[],[[0,3,"pl-s"],[3,5,"pl-en"]],[],[[0,3,"pl-s"]],[],[[0,9,"pl-mh"],[3,9,"pl-en"]],[],[],[[0,3,"pl-s"],[3,5,"pl-en"]],[],[[0,3,"pl-s"]],[],[],[[0,3,"pl-s"],[3,5,"pl-en"]],[],[[0,3,"pl-s"]],[],[],[[0,3,"pl-s"],[3,5,"pl-en"]],[],[[0,3,"pl-s"]],[],[],[[0,3,"pl-s"],[3,13,"pl-en"],[13,13,"pl-c1"]],[[0,45,"pl-c1"]],[[0,14,"pl-c1"]],[[0,54,"pl-c1"]],[[0,27,"pl-c1"]],[[0,13,"pl-c1"]],[[0,0,"pl-c1"]],[[0,17,"pl-c1"]],[[0,72,"pl-c1"]],[[0,34,"pl-c1"]],[[0,0,"pl-c1"]],[[0,27,"pl-c1"]],[[0,0,"pl-c1"]],[[0,83,"pl-c1"]],[[0,83,"pl-c1"]],[[0,83,"pl-c1"]],[[0,38,"pl-c1"]],[[0,0,"pl-c1"]],[[0,80,"pl-c1"]],[[0,80,"pl-c1"]],[[0,80,"pl-c1"]],[[0,80,"pl-c1"]],[[0,80,"pl-c1"]],[[0,80,"pl-c1"]],[[0,80,"pl-c1"]],[[0,80,"pl-c1"]],[[0,80,"pl-c1"]],[[0,80,"pl-c1"]],[[0,0,"pl-c1"]],[[0,17,"pl-c1"]],[[0,72,"pl-c1"]],[[0,34,"pl-c1"]],[[0,0,"pl-c1"]],[[0,83,"pl-c1"]],[[0,0,"pl-c1"]],[[0,24,"pl-c1"]],[[0,0,"pl-c1"],[0,3,"pl-s"]],[],[[0,8,"pl-mh"],[3,8,"pl-en"]],[],[],[],[[0,1,"pl-v"]],[[0,1,"pl-v"]],[[0,1,"pl-v"]],[[0,1,"pl-v"]],[[0,1,"pl-v"]],[[0,1,"pl-v"]],[[0,1,"pl-v"]],[[0,1,"pl-v"]],[],[[0,1,"pl-s"],[1,5,"pl-c1"],[5,6,"pl-s"],[10,11,"pl-s"],[11,12,"pl-c1"],[12,13,"pl-s"],[35,36,"pl-s"],[36,47,"pl-c1"],[47,48,"pl-s"],[52,53,"pl-s"],[53,57,"pl-c1"],[57,58,"pl-s"],[64,65,"pl-s"],[65,69,"pl-c1"],[69,70,"pl-s"]],[],[[3,4,"pl-s"],[4,8,"pl-c1"],[8,9,"pl-s"],[18,19,"pl-s"],[19,38,"pl-c1"],[38,39,"pl-s"]],[],[[6,7,"pl-s"],[7,11,"pl-c1"],[11,12,"pl-s"]],[[0,3,"pl-s"],[3,7,"pl-en"]],[[0,52,"pl-c"],[0,2,"pl-c"]],[[0,3,"pl-k"],[12,13,"pl-k"],[14,15,"pl-c1"]],[[0,8,"pl-smi"],[18,19,"pl-k"],[20,23,"pl-k"],[24,32,"pl-smi"]],[[8,9,"pl-k"]],[[0,57,"pl-c"],[0,2,"pl-c"]],[[8,9,"pl-k"],[24,25,"pl-k"],[39,57,"pl-s"],[39,40,"pl-pds"],[56,57,"pl-pds"]],[[0,102,"pl-c"],[0,2,"pl-c"]],[[0,60,"pl-c"],[0,2,"pl-c"]],[[8,9,"pl-k"],[24,25,"pl-k"],[57,60,"pl-k"],[61,64,"pl-smi"],[67,68,"pl-c1"],[69,70,"pl-c1"],[71,72,"pl-c1"]],[[0,3,"pl-s"]],[],[],[[3,5,"pl-s"],[9,11,"pl-s"],[41,42,"pl-s"],[42,57,"pl-c1"],[57,58,"pl-s"]],[[4,6,"pl-s"],[10,12,"pl-s"],[23,24,"pl-s"],[24,32,"pl-c1"],[32,33,"pl-s"]],[],[[0,1,"pl-s"],[1,9,"pl-c1"],[9,10,"pl-s"],[26,27,"pl-s"],[27,35,"pl-c1"],[35,36,"pl-s"]],[],[],[],[[6,7,"pl-s"],[7,15,"pl-c1"],[15,16,"pl-s"]],[[0,3,"pl-s"],[3,7,"pl-en"]],[[0,8,"pl-c"],[0,2,"pl-c"]],[[8,9,"pl-k"],[22,28,"pl-s"],[22,23,"pl-pds"],[27,28,"pl-pds"],[28,29,"pl-k"],[41,49,"pl-s"],[41,42,"pl-pds"],[48,49,"pl-pds"],[49,50,"pl-k"]],[[8,9,"pl-k"],[22,28,"pl-s"],[22,23,"pl-pds"],[27,28,"pl-pds"],[28,29,"pl-k"],[41,49,"pl-s"],[41,42,"pl-pds"],[48,49,"pl-pds"],[49,50,"pl-k"],[78,79,"pl-k"],[79,80,"pl-k"]],[[4,27,"pl-c"],[4,6,"pl-c"]],[],[[0,3,"pl-s"]],[],[[3,4,"pl-s"],[4,12,"pl-c1"],[12,13,"pl-s"]],[],[[0,10,"pl-mh"],[3,10,"pl-en"]],[],[[0,1,"pl-v"],[2,3,"pl-s"],[13,14,"pl-s"],[14,15,"pl-s"],[15,91,"pl-corl"],[91,92,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[12,13,"pl-s"],[13,14,"pl-s"],[14,89,"pl-corl"],[89,90,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[10,11,"pl-s"],[11,12,"pl-s"],[12,85,"pl-corl"],[85,86,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[16,17,"pl-s"],[17,18,"pl-s"],[18,97,"pl-corl"],[97,98,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[14,15,"pl-s"],[15,16,"pl-s"],[16,93,"pl-corl"],[93,94,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[16,17,"pl-s"],[17,18,"pl-s"],[18,97,"pl-corl"],[97,98,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[12,13,"pl-s"],[13,14,"pl-s"],[14,89,"pl-corl"],[89,90,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[22,23,"pl-s"],[23,24,"pl-s"],[24,109,"pl-corl"],[109,110,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[22,23,"pl-s"],[23,24,"pl-s"],[24,109,"pl-corl"],[109,110,"pl-s"]],[],[[0,11,"pl-mh"],[3,11,"pl-en"]],[],[[16,17,"pl-s"],[17,34,"pl-c1"],[34,35,"pl-s"],[38,39,"pl-s"],[39,57,"pl-c1"],[57,58,"pl-s"]],[],[[0,21,"pl-mh"],[4,21,"pl-en"]],[],[],[],[[0,1,"pl-v"],[2,4,"pl-s"],[10,12,"pl-s"]],[[0,1,"pl-v"],[2,4,"pl-s"],[17,19,"pl-s"]],[[0,1,"pl-v"],[2,4,"pl-s"],[15,17,"pl-s"]],[[0,1,"pl-v"],[2,4,"pl-s"],[16,18,"pl-s"]],[[0,1,"pl-v"],[2,4,"pl-s"],[21,23,"pl-s"]],[[0,1,"pl-v"],[2,4,"pl-s"],[18,20,"pl-s"]],[],[[0,22,"pl-mh"],[4,22,"pl-en"]],[],[[6,7,"pl-s"],[7,24,"pl-c1"],[24,25,"pl-s"]],[],[[0,1,"pl-v"],[2,4,"pl-s"],[11,13,"pl-s"]],[[0,1,"pl-v"],[2,4,"pl-s"],[13,15,"pl-s"]],[[0,1,"pl-v"],[2,4,"pl-s"],[23,25,"pl-s"]],[[0,1,"pl-v"],[2,4,"pl-s"],[20,22,"pl-s"]],[[0,1,"pl-v"],[2,4,"pl-s"],[25,27,"pl-s"]],[[0,1,"pl-v"],[2,4,"pl-s"],[31,33,"pl-s"]],[],[[0,22,"pl-mh"],[4,22,"pl-en"]],[],[],[],[[0,1,"pl-v"],[2,4,"pl-s"],[11,13,"pl-s"],[26,27,"pl-s"],[27,50,"pl-c1"],[50,51,"pl-s"]],[[2,5,"pl-s"],[5,15,"pl-en"]],[[2,9,"pl-c"],[2,3,"pl-c"]],[[2,9,"pl-k"]],[],[[2,9,"pl-c"],[2,3,"pl-c"]],[[2,9,"pl-k"],[30,68,"pl-c"],[30,31,"pl-c"]],[],[[2,21,"pl-c"],[2,3,"pl-c"]],[[2,9,"pl-k"],[28,62,"pl-c"],[28,29,"pl-c"]],[[2,5,"pl-s"]],[],[[0,1,"pl-v"],[2,4,"pl-s"],[24,26,"pl-s"],[31,32,"pl-s"],[32,85,"pl-c1"],[85,86,"pl-s"],[99,100,"pl-s"],[100,107,"pl-c1"],[107,108,"pl-s"]],[[2,5,"pl-s"],[5,15,"pl-en"]],[[2,25,"pl-k"]],[[2,5,"pl-s"]],[],[[0,1,"pl-v"],[2,4,"pl-s"],[26,28,"pl-s"],[33,34,"pl-s"],[34,89,"pl-c1"],[89,90,"pl-s"]],[[2,5,"pl-s"],[5,15,"pl-en"]],[[2,27,"pl-k"]],[[2,5,"pl-s"]],[],[],[],[[0,21,"pl-mh"],[4,21,"pl-en"]],[],[[51,53,"pl-s"],[57,59,"pl-s"]],[],[[0,6,"pl-mh"],[2,6,"pl-en"]],[],[[0,12,"pl-mh"],[3,12,"pl-en"]],[],[],[],[],[[0,1,"pl-s"],[1,2,"pl-v"]],[[0,1,"pl-s"],[1,2,"pl-v"],[58,60,"pl-s"],[99,101,"pl-s"]],[[0,1,"pl-s"],[1,2,"pl-v"]],[[0,1,"pl-s"],[1,2,"pl-v"],[45,46,"pl-s"],[46,102,"pl-c1"],[102,103,"pl-s"],[105,106,"pl-s"],[106,156,"pl-c1"],[156,157,"pl-s"]],[[0,1,"pl-s"],[1,2,"pl-v"],[27,28,"pl-s"],[28,51,"pl-c1"],[51,52,"pl-s"],[63,64,"pl-s"],[64,69,"pl-c1"],[69,70,"pl-s"]],[],[],[],[[51,53,"pl-s"],[57,59,"pl-s"]],[],[],[],[[0,9,"pl-mh"],[3,9,"pl-en"]],[],[],[],[[0,1,"pl-v"]],[[0,1,"pl-v"]],[[0,1,"pl-v"]],[[0,1,"pl-v"]],[[0,1,"pl-v"]],[],[],[],[[4,5,"pl-s"],[5,20,"pl-c1"],[20,21,"pl-s"]],[],[[0,7,"pl-mh"],[3,7,"pl-en"]],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[[0,22,"pl-mh"],[4,22,"pl-en"]],[],[],[],[[0,1,"pl-s"],[1,2,"pl-v"]],[[3,6,"pl-s"],[6,8,"pl-en"]],[],[[3,6,"pl-s"]],[],[[0,1,"pl-s"],[1,2,"pl-v"]],[[3,6,"pl-s"],[6,8,"pl-en"]],[],[[3,6,"pl-s"]],[],[],[],[[0,1,"pl-s"],[1,2,"pl-v"]],[[3,6,"pl-s"],[6,8,"pl-en"]],[[66,67,"pl-k"],[83,84,"pl-k"]],[[3,6,"pl-s"]],[],[[0,1,"pl-s"],[1,2,"pl-v"]],[[3,6,"pl-s"],[6,8,"pl-en"]],[],[[3,6,"pl-s"]],[],[[0,1,"pl-s"],[1,2,"pl-v"]],[[3,6,"pl-s"],[6,8,"pl-en"]],[],[[3,6,"pl-s"]],[],[[0,21,"pl-mh"],[4,21,"pl-en"]],[],[],[],[[0,1,"pl-s"],[1,2,"pl-v"]],[[3,6,"pl-s"],[6,8,"pl-en"]],[[3,5,"pl-c1"]],[],[[3,6,"pl-s"]],[],[[0,1,"pl-s"],[1,2,"pl-v"]],[[3,6,"pl-s"],[6,8,"pl-en"]],[],[[3,6,"pl-s"]],[[3,6,"pl-s"],[6,8,"pl-en"]],[],[[3,6,"pl-s"]],[[3,6,"pl-s"],[6,8,"pl-en"]],[],[[3,6,"pl-s"]],[],[],[],[[0,1,"pl-s"],[1,2,"pl-v"]],[[3,6,"pl-s"],[6,8,"pl-en"]],[],[[3,6,"pl-s"]],[],[[0,1,"pl-s"],[1,2,"pl-v"]],[[3,6,"pl-s"],[6,8,"pl-en"]],[],[[3,6,"pl-s"]],[],[],[],[[0,1,"pl-s"],[1,2,"pl-v"]],[[3,6,"pl-s"],[6,8,"pl-en"]],[[66,67,"pl-k"],[83,84,"pl-k"]],[[3,6,"pl-s"]],[],[[0,1,"pl-s"],[1,2,"pl-v"]],[[3,6,"pl-s"],[6,8,"pl-en"]],[],[[3,6,"pl-s"]],[],[],[],[],[[0,4,"pl-mh"],[2,4,"pl-en"]],[],[[0,11,"pl-mh"],[3,11,"pl-en"]],[],[[20,21,"pl-s"],[25,26,"pl-s"],[26,27,"pl-s"],[27,47,"pl-corl"],[47,48,"pl-s"]],[],[[0,18,"pl-mh"],[3,18,"pl-en"]],[],[[7,8,"pl-s"],[8,13,"pl-c1"],[13,14,"pl-s"],[32,33,"pl-s"],[33,37,"pl-c1"],[37,38,"pl-s"]],[],[],[[0,1,"pl-s"],[8,9,"pl-s"],[9,10,"pl-s"],[10,78,"pl-corl"],[78,79,"pl-s"]],[[5,6,"pl-s"],[6,17,"pl-c1"],[17,18,"pl-s"],[19,20,"pl-s"],[20,31,"pl-c1"],[31,32,"pl-s"],[35,36,"pl-s"],[36,47,"pl-c1"],[47,48,"pl-s"]],[[3,4,"pl-s"],[4,14,"pl-c1"],[14,15,"pl-s"]],[[3,4,"pl-s"],[4,19,"pl-c1"],[19,20,"pl-s"]],[[13,14,"pl-s"],[14,29,"pl-c1"],[29,30,"pl-s"]],[[6,7,"pl-s"],[7,19,"pl-c1"],[19,20,"pl-s"]],[[6,7,"pl-s"],[7,22,"pl-c1"],[22,23,"pl-s"]],[],[[0,1,"pl-s"],[11,12,"pl-s"],[12,13,"pl-s"],[13,84,"pl-corl"],[84,85,"pl-s"]],[],[[0,1,"pl-s"],[9,10,"pl-s"],[10,11,"pl-s"],[11,80,"pl-corl"],[80,81,"pl-s"]],[],[[0,1,"pl-s"],[13,14,"pl-s"],[14,15,"pl-s"],[15,88,"pl-corl"],[88,89,"pl-s"]],[[6,7,"pl-s"],[7,30,"pl-c1"],[30,31,"pl-s"],[32,33,"pl-s"],[33,56,"pl-c1"],[56,57,"pl-s"],[58,59,"pl-s"],[59,82,"pl-c1"],[82,83,"pl-s"],[86,87,"pl-s"],[87,110,"pl-c1"],[110,111,"pl-s"]],[],[[3,4,"pl-s"],[4,21,"pl-c1"],[21,22,"pl-s"]],[[3,4,"pl-s"],[4,21,"pl-c1"],[21,22,"pl-s"]],[],[[0,1,"pl-s"],[10,11,"pl-s"],[11,12,"pl-s"],[12,82,"pl-corl"],[82,83,"pl-s"]],[[3,4,"pl-s"],[4,24,"pl-c1"],[24,25,"pl-s"],[26,27,"pl-s"],[27,47,"pl-c1"],[47,48,"pl-s"],[51,52,"pl-s"],[52,72,"pl-c1"],[72,73,"pl-s"]],[[3,4,"pl-s"],[4,19,"pl-c1"],[19,20,"pl-s"]],[],[[3,4,"pl-s"],[4,19,"pl-c1"],[19,20,"pl-s"],[42,43,"pl-s"],[43,58,"pl-c1"],[58,59,"pl-s"]],[[3,4,"pl-s"],[4,22,"pl-c1"],[22,23,"pl-s"]],[],[[0,1,"pl-s"],[6,7,"pl-s"],[7,8,"pl-s"],[8,74,"pl-corl"],[74,75,"pl-s"]],[],[[0,1,"pl-s"],[4,5,"pl-s"],[5,6,"pl-s"],[6,70,"pl-corl"],[70,71,"pl-s"]],[],[[0,1,"pl-s"],[5,6,"pl-s"],[6,7,"pl-s"],[7,72,"pl-corl"],[72,73,"pl-s"]],[],[[0,18,"pl-mh"],[3,18,"pl-en"]],[],[[3,4,"pl-s"],[4,9,"pl-c1"],[9,10,"pl-s"],[39,40,"pl-s"],[52,53,"pl-s"],[53,54,"pl-s"],[54,74,"pl-corl"],[74,75,"pl-s"]],[],[[0,5,"pl-mh"],[2,5,"pl-en"]],[],[[0,23,"pl-corl"]],[],[],[],[[0,2,"pl-s"],[5,6,"pl-s"],[6,7,"pl-s"],[7,34,"pl-corl"],[34,35,"pl-s"]]],"colorizedLines":null}},"title":"dongting/README_CN.md at master · dtprj/dongting","appPayload":{},"meta":{"title":"dongting/README_CN.md at master · dtprj/dongting"}}</script>
  <div data-target="react-app.reactRoot"><meta name="github-code-view-meta-stats" id="github-code-view-meta-stats" data-hydrostats="publish"/> <!-- --> <a hidden="" id="code-view-repo-link" href="/dtprj/dongting" data-discover="true"></a> <div class="d-none"></div><div><div style="--spacing:var(--spacing-none)" class="prc-PageLayout-PageLayoutRoot--KH-d" data-component="SplitPageLayout" data-has-sidebar="true"><div class="prc-PageLayout-SidebarWrapper-kLG4B CopilotSidePanelSidebar-module__SidePanel__L3O0C CopilotSidePanelSidebar-module__HiddenSidePanel__TBRGn" style="--spacing-column:var(--spacing-none)" data-is-hidden="false" data-position="end" data-sticky="true" data-responsive-variant="fullscreen"><div class="prc-PageLayout-VerticalDivider-9QRmK prc-PageLayout-SidebarVerticalDivider-0Rl0V" data-component="PageLayout.VerticalDivider" data-variant="line" data-position="end" style="--spacing:var(--spacing-none)"><div class="prc-PageLayout-DraggableHandle-9s6B4" data-component="PageLayout.DragHandle" role="slider" aria-label="Draggable pane splitter" aria-valuemin="450" aria-valuemax="768" aria-valuenow="544" aria-valuetext="Pane width 544 pixels" tabindex="0"></div></div><div class="prc-PageLayout-Sidebar-iciWg" data-component="SplitPageLayout.Sidebar" data-resizable="true" style="--spacing:var(--spacing-normal);--pane-min-width:450px;--pane-max-width:768px;--pane-width-custom:544px;--pane-width-size:var(--pane-width-custom);--pane-width:544px"><div class="height-full" data-testid="copilot-code-view-side-panel"><div id="copilot-side-panel-content" class="height-full"></div></div></div></div><div class="prc-PageLayout-PageLayoutWrapper-2BhU2" data-width="full"><div class="prc-PageLayout-PageLayoutContent-BneH9"><div id="repos-file-tree-sidebar" class="CodeViewFileTreeLayout-module__sidebar__n_Aau" tabindex="0"><div class="prc-PageLayout-PaneWrapper-pHPop ReposFileTreePane-module__Pane__rBZpI ReposFileTreePane-module__HideTree__AYZnm ReposFileTreePane-module__HidePane__VHAVt" style="--offset-header:0px;--spacing-row:var(--spacing-none);--spacing-column:var(--spacing-none)" data-is-hidden="false" data-position="start" data-sticky="true"><div class="prc-PageLayout-HorizontalDivider-JLVqp prc-PageLayout-PaneHorizontalDivider-9tbnE" data-component="PageLayout.HorizontalDivider" data-variant-regular="none" data-variant-narrow="none" data-position="start" style="--spacing-divider:var(--spacing-none);--spacing:var(--spacing-none)"></div><div class="prc-PageLayout-Pane-AyzHK" data-component="SplitPageLayout.Pane" data-resizable="true" style="--spacing:var(--spacing-none);--pane-min-width:256px;--pane-max-width:calc(100vw - var(--pane-max-width-diff));--pane-width-size:var(--pane-width-large);--pane-width:320px"></div><div class="prc-PageLayout-VerticalDivider-9QRmK prc-PageLayout-PaneVerticalDivider-le57g" data-component="PageLayout.VerticalDivider" data-variant-narrow="none" data-variant-regular="line" data-variant-wide="line" data-position="start" style="--spacing:var(--spacing-none)"><div class="prc-PageLayout-DraggableHandle-9s6B4" data-component="PageLayout.DragHandle" role="slider" aria-label="Draggable pane splitter" aria-valuemin="256" aria-valuemax="600" aria-valuenow="320" aria-valuetext="Pane width 320 pixels" tabindex="0"></div></div></div></div><div data-component="SplitPageLayout.Content" class="prc-PageLayout-ContentWrapper-gR9eG"><div class="prc-PageLayout-Content-xWL-A" data-width="full" style="--spacing:var(--spacing-none)"><div class="SharedPageLayout-module__content__IwGAp" data-selector="repos-split-pane-content" id="repos-split-pane-content" tabindex="0"> <!-- --> <div class="container CodeViewHeader-module__Box__JkPOb"><div class="CodeViewHeader-module__StickyHeader__Qn7UN" id="StickyHeader"><div class="CodeViewHeader-module__Box_1__SbNDV"><div class="CodeViewHeader-module__Box_2__TB46f"><div class="react-code-view-header-wrap--narrow CodeViewHeader-module__Box_3__q1zUL"><div class="CodeViewHeader-module__treeToggleWrapper__RQ__9"><h2 class="use-tree-pane-module__Heading__s4QbZ prc-Heading-Heading-MtWFE" data-component="Heading"><button data-component="Button" type="button" aria-label="Expand file tree" data-testid="expand-file-tree-button-mobile" class="prc-Button-ButtonBase-9n-Xk ExpandFileTreeButton-module__Button_1__Svs95" data-loading="false" data-size="medium" data-variant="invisible"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-Iohp5"><span data-component="leadingVisual" class="prc-Button-Visual-YNt2F prc-Button-LeadingVisual-UySKu prc-Button-VisualWrap-E4cnq"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-arrow-left" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M7.78 12.53a.75.75 0 0 1-1.06 0L2.47 8.28a.75.75 0 0 1 0-1.06l4.25-4.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042L4.81 7h7.44a.75.75 0 0 1 0 1.5H4.81l2.97 2.97a.75.75 0 0 1 0 1.06Z"></path></svg></span><span data-component="text" class="prc-Button-Label-FWkx3">Files</span></span></button><button data-component="IconButton" type="button" data-testid="expand-file-tree-button" aria-controls="repos-file-tree" class="prc-Button-ButtonBase-9n-Xk position-relative ExpandFileTreeButton-module__expandButton__hDOcv ExpandFileTreeButton-module__filesButtonBreakpoint__zEvz3 fgColor-muted prc-Button-IconButton-fyge7" data-loading="false" data-no-visuals="true" data-size="medium" data-variant="invisible" aria-labelledby="_R_4lla9lik5_"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-sidebar-collapse" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M6.823 7.823a.25.25 0 0 1 0 .354l-2.396 2.396A.25.25 0 0 1 4 10.396V5.604a.25.25 0 0 1 .427-.177Z"></path><path d="M1.75 0h12.5C15.216 0 16 .784 16 1.75v12.5A1.75 1.75 0 0 1 14.25 16H1.75A1.75 1.75 0 0 1 0 14.25V1.75C0 .784.784 0 1.75 0ZM1.5 1.75v12.5c0 .138.112.25.25.25H9.5v-13H1.75a.25.25 0 0 0-.25.25ZM11 14.5h3.25a.25.25 0 0 0 .25-.25V1.75a.25.25 0 0 0-.25-.25H11Z"></path></svg></button><span class="prc-TooltipV2-Tooltip-tLeuB" data-direction="se" data-component="Tooltip" aria-hidden="true" id="_R_4lla9lik5_">Expand file tree</span><div class="d-none"></div></h2></div><div class="react-code-view-header-mb--narrow mr-2"><button data-component="Button" type="button" aria-haspopup="true" aria-expanded="false" tabindex="0" aria-label="master branch" data-testid="anchor-button" data-icv-name="Switch branches/tags" class="prc-Button-ButtonBase-9n-Xk ref-selector-class RefSelectorAnchoredOverlay-module__RefSelectorOverlayBtn__a3WK3" data-loading="false" data-size="medium" data-variant="default" id="ref-picker-repos-header-ref-selector-wide"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-Iohp5"><span data-component="leadingVisual" class="prc-Button-Visual-YNt2F prc-Button-LeadingVisual-UySKu prc-Button-VisualWrap-E4cnq"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-git-branch" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M9.5 3.25a2.25 2.25 0 1 1 3 2.122V6A2.5 2.5 0 0 1 10 8.5H6a1 1 0 0 0-1 1v1.128a2.251 2.251 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.5 0v1.836A2.493 2.493 0 0 1 6 7h4a1 1 0 0 0 1-1v-.628A2.25 2.25 0 0 1 9.5 3.25Zm-6 0a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Zm8.25-.75a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5ZM4.25 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Z"></path></svg></span><span data-component="text" class="prc-Button-Label-FWkx3"><div class="RefSelectorAnchoredOverlay-module__RefSelectorOverlayContainer__yaf4p"><div style="max-width:125px" class="ref-selector-button-text-container RefSelectorAnchoredOverlay-module__RefSelectorBtnTextContainer__Di3rk"><span class="RefSelectorAnchoredOverlay-module__RefSelectorText__w_fmP">master</span></div></div></span><span data-component="trailingVisual" class="prc-Button-Visual-YNt2F prc-Button-VisualWrap-E4cnq"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-triangle-down" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m4.427 7.427 3.396 3.396a.25.25 0 0 0 .354 0l3.396-3.396A.25.25 0 0 0 11.396 7H4.604a.25.25 0 0 0-.177.427Z"></path></svg></span></span></button><div class="d-none"></div></div><div class="react-code-view-header-mb--narrow CodeViewHeader-module__Box_5__MQ0hL"><div class="Breadcrumb-module__container__Vxvev Breadcrumb-module__lg__Rjz0A"><nav data-testid="breadcrumbs" aria-labelledby="repos-header-breadcrumb-heading" id="repos-header-breadcrumb" class="Breadcrumb-module__nav__rQFDj"><h2 class="sr-only ScreenReaderHeading-module__userSelectNone__rwWIk prc-Heading-Heading-MtWFE" data-component="Heading" data-testid="screen-reader-heading" id="repos-header-breadcrumb-heading">Breadcrumbs</h2><ol class="Breadcrumb-module__list__ZH6zr"><li class="Breadcrumb-module__listItem__Ib0x_"><a class="Breadcrumb-module__repoLink__O2Nbs prc-Link-Link-9ZwDx" data-component="Link" data-testid="breadcrumbs-repo-link" href="/dtprj/dongting/tree/master" data-discover="true">dongting</a></li></ol></nav><div data-testid="breadcrumbs-filename" class="Breadcrumb-module__filename__equZR"><span class="Breadcrumb-module__separator__eNwsI Breadcrumb-module__lg__Rjz0A" aria-hidden="true">/</span><h1 class="Breadcrumb-module__filenameHeading__MNMtw Breadcrumb-module__lg__Rjz0A prc-Heading-Heading-MtWFE" data-component="Heading" tabindex="-1" id="file-name-id">README_CN.md</h1></div><button data-component="IconButton" type="button" class="prc-Button-ButtonBase-9n-Xk ml-2 prc-Button-IconButton-fyge7" data-loading="false" data-no-visuals="true" data-size="small" data-variant="invisible" aria-labelledby="_R_7lla9lik5_"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-copy" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path></svg></button><span class="CopyToClipboardIconButton-module__tooltip__WyiwL prc-TooltipV2-Tooltip-tLeuB" data-direction="nw" data-component="Tooltip" aria-label="Copy path" aria-hidden="true" id="_R_7lla9lik5_">Copy path</span></div></div></div><div class="react-code-view-header-element--wide"><div class="CodeViewHeader-module__Box_7___0R6c"><div class="d-flex gap-2"><div><div class="CodeViewHeader-module__FileResultsList__JDzUy"><span class="d-flex FileResultsList-module__FilesSearchBox__ivVkc TextInput-wrapper prc-components-TextInputWrapper-Hpdqi prc-components-TextInputBaseWrapper-wY-n0" data-no-trailing-action="true" data-component="TextInput" data-leading-visual="true" data-trailing-visual="true" aria-busy="false"><span class="TextInput-icon" id="_R_1cpla9lik5_" aria-hidden="true" data-component="TextInput.LeadingVisual"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-search" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M10.68 11.74a6 6 0 0 1-7.922-8.982 6 6 0 0 1 8.982 7.922l3.04 3.04a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215ZM11.5 7a4.499 4.499 0 1 0-8.997 0A4.499 4.499 0 0 0 11.5 7Z"></path></svg></span><input type="text" aria-label="Go to file" role="combobox" aria-controls="file-results-list" aria-expanded="false" aria-haspopup="dialog" autoCorrect="off" spellCheck="false" placeholder="Go to file" aria-describedby="_R_1cpla9lik5_ _R_1cpla9lik5H1_" data-component="input" class="prc-components-Input-IwWrt" value=""/><span class="TextInput-icon" id="_R_1cpla9lik5H1_" aria-hidden="true" data-component="TextInput.TrailingVisual"></span></span></div><div class="d-none"></div></div><button data-component="Button" type="button" style="display:none" class="prc-Button-ButtonBase-9n-Xk NavigationMenu-module__Button__LpKgm" data-loading="false" data-no-visuals="true" data-size="medium" data-variant="default"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-Iohp5"><span data-component="text" class="prc-Button-Label-FWkx3">Blame</span></span></button><div class="d-none"></div><button data-component="IconButton" type="button" data-testid="more-file-actions-button-nav-menu-wide" aria-haspopup="true" aria-expanded="false" tabindex="0" class="prc-Button-ButtonBase-9n-Xk js-blob-dropdown-click NavigationMenu-module__IconButton__HpX3G prc-Button-IconButton-fyge7" data-loading="false" data-no-visuals="true" data-size="medium" data-variant="default" aria-labelledby="_R_7p9la9lik5_" id="_R_99la9lik5_"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-kebab-horizontal" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M8 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM1.5 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Zm13 0a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z"></path></svg></button><span class="prc-TooltipV2-Tooltip-tLeuB" data-direction="nw" data-component="Tooltip" aria-hidden="true" id="_R_7p9la9lik5_">More file actions</span></div></div></div><div class="react-code-view-header-element--narrow"><div class="CodeViewHeader-module__Box_7___0R6c"><div class="d-flex gap-2"><button data-component="Button" type="button" style="display:none" class="prc-Button-ButtonBase-9n-Xk NavigationMenu-module__Button__LpKgm" data-loading="false" data-no-visuals="true" data-size="medium" data-variant="default"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-Iohp5"><span data-component="text" class="prc-Button-Label-FWkx3">Blame</span></span></button><div class="d-none"></div><button data-component="IconButton" type="button" data-testid="more-file-actions-button-nav-menu-narrow" aria-haspopup="true" aria-expanded="false" tabindex="0" class="prc-Button-ButtonBase-9n-Xk js-blob-dropdown-click NavigationMenu-module__IconButton__HpX3G prc-Button-IconButton-fyge7" data-loading="false" data-no-visuals="true" data-size="medium" data-variant="default" aria-labelledby="_R_7pdla9lik5_" id="_R_9dla9lik5_"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-kebab-horizontal" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M8 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM1.5 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Zm13 0a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z"></path></svg></button><span class="prc-TooltipV2-Tooltip-tLeuB" data-direction="nw" data-component="Tooltip" aria-hidden="true" id="_R_7pdla9lik5_">More file actions</span></div></div></div></div></div></div></div><div class="CodeView-module__contentWrapper__cG2JH"><div class="react-code-view-bottom-padding"><div class="BlobTopBanners-module__Box__v_nvx"></div></div> <div class="d-none"></div><div class="d-flex flex-column border rounded-2 tmp-mb-3 pl-1"><div class="LatestCommit-module__Box__B25ZT"><h2 class="sr-only ScreenReaderHeading-module__userSelectNone__rwWIk prc-Heading-Heading-MtWFE" data-component="Heading" data-testid="screen-reader-heading">Latest commit</h2><div style="width:120px" class="Skeleton Skeleton--text" data-testid="loading"> </div><div class="d-flex flex-shrink-0 gap-2"><div data-testid="latest-commit-details" class="d-none d-sm-flex flex-items-center"></div><div class="d-flex gap-2"><h2 class="sr-only ScreenReaderHeading-module__userSelectNone__rwWIk prc-Heading-Heading-MtWFE" data-component="Heading" data-testid="screen-reader-heading">History</h2><a data-component="LinkButton" href="/dtprj/dongting/commits/master/README_CN.md" class="prc-Button-ButtonBase-9n-Xk d-none d-lg-flex LinkButton-module__linkButton__nFnov flex-items-center fgColor-default" data-loading="false" data-size="small" data-variant="invisible"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-Iohp5"><span data-component="leadingVisual" class="prc-Button-Visual-YNt2F prc-Button-LeadingVisual-UySKu prc-Button-VisualWrap-E4cnq"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-history" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m.427 1.927 1.215 1.215a8.002 8.002 0 1 1-1.6 5.685.75.75 0 1 1 1.493-.154 6.5 6.5 0 1 0 1.18-4.458l1.358 1.358A.25.25 0 0 1 3.896 6H.25A.25.25 0 0 1 0 5.75V2.104a.25.25 0 0 1 .427-.177ZM7.75 4a.75.75 0 0 1 .75.75v2.992l2.028.812a.75.75 0 0 1-.557 1.392l-2.5-1A.751.751 0 0 1 7 8.25v-3.5A.75.75 0 0 1 7.75 4Z"></path></svg></span><span data-component="text" class="prc-Button-Label-FWkx3"><span class="fgColor-default">History</span></span></span></a><div class="d-sm-none"></div><div class="d-flex d-lg-none"><a data-component="LinkButton" aria-label="View commit history for this file." href="/dtprj/dongting/commits/master/README_CN.md" class="prc-Button-ButtonBase-9n-Xk LinkButton-module__linkButton__nFnov flex-items-center fgColor-default" data-loading="false" data-size="small" data-variant="invisible" aria-describedby="_R_4mlala9lik5_"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-Iohp5"><span data-component="leadingVisual" class="prc-Button-Visual-YNt2F prc-Button-LeadingVisual-UySKu prc-Button-VisualWrap-E4cnq"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-history" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m.427 1.927 1.215 1.215a8.002 8.002 0 1 1-1.6 5.685.75.75 0 1 1 1.493-.154 6.5 6.5 0 1 0 1.18-4.458l1.358 1.358A.25.25 0 0 1 3.896 6H.25A.25.25 0 0 1 0 5.75V2.104a.25.25 0 0 1 .427-.177ZM7.75 4a.75.75 0 0 1 .75.75v2.992l2.028.812a.75.75 0 0 1-.557 1.392l-2.5-1A.751.751 0 0 1 7 8.25v-3.5A.75.75 0 0 1 7.75 4Z"></path></svg></span></span></a><span class="prc-TooltipV2-Tooltip-tLeuB" data-direction="s" data-component="Tooltip" role="tooltip" aria-hidden="true" id="_R_4mlala9lik5_">History</span></div></div></div></div></div><div class="d-flex flex-row"><div class="container BlobViewContent-module__blobContainer__DtH2d"><div class="react-code-size-details-banner BlobViewContent-module__codeSizeDetails__e5sUw"><div class="react-code-size-details-banner CodeSizeDetails-module__Box__VcD6l"><div class="text-mono CodeSizeDetails-module__Box_1__GVxQL"><div data-testid="blob-size" class="CodeSizeDetails-module__Truncate_1__lE93V prc-Truncate-Truncate-2G1eo" data-inline="true" title="23.7 KB" style="--truncate-max-width:100%"><span>491 lines (366 loc) · 23.7 KB</span></div></div></div></div><div class="react-blob-view-header-sticky BlobViewContent-module__stickyHeader__VwxB5" id="repos-sticky-header"><div class="BlobViewHeader-module__Box__yhm9u"><div class="react-blob-sticky-header"><div class="FileNameStickyHeader-module__outerWrapper__ZL4Xc FileNameStickyHeader-module__outerWrapperHidden__Zpynk"><div class="FileNameStickyHeader-module__Box_1__Hazu5"><div class="FileNameStickyHeader-module__Box_2__hoolP"><div class="FileNameStickyHeader-module__Box_3__MVKsk"><button data-component="Button" type="button" aria-haspopup="true" aria-expanded="false" tabindex="0" aria-label="master branch" data-testid="anchor-button" data-icv-name="Switch branches/tags" class="prc-Button-ButtonBase-9n-Xk ref-selector-class RefSelectorAnchoredOverlay-module__RefSelectorOverlayBtn__a3WK3" data-loading="false" data-size="medium" data-variant="default" id="ref-picker-repos-header-ref-selector"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-Iohp5"><span data-component="leadingVisual" class="prc-Button-Visual-YNt2F prc-Button-LeadingVisual-UySKu prc-Button-VisualWrap-E4cnq"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-git-branch" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M9.5 3.25a2.25 2.25 0 1 1 3 2.122V6A2.5 2.5 0 0 1 10 8.5H6a1 1 0 0 0-1 1v1.128a2.251 2.251 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.5 0v1.836A2.493 2.493 0 0 1 6 7h4a1 1 0 0 0 1-1v-.628A2.25 2.25 0 0 1 9.5 3.25Zm-6 0a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Zm8.25-.75a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5ZM4.25 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Z"></path></svg></span><span data-component="text" class="prc-Button-Label-FWkx3"><div class="RefSelectorAnchoredOverlay-module__RefSelectorOverlayContainer__yaf4p"><div style="max-width:125px" class="ref-selector-button-text-container RefSelectorAnchoredOverlay-module__RefSelectorBtnTextContainer__Di3rk"><span class="RefSelectorAnchoredOverlay-module__RefSelectorText__w_fmP">master</span></div></div></span><span data-component="trailingVisual" class="prc-Button-Visual-YNt2F prc-Button-VisualWrap-E4cnq"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-triangle-down" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m4.427 7.427 3.396 3.396a.25.25 0 0 0 .354 0l3.396-3.396A.25.25 0 0 0 11.396 7H4.604a.25.25 0 0 0-.177.427Z"></path></svg></span></span></button><div class="d-none"></div></div><div class="FileNameStickyHeader-module__Box_4__FLhtt"><div class="Breadcrumb-module__container__Vxvev Breadcrumb-module__md__Wb1Gs"><nav data-testid="breadcrumbs" aria-labelledby="sticky-breadcrumb-heading" id="sticky-breadcrumb" class="Breadcrumb-module__nav__rQFDj"><h2 class="sr-only ScreenReaderHeading-module__userSelectNone__rwWIk prc-Heading-Heading-MtWFE" data-component="Heading" data-testid="screen-reader-heading" id="sticky-breadcrumb-heading">Breadcrumbs</h2><ol class="Breadcrumb-module__list__ZH6zr"><li class="Breadcrumb-module__listItem__Ib0x_"><a class="Breadcrumb-module__repoLink__O2Nbs prc-Link-Link-9ZwDx" data-component="Link" data-testid="breadcrumbs-repo-link" href="/dtprj/dongting/tree/master" data-discover="true">dongting</a></li></ol></nav><div data-testid="breadcrumbs-filename" class="Breadcrumb-module__filename__equZR"><span class="Breadcrumb-module__separator__eNwsI Breadcrumb-module__md__Wb1Gs" aria-hidden="true">/</span><h1 class="Breadcrumb-module__filenameHeading__MNMtw Breadcrumb-module__md__Wb1Gs prc-Heading-Heading-MtWFE" data-component="Heading" tabindex="-1" id="sticky-file-name-id">README_CN.md</h1></div><button data-component="IconButton" type="button" class="prc-Button-ButtonBase-9n-Xk ml-2 prc-Button-IconButton-fyge7" data-loading="false" data-no-visuals="true" data-size="small" data-variant="invisible" aria-labelledby="_R_7lcpala9lik5_"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-copy" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path></svg></button><span class="CopyToClipboardIconButton-module__tooltip__WyiwL prc-TooltipV2-Tooltip-tLeuB" data-direction="s" data-component="Tooltip" aria-label="Copy path" aria-hidden="true" id="_R_7lcpala9lik5_">Copy path</span></div></div></div><button data-component="Button" type="button" class="prc-Button-ButtonBase-9n-Xk FileNameStickyHeader-module__Button__LSEU_ FileNameStickyHeader-module__GoToTopButton__nxAFn" data-loading="false" data-size="small" data-variant="invisible"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-Iohp5"><span data-component="leadingVisual" class="prc-Button-Visual-YNt2F prc-Button-LeadingVisual-UySKu prc-Button-VisualWrap-E4cnq"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-arrow-up" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M3.47 7.78a.75.75 0 0 1 0-1.06l4.25-4.25a.75.75 0 0 1 1.06 0l4.25 4.25a.751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018L9 4.81v7.44a.75.75 0 0 1-1.5 0V4.81L4.53 7.78a.75.75 0 0 1-1.06 0Z"></path></svg></span><span data-component="text" class="prc-Button-Label-FWkx3">Top</span></span></button></div></div></div><div class="BlobViewHeader-module__Box_1__VEmuQ"><h2 class="sr-only ScreenReaderHeading-module__userSelectNone__rwWIk prc-Heading-Heading-MtWFE" data-component="Heading" data-testid="screen-reader-heading">File metadata and controls</h2><div class="BlobViewHeader-module__Box_2__icUs2"><ul aria-label="File view" class="prc-SegmentedControl-SegmentedControl-lqIXp BlobTabButtons-module__SegmentedControl__jen2u" data-variant="default" data-size="small" data-component="SegmentedControl"><li class="prc-SegmentedControl-Item-tSCQh" data-selected="" data-component="SegmentedControl.Button"><button aria-pressed="true" class="prc-SegmentedControl-Button-E48xz" type="button" style="--separator-color:transparent"><span class="prc-SegmentedControl-Content-1COlk segmentedControl-content"><div class="prc-SegmentedControl-Text-7S2y2 segmentedControl-text" data-text="Preview">Preview</div></span></button></li><li class="prc-SegmentedControl-Item-tSCQh" data-component="SegmentedControl.Button"><button aria-pressed="false" class="prc-SegmentedControl-Button-E48xz" type="button" style="--separator-color:var(--borderColor-default)"><span class="prc-SegmentedControl-Content-1COlk segmentedControl-content"><div class="prc-SegmentedControl-Text-7S2y2 segmentedControl-text" data-text="Code">Code</div></span></button></li><li class="prc-SegmentedControl-Item-tSCQh" data-component="SegmentedControl.Button"><button aria-pressed="false" class="prc-SegmentedControl-Button-E48xz" type="button" style="--separator-color:var(--borderColor-default)"><span class="prc-SegmentedControl-Content-1COlk segmentedControl-content"><div class="prc-SegmentedControl-Text-7S2y2 segmentedControl-text" data-text="Blame">Blame</div></span></button></li></ul><div class="d-none"></div><div class="react-code-size-details-in-header CodeSizeDetails-module__Box__VcD6l"><div class="text-mono CodeSizeDetails-module__Box_1__GVxQL"><div data-testid="blob-size" class="CodeSizeDetails-module__Truncate_1__lE93V prc-Truncate-Truncate-2G1eo" data-inline="true" title="23.7 KB" style="--truncate-max-width:100%"><span>491 lines (366 loc) · 23.7 KB</span></div></div></div></div><div class="BlobViewHeader-module__Box_3__ng6v2"><div class="d-none"></div><div class="react-blob-header-edit-and-raw-actions BlobViewHeader-module__Box_4__J4Y4W"><div class="d-none"></div><div class="prc-ButtonGroup-ButtonGroup-vFUrY" data-component="ButtonGroup"><div class="prc-ButtonGroup-Item-PqvDl"><a data-component="LinkButton" href="https://github.com/dtprj/dongting/raw/refs/heads/master/README_CN.md" data-testid="raw-button" class="prc-Button-ButtonBase-9n-Xk LinkButton-module__linkButton__nFnov BlobViewHeader-module__LinkButton__X9kx2" data-loading="false" data-no-visuals="true" data-size="small" data-variant="default"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-Iohp5"><span data-component="text" class="prc-Button-Label-FWkx3">Raw</span></span></a></div><div class="prc-ButtonGroup-Item-PqvDl"><button data-component="IconButton" type="button" data-testid="copy-raw-button" class="prc-Button-ButtonBase-9n-Xk prc-Button-IconButton-fyge7" data-loading="false" data-no-visuals="true" data-size="small" data-variant="default" aria-labelledby="_R_qaucpala9lik5_"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-copy" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path></svg></button><span class="prc-TooltipV2-Tooltip-tLeuB" data-direction="n" data-component="Tooltip" aria-hidden="true" id="_R_qaucpala9lik5_">Copy raw file</span></div><div class="prc-ButtonGroup-Item-PqvDl"><button data-component="IconButton" type="button" data-testid="download-raw-button" class="prc-Button-ButtonBase-9n-Xk BlobViewHeader-module__downloadButton__ef459 prc-Button-IconButton-fyge7" data-loading="false" data-no-visuals="true" data-size="small" data-variant="default" aria-labelledby="_R_eaucpala9lik5_"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-download" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2.75 14A1.75 1.75 0 0 1 1 12.25v-2.5a.75.75 0 0 1 1.5 0v2.5c0 .138.112.25.25.25h10.5a.25.25 0 0 0 .25-.25v-2.5a.75.75 0 0 1 1.5 0v2.5A1.75 1.75 0 0 1 13.25 14Z"></path><path d="M7.25 7.689V2a.75.75 0 0 1 1.5 0v5.689l1.97-1.969a.749.749 0 1 1 1.06 1.06l-3.25 3.25a.749.749 0 0 1-1.06 0L4.22 6.78a.749.749 0 1 1 1.06-1.06l1.97 1.969Z"></path></svg></button><span class="prc-TooltipV2-Tooltip-tLeuB" data-direction="n" data-component="Tooltip" aria-hidden="true" id="_R_eaucpala9lik5_">Download raw file</span></div></div></div><button data-component="IconButton" type="button" aria-pressed="false" class="prc-Button-ButtonBase-9n-Xk tmp-mr-2 TableOfContents-module__IconButton__jrlNM prc-Button-IconButton-fyge7" data-loading="false" data-no-visuals="true" data-size="small" data-variant="invisible" aria-labelledby="_R_3ucpala9lik5_"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-list-unordered" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M5.75 2.5h8.5a.75.75 0 0 1 0 1.5h-8.5a.75.75 0 0 1 0-1.5Zm0 5h8.5a.75.75 0 0 1 0 1.5h-8.5a.75.75 0 0 1 0-1.5Zm0 5h8.5a.75.75 0 0 1 0 1.5h-8.5a.75.75 0 0 1 0-1.5ZM2 14a1 1 0 1 1 0-2 1 1 0 0 1 0 2Zm1-6a1 1 0 1 1-2 0 1 1 0 0 1 2 0ZM2 4a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg></button><span class="prc-TooltipV2-Tooltip-tLeuB" data-direction="n" data-component="Tooltip" aria-hidden="true" id="_R_3ucpala9lik5_">Outline</span><div class="react-blob-header-edit-and-raw-actions-combined"><button data-component="IconButton" type="button" title="More file actions" data-testid="more-file-actions-button" aria-haspopup="true" aria-expanded="false" tabindex="0" class="prc-Button-ButtonBase-9n-Xk js-blob-dropdown-click BlobViewHeader-module__IconButton__XrMQY prc-Button-IconButton-fyge7" data-loading="false" data-no-visuals="true" data-size="small" data-variant="invisible" aria-labelledby="_R_fkecpala9lik5_" id="_R_kecpala9lik5_"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-kebab-horizontal" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M8 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM1.5 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Zm13 0a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z"></path></svg></button><span class="prc-TooltipV2-Tooltip-tLeuB" data-direction="nw" data-component="Tooltip" aria-hidden="true" id="_R_fkecpala9lik5_">Edit and raw actions</span></div></div></div></div><div></div></div><div class="BlobViewContent-module__blobContentWrapper__JS0W6"><section aria-labelledby="file-name-id-wide file-name-id-mobile" class="BlobContent-module__blobContentSection__VOgZq BlobContent-module__blobContentSectionMarkdown__mPLOK" style="margin-top:46px"><div class="js-snippet-clipboard-copy-unpositioned BlobContent-module__markdownBlob__T8jpG" data-hpc="true" containertiming="hpc"><article class="markdown-body entry-content container-lg" itemprop="text"><p dir="auto"><a href="https://github.com/dtprj/dongting/actions/workflows/maven.yml"><img src="https://github.com/dtprj/dongting/actions/workflows/maven.yml/badge.svg" alt="Java CI with Maven" style="max-width: 100%;"></a>
<a href="https://app.codecov.io/github/dtprj/dongting" rel="nofollow"><img src="https://camo.githubusercontent.com/ee7bb20d97cc8706a17d41695c4a6995acd2714f0180c1f36a209a382efa01c1/68747470733a2f2f636f6465636f762e696f2f6769746875622f647470726a2f646f6e6774696e672f6272616e63682f6d61737465722f67726170682f62616467652e737667" alt="codecov" data-canonical-src="https://codecov.io/github/dtprj/dongting/branch/master/graph/badge.svg" style="max-width: 100%;"></a></p>
<p dir="auto"><a href="/dtprj/dongting/blob/master/README.md">English</a></p>
<blockquote>
<p dir="auto"><strong>注意</strong>：正在开发中的 0.9 版本可能会和 0.8.x 不兼容。</p>
</blockquote>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">目录</h2><a id="user-content-目录" class="anchor" aria-label="Permalink: 目录" href="#目录"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<ul dir="auto">
<li><a href="#%E9%A1%B9%E7%9B%AE%E4%BB%8B%E7%BB%8D">项目介绍</a></li>
<li><a href="#10%E5%80%8D%E5%90%9E%E5%90%90">10倍吞吐</a></li>
<li><a href="#%E9%9B%B6%E4%BE%9D%E8%B5%96">零依赖</a></li>
<li><a href="#try-it">TRY IT</a>
<ul dir="auto">
<li><a href="#%E6%9E%84%E5%BB%BA%E9%A1%B9%E7%9B%AE">构建项目</a></li>
<li><a href="#%E5%90%AF%E5%8A%A8%E6%9C%8D%E5%8A%A1%E5%99%A8">启动服务器</a></li>
<li><a href="#%E8%BF%90%E8%A1%8C%E5%9F%BA%E5%87%86%E6%B5%8B%E8%AF%95">运行基准测试</a></li>
<li><a href="#%E5%AE%A2%E6%88%B7%E7%AB%AF%E4%BD%BF%E7%94%A8">客户端使用</a></li>
<li><a href="#%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%AE%8C%E6%95%B4%E4%BE%8B%E5%AD%90">客户端完整例子</a></li>
<li><a href="#server%E9%85%8D%E7%BD%AE">server配置</a></li>
</ul>
</li>
<li><a href="#%E9%9B%86%E7%BE%A4%E8%BF%90%E7%BB%B4">集群运维</a>
<ul dir="auto">
<li><a href="#%E9%85%8D%E7%BD%AE%E4%B8%80%E4%B8%AA%E5%A4%9A%E8%8A%82%E7%82%B9%E9%9B%86%E7%BE%A4">配置一个多节点集群</a></li>
<li><a href="#%E8%BF%90%E8%A1%8C%E7%AE%A1%E7%90%86%E5%B7%A5%E5%85%B7">运行管理工具</a></li>
<li><a href="#%E9%9B%86%E7%BE%A4%E5%8F%98%E6%9B%B4">集群变更</a></li>
</ul>
</li>
<li><a href="#%E8%BF%9B%E9%98%B6">进阶</a>
<ul dir="auto">
<li><a href="#%E5%B0%86%E9%A1%B9%E7%9B%AE%E5%AF%BC%E5%85%A5ide">将项目导入IDE</a></li>
<li><a href="#%E9%80%9A%E8%BF%87%E4%BB%A3%E7%A0%81%E6%9E%84%E5%BB%BA-raft-%E6%9C%8D%E5%8A%A1%E5%99%A8">通过代码构建 raft 服务器</a></li>
<li><a href="#%E4%BD%BF%E7%94%A8-jlink-%E8%A3%81%E5%89%AA-jre">使用 JLink 裁剪 JRE</a></li>
</ul>
</li>
<li><a href="#%E5%85%B3%E4%BA%8E%E6%88%91">关于我</a></li>
</ul>
<div class="markdown-heading" dir="auto"><h1 tabindex="-1" class="heading-element" dir="auto">项目介绍</h1><a id="user-content-项目介绍" class="anchor" aria-label="Permalink: 项目介绍" href="#项目介绍"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Dongting是一个高性能的分布式引擎，集成了RAFT共识算法、配置服务器和消息队列功能。</p>
<p dir="auto">主要特性如下：</p>
<ul dir="auto">
<li><strong>Multi RAFT 组支持</strong>：在同一进程内运行多个RAFT组，支持动态添加、删除和更新RAFT group（分片），实现集群的动态扩展。状态机可以在raft框架中自定义。</li>
<li><strong>分布式配置服务器 DtKV</strong>：树形结构，支持线性一致性的通用K/V操作、监听(watch)、TTL过期和分布式锁，类似于etcd
<ul dir="auto">
<li><code>DtKV</code>是内存型数据库，因此数据总量不能太大，但它使用raft log作为redo日志，定期做快照，掉电也不会丢失一条数据。</li>
<li>原生支持树状目录，很多操作的复杂度是O(1)，比如在目录上进行watch，非常高效</li>
<li>支持临时目录，在TTL过期后自动删除整个目录，该删除操作为原子操作</li>
<li>不支持事务，但是提供了CAS和非常易用的分布式锁</li>
</ul>
</li>
<li><strong>（开发中）MQ</strong>：使用RAFT日志作为消息队列日志。</li>
</ul>
<div class="markdown-heading" dir="auto"><h1 tabindex="-1" class="heading-element" dir="auto">10倍吞吐</h1><a id="user-content-10倍吞吐" class="anchor" aria-label="Permalink: 10倍吞吐" href="#10倍吞吐"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Dongting使用面向性能编程（POP）的方式开发。</p>
<p dir="auto">在简单的同机测试中，现代高性能PC使用服务器默认配置即可轻松通过基准测试程序实现超过100万TPS，且响应时间(RT)也不会太大。</p>
<div class="markdown-heading" dir="auto"><h1 tabindex="-1" class="heading-element" dir="auto">零依赖</h1><a id="user-content-零依赖" class="anchor" aria-label="Permalink: 零依赖" href="#零依赖"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">你还在为一传十、十传百的依赖问题烦恼吗？你曾经因为1GB的镜像大小而被嘲笑吗？现在你有救了！</p>
<p dir="auto">Dongting没有依赖。它不依赖第三方jar文件（只有SLF4J是可选的，如果类路径中不存在，项目将使用JDK logging）。</p>
<p dir="auto">Dongting核心只有两个JAR包，客户端和服务器端加起来不到1MB，你可以轻松将它嵌入到你的程序中，不管是client还是server。</p>
<p dir="auto">Dongting对JDK的要求也不高：客户端仅需Java 8，服务器端仅需Java 11。</p>
<p dir="auto">Dongting不需要使用高性能硬件，如RDMA或Optane什么的，它也不依赖Amazon或其他云服务提供商提供的任何第三方服务，如存储服务。
它甚至可以在 HDD 硬盘和树莓派上良好运行。</p>
<p dir="auto">Dongting不需要调整Linux内核参数以达到最佳性能（生成环境你甚至可能没有权限这样做）。</p>
<p dir="auto">Dongting是完全模块化的（同时client仍兼容java8），服务端部署时，可以用jlink裁剪一个最小化的部署JRE。</p>
<div class="markdown-heading" dir="auto"><h1 tabindex="-1" class="heading-element" dir="auto">TRY IT</h1><a id="user-content-try-it" class="anchor" aria-label="Permalink: TRY IT" href="#try-it"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">构建项目</h2><a id="user-content-构建项目" class="anchor" aria-label="Permalink: 构建项目" href="#构建项目"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">首先构建项目，构建产物位于target/dongting-dist目录：</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="mvn clean package -DskipUTs"><pre>mvn clean package -DskipUTs</pre></div>
<p dir="auto">如果你想要安装maven artifact：</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="mvn clean install -Dmaven.test.skip=true"><pre>mvn clean install -Dmaven.test.skip=true</pre></div>
<p dir="auto">构建后的目录结构如下：</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="dongting-dist/
├── bin/                     # 脚本目录
│   ├── benchmark.sh         # 基准测试脚本
│   ├── benchmark.bat        # Windows基准测试脚本
│   ├── start-dongting.sh    # 启动服务器脚本（Linux/Mac）
│   ├── start-dongting.bat   # 启动服务器脚本
│   ├── stop-dongting.sh     # 停止服务器脚本
│   ├── stop-dongting.bat    # 停止服务器脚本
│   ├── dongting-admin.sh    # 管理工具脚本（Linux/Mac）
│   └── dongting-admin.bat   # 管理工具脚本
├── lib/                     # JAR包目录
│   ├── dongting-client-x.y.z-SNAPSHOT.jar
│   ├── dongting-server-x.y.z-SNAPSHOT.jar
│   ├── dongting-dist-x.y.z-SNAPSHOT.jar # 引导jar，如果使用代码构建raft server，它不是必须的
│   ├── slf4j-api-x.y.z.jar
│   ├── logback-x.y.z.jar
│   └── logback-x.y.z.jar
├── conf/                     # 配置文件目录
│   ├── config.properties
│   ├── servers.properties
│   ├── client.properties
│   ├── logback-server.xml
│   ├── logback-admin.xml
│   └── logback-benchmark.xml
├── docs/                     # 文档目录
├── data/                     # 数据目录（运行时生成）
└── logs/                     # 日志目录
    ├── dongting-server.log   # 服务器日志（运行时生成）
    └── dongting-stats.log    # 统计日志（运行时生成）"><pre class="notranslate"><code>dongting-dist/
├── bin/                     # 脚本目录
│   ├── benchmark.sh         # 基准测试脚本
│   ├── benchmark.bat        # Windows基准测试脚本
│   ├── start-dongting.sh    # 启动服务器脚本（Linux/Mac）
│   ├── start-dongting.bat   # 启动服务器脚本
│   ├── stop-dongting.sh     # 停止服务器脚本
│   ├── stop-dongting.bat    # 停止服务器脚本
│   ├── dongting-admin.sh    # 管理工具脚本（Linux/Mac）
│   └── dongting-admin.bat   # 管理工具脚本
├── lib/                     # JAR包目录
│   ├── dongting-client-x.y.z-SNAPSHOT.jar
│   ├── dongting-server-x.y.z-SNAPSHOT.jar
│   ├── dongting-dist-x.y.z-SNAPSHOT.jar # 引导jar，如果使用代码构建raft server，它不是必须的
│   ├── slf4j-api-x.y.z.jar
│   ├── logback-x.y.z.jar
│   └── logback-x.y.z.jar
├── conf/                     # 配置文件目录
│   ├── config.properties
│   ├── servers.properties
│   ├── client.properties
│   ├── logback-server.xml
│   ├── logback-admin.xml
│   └── logback-benchmark.xml
├── docs/                     # 文档目录
├── data/                     # 数据目录（运行时生成）
└── logs/                     # 日志目录
    ├── dongting-server.log   # 服务器日志（运行时生成）
    └── dongting-stats.log    # 统计日志（运行时生成）
</code></pre></div>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">启动服务器</h2><a id="user-content-启动服务器" class="anchor" aria-label="Permalink: 启动服务器" href="#启动服务器"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">在bin目录下，运行以下命令启动服务器：</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="./start-dongting.sh"><pre>./start-dongting.sh</pre></div>
<p dir="auto">服务器将启动并监听 9331 端口（用于server内部通信，如raft复制）和 9332 端口（服务端口）。
默认会启动一个<code>DtKV</code>实例，groupId为0。</p>
<p dir="auto">运行下面的命令可以（根据data目录下的pid文件找到进程）停止服务器：</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="./stop-dongting.sh"><pre>./stop-dongting.sh</pre></div>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">运行基准测试</h2><a id="user-content-运行基准测试" class="anchor" aria-label="Permalink: 运行基准测试" href="#运行基准测试"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">运行以下命令启动基准测试客户端：</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="./benchmark.sh -g 0"><pre>./benchmark.sh -g 0</pre></div>
<p dir="auto">你可能需要调整参数以获得最大吞吐量，例如：</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="./benchmark.sh -g 0 --max-pending 10000 --client-count 2"><pre>./benchmark.sh -g 0 --max-pending 10000 --client-count 2</pre></div>
<p dir="auto">尝试使用Java 21虚拟线程进行多线程压测（需要 Java 21）：</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="./benchmark.sh -g 0 --sync --thread-count 4000"><pre>./benchmark.sh -g 0 --sync --thread-count 4000</pre></div>
<p dir="auto">看看我的测试结果吧（AMD 9700X 6C12T，致态 TiPro 9000 运行在 PCI-E 4.0 模式）：</p>
<div class="highlight highlight-source-powershell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="PS D:\dongting-dist\bin&gt; .\benchmark.bat -g 0
Configuration:
  Config file: D:\dongting-dist\conf\client.properties
  Servers: 1,127.0.0.1:9332
  Group ID: 0

Benchmark config:
  Java 21, async put, 10000 keys, 256 bytes value, 2000 total maxPending
  1 clients, one thread per client

Warming up for 3 seconds...

[Warmup] TPS: 1,133,471, Success: 1,133,471, Fail: 0, Avg: 1,754 us, Max: 30,000 us
[Warmup] TPS: 1,355,100, Success: 1,355,100, Fail: 0, Avg: 1,483 us, Max: 13,000 us
[Warmup] TPS: 1,349,302, Success: 1,349,302, Fail: 0, Avg: 1,456 us, Max: 14,000 us
Warmup complete, starting benchmark...

[Now] TPS: 1,389,229, Success: 1,389,229, Fail: 0, Avg: 1,440 us, Max: 13,000 us
[Now] TPS: 1,331,667, Success: 1,331,667, Fail: 0, Avg: 1,494 us, Max: 13,000 us
[Now] TPS: 1,324,262, Success: 1,324,262, Fail: 0, Avg: 1,504 us, Max: 12,000 us
[Now] TPS: 1,369,651, Success: 1,369,651, Fail: 0, Avg: 1,453 us, Max: 13,000 us
[Now] TPS: 1,380,549, Success: 1,380,549, Fail: 0, Avg: 1,461 us, Max: 13,000 us
[Now] TPS: 1,344,967, Success: 1,344,967, Fail: 0, Avg: 1,479 us, Max: 19,000 us
[Now] TPS: 1,327,548, Success: 1,327,548, Fail: 0, Avg: 1,499 us, Max: 24,000 us
[Now] TPS: 1,363,815, Success: 1,363,815, Fail: 0, Avg: 1,458 us, Max: 12,000 us
[Now] TPS: 1,359,154, Success: 1,359,154, Fail: 0, Avg: 1,472 us, Max: 13,000 us
[Now] TPS: 1,345,889, Success: 1,345,889, Fail: 0, Avg: 1,476 us, Max: 13,000 us

Benchmark config:
  Java 21, async put, 10000 keys, 256 bytes value, 2000 total maxPending
  1 clients, one thread per client

[Final] TPS: 1,353,905, Success: 13,539,049, Fail: 0, Avg: 1,473 us, Max: 24,000 us

PS D:\dongting-dist\bin&gt;"><pre>PS D:\dongting<span class="pl-k">-</span>dist\bin<span class="pl-k">&gt;</span> .\<span class="pl-c1">benchmark.bat</span> <span class="pl-k">-</span>g <span class="pl-c1">0</span>
Configuration:
  Config file: D:\dongting<span class="pl-k">-</span>dist\conf\client.properties
  Servers: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">127.0</span>.<span class="pl-c1">0.1</span>:<span class="pl-c1">9332</span>
  Group ID: <span class="pl-c1">0</span>

Benchmark config:
  Java <span class="pl-c1">21</span><span class="pl-k">,</span> async put<span class="pl-k">,</span> <span class="pl-c1">10000</span> keys<span class="pl-k">,</span> <span class="pl-c1">256</span> bytes value<span class="pl-k">,</span> <span class="pl-c1">2000</span> total maxPending
  <span class="pl-c1">1</span> clients<span class="pl-k">,</span> one thread per client

Warming up <span class="pl-k">for</span> <span class="pl-c1">3</span> seconds...

[<span class="pl-k">Warmup</span>] TPS: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">133</span><span class="pl-k">,</span><span class="pl-c1">471</span><span class="pl-k">,</span> Success: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">133</span><span class="pl-k">,</span><span class="pl-c1">471</span><span class="pl-k">,</span> Fail: <span class="pl-c1">0</span><span class="pl-k">,</span> Avg: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">754</span> us<span class="pl-k">,</span> Max: <span class="pl-c1">30</span><span class="pl-k">,</span><span class="pl-c1">000</span> us
[<span class="pl-k">Warmup</span>] TPS: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">355</span><span class="pl-k">,</span><span class="pl-c1">100</span><span class="pl-k">,</span> Success: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">355</span><span class="pl-k">,</span><span class="pl-c1">100</span><span class="pl-k">,</span> Fail: <span class="pl-c1">0</span><span class="pl-k">,</span> Avg: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">483</span> us<span class="pl-k">,</span> Max: <span class="pl-c1">13</span><span class="pl-k">,</span><span class="pl-c1">000</span> us
[<span class="pl-k">Warmup</span>] TPS: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">349</span><span class="pl-k">,</span><span class="pl-c1">302</span><span class="pl-k">,</span> Success: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">349</span><span class="pl-k">,</span><span class="pl-c1">302</span><span class="pl-k">,</span> Fail: <span class="pl-c1">0</span><span class="pl-k">,</span> Avg: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">456</span> us<span class="pl-k">,</span> Max: <span class="pl-c1">14</span><span class="pl-k">,</span><span class="pl-c1">000</span> us
Warmup complete<span class="pl-k">,</span> starting benchmark...

[<span class="pl-k">Now</span>] TPS: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">389</span><span class="pl-k">,</span><span class="pl-c1">229</span><span class="pl-k">,</span> Success: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">389</span><span class="pl-k">,</span><span class="pl-c1">229</span><span class="pl-k">,</span> Fail: <span class="pl-c1">0</span><span class="pl-k">,</span> Avg: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">440</span> us<span class="pl-k">,</span> Max: <span class="pl-c1">13</span><span class="pl-k">,</span><span class="pl-c1">000</span> us
[<span class="pl-k">Now</span>] TPS: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">331</span><span class="pl-k">,</span><span class="pl-c1">667</span><span class="pl-k">,</span> Success: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">331</span><span class="pl-k">,</span><span class="pl-c1">667</span><span class="pl-k">,</span> Fail: <span class="pl-c1">0</span><span class="pl-k">,</span> Avg: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">494</span> us<span class="pl-k">,</span> Max: <span class="pl-c1">13</span><span class="pl-k">,</span><span class="pl-c1">000</span> us
[<span class="pl-k">Now</span>] TPS: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">324</span><span class="pl-k">,</span><span class="pl-c1">262</span><span class="pl-k">,</span> Success: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">324</span><span class="pl-k">,</span><span class="pl-c1">262</span><span class="pl-k">,</span> Fail: <span class="pl-c1">0</span><span class="pl-k">,</span> Avg: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">504</span> us<span class="pl-k">,</span> Max: <span class="pl-c1">12</span><span class="pl-k">,</span><span class="pl-c1">000</span> us
[<span class="pl-k">Now</span>] TPS: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">369</span><span class="pl-k">,</span><span class="pl-c1">651</span><span class="pl-k">,</span> Success: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">369</span><span class="pl-k">,</span><span class="pl-c1">651</span><span class="pl-k">,</span> Fail: <span class="pl-c1">0</span><span class="pl-k">,</span> Avg: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">453</span> us<span class="pl-k">,</span> Max: <span class="pl-c1">13</span><span class="pl-k">,</span><span class="pl-c1">000</span> us
[<span class="pl-k">Now</span>] TPS: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">380</span><span class="pl-k">,</span><span class="pl-c1">549</span><span class="pl-k">,</span> Success: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">380</span><span class="pl-k">,</span><span class="pl-c1">549</span><span class="pl-k">,</span> Fail: <span class="pl-c1">0</span><span class="pl-k">,</span> Avg: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">461</span> us<span class="pl-k">,</span> Max: <span class="pl-c1">13</span><span class="pl-k">,</span><span class="pl-c1">000</span> us
[<span class="pl-k">Now</span>] TPS: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">344</span><span class="pl-k">,</span><span class="pl-c1">967</span><span class="pl-k">,</span> Success: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">344</span><span class="pl-k">,</span><span class="pl-c1">967</span><span class="pl-k">,</span> Fail: <span class="pl-c1">0</span><span class="pl-k">,</span> Avg: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">479</span> us<span class="pl-k">,</span> Max: <span class="pl-c1">19</span><span class="pl-k">,</span><span class="pl-c1">000</span> us
[<span class="pl-k">Now</span>] TPS: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">327</span><span class="pl-k">,</span><span class="pl-c1">548</span><span class="pl-k">,</span> Success: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">327</span><span class="pl-k">,</span><span class="pl-c1">548</span><span class="pl-k">,</span> Fail: <span class="pl-c1">0</span><span class="pl-k">,</span> Avg: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">499</span> us<span class="pl-k">,</span> Max: <span class="pl-c1">24</span><span class="pl-k">,</span><span class="pl-c1">000</span> us
[<span class="pl-k">Now</span>] TPS: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">363</span><span class="pl-k">,</span><span class="pl-c1">815</span><span class="pl-k">,</span> Success: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">363</span><span class="pl-k">,</span><span class="pl-c1">815</span><span class="pl-k">,</span> Fail: <span class="pl-c1">0</span><span class="pl-k">,</span> Avg: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">458</span> us<span class="pl-k">,</span> Max: <span class="pl-c1">12</span><span class="pl-k">,</span><span class="pl-c1">000</span> us
[<span class="pl-k">Now</span>] TPS: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">359</span><span class="pl-k">,</span><span class="pl-c1">154</span><span class="pl-k">,</span> Success: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">359</span><span class="pl-k">,</span><span class="pl-c1">154</span><span class="pl-k">,</span> Fail: <span class="pl-c1">0</span><span class="pl-k">,</span> Avg: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">472</span> us<span class="pl-k">,</span> Max: <span class="pl-c1">13</span><span class="pl-k">,</span><span class="pl-c1">000</span> us
[<span class="pl-k">Now</span>] TPS: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">345</span><span class="pl-k">,</span><span class="pl-c1">889</span><span class="pl-k">,</span> Success: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">345</span><span class="pl-k">,</span><span class="pl-c1">889</span><span class="pl-k">,</span> Fail: <span class="pl-c1">0</span><span class="pl-k">,</span> Avg: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">476</span> us<span class="pl-k">,</span> Max: <span class="pl-c1">13</span><span class="pl-k">,</span><span class="pl-c1">000</span> us

Benchmark config:
  Java <span class="pl-c1">21</span><span class="pl-k">,</span> async put<span class="pl-k">,</span> <span class="pl-c1">10000</span> keys<span class="pl-k">,</span> <span class="pl-c1">256</span> bytes value<span class="pl-k">,</span> <span class="pl-c1">2000</span> total maxPending
  <span class="pl-c1">1</span> clients<span class="pl-k">,</span> one thread per client

[<span class="pl-k">Final</span>] TPS: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">353</span><span class="pl-k">,</span><span class="pl-c1">905</span><span class="pl-k">,</span> Success: <span class="pl-c1">13</span><span class="pl-k">,</span><span class="pl-c1">539</span><span class="pl-k">,</span><span class="pl-c1">049</span><span class="pl-k">,</span> Fail: <span class="pl-c1">0</span><span class="pl-k">,</span> Avg: <span class="pl-c1">1</span><span class="pl-k">,</span><span class="pl-c1">473</span> us<span class="pl-k">,</span> Max: <span class="pl-c1">24</span><span class="pl-k">,</span><span class="pl-c1">000</span> us

PS D:\dongting<span class="pl-k">-</span>dist\bin<span class="pl-k">&gt;</span></pre></div>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">客户端使用</h2><a id="user-content-客户端使用" class="anchor" aria-label="Permalink: 客户端使用" href="#客户端使用"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Dongting内置的DtKV支持以下功能：</p>
<ul dir="auto">
<li>get/batchGet</li>
<li>put/batchPut</li>
<li>remove/batchRemove</li>
<li>list</li>
<li>mkdir</li>
<li>compareAndSet</li>
<li>putTemp/makeTempDir（带 TTL 的节点）</li>
<li>createLock/createAutoRenewalLock</li>
</ul>
<p dir="auto"><code>DtKV</code> 使用 <code>.</code> 作为 key 的分隔符。例如，您可以使用 <code>"dir1.key1"</code> 访问 <code>dir1</code> 目录下的 <code>key1</code>，value不能为空值或空串。</p>
<p dir="auto">要使用<code>DtKV</code>客户端，需要引入 <code>dongting-client.jar</code>（300+KB），无其它依赖。</p>
<p dir="auto">以下是初始化<code>DtKV</code>客户端的简单示例：</p>
<div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// dongting 支持multi-raft，因此需要指定group id，默认group id是0
int groupId = 0;
KvClient kvClient = new KvClient();
kvClient.start();
// 在运行时添加node定义，每个node有一个唯一的正整数id 和一个 host:servicePort 地址
kvClient.getRaftClient().clientAddNode(&quot;1,127.0.0.1:9332&quot;);
// kvClient.getRaftClient().clientAddNode(&quot;1,192.168.0.1:9332;2,192.168.0.2:9332;3,192.168.0.3:9332&quot;);
// 在运行时添加group定义，这里添加一个 groupId 为 0 的组，包含 id 为 1、2、3 的 3 个节点
kvClient.getRaftClient().clientAddOrUpdateGroup(groupId, new int[]{1,2,3});"><pre><span class="pl-c">// dongting 支持multi-raft，因此需要指定group id，默认group id是0</span>
<span class="pl-smi">int</span> <span class="pl-s1">groupId</span> = <span class="pl-c1">0</span>;
<span class="pl-smi">KvClient</span> <span class="pl-s1">kvClient</span> = <span class="pl-k">new</span> <span class="pl-smi">KvClient</span>();
<span class="pl-s1">kvClient</span>.<span class="pl-en">start</span>();
<span class="pl-c">// 在运行时添加node定义，每个node有一个唯一的正整数id 和一个 host:servicePort 地址</span>
<span class="pl-s1">kvClient</span>.<span class="pl-en">getRaftClient</span>().<span class="pl-en">clientAddNode</span>(<span class="pl-s">"1,127.0.0.1:9332"</span>);
<span class="pl-c">// kvClient.getRaftClient().clientAddNode("1,192.168.0.1:9332;2,192.168.0.2:9332;3,192.168.0.3:9332");</span>
<span class="pl-c">// 在运行时添加group定义，这里添加一个 groupId 为 0 的组，包含 id 为 1、2、3 的 3 个节点</span>
<span class="pl-s1">kvClient</span>.<span class="pl-en">getRaftClient</span>().<span class="pl-en">clientAddOrUpdateGroup</span>(<span class="pl-s1">groupId</span>, <span class="pl-k">new</span> <span class="pl-smi">int</span>[]{<span class="pl-c1">1</span>,<span class="pl-c1">2</span>,<span class="pl-c1">3</span>});</pre></div>
<p dir="auto">请确保指定正确的端口。每个raft node开放两个端口：
一个是<strong>复制端口</strong>，默认9331，用于 raft 节点之间的内部通信，如复制。<code>AdminRaftClient</code> 也连接到此端口。
另一个是<strong>服务端口</strong>，默认9332，用于 <code>KvClient</code> 等客户端的连接。</p>
<p dir="auto"><code>KvClient</code> 提供同步和异步接口。对于单个 <code>KvClient</code>，异步操作可以获得最大吞吐量，而同步操作需要多个线程（或虚拟线程）才能达到较高吞吐量。
需要注意的是，异步操作的回调可能在 raft 线程或 IO 线程上执行。
因此，你不应在这些回调中执行任何阻塞或 CPU 密集型操作。 如果你不确定或缺乏高级技能，强烈建议使用同步接口。</p>
<p dir="auto">以下是使用 <code>KvClient</code> 的简单示例：</p>
<div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// 同步put
kvClient.put(groupId, &quot;key1&quot;.getBytes(), &quot;value1&quot;.getBytes());
kvClient.put(groupId, &quot;key1&quot;.getBytes(), &quot;value1&quot;.getBytes(), (raftIndex, ex) -&gt; {
    // 执行一些既不阻塞也不消耗 CPU 的操作
});"><pre><span class="pl-c">// 同步put</span>
<span class="pl-s1">kvClient</span>.<span class="pl-en">put</span>(<span class="pl-s1">groupId</span>, <span class="pl-s">"key1"</span>.<span class="pl-en">getBytes</span>(), <span class="pl-s">"value1"</span>.<span class="pl-en">getBytes</span>());
<span class="pl-s1">kvClient</span>.<span class="pl-en">put</span>(<span class="pl-s1">groupId</span>, <span class="pl-s">"key1"</span>.<span class="pl-en">getBytes</span>(), <span class="pl-s">"value1"</span>.<span class="pl-en">getBytes</span>(), (<span class="pl-s1">raftIndex</span>, <span class="pl-s1">ex</span>) -&gt; {
    <span class="pl-c">// 执行一些既不阻塞也不消耗 CPU 的操作</span>
});</pre></div>
<p dir="auto">有关 <code>KvClient</code> 类的详细用法，请参阅 Javadocs。</p>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">客户端完整例子</h2><a id="user-content-客户端完整例子" class="anchor" aria-label="Permalink: 客户端完整例子" href="#客户端完整例子"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<ul dir="auto">
<li><a href="/dtprj/dongting/blob/master/demos/src/main/java/com/github/dtprj/dongting/demos/kvclient/SimpleDemo.java">SimpleDemo</a> - 基本 put/get/list 操作</li>
<li><a href="/dtprj/dongting/blob/master/demos/src/main/java/com/github/dtprj/dongting/demos/kvclient/BatchDemo.java">BatchDemo</a> - 批量读写删</li>
<li><a href="/dtprj/dongting/blob/master/demos/src/main/java/com/github/dtprj/dongting/demos/kvclient/CasDemo.java">CasDemo</a> - CAS原子操作</li>
<li><a href="/dtprj/dongting/blob/master/demos/src/main/java/com/github/dtprj/dongting/demos/kvclient/TempValueDemo.java">TempValueDemo</a> - 临时值(TTL)</li>
<li><a href="/dtprj/dongting/blob/master/demos/src/main/java/com/github/dtprj/dongting/demos/kvclient/TempDirDemo.java">TempDirDemo</a> - 临时目录(TTL)</li>
<li><a href="/dtprj/dongting/blob/master/demos/src/main/java/com/github/dtprj/dongting/demos/kvclient/UpdateTtlDemo.java">UpdateTtlDemo</a> - 更新临时节点的TTL</li>
<li><a href="/dtprj/dongting/blob/master/demos/src/main/java/com/github/dtprj/dongting/demos/kvclient/WatchDemo.java">WatchDemo</a> - key/目录变化监听</li>
<li><a href="/dtprj/dongting/blob/master/demos/src/main/java/com/github/dtprj/dongting/demos/kvclient/DistributedLockDemo.java">DistributedLockDemo</a> - 分布式锁</li>
<li><a href="/dtprj/dongting/blob/master/demos/src/main/java/com/github/dtprj/dongting/demos/kvclient/AutoRenewalLockDemo.java">AutoRenewalLockDemo</a> - 自动续约分布式锁</li>
</ul>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">server配置</h2><a id="user-content-server配置" class="anchor" aria-label="Permalink: server配置" href="#server配置"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">服务器配置主要包含两个配置文件：<code>config.properties</code> 和 <code>servers.properties</code>。</p>
<div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">config.properties</h3><a id="user-content-configproperties" class="anchor" aria-label="Permalink: config.properties" href="#configproperties"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">此文件配置 Raft 服务器的基本参数：</p>
<ul dir="auto">
<li><strong>nodeId</strong>：每个服务器必须有一个唯一的正整数 node ID，注意是从1开始，如果只有一个node通常就设置为1</li>
<li><strong>replicatePort</strong>：用于服务器内部通信的端口，例如 Raft 复制（默认值：9331）</li>
<li><strong>servicePort</strong>：用于客户端-服务器通信的端口（默认值：9332）</li>
<li><strong>electTimeout</strong>：Raft 选举超时时间，单位毫秒（默认值：15000）</li>
<li><strong>heartbeatInterval</strong>：Raft 心跳间隔时间，单位毫秒（默认值：2000）</li>
<li><strong>blockIoThreads</strong>：处理阻塞 IO 的线程数（如果没有设置，默认值运行时根据CPU核心数计算生成）</li>
</ul>
<div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">Raft group公共配置（可选）</h3><a id="user-content-raft-group公共配置可选" class="anchor" aria-label="Permalink: Raft group公共配置（可选）" href="#raft-group公共配置可选"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">以下配置在 <code>config.properties</code> 中，影响 Raft group 的行为：</p>
<ul dir="auto">
<li><strong>dataDir</strong>：数据目录（默认值指向dongting-dist目录下的data目录）</li>
<li><strong>syncForce</strong>：如果为 true，任何操作都会在响应 leader 和计算法定人数之前持久化（fsync）到磁盘。此选项对性能有显著影响（默认值：true）</li>
<li><strong>saveSnapshotSeconds</strong>：保存快照的间隔秒数（默认值：3600）</li>
<li><strong>maxKeepSnapshots</strong>：最多保留的快照数量（默认值：2）</li>
<li><strong>saveSnapshotWhenClose</strong>：关闭时（通常是服务器关闭时）是否保存快照（默认值：true）</li>
<li><strong>deleteLogsAfterTakeSnapshot</strong>：创建快照后是否删除不再需要的raft日志文件（默认值：true）</li>
</ul>
<div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">servers.properties</h3><a id="user-content-serversproperties" class="anchor" aria-label="Permalink: servers.properties" href="#serversproperties"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">此文件配置集群拓扑和 Raft group：</p>
<ul dir="auto">
<li>
<p dir="auto"><strong>servers</strong>：node 列表，格式为 <code>nodeId,ip:replicatePort</code>。多个 node 用分号分隔。示例：</p>
<div class="highlight highlight-source-ini notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="# 单节点示例
servers = 1,127.0.0.1:9331

# 多节点示例
servers = 1,192.168.0.1:9331;2,192.168.0.2:9331;3,192.168.0.3:9331

# 本地多节点示例（使用不同端口隔离）
servers = 1,127.0.0.1:4001;2,127.0.0.1:4002;3,127.0.0.1:4003"><pre><span class="pl-c"><span class="pl-c">#</span> 单节点示例</span>
<span class="pl-k">servers</span> = 1,127.0.0.1:9331

<span class="pl-c"><span class="pl-c">#</span> 多节点示例</span>
<span class="pl-k">servers</span> = 1,192.168.0.1:9331<span class="pl-c"><span class="pl-c">;</span>2,192.168.0.2:9331;3,192.168.0.3:9331</span>

<span class="pl-c"><span class="pl-c">#</span> 本地多节点示例（使用不同端口隔离）</span>
<span class="pl-k">servers</span> = 1,127.0.0.1:4001<span class="pl-c"><span class="pl-c">;</span>2,127.0.0.1:4002;3,127.0.0.1:4003</span></pre></div>
</li>
<li>
<p dir="auto"><strong>Raft group member 配置</strong>：格式为 <code>group.&lt;groupId&gt;.nodeIdOfMembers = nodeId1,nodeId2,...</code>。node ID 必须在 <code>servers</code> 属性中定义。</p>
<div class="highlight highlight-source-ini notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="group.0.nodeIdOfMembers = 1,2,3"><pre><span class="pl-k">group.0.nodeIdOfMembers</span> = 1,2,3</pre></div>
</li>
<li>
<p dir="auto"><strong>Raft group observer 配置</strong>：格式为 <code>group.&lt;groupId&gt;.nodeIdOfObservers = nodeId1,nodeId2,...</code>。observer 不会参与 leader 选举。</p>
<div class="highlight highlight-source-ini notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="group.0.nodeIdOfObservers = 4"><pre><span class="pl-k">group.0.nodeIdOfObservers</span> = 4</pre></div>
</li>
</ul>
<p dir="auto">observer会接收leader的数据复制，但是不会参与raft投票。</p>
<div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">client.properties</h3><a id="user-content-clientproperties" class="anchor" aria-label="Permalink: client.properties" href="#clientproperties"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">client.properties文件被benchmark使用，注意里面配置是的servers参数连接<strong>服务端口</strong>。</p>
<div class="markdown-heading" dir="auto"><h1 tabindex="-1" class="heading-element" dir="auto">集群运维</h1><a id="user-content-集群运维" class="anchor" aria-label="Permalink: 集群运维" href="#集群运维"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">配置一个多节点集群</h2><a id="user-content-配置一个多节点集群" class="anchor" aria-label="Permalink: 配置一个多节点集群" href="#配置一个多节点集群"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">默认情况下，直接运行start-dongting脚本会启动一个单节点集群监听本机9331端口和9332端口。
如果需要启动多节点集群，以3节点为例，需要做以下工作：</p>
<ol dir="auto">
<li>准备3份dongting-dist目录，如果是自己做测试，在同一个机器上也可以，但要修改端口。</li>
<li>修改每个config.properties文件中的nodeId。nodeId从1开始，不同node必须不一样。<strong>注意服务器一旦启动，nodeId就不能再修改了（除非清空data目录系的数据）</strong>。</li>
<li>（可选）同一个机器上做测试的话，要修改每个config.properties文件中的端口。</li>
<li>修改servers.properties文件中的servers参数，3节点的话可能是<code>1,192.168.0.1:9331;2,192.168.0.2:9331;3,192.168.0.3:9331</code>或者<code>1,127.0.0.1:4001;2,127.0.0.1:4002;3,127.0.0.1:4003</code>，根据你的ip和端口配置而定</li>
<li>修改servers.properties文件中的<code>group.0.nodeIdOfMembers</code>参数，3节点的话可能是<code>1,2,3</code></li>
</ol>
<p dir="auto">以上2、3每个node配置不一样，而4、5每个节点配置是一样的。</p>
<p dir="auto">如果要做benchmark，还要修改client.properties文件中的servers参数，连接<strong>服务端口</strong>（默认9332）。</p>
<p dir="auto">配置好以后就可以分别启动了，可以随意杀掉一个进程，看看集群的表现。</p>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">运行管理工具</h2><a id="user-content-运行管理工具" class="anchor" aria-label="Permalink: 运行管理工具" href="#运行管理工具"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">bin目录下的 dongting-admin 脚本是一个服务器管理工具，可用于：</p>
<ul dir="auto">
<li>更改raft group member/observer</li>
<li>转移leader</li>
<li>添加/删除组（multi-raft）</li>
<li>添加/删除集群节点</li>
<li>查询服务器状态</li>
</ul>
<p dir="auto">运行时不带参数可查看使用方法。</p>
<p dir="auto">可以通过<code>AdminRaftClient</code>类执行所有的管理功能，记得连接到复制端口。</p>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">集群变更</h2><a id="user-content-集群变更" class="anchor" aria-label="Permalink: 集群变更" href="#集群变更"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Dongting实现了标准的raft joint consensus，变更分为两个阶段，先prepare，再commit（或abort）。
这3个操作都实现了幂等，如果因为网络原因失败了，可以放心重试。
在dongting中，prepare阶段完成以后，c-old保存在members/observers中，c-new保存在preparedMembers/preparedObservers中，
commit或abort后，preparedMembers/preparedObservers会被清空。</p>
<p dir="auto">集群变更的一个原因可能是发生了故障，新配置可能无法下发到故障node，造成集群不同node的配置不一致。接下来描述的概念会有助于你了解应该怎么办。</p>
<p dir="auto">在Dongting中，prepare/commit/abort操作也是一条raft日志，其中包含raft group的members/observers/preparedMembers/preparedObservers配置信息。
状态机保存snapshot时（默认关闭时会保存一次），也会将这些信息存到snapshot中，raft group启动时，会先恢复快照（如果有），
然后从检查点重做日志，使用其中最新的group配置信息，因此servers.properties中的nodeIdOfMembers/nodeIdOfObservers其实只是用来引导的，
如果有snapshot或者raft log中有prepare/commit信息，servers.properties中的nodeIdOfMembers/nodeIdOfObservers就被忽略了。尽管如此，
还是有必要维护正确的nodeIdOfMembers/nodeIdOfObservers。为了简化，servers.properties中没有preparedMembers/preparedObservers。</p>
<p dir="auto">为了避免错误，server会拒绝非members/preparedMembers发过来的投票和复制请求，如果一个node的配置数据太旧，导致它不认新的leader，
可以删除它的数据，将servers.properties的group信息维护正确，然后重新加入集群。</p>
<p dir="auto">注意，以上提到的group信息，都只定义了id，它必须在servers列表中定义，否则会出错，增加新node前，要在每个node上都更新定义。
dongting-server模块需要以一个配置来启动，并且可以在运行时修改，但它不负责维护配置的持久化，是dongting-dist（dongting-dist.jar）模块
负责servers.properties的读取和更新，使用dongting-admin脚本addNode/removeNode以后，它先更新内存，然后更新servers.properties，
这不是一个原子操作，你需要关注脚本的输出，确保配置已正确更新（dongting-admin脚本还提供了一个幂等的sync-config子命令用来手工同步）。
如果你没有使用dongting-dist的脚本，而是自己通过代码来组装嵌入式dongting-server，那这些配置的持久化就由你自己来保证。</p>
<div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">例1：从group中删除一个node</h3><a id="user-content-例1从group中删除一个node" class="anchor" aria-label="Permalink: 例1：从group中删除一个node" href="#例1从group中删除一个node"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">假设你有一个3节点的集群，节点ID为1、2、3，group ID为0，现在需要删除节点3。操作步骤如下：</p>
<ol dir="auto">
<li>
<p dir="auto">首先查询一下集群状态，随便找一个node，从返回的信息中找到leader：</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="./dongting-admin.sh query-status --node-id 1 --group-id 0"><pre>./dongting-admin.sh query-status --node-id 1 --group-id 0</pre></div>
</li>
<li>
<p dir="auto">准备配置变更，将成员从1、2、3改为1、2（以下prepare/commit/abort应该在leader上调用）：</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="./dongting-admin.sh prepare-change --group-id 0 --old-members 1,2,3 --new-members 1,2"><pre>./dongting-admin.sh prepare-change --group-id 0 --old-members 1,2,3 --new-members 1,2</pre></div>
<p dir="auto">这会返回一个prepare-index，记录下来用于下一步。</p>
</li>
<li>
<p dir="auto">提交配置变更：</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="./dongting-admin.sh commit-change --group-id 0 --prepare-index &lt;返回的prepare-index&gt;"><pre>./dongting-admin.sh commit-change --group-id 0 --prepare-index <span class="pl-k">&lt;</span>返回的prepare-index<span class="pl-k">&gt;</span></pre></div>
</li>
<li>
<p dir="auto">集群配置已更新，节点3已经从group中移除。你可以通过以下命令验证：</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="./dongting-admin.sh query-status --node-id 1 --group-id 0"><pre>./dongting-admin.sh query-status --node-id 1 --group-id 0</pre></div>
</li>
<li>
<p dir="auto">如果需要，可以删除节点3的定义（此操作应在所有节点1、2上都执行）：</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="./dongting-admin.sh remove-node --node-id 1 --remove-node-id 3"><pre>./dongting-admin.sh remove-node --node-id 1 --remove-node-id 3</pre></div>
</li>
</ol>
<div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">例2：为group增加一个node</h3><a id="user-content-例2为group增加一个node" class="anchor" aria-label="Permalink: 例2：为group增加一个node" href="#例2为group增加一个node"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">假设你有一个3节点的集群，节点ID为1、2、3，group ID为0，现在需要添加节点4。操作步骤如下：</p>
<ol dir="auto">
<li>
<p dir="auto">首先启动新节点4的服务器进程：</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="cd node4/bin
./start-dongting.sh"><pre><span class="pl-c1">cd</span> node4/bin
./start-dongting.sh</pre></div>
</li>
<li>
<p dir="auto">添加节点4的定义（此操作应在所有现有节点上执行）：</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="./dongting-admin.sh add-node --node-id 1 --add-node-id 4 --host theHost --port thePort"><pre>./dongting-admin.sh add-node --node-id 1 --add-node-id 4 --host theHost --port thePort</pre></div>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="./dongting-admin.sh add-node --node-id 2 --add-node-id 4 --host theHost --port thePort"><pre>./dongting-admin.sh add-node --node-id 2 --add-node-id 4 --host theHost --port thePort</pre></div>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="./dongting-admin.sh add-node --node-id 3 --add-node-id 4 --host theHost --port thePort"><pre>./dongting-admin.sh add-node --node-id 3 --add-node-id 4 --host theHost --port thePort</pre></div>
<p dir="auto">注意：port应该是新节点的复制端口，不是服务端口。</p>
</li>
<li>
<p dir="auto">确认节点4已添加：</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="./dongting-admin.sh list-nodes --node-id 1"><pre>./dongting-admin.sh list-nodes --node-id 1</pre></div>
</li>
<li>
<p dir="auto">准备配置变更，将成员从1、2、3改为1、2、3、4（prepare/commit/abort应该在leader上调用）：</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="./dongting-admin.sh prepare-change --group-id 0 --old-members 1,2,3 --new-members 1,2,3,4"><pre>./dongting-admin.sh prepare-change --group-id 0 --old-members 1,2,3 --new-members 1,2,3,4</pre></div>
<p dir="auto">这会返回一个prepare-index，记录下来用于下一步。</p>
</li>
<li>
<p dir="auto">提交配置变更：</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="./dongting-admin.sh commit-change --group-id 0 --prepare-index &lt;返回的prepare-index&gt;"><pre>./dongting-admin.sh commit-change --group-id 0 --prepare-index <span class="pl-k">&lt;</span>返回的prepare-index<span class="pl-k">&gt;</span></pre></div>
</li>
<li>
<p dir="auto">集群配置已更新，节点4已加入group。你可以通过以下命令验证：</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="./dongting-admin.sh query-status --node-id 1 --group-id 0"><pre>./dongting-admin.sh query-status --node-id 1 --group-id 0</pre></div>
</li>
</ol>
<p dir="auto">注意：新节点加入后会从leader同步所有数据，这可能需要一些时间，取决于数据量。现在既然新加了一个节点，那这个集群的投票quorum就变成了3了，
所以，生成环境上，建议新节点先作为observer加入，等数据同步完成后，再做一次变更将observer提升为member。</p>
<div class="markdown-heading" dir="auto"><h1 tabindex="-1" class="heading-element" dir="auto">进阶</h1><a id="user-content-进阶" class="anchor" aria-label="Permalink: 进阶" href="#进阶"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">将项目导入IDE</h2><a id="user-content-将项目导入ide" class="anchor" aria-label="Permalink: 将项目导入IDE" href="#将项目导入ide"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">将项目导入后，要设置 IDE，可以参考 <a href="/dtprj/dongting/blob/master/docs/developer_CN.md">开发指南</a>。</p>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">通过代码构建 raft 服务器</h2><a id="user-content-通过代码构建-raft-服务器" class="anchor" aria-label="Permalink: 通过代码构建 raft 服务器" href="#通过代码构建-raft-服务器"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">所有示例都在 <code>demos</code> 目录中。它们不需要配置，直接执行 <code>main</code> 方法即可运行。
建议在 IDE 中运行，以便更方便地设置断点和观察。</p>
<p dir="auto"><a href="/dtprj/dongting/blob/master/demos/src/main/java/com/github/dtprj/dongting/demos/advanced/cluster">cluster</a> 目录包含一个运行 3 节点 raft 集群的示例。
分别运行 <code>DemoServer1</code>、<code>DemoServer2</code> 和 <code>DemoServer3</code>，raft 集群通常会在一秒内就绪。
运行 <code>DemoClient</code>，它将发送 100 万次 put 和 get 请求，同时记录完成时间。
运行 <code>PeriodPutClient</code>，它每秒持续发送一个 put 请求，不会停止。
您可以随时关闭一个服务器，<code>PeriodPutClient</code> 将不受影响。
此外，执行 <code>ChangeLeader</code> 将 Raft leader 切换到指定节点。
正在运行的 <code>PeriodPutClient</code> 将完全不受影响。</p>
<p dir="auto"><a href="/dtprj/dongting/blob/master/demos/src/main/java/com/github/dtprj/dongting/demos/advanced/standalone">standalone</a> 目录包含一个运行单节点 raft 组的示例。</p>
<p dir="auto"><a href="/dtprj/dongting/blob/master/demos/src/main/java/com/github/dtprj/dongting/demos/advanced/embedded">embedded</a> 目录包含一个将 3 个服务器和 1 个客户端嵌入到单个进程中的示例。</p>
<p dir="auto"><a href="/dtprj/dongting/blob/master/demos/src/main/java/com/github/dtprj/dongting/demos/advanced/configchange">configchange</a> 目录包含在运行时动态更改 Raft 成员的示例。
首先，运行 <code>ConfigChangeDemoServer1</code>、<code>ConfigChangeDemoServer2</code>、<code>ConfigChangeDemoServer3</code> 和 <code>ConfigChangeDemoServer4</code>。
默认情况下，将启动包含节点 1、2、3 的 Raft 组。
执行 <code>ChangeTo234Client</code> 将 Raft 成员更改为节点 2、3、4。
执行 <code>ChangeTo123Client</code> 将 Raft 成员恢复为节点 1、2、3。</p>
<p dir="auto"><a href="/dtprj/dongting/blob/master/demos/src/main/java/com/github/dtprj/dongting/demos/advanced/multiraft">multiraft</a> 目录包含运行多 raft 的示例，通常用于分片或动态分片。
运行 <code>MultiRaftDemoServer1</code>、<code>MultiRaftDemoServer2</code> 和 <code>MultiRaftDemoServer3</code> 默认启动两个（静态）raft 组，ID 为 101 和 102。
执行 <code>PeriodPutClient</code> 将每秒向 raft 组 101、102 和 103 发送一个 put 请求。
由于组 103 不存在，每秒将有两个成功的 put 操作和一个失败的操作。
运行 <code>AddGroup103Demo</code> 在运行时添加 raft 组 103，之后 <code>PeriodPutClient</code> 将每秒输出三个成功的操作。
执行 <code>RemoveGroup103Demo</code> 将删除 raft 组 103。</p>
<p dir="auto"><a href="/dtprj/dongting/blob/master/demos/src/main/java/com/github/dtprj/dongting/demos/advanced/watch">watch</a> 目录包含使用客户端监控指定 key 变化的示例，同时演示如何监控目录，客户端将收到其子节点任何变化的通知。</p>
<p dir="auto"><a href="/dtprj/dongting/blob/master/demos/src/main/java/com/github/dtprj/dongting/demos/advanced/ttl">ttl</a> 目录包含使用客户端设置带 TTL 的 key 的示例，TTL 过期后，key 将被自动删除。</p>
<p dir="auto"><a href="/dtprj/dongting/blob/master/demos/src/main/java/com/github/dtprj/dongting/demos/advanced/lock">lock</a> 目录包含使用分布式锁的示例。分布式锁可以手动使用 tryLock/unlock 操作，也可以使用 tryLock/updateLease 完全自动化（可用于业务代码中的 leader 选举）。</p>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">使用 JLink 裁剪 JRE</h2><a id="user-content-使用-jlink-裁剪-jre" class="anchor" aria-label="Permalink: 使用 JLink 裁剪 JRE" href="#使用-jlink-裁剪-jre"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">使用 <code>jlink</code> 创建裁剪后的精简 JRE（约 30-45 MB）。参见 <a href="/dtprj/dongting/blob/master/docs/jlink-cut_CN.md">jlink-cut 指南</a>。</p>
<div class="markdown-heading" dir="auto"><h1 tabindex="-1" class="heading-element" dir="auto">关于我</h1><a id="user-content-关于我" class="anchor" aria-label="Permalink: 关于我" href="#关于我"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto"><a href="https://weibo.com/dtprj" rel="nofollow">https://weibo.com/dtprj</a></p>
<p dir="auto">微信公众号：</p>
<p dir="auto"><a target="_blank" rel="noopener noreferrer" href="/dtprj/dongting/blob/master/docs/imgs/qrcode_wechat.jpg"><img src="/dtprj/dongting/raw/master/docs/imgs/qrcode_wechat.jpg" alt="公众号" style="max-width: 100%;"></a></p>
</article></div><div class="d-none"></div></section></div></div></div> </div> <!-- --> </div></div></div></div></div></div><div class="ScrollMarksContainer-module__scrollMarksContainer__Eu7uU" id="find-result-marks-container"></div><div class="d-none"></div><div class="d-none"></div></div> <!-- --> <!-- --> </div>
</react-app>




  </div>

</turbo-frame>

    </main>
  </div>

  </div>

          <footer class="footer f6 color-fg-muted color-border-subtle tmp-pt-7 tmp-pb-6 p-responsive" role="contentinfo"  >
  <h2 class='sr-only'>Footer</h2>

  


  <div class="d-flex flex-justify-center flex-items-center flex-column-reverse flex-lg-row flex-wrap flex-lg-nowrap">
    <div class="d-flex flex-items-center flex-shrink-0 mx-2">
      <a aria-label="GitHub Homepage" class="footer-octicon mr-2" href="https://github.com">
        <svg aria-hidden="true" data-component="Octicon" height="24" viewBox="0 0 24 24" version="1.1" width="24" data-view-component="true" class="octicon octicon-mark-github">
    <path d="M10.226 17.284c-2.965-.36-5.054-2.493-5.054-5.256 0-1.123.404-2.336 1.078-3.144-.292-.741-.247-2.314.09-2.965.898-.112 2.111.36 2.83 1.01.853-.269 1.752-.404 2.853-.404 1.1 0 1.999.135 2.807.382.696-.629 1.932-1.1 2.83-.988.315.606.36 2.179.067 2.942.72.854 1.101 2 1.101 3.167 0 2.763-2.089 4.852-5.098 5.234.763.494 1.28 1.572 1.28 2.807v2.336c0 .674.561 1.056 1.235.786 4.066-1.55 7.255-5.615 7.255-10.646C23.5 6.188 18.334 1 11.978 1 5.62 1 .5 6.188.5 12.545c0 4.986 3.167 9.12 7.435 10.669.606.225 1.19-.18 1.19-.786V20.63a2.9 2.9 0 0 1-1.078.224c-1.483 0-2.359-.808-2.987-2.313-.247-.607-.517-.966-1.034-1.033-.27-.023-.359-.135-.359-.27 0-.27.45-.471.898-.471.652 0 1.213.404 1.797 1.235.45.651.921.943 1.483.943.561 0 .92-.202 1.437-.719.382-.381.674-.718.944-.943"></path>
</svg>
</a>
      <span>
        &copy; 2026 GitHub,&nbsp;Inc.
      </span>
    </div>

    <nav aria-label="Footer">
      <h3 class="sr-only" id="sr-footer-heading">Footer navigation</h3>

      <ul class="list-style-none d-flex flex-justify-center flex-wrap mb-2 mb-lg-0" aria-labelledby="sr-footer-heading">


          <li class="mx-2">
            <a data-analytics-event="{&quot;category&quot;:&quot;Footer&quot;,&quot;action&quot;:&quot;go to Terms&quot;,&quot;label&quot;:&quot;text:terms&quot;}" href="https://docs.github.com/site-policy/github-terms/github-terms-of-service" data-view-component="true" class="Link--secondary Link">Terms</a>
          </li>

          <li class="mx-2">
            <a data-analytics-event="{&quot;category&quot;:&quot;Footer&quot;,&quot;action&quot;:&quot;go to privacy&quot;,&quot;label&quot;:&quot;text:privacy&quot;}" href="https://docs.github.com/site-policy/privacy-policies/github-privacy-statement" data-view-component="true" class="Link--secondary Link">Privacy</a>
          </li>


            <li class="mx-2">
              <a data-analytics-event="{&quot;category&quot;:&quot;Footer&quot;,&quot;action&quot;:&quot;go to security&quot;,&quot;label&quot;:&quot;text:security&quot;}" href="https://github.com/security" data-view-component="true" class="Link--secondary Link">Security</a>
            </li>

            <li class="mx-2">
              <a data-analytics-event="{&quot;category&quot;:&quot;Footer&quot;,&quot;action&quot;:&quot;go to status&quot;,&quot;label&quot;:&quot;text:status&quot;}" href="https://www.githubstatus.com/" data-view-component="true" class="Link--secondary Link">Status</a>
            </li>

          <li class="mx-2">
            <a data-analytics-event="{&quot;category&quot;:&quot;Footer&quot;,&quot;action&quot;:&quot;go to community&quot;,&quot;label&quot;:&quot;text:community&quot;}" href="https://github.community/" data-view-component="true" class="Link--secondary Link">Community</a>
          </li>

          <li class="mx-2">
            <a data-analytics-event="{&quot;category&quot;:&quot;Footer&quot;,&quot;action&quot;:&quot;go to docs&quot;,&quot;label&quot;:&quot;text:docs&quot;}" href="https://docs.github.com/" data-view-component="true" class="Link--secondary Link">Docs</a>
          </li>

          <li class="mx-2">
            <a data-analytics-event="{&quot;category&quot;:&quot;Footer&quot;,&quot;action&quot;:&quot;go to contact&quot;,&quot;label&quot;:&quot;text:contact&quot;}" href="https://support.github.com?tags=dotcom-footer" data-view-component="true" class="Link--secondary Link">Contact</a>
          </li>

          
<li class="mx-2" >
  <cookie-consent-link>
    <button
      type="button"
      class="Link--secondary underline-on-hover border-0 p-0 color-bg-transparent"
      data-action="click:cookie-consent-link#showConsentManagement"
      data-analytics-event="{&quot;location&quot;:&quot;footer&quot;,&quot;action&quot;:&quot;cookies&quot;,&quot;context&quot;:&quot;subfooter&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;label&quot;:&quot;cookies_link_subfooter_footer&quot;}"
    >
      Manage cookies
    </button>
  </cookie-consent-link>
</li>

  <li class="mx-2">
    <cookie-consent-link>
      <button
        type="button"
        class="Link--secondary underline-on-hover border-0 p-0 color-bg-transparent text-left"
        data-action="click:cookie-consent-link#showConsentManagement"
        data-analytics-event="{&quot;location&quot;:&quot;footer&quot;,&quot;action&quot;:&quot;dont_share_info&quot;,&quot;context&quot;:&quot;subfooter&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;label&quot;:&quot;dont_share_info_link_subfooter_footer&quot;}"
      >
        Do not share my personal information
      </button>
    </cookie-consent-link>
  </li>

      </ul>
    </nav>
  </div>
</footer>



    <ghcc-consent id="ghcc" class="position-fixed bottom-0 left-0" style="z-index: 999999"
      data-locale="en"
      data-initial-cookie-consent-allowed=""
      data-cookie-consent-required="true"
    ></ghcc-consent>




  <div id="ajax-error-message" class="ajax-error-message flash flash-error" hidden>
    <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-alert">
    <path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path>
</svg>
    <button type="button" class="flash-close js-ajax-error-dismiss" aria-label="Dismiss error">
      <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-x">
    <path d="M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l3.22 3.22a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-3.22 3.22a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z"></path>
</svg>
    </button>
    You can’t perform that action at this time.
  </div>

    <template id="site-details-dialog">
  <details class="details-reset details-overlay details-overlay-dark lh-default color-fg-default hx_rsm" open>
    <summary role="button" aria-label="Close dialog"></summary>
    <details-dialog class="Box Box--overlay d-flex flex-column anim-fade-in fast hx_rsm-dialog hx_rsm-modal">
      <button class="Box-btn-octicon m-0 btn-octicon position-absolute right-0 top-0" type="button" aria-label="Close dialog" data-close-dialog>
        <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-x">
    <path d="M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l3.22 3.22a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-3.22 3.22a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z"></path>
</svg>
      </button>
      <div class="octocat-spinner tmp-my-6 js-details-dialog-spinner"></div>
    </details-dialog>
  </details>
</template>

    <div class="Popover js-hovercard-content position-absolute" style="display: none; outline: none;">
  <div class="Popover-message Popover-message--bottom-left Popover-message--large Box color-shadow-large" style="width:360px;">
  </div>
</div>

    <template id="snippet-clipboard-copy-button">
  <div class="zeroclipboard-container position-absolute right-0 top-0">
    <clipboard-copy aria-label="Copy code to clipboard" class="ClipboardButton btn js-clipboard-copy m-2 p-0" data-copy-feedback="Copied!" data-tooltip-direction="w">
      <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-copy js-clipboard-copy-icon m-2 tmp-m-2">
    <path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path>
</svg>
      <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-check js-clipboard-check-icon color-fg-success d-none m-2 tmp-m-2">
    <path d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z"></path>
</svg>
    </clipboard-copy>
  </div>
</template>
<template id="snippet-clipboard-copy-button-unpositioned">
  <div class="zeroclipboard-container">
    <clipboard-copy aria-label="Copy code to clipboard" class="ClipboardButton btn btn-invisible js-clipboard-copy m-2 p-0 d-flex flex-justify-center flex-items-center" data-copy-feedback="Copied!" data-tooltip-direction="w">
      <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-copy js-clipboard-copy-icon">
    <path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path>
</svg>
      <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-check js-clipboard-check-icon color-fg-success d-none">
    <path d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z"></path>
</svg>
    </clipboard-copy>
  </div>
</template>




    </div>
    <div id="js-global-screen-reader-notice" class="sr-only mt-n1" aria-live="polite" aria-atomic="true" ></div>
    <div id="js-global-screen-reader-notice-assertive" class="sr-only mt-n1" aria-live="assertive" aria-atomic="true"></div>
  </body>
</html>

