A payment flow that exposes the problems
A customer places an order and pays right away. To finish the order, the system needs to do four things: mark the order as paid, reduce inventory for the purchased items, tell the warehouse to ship, and add loyalty points for the purchase. That means four services: order, inventory, warehouse, and points. The order service is the one coordinating the flow. After it updates the order status, it calls inventory, warehouse, and points in turn.

On a diagram this is a tidy set of arrows. In production, each arrow crosses a process boundary. The order service must find the other services, make remote calls, choose among multiple instances, survive failures, and give external clients a single entry point. Spring Cloud's core components fit into those gaps.

Eureka: finding a service instance
The order service cannot call inventory just because it knows a service name. It needs a host and a port. Eureka is the registry that supplies that information. Every service includes a Eureka Client. When a service starts, its client registers the service's address and port with Eureka Server. Eureka Server keeps a registry of available service instances.
The order service also has a Eureka Client. It can fetch the registry from Eureka Server and keep a local cache. When the order service needs inventory, it asks its local Eureka Client for an inventory instance, gets an address, and sends the request there. Warehouse and points calls work the same way.

So the division is straightforward: Eureka Client registers the service and can fetch the registry; Eureka Server holds the registry of service locations.
Feign: making a remote call look like a method call
Knowing the address is not enough. A hand-written remote call has to open a connection, build the request, serialize the payload, send it, read the response, parse it, and handle errors. In a service with several downstream dependencies, that boilerplate spreads everywhere. A raw implementation can quickly become a wall of repetitive code.

Feign replaces that boilerplate with an annotated interface.

The interface does not need an implementation in the application. Feign creates a dynamic proxy for an interface annotated with @FeignClient. When application code calls a method on that interface, the call goes to the proxy. The proxy reads annotations such as @RequestMapping to work out the target service and path, constructs the URL, sends the request, and parses the response. The connection handling and request plumbing stay behind the proxy.

Ribbon: choosing one instance among many
Now suppose inventory service runs on five machines, for example 192.168.169:9000, 192.168.170:9000, 192.168.171:9000, 192.168.172:9000, and 192.168.173:9000. Feign still needs to know which instance to call. Ribbon handles that selection. It is a client-side load balancer. It gets the service registry information through Eureka Client, so it can see all inventory instances. By default it uses Round Robin: request one goes to the first machine, request two to the second, and so on; after the fifth, the next request returns to the first. Feign then builds the request for the chosen instance.

Hystrix: keeping one bad dependency from sinking the caller
A microservice system has many dependencies, and some of them will fail. Take the order service with a limited request-handling pool of 100 threads. If points service becomes unhealthy and every call waits several seconds before timing out, a burst of traffic can leave all 100 order-service threads waiting on points. The order service then has no thread left to handle anything else. Callers see it as down even though inventory and warehouse may be healthy. That chain reaction is the service avalanche.

The business flow also shows why this is unacceptable. Once inventory is reduced and the warehouse is notified, the order can proceed. Points can be reconciled later if the points service is unavailable. Hystrix addresses this with isolation, circuit breaking, and fallback.
Isolation uses small thread pools per dependency. Calls to inventory use one pool, calls to warehouse another, and calls to points another. If points fails, its pool fills, but inventory and warehouse calls can still run. Circuit breaking stops repeated waiting. After enough failures or timeouts, Hystrix short-circuits calls to points for a window such as five minutes, so callers get an immediate failure instead of blocking on the network. Fallback gives the failed call somewhere to go. For points, the order service can write a database record saying that a given user should receive a certain number of points, then reconcile those records when points service recovers.

Zuul: one front door for external clients
The last piece is the gateway. A backend may contain hundreds of services. A browser, mobile app, or mini program should not need to know every service name and host. Zuul is the API gateway that routes external requests. Clients send traffic to the gateway, and the gateway forwards each request to the appropriate backend service based on request characteristics. Putting a gateway in front also gives a central place for cross-cutting concerns such as fallback, rate limiting, authentication and authorization, and security.

Taken together, the components answer different questions in the same call path. Eureka provides the registry and discovery data. Ribbon selects an instance from that data. Feign turns an interface call into an HTTP request to the selected instance. Hystrix wraps the call with isolation, circuit breaking, and fallback. Zuul gives external clients a single entry point. The order-payment example is small, but it shows why these pieces are usually discussed as a set: each one solves a problem that appears as soon as the previous one is in place.