Spring Cloud在一个服务中使用Feign远程调用另一个服务

首先是搭建服务注册中心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

********************************* 不积跬步无以至千里,不积小流无以成江海 *********************************

猜你喜欢

转载自blog.csdn.net/weixin_42465125/article/details/88395453