### Apache Airflow version

main (development)

### What happened

Currently `extra_links` is a cached property that returns a list without any order since set is used. Since we have 3 links per operator this order gets shuffled during webserver restarts as reported by users. It would be good to have this sorted so that the order is predictable. This is already done in extra_links Airflow API output

https://github.com/apache/airflow/blob/d7899ecfafb20cc58f8fb43e287d1c6778b8fa9f/airflow/models/abstractoperator.py#L470-L472

https://github.com/apache/airflow/blob/d7899ecfafb20cc58f8fb43e287d1c6778b8fa9f/airflow/api_connexion/endpoints/extra_link_endpoint.py#L75-L78

### What you think should happen instead

extra link order should be predictable

### How to reproduce

1. Create an operator with 3 or more extra links.
2. Render the links in UI. 
3. Restart the webserver and check the extra link order.

### Operating System

Ubuntu

### Versions of Apache Airflow Providers

_No response_

### Deployment

Other

### Deployment details

_No response_

### Anything else

_No response_

### Are you willing to submit PR?

- [X] Yes I am willing to submit a PR!

### Code of Conduct

- [X] I agree to follow this project's [Code of Conduct](https://github.com/apache/airflow/blob/main/CODE_OF_CONDUCT.md)

#
#
#
from __future__ import annotations
import datetime
import inspect
from functools import cached_property
from typing import TYPE_CHECKING, Any, Callable, ClassVar, Collection, Iterable, Iterator, Sequence
from sqlalchemy import select
from airflow.compat.functools import cache
from airflow.configuration import conf
from airflow.exceptions import AirflowException
from airflow.models.expandinput import NotFullyPopulated
from airflow.models.taskmixin import DAGNode, DependencyMixin
from airflow.template.templater import Templater
from airflow.utils.context import Context
from airflow.utils.log.secrets_masker import redact
from airflow.utils.session import NEW_SESSION, provide_session
from airflow.utils.sqlalchemy import skip_locked, with_row_locks
from airflow.utils.state import State, TaskInstanceState
from airflow.utils.task_group import MappedTaskGroup
from airflow.utils.trigger_rule import TriggerRule
from airflow.utils.types import NOTSET, ArgNotSet
from airflow.utils.weight_rule import WeightRule
TaskStateChangeCallback = Callable[[Context], None]
if TYPE_CHECKING:
    import jinja2