Eureka registry
Eureka is like Didi, responsible for managing and recording the information of service providers. Service callers do not need to find services themselves, but tell Eureka their needs, and Eureka will tell you the services that meet your needs.
At the same time, the service provider and Eureka are monitored through the "heartbeat" mechanism. When a service provider has a problem, Eureka will naturally remove it from the service list.
This realizes the automatic registration, discovery and status monitoring of services.
schematic diagram
Eureka
It is the service registry (which can be a cluster) that exposes its address to the outside world
- Provider: register your information with Eureka after startup (address, what services to provide)
- Consumer: subscribe to the service from Eureka. Eureka will send the address list of all providers of the corresponding service to consumers and update it regularly
- Heartbeat (renewal): the provider periodically updates its status to Eureka through http
eureka services
server: port: 8007 # port spring: application: name: eureka-server # The application name will be displayed in Eureka eureka: client: service-url: # The address of EurekaServer is now your own address. If it is a cluster, you need to add the addresses of other servers. defaultZone: http://127.0.0.1:${server.port}/eureka register-with-eureka: false fetch-registry: false
@SpringBootApplication @EnableEurekaServer public class SpringclouddemoeurekaApplication { public static void main(String[] args) { SpringApplication.run(SpringclouddemoeurekaApplication.class, args); } }
Register with eureka service
server: port: 8081 spring: application: name: service-provider # Application name, service name after registration with eureka eureka: client: service-url: # EurekaServer address defaultZone: http://127.0.0.1:8007/eureka
@SpringBootApplication //Other services of the registry client can @EnableDiscoveryClient //Or Eureka services only @EnableEurekaClient public class SpringclouddemozuulApplication { @Bean public RestTemplate restTemplate(){ return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(SpringclouddemozuulApplication.class, args); } }
@Controller @RequestMapping("consumer/user") public class UserController { @Autowired private RestTemplate restTemplate; @Autowired private DiscoveryClient discoveryClient; // eureka client can obtain the information of services in eureka @GetMapping @ResponseBody public User queryUserById(@RequestParam("id") Long id){ // Obtain the service instance according to the service name. It may be a cluster, so it is a collection of service instances List<ServiceInstance> instances = discoveryClient.getInstances("service-provider"); // Because there is only one service provider. So get the first instance ServiceInstance instance = instances.get(0); // Obtain ip and port information and splice them into service addresses String baseUrl = "http://" + instance.getHost() + ":" + instance.getPort() + "/user/" + id; User user = this.restTemplate.getForObject(baseUrl, User.class); return user; } }
Load balancing Ribbon
Eureka has integrated a load balancing component: Ribbon, which can be used simply by modifying the code.
Turn on load balancing
@Bean @LoadBalanced // Note to enable load balancing public RestTemplate getRestTemplate(){ return new RestTemplate(); }
The ip and port of the controller class url become the service name
@Controller @RequestMapping("/consumer") public class StuController { @Autowired RestTemplate restTemplate; @RequestMapping("/findStu") @ResponseBody public List<Student> findStu(){ String baseUrl = "http://service-provider/findStu"; List<Student> list = restTemplate.getForObject(baseUrl, List.class); return list; } }
The default setting for load balancing is polling
Change to random. Add this configuration to the caller of the service. The service name is. ribbon.NFLoadBalancerRuleClassName
service-provider: ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
Fuse mechanism Hystrix
Hystrix, which means porcupine in English, is covered with thorns. It looks bad. It is a protective mechanism.
Hystrix is also a component of Netflix.
Home page: https://github.com/Netflix/Hystrix/
So what is the role of Hystix? What exactly should we protect?
Hystix is an open-source delay and fault-tolerant Library of Netflix, which is used to isolate access to remote services and third-party libraries to prevent cascading failures.
Avalanche problem
In microservices, the calling relationship between services is complex. A request may need to call multiple microservice interfaces to realize, which will form a very complex calling link:
As shown in the figure, A business request needs to call four services A, P, H and I, which may call other services.
If an exception occurs in a service at this time:
For example, if an exception occurs in microservice I, the request is blocked and the user will not get a response, the thread of tomcat will not be released. Therefore, more and more user requests come and more threads will be blocked:
The number of threads and concurrency supported by the server is limited, and the requests are blocked all the time, which will lead to the depletion of server resources, resulting in the unavailability of all other services, forming an avalanche effect.
This is like an automobile production line that needs different parts to produce different cars. If a part cannot be used for various reasons, the whole car will not be able to assemble and fall into the state of waiting for parts. Assembly can not continue until the parts are in place. At this time, if many models need this part, the whole factory will fall into a waiting state, resulting in the paralysis of all production. The scope of a part is expanding.
There are two ways for Hystix to solve the avalanche problem:
- Thread isolation
- Service fuse
Thread isolation, service degradation
Thread isolation diagram:
Hystrix allocates a small thread pool for each dependent service call. If the thread pool is full, the call will be rejected immediately. By default, queuing is not used (to prevent avalanche). Speed up the failure determination time.
The user's request will no longer directly access the service, but through the idle thread in the thread pool. If the thread pool is full or the request times out, it will be degraded. What is service degradation?
Service degradation: give priority to the core services, rather than the unavailability or weak availability of the core services.
When the user's request fails, it will not be blocked, nor will it wait endlessly or see the system crash. At least one execution result can be seen (for example, return a friendly prompt).
Although service degradation will lead to request failure, it will not lead to blocking. At most, it will affect the resources in the thread pool corresponding to the dependent service and will not respond to other services.
The following conditions trigger the degradation of the Hystix service:
- Thread pool full
- request timeout
Enable Hystrix
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
Open fuse annotation
//Fuse notes
@EnableCircuitBreaker //Registry client @EnableEurekaServer @SpringBootApplication public class SpringclouddemocustomerApplication { @Bean @LoadBalanced public RestTemplate getRestTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(SpringclouddemocustomerApplication.class, args); } }
We can see that there are more and more annotations on our classes. The above three annotations are often introduced into micro services, so Spring provides a composite annotation: @ SpringCloudApplication
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootApplication @EnableDiscoveryClient @EnableCircuitBreaker public @interface SpringCloudApplication { }
@SpringCloudApplication public class GecServiceConsumerApplication { @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(GecServiceConsumerApplication.class, args); } }
Write degradation logic
We transform the GEC service consumer. When the call of the target service fails, we hope to fail quickly and give users a friendly prompt. Therefore, it is necessary to write the degradation processing logic in case of failure in advance, and use HystixCommond to complete:
Modify com.gec.service.controller.StuController and add degradation code
@Controller @RequestMapping("/consumer") public class StuController { @Autowired RestTemplate restTemplate; @RequestMapping("/findStu") @ResponseBody @HystrixCommand(fallbackMethod = "findStuFallBack" ) public String findStu(){ String baseUrl = "http://service-provider/findStu"; List<Student> list = restTemplate.getForObject(baseUrl, List.class); return list.toString(); } // Logical method of service degradation public String findStuFallBack(){ return "The request timed out. Please try again later"; } }
@HystrixCommand(fallbackMethod = "queryByIdFallBack"): a method used to declare a degraded logic
When the GEC service provder provides services normally, the access is the same as before. However, when we shut down the GEC service provider, we will find that the page returns the degradation processing information:
Set timeout
**In the previous case, the request will return an error message after more than 1 second. This is because the default timeout of Hystix is 1. We can modify this value through configuration:
**
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds to set the hystrix timeout.
hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 6000 # Set the timeout of hystrix to 6000ms
Feign - Integrated Ribbon, hystrix
Feign can hide the Rest request and disguise it as a Controller similar to spring MVC. You don't have to splice URLs, splice parameters and so on. Let feign do everything.
Feign only needs to define the interface and use annotations
Feign also integrates RIbbon and supports load balancing by default
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
Enable Feign function
@SpringBootApplication feign @EnableFeignClients Fuse notes @EnableCircuitBreaker //@SpringCloudApplication //Registration Center @EnableEurekaServer public class CloudNewsWebApplication { public static void main(String[] args) { SpringApplication.run(CloudNewsWebApplication.class, args); } }
Delete RestTemplate: feign has automatically integrated the RestTemplate of Ribbon load balancing. Therefore, there is no need to register RestTemplate here.
In the GEC service consumer project, add the UserClient remote call interface
@FeignClient(value = "cloud-news-query", qualifier = "cloud-news-query", fallback = NewsClientFallback.class) public interface NewsFeignClient { @LoadBalanced @GetMapping("/queryNews") ResultDTO<List<News>> queryNews(); @LoadBalanced @GetMapping("/queryCategory") ResultDTO<List<Category>> queryCategory(); @LoadBalanced @GetMapping("/queryNewById") ResultDTO<News> queryNewById(@RequestParam(name = "id") Integer id); @LoadBalanced @PostMapping("/findByCategoryIdTitle") ResultDTO<List<News>> findByCategoryIdTitle(@RequestBody News news); }
First, this is an interface. Feign will help us generate implementation classes through dynamic proxies. This is very similar to mybatis mapper
@FeignClient, declare that this is a Feign client, similar to the @ Mapper annotation. At the same time, specify the service name through the value attribute
The methods defined in the interface completely adopt the annotations of spring MVC. Feign will help us generate URL s according to the annotations and access the results
Hystrix support
Turn on the fuse function
feign: hystrix: enabled: true # Turn on the fusing function of feign
Implement the StuClient interface just written as a fallback processing class
@Component public class NewsClientFallback implements NewsFeignClient{ @Override public ResultDTO<List<News>> queryNews() { return ResultUtil.error(CodeEnum.FAil.code,"request timeout"); } @Override public ResultDTO<List<Category>> queryCategory() { return ResultUtil.error(CodeEnum.FAil.code,"request timeout"); } @Override public ResultDTO<News> queryNewById(Integer id) { return ResultUtil.error(CodeEnum.FAil.code,"request timeout"); } @Override public ResultDTO<List<News>> findByCategoryIdTitle(News news) { return ResultUtil.error(CodeEnum.FAil.code,"request timeout"); } }
controller class
@Controller public class NewsController { @Qualifier("cloud-news-query") @Autowired NewsFeignClient newsFeignClient; @Qualifier("cloud-news-edit") @Autowired NewsEditFeignClient newsEditFeignClient; @GetMapping("/queryNews") public ModelAndView queryNews(@ModelAttribute("newsCondition") News news) { ModelAndView modelAndView = new ModelAndView(); ResultDTO<List<News>> newsResultDTO = newsFeignClient.queryNews(); ResultDTO<List<Category>> categoryResultDTO = newsFeignClient.queryCategory(); modelAndView.addObject("newsList", newsResultDTO.getData()); modelAndView.addObject("cateList", categoryResultDTO.getData()); modelAndView.setViewName("index"); return modelAndView; } @GetMapping("/toUpdate/{id}") public ModelAndView toupdate(@PathVariable int id) { ModelAndView modelAndView = new ModelAndView(); ResultDTO<News> newsResultDTO = newsFeignClient.queryNewById(id); ResultDTO<List<Category>> categoryResultDTO = newsFeignClient.queryCategory(); modelAndView.addObject("newsDetail", newsResultDTO.getData()); modelAndView.addObject("categoryList", categoryResultDTO.getData()); modelAndView.setViewName("updateNews"); return modelAndView; } @PostMapping("/doUpdateSubmit") public String doUpdateSubmit(News news) { ResultDTO<Integer> integerResultDTO = newsEditFeignClient.updateNewsById(news); return "redirect:/queryNews"; } @PostMapping("/queryByCondition") public ModelAndView queryByCondition(@ModelAttribute("newsCondition") News news) { ModelAndView modelAndView = new ModelAndView(); ResultDTO<List<News>> newsResultDTO = newsFeignClient.findByCategoryIdTitle(news); ResultDTO<List<Category>> categoryResultDTO = newsFeignClient.queryCategory(); modelAndView.addObject("newsList", newsResultDTO.getData()); modelAndView.addObject("cateList", categoryResultDTO.getData()); modelAndView.setViewName("index"); return modelAndView; } }
zuul gateway
The architecture of using Spring Cloud to realize microservices is basically formed, which is roughly as follows:
We use Eureka in Spring Cloud Netflix to implement the service registry and service registration and discovery; The service room realizes service consumption and load balancing through Ribbon or Feign. In order to make the service cluster more robust, Hystrix's breaking mechanism is used to avoid the fault spread caused by the exception of individual services in the microservice architecture.
In this architecture, our service cluster includes internal services Service A and Service B, both of which register and subscribe to Eureka Server, while Open Service is an external service, which is exposed to the service caller through load balancing. It is unreasonable for us to focus on external services and directly expose our service address
Let's talk about some things that need to be done and shortcomings of this architecture:
-
It destroys the stateless characteristics of service.
In order to ensure the security of external services, we need to realize the permission control of service access, and the permission control mechanism of open services will run through and pollute the business logic of the whole open services. The most direct problem will be that it destroys the stateless characteristics of rest APIs in service clusters. (in stateless Web services, each Web request must be independent and completely separated from each other. The Server does not save the status information of the Client, so the request sent by the Client must contain all the information that can enable the Server to understand the request, including its own status information. This enables a Client's Web request to be answered by any available Server, so that the Web The system is extended to a large number of clients.)
From the perspective of specific development and testing, in addition to the actual business logic, we also need to consider the control and processing of interface access.
-
Existing interfaces cannot be reused directly.
When we need to access external services to an existing access interface in the cluster, we have to add verification logic to the original interface or add a proxy call to realize permission control, so we can't reuse the original interface directly.
In order to solve the above problems, we need to separate things such as permission control from our service unit, and the most suitable place for these logic is at the front end of external access. We need a more powerful service gateway of load balancer.
Service gateway is an indispensable part of microservice architecture. In the process of uniformly providing rest APIs to the external system through the service gateway, it not only has the functions of service routing and load balancing, but also has the functions of permission control. Zuul in Spring Cloud Netflix plays such a role, providing front door protection for the micro service architecture. At the same time, it migrates the heavy non business logic content of permission control to the service routing level, so that the service cluster subjects can have higher reusability and testability.
Architecture after Zuul's addition
Whether it is a request from the client (PC or mobile terminal) or an internal call of the service. All requests for services will pass through zuul gateway, and then the gateway will realize authentication, dynamic routing and other operations. Zuul is the unified entrance to our services.
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency>
@EnableZuulProxy// Notes for opening the gateway @SpringBootApplication public class GecZuulApplication { public static void main(String[] args) { SpringApplication.run(GecZuulApplication.class, args); } }
spring: application: name: gec-zuul server: port: 8899 zuul: routes: service-provider: # The route name is usually the name of the microservice path: /service-provider # Mapping path url: http://Localhost: url address corresponding to 8080 # mapping path
Integration of eureka registration service and automatic routing
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
zuul: routes: service-provider: # The route name is usually the name of the microservice path: /service-provider # Mapping path serviceId: service-provider # Service name
@EnableDiscoveryClient // Enable eureka client function @EnableZuulProxy // Notes for opening the gateway @SpringBootApplication public class GecZuulApplication { public static void main(String[] args) { SpringApplication.run(GecZuulApplication.class, args); } }
zuul: routes: service-provider: /service-provider eureka: client: registry-fatch-interval-seconds: 5 # Get cycle of service list service-url: # EurekaServer address defaultZone: http://127.0.0.1:8007/eureka spring: application: name: gec-zuul server: port: 8899