11 zuul routing gateway
After skipping ~ a brain map will be sent, and zuul gateway can be seen on the brain map
12 Gateway next generation gateway
12.1 introduction
First, create a new module cloud gateway gateway 9527
pom
<dependencies> <!--gateway--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <!--eureka-client--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!-- Introduce self defined api General package, you can use Payment payment Entity --> <dependency> <groupId>com.atguigu.springcloud</groupId> <artifactId>cloud-api-commons</artifactId> <version>${project.version}</version> </dependency> <!--General basic configuration class--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
yml
server: port: 9527 spring: application: name: cloud-gateway eureka: instance: hostname: cloud-gateway-service client: #The service provider is registered in the eureka service list service-url: register-with-eureka: true fetch-registry: true defaultZone: http://eureka7001.com:7001/eureka
The main startup class needs to open the annotation registered in the eureka center
@SpringBootApplication @EnableEurekaClient public class GateWayMain9527 { public static void main(String[] args) { SpringApplication.run(GateWayMain9527.class,args); } }
Because this is a gateway, we don't need a business class
Such a gateway is ready. How does the 9527 gateway do routing mapping???
At present, we do not want to expose the 8001 port. We hope to set a layer of 9527 outside the 8001
spring: application: name: cloud-gateway cloud: gateway: routes: - id: payment_routh #payment_route #The ID of the route. There are no fixed rules, but it is required to be unique. It is recommended to match the service name uri: http://localhost:8001 # matches the routing address of the service. / / that is, the forwarding address predicates: - Path=/payment/get/** # Assert that the path matches the route / / that is, the interface - id: payment_routh2 #payment_route #The ID of the route. There are no fixed rules, but it is required to be unique. It is recommended to match the service name uri: http://localhost:8001 # matches the routing address of the service predicates: - Path=/payment/lb/** # Assert that the path matches the route
Look at the controller interface
The path is filled in the intercepted method interface
Last start
We type in the address field http://localhost:9527/payment/get/31
The gateway will help us forward to the corresponding address, so that we can access the data without exposing the 8001 interface
Of course, in addition to yml, we can also configure the gateway through bean
Create a new configuration class:
package com.atguigu.springcloud.config; import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @Author XuZhuHong * @CreateTime 2021/11/5 16:42 */ @Configuration public class GateWayConfig { /** * A routing rule with id route name is configured, * When accessing address http://localhost:9527/guonei Automatically forwarded to address: http://news.baidu.com/guonei * @param builder * @return */ @Bean public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { RouteLocatorBuilder.Builder routes = builder.routes(); routes.route("path_route_atguigu", r -> r.path("/guonei").uri("http://news.baidu.com/guonei")).build(); return routes.build(); } @Bean public RouteLocator customRouteLocator2(RouteLocatorBuilder builder) { RouteLocatorBuilder.Builder routes = builder.routes(); routes.route("path_route_atguigu2", r -> r.path("/guoji").uri("http://news.baidu.com/guoji")).build(); return routes.build(); } }
Then when we visit http://localhost:9527/guonei Will help us forward to http://news.baidu.com/guonei
However, when we click other addresses that are not configured, an error will be reported
12.2 optimization
We found that the above method can realize routing, but the address is written dead. It is inconvenient to call when our services increase
How to solve it? Dynamic routing through microservice name
By default, the Gateway will register the service list according to the registry,
Create a dynamic route with the micro service name on the registry as the path for forwarding, so as to realize the function of dynamic route
Just modify the yml of our router
server: port: 9527 spring: application: name: cloud-gateway cloud: gateway: discovery: locator: enabled: true #Enable the function of dynamically creating routes from the registry, and use the microservice name for routing routes: - id: payment_routh #payment_route #The ID of the route. There are no fixed rules, but it is required to be unique. It is recommended to match the service name # uri: http://localhost:8001 # matches the routing address of the service uri: lb://Cloud payment service # matches the routing address of the service provided predicates: - Path=/payment/get/** # Assert that the path matches the route - id: payment_routh2 #payment_route #The ID of the route. There are no fixed rules, but it is required to be unique. It is recommended to match the service name # uri: http://localhost:8001 # matches the routing address of the service uri: lb://Cloud payment service # matches the routing address of the service provided predicates: - Path=/payment/lb/** # Assert that the path matches the route eureka: instance: hostname: cloud-gateway-service client: #The service provider is registered in the eureka service list service-url: register-with-eureka: true fetch-registry: true defaultZone: http://eureka7001.com:7001/eureka
It should be noted that the protocol of uri is lb, which means that the load balancing function of Gateway is enabled.
lb://serviceName is the load balancer that spring cloud gateway automatically creates for us in microservices
12.3 assertions
In our configuration attribute, we found that there is a predicate configuration, so what is this? It is assertion
In short, it is the release mismatch interception with matching conditions
What are the configurations for assertions?
12.3.1 After route assertion Factory
After route predict factory takes one parameter - date and time. Requests that occur after that date and time will be matched.
application.yml
spring: cloud: gateway: routes: - id: after_route uri: http://example.org predicates: - After=2017-01-20T17:42:47.789-07:00[America/Denver]
How to get the time string?
ZonedDateTime zbj = ZonedDateTime.now(); // Default time zone System.out.println(zbj); // ZonedDateTime zny = ZonedDateTime.now(ZoneId.of("America/New_York")); // Gets the current time in the specified time zone // System.out.println(zny);
12.3.2 Before route assertion Factory
Before route predict factory takes one parameter - date and time. Requests that occur before that date and time will be matched.
application.yml.
spring: cloud: gateway: routes: - id: before_route uri: http://example.org predicates: - Before=2017-01-20T17:42:47.789-07:00[America/Denver]
12.3.3 Between route assertion Factory
Between route assertion Factory has two parameters, datetime1 and datetime2. The request between datetime1 and datetime2 will be matched. The actual time of the datetime2 parameter must be after datetime1.
application.yml.
spring: cloud: gateway: routes: - id: between_route uri: http://example.org predicates: - Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
12.3.4 Cookie route assertion Factory
The cookie route assertion Factory has two parameters, cookie name and regular expression. If the request contains the secondary cookie name and the regular expression is true, it will be matched.
application.yml
spring: cloud: gateway: routes: - id: cookie_route uri: http://example.org predicates: - Cookie=chocolate, ch.p
12.3.5 Header route assertion Factory
Header route assertion Factory has two parameters, header name and regular expression. If the request contains a secondary header name and the regular expression is true, it will be matched.
application.yml.
spring: cloud: gateway: routes: - id: header_route uri: http://example.org predicates: - Header=X-Request-Id, \d+
12.3.6 Host route assertion Factory
The Host route assertion Factory includes a parameter: host name list. Use Ant path matching rule,. As separator.
application.yml.
spring: cloud: gateway: routes: - id: host_route uri: http://example.org predicates: - Host=**.somehost.org,**.anotherhost.org
12.3.7 Method route assertion Factory
Method route asserts that the Factory contains only one parameter: the matching HTTP request method is required
application.yml.
spring: cloud: gateway: routes: - id: method_route uri: http://example.org predicates: - Method=GET
All GET requests will be routed
12.3.8 Path route assertion Factory
Path route asserts that Factory has two parameters: a Spring PathMatcher expression list and an optional matchOptionalTrailingSeparator ID
application.yml.
spring: cloud: gateway: routes: - id: host_route uri: http://example.org predicates: - Path=/foo/{segment},/bar/{segment}
For example, the requests of: / foo/1 or /foo/bar or /bar/baz will be matched
URI template variables (such as segment in the above example) will be saved in the form of Map
ServerWebExchange.getAttributes() key is
ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE.
These values will be used in GatewayFilter Factories
You can use the following methods to access these variables more easily.
Map<String, String> uriVariables = ServerWebExchangeUtils.getPathPredicateVariables(exchange); String segment = uriVariables.get("segment");
12.3.9 Query route assertion Factory
Query route asserts that Factory has two parameters: required param and optional regexp
application.yml.
spring: cloud: gateway: routes: - id: query_route uri: http://example.org predicates: - Query=baz
All that contain the request parameter baz will be matched.
application.yml.
spring: cloud: gateway: routes: - id: query_route uri: http://example.org predicates: - Query=foo, ba.
If the request parameter contains foo parameter and the value matches ba. Expression, it will be routed, such as bar and baz
12.3.10 RemoteAddr route assertion Factory
The parameter of RemoteAddr route assertion Factory is a list of CIDR symbol (IPv4 or IPv6) strings. The minimum value is 1, for example, 192.168.0.1/16 (where 192.168.0.1 is the IP address and 16 is the subnet mask).
application.yml.
spring: cloud: gateway: routes: - id: remoteaddr_route uri: http://example.org predicates: - RemoteAddr=192.168.1.1/24
If the requested remote address is 192.168.1.10, it will be routed
Summary
spring: application: name: cloud-gateway cloud: gateway: discovery: locator: enabled: true #Enable the function of dynamically creating routes from the registry routes: - id: payment_routh #payment_route #The ID of the route. There are no fixed rules, but it is required to be unique. It is recommended to match the service name # uri: http://localhost:8001 # matches the routing address of the service uri: lb://Cloud payment service # matches the routing address of the service provided predicates: - Path=/payment/get/** # Assert that the path matches the route - id: payment_routh2 #payment_route #The ID of the route. There are no fixed rules, but it is required to be unique. It is recommended to match the service name # uri: http://localhost:8001 # matches the routing address of the service uri: lb://Cloud payment service # matches the routing address of the service provided predicates: - Path=/payment/lb/** # Assert that the path matches the route - After=2020-02-05T15:10:03.685+08:00[Asia/Shanghai] # Assert that the path matches the route #- Before=2020-02-05T15:10:03.685+08:00[Asia/Shanghai] # Assert that the path matches the route #- Between=2020-02-02T17:45:06.206+08:00[Asia/Shanghai],2020-03-25T18:59:06.206+08:00[Asia/Shanghai] #- Cookie=username,zzyy #- Header=X-Request-Id, \d+ # The request header must have an X-Request-Id attribute and a regular expression with an integer value #- Host=**.atguigu.com - Method=GET - Query=username, \d+ # You must have a parameter name username and the value must be an integer to route
12.4 use of filter
The routing filter can be used to modify incoming HTTP requests and returned HTTP responses. The routing filter can only be used by specifying routes.
Spring Cloud Gateway has built-in multiple routing filters, which are generated by the factory class of GatewayFilter
If you use the official, you can refer to the writing method of assertion
Let's talk about customizing the global GlobalFilter filter
The custom filter must implement two interfaces, globalfilter and ordered
package com.atguigu.springcloud.filter; 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; import java.util.Date; @Component //Must add, must add, must add public class MyLogGateWayFilter implements GlobalFilter,Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { //exchange is similar to a servlet //chain is the next data to return. You can see return System.out.println("time:"+new Date()+"\t A custom global filter was implemented: "+"MyLogGateWayFilter"+"hello"); String uname = exchange.getRequest().getQueryParams().getFirst("uname"); if (uname == null) { System.out.println("****User name is null,Unable to log in"); exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE); return exchange.getResponse().setComplete(); } return chain.filter(exchange); } @Override public int getOrder() { //The smaller the number, the higher the execution return 0; } }
13 SpringCloud Config distributed configuration center
Microservice means to split the business in a single application into one sub service. The granularity of each service is relatively small, so there will be a large number of services in the system. Because each service needs the necessary configuration information to run, a centralized and dynamic configuration management facility is essential.
Spring cloud provides ConfigServer to solve this problem. Each of our microservices carries an application.yml and manages hundreds of configuration files
/(ㄒoㄒ)/~~
13.1 getting started
Code cloud warehouse will be used here
First, create a spring cloud config repository with your own account
Then write several configuration files
The file name is:
File content
config: info: 2021/11/6 config-prod.yml Version=1.0
Create a new configuration center module cloud-config-center-3344
<dependencies> <dependency> <!-- Indicates that this is a configuration center file--> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
yml
server: port: 3344 spring: application: name: cloud-config-center #Microservice name registered into Eureka server cloud: config: server: git: uri: https://Gitee.com/x229827570/springcloud config / # warehouse name above ####search for directory search-paths: - springcloud-config ####Read branch label: master #Service registration to eureka address eureka: client: service-url: defaultZone: http://localhost:7001/eureka
Don't forget to annotate the main startup class @ EnableConfigServer to open the configuration center
@SpringBootApplication @EnableConfigServer //Open the annotation of spring cloudconfig server public class ConfigCenterMain3344 { public static void main(String[] args) { SpringApplication.run(ConfigCenterMain3344.class, args); } }
Then run when we visit http://config-3344.com:3344/master/config-dev.yml, you will see the following interface
Don't forget to map in the local host~
Connection succeeded
13.2 the server obtains information from the configuration center
Create a new module cloud-config-client-3355
Modify pom
<dependencies> <dependency> <!--Client config The dependency of is different from that of the server --> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
New yml
Note that the YML here is different from the previous application.yml
What needs to be created now is bootstrap.yml, so what is it?
applicaiton.yml is a user level resource configuration item
bootstrap.yml is system level, with higher priority
Spring Cloud will create a "Bootstrap Context" as the parent context of the Application Context of the spring application. During initialization, the Bootstrap Context is responsible for loading configuration properties from an external source and parsing the configuration. The two contexts share an Environment obtained from the outside.
Bootstrap properties have high priority. By default, they are not overwritten by local configuration. Bootstrap Context and Application Context have different conventions, so a bootstrap.yml file is added to ensure the separation of Bootstrap Context and Application Context configuration.
It is very important to change the application.yml file under the Client module to bootstrap.yml,
Because bootstrap.yml is loaded before application.yml. Bootstrap.yml has higher priority than application.yml
content
server: port: 3355 spring: application: name: config-client cloud: #Config client configuration config: label: master #Branch name name: config #Profile name profile: dev #Read the suffix name and the above three combinations: the configuration file of config-dev.yml on the master branch is read http://config-3344.com:3344/master/config-dev.yml uri: http://localhost:3344 # configuration center address k #Service registration to eureka address eureka: client: service-url: defaultZone: http://localhost:7001/eureka
Create startup class
@EnableEurekaClient @SpringBootApplication public class ConfigClientMain3355 { public static void main(String[] args) { SpringApplication.run(ConfigClientMain3355.class,args); } }
Business class
@RestController public class ConfigClientController { @Value("${config.info}") private String configInfo; @GetMapping("/configInfo") public String getConfigInfo() { return configInfo; } }
Finally, when we start, we can find that we can get files
But when we refresh the file, we find that the client can't get the message in real time
So what
13.3 dynamic refresh of config client
Avoid restarting the client micro service 3355 every time the configuration is updated
First, introduce dependencies on the client
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
Modify yml
# Exposure monitoring endpoint management: endpoints: web: exposure: include: "*"
Then modify the controller and add @ RefreshScope annotation on the business class
@RestController @RefreshScope public class ConfigClientController { @Value("${config.info}") private String configInfo; @GetMapping("/configInfo") public String getConfigInfo() { return configInfo; } }
Send to the client after the maintenance personnel modify the configuration
curl -X POST "http://localhost:3355/actuator/refresh Command to find that it has changed
The client 3355 is successfully refreshed to the latest configuration content
However, there are still problems:
How to solve it
14. Springcloud bus message bus
14.1 global notification
Install RabbitMQ before using the message bus
Erlang 21.3 and rabbitmq-server-3.7.14 are used here
Then we were in the previous
cloud-config-center-3344 configuration center server
Cloud config client 3355 / 3366 adding RabbitMQ service support for clients
<!--Add message bus RabbitMQ support--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
Then write the corresponding configuration in the yml configuration file
The first is the configuration of two points corresponding to 3344
server: port: 3344 spring: application: name: cloud-config-center #Microservice name registered into Eureka server cloud: config: server: git: uri: https://Gitee.com/x229827570/springcloud config / # warehouse name above ####search for directory search-paths: - springcloud-config ####Read branch label: master #rabbitmq configuration is mainly here------------------ rabbitmq: host: localhost port: 5672 username: guest password: guest #Service registration to eureka address eureka: client: service-url: defaultZone: http://localhost:7001/eureka ##rabbitmq related configuration, exposing the endpoint of bus refresh configuration management: endpoints: #The endpoint of the exposed bus refresh configuration is mainly here------------------ web: exposure: include: 'bus-refresh'
Then the yml of ports 3355 and 3366
server: port: 3366 spring: application: name: config-client cloud: #Config client configuration config: label: master #Branch name name: config #Profile name profile: dev #Read the suffix name and the above three combinations: the configuration file of config-dev.yml on the master branch is read uri: http://localhost:3344 # configuration center address k #rabbitmq related configuration 15672 is the port of the Web management interface; 5672 is the port for MQ access # Mainly here------------------ rabbitmq: host: localhost port: 5672 username: guest password: guest #Service registration to eureka address eureka: client: service-url: defaultZone: http://localhost:7001/eureka # Exposure monitoring endpoints are mainly here------------------ management: endpoints: web: exposure: include: "*" # 'refresh'
Finally, we start eureka and 3344, and then start 3355 and 3366
When we modify the code on the code cloud and execute this command in cmd, we will find that all the information has been modified together
curl -X POST "http://localhost:3344/actuator/bus-refresh"
14.2 fixed point notice
Execute the following code to achieve
curl -X POST "http://localhost:3344/actuator/bus-refresh/config-client:3355"
Bus refresh is the exposed refresh interface previously written in the 3344 yml configuration
15 spring cloud stream message driven
15.1 general description
It solves the problem caused by different message plug-ins
Shield the differences between the underlying message middleware, reduce the switching cost, and unify the programming model of message
Why use him
For example, we use RabbitMQ and Kafka. Due to the different architectures of the two message oriented middleware,
For example, RabbitMQ has exchange and kafka has Topic and Partitions,
The differences of these middleware lead to some problems in our actual project development. If we use one of the two message queues and the business requirements behind it, I want to migrate to another message queue. At this time, it is undoubtedly disastrous. A lot of things have to be pushed down and redone because it is coupled with our system, At this time, springcloud Stream provides us with a decoupling method.
Common notes are as follows
15.2 message sending operation
Before performing these operations, we must first ensure that the RabbitMQ environment is OK
Create a module cloud stream rabbitmq provider8801 as a message sending module for producers
pom
<dependencies> <!--Because it's used here rabbitmq So we need to introduce rabbit Something--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-stream-rabbit</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!--Basic configuration--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
yml its binder attribute may be red, and it can still run regardless
server: port: 8801 spring: application: name: cloud-stream-provider cloud: stream: binders: # Configure the service information of rabbitmq to be bound here; defaultRabbit: # Represents the name of the definition, which is used for binding integration type: rabbit # The message component type used here is rabbitmq environment: # Set the environment configuration related to rabbitmq spring: rabbitmq: host: localhost port: 5672 username: guest password: guest bindings: # Integration of services output: # output represents the sender of the message. This name is the name of a channel destination: studyExchange # Represents the Exchange name definition to use content-type: application/json # Set the message type, json this time, and "text/plain" for text binder: defaultRabbit # Set the specific settings of the message service to be bound eureka: client: # Configure Eureka registration on the client service-url: defaultZone: http://localhost:7001/eureka instance: lease-renewal-interval-in-seconds: 2 # Set the heartbeat interval (30 seconds by default) lease-expiration-duration-in-seconds: 5 # If the interval of 5 seconds is exceeded now (the default is 90 seconds) instance-id: send-8801.com # Displays the host name in the information list prefer-ip-address: true # The access path becomes an IP address
Main start
@SpringBootApplication public class StreamMQMain8801 { public static void main(String[] args) { SpringApplication.run(StreamMQMain8801.class,args); } }
A simple service interface
public interface IMessageProvider{ public String send() ; }
His implementation class should pay attention not to introduce the wrong package, and the call can be implemented without service annotation
package com.atguigu.springcloud.service.impl; import com.atguigu.springcloud.service.IMessageProvider; import org.springframework.cloud.stream.annotation.EnableBinding; import org.springframework.cloud.stream.messaging.Source; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.support.MessageBuilder; import javax.annotation.Resource; import java.util.UUID; /** * @Author XuZhuHong * @CreateTime 2021/11/6 18:05 */ @EnableBinding(Source.class) //Source indicates the sender of the message public class MessageProviderImpl implements IMessageProvider { @Resource private MessageChannel output; //Components required to send messages @Override public String send() { String serial = UUID.randomUUID().toString(); //Put the message into the message queue output.send(MessageBuilder.withPayload(serial).build()); System.out.println(serial); return serial; } }
Simple controller interface
@RestController public class SendMessageController { @Resource private IMessageProvider messageProvider; @GetMapping(value = "/sendMessage") public String sendMessage() { return messageProvider.send(); } }
When we successfully started this service
You can add one more channel in the visualization page of robbinMQ
This channel is what we set up before
When we refresh crazily, we can see the crest map in the visual interface
15.3 message reception
Create a cloud stream rabbitmq consumer 8802
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-stream-rabbit</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!--Basic configuration--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
yml
server: port: 8802 spring: application: name: cloud-stream-consumer cloud: stream: binders: # Configure the service information of rabbitmq to be bound here; defaultRabbit: # Represents the name of the definition, which is used for binding integration type: rabbit # Message component type environment: # Set the environment configuration related to rabbitmq spring: rabbitmq: host: localhost port: 5672 username: guest password: guest bindings: # Integration of services input: # input indicates that it is the receiving port. This name is the name of a channel destination: studyExchange # Indicates the Exchange name to use to define the channel content-type: application/json # Set the message type. This time it is an object json. If it is text, set the data received by "text/plain" binder: defaultRabbit # Set the specific settings of the message service to be bound eureka: client: # Configure Eureka registration on the client service-url: defaultZone: http://localhost:7001/eureka instance: lease-renewal-interval-in-seconds: 2 # Set the heartbeat interval (30 seconds by default) lease-expiration-duration-in-seconds: 5 # If the interval of 5 seconds is exceeded now (the default is 90 seconds) instance-id: receive-8802.com # Displays the host name in the information list prefer-ip-address: true # The access path becomes an IP address
Main startup class
@SpringBootApplication public class StreamMQMain8802{ public static void main(String[] args) { SpringApplication.run(StreamMQMain8802.class,args); } }
The controller class needs to be annotated with @ EnableBinding(Sink.class) to indicate that it is enabled and is the receiver
@Controller @EnableBinding(Sink.class) //EnableBinding enables this message binding. sink indicates that this is the receiver public class ReceiveMessageListener { @Value("${server.port}") private String serverPort; @StreamListener(Sink.INPUT) //Sink.INPUT indicates that this is the receiving method //A simple receiving method, in which the receiving method parameters are fixed public void input(Message<String> message) { System.out.println("Consumer 1,------->Received message:" + message.getPayload()+"\t port: "+serverPort); } }
So when we start this class
Re access message sender http://localhost:8801/sendMessage
You'll see the news on our recipient's side
15.4 group consumption and persistence
When we simulate 8802 port 8803 to run, we find that when sending a message, both consumers will read the message
How to solve the problem of repeated consumption
When microservice applications are placed in the same group, it can ensure that messages will only be consumed once by one of them.
Different groups can be consumed. There will be competition in the same group, and only one of them can be consumed.
In fact, it is very simple to add a group. If the two groups are the same, this problem can be solved
In addition, adding packets can also effectively prevent message loss (that is, when the message sender sends a message and there is no receiver, we will not receive the message when we start the receiver without packets, but the receiver with groups can receive it)