preface
In the last video, we looked at the Eureka Registry. In fact, we also used the Ribbon, but we didn’t talk about it in detail, so let’s take a look at the Ribbon.
What is the Ribbon
So all the load balancers we’ve been working with are hard load balancers, so what is a hard load balancer? Hard load balancing means that in the old large system, there would be a separate system responsible for the load balancing policy, so all our requests would first go to the load balancing system and be distributed to different servers for processing. For example, we are familiar with nginx. In fact, it can be regarded as a load balancing system, the client request interface is first allocated to different servers through nGINx load balancing policy.Isn’t the Ribbon like that? So how does that work? Ribbon, like Eureka, is an open source product launched by Netflix. It can be seamlessly integrated with Eureka. The Ribbon mainly realizes client load balancing. So what is client load balancing? When the client requests, the request is distributed to different servers through a balancing policy, as shown in the following figureThis diagram is adapted from the Eureka architecture diagram in the previous section. The main process remains the same. Both service consumers and service providers register with the service center.
All right, so let’s look at it in code.
demo
For simplicity, the registry server, we’ll stick to the single node of the previous section. I won’t tell you how to configure it. Then we create a new module to be the service provider. We could have used the service provider in the previous section, but I didn’t want to mix them together so we separated them, but a lot of the code is the same.
Service provider
After we create a new module, the pom.xml file looks like this:
<parent> <groupId>cn. Quellanan </groupId> <artifactId>SpringCloud</artifactId> <version>1.0.0</version> </parent> < the groupId > com. Quellanan. Springcloud < / groupId > < artifactId > ribbon - the provider - 9004 < / artifactId > < version > 1.0.0 < / version > <name>ribbon provider-9004</name> <description>ribbon provider </description> <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> </dependencies>Copy the code
The main idea is to introduce eureka-Client to register with the registry and then start the class with @enableEurekaclient annotationsAdd the following configuration to the configuration file:
server.port=9004
spring.application.name=ribbon-provider
eureka.client.service-url.defaultZone=http://localhost:8000/eureka/Copy the code
We are creating a test class: HelloController
@RestController @Slf4j public class HelloController { @Value("${server.port}") private String port; @RequestMapping("/hello") public String hello(){ log.info(port); return "hello "+port; }}Copy the code
So we have one service provider, and to see the load balancing effect, we need to create the same service provider. Or a different port to boot. We change the port to 9005. The others are the same as above.
Service consumer
With the service provider, let’s create a service consumer module. Pom.xml has one more ribbon dependency than the service provider
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
Copy the code
Then add @enableeurekaclient and RestTemplate to the start class
@SpringBootApplication @EnableEurekaClient public class RibbonConsumerApplication { @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(RibbonConsumerApplication.class, args); }}Copy the code
The @loadBalanced annotation is used to implement client-side load balancing.
Add the following configuration to the configuration file:
Server. port=9003 Used the spring at the time of registration. Application. Name = ribbon - consumer eureka. Client. ServiceUrl. DefaultZone = http://localhost:8000/eureka/Copy the code
Finally, we write an interface that calls the service provider and creates an IndexController class that looks like this
@RestController public class IndexController { private static final String applicationName = "ribbon-provider"; @Autowired private RestTemplate restTemplate; @RequestMapping("/index") public String getHello(){ String url = "http://"+ applicationName +"/hello"; return restTemplate.getForObject(url,String.class); }}Copy the code
test
Let’s now start the service center, two service providers, one service consumer. After startup we type:
http://localhost:8000/Copy the code
Note that ribbon Provider has two ports, one for each of our two service providers. Their appliaction.name is the same. Let’s call the lower interface again
http://localhost:9003/indexCopy the code
You can see that each call calls a different service provider, and the two service providers are called in round robin. In this way, load balancing is implemented for clients
RestTemplate
Load balancing is done by adding @loadBalanced to the RestTemplate object. And the previous is just a simple call, without involving parameters and request mode, let’s look at the common request mode and call with parameters.
A Get request
In fact, what we’ve written before is a get request, so we’re going to write a request with parameters
@RequestMapping("index2") public String getHello2(){ String url = "http://"+ applicationName +"/hello2? name={1}"; return restTemplate.getForObject(url,String.class,"quellanan"); }Copy the code
So you can see that the requested parameter in the URL is replaced by a placeholder, and the third parameter of getForObject or getForEntity is the actual parameter that we passed. GetForObject gets the returned content directly, whereas getForEntity returns an HTTP object that contains the status code. To get that back, you need to get getForEntity().getBody().
What about multiple arguments? There are two common ways to add multiple parameters. One is the same as above, and it is good to add parameters as follows:
@requestMapping ("index3") public String getHello3(){url = "http://applicationName +"/hello3? name={1}&age={2}"; return restTemplate.getForObject(url,String.class,"quellanan","18"); }Copy the code
Another option is to encapsulate the parameters in a map and pass them. The same thing can be resolved
@requestMapping ("index4") public String getHello4(){String <String,String> parms=new HashMap<>(); parms.put("name","quellanan"); parms.put("age","18"); String url = "http://"+ applicationName +"/hello3? name={name}&age={age}"; return restTemplate.getForObject(url,String.class,parms); }Copy the code
We write two methods in the provider for testing purposes
@RequestMapping("/hello2")
public String hello2(@RequestParam("name") String name){
log.info(name);
return "hello "+name+port;
}
@RequestMapping("/hello3")
public String hello3(@RequestParam("name") String name,@RequestParam("age") String age){
log.info(name+age);
return "hello "+name+age+port;
}Copy the code
Let’s go ahead and see what happensYou can see that the parameter was passed successfully.
A Post request
A POST request is similar to a GET request, but I’ve wrapped the argument in a map, post forentity
@RequestMapping("index6")
public String getHello6(){
//postForEntity
JSONObject params=new JSONObject();
params.put("name","quellanan");
params.put("age","18");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity request = new HttpEntity(params.toJSONString(), headers);
String url = "http://"+ applicationName +"/hello4";
return restTemplate.postForEntity(url,request,String.class).getBody();
}Copy the code
postForObject
@RequestMapping("index7")
public String getHello7(){
//postForObject
JSONObject params=new JSONObject();
params.put("name","quellanan");
params.put("age","18");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity request = new HttpEntity(params.toJSONString(), headers);
String url = "http://"+ applicationName +"/hello4";
return restTemplate.postForObject(url,params,String.class);
}Copy the code
The main idea is to wrap the parameters in JSONObject first, then set the HttpHeaders and HttpEntity, and then request. We use the application/ JSON format, and we add a method to the service provider. Used to receive parameters in JSON format.
@RequestMapping("/hello4")
public String hello4(@RequestBody Map<String, Object> parms){
return "hello "+parms.get("name")+parms.get("age")+port;
}Copy the code
Now let’s go ahead and see what happens.The proof is that it passes normally.
Feign simplifies microservice calls
As we can see above, when we make microservice calls, whether we use get or POST methods, too many parameters will cause the code to become bloated, so we can use Feign, also introduced by Netflix, to simplify microservice calls. Feign combines the Ribbon and Hystrix. It has all the Ribbon functions and is packaged to make it easier to use. So when we use it, we just need to introduce Feign dependencies.
So let’s adjust these guys up here.
pom.xml
Remove ribbon dependencies from our ribbon-Consumer POM file and add feign dependencies.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>Copy the code
Add @ EnableFeignClients
Add the @enableFeignClients annotation to the startup classAdding the @enableFeignClients annotation to the startup class enables Fegin to be used in projects. How do we use Fegin? Instead, you use the @FeignClient annotation, which is injected in your application to specify the service name of the service provider. And this annotation only works on interfaces. Previously, we needed to write URLS, parameters, return types and so on to call the service provider’s interface, which was very tedious. Therefore, Fegin simplified it for us. It was very convenient for us to call the service provider’s interface just like calling itself.
HelloService
Let’s create an interface to HelloService. The following
@FeignClient("ribbon-provider")
public interface HelloService {
@RequestMapping("/hello")
public String hello();
@RequestMapping("/hello2")
public String hello2(@RequestParam(value = "name") String name);
@RequestMapping("/hello3")
public String hello3(@RequestParam(value = "name") String name,@RequestParam(value = "age") String age);
@RequestMapping("/hello4")
public String hello4(@RequestBody Map<String, Object> parms);
}
Copy the code
As you can see, the interface content above is mainly for the invocation of several interfaces exposed by the service provider.By comparing the interface in the service consumer with the interface in the service provider, you can see that this is actually the implementation of the interface in the service provider to the HelloService in the consumer. I’ll talk about that later. As can be seen from HelloService, we use @requestMapping, @requestParam, @requestBody to call the interface of the service provider, which is more concise and convenient.
FeginController
Let’s create another FeginController to test this:
@RestController public class FeginController { @Autowired public HelloService helloService; @RequestMapping("/fegin") public String getHello(){ return helloService.hello(); } @RequestMapping("/fegin2") public String getHello2(){ String name="quellanan"; return helloService.hello2(name); } @RequestMapping("/fegin3") public String getHello3(){ String name="quellanan"; String age="18"; return helloService.hello3(name,age); } @RequestMapping("/fegin4") public String getHello4(){ Map<String, Object> parms=new HashMap<>(); parms.put("name","quellanan"); parms.put("age","18"); return helloService.hello4(parms); }}Copy the code
As you can see, the normal Controller layer calls the Service layer.
test
All right, so let’s test that out. Enter the following addresses to see the effect:
http://localhost:9003/fegin
http://localhost:9003/fegin2
http://localhost:9003/fegin3
http://localhost:9003/fegin4Copy the code
inheritance
Ok, last question, we said that the HelloService in the service consumer is very similar to the HelloController in the service provider, it feels like the HelloController implements the HelloService. Good, when we officially development, will find the interface call very much, and also very complicated, if in a way that is in accordance with the above, there are a lot of duplicated code and easy to go wrong, so we can service call separately extracted into a module, and then respectively in the service providers and service consumers to introduce its dependence, The HelloService in the consumption then inherits its corresponding interface. The corresponding interface is implemented in the service provider. Of course @FeignClient is still in the service consumer.
Here only provides a way of thinking, did not give the way to implement, interested in “Spring Cloud micro-service combat”, can also discuss with me under hey hey.
One day
Finally, I am done. I have some understanding of ribbon and Fegin. At least now I can use them and apply them into the project without any problems.
Upload the code to github: github.com/QuellanAn/s…
Follow-up fuel ♡
It is my honor to participate in the CSDN Blog Star selection this year. Please help to cast your vote. You can cast 5 votes
CSDN Blog Star selection event
Finally, welcome to pay attention to personal public account “Programmers love yogurt”
Share all kinds of learning materials, including Java, Linux, big data, etc. Information includes video documents and source code, and share myself and deliver quality technical blog posts.
Be sure to follow and share if you like ❤