原服务是基于springcloud的微服务架构,这套服务从2018年使用至今。核心组件是spring-cloud-gateway、oauth2、eureka(nacos)、spring-cloud-config。经过多年的发展,k8s➕servicemesh已经逐步成熟,已经成为研发解放生产力的核心技能和工具。
更多细节参阅:https://xie.infoq.cn/article/2baee95d42ed7f8dd83cec170
一、迁移场景
servicemesh和springcloud对比
二、servicemesh对比springcloud的优势
- 微服务基础设施下沉 —— 微服务架构支撑、网络通信、治理等相关能力下沉到基础设施层,业务部门无需投入专人开发与维护,可以有效降低微服务架构下研发与维护成本;
- 降低升级成本 —— Sidecar支持热升级,降低中间件和技术框架客户端、SDK升级成本;
- 语言无关 —— 提供多语言服务治理能力;
- 降低复杂测试、演练成本 —— 降低全链路压测、故障演练成本和业务侵入性。
Feign:
服务网格:
spirngcloud依赖客户端sdk集成,需要将业务服务注册到注册中心,调用时feignClient从注册中心获取服务实例进行调用。Java的客户端sdk十分成熟,但是我们在实际开发过程中nodejs服务接入集群十分费劲,没有官方提供的eurekaClient和feignClient可以集成。服务心跳机制、下线、健康检查功能都不健全。所以受语言限制十分严重,同时每个服务接入springcloud都需要集成SDK,并进行重复的配置。
servicemesh使用istio组件,sidercar帮我们解决了服务注册发现和服务调用的负载均衡问题。无需在客户端实现这部分逻辑,使用hostname+port调用即可。
三、在kubesphere中开启servicemesh
参考官方文档:https://www.kubesphere.io/zh/docs/v3.3/pluggable-components/service-mesh/
四、服务间调用
创建2个springboot服务:file-system(调用方) user(被调用方)
方式1:使用resttemplate进行服务间调用。
@Slf4j
@RestController
@RequestMapping("/api")
public class FileController2 {
@Autowired
private RestTemplate restTemplate;
/**
* 查询用户信息
*
* @return
*/
@GetMapping("/file/{fid}")
public JSONObject getLicense(@PathVariable String fid, HttpServletRequest request) {
//查询数据库获取fid对应file信息
JSONObject fileInfo = new JSONObject();
fileInfo.put("fid", fid);
fileInfo.put("filename", "test.docx");
//调用用户服务查询uid为1的用户信息
HttpHeaders headers = getHeadersFromHttpServletRequest(request);
log.info("Request headers:{}", JSON.toJSONString(headers));
HttpEntity<String> entity = new HttpEntity<>(headers);
String url = "http://istio-demo-user:8082/user/1";
ResponseEntity<JSONObject> userObj = restTemplate.exchange(
url,
HttpMethod.GET,
entity,
JSONObject.class
);
fileInfo.putAll(userObj.getBody());
return fileInfo;
}
private static HttpHeaders getHeadersFromHttpServletRequest(HttpServletRequest request) {
HttpHeaders headers = new HttpHeaders();
// 获取所有请求头的名称
Enumeration<String> headerNames = request.getHeaderNames();
// 遍历请求头,将其添加到 HttpHeaders 中
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
String headerValue = request.getHeader(headerName);
if (headerName.equals("content-length") || headerName.toLowerCase(Locale.ROOT).equals("content-type")) {
continue;
}
headers.add(headerName, headerValue);
}
return headers;
}
}
方式2:模拟FeignClient进行服务调用,替换相关注解
源自开源项目改造:https://github.com/ring1012/istio-fake
添加依赖:
<dependency>
<groupId>istio.fake</groupId>
<artifactId>istio-fake</artifactId>
<version>1.1</version>
<exclusions>
<exclusion>
<artifactId>lombok</artifactId>
<groupId>org.projectlombok</groupId>
</exclusion>
</exclusions>
</dependency>
启用注解
@EnableFakeClients(basePackages = "com.iflytek.filesystem.feign")
改造FeignClient为FakeClient,name为被调用方应用名称➕端口。
@FakeClient(name = "istio-demo-user:8082")
public interface UserFeignClient {
// http:istio-demo-user:8082/user/1
/**
* 根据用户Uid列表获取用户基础信息
* @param uid
* @return
*/
@GetMapping(value = "/user/{uid}")
JSONObject getUserBaseInfoByUid(@PathVariable Long uid);
}
配置请求头转发,增加业务使用请求头和链路追踪请求头。
链路追踪所需转发的请求头参考:https://istio.io/latest/zh/docs/tasks/observability/distributed-tracing/overview/
@Component
public class FakeConfiguration {
/**
* 注入默认透传的请求头
* @return
*/
@Bean
public List<String> tracingHeaderList() {
return Arrays.asList("x-request-id", "x-b3-traceid",
"x-b3-spanid",
"x-b3-parentspanid",
"x-b3-sampled",
"x-b3-flags", "uid", "enterprise_id");
}
}
服务调用
@Autowired
private UserFeignClient userFeignClient;
JSONObject userJsonObject = userFeignClient.getUserBaseInfoByUid(1L);
使用第二种方式可以轻松将feignClient切换到FakeClient,降低服务改造成本。
四、在kubesphere中展示请求链路
流量监控
链路追踪