SpringCloud-远程调用-Feign-Dubbo-SpringLoadBalance-feign上传文件
package cn.jiangjiesheng.edu.controller.frontend.test;
import com.alibaba.fastjson.JSON;
import cn.jiangjiesheng.commons.annotation.ResponseJson;
import cn.jiangjiesheng.edu.common.PaasUserUtil;
import cn.jiangjiesheng.edu.service.*;
import cn.jiangjiesheng.fst.dto.FileConvert;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping
public class TestController {
//DOTO 导出pdf 和 word 两个版本
/**
* nacos.server-addr=192.168.0.36:8848
* nacos.namespace=f6ee3083-4bb5-4f82-97a7-ba23a956cf45
*
* 所有方式都加
* spring.cloud.nacos.discovery.server-addr=${nacos.server-addr}
* spring.cloud.nacos.discovery.namespace=${nacos.namespace}
*
* dubbo 方式必传
* dubbo.registry.address=nacos://${nacos.server-addr}
* dubbo.registry.parameters.namespace=${nacos.namespace}
*/
@Autowired
private DiscoveryClient discoveryClient;
@Autowired
private RestTemplate restTemplate;
//后期使用的时候,这个单独提取出来
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean
@LoadBalanced // getResultWithSpringLoadBalance专用测试
public RestTemplate restTemplateWithSpringLoadBalance() {
return new RestTemplate();
}
private String serviceName = "fst";//serviceId
@GetMapping("/info")
public String info() {
List<ServiceInstance> serviceInstances = discoveryClient.getInstances(serviceName);
StringBuilder sb = new StringBuilder();
sb.append("All services:" + discoveryClient.getServices() + "<br/>");
sb.append("fst instance list : <br/>");
serviceInstances.forEach(instance -> {
sb.append("[ serviceId:" + instance.getServiceId() + ",host:" + instance.getHost() + ",port:" + instance.getPort() + "]");
sb.append("<br/>");
});
return sb.toString();
// All services:[consumers:cn.jiangjiesheng.api.service.IHolidayService::, providers:cn.jiangjiesheng.fst.service.IUploadService::alioss, fst, providers:cn.jiangjiesheng.microserver.test.UserServiceT::jsTestDubboGroup, openapi, pcc, rca, consumers:cn.jiangjiesheng.api.service.INoticeService::, providers:cn.jiangjiesheng.fst.service.IUploadService::hwobs, providers:cn.jiangjiesheng.fst.service.IFileConvertService::hwobs, safety.node.wy001, edu, consumers:cn.jiangjiesheng.fst.service.IFileConvertService::hwobs, ewi, providers:cn.jiangjiesheng.microserver.api.user.UserApi::jsTestDubboGroup, safety-openapi, providers:cn.jiangjiesheng.microserver.api.user.UserApi::, hfw, safety.node, microsever, hdi, safety-edu, spm, consumers:cn.jiangjiesheng.fst.service.IUploadService::hwobs]
// fst instance list :
// [ serviceId:fst,host:192.168.0.36,port:10085]
}
/**
* 实测消费者端断网后可以重连
* http请求协议必传
* 启动类上加上了@EnableDiscoveryClient(autoRegister = false) ,似乎作用不明显
* 如果接口访问 No instances available for 192.168.0.36 ,跟@LoadBalanced注解有关
*
* @return
*/
@GetMapping("/getResult")
public String getResult() {
List<ServiceInstance> serviceInstances = discoveryClient.getInstances(serviceName);
//这里使用了findAny,也就是说这个负载均衡是随机的
ServiceInstance serviceInstance = serviceInstances.stream().findAny().orElseThrow(() -> {
return new IllegalStateException("no " + serviceName + " instance avilable");
});
String param = "seats/2021/09/course/file/98b21275fb92c6aa2bbd29f6f56f35bc.docx";
// {"success":true,"message":null,"result":{"seats/2021/09/course/file/98b21275fb92c6aa2bbd29f6f56f35bc.docx":{"id":149,"containerId":"BaoXianFuWu","domain":"GIN-EDU","objectId":"seats/2021/09/course/file/98b21275fb92c6aa2bbd29f6f56f35bc.docx","convStatus":2,"convErr":0,"convResultDesc":"","lastSuccessTime":"2021-09-08T14:53:37","withoutCheckUploaded":1,"convertedContent":"seats/2021/09/course/file/transformedFile/1.jpg,seats/2021/09/course/file/transformedFile/2.jpg,seats/2021/09/course/file/transformedFile/3.jpg,seats/2021/09/course/file/transformedFile/4.jpg,seats/2021/09/course/file/transformedFile/5.jpg,seats/2021/09/course/file/transformedFile/6.jpg,seats/2021/09/course/file/transformedFile/7.jpg,seats/2021/09/course/file/transformedFile/8.jpg,seats/2021/09/course/file/transformedFile/9.jpg","assetsId":"","lastStartTime":"2021-09-08T14:53:17","createTime":"2021-09-08T14:53:15","duration":0,"pages":9,"fileType":1,"upload":null}}}
// 这个接口的返回就是 key value形式( Map<String, FileConvert>),
return restTemplate.getForObject("http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/safetyjapi/fst/file/convert/getConvertResult?objectIdList=" + param, String.class);
}
/**
* 实测消费者端断网后无法重连,原因不明,不知是nacos还是dubbo的原因
*/
@Autowired
private FileConvertService fileConvertService;
@GetMapping("/getResultWithDubbo")
public String getResultWithDubbo() {
String param = "seats/2021/09/course/file/98b21275fb92c6aa2bbd29f6f56f35bc.docx";
Map<String, FileConvert> stringFileConvertMap = fileConvertService.getConvertResult(param);
return JSON.toJSONString(stringFileConvertMap);
}
/**
* 实测消费者端断网后可以重连
* 这种不需要指定ip、port,http请求协议
* <dependency>
* <groupId>org.springframework.cloud</groupId>
* <artifactId>spring-cloud-starter-openfeign</artifactId>
* <version>2.2.5.RELEASE</version>
* </dependency>
* <dependency>
* <groupId>org.springframework.cloud</groupId>
* <artifactId>spring-cloud-openfeign-core</artifactId>
* <version>2.2.5.RELEASE</version>
* </dependency>
* 不写版本会导致 A component required a bean of type ‘xxxClientFeign’ that could not be found cloud
* 另外可以测试 加上@EnableFeignClients("feign接口所在包") 或者 @ComponentScan(basePackages={"feign接口所在包"})
*
* @EnableFeignClients
*/
@Autowired
private FileConvertWithFeignService fileConvertWithFeignService;
@Autowired
@Lazy
private com.fasterxml.jackson.databind.ObjectMapper objectMapper;
@GetMapping("/getResultWithFeign")
public String getResultWithFeign() {
String param = "seats/2021/09/course/file/98b21275fb92c6aa2bbd29f6f56f35bc.docx";
// Map<String, FileConvert> stringFileConvertMap = fileConvertWithFeignService.getConvertResult(param);
//这里如果接受实体,似乎有个字段类型不匹配的问题
//因为生产者的http接口返回值会包一层
//似乎只能是string : https://blog.csdn.net/CoderYin/article/details/90754547
String result = fileConvertWithFeignService.getConvertResult(param);
result = "{\"success\":true,\"message\":null,\"result\":{\"seats/2021/09/course/file/98b21275fb92c6aa2bbd29f6f56f35bc.docx\":{\"id\":149,\"containerId\":\"BaoXianFuWu\",\"domain\":\"GIN-EDU\",\"objectId\":\"seats/2021/09/course/file/98b21275fb92c6aa2bbd29f6f56f35bc.docx\",\"convStatus\":2,\"convErr\":0,\"convResultDesc\":\"\",\"lastSuccessTime\":\"2021-09-08T14:53:37\",\"withoutCheckUploaded\":1,\"convertedContent\":\"seats/2021/09/course/file/transformedFile/1.jpg,seats/2021/09/course/file/transformedFile/2.jpg,seats/2021/09/course/file/transformedFile/3.jpg,seats/2021/09/course/file/transformedFile/4.jpg,seats/2021/09/course/file/transformedFile/5.jpg,seats/2021/09/course/file/transformedFile/6.jpg,seats/2021/09/course/file/transformedFile/7.jpg,seats/2021/09/course/file/transformedFile/8.jpg,seats/2021/09/course/file/transformedFile/9.jpg\",\"assetsId\":\"\",\"lastStartTime\":\"2021-09-08T14:53:17\",\"createTime\":\"2021-09-08T14:53:15\",\"duration\":0,\"pages\":9,\"fileType\":1,\"upload\":null},\"11111111111111\":{\"id\":149,\"containerId\":\"BaoXianFuWu2\",\"upload\":null}}}";
//feign会自己包装一层
JSONObject json = JSON.parseObject(result);
JSONObject convertResult = json.getJSONObject("result");
//注意新的技术点,JSON 转 Map<String, 实体> 用法(以下不同的类)
Map<String, FileConvert> map = JSON.parseObject(convertResult.toJSONString(), new com.alibaba.fastjson.TypeReference<Map<String, FileConvert>>() {
});
Map<String, FileConvert> map2 = objectMapper.readValue(convertResult.toJSONString(), new com.fasterxml.jackson.core.type.TypeReference<Map<String, FileConvert>>() {
});
FileConvert fileConvert = map.get("seats/2021/09/course/file/98b21275fb92c6aa2bbd29f6f56f35bc.docx");
FileConvert fileConvert2 = map2.get("11111111111111");
System.out.println(fileConvert);
System.out.println(fileConvert2);
return result;
}
/**
* enctype="multipart/form-data" 方式提交,文件key为file
* 上传下载文件
* 0、
* <dependency>
* <groupId>io.github.openfeign.form</groupId>
* <artifactId>feign-form</artifactId>
* <version>3.8.0</version>
* </dependency>
*
* <dependency>
* <groupId>io.github.openfeign.form</groupId>
* <artifactId>feign-form-spring</artifactId>
* <version>3.8.0</version>
* </dependency>
* 1、configuration= FeignMultipartSupportConfig.class
* 2、@RequestMapping
* 3、consumes = MediaType.MULTIPART_FORM_DATA_VALUE
* 4、@RequestPart("file")
* 5、参考文档 https://www.cnblogs.com/geekdc/p/13685039.html
* https://blog.csdn.net/qq_43603116/article/details/93304319
*/
@PostMapping("/uploadFileWithFeign")
@Operation(summary = "上传文件到OSS")
@ApiImplicitParams({
@ApiImplicitParam(name = "file", value = "文件对象", dataTypeClass = MultipartFile.class, example = "CMG/2021-07/CMG/3a89e9b4b33f0107d0025dd2846cd293.pdf")
})
@ResponseJson
@ResponseBody
public String uploadFileWithFeign(@RequestParam("file") MultipartFile file) throws IOException {
Preconditions.checkArgument(file != null, "file字段不能为空");
String result = fileConvertWithFeignService.uploadFile("1", "pcc", "admin_51", true, true, file);
return result;
}
/**
* 实测消费者端断网后可以重连
* http请求协议必传 这种不需要指定ip、port
* <dependency>
* <groupId>org.springframework.cloud</groupId>
* <artifactId>spring-cloud-starter-loadbalancer</artifactId>
* </dependency>
*
* @LoadBalanced
*/
@GetMapping("/getResultWithSpringLoadBalance")
public String getResultWithSpringLoadBalance() {
String param = "seats/2021/09/course/file/98b21275fb92c6aa2bbd29f6f56f35bc.docx";
//这种不需要指定ip、port
String result = restTemplateWithSpringLoadBalance.getForObject("http://" + serviceName + "/safetyjapi/fst/file/convert/getConvertResult?objectIdList=" + param, String.class);
return result;
}
}
--------------------------------------------------------------------------------
FileConvertWithFeignService.java
package cn.jiangjiesheng.edu.service;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* 转码服务
*/
@FeignClient(name = "fst", configuration = FeignMultipartSupportConfig.class)
public interface FileConvertWithFeignService {
// Get @RequestParam 或 Post @RequestBody 必须
@GetMapping("/safetyjapi/fst/file/convert/createConvertJob")
public String createConvertJob(@RequestParam("objectId") String objectId);
@GetMapping("/safetyjapi/fst/file/convert/retryJob")
public StringretryJob(@RequestParam("objectIdList")String... objectIdList);
@GetMapping("/safetyjapi/fst/file/convert/getConvertResult")
//这里如果接收实体,似乎有个字段类型不匹配的问题
public String getConvertResult(@RequestParam("objectIdList")String... objectIdList);
// produces = {MediaType.APPLICATION_JSON_UTF8_VALUE},
//这里指定了multipart/form-data,所以应该就不能使用实体作为参数了 @RequestBody UploadFileDto uploadFileDto
@RequestMapping(value = "/safetyjapi/fst/file/upload/uploadFile", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public String uploadFile(
@RequestParam("containerId") String containerId,
@RequestParam("domain") String domain,
@RequestParam("userId") String userId,
@RequestParam("publicRead") boolean publicRead,
@RequestParam("overWrite") boolean overWrite,
@RequestPart("file") MultipartFile file);
}
--------------------------------------------------------------------------------
FeignMultipartSupportConfig.java
package cn.jiangjiesheng.edu.config;
import feign.form.spring.SpringFormEncoder;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
import feign.codec.Encoder;
@Configuration
public class FeignMultipartSupportConfig {
@Autowired
private ObjectFactory<HttpMessageConverters> messageConverters;
@Bean
@Primary
@Scope("prototype")
public Encoder feignEncoder() {
return new SpringFormEncoder(new SpringEncoder(messageConverters));
}
}
目录
通用的查找服务
openfeign
loadbalance
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
服务间RPC调用
调用方法
基于RestTemplate,加@LoadBalanced注解后,可以实现基于服务ID的调用。
调用示例
@SpringBootTest
public class RpcTest {
@Autowired
private RestTemplate restTemplate;
@Test
void test() {
String resp = restTemplate.getForObject("http://服务id/safetyjapi/demo/", String.class);
System.out.println(resp);
}
}
@Autowired
private RestTemplate restTemplate;
@Autowired
private RestTemplate restTemplateWithSpringLoadBalance;
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean
@LoadBalanced
public RestTemplate restTemplateWithSpringLoadBalance() {
return new RestTemplate();
}
不需要关注http,因为自己的服务见调用都是内网,如果有外网注册的情况,最好还是使用
(RestTemplate + @LoadBalanced ) 或者 Feign ,应该去动态设置https。未验证。