Custom service adds another entry point in the traces. If you create a custom service (Java/Net/PHP/Go), a new service entity will be created and each code execution at the defined code places will result into requests on the newly defined service. It's a good method of how to split application code into smaller, individually measurable parts or for reverse engineering.
A simple example can be defining an entry point in your custom code - if you don't use any of the common frameworks Dynatrace supports and recognizes. In this case, you might need to define an entry point, so that each code execution of this code will be traced and measured. Other common use case is to split a long-running code into smaller pieces, so you can trace and measure them individually.
Request attributes, on the other way, add some extra information to existing service requests. A request attribute can hold different data types captured either from the request data (HTTP headers for example) or from code directly (method arguments). This information is then attached to the request and it can be used for various purposes such as splitting requests or analyzing them.
See help for examples and further explanation of request attributes.
Hope this helps.