I'm trying to use Spring Cloud to consume a generic REST service from a Cloud Foundry app.
This service is created using Spring Boot, as follows:
package com.something;
#RestController
public class DemoServiceController {
#RequestMapping("/sayHi")
public String sayHi() {
return "Hello!";
}
}
This works fine - I can access http://www.example.com/srv/demo/sayHi and get "Hello!" back.
Next, I created a user-provided service instance using the CF-CLI and bound it to my app. I can now see the bound service in VCAP_SERVICES.
cf cups my-demo-service -p '{"url":"http://www.example.com/srv/demo/"}'
cf bs my-demo-app my-demo-service
Next, as described here, I added this bean to my app's Spring config, with the connector-type set to my original controller (I have a reference to it as well).
<cloud:service id="myDemoService"
service-name="my-demo-service"
connector-type="com.something.DemoServiceController"
/>
Now when I auto-wire "myDemoService" into my app,
#Autowired
private DemoController myDemoService;
I get an error:
No services of the specified type could be found.
I've made sure to include all required dependencies, including spring-cloud-spring-service-connector and spring-cloud-cloudfoundry-connector.
What's going wrong here? Am I giving the wrong bean parameters? Any help is much appreciated.
Spring Cloud Connectors won't know what to do with this service, as each supported service must be of a known type (MySQL, Postgres, Redis, MongoDB, RabbitMQ, etc). Setting the connector-type to your Controller class won't do what you want.
What you will need to do is to create a custom Connectors extension. Here's an example of a project that does that: https://github.com/cf-platform-eng/spring-boot-cities.
Related
I am currently building an application that connects to a database on Spanner. The end goal of the application is to be able to connect to multiple databases (and possibly instances) so it can pull data using a GraphQL implementation. I am currently using Spring Cloud GCP Starter and Spring Cloud GCP Starter Data Spanner Maven packages to handle the configuration and data mapping. The Spring Cloud GCP Starter asks me to set up these lines in application.properties:
spring.cloud.gcp.spanner.instance-id=blah
spring.cloud.gcp.spanner.database=blah
spring.cloud.gcp.project-id=blah
Currently the application is set up to have models for each table, a repository (using SpannerRepository), and a controller.
The issue is I haven't been able to figure out how to change the configuration from the initial values when the application is run. Has anyone run into this and figured it out, or is it a limitation of my current implementation in Spring Cloud GCP Starter and I should look for different solution?
What I have tried:
Tried finding someone with the same issue online, nothing similar that I can find currently
Tried looking how to use/change the low level implementations things like SpannerTemplate that the autoconfiguration creates, but wasn't able to figure out how to change/use them
Tried finding a way to change application.properties and reloading during runtime, but after some research this seemed like a horrible idea
Any help would be greatly appreciated, thank you!
I finally found it!
Documentation
By creating a custom bean for DatabaseIdProvider you can set the projectId, instanceId, and database to whatever you would like. Confirmed to be working the SpannerRepository implementation as well.
If anyone else finds this, here is an example of how you would create your own implementation (how you go about feeding it a new database name is up to you):
#Bean
public DatabaseIdProvider databaseIdProvider() {
return () -> DatabaseId.of("projectId", "instanceId", "databaseName");
}
The DatabaseIdProvider interface extends Supplier<DatabaseId>. DatabaseId has a method (of) that creates the new DatabaseId that you need to connect to a different database/instance.
I have multiple microservices which communicates with each other through REST calls.
I have used spring boot and spring rest and have configured the URLS of the rest end points in application.properties file.
Now the problems is if the URL for one end point changes then I to have to manually modify all the property files of the services which are calling that particular end point which has got changed.
Is there a workaround for this so that the URLS can be somehow placed in a centralized location so that any modification does not impacts the other services which are using it.
You can use spring-cloud to achieve this. Usual way used in spring-cloud is by configuring the required properties in a git repo. And then those properties can be accessed by any micro-service you want with minimal configurations. You can refer projects in this repo
limits-services acts as a client that needs certain properties those are configured in spring-cloud-config-server. Hope this helps.
In case with microservices you can use Spring Cloud Config (Spring Cloud Config, Spring Cloud Config Server). It's very usefull and you can update your configuration at runtime.
Spring Cloud Config provides server and client-side support for externalized configuration in a distributed system. With the Config Server you have a central place to manage external properties for applications across all environments. The concepts on both client and server map identically to the Spring Environment and PropertySource abstractions, so they fit very well with Spring applications, but can be used with any application running in any language. As an application moves through the deployment pipeline from dev to test and into production you can manage the configuration between those environments and be certain that applications have everything they need to run when they migrate.
As others have mentioned you can use Spring Cloud Config Server to remotly load your application configuration. All you need is git repository containing your configuration.
Spring cloud configuration supporst Git, database as your store for configuration.
Idea is to create an spring-boot app that can provide configuration to other applications.
#SpringBootApplication
#EnableConfigServer
public class ConfigServer {
public static void main(String[] args) {
SpringApplication.run(ConfigServer.class, args);
}
}
You can configurae port and provide your git repository using key spring.cloud.config.server
server.port: 8888
spring.cloud.config.server.git.uri: file://${user.home}/config-repo
At client side, if you have spring-config in your classpath, application will try to connect to an application runnign at port 8888 to retrieve configuration.
More information can be found here.
may put configuration inside a database.
after that need have one centralize cache service that used by other services, can be .jar service,
then the values can be load inside a cache class in this service,
then in the front end side need have update button for updating the cache after modify the URL value in the database, so then all impacted services can use new value.
and also to be easier may have stand alone UI for update those configuration rather than updating database directly.
You can use Microconfig.IO to manage your service configuration and it's placeholders functionality to reference configuration values of certain services from others. So in your case you configure your deploy url in your server and put placeholders on it in your clients. This allows you to edit value only in one place and then everyone who depend on it will get it automatically.
I have a simple CRUD API for a Spring Boot service based on this example.
The API is provided with the following definition:
#RepositoryRestResource
public interface PersonRepository extends CrudRepository<Person, Long> {}
From a curl client, I can get the list of users using the following implicit GET command: curl localhost:8080/persons.
From Java's perspective we just called the remote equivalent of Iterable<Person> persons = personRepository.findAll().
The question is whether spring can auto-create a remote implementation (client) of the same API without the need to manually type paths or know the method type (like needed when using RestTemplate)?
Example of an such a client usage:
PersonRepository personRepository = new PersonRepositoryClient(http://localhost:8080");
Iterable<Person> persons = personRepository.findAll(); // findAll does a remote call as if it was local.
How can I get to such a client "generated" implementation"?
* I know that passing the URL directly contradicts the service discovery architecture, but it's just for the sake of understanding how to get to a well defined client. The same client can be used within a service discovery architecture, by discovering the URL instead of manually setting it.
Try Feign Client. It provides autogeneration of HTTP request handlers for your Java interfaces. To make it work, you need to include Spring Cloud, standard Spring framework doesn't have this feature.
I want to replace Basic Authentication for Spring Cloud Config Server with oAuth implementation. Let's leave Config Server alone for now and focus on changes for Config Client. Obviously I don't want to write my own implementation for whole thing, but instead execute my own logic and fallback on standard Config Client. Also I have to pack my changes into library since I will use it in multiple micro-services.
Long story short I want to achieve following:
1a. Create custom Starter which will contain Spring Cloud Config Client as dependency. Is it even doable or necessary?
or
1b. Create custom Starter with only my custom logic which will be executed before Spring Cloud Config Client. In this case each micro-service will have Spring Cloud Config Client and custom Starter as dependencies. How can I manage execution order and inject custom logic results into Config Client?
2.Introduce new bootstrap settings. e.g. spring.cloud.config.custom.username and spring.cloud.config.custom.password (Optional).
3.Introduce custom annotation for custom Starter. e.g. #enableCustomConfigClient (Optional).
I started with building custom Starter with following code in /resources/META-INF/spring.factories:
# Bootstrap components
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.example.greeter.config.ConfigClientBootstrapConfiguration
But this code invoked after profile is set, not the first thing like Config Client does.
Any suggestions and especially code samples are appreciated. Thanks!
Posting approach I chose for future reference.
Create new package which will be executed on top of / before Spring Cloud Config Client. Two main features here:
Create file src/main/resources/META-INF/spring.factories with org.springframework.cloud.bootstrap.BootstrapConfiguration={YOUR_CLASS}
In {YOUR_CLASS} apply custom logic. Don't forget to use #org.springframework.core.annotation.Order({YOUR_PRECEDENCE}) and fact that Ordered.LOWEST_PRECEDENCE will be executed first
Build jar from previous step and include it into your project (as local file or via artifactory)
Add Custom logic to Spring Cloud Config Server so it can use JWT.
Working example is here: https://github.com/ka4ok85/spring-cloud-config-client-jwt
I'm trying to make a basic project using spring cloud with the netflix addons such as Hystrix, Eureka and Ribbon to learn how this works. The project I'm trying to make is a simple message server that will keep messages. And a message-client that will just ask the server for a message, and I want to use the auto discovery client for this, or the RestTemplate discovery. But I can't get either to work.
I have the following structure:
message-client (eureka client)
message-server (eureka client)
configuration-service (config server)
discovery-service (eureka server)
What I currently do is I start up the configuration-service, and expose the application.yml details to all of these "apps/clients" when they are connecting by the following structure:
config-service\src\main\resources\config\appname.yml
app\src\main\resources\bootstrap.yml (contains the appname and url to cloud config)
This is working just fine, and my apps start up on the port they receive from the config server, as well as they all connect to my eureka server, and all of them are visible there. As well as the Hystrix failover is also working, not that it is related to this but it tells me that it can't be completely wrong then.
But here comes my confusion...
When using the #Autowired annotation in my service class (#Service annotated) inside my client module, I get a discoveryClient object, but I am unable to find any other services using that one.
Message Client - boot class:
#EnableAutoConfiguration
#EnableHystrix
#EnableEurekaClient
#ComponentScan("cloud.rest.resources, spring.cloud.client")
public class ClientBoot {
public static void main(String[] args) {
SpringApplication.run(ClientBoot.class, args);
}
}
Message Client - REST Resource:
#RestController
public class MessageResource {
#Autowired
private MessageClient messageClient;
#RequestMapping(value = "/message/{client}", method = RequestMethod.GET)
public Message getMessage(#PathVariable String client) {
return messageClient.getMessage(client);
}
}
Message Client - MessageClient:
#Service
public class RestMessageClient implements MessageClient {
#Autowired
private DiscoveryClient discoveryClient;
#Autowired
private RestTemplate restTemplate;
#Override
public Message getMessage(String client) {
return restTemplate.getForObject(String.format("http://message-server/message/%s", client), Message.class);
}
}
My message server boot class that is holding the messages has the same annotations as my client one.
And as I said, my service class are unable to find anything..
Which leads me to all of my questions:
What is required to actually use ribbon load balancer?
Do I have to use ribbon to be able to use the "auto discovery", I thought not but now I'm just confused.
From what I've understood, when using EnableEurekaClient I should not need to use the EnableDiscoveryClient as well?
Can I change the yml files on my config-server for the clients in runtime and just have to reboot the client?
How much configuration is really meant to be shared by the config-server, because currently all of my clients just contain a super basic bootstrap.yml file.
Does anyone have a good link to where I can read more about all the properties that is being set in my yml files? Both a documentation of what the properties that exists actually do as well as some documentation on how I can use them in combination with spring cloud?
Do I need specific properties to enable my apps/clients to find other apps/clients?
Edited information
Thank you for your quick and excellent reply, I've gone through this over and over today and I finally got my application working..
The problem (I can't understand why and was hoping you could help me understand that) is that my discovery service contains yml files for each of my other clients where I specify things like port and eureka information.. What I specified here as well was:
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka
So, when I set this value it seems to override something that makes my service discovery not working.. Even tho I can see all my applications in the eureka server, they were unable to find each other when I had this value set.
I set this value by having a message-server.yml file in my configuration service that is sent out to my message-server application after bootstrap..
So then I have two new questions.
How do I override this eureka server property?
Why does my discovery client stop working when I set this value, what is it that it actually does?
What is required to actually use ribbon load balancer?
The ribbon-loadbalancer must be on the classpath (e.g. via "spring-cloud-starter-ribbon"). Then you can inject one as a LoadBalancerClient or you can inject a RestTemplate (it will be load-balancer aware if you have a LoadBalancerClient).
Do I have to use ribbon to be able to use the "auto discovery", I thought not but now I'm just confused.
What is "auto discovery"? You don't need to use Ribbon to use the DiscoveryClient (Ribbon is a load balancer, not a service registry).
From what I've understood, when using EnableEurekaClient I should not need to use the EnableDiscoveryClient as well?
Correct. #EnableEurekaClient is annotated with #EnableDiscoveryClient so it is only there to express a preference.
Can I change the yml files on my config-server for the clients in runtime and just have to reboot the client?
Yes. Or you can use the /refresh or /restart endpoints (a full reboot is probably best in production, at least periodically).
How much configuration is really meant to be shared by the config-server, because currently all of my clients just contain a super basic bootstrap.yml file.
As much as you want. How long is a piece of string? If I were you I would try and keep the central config to a minimum (only the things that change between environments, or at runtime).
Does anyone have a good link to where I can read more about all the properties that is being set in my yml files? Both a documentation of what the properties that exists actually do as well as some documentation on how I can use them in combination with spring cloud?
Spring Boot and Spring Cloud have autogenerated metadata for externalized properties. The new generation of IDEs understands them (so get STS 3.6.4 or IDEA 14.1), and they are listed in the user guide (for Spring Boot at least) under http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#common-application-properties
Do I need specific properties to enable my apps/clients to find other apps/clients?
You need to be able to locate your service registry (Eureka in this case). If you are using Eureka and your clients have registered then that is enough.