假设,有一家科技公司,处理复杂的数学计算,进行尖端的生物科学研究,并为用户提供服务:
黄金分割数列计算
蜜蜂繁殖规律(计算每只雄峰/雌蜂的祖先数量)
在为用户提供服务之前,首先确保通过认证避免公司的计算资源被用户滥用;同时为用户提供REST访问方式。
业务场景

Worker
黄金分割运算
interface FibonacciService{
long term(int n);
}
@Service
class FibonacciServiceImpl implements FibonacciService{
@Override
public long term(int n){
if(n==0){
return 0;
}else if(n==1){
return 1;
}
return term(n-1)+term(n-2);
}
}
将黄金分割运算提供服务,首先引入ServiceComb依赖
<dependency>
<groupId>org.apache.servicecomb</groupId>
<artifactId>spring-boot-starter-provider</artifactId>
</dependency>
提供Restful和RPC端点
@RestSchema(schemaId="fibonacciRestEndpoint")
@RequestMapping("/fibonacci")
@Controller
public class FibonacciRestEndpoint implements FibonacciEndpoint{
private final FibonacciService fibonacciService;
@Autowired
FibonacciRestEndpoint(FibonacciService fibonacciService){
this.fibonacciService=fibonacciService;
}
@Override
@RequestMapping(value="/term",method=RequestMethod.GET)
@ResponseBody
public long term(int n){
return fibonacciService.term(n);
}
}
@RpcSchema(schemaId="fibonacciRpcEndpoint")
public class FibonacciRpcEndpoint implements FibonacciEndpoint{
private final FibonacciService fibonacciService;
@Autowired
public FibonacciRpcEndpoint(FibonacciService fibonacciService){
this.fibonacciService=fibonacciService;
}
@Override
public long term(int n){
return fibonacciService.term(n);
}
}
@RestSchema和@RpcSchema注释两个端点之后,ServiceComb会自动生成对应的服务端点契约。
在microservice.yaml 配置端点端口,并注册到Service Center
APPLICATION_ID: company
service_description:
name: worker
version: 0.0.1
cse:
service:
registry:
address: http://sc.servicecomb.io:30100
highway:
address: 0.0.0.0:7070
rest:
address: 0.0.0.0:8080
worker应用启动入口
@SpringBootApplication
@EnableServiceComb
public class WorkerApplication{
public static void main(String[] args){
SpringApplication.run(WorkerApplication.class,args);
}
}
Bulletin Board
Service Center提供契约和服务注册、发现功能,并且检验服务提供方和消费方的契约是否匹配。
Beekeeper
密封的祖先数量符合黄金分割数列模型,定义黄金数列运算接口
public interface FibonacciCalculator{
long term(int n);
}
密封繁殖规律研究服务
interface BeekeeperService{
long ancestorsOfDroneAt(int generation);
long ancestorsOfQueenAt(int generation);
}
class BeekeeperServiceImpl implements BeekeeperService {
private final FibonacciCalculator fibonacciCalculator;
BeekeeperServiceImpl(FibonacciCalculator fibonacciCalculator){
this.fibonacciCalculator=fibonacciCalculator;
}
@Override
public long ancestorsOfDroneAt(int generation){
if(generation<=0){
return 0;
}
return fibonacciCalculator.term(generation+1);
}
@Override
public long ancestorsOfQueenAt(int generation){
if(generation<=0){
return 0;
}
return fibonacciCalculator.term(generation+2);
}
}
从Service Center获取worker服务
@Configuration
class BeekeeperConfig {
@RpcReference(microserviceName="worker",schemaId="fibonacciRpcEndpoint")
private FibonacciCalculator fibonacciCalculator;
@Bean
BeekeeperService beekeeperService(){
return new BeekeeperServiceImpl(fibonacciCalculator);
}
}
beekeeper的服务端点
@RestSchema(schemaId="beekeeperRestEndpoint")
@RequestMapping("/rest")
@Controller
public class BeekeeperController{
private static final Logger logger=LoggerFactory.getLogger(BeekeeperController.class);
private final BeekeeperService beekeeperService;
@Autowired
BeekeeperController(BeekeeperService beekeeperService){
this.beekeeperService=beekeeperService;
}
@RequestMapping(value="/drone/ancestors/{generation}",method=GET,produces=APPLICATION_JSON_UTF8_VALUE)
@ResponseBody
public Ancestor ancestorsOfDrone(@PathVariable int generation){
logger.info("Received request to find the number of ancestors of drone at generation {}".generation);
return new Ancestor(beekeeperService.ancestorsOfDroneAt(generation));
}
@RequestMapping(value="/queen/ancestors/{generation}",method=GET,produces=APPLICATION_JSON_UTF8_VALUE)
@ResponseBody
public Ancestor ancestorsOfQueen(@PathVariable int generation){
logger.info("Received request to find the number of ancestors of queen at generation {}",generation);
return new Ancestor(beekeeperService.ancestorsOfQueenAt(generation));
}
}
class Ancestor{
private long ancestors;
Ancestor(){
}
Ancestor(long ancestors){
this.ancestors=ancestors;
}
public long getAncestors(){
return ancestors;
}
}
配置microservice.yaml
APPLICATION_ID: company
service_description:
name: beekeeper
version: 0.0.1
cse:
service:
registry:
address: http://sc.servicecomb.io:30100
rest:
address: 0.0.0.0:8090
handler:
chain:
Consumer:
default: bizkeeper-consumer.loadbalance
references:
worker:
version-rule: 0.0.1
定义beekeeper的应用入口
@SpringBootApplication
@EnableServiceComb
public class BeekeeperApplication{
public static void main(Stirng[] args){
SpringApplication.run(BeekeeperApplication.class,args);
}
}
Doorman
屏蔽非法用户,提供安全保障。
认证采用JSON Web Token(JWT)机制。authenticate方法根据用户名和密码确认用户存在,并返回对应的JWT token。用户登录后的每次请求都需要带上JWT token;而validate方法将验证token是否有效。
public interface AuthenticationService{
String authenticate(String username,String password);
String validate(String token);
}
认证服务端点
@RestSchema(schemaId="authenticationRestEndpoint")
@Controller
@RequestMapping("/rest")
public class AuthenticationController{
private static final Logger logger=LoggerFactory.getLogger(AuthenticationController.class);
static final String USERNAME="username";
static final String PASSWORD="password";
static final String TOKEN="token";
private final AuthenticationService authenticationService;
@Autowired
AuthenticateionController(AuthenticationService authenticationService){
this.authenticationService=authenticationService;
}
@RequestMapping(value="/login",method=POST,produces=TEXT_PLAIN_VALUE)
public ResponseEntity<String> login(@RequestParam(USERNAME) String username,@RequestParam(PASSWORD) String password){
logger.info("Received login request from user {}",username);
String token=authenticationService.authenticate(username,password);
HttpHeaders headers=new HttpHeaders();
headers.add(AUTHORIZATION,TOKEN_PREFIX+token);
logger.info("Authenticated user {} successfully",username);
return new ResponseEntity<>("Welcome,"+username,headers,OK);
}
@RequestMapping(value="/validate",method=POST,consumes=APPLICATION_JSON_UTF8_VALUE,produces=TEXT_PLAIN_VALUE)
@ResponseBody
public String validate(@RequestBody Token token){
logger.info("Received validation request of token {}",token);
return authenticationService.validate(token.getToken());
}
}
class Token{
private String token;
Token(){
}
public String getToken(){
return token;
}
@Override
public String toString(){
return "Token{token='"+token+"'}";
}
}
microservice.yaml 配置
APPLICATION_ID: company
service_description:
name: doorman
version: 0.0.1
cse:
service:
registry:
address: http://sc.servicecomb.io: 30100
rest:
address: 0.0.0.0:9090
服务应用入口
@SpringBootApplication
@EnableServiceComb
public class DoormanApplication{
public static void main(String[] args){
SpringApplication.run(DoormanApplication.class,args);
}
}
Manager
首先引入依赖
<dependency>
<groupId>org.apache.servicecomb</groupId>
<artifactId>spring-boot-starter-discovery</artifactId>
</dependency>
用户认证服务
当用户发送非登录请求时,需要验证用户是否合法,ServiceComb会自动查询对应服务并发送请求到地址中的服务端端点
@Service
public class AuthenticationService {
private static final Logger logger=LoggerFactory.getLogger(AuthenticationService.class);
private static final String DOORMAN_ADDRESS="cse://doorman";
private final RestTemplate restTemplate;
AuthenticationService(){
this.restTemplate=RestTemplateBuilder.create();
this.restTemplate.setErrorHandler(new ResponseErrorHandler(){
@Override
public boolean hasError(ClientHttpResponse clientHttpResponse) throws IOException{
return false;
}
@Override
public void handleError(ClientHttpResponse clientHttpResponse) throws IOException{
}
});
}
@HystrixCommand(fallbackMethod="timeout")
public ResponseEntity<String> validate(String token){
logger.info("validating token {}",token);
ResponseEntity<String> responseEntity=restTemplate.postForEntity(
DOORMAN_ADDRESS+"/rest/validate",
validationRequest(token),
String,class
);
if(!responseEntity.getStatusCode().is2xxSuccessful()){
logger.warn("No such user found with token {}",token);
}
logger.info("validated request of token {} to be user {}",token,responseEntity,getBody());
return responseEntity;
}
private ResponseEntity<String> timeout(String token){
logger.warn("Request to validate token {} timeout",token);
return new ResponseEntity<>(REQUEST_TIMEOUT);
}
private HttpEntity<Token> validationRequest(String token){
HttpHeaders headers=new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
return new HttpEntity<>(new Token(token),headers);
}
}
请求过滤
使用ZuulFilter实现过滤用户请求,调用authenticationService.validate(token)认证用户
@Component
class AuthenticationAwareFilter extends ZuulFilter{
private static final Logger logger=LoggerFactory.getLogger(AuthenticationAwareFilter.class);
private static final String LOGIN_PATH="/login";
private final AuthenticationService authenticationService;
private final PathExtractor pathExtractor;
@Autowired
AuthenticationAwareFilter(AuthenticationService authenticationService,PathExtractor pathExtractor){
this.authenticationService=authenticationService;
this.pathExtractor=pathExtractor;
}
@Overrid
public String filterType(){
return "pre";
}
@Override
public int filterOrder(){
return 1;
}
@Override
public Object run(){
filter();
return null;
}
private void filter(){
RequestContext context=RequestContext.getCurrentContext();
if(doesNotContainToken(context)){
logger.warn("No token found in request header");
rejectRequest(context);
}else{
String token=token(context);
ResponseEntity<String> responseEntity=authenticationService.validate(token);
if(!responseEntity.getStatusCode().is2xxSuccessful()){
logger.warn("Unauthorize token {} and request rejected",token);
rejectRequest(context);
}else{
logger.info("Token {} validated",token);
}
}
}
private void rejectRequest(RequestContext context){
context.setResponseStatusCode(SC_FORBIDDEN);
context.setSendZuulResponse(false);
}
private boolean doesNotContainToken(RequestContext context){
return authorizationHeader(context)==null || !authorizationHeader(context).startsWith(TOKEN_PREFIX);
}
private String token(RequestContext context){
return authorizationHeader(context).replace(TOKEN_PREFIX,"");
}
private String authorizationHeader(RequestContext context){
return context.getRequest().getHeader(AUTHORIZATION);
}
}
application.yaml 中定义路由规则
zuul:
routes:
doorman:
serviceId: doorman
sensitiveHeaders:
worker:
serviceId: worker
beekeeper:
serviceId: beekeeper
ribbon:
eureka:
enabled: false
microservice.yaml 定义服务中心地址
APPLICATION_ID: company
service_description:
name: manager
version: 0.0.1
cse:
service:
registry:
address: http://sc.servicecomb.io:30100
应用入口
@SpringBootApplication
@EnableCircuitBreaker
@EnableZuulProxy
@EnableDiscoveryClient
@EnableServiceComb
public class ManagerApplication{
public static void main(String[] args){
SpringApplication.run(ManagerApplication.class,args);
}
}
项目归档(Project Archive)
manager会将每次用户请求缓存,这里采用Spring Cache Abstraction
人力资源(Human Resouce)
从运维层面保证服务的可靠性,主要包括:
弹性伸缩:用户请求量增多与回落
健康检查
滚动升级
【参考】