I am trying to set up a simple spring cloud consul app.
I have a "distribution" service up and registered in consul (with the spring.application.name property set to "distribution")
I have an "acquisition" service that is trying to make a call to the "distribution" service using feign.
Here is my main class
#Configuration
#EnableAutoConfiguration
#EnableDiscoveryClient
#RestController
#EnableFeignClients
#EnableHystrix
public class Acquisition {
#Autowired
private DiscoveryClient discoveryClient;
#Autowired
private DistributionClient distributionClient;
#RequestMapping("/use-feign")
public String sendData() {
distributionClient.sendData(new Data("Hello World"));
return "sent";
}
#RequestMapping("/disco")
public String disco() {
List<ServiceInstance> list = discoveryClient.getInstances("distribution");
if (list != null && list.size() > 0) {
return list.get(0).getUri().toString();
}
return null;
}
public static void main(String[] args) {
SpringApplication.run(Acquisition.class, args);
}
}
here is my feign client
#FeignClient(value = "distribution")
interface DistributionClient {
#RequestMapping(method = RequestMethod.POST, value = "/data", consumes = "application/json")
void sendData(Data data);
}
and here is my pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-all</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-consul-dependencies</artifactId>
<version>1.0.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
When I request the "/disco" Url, the url of the "distribution" service is properly retrieved, which means that the whole discovery thing is working as expected.
But, when I request the "/use-feign" url, I get the following exception :
com.netflix.client.ClientException: Load balancer does not have
available server for client: distribution at
com.netflix.loadbalancer.LoadBalancerContext.getServerFromLoadBalancer(LoadBalancerContext.java:468)
~[ribbon-loadbalancer-2.2.0.jar:2.2.0] at
com.netflix.loadbalancer.reactive.LoadBalancerCommand$1.call(LoadBalancerCommand.java:184)
~[ribbon-loadbalancer-2.2.0.jar:2.2.0] at
com.netflix.loadbalancer.reactive.LoadBalancerCommand$1.call(LoadBalancerCommand.java:180)
~[ribbon-loadbalancer-2.2.0.jar:2.2.0] at
rx.Observable.unsafeSubscribe(Observable.java:8460)
~[rxjava-1.1.5.jar:1.1.5] at
rx.internal.operators.OnSubscribeConcatMap.call(OnSubscribeConcatMap.java:94)
~[rxjava-1.1.5.jar:1.1.5] at
rx.internal.operators.OnSubscribeConcatMap.call(OnSubscribeConcatMap.java:42)
~[rxjava-1.1.5.jar:1.1.5]
Am I missing any configuration ?
Thank you for your help.
spencergibb pointed out the problem : no health check endpoint was deployed.
Just adding spring-boot-actuator to the dependencies solved the issue.
In my case I had: spring-boot (2.4.2) with spring-cloud 2020.0.1, I have just added:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
Related
my Rest API is working fine locally but the moment I put the same JAR and run it from cloud server, I can only call the GET API and none of the POST API from Postmen.
Below are my code.
RestController
#CrossOrigin(origins = "*", maxAge = 3600)
#RestController
#RequestMapping("/api/test")
public class TestController {
#GetMapping("/all")
public String allAccess() {
return "Public Content.";
}
#PostMapping("/testpostA")
public ResponseEntity<?> testPostA() {
return ResponseEntity.ok(new MessageResponse("Test POST successfully!"));
}
#PostMapping("/testpostB")
public String testPostB() {
return "String return POST successfully.";
}
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.2</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.eurogain</groupId>
<artifactId>WebPortal</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>WebPortal</name>
<description>Java Spring Boot project for the Eurogain web portal for both internal and external client.</description>
<properties>
<java.version>15</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-envers</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build></project>
WebSecurityConfigurationAdapter
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(
// securedEnabled = true,
// jsr250Enabled = true,
prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsServiceImpl userDetailsService;
#Autowired
private AuthEntryPointJwt unauthorizedHandler;
#Bean
public AuthTokenFilter authenticationJwtTokenFilter() {
return new AuthTokenFilter();
}
#Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
System.out.println("============================ Inside WebSecurityConfig ===========================Ken");
http.csrf().disable().authorizeRequests().anyRequest().permitAll();
}}
Lastly, the web application:
#SpringBootApplication
public class WebPortalApplication {
public static void main(String[] args) {
SpringApplication.run(WebPortalApplication.class, args);
}}
Running the same JAR file locally, no issue on calling all of the POST and GET API. However, when we move the same JAR over to AWS EC2 cloud instance and run it, we can only call the GET API. The following error was thrown at the Postmen console when calling any of the POST API:
However, no issue when calling the GET API:
Server console output - no error:
UPDATE - Round 1
Tested using wget, error 500 as well. No futher info.
UPDATE - Round 2
Tested using wget POST, and after modifying WebSecurityConfig. Here is the screeshot:
Server console contains warning upon the wget POST:
The updated code for WebSecurityConfig:
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable();
}
}
Try config cors like that:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
#Configuration
public class CorsConfig {
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedMethods("HEAD", "GET", "PUT", "POST", "DELETE", "PATCH");
}
};
}
}
The problem was due to the Nginx reverse proxy handling. In localhost, I was using HTTP, but in the dev server, we are only using HTTPS. Thus, I used HTTPS for all the RESTful API URLs.
I've built a small project to provide rest services using springframework. the problem is to access this project inside an EAR after publishing this EAR into a WebLogic server.
When I publish this project as an standalone war in my weblogic everything ends well, my SpringWebAppInitializer is loaded as expected when the server starts and I can reach the webservice using the address localhost:7001/internal-webservices/project/12. But when I add this WAR to the EAR, the SpringWebAppInitializer is never invoked.
Any ideas?
Some of my configurations:
The pom.xml:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.1.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.3.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.1.RELEASE</version>
</dependency>
<dependency>
My ApplicationContextConfig class:
#Configuration
#EnableWebMvc
#ComponentScan("my_package.*")
public class ApplicationContextConfig {
// No need ViewSolver
// Other declarations if needed ...
}
My WebApplicationInitializer:
public class SpringWebAppInitializer implements WebApplicationInitializer{
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext();
appContext.register(ApplicationContextConfig.class);
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("internal-webservices",
new DispatcherServlet(appContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
// UTF8 Charactor Filter.
FilterRegistration.Dynamic fr = servletContext.addFilter("encodingFilter", CharacterEncodingFilter.class);
fr.setInitParameter("encoding", "UTF-8");
fr.setInitParameter("forceEncoding", "true");
fr.addMappingForUrlPatterns(null, true, "/*");
}
}
And, my RestController class:
#RestController
public class ProjectController {
#RequestMapping(value = "/project/{id}", method = RequestMethod.GET,
produces = { MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE })
public List<String> getProject(#PathVariable("id") String id) {
List<String> result = new ArrayList<String>();
result.add("id: "+ id);
return result;
}
}
Regards,
Felipe
We configured springsessions using redis as session store. With bigger load it ended in a result where users happen to get back random session data.
Unfortunately we are not able to reproduce it again and it is not reproducable in test environment.
Has anyone had similar experience and can maybe point a direction what to look at?
I have used spring session using redis session store in my project as follows ,it works normal ,i share it , hope it helps!
used spring boot starter version : 1.4.3
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>4.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>biz.paluch.redis</groupId>
<artifactId>lettuce</artifactId>
<version>3.5.0.Final</version>
</dependency>
Spring session redis configuration class :
package az.com.cybernet.aas.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.lettuce.
LettuceConnectionFactory;
import org.springframework.session.data.redis.config.annotation.web.http.
EnableRedisHt
tpSession;
import org.springframework.session.web.http.CookieHttpSessionStrategy;
import org.springframework.session.web.http.CookieSerializer;
import org.springframework.session.web.http.DefaultCookieSerializer;
import org.springframework.session.web.http.HttpSessionStrategy;
#EnableRedisHttpSession
public class RedisConfig {
#Value("${spring.redis.host}")
private String hostname;
#Value("${spring.redis.port}")
private String port;
// #Value("${spring.redis.password}")
// private String password;
#Bean
public LettuceConnectionFactory connectionFactory() {
LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory();
connectionFactory.setHostName(hostname);
// connectionFactory.setPassword(password);
connectionFactory.setDatabase(2);
connectionFactory.setPort(Integer.parseInt(port));
return connectionFactory;
}
#Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setCookieName("JSESSIONID");
serializer.setCookiePath("/");
serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$");
return serializer;
}
#Bean
public HttpSessionStrategy strategy() {
CookieHttpSessionStrategy cookieHttpSessionStrategy = new CookieHttpSessionStrategy();
cookieHttpSessionStrategy.setCookieSerializer(cookieSerializer());
return cookieHttpSessionStrategy;
}
}
I have selected Jersey Test Framework to implement unit test cases for REST services.But i am getting following issue once i ran the test.
Note: I even add the resteasy-jackson-provider into pom file but couldn't help.
Here is the .pom file dependency
<!-- jersey security dependency -->
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet-core</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-multipart</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>${jersey.version}</version>
</dependency>
<!-- jersey test framework dependency -->
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-jetty</artifactId>
<version>${jersey.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson-provider</artifactId>
<version>2.3.4.Final</version>
</dependency>
<!--junit Dependency-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
MockServices.Java
#Path("/hello")
public class MockServices {
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("/world")
public DateVO getHello() {
DateVO j=new DateVO ();
j.setActive(true);
return j;
}
}
MockServicesTest.Java
public class MockServicesTest extends JerseyTest {
#Override
protected Application configure() {
return new ResourceConfig(MockServices.class);
}
#Test
public void test() {
Response hello = target("/hello/world").request().get();
System.out.println(hello.readEntity(String.class));//throw an above exception
}
}
Please let me know how can i overcome this problem.
Override your provider method like this
#Override
protected Application configure() {
ResourceConfig config =new ResourceConfig(MockServices.class).register(JacksonFeature.class).register("Your ContextResolver<ObjectMapper> implementation class");
return config;
}
I had to use explicitly Jersey client implementation to invoke the REST end points.
#Test
public void test() {
final Client client = new JerseyClientBuilder().build();
WebTarget target = client.target("http://localhost:9998");
final Response response =
target.path("/hello/world").request().get();
final String json = response.readEntity(String.class);
}
Reference
I have added sleuth/zipkin into my project. I'm using logback, and by default I get very well formatted logs in my console and files as well. I'm also using a logstash appender, and when I look at how kibana presents those logs - I'm not satisfied at all. Here are details:
pom.xml:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Brixton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>4.6</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
...
</dependencies>
My logstash appender:
#Component
#ConfigurationProperties(prefix = "logstash")
public class LogstashConfig {
private String uri;
#Value("${spring.application.name}")
private String applicationName;
public void setUri(String uri) {
this.uri = uri;
}
#PostConstruct
public void init() {
final Logger rootLogger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
final LoggerContext loggerContext = rootLogger.getLoggerContext();
final LogstashTcpSocketAppender logstashTcpSocketAppender = new LogstashTcpSocketAppender();
logstashTcpSocketAppender.setName(applicationName);
logstashTcpSocketAppender.setContext(loggerContext);
logstashTcpSocketAppender.addDestination(uri);
final LogstashEncoder encoder = new LogstashEncoder();
encoder.setContext(loggerContext);
encoder.setCustomFields("{\"application_name\":\"" + applicationName + "\"}");
encoder.start();
logstashTcpSocketAppender.setEncoder(encoder);
logstashTcpSocketAppender.start();
rootLogger.addAppender(logstashTcpSocketAppender);
rootLogger.setLevel(Level.INFO);
rootLogger.info("Logstash succesfully configured: application connected to logstatsh server {}", uri);
}
}
And this is what I see in kibana:
LOG_LEVEL_PATTERN:%clr(%5p) %clr([my-app-name,%X{X-B3-TraceId:-},%X{X-B3-SpanId:-},%X{X-Span-Export:-}]){yellow} application_name:my-app-name
The only thing that is resolved by the log pattern is the application name. Am I missing some configuration? Or maybe there's something wrong in my logstash appender?