ソースを参照

0731 loadbalancer & openfeign

Qing 9 ヶ月 前
コミット
c8d34e25f3

+ 10 - 4
springcloud-demo/service-consumer-demo/pom.xml

@@ -35,10 +35,16 @@
             <artifactId>spring-cloud-starter-loadbalancer</artifactId>
         </dependency>
 
-<!--        <dependency>-->
-<!--            <groupId>org.springframework.cloud</groupId>-->
-<!--            <artifactId>spring-cloud-starter-openfeign</artifactId>-->
-<!--        </dependency>-->
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-openfeign</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <version>1.18.34</version>
+        </dependency>
 
         <dependency>
             <groupId>org.springframework.boot</groupId>

+ 8 - 0
springcloud-demo/service-consumer-demo/src/main/java/com/sf/ServiceConsumerDemoApplication.java

@@ -1,8 +1,16 @@
 package com.sf;
 
+import com.sf.config.LoadBalancerConfig;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClientConfiguration;
+import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
+import org.springframework.cloud.openfeign.EnableFeignClients;
 
+// @LoadBalancerClients修改默认配置 使得自定义的配置生效
+//@LoadBalancerClients(defaultConfiguration = LoadBalancerConfig.class)
+// @EnableFeignClients 是打开对openFeign使用的开关
+@EnableFeignClients
 @SpringBootApplication
 public class ServiceConsumerDemoApplication {
 

+ 39 - 0
springcloud-demo/service-consumer-demo/src/main/java/com/sf/config/LoadBalancerConfig.java

@@ -0,0 +1,39 @@
+package com.sf.config;
+
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer;
+import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
+import org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer;
+import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
+import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
+
+//@Configuration
+public class LoadBalancerConfig {
+
+    // 将默认的轮询 设置为随机
+    @Bean
+    public ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
+                                                                   LoadBalancerClientFactory loadBalancerClientFactory) {
+        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
+        // RoundRobinLoadBalancer 是轮询的负载均衡
+        // RandomLoadBalancer 是随机的负载均衡
+        return new RandomLoadBalancer(
+                loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
+    }
+
+//
+//    @Bean
+//    public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
+//            ConfigurableApplicationContext context) {
+//        return ServiceInstanceListSupplier.builder()
+//                .withDiscoveryClient()
+//                .withWeighted()
+//                .withCaching()
+//                .build(context);
+//    }
+
+}

+ 1 - 1
springcloud-demo/service-consumer-demo/src/main/java/com/sf/config/RestConfig.java

@@ -12,7 +12,7 @@ public class RestConfig {
     // 将RestTemplate对象注册到spring容器中
     // RestTemplate是发送http请求的工具
     @Bean
-//    @LoadBalanced
+    @LoadBalanced
     public RestTemplate restTemplate() {
         return new RestTemplate();
     }

+ 13 - 29
springcloud-demo/service-consumer-demo/src/main/java/com/sf/controller/ConsumerController.java

@@ -22,43 +22,27 @@ public class ConsumerController {
     @Autowired
     private DiscoveryClient discoveryClient;
 
-    // http://localhost:8080/echo/helloConsumer
+    // http://localhost:8080/echo/weight123
     @GetMapping("/echo/{string}")
     public String echo(@PathVariable("string") String str) {
         // 通过consumer提供的接口 来调用provider的相同接口
         // 就得把当前服务作为客户端 来构建http请求
-        // getForObject是发送指定url的请求 参数是url地址 + 参数类型 + 参数具体值
+        // getForObject是发送指定url的请求 参数是url地址 + 返回数据的类型 + 参数具体值
 //        String result = restTemplate.getForObject("http://localhost:8070/echo/{string}", String.class, str);
-        // 使用服务名称 代替ip地址+端口号
-//        String result = restTemplate.getForObject("http://service-provider-demo/echo/{string}", String.class, str);
-//        System.out.println("result:" + result);
 
-        String serviceId = "service-provider-demo";
-        List<ServiceInstance> serviceInstances = new ArrayList<>();
-        // 找到所有服务名
-        List<String> services = discoveryClient.getServices();
-        System.out.println(services);
-        for (String service : services) {
-            // 找到指定服务的实例
-            List<ServiceInstance> instances = discoveryClient.getInstances(service);
-            for (ServiceInstance instance : instances) {
-                System.out.println(instance.getInstanceId() + ":" + instance.getHost() +
-                        ":" + instance.getPort() + ":" + instance.getUri());
-            }
-            if (service.equals(serviceId)) {
-                serviceInstances = instances;
-            }
-        }
+        // 使用服务名称 代替ip地址+端口号
+        String result = restTemplate.getForObject("http://service-provider-demo/echo/{string}", String.class, str);
+        System.out.println("result:" + result);
+        return "result:" + result;
+    }
 
-        // 使用随机算法来分发
-        Random random = new Random();
-        int randomIndex = random.nextInt(serviceInstances.size());
-        System.out.println("randomIndex:" + randomIndex);
-        ServiceInstance serviceInstance = serviceInstances.get(randomIndex);
-        String newUrl = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/echo/{string}";
-        System.out.println("newUrl:" + newUrl);
-        String result = restTemplate.getForObject(newUrl, String.class, str);
 
+    // http://localhost:8080/random/100
+    @GetMapping("/random/{range}")
+    public String random(@PathVariable("range") int range) {
+        // getForObject 的参数 第二个是返回数据的类型 参数是直接传递的
+        String result = restTemplate.getForObject("http://service-provider-demo/random/{range}", String.class, range);
+        System.out.println("result:" + result);
         return "result:" + result;
     }
 }

+ 51 - 0
springcloud-demo/service-consumer-demo/src/main/java/com/sf/controller/OpenFeignController.java

@@ -0,0 +1,51 @@
+package com.sf.controller;
+
+import com.sf.dto.UserDto;
+import com.sf.remote.MyProviderClient;
+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.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class OpenFeignController {
+
+    // 把MyProviderClient作为对象 从spring容器中注入进来
+    @Autowired
+    private MyProviderClient myProviderClient;
+
+    // http://localhost:8080/openfeign/echo/123456
+    @GetMapping("/openfeign/echo/{string}")
+    public String echo(@PathVariable("string") String string) {
+        String result = myProviderClient.echo(string);
+        // ctrl + alt + L 格式化
+        return "result:" + result;
+    }
+
+    // http://localhost:8080/openfeign/random/123
+    @GetMapping("/openfeign/random/{range}")
+    public String random(@PathVariable("range") int range) {
+        System.out.println("random:" + range);
+        String result = myProviderClient.random(range);
+        // ctrl + alt + L 格式化
+        return "result:" + result;
+    }
+
+    // http://localhost:8080/openfeign/testParam?param=123
+    @GetMapping("/openfeign/testParam")
+    public String testParam(@RequestParam("param") String param) {
+        System.out.println("testParam:" + param);
+        String result = myProviderClient.testParam(param);
+        return "result:" + result;
+    }
+
+    // http://localhost:8080/openfeign/testMultiParam?name=zhangsan&age=80
+    @GetMapping("/openfeign/testMultiParam")
+    public String testMultiParam(UserDto userDto) {
+        System.out.println("testMultiParam:" + userDto);
+//        String result = myProviderClient.testMultiParam(userDto);
+        String result = myProviderClient.testMultiParam(userDto.getName(), userDto.getAge());
+        return "result:" + result;
+    }
+}

+ 65 - 0
springcloud-demo/service-consumer-demo/src/main/java/com/sf/controller/my/MyRandomController.java

@@ -0,0 +1,65 @@
+package com.sf.controller.my;
+
+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.RestController;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+// 注意此时的RestConfig中 @LoadBalanced需要注释掉
+@RestController
+public class MyRandomController {
+
+    @Autowired
+    private RestTemplate restTemplate;
+
+    // 从spring容器中拿到DiscoveryClient 服务注册与发现的客户端
+    @Autowired
+    private DiscoveryClient discoveryClient;
+
+    // http://localhost:8080/echo/helloConsumer
+    @GetMapping("/echoRandom/{string}")
+    public String echo(@PathVariable("string") String str) {
+        // 通过consumer提供的接口 来调用provider的相同接口
+        // 就得把当前服务作为客户端 来构建http请求
+        // getForObject是发送指定url的请求 参数是url地址 + 参数类型 + 参数具体值
+//        String result = restTemplate.getForObject("http://localhost:8070/echo/{string}", String.class, str);
+        // 使用服务名称 代替ip地址+端口号
+//        String result = restTemplate.getForObject("http://service-provider-demo/echo/{string}", String.class, str);
+//        System.out.println("result:" + result);
+
+        String serviceId = "service-provider-demo";
+        List<ServiceInstance> serviceInstances = new ArrayList<>();
+        // 找到所有服务名
+        List<String> services = discoveryClient.getServices();
+        System.out.println(services);
+        for (String service : services) {
+            // 找到指定服务的实例
+            List<ServiceInstance> instances = discoveryClient.getInstances(service);
+            for (ServiceInstance instance : instances) {
+                System.out.println(instance.getInstanceId() + ":" + instance.getHost() +
+                        ":" + instance.getPort() + ":" + instance.getUri());
+            }
+            if (service.equals(serviceId)) {
+                serviceInstances = instances;
+            }
+        }
+
+        // 使用随机算法来分发
+        Random random = new Random();
+        int randomIndex = random.nextInt(serviceInstances.size());
+        System.out.println("randomIndex:" + randomIndex);
+        ServiceInstance serviceInstance = serviceInstances.get(randomIndex);
+        String newUrl = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/echo/{string}";
+        System.out.println("newUrl:" + newUrl);
+        String result = restTemplate.getForObject(newUrl, String.class, str);
+
+        return "result:" + result;
+    }
+}

+ 10 - 0
springcloud-demo/service-consumer-demo/src/main/java/com/sf/dto/UserDto.java

@@ -0,0 +1,10 @@
+package com.sf.dto;
+
+import lombok.Data;
+
+@Data
+public class UserDto {
+
+    private String name;
+    private Integer age;
+}

+ 38 - 0
springcloud-demo/service-consumer-demo/src/main/java/com/sf/remote/MyProviderClient.java

@@ -0,0 +1,38 @@
+package com.sf.remote;
+
+import com.sf.dto.UserDto;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestParam;
+
+// 使用注解@FeignClient 来声明接口对应的服务名
+@FeignClient("service-provider-demo")
+public interface MyProviderClient {
+
+    // 通过一系列的注解配置 可以映射为当前方法对应的远程调用地址为
+    // http://service-provider-demo/echo/{string}
+    // 此时的入参是方法的入参  返回结果是方法的结果
+    // String result = restTemplate.getForObject("http://service-provider-demo/echo/{string}", String.class, str);
+    // 本质上 是对restTemplate更上层的封装
+
+    // 把远程服务中的接口声明 放在当前接口类中
+    @GetMapping("/echo/{string}")
+    String echo(@PathVariable("string") String string);
+
+    // String result = restTemplate.getForObject("http://service-provider-demo/random/{range}", String.class, range);
+    @GetMapping("/random/{range}")
+    String random(@PathVariable("range") int range);
+
+    @GetMapping("/testParam")
+    String testParam(@RequestParam("param") String param);
+
+    // 在传递多个参数时 不能直接通过类对象传输
+    @GetMapping("/testMultiParam")
+    String testMultiParam(UserDto userDto);
+
+    // 第一种解决方案是 把参数拆解出来
+    @GetMapping("/testMultiParam")
+    String testMultiParam(@RequestParam("name") String name, @RequestParam("age") Integer age);
+
+}

+ 11 - 0
springcloud-demo/service-provider-demo/pom.xml

@@ -42,6 +42,17 @@
             <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <version>1.18.34</version>
+        </dependency>
+
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-test</artifactId>

+ 28 - 0
springcloud-demo/service-provider-demo/src/main/java/com/sf/controller/HelloController.java

@@ -1,9 +1,13 @@
 package com.sf.controller;
 
+import com.sf.dto.UserDto;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
+import java.util.Random;
+
 @RestController
 public class HelloController {
 
@@ -13,4 +17,28 @@ public class HelloController {
         System.out.println("echo:" + string);
         return "echo:" + string;
     }
+
+
+    // http://localhost:8070/random/100
+    @GetMapping("/random/{range}")
+    public String random(@PathVariable("range") int range) {
+        System.out.println("random:" + range);
+        int randomResult = (int) (Math.random() * range);
+        System.out.println("randomResult:" + randomResult);
+        return randomResult + "";
+    }
+
+    // http://localhost:8070/testParam?param=1234
+    @GetMapping("/testParam")
+    public String testParam(@RequestParam("param") String param) {
+        System.out.println("testParam:" + param);
+        return "testParam:" + param;
+    }
+
+    // http://localhost:8070/testMultiParam?name=zhangsan&age=80
+    @GetMapping("/testMultiParam")
+    public String testMultiParam(UserDto userDto) {
+        System.out.println("testMultiParam:" + userDto);
+        return "testMultiParam success";
+    }
 }

+ 10 - 0
springcloud-demo/service-provider-demo/src/main/java/com/sf/dto/UserDto.java

@@ -0,0 +1,10 @@
+package com.sf.dto;
+
+import lombok.Data;
+
+@Data
+public class UserDto {
+
+    private String name;
+    private Integer age;
+}

+ 12 - 0
springcloud-demo/service-provider-demo/src/main/resources/application-dev.yml

@@ -0,0 +1,12 @@
+spring:
+  application:
+    name: service-provider-demo
+  cloud:
+    nacos:
+      # 配置nacos的注册中心
+      discovery:
+        server-addr: 127.0.0.1:8848
+    loadbalancer:
+      configurations: 10
+server:
+  port: 8071

+ 3 - 0
springcloud-demo/service-provider-demo/src/main/resources/application.yml

@@ -6,5 +6,8 @@ spring:
       # 配置nacos的注册中心
       discovery:
         server-addr: 127.0.0.1:8848
+    loadbalancer:
+      configurations: 5
+#      spring.cloud.loadbalancer.configurations
 server:
   port: 8070