Ribbon load balance between server instead of instance - spring-boot

I have 2 server running the same service, I want to use ribbon to load balance the traffic between 2 servers, is it possible?
I've learn that ribbon can load balance between instance in the server. but my server only have 1 instances, and there are 2 servers. Here is my code to load balance instance, is there any changes i can make to load balance server?
Thank you so much!
#SpringBootApplication
#EnableDiscoveryClient
#RestController
#RibbonClient(name= "myInstanceName", configuration=RibbonConfig.class )
public class RibbonAppApplication {
#Inject
private RestTemplate restTemplate;
public static void main(String[] args) {
SpringApplication.run(RibbonAppApplication.class, args);
}
#GetMapping
public String getService() {
return restTemplate.getForEntity("http://myInstanceName",String.class).getBody();
}
#Bean
#LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}

Related

Can the Sprint RESTful server support Accept=application/xml

Everything I've read says the Sprint RESTful server talks with the client passing JSON. What if the client passes up XML and has Accept=xml for the response. Will it then communicate using XML?
Or is it JSON only?
Here is a minimal solution that works, the URL https://httpbin.org/xml returns XML and we can use RestTemplate to read it.
#SpringBootApplication
public class Application {
#Autowired
static RestTemplate restTemplate;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
testRest();
}
#Bean
public static RestTemplate restTemplate() {
return new RestTemplate();
}
public static void testRest() {
ResponseEntity<String> response = restTemplate().getForEntity("https://httpbin.org/xml", String.class);
System.out.println(response.getBody());
}
}

Spring get open websocket connections

I am using Spring Boot Websocket to enable my Spring Boot 2 microservice to deal with websocket connections.
My config:
#Configuration
#EnableWebSocket
public class WsConfig implements WebSocketConfigurer {
#Autowired
WebSocketHandler webSocketHandler;
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
DefaultHandshakeHandler handshakeHandler = new DefaultHandshakeHandler();
handshakeHandler.setSupportedProtocols(HANDSHAKE_PROTOCOL);
registry.addHandler(webSocketHandler, WS_HANDLER_PATH + WILDCARD)
.setAllowedOrigins("*")
.setHandshakeHandler(handshakeHandler);
}
}
My clients are able to connect to my Service via Websocket. I am implementing WebSocketHandler interface to handle messages and log connections.
Now my question: Is there a way to show all current websocket users/sessions?
I was trying to use the SimpUserRegistry:
#Configuration
public class UserConfig {
final private SimpUserRegistry userRegistry = new DefaultSimpUserRegistry();
#Bean
public SimpUserRegistry userRegistry() {
return userRegistry;
}
}
and to show the users via a REST endpoint
#RestController
public class WebSocketManager {
private final SimpUserRegistry userRegistry;
public WebSocketManager(SimpUserRegistry userRegistry) {
this.userRegistry = userRegistry;
}
#GetMapping(path = "/users")
public List<String> getConnectedUsers() {
userRegistry.getUsers().stream()
.map(SimpUser::getName)
.forEach(System.out::println);
System.out.println("Users " + userRegistry.getUsers());
System.out.println("UsersCount " + userRegistry.getUserCount());
return this.userRegistry
.getUsers()
.stream()
.map(SimpUser::getName)
.collect(Collectors.toList());
}
}
But this always gives my an empty list: [] even when obviously WS connections are established.
Is this SimpUserRegistry working with the Websocket system of Spring which is configured with the WebSocketConfigurer and #EnableWebSocket? What am I doing wrong? Any tips or alternatives?
Thank you in advance!

Spring boot Oauth2 : Token relay from a client using Feign, Ribbon, Zull and Eureka to a ressource

