This time I share with you how to consume services. The previous article talks about using Feign for consumption, this article uses rest+ribbon consumption services, and customizes a simple consumption component through polling. The purpose of this article is: the idea of customizing consumption services; if there are any advantages, please "like":
Rest+Ribbon realizes consumer services
As a service consumer, we have made two main processes to distinguish between 1) Obtaining services and 2) Calling services. So how to obtain services and what to call services? Let’s take a look at a manual diagram below:
It can be seen from the manual diagram that the consumer first obtains the real interface address of the service provider, and then calls the interface through the address; then for the microservice architecture, it is definitely not advisable to obtain a certain class IP or port and then call the interface, so a concept of serviceid has emerged in the microservice; the simple process has been introduced, and the following is an example to analyze it; first add dependencies such as:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency><dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId></dependency>
Let’s use the eureka_server (service center) and eureka_provider (service provider) built in the previous article to perform test cases. Here I redefine the eureka_consumer_ribbon module as a consumer service; first create the service layer class and code:
@Servicepublic class UserService implements UserInterface { @Autowired protected RestTemplate restTemplate; @Override public MoRp<List<MoUser>> getUsers(MoRq rq) { return null; } @Override public String getMsg() { String str = restTemplate.getForObject("http://EUREKA-PROVIDER/msg", String.class); return str; }}Mainly use the restTemplate.getForObject function of RestTemplate, and then you need to define a controller to respond to the obtained data on the page. For simplicity, just use the getMsg service interface to test:
@RestControllerpublic class UserController { @Autowired private UserService userService; @GetMapping("/msg") public String getMsg(){ return userService.getMsg(); }}Finally, we add the following code to the startup class. Note that the @LoadBalanced tag must be added, because the eureka dependency we introduced contains ribbon (Dalston.RELEASE version). ribbon encapsulates load balancing algorithm. If this annotation is not added, the url of the rest method must be the available url path. Of course, if the annotation is added here, you can use the serviceId mentioned above:
@SpringBootApplication@EnableDiscoveryClient //Consumer client public class EurekaConsumerRibbonApplication { @Bean @LoadBalanced //Load Balancing RestTemplate restTemplate(){ return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(EurekaConsumerRibbonApplication.class, args); }}The following is the effect displayed by the consumer:
Rest+Poll Custom Simple Consumption Components
The custom consumption component is almost the same as the manual drawing. It is to first get the real interface address of the service provider, and then call the url through rest to get the corresponding result output; here is a ShenniuBanlance component class:
/** * Created by shenniu on 2018/6 * <p> * rest+eureka+custom client*/@Componentpublic class ShenniuBanlance { @Autowired private RestTemplate restTemplate; @Autowired private DiscoveryClient discoveryClient; /** * Service real address ConcurrentHashMap<"Service application name", ("Real interface ip", number of visits)> */ public static ConcurrentHashMap<String, List<MoService>> servicesMap = new ConcurrentHashMap<>(); /** * Set service provider information to map */ public void setServicesMap() { //Get all service provider applicationName List<String> appNames = discoveryClient.getServices(); //Storage the real address to map for (String appName: appNames) { //Get information of a service provider List<ServiceInstance> instanceInfos = discoveryClient.getInstances(appName); if (instanceInfos.isEmpty()) { continue; } List<MoService> services = new ArrayList<>(); instanceInfos.forEach(b -> { MoService service = new MoService(); //Number of accessed service.setWatch(0L); //Real interface address service.setUrl(b.getUri().toString()); services.add(service); }); //If there is, update servicesMap.put(appName.toLowerCase(), services); } } /** * Service selected according to the app * * @param appName * @return */ public MoService choiceServiceByAppName(String appName) throws Exception { appName = appName.toLowerCase(); //Some app service service collection List<MoService> serviceMap = servicesMap.get(appName); if (serviceMap == null) { //Initialize all app services setServicesMap(); serviceMap = servicesMap.get(appName); if (serviceMap == null) { throw new Exception("Failed to find" + appName + "Related Services"); } } //Filter out the service polling method with the smallest number of visits MoService moService = serviceMap.stream().min( Comparator.comparing(MoService::getWatch) ).get(); //Load record +1 moService.setWatch(moService.getWatch() + 1); return moService; } /** * Automatically refresh the service provider information to map */ @Scheduled(fixedDelay = 1000 * 10) public void refreshServicesMap() { setServicesMap(); } /** * get request service to get the return data* * @param appName Application name ApplicationName * @param serviceName ServiceName * @param map request parameter on url* @param tClass Return type* @param <T> * @return */ public <T> T getServiceData( String appName, String serviceName, Map<String, ?> map, Class<T> tClass) { T result = null; try { //Filter to get the real Service MoService service = choiceServiceByAppName(appName); //Request the url of the service String apiUrl = service.getUrl() + "/" + serviceName; System.out.println(apiUrl); result = map != null ? restTemplate.getForObject(apiUrl, tClass, map) : restTemplate.getForObject(apiUrl, tClass); } catch (Exception ex) { ex.printStackTrace(); } return result; } /** * Service information*/ public class MoService { /** * Number of load records*/ private Long watch; /** * Real interface address: http://xxx.com/api/add */ private String url; public Long getWatch() { return watch; } public void setWatch(Long watch) { this.watch = watch; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } }}The above is the main implementation code. Code logic: Set the service provider information to map-》 According to the app to obtain the polling method, the service-》 requests the service to obtain the return data; the principle of the polling implementation is to use a load record number, which automatically +1 after each request. When you want to obtain a certain service provider, an instance of the minimum value is filtered out through the record number, and the real interface address url is stored; the call only needs to be like this (of course, it can be called as annotations):
@Override public String getMsg() { String str = banlance.getServiceData( "EUREKA-PROVIDER", "msg", null, String.class ); return str; }It should be noted here that we added the annotation @LoadBalanced in the previous RestTemplate, so that the rest request must be accessed in a non-ip (that is, serviceid) to respond normally, otherwise an error will be prompted such as:
Simply put, you don’t need to use IP anymore because there is a load balancing mechanism; when we remove this annotation, our customized components will be able to run successfully, and the renderings are the same as those in Example 1, and the map will not be sticked to;
Refresh service provider information using Scheduled
In the microservice architecture, if a service is hung up, the client's service cache information must be updated in time, otherwise it may request to the down URL. Based on this consideration, I used the EnableSched tag to perform timed refresh; first add @EnableScheduling to the startup class, and then define a service that flashes the service information such as:
/** * Automatically refresh the service provider information to map */ @Scheduled(fixedDelay = 1000 * 10) public void refreshServicesMap() { setServicesMap(); }In order to facilitate the test effect, when the server, provider (2), and consumer have been started, we start a provider service with port 2005; then refresh the consumer interface to see the effect:
At this time, you can see that the interface calling the 2005 port has been successfully called. After the latest or invalid service is added to the @Scheduled timed service, it will meet the needs of what you need; if you think this content is helpful to you, please like it, thank you. I hope it will be helpful to everyone's learning, and I hope everyone will support Wulin.com more.