首先是搭建服务注册中心Eureka Server
参考:
https://blog.csdn.net/weixin_42465125/article/details/88233722
https://blog.csdn.net/weixin_42465125/article/details/88337698
两个客户端工程结构如下:
采用的是Maven的聚合工程,聚合工程的创建参考:https://blog.csdn.net/weixin_42465125/article/details/87906095
cuit-product-center和cuit-user-center作为父工程,cuit-product-center-*和cuit-user-center-*为聚合的子模块
cuit-product-center父工程:
pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.cuit.product</groupId>
<artifactId>cuit-product-center</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<modules>
<module>cuit-product-center-api</module>
<module>cuit-product-center-service</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- 因为cuit-product-center-api工程没main class,加这个会报错 -->
<!--
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
-->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
</project>
cuit-product-center-api子模块:
该模块下面放的是domain类和service接口
pom文件:
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.cuit.product</groupId>
<artifactId>cuit-product-center</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>cuit-product-center-api</artifactId>
<name>cuit-product-center-api</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
</dependencies>
</project>
Product类:
package cn.cuit.product.center.api.domian;
import java.io.Serializable;
public class Product implements Serializable {
private static final long serialVersionUID = 3895325943679327522L;
private String id;
private String name;
private Integer price;
public Product() {
}
public Product(String name, Integer price) {
this.name = name;
this.price = price;
}
public Product(String id, String name, Integer price) {
this.id = id;
this.name = name;
this.price = price;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getPrice() {
return price;
}
public void setPrice(Integer price) {
this.price = price;
}
}
ProductService接口:
package cn.cuit.product.center.api.service;
import cn.cuit.product.center.api.domian.Product;
public interface ProductService {
Product getProductById(String id);
}
cuit-product-center-service子模块:
该模块主要放Controller类和Service实现及Feign接口和启动类
pom文件:
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.cuit.product</groupId>
<artifactId>cuit-product-center</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>cuit-product-center-service</artifactId>
<name>cuit-product-center-service</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>cn.cuit.product</groupId>
<artifactId>cuit-product-center-api</artifactId>
<version>1.0.0</version>
</dependency>
<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-feign</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
</dependencies>
</project>
properties配置文件:
spring.aop.auto = true
spring.aop.proxy-target-class = true
# logback日志配置,日志环境类型,服务名,级别
log.env.profile = dev
log.env.module = cuit-product-center-service
log.env.logger.level = info
#服务提供者的名字
spring.application.name = cuit-product-center-service
#服务提供者的端口号
server.port=8088
#服务上下文配置,springboot2.X
server.servlet.context-path=/cuit-product-center-service
#服务注册中心的地址
#无需验证的方式:
#eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
#有用户名密码的验证方式,Eureka服务端依赖SpringSecurity
eureka.client.serviceUrl.defaultZone=http://user:pwd@localhost:8761/eureka/
日志文件:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<property resource="application.properties"/>
<appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/${log.env.module}.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>logs/${log.env.module}-%d{yyyy-MM-dd}.%i.log
</fileNamePattern>
<maxFileSize>5MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%.15thread] %logger{36}:%X{sysUser} - %.-4096msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<root level="${log.env.logger.level}">
<appender-ref ref="ROLLING"/>
</root>
</configuration>
启动类:
package cn.cuit.product.center.service;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
// 将自己作为一个可以被Eureka Server发现注册的Client
@EnableDiscoveryClient
// 将自己作为一个可以被其他服务通过Feign调用的Client
@EnableFeignClients
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
Controller:
package cn.cuit.product.center.service.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import cn.cuit.product.center.api.domian.Product;
import cn.cuit.product.center.api.service.ProductService;
@RestController
@RequestMapping("/api/products")
public class ProductController {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private ProductService productService;
// 远程访问的经验,先保证自己访问能访问通,再去整另外一个服务来调这个服务,否则坑是填不完的
// http://localhost:8088/cuit-product-center-service/api/products/testFeign?a=10&b=20
@GetMapping("/testFeign")
public String testFeign(@RequestParam Integer a, @RequestParam Integer b) {
logger.info(">>>>>>>>>>>>>>>>通过Feign远程调用成功 a = {}, b = {}", a, b);
return "通过Feign远程调用成功 a + b = " + (a + b);
}
// http://localhost:8088/cuit-product-center-service/api/products/getProductById/10
@GetMapping("/getProductById/{id}")
public Product getProductById(@PathVariable("id") String id) {
logger.info(">>>>>>>>>>>>>>>>通过Feign远程调用成功 ");
return productService.getProductById(id);
}
}
Service实现:
package cn.cuit.product.center.service.impl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import cn.cuit.product.center.api.domian.Product;
import cn.cuit.product.center.api.service.ProductService;
@Service
public class ProductServiceImpl implements ProductService {
private static final Logger LOG = LoggerFactory.getLogger(ProductServiceImpl.class);
@Override
public Product getProductById(String id) {
LOG.info(">>>>>>>>>>>>>id = {}", id);
return new Product(id, "cuit", 998);
}
}
测试本地调用是否好使:
访问:
http://localhost:8088/cuit-product-center-service/api/products/testFeign?a=10&b=20
http://localhost:8088/cuit-product-center-service/api/products/getProductById/10
然后搭建另一个user服务:大体与上面一致
主要实现:
Controller:
package cn.cuit.user.center.service.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import cn.cuit.product.center.api.domian.Product;
import cn.cuit.user.center.service.feign.UserCenterProductFeign;
@RestController
@RequestMapping("/api/users")
public class UserController {
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
@Autowired
private UserCenterProductFeign userCenterProductFeign;
// http://localhost:8081/cuit-user-center-service/api/users/testFeign?a=10&b=20
@GetMapping(value = "/testFeign")
public String testCallUserServiceWithFeign(@RequestParam Integer a, @RequestParam Integer b) {
LOG.info("in cuit-user-center-service a = {}, b = {}", a, b);
String msg = userCenterProductFeign.sum(a, b);
return msg;
}
// http://localhost:8081/cuit-user-center-service/api/products/getProductById/10
@GetMapping(value = "/getProductById/{id}")
public Product getProductById(@PathVariable("id") String id) {
LOG.info(".................in cuit-user-center-service id = {}", id);
Product product = userCenterProductFeign.getProductById(id);
return product;
}
}
UserCenterProductFeign:
package cn.cuit.user.center.service.feign;
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.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import cn.cuit.product.center.api.domian.Product;
/**
* Feign Client
*/
// @FeignClient注解配置 要调用的那个服务
// 如果配置错误会报:
/**
* java.lang.RuntimeException: com.netflix.client.ClientException: Load balancer
* does not have available server for client: XXX-Service
*/
@FeignClient("cuit-product-center-service")
public interface UserCenterProductFeign {
// 远程服务请求的url,
// 注意这里的神坑,虽然不是问题,但是真的很坑,就是不要忘了Controller类上面的@RequestMapping路径和properties里面的context-path路径
// 如果没配对本服务报500,Feign报404【404表示找不到资源,一般大多数是请求路径不对,或者参数没对】,
// SynchronousMethodHandler#executeAndDecode#97行发起调用,到LoadBalancerFeignClient类的57行,就可以看到请求的url
@RequestMapping(value = "/product-service/api/products/testFeign", method = RequestMethod.GET)
public String sum(@RequestParam Integer a, @RequestParam Integer b);
// 旧版Feign不能使用@GetMapping会报错
@GetMapping("/product-service/api/products/getProductById/{id}")
public Product getProductById(@PathVariable("id") String id);
}
发起远程调用:
访问:
http://localhost:8081/cuit-user-center-service/api/users/testFeign?a=10&b=20
http://localhost:8081/cuit-user-center-service/api/users/getProductById/23
完整代码GitHub地址: https://github.com/CUITLLB/eureka-feign-remote-call-demo
********************************* 不积跬步无以至千里,不积小流无以成江海 *********************************