java接口常见问题分析_常见问题 - Apache ServiceComb
Q: ServiceComb和SpringCloud是什么關系,具體的應用場景是什么?
A: ServiceComb是華為基于內部多個大型IT系統實踐提煉出來的一套微服務開發框架,在開發態基于最佳實踐封裝了一套微服務運行模型,這些能力對用戶完全透明,可以通過配置引入功能和對其進行調整。在運維階段充分考慮了微服務運維,提供了豐富的監控指標和動態治理能力。
B: ServiceComb的這套能力可以作為一個單獨的開發框架,在需要輕量級微服務解決方案的的場景中單獨使用,也可以建立在SpringCloud上,與SpringCloud提供的其他組件一起工作,在重量級場景中和SpringCloud一起產生 “1+1大于2”的效果。
Q: 用IntelliJ的免費版開發,有什么問題?
A: 沒有問題,使用IntelliJ 開發,可參考 Setup Developer Environment 進行相應的環境配置。
Q: 使用Java-Chassis這個框架時有什么需要注意的地方?
A: 使用Java-Chassis有以下這些限制:
(1) 0.3.0-SNAPSHOT之前的版本不支持類似@GetMapping這樣的標注。
(2) 所用到的HTTP請求方法一樣時,如GET,則方法名不能重載。這是由于生成契約時方法名會作為其Operation ID,所以要保證其唯一性。
(3) 方法和類必須是public的。
Q: 契約生成會報錯Caused by: java.lang.Error: OperationId must be unique,不支持函數重載?
A: 我們是支持函數重載的, 加上@ApiOperation標簽即可,demo-pojo中有示例。每個接口必須有唯一的operation id。
Q: 使用spring-boot-starter-provider這個依賴時,在application.yml文件中聲明的spring.main.web-application屬性并沒有生效?
A: 使用starter-provider這個依賴時,如果用到了servlet這種方式時,需要在application.properties這個文件引入spring.main.web-application=true這樣的屬性或者在application.yml文件中聲明,但是此時需要新建一個application.properties的文件,其內容可以為空。
Q: 網關依賴的jar和其他微服務的一樣嗎?
org.apache.servicecomb
spring-boot-starter-provider
A: 網關除了要依賴spring-boot-starter-provider之外還要依賴spring-boot-starter-discovery,可以參考LinuxCon-Beijing-Workshop中manager的實現。
Q: 網關需要像其他微服務一樣配置assembly嗎?其中的/maven/gateway這個路徑是docker maven plugin默認的嗎?
A: 需要,由于項目現在使用的是spring-boot的打包方式,docker maven plugin也是依賴打包生成的文件來生成docker鏡像的。/maven這個路徑是docker maven plugin指定的,而gateway這個路徑是在assembly中指定的。
Q: 服務接口的返回類型可以是任意類型嗎?還是必須是responseEntity?
A: 可以,具體可以參考java-chassis的integration-test的實現。
Q: 微服務啟動后,無法正確調用接口,或返回不存在錯誤?
A: 請檢查調用路徑與Producer實現代碼中發布的路徑完全一致。Producer端的啟動日志可以查看到映射路徑輸出,例如:
[INFO] Swagger mapped "{[/hello/], method=[GET], produces=[application/json]}"
不同編程風格(模型)實現Producer的文檔和注意事項請參見:Jaxrs SpringMVC Pojo Spring Boot 。
Q: 在eclipse下修改了microservice.yaml配置文件下的端口號,啟動程序后,端口號沒生效?
A: 需要單獨導入sample項目,如果導入整個ServiceComb-Java-Chassis項目,由于sample目錄不在ServiceComb-Java-Chassis模塊中,IDE不會對sample進行編譯,eclipse下并沒有提示錯誤信息,IDEA下會有提示信息。因此eclipse啟動sample的demo會發現修改了端口沒有生效。
Q: 如何自定義某個Java方法對應的REST接口里的HTTP Status Code?
A: 對于正常的返回值,可以通過SwaggerAnnotation實現,例如:
@ApiResponse(code = 300, response = String.class, message = "")
public int test(int x) {
return 100;
}
對于異常的返回值,可以通過拋出自定義的InvocationException實現,例如:、
public String testException(int code) {
String strCode = String.valueOf(code);
switch (code) {
case 200:
return strCode;
case 456:
throw new InvocationException(code, strCode, strCode + " error");
case 556:
throw new InvocationException(code, strCode, Arrays.asList(strCode + " error"));
case 557:
throw new InvocationException(code, strCode, Arrays.asList(Arrays.asList(strCode + " error")));
default:
break;
}
return "not expected";
}
Q: 如何定制自己微服務的日志配置?
A: ServiceComb不綁定日志器,只是使用了slf4j,用戶可以自由選擇log4j/log4j2/logback等等。ServiceComb提供了一個log4j的擴展,在標準log4j的基礎上,支持log4j的properties文件的增量配置。
默認以規則:”classpath*:config/log4j.properties”加載配置文件
實際會搜索出classpath中所有的config/log4j.properties和config/log4j.*.properties, 從搜索出的文件中切出\*的部分,進行alpha排序,然后按順序加載,最后合成的文件作為log4j的配置文件。
如果要使用ServiceComb的log4j擴展,則需要調用Log4jUtils.init,否則完全按標準的日志器的規則使用。
Q: 當服務配置了多個transport的時候,在運行時是怎么選擇使用哪個transport的?
A:
ServiceComb的consumer、transport、handler、producer之間是解耦的,各功能之間通過契約定義聯合在一起工作的,即:
consumer使用透明rpc,還是springmvc開發與使用highway,還是RESTful在網絡上傳輸沒有關系與producer是使用透明rpc,還是jaxrs,或者是springmvc開發,也沒有關系handler也不感知,業務開發方式以及傳輸方式
consumer訪問producer,在運行時的transport選擇上,總規則為:
consumer的transport與producer的endpoint取交集,如果交集后,還有多個transport可選擇,則輪流使用
分解開來,存在以下場景:
當一個微服務producer同時開放了highway以及RESTful的endpoint
consumer進程中只部署了highway transport jar,則只會訪問producer的highway endpoint
consumer進程中只部署了RESTful transport jar,則只會訪問producer的RESTful endpoint
consumer進程中,同時部署了highway和RESTful transport jar,則會輪流訪問producer的highway、RESTful endpoint
如果,此時consumer想固定使用某個transport訪問producer,可以在consumer進程的microservice.yaml中配置,指定transport的名稱:
servicecomb:
references:
:
transport: highway
當一個微服務producer只開放了highway的endpoint
consumer進程只部署了highway transport jar,則正常使用highway訪問
consumer進程只部署了RESTful transport jar,則無法訪問
consumer進程同時部署了highway和RESTful transport jar,則正常使用highway訪問
當一個微服務producer只開放了RESTful的endpoint
consumer進程只部署了highway transport jar,則無法訪問
consumer進程只部署了RESTful transport jar,則正常使用RESTful訪問
consumer進程同時部署了highway和RESTful transport jar,則正常使用RESTful訪問
Q: swagger body參數類型定義錯誤,導致服務中心注冊的內容沒有類型信息
現象描述:
定義如下接口,將參數放到body傳遞
/testInherate:
post:
operationId: "testInherate"
parameters:
- in: "body"
name: "xxxxx"
required: false
type: string
responses:
200:
description: "responseof200"
schema:
$ref: "#/definitions/ResponseImpl"
采用上面方式定義接口。在服務注冊以后,從服務中心查詢下來的接口type: string 丟失,變成了:
/testInherate:
post:
operationId: "testInherate"
parameters:
- in: "body"
name: "xxxxx"
required: false
responses:
200:
description: "responseof200"
schema:
$ref: "#/definitions/ResponseImpl"
如果客戶端沒有放置swagger,還會報告如下異常:
Caused by: java.lang.ClassFormatError: Method "testInherate" in class ? has illegal signature "
A:定義body參數的類型的時候,需要使用schema,不能直接使用type。
/testInherate:
post:
operationId: "testInherate"
parameters:
- in: "body"
name: "request"
required: false
schema:
type: string
responses:
200:
description: "responseof200"
schema:
$ref: "#/definitions/ResponseImpl"
Q: ServiceComb微服務框架服務調用是否使用長連接?
A: http使用的是長連接(有超時時間),highway方式使用的是長連接(一直保持)。
Q: 服務斷連服務中心注冊信息是否自動刪除
A: 服務中心心跳檢測到服務實例不可用,只會移除服務實例信息,服務的靜態數據不會移除。
Q: 如果使用tomcat方式集成ServiceComb微服務框架,如何實現服務注冊
A: 如果使用cse sdk servlet方式(使用transport-rest-servlet依賴)制作為war包部署到tomcat,需要保證,服務描述文件(microservice.yaml)中rest端口配置和外置容器一致才能實現該服務的正確注冊。否則無法感知tomcat開放端口。
Q: 如果使用tomcat方式集成CSE微服務框架,服務注冊的時候如何將war包部署的上下文注冊到服務中心
A: 發布服務接口的時候需要將war包部署的上下文(context)放在baseurl最前面,這樣才能保證注冊到服務中心的路徑是完整的路徑(包含了上下文)。實例:
@path(/{context}/xxx)
class ServiceA
Q: ServiceComb微服務框架如何實現數據多個微服務間透傳
A:
透傳數據塞入:
CseHttpEntity httpEntity = new CseHttpEntity<>(xxx);
//透傳內容
httpEntity.addContext("contextKey","contextValue");
ResponseEntity responseEntity = RestTemplateBuilder.create().exchange("cse://springmvc/springmvchello/sayhello",HttpMethod.POST,httpEntity,String.class);
透傳數據獲取:
@Override
@RequestMapping(path="/sayhello",method = RequestMethod.POST)
public String sayHello(@RequestBody Person person,InvocationContext context){
//透傳數據獲取
context.getContext();
return "Hello person " + person.getName();
}
Q: ServiceComb微服務框架服務如何自定義返回狀態碼?
A:
@Override
@RequestMapping(path = "/sayhello",method = RequestMethod.POST)
public String sayHello(@RequestBody Person person){
InvocationContext context = ContextUtils.getInvocationContext();
//自定義狀態碼
context.setStatus(Status.CREATED);
return "Hello person "+person.getName();
}
Q: ServiceComb body Model部分暴露
A: 一個接口對應的body對象中,可能有一些屬性是內部的,不想開放出去,生成schema的時候不要帶出去,使用:
@ApiModelProperty(hidden = true)
Q: ServiceComb框架獲取遠端consumer的地址
A: 如果使用http rest方式(使用transport-rest-vertx依賴)可以用下面這種方式獲取:
AbstractProducerContextArgMapper httpRequestCreator = (AbstractProducerContextArgMapper)invocation.getHandlerContext().get(RestConst.HTTP_REQUEST_CREATOR);
if(httpRequestCreator != null){
HttpServletRequest req = (HttpServletRequest)httpRequestCreator.createContextArg(invocation);
System.out.println(req.getRemoteHost());
}
實際場景是拿最外層的地址,所以應該是LB傳入到edgeservice,edgeService再放到context外下傳遞。
Q: ServiceComb不支持泛型
A: 明確不支持,需要修改接口,接口修改后需要修改版本號,以免consumer還是使用舊的版本。
Q: ServiceComb對handler描述
A: consumer默認的handler是simpleLB,沒有配置的時候handler鏈會使用這個,如果配置了handler,里面一定要包含lb的handler,否則調用報錯,需要在文檔里面進行說明。
Q: ServiceComb日志替換
A: CSE java-chassis日志推薦方式是在啟動的時候使用Log4jUtils.init(),直接使用推薦的Log4j來做日志管理,但是有些場景不想用log4j,比如想使用log4j2或者logback,下面以log4j2為例簡單介紹下步驟:
在代碼里面不要使用Log4jUtils.init();
去掉log4j的配置文件(不刪掉也沒關系,因為不會使用);
exclude掉CSE框架引入的log4j,例如:
org.apache.servicecomb
provider-springmvc
log4j
log4j
引入log4j2的依賴
org.apache.logging.log4j
log4j-slf4j-impl
org.apache.logging.log4j
log4j-api
org.apache.logging.log4j
log4j-core
如果沒有版本依賴管理,還需要填寫上版本號。
加入log4j2的配置文件log4j2.xml,關于這個請查看官方說明,例如:
filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log">
filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log">
filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log">
啟動服務進行驗證
Q: 服務超時設置
A: 在微服務描述文件(microservice.yaml)中添加如下配置:
servicecomb:
request:
timeout: 30000
Q: URL 地址就可以唯一定位,為什么要加上一個schema?
A:
schema 是用來匹配服務契約的,用來保證服務端和消費端契約兼容,每個契約需要一個唯一ID,在服務中心存儲。
schema映射到java的interface概念,在consumer使用透明rpc模式開發時,可以找到是微服務里的哪個operation。schema之間的方法名是沒有唯一性要求的。
operation qualified name是治理的key,而URL 因為path參數的存在,沒辦法直接查找,而qualified name是不會變的。治理是不區分傳輸的,如果治理按URL 走,那么highway調進來時,還得根據參數反向構造出url,再來正則表達式匹配,太折騰了。
http只是一種傳輸通道,還有別的傳輸通道不需要映射到URL的。
Q: rest客戶端調用的時候,實際上只帶上了服務名和URL,并不需要指定schema id的, 而實際上根據這個URL也能找到具體契約的,所以指定schema id作用何在?
A: 由于透明rpc是接口式調用,并沒有URL,內部實際都歸一化到operation來描述的,這樣就可以結合schema id唯一定位到具體的請求處理中。
Q: Transport是個什么概念?用來干什么的?
A: transport負責編解碼,以及傳輸。通信模型有rest和highway兩種,highway對應的是私有協議,使用protobuf編碼,rest用的是json。highway和rest都是基于vertx做的,vertx是基于netty的。
Q: 框架中引入了vertx會有什么好處?
A: 啟用vertx的標準工作模式更強大,不過對業務人員要求就有些高了,目前還沒開放業務接口出來。vertx標準的reactive工作模式,要求業務代碼中不能有任何的block wait,sleep,大循環,總之不能有阻塞,做到這一點,就可以用更少的CPU,提供更多的服務。
Q: 一個服務提供者里面會有多個 appid 和微服務嗎?什么場景會出現這種情況?
A: 會,這里表達的是一個merge的概念。microservice.yaml文件,可能同時存在于jar,磁盤,命令行參數指定這幾個地方,此時他們按優先級合并,是用于增加靈活性的。在jar里的是默認值,在此之外,還有環境變量,命令行參數,配置中心覆蓋,提供多層定制。
Q: ServiceComb和服務中心是怎么交互的?
A: 走rest,主要負責注冊,取數據和心跳等;watch事件走websocket,watch事件是觀察服務中心實例信息有沒有變更。
Q: 有類似dubbo那種治理中心嗎?
A: bizkeeper是一個handler,是治理的其中一個內容。治理可以通過handler擴展。
Q: service path怎么理解?
A: 每個微服務有一個servicePathManager,每一個schema將自己的path注冊進去。
Q: 瀏覽器能直接訪問微服務Endpoint嗎?
A: 可以,restful發布的微服務Endpoint,可以直接在瀏覽器中使用HTTP加service path訪問提供get方法的服務,如果是訪問其他Http方法提供的服務建議安裝使用Postman。
Q: 契約生成時,需要強制帶上版本號和語言嗎?
A: 契約是屬于微服務的,微服務本來就有版本,但語言是不應該帶上版本號的。應該契約要求與語言無關。契約“沒有版本”,契約的版本體現在微服務上,實例能找到所屬的微服務的版本,就能找到一個確定的契約。
Q: ServiceRegistry里的設計代碼和Eureka很類似?
A: 我們第一個版本就是在Spring Cloud的基礎上做的 后來隨著發展發現不夠用了才逐漸自己做的一套,所以的確是在充分參考Eureka后設計的。
Q: 有些rpc是netty調用redis實現,比直接netty轉發優勢在哪里?
A: 可能是想用redis解決訂閱發布吧。但這樣意義也不大,之前也嘗試過這么用,但后來都改成ServiceComb了。
Q: 如果同時引入了transport-rest-servlet和transport-rest-vertx的依賴,那么它怎么決定采用哪一個?
A: 如果端口沒被占用,就用vertx;如果被占用了,就用servlet。
Q: qps流控設計時是出于什么場景考慮的?
A: 限流有兩個主要作用,第一通過給不同的消費者限流保證對一些重點服務的服務效果,第二防止雪崩效應。可根據服務的重要性來決定水管的粗細,ServiceComb是支持消費端限流和服務端限流兩種限流方式的,消費端限流可以做到比較精細的控制。
Q: 如果服務端是鏈式調用,即類似a->b->c,那設置了qps 流控會不會造成水管粗細不均的事情?
A: 一般采取的模式是先測量再設置。qps設置最終是結合整體業務需求來進行調控的,而不是就單個節點來進行設置。
Q: 通過cse://serviceName/appPath調用服務失敗,報錯:java.lang.Error:not support def type:class io.swagger.models.properties xxx
A: 檢查consumer和provider依賴的java-chassis版本是否一致,如果不一致請修改并使用較新版本。
Q: 發送rest請求時,出現如下報錯:Bad Request,description:http:request body too large
A: 檢查Service Center是否老版本,如果是,則升級到最新版本。
Q: 如何在契約DTO中忽略中指定的屬性?
A: 如果是使用rest transport,因為是Json序列化,可以使用@JsonIgnore注解標記需要忽略的屬性;highway transport目前尚不支持。注意修改后需要更新微服務的version,例如:
public class OutputForTest{
@JsonIgnore
private String outputId = null;
private String inputId = null;
...
}
Q: 如何在用戶自定義的handler中獲取header中某個字段的值
A: Invocation.getArgs可以獲取到接口定義里面聲明的所有參數信息。在接口里面未定義的信息,比如額外的header,則需要通過InvocationContext來傳遞和獲取。基本原理是實現HttpServerFilter將header設置到InvocationContext里面,然后在通過invocation.getContext獲取。示例代碼可以參考servicecomb-fence的 AuthHandler 和 AuthenticationFilter 。
Q: 微服務運行時拋出異常:java.lang.Error:not support def type:calss io.swagger.models.properties BaseIntegerProperty?
A: 可將Service Center升級至最新版本來解決。ServiceComb版本下載傳送門。
總結
以上是生活随笔為你收集整理的java接口常见问题分析_常见问题 - Apache ServiceComb的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 周鸿祎携三份提案上两会 聚焦ChatGP
- 下一篇: python如何自动打印_利用Pytho