### 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)

"""Get a flat set of relative IDs, upstream or downstream.
        Will recurse each relative found in the direction specified.
        :param upstream: Whether to look for upstream or downstream relatives.
        """
        dag = self.get_dag()
        if not dag:
            return set()
        relatives: set[str] = set()
        
        
        
        
        task_ids_to_trace = self.get_direct_relative_ids(upstream)
        while task_ids_to_trace:
            task_ids_to_trace_next: set[str] = set()
            for task_id in task_ids_to_trace:
                if task_id in relatives:
                    continue
                task_ids_to_trace_next.update(dag.task_dict[task_id].get_direct_relative_ids(upstream))
                relatives.add(task_id)
            task_ids_to_trace = task_ids_to_trace_next
        return relatives
    def get_flat_relatives(self, upstream: bool = False) -> Collection[Operator]:
        """Get a flat list of relatives, either upstream or downstream."""
        dag = self.get_dag()
        if not dag:
            return set()
        return [dag.task_dict[task_id] for task_id in self.get_flat_relative_ids(upstream=upstream)]
    def get_upstreams_follow_setups(self) -> Iterable[Operator]: