Ribbon (load balancing)
In a production environment, one server is often not enough. Building clusters is essential. After the cluster is set up and registered with Eureka, how does Eureka know which to call each time? Call the same one every time? The cluster is the same as not built. Random call? China is OK, I think so! But I think it's better to configure the same server one by one. So Ribbon was born.
There is no need to introduce any jar package to use ribbon because ribbon and Eureka are produced by the same company. Eureka was designed with load balancing in mind. You only need to open it with a comment.
Using the ribbon process:
- Add the @ LoadBalanced annotation on the consumer RestTemplate class. Because the consumer invokes the service remotely through HTTP. RestTemplate encapsulates http. After load balancing is enabled, your service list is pulled by RestTemplate. Here is a code snippet:
package com.zhao.user; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @SpringBootApplication @EnableDiscoveryClient //Enable eureka client discovery public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); } }
Modify the Controller to access the service id.
package com.zhao.user.controller; import com.zhao.user.pojo.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import java.util.List; @RestController @RequestMapping("/consumer") public class UserController { @Autowired private RestTemplate restTemplate; @GetMapping("/{id}") public User queryById(@PathVariable Long id){ String url = "http://user-service/user/"+id; //Get the instance of user service registered in eureka return restTemplate.getForObject(url, User.class); } }
- Register multiple service s, no more demo
- Source tracking
As can be seen from the figure above, ribbon load balancing uses polling by default and accesses one by one. In fact, there are 7 load balancing strategies by default
Policy class | name | describe |
---|---|---|
RandomRule | Random strategy | Randomly select server |
RoundRobinRule | round-robin policy | Polling selection, polling index, and selecting the Server corresponding to the index; |
RetryRule | Retry policy | For the on-board retry mechanism of the selected load balancing policy, if the server selection is unsuccessful within a configured time period, it has been trying to select an available server by using the subRule method |
BestAvailableRule | Minimum concurrency policy | Check the servers one by one. If the server circuit breaker is open, ignore it, and then select the server with the lowest concurrent link |
AvailabilityFilteringRule | Available filtering policies | Filter out the servers that fail all the time and are marked as circuit tripped, filter out those servers with high concurrency links (active connections exceed the configured threshold), or use an availability predict to include the logic of filtering servers, which is actually to check the running status of each server recorded in the status; |
ResponseTimeWeightedRule | Response time weighted weight strategy | The weight is allocated according to the response time of the server. The longer the response time, the lower the weight, and the lower the probability of being selected. The shorter the response time, the higher the weight, and the higher the probability of being selected. This strategy is very appropriate and integrates various factors, such as network, disk, io, etc., which directly affect the response time |
ZoneAvoidanceRule | Regional weight strategy | Comprehensively judge the performance of the area where the server is located and the availability of the server, poll and select the server, judge whether the operation performance of an AWS Zone is available, and eliminate all servers in the unavailable Zone |
Modify by setting yml file
user-service: ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
Hystrix (fuse)
Minor sequence:
My name is porcupine, also known as Zhugang hyena. My nickname is Jie Jie, and my number is Wuneng. Everyone calls me the messenger of the net altar. In short, I'm covered with thorns to protect myself.
Text:
When I call a service, the thread has been blocking the provider. In this way, the thread pool resources of our consumer will be exhausted before long. This is not what I want to see, but it is inevitable. So Zhugang hyena came. She was like a rich woman. She saw through my strength and let me take off all my disguises and walk into her heart.
How safe is she? For example, as long as your consumer integrates Hystrix, it will allocate a small thread pool for your service. If the thread pool is full or the request times out, it will be degraded. Queuing is not used by default to speed up the failure determination time.
Take you to experience:
- Introducing jar package on the consumer side:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
- Add the @ enablercircuitbreaker annotation above the startup class to start the service
/*@SpringBootApplication @EnableDiscoveryClient //Enable eureka client discovery @EnableCircuitBreaker //Open service fuse*/ @SpringCloudApplication //The above three annotations can be combined into one annotation public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } }
- Write a service degradation method to give users a friendly prompt.
public String queryByIdFallBack(Long id){ log.error("Failed to query user information. id: {}", id); return "Sorry, the Internet is too crowded!"; //Sometimes this line appears when you grab tickets because the server has hung up }
- Add @ HystrixCommand(fallbackMethod = "queryByIdFallBack") above the method to be degraded and indicate the name of the degraded method
@GetMapping("/{id}") @HystrixCommand(fallbackMethod = "queryByIdFallBack") public String queryById(@PathVariable Long id){ if (id == 1){ throw new RuntimeException("invalid id"); } String url = "http://user-service/user/"+id; //Get the instance of user service registered in eureka return restTemplate.getForObject(url, String.class); }
- Test the effect yourself, but remind me that when you test for the first time, it will show "sorry, the network is too crowded!", This is because java code compilation takes time. If the consumer calls the provider for more than 1 second, the service degradation will be triggered, and the retest will be normal after compilation. Timeout waiting can be modified through configuration
hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 2000
Eureka inexplicably removed me from the registration center list last time, just because I was a little late because of my network delay. In the end, he made both compensation and land cutting, which made me very uncomfortable. There are similar scenarios in Hystrix. There is a common thing in everyone's home, the electric switch, which has a fuse made of aluminum antimony alloy. When the current passing through it is too large, it will fuse itself to protect the safety of the circuit.
Put it on the Hystrix, that is, you have a request that always fails. But there's no problem with other requests. Now a hacker knows this, and he is always happy to call your failed requests. Your thread pool will soon be full of useless requests. Other available requests can only be watched! The uncle can bear it, neither can the aunt!
Pig ganghyena's solution is that if you call useless requests more than 20 times, and half or more of the call results fail. I think you're here to do something. I'll turn on the service fuse and downgrade all services of your interface. I go to sleep for five seconds and wake up. I will release some requests and take a sneak look. If the request for something is still there, I will sleep for another 5 seconds. This cycle. If the trouble maker is gone, I'll turn off the fuse until I meet the next one
effect:
Implementation steps:
- Set the fusing duration and the minimum number of fusing trigger requests
hystrix: command: default: circuitBreaker: errorThresholdPercentage: 50 # Trigger fusing error proportional threshold, default 50% sleepWindowInMilliseconds: 10000 # Sleep duration after fusing, the default value is 5 seconds requestVolumeThreshold: 10 # The minimum number of fuse trigger requests. The default value is 20 execution: isolation: thread: timeoutInMilliseconds: 2000 # Fuse timeout setting, the default is 1 second
- Modify controller
@GetMapping("/{id}") @HystrixCommand() public String queryById(@PathVariable Long id){ if (id == 1){ time++; System.out.println(time); throw new RuntimeException("invalid id"); } String url = "http://user-service/user/"+id; //Get the instance of user service registered in eureka return restTemplate.getForObject(url, String.class); }
Just ask http://localhost:8080/consumer/1 It's bound to go wrong
Just ask http://localhost:8080/consumer/ 3. You will succeed
-
The effect is shown in the figure above. If the request id is 1 for more than 10 times, an error is reported for 10 times, triggering the fuse mechanism. After 10 seconds, request the correct address again, and the fuse is half closed and open. You can request the correct results.
Feign (disguise)
Now most of the requirements of spring cloud are met, but the only thing that bothers me is that the address of my consumer calling server needs to be spliced manually. If you don't pay attention, you'll splice it wrong. Take a closer look at this address:
String url = "http://user-service/user/"+id;
I should always have a service address id and protocol for calling the service provider. The protocol stipulates that HTTP is commonly used. I know which service name and path to call myself. At this point, I can use dynamic proxy to http://user-service/user/ This section is packed. The packaging process is as follows:
- The consumer introduces feign component jar package
-
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
- Create proxy interface
package com.zhao.user.client; import com.zhao.user.pojo.User; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @FeignClient("user-service"). //@FeignClient, declare that this is a Feign client, and specify the service name through the value attribute public interface UserClient { @GetMapping("/user/{id}") public User queryById(@PathVariable("id") long id); }
- Write a new controller and use UserClient access (the above proxy interface can be regarded as the interface of mapper layer)
-
package com.zhao.user.controller; import com.zhao.user.client.UserClient; import com.zhao.user.pojo.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/cf") public class ConsumerFeignController { @Autowired private UserClient userClient; @GetMapping("/{id}") public User queryById(@PathVariable Long id) { return userClient.queryById(id); } }
- Visit http://localhost:8080/cf/3 Success!
Access process: access the cf/3 interface -- > call the fegin client (create a proxy class and access the provider) - > call the provider service -- > get the result and return it
The above is to achieve the effect that there is no need to manually splice url addresses, but what should I do with my load balancing and service fusing? Don't worry, fegin has already figured it out for you. It has integrated Ribbon and Hystrix into its own project. By default, the service provider calls the provider for more than 1 second and times out to degrade the service. The configuration in fegin is as follows
ribbon: ConnectTimeout: 1000 # Connection timeout duration ReadTimeout: 3000 # Call the service, and the total time returned by the service is equivalent to the fuse timeout setting above MaxAutoRetries: 0 # Number of retries for the current server MaxAutoRetriesNextServer: 0 # How many service retries OkToRetryOnAllOperations: false # Do you want to retry all request methods NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
Remember! The above configuration is only configured by the fegin integration ribbon. In other words, you can configure this configuration only when you use the fegin client.
Load balancing is realized. How should the service be configured? The configuration is as follows:
- Turn on the fuse function of fegin in the yml of the consumer
feign: hystrix: enabled: true # Turn on the fusing function of Feign
- Write service degradation class
package com.zhao.user.client.fallback; import com.zhao.user.client.UserClient; import com.zhao.user.pojo.User; import org.springframework.stereotype.Component; @Component public class UserClientFallback implements UserClient { @Override public User queryById(long id) { User user = new User(); user.setId(id); user.setName("User exception"); return user; } }
- Specify the service degradation class in the fegin client
package com.zhao.user.client; import com.zhao.user.client.fallback.UserClientFallback; import com.zhao.user.pojo.User; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @FeignClient(value = "user-service",fallback = UserClientFallback.class) //fallback indicates the service degradation class public interface UserClient { @GetMapping("/user/{id}") public User queryById(@PathVariable("id") long id); }
- When the service provider sleeps for 2 seconds, it will definitely timeout. After timeout, the service will be degraded and the effect will be worse
Sometimes the amount of data requested or responded to is too large, so your transmission process will be very slow. To be ridiculous, you have transmitted half of it, and the server is down. You are in a hurry! So transmission speed is very important. fegin supports request / response compression. The configuration is as follows
Add configuration in the consumer yml
feign: hystrix: enabled: true # Turn on the fusing function of Feign compression: request: enabled: true # Turn on request compression mime-types: text/html,application/xml,application/json # Set compressed data type min-request-size: 2048 # Sets the lower limit of the size that triggers compression response: enabled: true # Turn on response compression
Effect, I'm sorry, I can't find a larger format to test. As long as the startup does not report an error, it is feasible by default
Log level of fegin. The spring boot configuration log level is to add logging. XML in yml level.** = debug (* * indicates the package name). This is not the case in fegin. When the client modified by @ FeignClient} annotation is proxied, a new instance of Fegin.Logger will be created. We need to specify the level of this log. The process is as follows:
- Added to the consumer yml
logging: level: com.zhao.user: debug
- Write config class
package com.zhao.user.config; import feign.Logger; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class FeignConfig { @Bean Logger.Level feignLoggerLevel() { //Record the details of all requests and responses, including header information, request body and metadata return Logger.Level.FULL; } }
- Add the debug log class in the fegin client interface
package com.zhao.user.client; import com.zhao.user.client.fallback.UserClientFallback; import com.zhao.user.config.FeignConfig; import com.zhao.user.pojo.User; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @FeignClient(value = "user-service",fallback = UserClientFallback.class,configuration = FeignConfig.class) public interface UserClient { @GetMapping("/user/{id}") public User queryById(@PathVariable("id") long id); }
- The test has no effect of adding logs
- Added log effect
Aside, fegin supports 4 log levels
- NONE: no log information is recorded, which is the default value.
- BASIC: only record the requested method, URL, response status code and execution time
- HEADERS: on the basis of BASIC, additional header information of request and response is recorded
- FULL: records the details of all requests and responses, including header information, request body and metadata.
FULL is used in the above code, and the last one
Spring cloud gateway (Gateway)
Whether the request comes from the client (PC or mobile terminal) or the internal call of the service, all requests for the service can pass through the Gateway, and then the Gateway can realize authentication, dynamic routing and other operations. The Gateway is the unified entrance of our service.
The composition of route information: it consists of an ID, a destination URL, a group of assertion factories and a group of filters. If the route assertion is true, it indicates that the request URL matches the configured route.
The input type of the assertion function in Spring Cloud Gateway is ServerWebExchange in Spring 5.0 framework. The assertion function of Spring Cloud Gateway allows developers to define and match any information from Http Request, such as request header and parameters.
Filter is a standard Spring WebFilter. There are two types of filters in Spring Cloud Gateway: Gateway Filter and Global Filter. The filter will modify requests and responses.
In my understanding of routing, when your front-end request comes and accesses my gateway layer, you should do it according to my rules. You have to visit whoever you want to visit.
For the understanding of assertions, my request path contains some values, but if you don't carry them, I assert that you access through the interface. If you don't follow the right path, you won't be released. Once my assertion is true, your visit fails
For the understanding of the filter, have you heard of it? Once someone approaches the emperor, search first and turn in all your weapons. Maybe you will wear special clothes to see the driver. Protect the server from malicious attacks.
Take you one by one:
Create a new project "gateway"
- Import jar package
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
- Create boot class
package com.zhao.geteway; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @SpringBootApplication @EnableDiscoveryClient //You need to register with the eureka registry. The eureka client discovery function must be enabled public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } }
- Set yml file
server: port: 10010 spring: application: name: api-gateway cloud: gateway: routes: # The routing id can be written at will - id: user-service-route # Service address of agent uri: http://127.0.0.1:9091 # Route assertion, you can configure the mapping path predicates: - Path=/user/** eureka: client: service-url: defaultZone: http://zhao:123456@localhost:10086/eureka instance: prefer-ip-address: true # Prefer to use ip rather than host name
-
Effect
I visited 10010, but I actually visited the service provider of 9091. Isn't it amazing! The gateway is equivalent to a forwarding operation.
The above operations still have some disadvantages. For example, I write the url of the provider to death, which makes it difficult for the instance under the same service id. the gateway's solution is that you give me the service id name and decide which one to call. Just modify the configuration
server: port: 10010 spring: application: name: api-gateway cloud: gateway: routes: # The routing id can be written at will - id: user-service-route # The service address of the agent; lb means to obtain specific services from eureka uri: lb://user-service # Route assertion, you can configure the mapping path predicates: - Path=/** filters: # Add prefix to request path - PrefixPath=/user eureka: client: service-url: defaultZone: http://zhao:123456@localhost:10086/eureka instance: prefer-ip-address: true # Prefer to use ip rather than host name
Adding the request path prefix above means that the original address to access the service is
Now you don't need to add / user for access. You can access it directly
If you are not satisfied with the provider's address and the front end wants to add some inexplicable rules, how can you ensure that the route is routed to the correct provider?
Very simple, refer to the following configuration
server: port: 10010 spring: application: name: api-gateway cloud: gateway: routes: # The routing id can be written at will - id: user-service-route # The service address of the agent; lb means to obtain specific services from eureka uri: lb://user-service # Route assertion, you can configure the mapping path predicates: - Path=/api/user/** filters: # It means filtering 1 path, 2 means two paths, and so on - StripPrefix=1 eureka: client: service-url: defaultZone: http://zhao:123456@localhost:10086/eureka instance: prefer-ip-address: true # Prefer to use ip rather than host name
Visit http://localhost:10010/api/user/2 You can get the correct return
Filter: divided into global filter and local filter.
Global filter adding method 1
Add filter to yml file
server: port: 10010 spring: application: name: api-gateway cloud: gateway: default-filters: # The response header filter sets the header attribute name of the output response as X-Response-Default-MyName and the value as zhao; If there are multiple parameters, override one line to set different parameters - AddResponseHeader=X-Response-Default-MyName, zhao routes: # The routing id can be written at will - id: user-service-route # The service address of the agent; lb means to obtain specific services from eureka uri: lb://user-service # Route assertion, you can configure the mapping path predicates: - Path=/api/user/** filters: # It means filtering 1 path, 2 means two paths, and so on - StripPrefix=1 eureka: client: service-url: defaultZone: http://zhao:123456@localhost:10086/eureka instance: prefer-ip-address: true # Prefer to use ip rather than host name
The obtained response header contains information with the value zhao
Global filter addition method 2
- Create global filter class
package com.zhao.geteway.filter; import org.apache.commons.lang.StringUtils; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; @Component public class MyGlobalFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { System.out.println("-----------------Global filter MyGlobalFilter------------------- --"); String token = exchange.getRequest().getQueryParams().getFirst("token"); if (StringUtils.isBlank(token)) { exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); return exchange.getResponse().setComplete(); } return chain.filter(exchange); } @Override public int getOrder() { //The smaller the value, the earlier the execution return 1; } }
- The access does not carry a token
- Carry token
Local filter
- Create local filter class
package com.zhao.geteway.filter; import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.stereotype.Component; import java.util.Arrays; import java.util.List; @Component public class MyParamGatewayFilterFactory extends AbstractGatewayFilterFactory<MyParamGatewayFilterFactory.Config> { static final String PARAM_NAME = "param"; public MyParamGatewayFilterFactory() { super(Config.class); } public List<String> shortcutFieldOrder() { return Arrays.asList(PARAM_NAME); } @Override public GatewayFilter apply(Config config) { return (exchange, chain) -> { // http://localhost:10010/api/user/8?name=itcast config.param ==> name //Gets the parameter value of the parameter name corresponding to param in the request parameter ServerHttpRequest request = exchange.getRequest(); if(request.getQueryParams().containsKey(config.param)){ request.getQueryParams().get(config.param). forEach(value -> System.out.printf("------------Local filter--------%s = %s------", config.param, value)); } return chain.filter(exchange); }; } public static class Config{ //Corresponds to the parameter name specified when configuring the filter private String param; public String getParam() { return param; } public void setParam(String param) { this.param = param; } } }
- Add filter parameters to yml
routes: # The routing id can be written at will - id: user-service-route # The service address of the agent; lb means to obtain specific services from eureka uri: lb://user-service # Route assertion, you can configure the mapping path predicates: - Path=/api/user/** filters: # It means filtering 1 path, 2 means two paths, and so on - StripPrefix=1 # Custom filter - MyParam=name
- Test: http://localhost:10010/api/user/8?name=zhao&token=zhao The console gets the results and prints them
The Gateway configures the service provider's load balancing and fusing, and adds the configuration in yml. As long as there is no error and the access path can be accessed, the default configuration has taken effect
hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 6000 ribbon: ConnectTimeout: 1000 ReadTimeout: 2000 MaxAutoRetries: 0 MaxAutoRetriesNextServer: 0
Gateway cross domain configuration
Cross domain: in js request access, if the access address is inconsistent with the domain name, ip or port number of the current server, it is called cross domain request. If it is not solved, the return result of the corresponding address cannot be obtained.
It is not difficult to understand that when someone accesses my server, because it is not the same domain, the request is sent from js. I suspect that others are attacking me and will prohibit access. When I set the access permission for a url, it can access my service through js. The configuration is as follows:
spring: application: name: api-gateway cloud: gateway: globalcors: corsConfigurations: '[/**]': #allowedOrigins: * # This way of writing or the following can be used, * means all allowedOrigins: - "http://docs.spring.io" allowedMethods: # Indicates that it can be accessed through get requests - GET
Start without error, and the default is feasible! Well, I admit I'm too lazy
The provider services registered in Eureka can be load balanced through rabbit. How does Ga teway registered in Eureka perform load balancing? Very simple, load balancing can be achieved through nginx. Nginx can distribute services by polling or randomly. Then you can call the distributed gateway from Eureka.
Spring Cloud Config (distributed configuration center)
Is it annoying to configure so many of the above configurations? Besides, it is only the configuration of one server. If it is the configuration of multiple servers, it is even more troublesome to modify them at the same time. Is there a way to centralize conventional configurations and pull them when they are used?
Yes, that's what we're going to talk about now. How does it do it? It is first like this, then like that, and finally like this. Genius is lonely, especially what he said. Fortunately, I can barely be an interpreter. It starts by creating a central repository in Git with regular configuration files. Then you can pull from git when you need it, and finally cache the pulled configuration locally.
The process is as follows:
- Create a git warehouse and place the service provider's application YML file. Code cloud is recommended for git warehouse practice. (Note: be careful when naming the document, because you need to use it when pulling)
- Create a new project
- Import jar package
<dependencies> <!--Service to register with eureka In, the index is to be imported eureka of jar package--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency> </dependencies>
- Create boot class
package com.zhao.config; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.config.server.EnableConfigServer; @SpringBootApplication @EnableConfigServer //Flag of configuration center service public class ConfigServerApplication { public static void main(String[] args) { SpringApplication.run(ConfigServerApplication.class, args); } }
-
Create application yml
server: port: 12000 spring: application: name: config-server cloud: config: server: git: # git address of remote warehouse uri: https://gitee.com/Iso-8859-2/zhao-config.git eureka: client: service-url: # The service needs to be registered with the registry defaultZone: http://zhao:123456@localhost:10086/eureka
-
Start test, access address http://localhost:12000/user-dev.yml (note) user-dev Is the file name created by my git repository)
-
The association between java and git is completed!
The above git warehouse is configured by our service provider!
- Add dependency to provider
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> <version>2.1.1.RELEASE</version> </dependency>
- Delete service provider application yml
- Add bootstrap. In the provider YML file. (the bootstrap.yml file is also the default configuration file for Spring Boot, and its loading time is earlier than that of application.yml.)
spring: cloud: config: # Be consistent with the application of the configuration file in the warehouse name: user # Keep consistent with the profile of the configuration file in the warehouse profile: dev # It should be the same as the version (Branch) to which the configuration file in the warehouse belongs label: master discovery: # Use configuration center enabled: true # Configuration center service name service-id: config-server # Configure rabbitmq information; If both are consistent with the default values, no configuration is required eureka: client: service-url: defaultZone: http://zhao:123456@localhost:10086/eureka
- Future configurations can be pulled directly from the configuration center
- Start test
So far, a modification has been completed, and the effect can be obtained everywhere. However, every time you pull the cache to the local, it is carried out when the service is just started. If you don't take the initiative to pull it, you won't get the latest notice! This is simple. Let me design it. When I finish modifying the configuration file in the warehouse, I will actively notify you through message oriented middleware. You can pull the latest configuration and update the cache.
Spring Cloud Bus (Service Bus)
The current service pulling process of our service provider is: config first obtains the service configuration file from git, and then the service provider obtains the configuration file from the config center and caches it locally. However, the configuration file config and provider are modified on git, but the latest notification is not available. It's really a headache. But I can take the initiative to inform you after every modification. The implementation process is as follows:
- Add the dependency of bus and rabbitmq in the config Center
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-bus</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-stream-binder-rabbit</artifactId> </dependency>
- Add rabbitmq and the address of the service exposure center to yml
server: port: 12000 spring: application: name: config-server cloud: config: server: git: # git address of remote warehouse uri: https://gitee.com/Iso-8859-2/zhao-config.git rabbitmq: host: 127.0.0.1 port: 5672 username: guest password: guest eureka: client: service-url: # The service needs to be registered with the registry defaultZone: http://zhao:123456@localhost:10086/eureka management: endpoints: web: exposure: # Expose the address of the trigger message bus, notify through this address every time, and re obtain the latest configuration file after notification include: bus-refresh
Configuration process of service provider
- Import jar package
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-bus</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-stream-binder-rabbit</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
- Add configuration of rabbitmq in yml
# Configure rabbitmq information; If both are consistent with the default values, no configuration is required rabbitmq: host: 127.0.0.1 port: 5672 username: guest password: guest
- Add @ RefreshScope annotation to provider controller
- Modify the configuration file in git and send the url through postman: http://127.0.0.1:12000/actuator/bus-refresh Test the config configuration center
- The provider has been updated
- Visit the provider controller to get the latest news. The test is successful
So far, the five components of spring cloud have been demonstrated!