hosting zipkin inside proxy/zuul-gateway - spring

zipkin is a tool for tracing request as well as tracking the span of time a service took to process the request useful in multi-service projects it doesnt require much effort for setting up u just have to add zipkin dependency in your services and define a sampler bean.
add the following dependency in project
compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-zipkin', version: '1.3.2.RELEASE'
add sampler bean inside ur project
` #Value("${spring.sleuth.sampler.percentage}")
String percentage;
#Bean
public PercentageBasedSampler defaultSampler() {
SamplerProperties configuration= new SamplerProperties();
configuration.setPercentage(Float.parseFloat(percentage));
return new PercentageBasedSampler(configuration);
}
`
add above bean when u want only fraction of ur requests traces to send to zipkin else define a bean
` #Bean
public AlwaysSampler defaultSampler() { return new AlwaysSampler();
}
`
add
spring.zipkin.base-url=localhost:9411 in ur properties file and host the zipkin server on the same port defined above.
but if u r using api-gateway for accessing zipkin (in case of deplyment in cloud ) or inside proxy u may face the issue of broken ui elements when accessing thru gateway in this case im using zuul with propertis as:
zuul.routes.zipkin.path=/zipkin/*
zuul.routes.zipkin.url=http://localhost:9411

the simplest solution i have found for solving broken ui for zipkin thru gateway
is by changing following property of zipkin-server-shared.yml file inside zipkin server
zipkin:
ui:
base-path: /zipkin
change above property to
zipkin:
ui:
base-path: /api/tracing/zipkin
and change ur zuul path to following
zuul.routes.zipkin.path=/api/tracing/*
and than access zipkin using follwing url
https://gatewayhost:port/api/tracing/zipkin/
give attention to small details in config and dont forget to put trailing "/" after zipkin in url

Related

Spring cloud gateway proxy to controller in same application

I want to achieve the following with a spring boot webflux application:
I have an endpoint api/test. I would like the same controller to be available on dynamically configured sub paths. E.g. if configured a sub-route "app" then a request to app/api/test should end up in the same controller.
To facilitate this I did the following using a RouteLocatorBuilder:
route(id = "proxy_api_test") {
host(location.host)
path("/${location.route}/api/test/**" )
filters{
filter(setPathGatewayFilter.apply(createSetPathConfig("/api/test")))
}
uri(location.uri)
}
In case of testing on localhost for example location.host would be "localhost:8080" and location.route could be "app" and location.uri would be "http://localhost:8080".
And createSetPathConfig is given as:
fun createSetPathConfig(template: String): SetPathGatewayFilterFactory.Config{
val config = SetPathGatewayFilterFactory.Config()
config.template = template
return config
}
When running the application that would work like a charm because requests to http://localhost:8080/app/api/test would be redirected to http://localhost:8080/api/test with the help of spring cloud gateway. I have chose this approach also because it could be various sub paths at the same time, so the same controller must be available from different entry paths.
Now what I see is that this does not work in unit-tests using an #Autowired val client: WebTestClient because when executing the unit-test in fact no web-server is running. Is there a way to indicate with the uri in with RouteLocatorBuilder that the request should be executed on the same host such that unit-test would also work with the same logic? Because in fact in this case I would like spring cloud gateway not to forward to another host but to just change the routes dynamically.

Spring-Doc open api not working with Spring cloud config server #EnableConfigServer

Im using spring boot 2.3.2.RELEASE with spring-cloud-config-server 2.2.4.RELEASE. Im trying to implement the spring-doc-openapi (1.4.3) in a existing project. If i add #EnableConfigServer in one the configuration class file, the swagger-ui.html endpoint returns a weird json:
"name":"swagger-ui",
"profiles":[
"index.html"
],
"label":null,
"version":null,
"state":null,
"propertySources":[
]
}
and not the the swagger ui as expected. Im not sure if its a bug, but would appreciate any kind of help.
Not sure if its relevant to add the springdoc dependency on spring cloud config server, unless you need to explore some APIs on the config server it self.
Here is the link of a fully working example using springdoc with config server:
https://github.com/springdoc/springdoc-openapi-demos/tree/master/sample-springdoc-openapi-microservices
And this is the link of a blog which explains the natural usage with microservies and spring cloud modules:
https://piotrminkowski.com/2020/02/20/microservices-api-documentation-with-springdoc-openapi/
Answer from #brianbro seems not to be working anymore...
Verified on: springdoc-openapi v1.6.6 and org.springframework.cloud:spring-cloud-config-server:v2.2.4.RELEASE
Here is how I solved it:
List item spring.cloud.config.server.prefix=config-server - please note that any request to config server will require to add prefix!
Add following bean (sample implementation in Kotlin)
#Bean fun configServerApi(): GroupedOpenApi =
GroupedOpenApi.builder()
.group("Config server")
.pathsToMatch(
"/config-server/**"
)
.build()
Now you should be able to reach swagger ui :)

Spring Cloud Config client doesn't get values from server

I have been following this tutorial from Baeldung in setting up a Spring Cloud Config server and client. I believe I have set up the server correctly since going to http://localhost:8888/service-config/development/master will seemingly correctly show the json with the user.role defined:
{"name":"service-config","profiles":["development"],"label":"master","version":"d30a6015a6b8cb1925f6e61cee22721f331e5783","state":null,"propertySources":[{"name":"file:///C:.../git/service-config-data/service-config-development.properties","source":{"user.role":"Developer"}},{"name":"file:///C:.../git/service-config-data/service-config.properties","source":{"user.role":"Developer"}}]}
However, I cannot seem to correctly connect the Spring Cloud Config client to the server - the client fails to run with cannot resolve placeholder errors on the #Value("${user.role}") annotation. I've tried adding a default value to the annotation and then manually refreshing the values (using the #refreshscope annotation), but to no avail.
I've looked at the debug and trace logs by running the tools with --debug --trace flags but I cannot find when/if the client is making the call to the server and which config-data .properties file the server is actually looking for. I am a bit lost on how to proceed.
Here is my server application.properties:
spring.application.name=service-config
server.port=8888
spring.cloud.config.server.git.uri=file:///${user.home}/git/service-config-data
spring.cloud.config.server.git.clone-on-start=false
spring.security.user.name=root
spring.security.user.password=toor
And inside the service-config-data folder, there are files with this inside:
user.role=Developer
I have tried files called service-config.properties, service-config-client.properties, service-config-development.properties, and service-config-client-development.properties, and none of them seem to pull through the user.role.
Here is my client:
#SpringBootApplication
#RestController
#RefreshScope
public class ServiceConfigClient {
#Value("${user.role:unknown}")
private String role;
#RequestMapping(
value = "/whoami/{username}",
method = RequestMethod.GET,
produces = MediaType.TEXT_PLAIN_VALUE)
public String whoami(#PathVariable("username") String username) {
return String.format("Hello! You're %s and you'll become a(n) %s...\n", username, role);
}
}
And my client bootstrap.properties:
spring.application.name=service-config-client
spring.profiles.active=development
spring.cloud.config.uri=http://localhost:8888
spring.cloud.config.username=root
spring.cloud.config.password=toor
management.endpoints.web.exposure.include=*
How do I correctly connect the client to the server so that it can successfully pull the config data? Thank you.
My problem was fixed by adding spring.cloud.config.name=service-config to the bootstrap.properties of the service-config-client as well as adding spring-cloud-config-client to and removing spring-cloud-config-server from my pom.xml
There is confusion about client service name and corresponding property sources in config server.
You need to do one of the following:
Change client service name:
//client bootstrap.properties
spring.application.name=service-config
Change folder name in git repo from service-config to service-config-client

Feign Client Prioritizing URL's in yaml over Eureka

I have a Spring Boot application which serves as a Eureka client. The application has the need to call another micro-service through REST, and I wish to make this call using Feign. The issue I am having is, my application is trying to lookup the service name in Eureka, when it is only defined in my applications yaml file.
I apologize for the hard to follow explanation, hopefully the following code snippets will help clarify.
Feign client:
#FeignClient("foo")
#Component
public interface FooServiceProxy{
#RequestMapping(value = "/balance", method = RequestMethod.POST, produces = "application/json")
ServiceResponse execute(ServiceRequest serviceRequest);
}
In my controller who calls this Feign client, the FooServiceProxy is defined using #AutoWired:
#Autowired
private FooServiceProxy fooServiceProxy;
My yaml file is as follows:
spring:
application:
name: app-name
server:
port: 8080
foo:
ribbon:
listOfServers: http://hostname:8081/balance
eureka:
client:
fetchRegistry: false
serviceUrl:
defaultZone: http://eurekasrver:8761/eureka/
My issue is, during run-time, the following error is thrown:
java.lang.RuntimeException: com.netflix.client.ClientException: Load balancer does not have available server for client: foo
Interestingly, if I remove the #EnableEurekaClient annotation from the application, everything works. I believe I understand the problem which is that instead of looking up the server for foo in my yaml file, because the application is a Eureka client, Feign is going straight to Eureka to lookup a server ip, then failing as none can be found. Despite seeming to understand the problem, I have been unable to find a solution online or to think of one myself.
Any help will be appreciated.
Thank you!
Concerning this question, you should take in account that when eureka is on your classpath, all ribbon configuration are charged by eureka, so it'll use eureka server's list.
Spring Cloud uses #RibbonClient to configure the types used by ribbon, like server list. If you have eureka on the classpath, by default it uses the eureka server list (hence your need for the flag to disable eureka).
Commented by spencergibb https://github.com/spring-cloud/spring-cloud-netflix/issues/564
You can try either by adding the NIWSServerListClassName configuration:
`someservice.ribbon:
NIWSServerListClassName:com.netflix.loadbalancer.ConfigurationBasedServerList
listOfServers: server1:80`
Or try the solution proposed in this issue https://github.com/spring-cloud/spring-cloud-netflix/issues/564

Spring-Cloud Zuul breaks UTF-8 symbols in forwarded multipart request filename

this is first time for me on SO, so please be patient for my first question.
I think i have some kind of configuration problem, but after a day of experiments i'm stuck. Our application is based on Spring-Cloud [Brixton release]. We have configuration like this: Portal (web application serving angular-based web-ui), which has zuul proxy with single route configured to our gateway service, like so:
zuul:
ignoredServices: '*'
prefix: /api
routes:
api-proxy:
path: /**
serviceId: api-gateway
which has another Zuul configured and relays requests to inner bussiness logic services:
zuul:
ignoredServices: '*'
routes:
service1:
path: /service1/**
serviceId: service1
service2:
path: /service2/**
serviceId: service2
All this configuration is working with no problem.
The problem now that i am facing is with file upload multipart requests. To be more precise - those multipart requests, when file to be uploaded has non latin symbols (e.g. ąčęėįš) from UTF-8. When request reaches service which has to deal with #RequestPart MultipartFile file, then file.getOriginalFilename() returns questionmarks in the places of aforementioned symbols. Now, i have tried to directly upload such file to such controller, and filename comes without questionmarks, that is, not broken, which suggests, that some bad interpretation/parsing of multipart request occurs somewhere in Zuul filters, when proxy relays incomming request.
Maybe someone had similar experience with Zuul and can direct me some way to resolve this problem?
I just ran into the same issue myself, and created the following issue:
https://jira.spring.io/browse/SPR-15396
Hopefully this is getting configurable in Spring 4.3.8.
In the meantime, you have to create a bean of type FormBodyWrapperFilter (that overrides the one in ZuulConfiguration). In the constructor, you pass a copy of FormHttpMessageConverter, which extends from FormHttpMessageConverter, and you change the encoding used in FormHttpMessageConverter.MultipartHttpOutputMessage#getAsciiBytes(String) to UTF-8 (you might also want to delete any references to javax-mail, unless you have that on classpath). You need a pretty recent version of Spring Cloud Netflix to do this.
Example:
#Bean
FormBodyWrapperFilter formBodyWrapperFilter() {
return new FormBodyWrapperFilter(new MyFormHttpMessageConverter());
}
Then you create a copy of FormHttpMessageConverter, and change the following method:
private byte[] getAsciiBytes(String name) {
try {
// THIS IS THE ONLY MODIFICATION:
return name.getBytes("UTF-8");
} catch (UnsupportedEncodingException ex) {
// Should not happen - US-ASCII is always supported.
throw new IllegalStateException(ex);
}
}
Still having issues after modifying response even with newer versions
Using spring boot 2.3.8.RELEASE
Managed to fix it by forcing the following spring properties
server.servlet.encoding.force= true
server.servlet.encoding.charset= UTF-8

Resources