I have an oauth2 client that get a token from an authorization server successfully. (not always has been the case but now it is... :))
The client, the zuul gateway and the resource server are all registered in Eureka.
My client use a Proxy to access to a remote ressource service named microservice-files.
#RestController
#FeignClient(name = "zuul-server")
#RibbonClient(name = "microservice-files")
public interface ProxyMicroserviceFiles {
#GetMapping(value = "microservice-files/root")
FileBean getUserRoot();
}
So I'd like to relay the token to Zull and then to the resource server.
I can relay the token this way to contact Zuul and apparently the load balancing is managed too (I've just test I didn't know and it's great) also zuul can relay the token, but it's not very convenient I'd prefer the previous approach.
#EnableConfigurationProperties
#SpringBootApplication
#EnableFeignClients("com.clientui")
public class ClientUiApplication {
#Bean
public OAuth2RestOperations restOperations(
OAuth2ProtectedResourceDetails resource,
OAuth2ClientContext context) {
return new OAuth2RestTemplate(resource, context);
}
public static void main(String[] args) {
SpringApplication.run(ClientUiApplication.class, args);
}
}
here is the test controler
#Controller
public class ClientController {
#Autowired
private RestOperations restOperations;
#RequestMapping("/root")
public ResponseEntity userRootTest() {
String rootUrl = "http://localhost:9004/microservice-files/root";
return restOperations.getForEntity(rootUrl,FileBean.class);
}
}
If I correctly understand your problem then you can use a RequestInterceptor to add a token in each request by the feign. In order to do it you can use the next configuration:
#Bean
public RequestInterceptor oauth2FeignRequestInterceptor(OAuth2ClientContext oauth2ClientContext,
OAuth2ProtectedResourceDetails resource) {
return new OAuth2FeignRequestInterceptor(oauth2ClientContext, resource);
}
#Bean
protected OAuth2ProtectedResourceDetails resource() {
AuthorizationCodeResourceDetails resource = new AuthorizationCodeResourceDetails();
resource.setAccessTokenUri("http://127.0.0.1:9000/auth/login");
resource.setUserAuthorizationUri("http://127.0.0.1:9000/auth/authorize");
resource.setClientId("my-client");
resource.setClientSecret("my-secret");
return resource;
}
This is what I did to make it work.
#Bean(name = "oauth2RestTemplate")
#LoadBalanced
public OAuth2RestTemplate restTemplate(SpringClientFactory clientFactory) {
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails());
RibbonLoadBalancerClient ribbonLoadBalancerClient = new RibbonLoadBalancerClient(clientFactory);
LoadBalancerInterceptor loadBalancerInterceptor = new LoadBalancerInterceptor(ribbonLoadBalancerClient);
ClientCredentialsAccessTokenProvider accessTokenProvider = new ClientCredentialsAccessTokenProvider();
accessTokenProvider.setInterceptors(Arrays.asList(loadBalancerInterceptor));
restTemplate.setAccessTokenProvider(accessTokenProvider);
return restTemplate;
}
public ClientCredentialsResourceDetails resourceDetails() {
ClientCredentialsResourceDetails clientCredentialsResourceDetails = new ClientCredentialsResourceDetails();
clientCredentialsResourceDetails.setId("1");
clientCredentialsResourceDetails.setClientId("my-ms");
clientCredentialsResourceDetails.setClientSecret("123");
clientCredentialsResourceDetails.setAccessTokenUri("http://oauth-server/oauth/token");
clientCredentialsResourceDetails.setScope(Arrays.asList("read"));
clientCredentialsResourceDetails.setGrantType("client_credentials");
return clientCredentialsResourceDetails;
}

How to run spring boot admin client and server in same application

