I am planning to create a microservice aplication with a dedicated service for dealing with data (mostly a Mongodb based service). I am wondering if there is a way using which my other microservices will be able to communicate with this service to make use of the shared data. Is it possible with JHipster API Gateway ?
If not how can I achieve this. I dont want to keep multiple copies of the same data within each microservice.
You can also use Feign clients with JHipster.
Annotate your SpringBootApplication with #EnableFeignClients
...
import org.springframework.cloud.openfeign.EnableFeignClients;
...
#SpringBootApplication
#EnableConfigurationProperties({LiquibaseProperties.class, ApplicationProperties.class})
#EnableDiscoveryClient
#EnableFeignClients
public class MyApp {
...
}
Create a Feign client in your microservice
...
import org.springframework.cloud.openfeign.FeignClient;
...
#FeignClient("another-service")
public interface AnotherClient {
#RequestMapping(method = RequestMethod.GET, value = "/api/another")
List<AnotherDTO> getAll();
}
Inject the Feign client with #Autowired and call it. It should be ready to use.
#RestController
#RequestMapping("/api")
public class MyResource {
...
#Autowired
private AnotherClient anotherClient;
...
#GetMapping("/another")
#Timed
public List<AnotherDTO> getAll() {
log.debug("REST request to get all");
return anotherClient.getAll();
}
}
For us, it worked without implementing a ClientHttpRequestInterceptor and setting a JWT token.
You can register your microservices to the same registry and then they can call each other.
UPDATE : Here is how I made it work.
In the microservice consuming the data one, use RestTemplate with the current user's jwt-token in the Authorization-header for the API calls :
#Component
public class AuthenticateClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
#Override
public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
String token = SecurityUtils.getCurrentUserJWT();
httpRequest.getHeaders().add("Authorization","Bearer "+token);
return clientHttpRequestExecution.execute( httpRequest, bytes );
}
}
My custom restTemplate using ClientHttpRequestInterceptor for adding token in header.
#Configuration
public class CustomBean {
#Autowired
AuthenticateClientHttpRequestInterceptor interceptor;
#Bean
#LoadBalanced
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setInterceptors(Collections.singletonList(interceptor));
return restTemplate;
}
}
And in the resource controller where your are making the call for data:
#RestController
#RequestMapping("/api")
public class DataResource {
#Autowired
RestTemplate restTemplate;
#PostMapping("/hello")
#Timed
public ResponseEntity<Hello> createHello(#RequestBody Hello Hello) throws URISyntaxException {
//The name your data micro service registrated in the Jhipster Registry
String dataServiceName = "data_micro_service";
URI uri = UriComponentsBuilder.fromUriString("//" + dataServiceName + "/api/datas")
.build()
.toUri();
//call the data microservice apis
List<Data> result = restTemplate.getForObject(uri, Data[].class);
return ResponseEntity.created(new URI("/api/hellos/" + result.getId()))
.headers(HeaderUtil.createEntityCreationAlert(ENTITY_NAME, result.getId().toString()))
.body(result);
}
}
Typically microservices talk to each other. Thats the whole point. With Eureka discovery in place you simply call the microservice by name instead of the FQDN which we normally would use without microservice.
For e.g. your book-service will call the author-service like this
http://author-service/authors
full example here https://spring.io/blog/2015/01/20/microservice-registration-and-discovery-with-spring-cloud-and-netflix-s-eureka
Please don't forget that JHipster is an opinionated framework based off of Spring Cloud so you can find most of this stuff by searching Spring docs.
you can use below solution :
Microservice A (i.e UAA-SERVICE), and Microservice B
Microservice B want to connect microservice A and call services with Feign client.
1)This code for Microservice B
Client proxy :- #AuthorizedFeignClient(name = "UAA-SERVICE")
#AuthorizedFeignClient(name = "UAA-SERVICE")
public interface UaaServiceClient {
#RequestMapping(method = RequestMethod.GET, path = "api/users")
public List<UserDTO> getUserList();
#RequestMapping(method = RequestMethod.PUT, path = "api/user-info")
public String updateUserInfo(#RequestBody UserDTO userDTO);
}
UAA-SERVICE : find this name with running Application Instances with registry.
2) In Microservice B (application.yml)
Increase feign client connection Time Out:
feign:
client:
config:
default:
connectTimeout: 10000
readTimeout: 50000
Increase hystrix Thread time out:-
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 60000
shareSecurityContext: true
3) add #EnableFeignClients in main #SpringBootApplication class.
This solution is working fine for me.
Related
With AspNetCore.SignalR (1.0.0 preview1-final) and AspNetCore.All (2.0.6), how can I invoke a method on a hub in server code that is not directly in a Controller and is in a class that cannot be made via Dependency Injection?
Most examples assume the server code is in a Controller and should 'ask' for the hub via an injectable parameter in a class that will created by DI.
I want to be able to call the hub's method from server code at any time, in code that is not injected. The old SignalR had a GlobalHost that enabled this approach. Basically, I need the hub to be a global singleton.
Now, everything seems to be dependent on using Dependency Injection, which is introducing a dependency that I don't want!
I've seen this request voiced in a number of places, but haven't found a working solution.
Edit
To be more clear, all I need is to be able to later access the hubs that I've registered in the Configure routine of the Startup class:
app.UseSignalR(routes =>
{
routes.MapHub<PublicHubCore>("/public");
routes.MapHub<AnalyzeHubCore>("/analyze");
routes.MapHub<ImportHubCore>("/import");
routes.MapHub<MainHubCore>("/main");
routes.MapHub<FrontDeskHubCore>("/frontdesk");
routes.MapHub<RollCallHubCore>("/rollcall");
// etc.
// etc.
});
If I register them like this:
services.AddSingleton<IPublicHub, PublicHubCore>();
it doesn't work, since I get back an uninitiated Hub.
No It's not possible. See "official" answer from david fowler https://github.com/aspnet/SignalR/issues/1831#issuecomment-378285819
How to inject your hubContext:
Best solution is to inject your hubcontext like IHubContext<TheHubWhichYouNeedThere> hubcontext
into the constructor.
See for more details:
Call SignalR Core Hub method from Controller
Thanks to those who helped with this. Here's what I've ended up on for now...
In my project, I can call something like this from anywhere:
Startup.GetService<IMyHubHelper>().SendOutAlert(2);
To make this work, I have these extra lines in Startup.cs to give me easy access to the dependency injection service provider (unrelated to SignalR):
public static IServiceProvider ServiceProvider { get; private set; }
public static T GetService<T>() { return ServiceProvider.GetRequiredService<T>(); }
public void Configure(IServiceProvider serviceProvider){
ServiceProvider = serviceProvider;
}
The normal SignalR setup calls for:
public void Configure(IApplicationBuilder app){
// merge with existing Configure routine
app.UseSignalR(routes =>
{
routes.MapHub<MyHub>("/myHub");
});
}
I don't want all my code to have to invoke the raw SignalR methods directly so I make a helper class for each. I register that helper in the DI container:
public void ConfigureServices(IServiceCollection services){
services.AddSingleton<IMyHubHelper, MyHubHelper>();
}
Here's how I made the MyHub set of classes:
using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;
public class MyHub : Hub { }
public interface IMyHubHelper
{
void SendOutAlert(int alertNumber);
}
public class MyHubHelper : IMyHubHelper
{
public IHubContext<MyHub> HubContext { get; }
public MyHubHelper(IHubContext<MyHub> hubContext)
{
HubContext = hubContext;
}
public void SendOutAlert(int alertNumber)
{
// do anything you want to do here, this is just an example
var msg = Startup.GetService<IAlertGenerator>(alertNumber)
HubContext.Clients.All.SendAsync("serverAlert", alertNumber, msg);
}
}
This is a nice solution. In .NET Core 2.1 the service provider is disposed and you get cannot access disposed object. The fix is to create a scope:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider.CreateScope().ServiceProvider;
I am using Jhipster Microservice Archi which suit better for my need.
I am trying to find a way to send request from Gateway to Microservice, Or at least send some more user information (Phone number, email ...) to microservice. Is there is any way to do that ?
What i wanted to do is basically when an user are just freshly registered, i want to send a request from the gateway to microservice in order to create for example TaxiLocation if the authority of the user is taxi, or create a shop if the user is a Shop manager ...
Thanks for your help.
What you want to do is communication between microservices, so here, the backend of your gateway wants to send request to your other microservice.
Am I correct?
If it's the case, you can read this ticket, it has been discussed:
https://github.com/jhipster/generator-jhipster/issues/3649
You have this tutorial too, by Xetys, from JHipster team:
http://stytex.de/blog/2016/03/25/jhipster3-microservice-tutorial/
And, you have a specific module which can do the job too, made by Christophe, from JHipster team too:
https://github.com/cbornet/generator-jhipster-swagger-cli
Hope, it will help you.
Thanks for your help, i finally figured it out :
1 - Create a Feign Config with a RequestInterceptor so then request will be authentificate
public class FeignConfig {
#Inject
private JHipsterProperties properties;
#Bean
public RequestInterceptor requestTokenBearerInterceptor() {
return requestTemplate -> {
String token = Jwts.builder().
setSubject("xxx")
.claim("auth", "xxx")
.signWith(SignatureAlgorithm.HS512, properties.getSecurity().getAuthentication().getJwt().getSecret())
.compact();
requestTemplate.header("Authorization", "Bearer " + token);
};
}
}
2- Create MicroserviceClient & MicroserviceClientFallback
#FeignClient(name = "xxxxxxxx", fallback = MicroserviceClientFallback.class, configuration = FeignConfig.class)
public interface MicroserviceClient {
#RequestMapping(value = "/api/createuser",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
UserInfo createUser(UserInfo user);
}
#Component
public class MicroserviceClientFallback implements MicroserviceClient {
private final Logger log = LoggerFactory.getLogger(MicroserviceClientFallback.class);
#Override
public UserInfo createUser(UserInfo user) {
log.warn("Triggered fallback for createUser : {}", user);
return null;
}
}
3 - Finally Call it from a rest controller
#Inject
private MicroserviceClient microserviceClient;
...
microserviceClient.createUser(userInfo);
I deployed two different spring boot micro-service on azure cloud. With micro-service I also deployed eureka. When I register both the micro-service on eureka its done.My next task is to send the data from one micro-service to another. I am new to spring-boot so, on local system I perform same task and its working fine but on azure micro-services are not communicating with each other. I am unable to establish a connection by using feign client and rest template as the IP and Hostname is dynamic. Please suggest what configuration do I need to make to achieve this task.
What I have done
I have configured eureka with below properties in its application properties:
spring.application.name=eureka.azurewebsites.net
eureka.client.registerWithEureka=false
eureka.client.fetchRegistry=false
eureka.instance.hostname=eureka.azurewebsites.net
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/
Service1 in azure:
spring.application.name=service1.azurewebsites.net
eureka.client.registerWithEureka=true
eureka.client.serviceUrl.defaultZone=http://eureka.azurewebsites.net/eureka/
Service 2 in azure:
spring.application.name=service2.azurewebsites.net
eureka.client.registerWithEureka=true
eureka.client.serviceUrl.defaultZone=http://eureka.azurewebsites.net/eureka/
Now when i try to call rest api from service 1 to another service 2 get unknowhost exception.
Below is the code snippet:
#RestController
#EnableAutoConfiguration
#ComponentScan
#SpringBootApplication
#EnableDiscoveryClient
public class Service2 {
private static final Logger LOG = LoggerFactory.getLogger(PLMBommsApplication.class);
#Bean
#LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
#Autowired
RestTemplate restTemplate;
#RequestMapping(value = "/")
public StringBuilder fromCall() throws URISyntaxException {
StringBuilder response = new StringBuilder();
URI uri = new URI("http://service1.azurewebsites.net/receiverPOST");
MultiValueMap<String, Object> mvm = new LinkedMultiValueMap<String, Object>();
mvm.add("abc", "Hello Part");
result = restTemplate.postForObject(uri, mvm, String.class);
response.append("successPost = " + result);
response.append("\n");
return response;
}
public static void main(String[] args) {
SpringApplication.run(Service2.class, args);
}
I'm following these recommendations: https://jhipster.github.io/microservices-architecture/ to organize the architecture of my microservices applications.
I have a setting with some microservices to be accessed by a gateway. I've already put the same secret (jhipster.security.authentication.jwt.secret) for all components (gateway, microservices and registry).
But, when I try to consume a microservice from the gateway, by the following way, I always receive a Access Denied Message (Forbidden 403):
#FeignClient(value = "storageservice")
public interface StorageServiceClient {
#RequestMapping(method = RequestMethod.GET, value = "api/applications")
ResponseEntity<List<ApplicationDTO>> getApplications();
}
I'm testing by this way (in my Gateway):
#RestController
#RequestMapping("/api")
public class StorageServiceTest {
#Inject
private DiscoveryClient discoveryClient;
#Inject
private RestTemplate template;
#Inject
private SecurityConfiguration security;
#Inject
private StorageServiceClient storageServiceClient;
#Inject
private AuthenticationManager authenticationManager;
#RequestMapping(value = "/apps",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
#Timed
public ResponseEntity<List<ApplicationDTO>> getApplications(){
ResponseEntity<List<ApplicationDTO>> apps = storageServiceClient.getApplications();
Stream.of(apps).forEach(System.out::println);
return apps;
}
}
The interest thing is that I've tested the direct access to the microservice (via postman) and the routed access by the gateway and either have worked well. But, specifically in this case when I try to consume the microservice data from gateway via API, I receive the 403 error.
In my microservice I have the configuration (in MicroServiceConfiguration class ".antMatchers("/api/**").authenticated()"). But in all the cases, when I'm testing via Postman, I'm always passing a valid JWT Token. But only in this specific case it doesn't works... I even tried to use a #Header annotation in my "StorageServiceClient" but it didn't any effect.
*********** EDITED ***********:
I've solved the authentication issue by creating a RequestInterceptor like that:
#Configuration
public class FeignConfig {
#Inject
private JHipsterProperties properties;
#Bean
public RequestInterceptor requestTokenBearerInterceptor() {
return requestTemplate -> {
String token = Jwts.builder().
setSubject("admin")
.claim("auth", "admin")
.signWith(SignatureAlgorithm.HS512, properties.getSecurity().getAuthentication().getJwt().getSecret())
.compact();
requestTemplate.header("Authorization", "Bearer " + token);
};
}}
I have developed own idGenerator based on Hazelcast IdGenerator class (with storing each last_used_id into db). Now I want to run hazelcast cluster as a single java application and my web-application as other app (web-application restart shouldn't move id values to next block). I move MyIdGeneratorProxy and MyIdGeneratorService to new application, run it, run web-application as a hazelcast-client and get
IllegalArgumentException: No factory registered for service: ecs:impl:idGeneratorService
It was okay when client and server were the same application.
It seems it's unable to process without some clientProxy. I have compared IdGeneratorProxy and ClientIdGeneratorProxy and it looks the same. What is the idea? How to write client proxy for services? I have found no documentation yet. Is direction of investigations correct? I thought it is possible to divide hazelcast inner services (like a id generator service) and my business-processes. Should I store custom ClientProxy (for custom spi) in my web-application?
This is a demo how to create a client proxy, the missing part CustomClientProxy function call, is quit complicated(more like a server proxy,here is called ReadRequest, the server is called Operation), you can find a how AtomicLong implement.For every client proxy method you have to make a request.
#Test
public void client() throws InterruptedException, IOException
{
ClientConfig cfg = new XmlClientConfigBuilder("hazelcast-client.xml").build();
ServiceConfig serviceConfig = new ServiceConfig();
serviceConfig.setName(ConnectorService.NAME)
.setClassName(ConnectorService.class.getCanonicalName())
.setEnabled(true);
ProxyFactoryConfig proxyFactoryConfig = new ProxyFactoryConfig();
proxyFactoryConfig.setService(ConnectorService.NAME);
proxyFactoryConfig.setClassName(CustomProxyFactory.class.getName());
cfg.addProxyFactoryConfig(proxyFactoryConfig);
HazelcastInstance hz = HazelcastClient.newHazelcastClient(cfg);
Thread.sleep(1000);
for (int i = 0; i < 10; i++)
{
Connector c = hz.getDistributedObject(ConnectorService.NAME, "Connector:" + ThreadLocalRandom.current()
.nextInt(10000));
System.out.println(c.snapshot());
}
}
private static class CustomProxyFactory implements ClientProxyFactory
{
#Override
public ClientProxy create(String id)
{
return new CustomClientProxy(ConnectorService.NAME, id);
}
}
private static class CustomClientProxy extends ClientProxy implements Connector
{
protected CustomClientProxy(String serviceName, String objectName)
{
super(serviceName, objectName);
}
#Override
public ConnectorState snapshot()
{
return null;
}
#Override
public void loadState(ConnectorState state)
{
}
#Override
public boolean reconnect(HostNode node)
{
return false;
}
#Override
public boolean connect()
{
return false;
}
}
EDIT
In hazelcast the IdGenerate is implemented as a wrapper for AtomicLong, you should implement you IdGenerate by you own, instead of extend IdGenerate.
So you have to implement these(more like a todo list XD):
API
interface MyIdGenerate
Server
MyIdGenerateService
MyIdGenerateProxy
MyIdGenerateXXXOperation
Client
ClientMyIdGenerateFactory
ClientMyIdGenerateProxy
MyIdGenerateXXXRequest
I also made a sequence(same as IdGenerate) here, this is backed by zookeeper or redis,also it's easy to add a db backend,too.I will integrate to hazelcast if I got time.