This article is a summary of the video summary of SpringCloud micro-service combat, which involves
- Configure the Eureka Server and Eureka Client
- Eureka Server high availability
- There are two modes of communication between services: RestTemplate and Feign
- Install and use RabbitMQ
- Configure the use of the center
- Use of Spring Cloud Stream
- Various uses of the service gateway Zuul
As it is a class note, write a little random, big guys forgive me ~
Most of the techniques mentioned in this article will be used in one of my open source projects, where the back-end business logic has been basically written, except for permission verification, gateway configuration and post-optimization, for those interested.
Project address: github.com/cachecats/c…
About the command line
Start the SpringBoot project
java -jar test.jar
Copy the code
Start the SpringBoot project and specify a port
java -jar -Dserver.port=8899 test.jar
Copy the code
Start the SpringBoot project and specify a background run
nohup java -jar test.jar > /dev/null 2> &1 &
Copy the code
Check the process
ps -ef | grep eureka
Copy the code
Kill the process
kill- 9 processCopy the code
Install the project locally to the local Maven repository
mvn -Dmaven.test.skip=true -U clean install
Copy the code
Second, Eureka Server
2.1 new
Choose CloudDiscovery -> Eureka Server
Note the SpringBoot version
2.2 configuration
Annotate the startup class Application
@EnableEurekaServer
Copy the code
Register the service in the configuration file application.yml
eureka:
client:
service-url:
defaultZone: http://localhost:8080/eureka/
Copy the code
Start the project, enter the browser address http://localhost:8080 to see the project started normally, and register yourself
ApplicationName is UNKNOWN. If you want to change the name of your application, do the following in application.yml
eureka:
client:
service-url:
defaultZone: http://localhost:8080/eureka/
spring:
application:
name: eureka
Copy the code
Restart the project, view in the browser, the name is displayed correctly
If you do not want the registry to appear in the registry, register-with-eureka: false
eureka:
client:
service-url:
defaultZone: http://localhost:8080/eureka/ Configure the default registration address
register-with-eureka: false Do not allow the service to be displayed in the app list
spring:
application:
name: eureka Configure the application name
Copy the code
Three, had been the Client
3.1 Creating a Project
Choose CloudDiscovery -> Eureka Discovery
Notice The SpringBoot and SpringCloud versions are the same as the Server versions
3.2 configuration
- The entrance
Application
Add annotations@EnableDiscoveryClient
@SpringBootApplication
@EnableDiscoveryClient
public class ClientApplication {
public static void main(String[] args) { SpringApplication.run(ClientApplication.class, args); }}Copy the code
- Configuring the Server Address
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
spring:
application:
name: client
Copy the code
- User-defined link address. After configuration, the browser address will become
http://clientname:8080/
eureka:
instance:
hostname: clientName
Copy the code
- If the client service is restarted frequently, the following warning is displayed
This is SpringCloud’s self-protection mechanism, which is to treat the service as online regardless of whether it is online or not. This function can be turned off in the development environment for debugging purposes, but must be turned on in the production environment.
Do the following in applicaitono. Yml of the server
eureka:
server:
enable-self-preservation: false
Copy the code
High availability of Eureka Server
Currently, clients register with a Eureka Server. What if the Server fails?
Multiple Eureka Servers can be started and registered with each other.
Here is a demonstration of starting three Eureka servers to register with each other and registering clients with each of the three servers.
Configure the server
Start EurekaApplication, EurekaApplication2 and EurekaApplication3 on ports 8761, 8762 and 8763 respectively. Configure the addresses of the other two services in applicaton.yml for each of the three services.
As EurekaApplication with http://localhost:8762/eureka/, http://localhost:8763/eureka/,
EurekaApplication2 with http://localhost:8761/eureka/, http://localhost:8763/eureka/,
EurekaApplication3 with http://localhost:8761/eureka/, http://localhost:8762/eureka/,
The EurekaApplication’s applicaton.yml is as follows:
eureka:
client:
service-url:
defaultZone: http://localhost:8762/eureka/,http://localhost:8763/eureka/
Copy the code
This correlates the three services to each other.
Configure the client
Then add all three service addresses to the Client’s applicaton.yml file
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/,http://localhost:8763/eureka/
Copy the code
The EurekaApplication is registered to 8762 and 8763. As long as one of the three servers is still alive, the service will not hang.
5. Inter-application communication
There are two main communication modes for inter-application communication:
HTTP stands for: SpringCloud
RPC representative: Dubbo
Two restful invocation methods between services in SpringCloud
- RestTemplate
- Feign
5.1 RestTemplate way
There are three methods of the RestTemplate call, described below.
First write a Controller exposed interface in the application that will provide the data. Call it ServerController
@RestController
@RequestMapping("/product")
public class ServerController {
@GetMapping("/msg")
public String getMsg(a){
return "I am product msg"; }}Copy the code
Then write a Controller called ClientController in the application that needs to receive data
5.1.1 method a
Use RestTemplate directly to manually write the URL that provides the data
@RestController
@RequestMapping("/order")
@Slf4j
public class ClientController {
@GetMapping("/getmsg")
public String getMsg(a){
RestTemplate restTemplate = new RestTemplate();
String result = restTemplate.getForObject("http://localhost:8080/product/msg", String.class);
log.info("result={}", result);
returnresult; }}Copy the code
5.1.2 method 2
Instead of manually entering the URL address, use LoadBalancerClient to dynamically obtain the url by application name, and then use RestTemplate.
loadBalancerClient.choose(“product”); The product is the application ID that provides the data
@RestController
@RequestMapping("/order")
@Slf4j
public class ClientController {
@Autowired
LoadBalancerClient loadBalancerClient;
@GetMapping("/getmsg")
public String getMsg(a){
ServiceInstance serviceInstance = loadBalancerClient.choose("product");
String url = String.format("http://%s:%s/product/msg", serviceInstance.getHost(), serviceInstance.getPort());
RestTemplate restTemplate = new RestTemplate();
String result = restTemplate.getForObject(url, String.class);
returnresult; }}Copy the code
5.1.3 method 3
Use the @ LoadBalanced annotation
New RestTemplateConfig class
@Component
public class RestTemplateConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate(a){
return newRestTemplate(); }}Copy the code
It is then used in ClientController.
restTemplate.getForObject(“http://product/product/msg”, String.class); The two products in the URL, the first representing the application name and the second the ADDRESS of the API. If the API address is/ABC, the URL is http://product/abc
@RestController
@RequestMapping("/order")
@Slf4j
public class ClientController {
@Autowired
RestTemplate restTemplate;
@GetMapping("/getmsg")
public String getMsg(a){
String result = restTemplate.getForObject("http://product/product/msg", String.class);
returnresult; }}Copy the code
5.2 Feign way
There are several steps to using Feign
Step 1: Introduce dependencies
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
Copy the code
Pay attention to
One problem is that some videos and articles cite the spring-cloud-starter-feign dependency. I used the same dependency in the beginning, but it didn’t work. Go to the Maven repository mvnrepository.com/, search spring-Cloud-starter-feign, and find the following:
Spring Cloud Starter Feign (deprecated, please use spring-cloud-starter-openfeign)
To say that spring-cloud-starter-OpenFeign is deprecated, use spring-cloud-starter-OpenFeign.
I’m using a higher version of SpringCloud, which may not support Spring-Cloud-starter-feign anymore.
Step 2: Configure the @enableFeignClients annotation
Configure the @enableFeignClients annotation in the entry class of the application
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderApplication {
public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); }}Copy the code
If @enableFeignClients cannot be found, check that the dependency is cited correctly and the version is correct.
Step 3: Create an encapsulation interface
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
@FeignClient(name = "product")
@Component
public interface ProductClient {
@GetMapping("/product/msg")
String getMsg(a);
}
Copy the code
Interface annotation @feignClient, parentheses name = “product” to declare the application named product to find data (the application name is case-insensitive).
@getMapping (“/product/ MSG “) Specify the request mode and path.
So the getMsg() method is asking for a string with API /product/ MSG in the product application.
Step 4: Invoke
@RestController
@RequestMapping("/order")
@Slf4j
public class ClientController {
@Autowired
ProductClient productClient;
@GetMapping("/getmsg")
public String getMsg(a){
returnproductClient.getMsg(); }}Copy the code
Inject the ProductClient created in step 3 and call the methods defined in the interface directly.
If I inject the ProductClient editor here, I will get an error, but it does not affect compilation.
Could not autowire. No beans of 'ProductClient' type found
Copy the code
I added an @Component annotation to ProductClient when it didn’t look right.
Feign:
- Declarative REST client (pseudo RPC)
- Interface-based annotations are used
Install RabbitMQ
Install RabbitMQ with Docker
Open the RabbitMQ official download page www.rabbitmq.com/download.ht… Docker
Click the Docker Image link to go to the details page
As you can see the latest version is 3.7.7, copy 3.7.7-management, type the following code on the command line and run it
docker run -d--hostname my-rabbit -P 5672:5672 -p 15672:15672 RabbitMQ :3.7.7- ManagementCopy the code
Use Docker PS to see the container we are running
Solo-mac:~ Solo $docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 345859e88EAD RabbitMQ :3.7.7- Management"Docker - entrypoint. S..."2 minutes ago Up 2 minutes 4369/ TCP, 5671/ TCP, 0.0.0.0:5672->5672/ TCP, 15671/ TCP, 25672/ TCP, 0.0.0.0:15672 - > 15672 / TCP goofy_volhardCopy the code
Enter http://localhost:15672 to start RabbitMQ. For the first time, you will be asked to enter the username and password, which are both guest
The RabbitMQ installation is complete.
7. Configuration center
7.1 Configuring the Central Server
-
New project Config
Check Cloud Config -> Config Server and Cloud Discovery -> Eureka DiscoveryCopy the code
-
Add annotations to the startup class
@SpringBootApplication @EnableDiscoveryClient @EnableConfigServer public class ConfigApplication { public static void main(String[] args) { SpringApplication.run(ConfigApplication.class, args); }}Copy the code
-
Create a new project on Github or in the code cloud and upload the Application. Yml configuration file for the Order project to test.
-
Configure the application.yml file for the project
eureka: client: service-url: defaultZone: http://localhost:8761/eureka/ server: port: 8081 spring: application: name: config cloud: config: server: git: uri: https://gitee.com/xxxxxxx username: xxxxxx password: xxxxxx basedir: xxxxxx # Local path Copy the code
The uri is the address of the warehouse, and the username and password are the username and password of the warehouse
-
Start the project after configuration is complete, can see up project registration in the registry, the browser to http://localhost:8081/order-a.yml, can also be read properly on the git config file.
The suffix for the access address input is ‘/order-a.yml’, as explained here.
/{name}-{profiles}.yml /{label}/{name}-{profiles}.yml name: service name, where order profiles environment label branch (branchCopy the code
7.2 Configuring the Central Client
Use the Order project as the client
- In the Server module of order
pom.xml
Add to fileconfig-client
Rely on
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
Copy the code
-
Rename application.yml to bootstrap.yml
-
Configure the bootstrap. Yml
spring: application: name: order cloud: config: discovery: enabled: true service-id: config Configure the application name of the central server profile: dev eureka: client: service-url: defaultZone: http://localhost:8761/eureka/ Copy the code
After the configuration, start the project and it can run normally.
Note:
- Don’t forget to change the profile name
bootstrap.yml
- Configure eureka’s service-URL in the local configuration file instead of reading it from config because it will not be found if eureka’s port number is not the default 8761.
- If there is one on Git
order.yml
.order-dev.yml
Is configuredorder-dev.yml
That will also load by default when it loadsorder.yml
And merge the two files. Using this characteristic, can be inorder.yml
Write common configuration in.
7.3 Spring Cloud Bus Automatically Refreshes Configurations
7.3.1 Config Project Add spring Cloud Bus dependencies
Add spring Cloud Bus dependencies to the Config project
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
Copy the code
Start the project and check on the RabbitMQ console. If there is a connection, the configuration is successful.
7.3.2 Order project adds spring Cloud Bus dependencies
Add a dependency to order’s server module
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
Copy the code
Run RabbitMQ and two connections appear
7.3.3 Configuring Automatic Refreshing
Configure the application.yml file of the Config project to expose the bus-Refresh interface
management:
endpoints:
web:
exposure:
include: "*"
Copy the code
Create a new controller in order to read the env field in the remote configuration
Value indicates the configuration
@RestController
@RequestMapping("/env")
@RefreshScope
public class EnvController {
@Value("${env}")
private String env;
@GetMapping("/print")
public String print() {returnenv; }}Copy the code
Note that the @refreshScope annotation must be added, otherwise the configuration will not automatically refresh
Start again two projects, visit http://localhost:8899/env/print and get the value of the result is configured on the git env.
The value of the changes on the git env to send a post request http://127.0.0.1:8081/actuator/bus-refresh to refresh the message queue, Refresh again http://localhost:8899/env/print will not restart the project but env value has changed.
The prefix mode is configured
Git configuration
env: dev5
girl:
name: lili
age: 18
Copy the code
A new kind of GirlConfig
@Data
@Component
@ConfigurationProperties("girl")
@RefreshScope
public class GirlConfig {
private String name;
private Integer age;
}
Copy the code
New GirlController
@RestController
public class GirlController {
@Autowired
GirlConfig girlConfig;
@GetMapping("girl/print")
public String print(a){
return "name= " + girlConfig.getName() + ", age= "+ girlConfig.getAge(); }}Copy the code
Browser enter http://localhost:8899/girl/print, get results name = lili, age = 18.
To change the configuration of the git as above, send a post request to http://127.0.0.1:8081/actuator/bus-refresh to refresh the message queue, you can see the result also follow changes.
If send request http://127.0.0.1:8081/actuator/bus-refresh the return value is 500, that is the bus didn’t match well. The last possible reason is a version problem, change the SpringBoot version to 2.0.0. build-snapshot and SpringCloud version to finchley.build-snapshot.
Basic use of RabbitMQ
Demonstrate this in the Order project
Configure rabbitMQ information in the configuration file. These configurations can be placed on remote Git
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
Copy the code
8.1 Basic Usage
There are three basic uses for receiving messages
Method 1: Queues must be declared in advance
- Start by creating a queue in the RabbitMQ console
myQueue
- Then create the message receiver and print the message on the console after receiving it
/** * RabbitMQ message receiver */
@Slf4j
@Component
public class MqReceiver {
@RabbitListener(queues = "myQueue")
public void process(String msg){
log.info("reveicer: "+ msg); }}Copy the code
-
To create a message sender, write a method in the test class for simplicity
/** * RabbitMQ message sender */ @Component public class RabbitMQTest extends OrderApplicationTests { @Autowired AmqpTemplate amqpTemplate; @Test public void test1(a){ amqpTemplate.convertAndSend("myQueue"."now " + newDate()); }}Copy the code
Run the test, and the console successfully prints out the received message.
Method 2: Automatically create a queue
Delete the queue created in method 1, myQueue, and change the sender
@RabbitListener(queuesToDeclare = @Queue("myQueue"))
public void process(String msg){
log.info("reveicer: " + msg);
}
Copy the code
Using queuesToDeclare automatically creates queues.
Method 3: Automatically create a queue and bind queue to Exchange
I’m going to delete myQueue, I’m going to change the sender, I’m going to change the receiver
@RabbitListener(bindings = @QueueBinding(
value = @Queue("myQueue"),
exchange = @Exchange("myExchange")))public void process(String msg){
log.info("reveicer: " + msg);
}
Copy the code
8.2 Message Group
Suppose the order service has two groups, digital vendors and fruit vendors. After the order is placed, the computer order is sent to the digital supplier, and the fruit order is sent to the fruit supplier. Both vendors receive their own messages.
The receiver
/** * Digital vendors receive messages *@param msg
*/
@RabbitListener(bindings = @QueueBinding(
exchange = @Exchange("myOrder"),
key = "computer",
value = @Queue("computerOrder")))public void processComputer(String msg){
log.info("computerOrder reveicer: " + msg);
}
/** * Fruit vendor receives message *@param msg
*/
@RabbitListener(bindings = @QueueBinding(
exchange = @Exchange("myOrder"),
key = "fruit",
value = @Queue("fruitOrder")))public void processFruit(String msg){
log.info("fruitOrder reveicer: " + msg);
}
Copy the code
Message sender
@Test
public void send(){
amqpTemplate.convertAndSend("myOrder"."computer"."now " + new Date());
}
Copy the code
This is a computer order, convertAndSend() exchange, routingKey, and message
Only computerOrder received the message after sending it.
Check the RabbitMQ console and you can see the relationship between exchanges and queues
Spring Cloud Stream
Spring Cloud Stream is a framework for building highly scalable event-driven microservices connected with shared messaging systems.
Copy the code
Spring Cloud Stream currently supports only RabbitMQ and Kafka messaging middleware
9.1 Procedure
The following shows the use of Spring Cloud Stream with RabbitMQ
-
Introduction of depend on
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-stream-rabbit</artifactId> </dependency> Copy the code
-
Configure RabbitMQ as in the previous section
spring: rabbitmq: host: localhost port: 5672 username: guest password: guest Copy the code
-
Create the interface StreamClient
import org.springframework.cloud.stream.annotation.Input; import org.springframework.cloud.stream.annotation.Output; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.SubscribableChannel; public interface StreamClient { String INPUT = "messageInput"; String OUTPUT = "messageOut"; @Input(INPUT) SubscribableChannel input(a); @Output(OUTPUT) MessageChannel output(a); } Copy the code
-
Create the message receiver, which receives the string first
@Component @EnableBinding(StreamClient.class) @Slf4j public class StreamReceiver { @StreamListener(StreamClient.OUTPUT) public void process(String obj){ log.info("StreamReceiver: "+ obj); }}Copy the code
-
Create a message sender
import com.solo.order.message.StreamClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.support.MessageBuilder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Date; @RestController public class SendMessageController { @Autowired private StreamClient streamClient; @GetMapping("/sendMessage") public void send(a) { String message = "now: " + newDate(); streamClient.output().send(MessageBuilder.withPayload(message).build()); }}Copy the code
Note that MessageBuilder does not mislead packages
9.2 Message Group
If multiple instances are started at the same time, it is possible for multiple instances to receive messages. To avoid this problem, you can group messages.
Add it to the configuration file
spring:
cloud:
# Message grouping
stream:
bindings:
messageInput: # define the queue name
group: order The group name is optional
Copy the code
9.3 Sending Receiving Objects
Transforming the message receiver
/** * Receive object *@param dto
*/
@StreamListener(StreamClient.OUTPUT)
public void process(OrderDTO dto){
log.info("StreamReceiver: " + dto);
}
Copy the code
Transform the message sender
/** * send object */
@GetMapping("/sendMessage")
public void send(a) {
OrderDTO dto = new OrderDTO();
dto.setOrderId("12345678");
streamClient.output().send(MessageBuilder.withPayload(dto).build());
}
Copy the code
If you want to see the serialized JSON string instead of the object name on the MQ console, change the configuration as follows
spring:
cloud:
# Message grouping
stream:
bindings:
messageInput: # define the queue name
group: order The group name is optional
content-type: application/json Let MQ display JSON strings instead of objects
Copy the code
Add the content-type: application/json
9.4 Responding to Messages Received
Add two interfaces to StreamClient
public interface StreamClient {
String INPUT = "messageInput";
String OUTPUT = "messageOut";
String INPUT2 = "messageInput2";
String OUTPUT2 = "messageOut2";
@Input(INPUT)
SubscribableChannel input();
@Output(OUTPUT)
MessageChannel output();
@Input(INPUT2)
SubscribableChannel input2();
@Output(OUTPUT2)
MessageChannel output2();
}
Copy the code
The message receiver makes the following changes
@StreamListener(StreamClient.OUTPUT)
@SendTo(StreamClient.OUTPUT2)
public String process(OrderDTO dto){
log.info("StreamReceiver: " + dto);
return "Received...";
}
@StreamListener(StreamClient.OUTPUT2)
public void process2(String msg){
log.info("StreamReceiver2: " + msg);
}
Copy the code
Basically add an @sendto (streamClient.output2) annotation and return the desired value. Define a receiver to receive StreamClient.output2.
Ten, Redis installation and simple use
10.1 installation
Install and start with Docker
docker run -dRedis: 6379-6379 - p 4.0.8Copy the code
Redis visualization tool for MAC: Redis Desktop Manager, RDM for short
10.2 the use of
Add dependencies first
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
Copy the code
Then configure the redis address and port number
spring:
redis:
host: localhost
port: 6379
Copy the code
11. Service gateway Zuul
11.1 introduction
Elements of a service gateway
- Stability, high availability
- Performance, concurrency
- security
- scalability
Common Gateway schemes
- Nginx + Lua
- Kong based on Nginx + Lua
- Tyk
- Spring Cloud Zuul
The characteristics of Zuul
- Route + Filter
- The core is a series of filters
Zuul’s four filter apis
- Front (Pre)
- Route
- Post
- Error
11.2 Use Zuul to implement simple routing and forwarding
Create a project api-gateway, check Cloud Config -> Config Client, CloudDiscovery -> Eureka Discovery, Cloud Routing -> Zuul. Click Next to complete the creation
Change the application.properties file to bootstrap.yml and do the following
spring:
application:
name: api-gateway
cloud:
config:
discovery:
enabled: true
service-id: config
profile: dev
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
Copy the code
Add the @enableZuulProxy annotation to the entry class
@SpringBootApplication
@EnableZuulProxy
public class ApiGatewayApplication {
public static void main(String[] args) { SpringApplication.run(ApiGatewayApplication.class, args); }}Copy the code
Start a project on port 9000 and you can access the API of other projects through the gateway
If you want to access the product the product/project list interface, directly in the browser to enter http://localhost:9000/product/product/list.
The access format is http://localhost:9000/ application ID/API address
11.3 User-defined Routes
The bootstrap. Yml added
zuul:
routes:
myProduct: # Self-defined name
path: /myProduct/**
serviceId: product
Copy the code
Can be accessed through http://localhost:9000/myProduct/product/list the above interface
Concise writing
zuul:
routes:
product: /myProduct/**
Copy the code
11.4 Excluding Some Routes
Exclude /product/list so that it cannot be accessed
zuul:
routes:
# Introduction writing method
product: /myProduct/**
# Exclude certain routes
ignored-patterns:
- /**/product/list
Copy the code
11.5 Cookies and Dynamic Routing
Read the Cookie
Cookies are filtered by default. If you want to get cookies, set sensitiveHeaders: to null
zuul:
routes:
myProduct:
path: /myProduct/**
serviceId: product
sensitiveHeaders:
Copy the code
Set the global sensitive header
zuul:
Set the global sensitive header
sensitive-headers:
Copy the code
Dynamically Configuring Routes
Move the zuul configuration to Git by creating api-gateway-dev.yml
Create a configuration class or prefix the configuration directly on the entry class
@SpringBootApplication
@EnableZuulProxy
public class ApiGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApplication.class, args);
}
@ConfigurationProperties("zuul")
@RefreshScope
public ZuulProperties ZuulProperties(a){
return newZuulProperties(); }}Copy the code
11.6 Pre and Post Filters
Token verification is implemented using the Pre filter
The following uses Zuul’s Pre filter to verify the requested token
New TokenFilter
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_DECORATION_FILTER_ORDER;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
@Component
public class TokenFilter extends ZuulFilter {
@Override
public String filterType(a) {
return PRE_TYPE;
}
@Override
public int filterOrder(a) {
return PRE_DECORATION_FILTER_ORDER - 1;
}
@Override
public boolean shouldFilter(a) {
return true;
}
@Override
public Object run(a) throws ZuulException {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
// It can be retrieved from the url
String token = request.getParameter("token");
if (StringUtils.isEmpty(token)){
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
}
return null; }}Copy the code
A request without a token will report a 401 error.
Add content to the return header with a Post filter
New AddResponseFilter
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;
@Component
public class AddResponseFilter extends ZuulFilter {
@Override
public String filterType(a) {
return FilterConstants.POST_TYPE;
}
@Override
public int filterOrder(a) {
return FilterConstants.SEND_RESPONSE_FILTER_ORDER - 1;
}
@Override
public boolean shouldFilter(a) {
return true;
}
@Override
public Object run(a) throws ZuulException {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletResponse response = requestContext.getResponse();
response.addHeader("X-Foo", UUID.randomUUID().toString());
return null; }}Copy the code
After adding x-foo to the return header, the restart project request interface found that the value was successfully added
11.7 Zuul current limit
import com.google.common.util.concurrent.RateLimiter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.exception.ZuulException;
import com.solo.apigateway.exception.RateLimitException;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.SERVLET_DETECTION_FILTER_ORDER;
/** * current limiting interceptor. Token bucket, implemented with Google's Guava */
public class RateLimitFilter extends ZuulFilter {
public static final RateLimiter RATE_LIMITER = RateLimiter.create(100);
@Override
public String filterType(a) {
return PRE_TYPE;
}
@Override
public int filterOrder(a) {
return SERVLET_DETECTION_FILTER_ORDER - 1;
}
@Override
public boolean shouldFilter(a) {
return true;
}
@Override
public Object run(a) throws ZuulException {
if (RATE_LIMITER.tryAcquire()){
throw new RateLimitException();
}
return null; }}Copy the code
Xii. Zuul authentication and adding user services
To be perfect
Zuul cross domain
Cross-domain problems can be resolved in a variety of ways, from annotations on a single interface to a unified approach on the Zuul gateway
13.1 Adding Annotations to Interfaces for Cross-domain implementation
Adding the @Crossorigin annotation to the interface makes it cross-domain
13.2 Zuul solves cross-domain
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import java.util.Arrays;
/** * Cross-domain configuration */
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter(a) {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true); // Whether cookies are supported across domains
config.setAllowedHeaders(Arrays.asList("*"));
config.setAllowedOrigins(Arrays.asList("*"));
config.setAllowedMethods(Arrays.asList("*"));
config.setMaxAge(300l); // Cache time. During this time period, the same cross-domain request will not be checked
source.registerCorsConfiguration("/ * *", config);
return newCorsFilter(source); }}Copy the code
As the open source project goes on, I will write many articles to introduce these technologies in detail, welcome to pay attention to ~
Project address: github.com/cachecats/c…