I want to run a spring boot admin server and client inside the same application.I changed server port, when I change the server port spring admin will access my changed port. so I can run an admin server. but I can't see my web application pages.
i need output like this.
Localhost:8080/myapplication (my client application)
localhost:8090/admin (spring boot admin server)
Here is a simple example to run the application on two different ports for admin client and for server client.
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplicationBuilder parentBuilder = new SpringApplicationBuilder(Application.class);
parentBuilder.child(ServiceOneConfiguration.class).properties("server.port:8081").run(args);
parentBuilder.child(ServiceTwoConfiguration.class).properties("server.port:8082").run(args);
}
#Service
static class SharedService {
public String getMessage(String name) {
return String.format("Hello, %s, I'm shared service", name);
}
}
#Configuration
#EnableAutoConfiguration
static class ServiceOneConfiguration {
#Controller
#RequestMapping("/server")
static class ControllerOne {
#Autowired
private SharedService service;
#RequestMapping(produces = "text/plain;charset=utf-8")
#ResponseBody
public String getMessage(String name) {
return "ControllerOne says \"" + service.getMessage(name) + "\"";
}
}
}
#Configuration
#EnableAutoConfiguration
static class ServiceTwoConfiguration {
#Bean
EmbeddedServletContainerFactory servletContainer() {
TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory();
tomcat.setUriEncoding("cp1251");
return tomcat;
}
#Controller
#RequestMapping("/client")
static class ControllerTwo {
#Autowired
private SharedService service;
#RequestMapping(produces = "text/plain;charset=utf-8")
#ResponseBody
public String getMessage(String name) {
return "ControllerTwo says \"" + service.getMessage(name) + "\"";
}
}
}
}
For more detail here is a link:
spring-boot-connectors
Hope this will help.
we can use spring boot multi modules like following.
main app
spring-boot-admin
pom.xml ( running port 8888 )
my project
pom.xml ( running port 8080 )
pom.xml

Expose/Filter Controller Request Mappings by Port/Connector

I have a relatively simple Spring Boot application that, by default, is secured over SSL on port 9443 using a self-signed certificate, which works great for serving up APIs to, say, a mobile app. However, I would now like to develop an unsecured web application with its own frontend and serve up a subset of the content I allow over SSL.
This is what I've come up with so far, which enables port 8080 over HTTP in addition to port 9443, the latter I've defined in application.properties:
#SpringBootApplication
#ComponentScan
public class Application extends WebMvcConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public EmbeddedServletContainerFactory servletContainer() {
TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory();
tomcat.addAdditionalTomcatConnectors(createWebsiteConnector());
return tomcat;
}
private Connector createWebsiteConnector() {
Connector connector = new Connector(TomcatEmbeddedServletContainerFactory.DEFAULT_PROTOCOL);
connector.setPort(8080);
return connector;
}
}
I am now faced with the task of only exposing endpoints to the 8080 connection, and all of them to 9443. Obviously, the latter currently works by default, but right now 8080 can access everything 9443 can. Ideally, I would like to control access to certain request mappings defined in a "shared" controller that both connections have access to, i.e. something like this:
#RequestMapping(value = "/public", method = RequestMethod.GET)
public List<String> getPublicInfo() {
// ...
}
#HTTPSOnly
#RequestMapping(value = "/secured", method = RequestMethod.GET)
public List<String> getSecuredInfo() {
// ...
}
I assume something like what I have above isn't actually possible, but does anyone know how I could achieve the same effect?
Thanks in advance for any help!
Alright, I think I actually managed to solve this myself, but I'm open to other suggestions if anyone thinks they have a better solution:
#SpringBootApplication
#ComponentScan
public class Application extends WebMvcConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public EmbeddedServletContainerFactory servletContainer() {
TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory();
tomcat.addAdditionalTomcatConnectors(createWebsiteConnector());
return tomcat;
}
private Connector createWebsiteConnector() {
Connector connector = new Connector(TomcatEmbeddedServletContainerFactory.DEFAULT_PROTOCOL);
connector.setPort(8080);
return connector;
}
private static HashSet<String> uriWhitelist = new HashSet<>(4);
static {
// static website content
uriWhitelist.add("/");
uriWhitelist.add("/index.html");
uriWhitelist.add("/logo_48x48.png");
// public APIs
uriWhitelist.add("/public");
}
private static class PortFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if (request instanceof RequestFacade) {
RequestFacade requestFacade = (RequestFacade) request;
if (requestFacade.getServerPort() != 9443 && !uriWhitelist.contains(requestFacade.getRequestURI())) {
// only allow unsecured requests to access whitelisted endpoints
return;
}
}
filterChain.doFilter(request, response);
}
}
#Bean
public FilterRegistrationBean portFilter() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
PortFilter filter = new PortFilter();
filterRegistrationBean.setFilter(filter);
return filterRegistrationBean;
}
}

Resources