parent
2b77ab5f8f
commit
6b69d06381
@ -0,0 +1,18 @@
|
||||
# 项目大致说明
|
||||
|
||||
- 微服务通信 - (RestTemplate/Ribbon/Feign/OpenFeign)
|
||||
- [dev-protocol-springcloud-communication](dev-protocol-springcloud-communication)
|
||||
- 微服务网关 - (Gateway)
|
||||
- [dev-protocol-springcloud-gateway](dev-protocol-springcloud-gateway)
|
||||
- 分布式链路、日志追踪 - (Sleuth + Zipkin)
|
||||
- todo
|
||||
- 微服务容错 - (SpringCloud Netflix Hystrix)
|
||||
- todo
|
||||
- 消息驱动微服务 - (SpringCloud Stream)
|
||||
- todo
|
||||
- 分布式事务 - (SpringCloud Alibaba Seata)
|
||||
- todo
|
||||
- 网关动态限流 - (SpringCloud Alibaba Sentinel)
|
||||
- todo
|
||||
- 微服务工程部署与整体可用性验证
|
||||
- todo
|
@ -0,0 +1,55 @@
|
||||
<?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>
|
||||
<parent>
|
||||
<groupId>org.example</groupId>
|
||||
<artifactId>dev-protocol</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>dev-protocol-springcloud-communication</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<jjwt.version>0.9.1</jjwt.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.github.openfeign</groupId>
|
||||
<artifactId>feign-micrometer</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>1.2.51</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt</artifactId>
|
||||
<version>${jjwt.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -0,0 +1,17 @@
|
||||
package org.example;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient // fixme 可以不使用服务发现的方式进行配置, 直接使用 http 的方式进行配置也可
|
||||
@EnableFeignClients // 用于扫描包中使用了 @FeignClient 注解的类
|
||||
@RefreshScope // 刷新配置
|
||||
public class OpenFeignDemoApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(OpenFeignDemoApplication.class, args);
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package org.example.common.constant;
|
||||
|
||||
/**
|
||||
* <h1>通用模块常量定义</h1>
|
||||
* */
|
||||
public final class CommonConstant {
|
||||
|
||||
/** RSA 公钥 */
|
||||
public static final String PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnV+iGlE1e8Z825G+ChIwRJ2H2jOMCBu" +
|
||||
"HV7BPrUE8dAGjqAlRtCaxMyJw7NV9NIUl/rY7RWBUQwelkGmGuQomnUAFIgN9f8UxSC6G935lo1ZoBVJWYmfs5ToXLz+fQugmqHZvF+Vc5l" +
|
||||
"UEo1YapeiaymkOxDORMGjzQBoxoBt316IAwNEPIvcV+F6T+WNFJX/p5Xj48Z1rtmbOQ8ffF+pEWKZGsYg/9b+pKiqFJtuyHqwj/9oxFBE98" +
|
||||
"MCu5RfK6M7Ff9/1dyNen1HKjI7Awj8ZnSceVUldcXEdnP89YagevbhtSl/+CvCsKwHq5+ZLkcuONSxE4dIFWTjxA92wJjYf9wIDAQAB";
|
||||
|
||||
/** JWT 中存储用户信息的 key */
|
||||
public static final String JWT_USER_INFO_KEY = "e-commerce-user";
|
||||
|
||||
/** 授权中心的 service-id */
|
||||
public static final String AUTHORITY_CENTER_SERVICE_ID = "e-commerce-authority-center";
|
||||
}
|
@ -0,0 +1 @@
|
||||
package org.example.common;
|
@ -0,0 +1,63 @@
|
||||
package org.example.common.util;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Jws;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import org.example.common.constant.CommonConstant;
|
||||
import org.example.common.vo.LoginUserInfo;
|
||||
import sun.misc.BASE64Decoder;
|
||||
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PublicKey;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Calendar;
|
||||
|
||||
/**
|
||||
* <h1>JWT Token 解析工具类</h1>
|
||||
* */
|
||||
public class TokenParseUtil {
|
||||
|
||||
/**
|
||||
* <h2>从 JWT Token 中解析 LoginUserInfo 对象</h2>
|
||||
* */
|
||||
public static LoginUserInfo parseUserInfoFromToken(String token) throws Exception {
|
||||
|
||||
if (null == token) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Jws<Claims> claimsJws = parseToken(token, getPublicKey());
|
||||
Claims body = claimsJws.getBody();
|
||||
|
||||
// 如果 Token 已经过期了, 返回 null
|
||||
if (body.getExpiration().before(Calendar.getInstance().getTime())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 返回 Token 中保存的用户信息
|
||||
return JSON.parseObject(
|
||||
body.get(CommonConstant.JWT_USER_INFO_KEY).toString(),
|
||||
LoginUserInfo.class
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* <h2>通过公钥去解析 JWT Token</h2>
|
||||
* */
|
||||
private static Jws<Claims> parseToken(String token, PublicKey publicKey) {
|
||||
|
||||
return Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* <h2>根据本地存储的公钥获取到 PublicKey 对象</h2>
|
||||
* */
|
||||
private static PublicKey getPublicKey() throws Exception {
|
||||
|
||||
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(
|
||||
new BASE64Decoder().decodeBuffer(CommonConstant.PUBLIC_KEY)
|
||||
);
|
||||
return KeyFactory.getInstance("RSA").generatePublic(keySpec);
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package org.example.common.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* <h1>通用响应对象定义</h1>
|
||||
* {
|
||||
* "code": 0,
|
||||
* "message": "",
|
||||
* "data": {}
|
||||
* }
|
||||
* */
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class CommonResponse<T> implements Serializable {
|
||||
|
||||
/** 错误码 */
|
||||
private Integer code;
|
||||
|
||||
/** 错误消息 */
|
||||
private String message;
|
||||
|
||||
/** 泛型响应数据 */
|
||||
private T Data;
|
||||
|
||||
public CommonResponse(Integer code, String message) {
|
||||
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package org.example.common.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* <h1>授权中心鉴权之后给客户端的 Token</h1>
|
||||
* */
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class JwtToken {
|
||||
|
||||
/** JWT */
|
||||
private String token;
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package org.example.common.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* <h1>登录用户信息</h1>
|
||||
* */
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class LoginUserInfo {
|
||||
|
||||
/** 用户 id */
|
||||
private Long id;
|
||||
|
||||
/** 用户名 */
|
||||
private String username;
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package org.example.common.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* <h1>用户名和密码</h1>
|
||||
* */
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class UsernameAndPassword {
|
||||
|
||||
/** 用户名 */
|
||||
private String username;
|
||||
|
||||
/** 密码 */
|
||||
private String password;
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
package org.example.controller;
|
||||
|
||||
import org.example.common.vo.JwtToken;
|
||||
import org.example.common.vo.UsernameAndPassword;
|
||||
import org.example.service.UseRestTemplateService;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* <h1>微服务通信 Controller</h1>
|
||||
* */
|
||||
@RestController
|
||||
@RequestMapping("/communication")
|
||||
public class CommunicationController {
|
||||
|
||||
private final UseRestTemplateService restTemplateService;
|
||||
// private final UseRibbonService ribbonService;
|
||||
// private final AuthorityFeignClient feignClient;
|
||||
// private final UseFeignApi useFeignApi;
|
||||
|
||||
public CommunicationController(UseRestTemplateService restTemplateService
|
||||
// UseRibbonService ribbonService,
|
||||
// AuthorityFeignClient feignClient,
|
||||
// UseFeignApi useFeignApi
|
||||
) {
|
||||
this.restTemplateService = restTemplateService;
|
||||
// this.ribbonService = ribbonService;
|
||||
// this.feignClient = feignClient;
|
||||
// this.useFeignApi = useFeignApi;
|
||||
}
|
||||
|
||||
@PostMapping("/rest-template")
|
||||
public JwtToken getTokenFromAuthorityService(
|
||||
@RequestBody UsernameAndPassword usernameAndPassword) {
|
||||
return restTemplateService.getTokenFromAuthorityService(usernameAndPassword);
|
||||
}
|
||||
|
||||
@PostMapping("/rest-template-load-balancer")
|
||||
public JwtToken getTokenFromAuthorityServiceWithLoadBalancer(
|
||||
@RequestBody UsernameAndPassword usernameAndPassword) {
|
||||
return restTemplateService.getTokenFromAuthorityServiceWithLoadBalancer(
|
||||
usernameAndPassword);
|
||||
}
|
||||
|
||||
/* @PostMapping("/ribbon")
|
||||
public JwtToken getTokenFromAuthorityServiceByRibbon(
|
||||
@RequestBody UsernameAndPassword usernameAndPassword) {
|
||||
return ribbonService.getTokenFromAuthorityServiceByRibbon(usernameAndPassword);
|
||||
}
|
||||
|
||||
@PostMapping("/thinking-in-ribbon")
|
||||
public JwtToken thinkingInRibbon(@RequestBody UsernameAndPassword usernameAndPassword) {
|
||||
return ribbonService.thinkingInRibbon(usernameAndPassword);
|
||||
}
|
||||
|
||||
@PostMapping("/token-by-feign")
|
||||
public JwtToken getTokenByFeign(@RequestBody UsernameAndPassword usernameAndPassword) {
|
||||
return feignClient.getTokenByFeign(usernameAndPassword);
|
||||
}
|
||||
|
||||
@PostMapping("/thinking-in-feign")
|
||||
public JwtToken thinkingInFeign(@RequestBody UsernameAndPassword usernameAndPassword) {
|
||||
return useFeignApi.thinkingInFeign(usernameAndPassword);
|
||||
}*/
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package org.example.feign;
|
||||
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
|
||||
/**
|
||||
* 基于注解的方式进行实现
|
||||
*/
|
||||
@Component
|
||||
@FeignClient(name = "nacos-provider", configuration = MyFeignConfiguration.class) // name 对应的是服务注册中心的服务名称
|
||||
public interface EchoService {
|
||||
|
||||
@GetMapping(value = "/echo/{string}")
|
||||
String echo(@PathVariable String string);
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package org.example.feign;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
|
||||
/**
|
||||
* 不基于注解的方式进行装配需要的接口类
|
||||
*/
|
||||
public interface EchoServiceManually {
|
||||
|
||||
@GetMapping(value = "/echo/{string}")
|
||||
public String echo(@PathVariable String string);
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package org.example.feign;
|
||||
|
||||
|
||||
import feign.Client;
|
||||
import feign.Contract;
|
||||
import feign.Feign;
|
||||
import feign.codec.Decoder;
|
||||
import feign.codec.Encoder;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cloud.openfeign.FeignClientsConfiguration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
@Import(FeignClientsConfiguration.class)
|
||||
public class FeignClientsConfig {
|
||||
|
||||
@Autowired
|
||||
private Client client;
|
||||
@Autowired
|
||||
private Encoder encoder;
|
||||
@Autowired
|
||||
private Decoder decoder;
|
||||
@Autowired
|
||||
private Contract contract;
|
||||
|
||||
public EchoServiceManually buidService(){
|
||||
EchoServiceManually serviceManually = Feign.builder()
|
||||
.client(client)
|
||||
.encoder(encoder)
|
||||
.decoder(decoder)
|
||||
.contract(contract)
|
||||
// url : ip+port 不可以, 使用服务名称
|
||||
// .target(EchoServiceManually.class, "http://192.168.10.227:8081");
|
||||
.target(EchoServiceManually.class, "http://nacos-provider");
|
||||
return serviceManually;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
package org.example.feign;
|
||||
|
||||
public class MyFeignConfiguration {
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
package org.example.service;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.example.common.constant.CommonConstant;
|
||||
import org.example.common.vo.JwtToken;
|
||||
import org.example.common.vo.UsernameAndPassword;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* <h1>使用 RestTemplate 实现微服务通信</h1>
|
||||
* */
|
||||
@Slf4j
|
||||
@Service
|
||||
public class UseRestTemplateService {
|
||||
|
||||
private final LoadBalancerClient loadBalancerClient;
|
||||
|
||||
public UseRestTemplateService(LoadBalancerClient loadBalancerClient) {
|
||||
this.loadBalancerClient = loadBalancerClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* <h2>从授权服务中获取 JwtToken</h2>
|
||||
* */
|
||||
public JwtToken getTokenFromAuthorityService(UsernameAndPassword usernameAndPassword) {
|
||||
|
||||
// 第一种方式: 写死 url
|
||||
String requestUrl = "http://127.0.0.1:7000/ecommerce-authority-center" +
|
||||
"/authority/token";
|
||||
log.info("RestTemplate request url and body: [{}], [{}]",
|
||||
requestUrl, JSON.toJSONString(usernameAndPassword));
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
return new RestTemplate().postForObject(
|
||||
requestUrl,
|
||||
new HttpEntity<>(JSON.toJSONString(usernameAndPassword), headers),
|
||||
JwtToken.class
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* <h2>从授权服务中获取 JwtToken, 且带有负载均衡</h2>
|
||||
* */
|
||||
public JwtToken getTokenFromAuthorityServiceWithLoadBalancer(
|
||||
UsernameAndPassword usernameAndPassword
|
||||
) {
|
||||
|
||||
// 第二种方式: 通过注册中心拿到服务的信息(是所有的实例), 再去发起调用
|
||||
ServiceInstance serviceInstance = loadBalancerClient.choose(
|
||||
CommonConstant.AUTHORITY_CENTER_SERVICE_ID
|
||||
);
|
||||
log.info("Nacos Client Info: [{}], [{}], [{}]",
|
||||
serviceInstance.getServiceId(), serviceInstance.getInstanceId(),
|
||||
JSON.toJSONString(serviceInstance.getMetadata()));
|
||||
|
||||
String requestUrl = String.format(
|
||||
"http://%s:%s/ecommerce-authority-center/authority/token",
|
||||
serviceInstance.getHost(),
|
||||
serviceInstance.getPort()
|
||||
);
|
||||
log.info("login request url and body: [{}], [{}]", requestUrl,
|
||||
JSON.toJSONString(usernameAndPassword));
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
return new RestTemplate().postForObject(
|
||||
requestUrl,
|
||||
new HttpEntity<>(JSON.toJSONString(usernameAndPassword), headers),
|
||||
JwtToken.class
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
### 获取 Token
|
||||
POST http://127.0.0.1:8000/ecommerce-nacos-client/communication/rest-template
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"username": "123@126.com",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad"
|
||||
}
|
||||
|
||||
|
||||
### 获取 Token, 带有负载均衡
|
||||
POST http://127.0.0.1:8000/ecommerce-nacos-client/communication/rest-template-load-balancer
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"username": "123@126.com",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad"
|
||||
}
|
||||
|
||||
### 通过 OpenFeign 获取 Token
|
||||
POST http://127.0.0.1:8000/ecommerce-nacos-client/communication/token-by-feign
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"username": "123@126.com",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad"
|
||||
}
|
||||
|
||||
|
||||
### 通过原生 Feign Api 获取 Token
|
||||
POST http://127.0.0.1:8000/ecommerce-nacos-client/communication/thinking-in-feign
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"username": "123@126.com",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad"
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package org.example;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.example.feign.EchoService;
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
@Slf4j
|
||||
@SpringBootTest
|
||||
class EchoServiceManuallyTest {
|
||||
|
||||
@Resource
|
||||
private EchoService echoService;
|
||||
|
||||
@Test
|
||||
public void testOpenFeignInit(){
|
||||
log.info("echoService = {}", echoService);
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package org.example;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.example.feign.EchoServiceManually;
|
||||
import org.example.feign.FeignClientsConfig;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
|
||||
@Slf4j
|
||||
@SpringBootTest
|
||||
class FeignClientsConfigTest implements ApplicationContextAware, InitializingBean {
|
||||
private EchoServiceManually echoServiceManually;
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
|
||||
/**
|
||||
* 1
|
||||
* @param applicationContext the ApplicationContext object to be used by this object
|
||||
*/
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* 2
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
context.setParent(applicationContext);
|
||||
// 注册类
|
||||
context.register(FeignClientsConfig.class);
|
||||
context.refresh();
|
||||
// 获取 EchoServiceManually
|
||||
FeignClientsConfig bean = context.getBean(FeignClientsConfig.class);
|
||||
this.echoServiceManually = bean.buidService();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEchoService(){
|
||||
String echo = this.echoServiceManually.echo("TestAppManually");
|
||||
log.info("result = {}", echo);
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
# 微服务通信方案
|
||||
|
||||
## 1. 微服务通信方案解读
|
||||
|
||||
- 早期-第一种方案: 微服务通信方案: RPC
|
||||
- RPC 实现微服务通信的核心思想
|
||||
- 全局注册表: 将 RPC 支持的所有方法都注册进去
|
||||
- 通过将 java 对象进行编码(IDL, json, xml 等) + 方法名传递(TCP/IP 协议)到目标服务器实现微服务通信
|
||||
---
|
||||
- RPC 的优缺点
|
||||
- 目前市面上最流行的 RPC 框架有:gRPC、Thrift、Dubbo, 有较多的选择性
|
||||
- 速度快、并发性能高 - (使用TCP作为传输协议)
|
||||
- 实现复杂(相对 Rest 而言), 需要做的工作与维护上更多(例如:Server 的地址一般存储于 Zookeeper 上, 就需要引入和维护 ZK)
|
||||
- Tip: 优缺点是相对的, 不需要拘泥于理论
|
||||
---
|
||||
- 第二种方案: 微服务通信方案: HTTP(Rest)
|
||||
- 标准化的 HTTP 协议(GET、POST、PUT、DELETE 等), 前主流的微服务框架通信实现都是 HTTP
|
||||
- 简单、标准,需要做的工作和维护工作少;几乎不需要做额外的工作即可与其他的微服务集成
|
||||
---
|
||||
- 第三种方案: 微服务通信方案: Message
|
||||
- 通过 Kafka、RocketMQ 等消息队列实现消息的发布与订阅(消费)
|
||||
- 可以实现"削峰填谷",缓冲机制实现数据、任务处理
|
||||
- 最大的缺点是只能够做到最终一致性,而不能做到实时一致性;当然,这也是看业务需求
|
||||
---
|
||||
- 微服务通信该做何选择
|
||||
- 结合微服务框架与业务的需要做出选择
|
||||
- SpringCloud 建议的通信方案是 OpenFeign(Rest)
|
||||
- 需要最终一致性且不要求快速响应的业务场景可以选择使用 Message(异步处理对系统性能有很大的提升)
|
||||
- 问题来了: SpringCloud 可不可以使用 RPC 呢?(但是,要有足够强的理由说明你为什么要使用 RPC)
|
||||
---
|
||||
|
||||
## 2. 使用 RestTemplate 实现微服务通信
|
||||
|
||||
- 使用 RestTemplate 的两种方式(思想)
|
||||
- 在代码(或配置文件中)写死IP 和 端口号(需要知道,这并不是不可行!)
|
||||
- 通过注册中心(推荐Nacos)获取服务地址,可以实现负载均衡的效果
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## OpenFeign 核心源码解析
|
||||
|
||||
|
||||
|
||||
## OpenFeign 应用技巧
|
||||
|
||||
- 认识 FeignClientsConfiguration
|
||||
- 修改 Feign 的默认配置
|
||||
- 自定义 @FeignClient configuration
|
||||
|
||||
|
||||
## OpenFeign 二次改造
|
||||
|
||||
- 认识 MicrometerCapability
|
||||
- 基于 Capability 的扩展机制
|
||||
|
||||
## OpenFeign 造轮子
|
||||
|
||||
- 手动创建 FeignClient
|
||||
|
||||
## OpenFeign 面试题深度解析
|
||||
|
||||
- Feign 和 OpenFeign 的区别
|
||||
|
||||
- OpenFeign 的运行原理
|
||||
|
||||
- FeignClient 配置方式
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue