浏览代码

content-service 引入 spring-cloud-starter-loadbalancer 依赖, 实现三种调用 SpringCloud 服务的方式

reghao 7 月之前
父节点
当前提交
8adef99a97

+ 4 - 0
content/content-service/pom.xml

@@ -149,6 +149,10 @@
             <groupId>org.springframework.cloud</groupId>
             <artifactId>spring-cloud-starter-openfeign</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.springframework.cloud</groupId>
             <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>

+ 1 - 1
content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/db/repository/MallRepository.java

@@ -40,7 +40,7 @@ public class MallRepository {
         productMapper.updateStockMinus(productId, amount);
     }
 
-    @Cacheable(cacheNames = "tnb:mall:product", key = "#itemId", unless = "#result == null")
+    //@Cacheable(cacheNames = "tnb:mall:product", key = "#itemId", unless = "#result == null")
     public Product getProduct(Long itemId) {
         log.info("miss cache");
         return productMapper.findByItemId(itemId);

+ 0 - 77
content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/feign/CircuitBreakerConfig.java

@@ -1,77 +0,0 @@
-package cn.reghao.tnb.content.app.mall.feign;
-
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.cloud.client.circuitbreaker.CircuitBreaker;
-import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
-import org.springframework.cloud.client.circuitbreaker.ConfigBuilder;
-import org.springframework.cloud.client.circuitbreaker.NoFallbackAvailableException;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.Function;
-import java.util.function.Supplier;
-
-/**
- * @author reghao
- * @date 2025-07-22 10:46:17
- */
-@Slf4j
-@Configuration
-public class CircuitBreakerConfig {
-    @Bean
-    MyCircuitBreaker myCircuitBreaker() {
-        return new MyCircuitBreaker();
-    }
-
-    @SuppressWarnings("rawtypes")
-    @Bean
-    CircuitBreakerFactory circuitBreakerFactory(MyCircuitBreaker myCircuitBreaker) {
-        return new CircuitBreakerFactory() {
-            @Override
-            public CircuitBreaker create(String id) {
-                log.info("Creating a circuit breaker with id [{}]", id);
-                return myCircuitBreaker;
-            }
-
-            @Override
-            protected ConfigBuilder configBuilder(String id) {
-                return Object::new;
-            }
-
-            @Override
-            public void configureDefault(Function defaultConfiguration) {
-
-            }
-        };
-    }
-
-    static class MyCircuitBreaker implements CircuitBreaker {
-        AtomicBoolean runWasCalled = new AtomicBoolean();
-
-        @Override
-        public <T> T run(Supplier<T> toRun) {
-            try {
-                this.runWasCalled.set(true);
-                return toRun.get();
-            }
-            catch (Throwable throwable) {
-                throw new NoFallbackAvailableException("No fallback available.", throwable);
-            }
-        }
-
-        @Override
-        public <T> T run(Supplier<T> toRun, Function<Throwable, T> fallback) {
-            try {
-                return run(toRun);
-            }
-            catch (Throwable throwable) {
-                return fallback.apply(throwable);
-            }
-        }
-
-        public void clear() {
-            this.runWasCalled.set(false);
-        }
-    }
-}

+ 13 - 0
content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/feign/DiscoveryClientConfig.java

@@ -0,0 +1,13 @@
+package cn.reghao.tnb.content.app.mall.feign;
+
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author reghao
+ * @date 2025-07-24 09:41:04
+ */
+@Configuration
+@EnableDiscoveryClient
+public class DiscoveryClientConfig {
+}

+ 66 - 0
content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/feign/FeignClientConfig.java

@@ -1,13 +1,79 @@
 package cn.reghao.tnb.content.app.mall.feign;
 
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.cloud.client.circuitbreaker.CircuitBreaker;
+import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
+import org.springframework.cloud.client.circuitbreaker.ConfigBuilder;
+import org.springframework.cloud.client.circuitbreaker.NoFallbackAvailableException;
 import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
 /**
  * @author reghao
  * @date 2025-07-20 17:25:49
  */
+@Slf4j
 @Configuration
 @EnableFeignClients
 public class FeignClientConfig {
+    @Bean
+    MyCircuitBreaker myCircuitBreaker() {
+        return new MyCircuitBreaker();
+    }
+
+    @SuppressWarnings("rawtypes")
+    @Bean
+    CircuitBreakerFactory circuitBreakerFactory(MyCircuitBreaker myCircuitBreaker) {
+        return new CircuitBreakerFactory() {
+            @Override
+            public CircuitBreaker create(String id) {
+                log.info("Creating a circuit breaker with id [{}]", id);
+                return myCircuitBreaker;
+            }
+
+            @Override
+            protected ConfigBuilder configBuilder(String id) {
+                return Object::new;
+            }
+
+            @Override
+            public void configureDefault(Function defaultConfiguration) {
+
+            }
+        };
+    }
+
+    static class MyCircuitBreaker implements CircuitBreaker {
+        AtomicBoolean runWasCalled = new AtomicBoolean();
+
+        @Override
+        public <T> T run(Supplier<T> toRun) {
+            try {
+                this.runWasCalled.set(true);
+                return toRun.get();
+            }
+            catch (Throwable throwable) {
+                throw new NoFallbackAvailableException("No fallback available.", throwable);
+            }
+        }
+
+        @Override
+        public <T> T run(Supplier<T> toRun, Function<Throwable, T> fallback) {
+            try {
+                return run(toRun);
+            }
+            catch (Throwable throwable) {
+                return fallback.apply(throwable);
+            }
+        }
+
+        public void clear() {
+            this.runWasCalled.set(false);
+        }
+    }
 }

+ 51 - 0
content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/feign/LoadBalanceConfig.java

@@ -0,0 +1,51 @@
+package cn.reghao.tnb.content.app.mall.feign;
+
+//import com.netflix.loadbalancer.IRule;
+//import com.netflix.loadbalancer.RoundRobinRule;
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.client.loadbalancer.LoadBalanced;
+import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
+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.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * @author reghao
+ * @date 2025-07-23 15:40:24
+ */
+@LoadBalancerClient(name = "user-service", configuration = LoadBalanceConfig.CustomLoadBalancerConfiguration.class)
+@Configuration
+public class LoadBalanceConfig {
+    /*@Bean
+    public IRule iRule() {
+        return new RoundRobinRule();
+    }*/
+
+    @Bean(name = "lbRestTemplate")
+    // 提供客户端负载均衡
+    @LoadBalanced
+    public RestTemplate restTemplate() {
+        return new RestTemplate();
+    }
+
+    static class CustomLoadBalancerConfiguration {
+        @Bean
+        ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
+                                                                LoadBalancerClientFactory loadBalancerClientFactory) {
+            String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
+            // 随机负载均衡策略
+            RandomLoadBalancer randomLoadBalancer = new RandomLoadBalancer(
+                    loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
+            // 轮询负载均衡策略
+            RoundRobinLoadBalancer roundRobinLoadBalancer = new RoundRobinLoadBalancer(
+                    loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
+            return roundRobinLoadBalancer;
+        }
+    }
+}

+ 51 - 0
content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/feign/UserDiscoveryClient.java

@@ -0,0 +1,51 @@
+package cn.reghao.tnb.content.app.mall.feign;
+
+import cn.reghao.tnb.account.api.iface.AccountQuery;
+import cn.reghao.tnb.user.api.dto.UserInfo;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.dubbo.config.annotation.DubboReference;
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.client.discovery.DiscoveryClient;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.List;
+
+/**
+ * @author reghao
+ * @date 2025-07-23 17:18:00
+ */
+@Slf4j
+@Service
+public class UserDiscoveryClient {
+    @DubboReference(check = false, retries = 0, timeout = 1000, mock = "fallback")
+    private AccountQuery accountQuery;
+
+    String serviceName = "user-service";
+    private DiscoveryClient discoveryClient;
+    // 必须使用 new 新建, 否则会被 Ribbon 拦截器拦截,改变 URL 行为
+    private RestTemplate restTemplate = new RestTemplate();
+
+    public UserDiscoveryClient(DiscoveryClient discoveryClient) {
+        this.discoveryClient = discoveryClient;
+    }
+
+    public UserInfo getUserInfo(long userId) {
+        List<ServiceInstance> list = discoveryClient.getInstances(serviceName);
+        if (list.isEmpty()) {
+            return null;
+        }
+
+        ServiceInstance serviceInstance = list.get(0);
+        String url = serviceInstance.getUri().toString();
+        String url1 = String.format("%s//api/user/hystrix/info1?userId=%s", url, userId);
+
+        UserInfo userInfo = restTemplate.getForObject(url1, UserInfo.class);
+        if (userInfo != null) {
+            long userIdLong = accountQuery.getUserIdLong(userInfo.getUserId());
+            log.info("{} -> {}", userIdLong, userInfo.getScreenName());
+        }
+
+        return userInfo;
+    }
+}

+ 47 - 0
content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/feign/UserLoadBalancerClient.java

@@ -0,0 +1,47 @@
+package cn.reghao.tnb.content.app.mall.feign;
+
+import cn.reghao.tnb.account.api.iface.AccountQuery;
+import cn.reghao.tnb.user.api.dto.UserInfo;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.dubbo.config.annotation.DubboReference;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Service;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * @author reghao
+ * @date 2025-07-23 17:18:27
+ */
+@Slf4j
+@Service
+public class UserLoadBalancerClient {
+    @DubboReference(check = false, retries = 0, timeout = 1000, mock = "fallback")
+    private AccountQuery accountQuery;
+
+    String serviceName = "user-service";
+    // 基于 Ribbon 的 RestTemplate
+    private RestTemplate lbRestTemplate;
+
+    public UserLoadBalancerClient(@Qualifier("lbRestTemplate") RestTemplate lbRestTemplate) {
+        this.lbRestTemplate = lbRestTemplate;
+    }
+
+    public UserInfo getUserInfo(long userId) {
+        String serviceUrlPrefix = String.format("http://%s", serviceName);
+        String url1 = String.format("%s//api/user/hystrix/info1?userId=%s", serviceUrlPrefix, userId);
+        UserInfo userInfo = lbRestTemplate.getForObject(url1, UserInfo.class);
+        if (userInfo != null) {
+            long userIdLong = accountQuery.getUserIdLong(userInfo.getUserId());
+            log.info("{} -> {}", userIdLong, userInfo.getScreenName());
+        }
+        return userInfo;
+    }
+
+    public void add(long userId) {
+        MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<String, Object>();
+        paramMap.add("userId", userId);
+        // UserInfo userInfo = lbRestTemplate.postForObject(url1, paramMap, UserInfo.class);
+    }
+}

+ 22 - 1
content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/service/ProductService.java

@@ -6,15 +6,25 @@ import cn.reghao.jutil.jdk.result.Result;
 import cn.reghao.jutil.jdk.serializer.JsonConverter;
 import cn.reghao.jutil.tool.id.SnowFlake;
 import cn.reghao.oss.sdk.model.dto.media.ImageInfo;
+import cn.reghao.tnb.account.api.iface.AccountQuery;
 import cn.reghao.tnb.content.api.dto.TaobaoItem;
 import cn.reghao.tnb.content.app.mall.db.mapper.ProductMapper;
 import cn.reghao.tnb.content.app.mall.db.repository.MallRepository;
+import cn.reghao.tnb.content.app.mall.feign.UserDiscoveryClient;
+import cn.reghao.tnb.content.app.mall.feign.UserLoadBalancerClient;
 import cn.reghao.tnb.content.app.mall.model.dto.ProductAddDto;
 import cn.reghao.tnb.content.app.mall.model.po.Product;
 import cn.reghao.tnb.content.app.mall.feign.UserFeignClient;
 import cn.reghao.tnb.user.api.dto.UserInfo;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.dubbo.config.annotation.DubboReference;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.client.discovery.DiscoveryClient;
 import org.springframework.stereotype.Service;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.RestTemplate;
 
 import java.util.List;
 
@@ -22,22 +32,31 @@ import java.util.List;
  * @author reghao
  * @date 2024-04-16 11:00:17
  */
+@Slf4j
 @Service
 public class ProductService {
     @DubboReference(check = false)
     private OssService ossService;
+    @DubboReference(check = false, retries = 0, timeout = 1000, mock = "fallback")
+    private AccountQuery accountQuery;
 
     private final int pageSize = 12;
     private final SnowFlake idGenerator;
     private final ProductMapper productMapper;
     private final MallRepository mallRepository;
     private final UserFeignClient userFeignClient;
+    private UserDiscoveryClient userDiscoveryClient;
+    private UserLoadBalancerClient userLoadBalancerClient;
 
-    public ProductService(ProductMapper productMapper, MallRepository mallRepository, UserFeignClient userFeignClient) {
+    public ProductService(ProductMapper productMapper, MallRepository mallRepository,
+                          UserFeignClient userFeignClient, UserDiscoveryClient userDiscoveryClient,
+                          UserLoadBalancerClient userLoadBalancerClient) {
         this.idGenerator = new SnowFlake(1L, 1L);
         this.productMapper = productMapper;
         this.mallRepository = mallRepository;
         this.userFeignClient = userFeignClient;
+        this.userDiscoveryClient = userDiscoveryClient;
+        this.userLoadBalancerClient = userLoadBalancerClient;
     }
 
     public void add(TaobaoItem taobaoItem) {
@@ -78,6 +97,8 @@ public class ProductService {
         long sellerId = product.getSellerId();
         String userInfoJson = userFeignClient.getUserInfo(sellerId);
         UserInfo userInfo = JsonConverter.jsonToObject(userInfoJson, UserInfo.class);
+        //UserInfo userInfo1 = userDiscoveryClient.getUserInfo(sellerId);
+        //UserInfo userInfo2 = userLoadBalancerClient.getUserInfo(sellerId);
 
         return mallRepository.getProduct(itemId);
     }