针对多个提供商(小型服务)的服务的Spring Boot设计



我正在创建一个Spring启动应用程序,我需要帮助设计我的服务。我将以自行车服务为例。让我们考虑一下这些端点:

/api/bike/v1/info
/api/bike/v1/find

端点将调用Bike服务,该服务可能会调用不同的提供商,如BikeProvider1、BikeProvider2等。提供程序将在应用程序的属性中指定,这允许在不重新启动应用程序的情况下更改提供程序。

结构示例

+ bike
+ Client
- BikeRequest.java
- BikeResponse.java
+ domain
- Providers.java //providers enums
- ServiceType.java // service type enums
+ Exceptions
+ Controller
- BikeController.java
+ Service
+ Providers
- BikeProviderService.java //interface
- BikeProbider1ProviderService.java // implements interface
- BikeProbider2ProviderService.java // implements interface
- BikeService.java //interface
- BikeServiceImpl.java
Properties.file
bike.provider.primary: BikeProvider1
bike.provider.fallback: BikeProvider2

我还认为,在未来,我希望有两个提供商,一个作为后备,以防第一个提供商没有响应。但现在,我只会使用初级。

我目前的设计是这样的,我有我的服务类型和提供者的枚举类。在BikeServiceImpl.java中,我动态地找到我需要的提供者并进行调用。

*******************ENUM CLASSES **************************
public enum Provider {
BIKEPROVIDER1 ("BikeProvider1"),
BIKEPROVIDER2  ("BikeProbider2"),
private String name;
Provider(final String name) {
this.name = name;
}
}
public enum ServiceType {
BIKEINFORMATION ("BikeInformation"),
FINDBIKE ("FindBike");
private String type;
ServiceType(final String name) {
this.type = name;
}
}

*****************BikeProviderService interface**************************
// this is the interface that all providers will implement
public interface BikeProviderService {
Optional<Bike> getBikeInformation(BikeRequest bike);
Optional<Bike> findBike(BikeRequest bike);
}
*****************BikeServiceImpl**************************
public class BikeServiceImpl implements BikeService {
// I autowired providers
@Autowired
private BikeProvider1 bikeProvider1;
@Autowired
private BikeProvider2 bikeProvider2;
//Get the provider from the properties
@Value("${bike.provider.primary:}")
private String primaryProvider;
// This is the call from controller endpoint /api/bike/v1/info 
@Override
public Bike findBikeInformation(BikeRequest bike, ServiceType serviceType) throws ServiceProviderException, BikeNotFoundException {
return executeBikeInformationWithProvider(primaryProvider, bike, serviceType);
}
private Bike executeBikeInformationWithProvider(String provider, BikeRequest bike, ServiceType serviceType) throws ServiceProviderException, BikeNotFoundException {
// find the providers based on the property value
Optional<BikeProviderService> bikeProviderService = providerImplementation(provider);
Optional<Bike> bikeResponse;
if (BikeProviderService.isPresent()) {
try {
bikeResponse = bikeProviderService.get().getBikeInformation(bike);
if (bikeResponse.isPresent()) {
LogCacheService.logRequest(bikeResponse, provider, serviceType.getType());
return bikeResponse.get();
}
} catch (Exception ex) {
throw new ServiceProviderException("message");
}
}
throw new BikeNotFoundException("Bike Not found");
}
// This is the call from controller endpoint /api/bike/v1/find 
@Override
public Bike findBike(BikeRequest bike, ServiceType serviceType) throws ServiceProviderException, BikeNotFoundException {
return executeFindBikeWithProvider(primaryProvider, bike, serviceType) ;
}
private Bike executeFindBikeWithProvider(String provider, BikeRequest bike, ServiceType serviceType) throws ServiceProviderException, BikeNotFoundException {
// find the providers based on the property value
Optional<BikeProviderService> bikeProviderService = providerImplementation(provider);
Optional<Bike> bikeResponse;
if (bikeProviderService.isPresent()) {
try {
bikeResponse = bikeProviderService.get().findBike(bike);
if (bikeResponse.isPresent()) {
LogCacheService.logRequest(bike, provider, serviceType.getType());
return bikeResponse.get();
}
} catch (Exception ex) {
throw new ServiceProviderException("message");
}
}
throw new BikeNotFoundException("Bike Not found");
}
private Optional<BikeProviderService> providerImplementation(String bikeProvider) {
Provider provider = null;
try {
provider = Provider.valueOf(bikeProvider.toUpperCase());
} catch (IllegalArgumentException | NullPointerException ex) {
log.error("Bike:: invalid Provider {}" , bikeProvider);
}
if (provider != null) {
switch (provider) {
case BIKEPROVIDER1:
return Optional.ofNullable(bikeProvider1);
case BIKEPROVIDER2:
return Optional.ofNullable(bikeProvider2);
default:
return Optional.empty();
}
}
return Optional.empty();
}

我真的很努力不重复代码,但我担心我会让这个代码变得过于复杂和难以维护。现在添加一个新的提供者:

1.- add to the enum provider class
2.- add to the switch statement
3.- add the service and @AutoWire the class

我尝试了工厂模式,但不想每次都创建一个新的提供者实例。并且接近于我所具有的CCD_ 1方法。

此外,我创建了execute/helper方法,因为正如我之前提到的,我希望将来使用fallback.Provider执行回退或恢复方法。示例如果findBike失败,我可以使用回退调用executeFindBikeWithProvider。executeFindBikeWithProvider(**fallbackProvider**, bike, serviceType)

谢谢。如果你需要更多的细节,请告诉我。

这不是Spring@Profile的一个好用例吗?

https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html

https://www.baeldung.com/spring-profiles

您可以使用不同的配置文件更改运行时配置,而不是使用属性文件中的提供程序列表。这将有效地允许您根据您的配置文件设置忽略或启用您的提供商。

这样,添加新提供程序只需要实现新提供程序并将其与配置文件关联。

最新更新